엔진 게이트웨이 계약 고정

This commit is contained in:
Yun Chan 2026-06-28 20:12:05 +09:00
parent ebef20560e
commit f0771db919
8 changed files with 1123 additions and 72 deletions

View file

@ -24,6 +24,8 @@ from .config import settings
from .contracts.engine_gateway import (
AIRole,
EngineMessage,
EngineGatewaySseLineDecoder,
EngineGatewaySsePacket,
GenerateRequest,
GenerateResponse,
StreamRequest,
@ -147,9 +149,8 @@ class EngineClient:
async def stream(self, req: StreamRequest) -> AsyncIterator[str]:
"""SSE 토큰 스트림 프록시.
게이트웨이 SSE(`text/event-stream`) data: 청크를 그대로 yield.
라우트(sessions.py) 이걸 받아 자체 SSE 이벤트(heartbeat 포함) 재방출.
TODO: 게이트웨이 이벤트 프레이밍 확정(토큰/usage/done 이벤트 구분).
게이트웨이 SSE(`text/event-stream`) 원시 non-empty line을 yield한다.
호출부는 raw line 대신 `stream_packets()` 사용한다.
"""
try:
async with self.client.stream(
@ -164,6 +165,19 @@ class EngineClient:
except httpx.HTTPError as e:
raise EngineError(f"engine stream transport error: {e}") from e
async def stream_packets(self, req: StreamRequest) -> AsyncIterator[EngineGatewaySsePacket]:
"""Decode gateway SSE into the shared token/done/error contract.
Keep wire-format parsing at the gateway client boundary so app services
do not depend on raw SSE line structure. A future Node.js gateway should
only need to preserve `app.contracts.engine_gateway`.
"""
decoder = EngineGatewaySseLineDecoder()
async for raw in self.stream(req):
packet = decoder.feed_line(raw)
if packet is not None:
yield packet
# 앱 전역 싱글톤 (main lifespan 에서 startup/shutdown)
engine_client = EngineClient()