음성 재생과 운영 배포 정리

This commit is contained in:
Yun Chan 2026-06-28 12:18:20 +09:00
parent 8ed185ce6c
commit ac7db95542
1020 changed files with 46863 additions and 2175 deletions

View file

@ -0,0 +1 @@
"""Cross-runtime contracts for replaceable service boundaries."""

View file

@ -0,0 +1,82 @@
"""Engine gateway HTTP and SSE contract.
Keep this module adapter-neutral. The Python API client, the current Python
gateway, and a future Node.js gateway must preserve these shapes.
"""
from __future__ import annotations
import json
from typing import Any, Literal, Optional
from pydantic import BaseModel, Field
AIRole = Literal["client", "counselor", "evaluator"]
EngineMessageRole = Literal["system", "user", "assistant"]
EngineGatewaySseEvent = Literal["token", "done", "error"]
ENGINE_GATEWAY_SSE_TOKEN: EngineGatewaySseEvent = "token"
ENGINE_GATEWAY_SSE_DONE: EngineGatewaySseEvent = "done"
ENGINE_GATEWAY_SSE_ERROR: EngineGatewaySseEvent = "error"
ENGINE_GATEWAY_SSE_EVENTS: tuple[EngineGatewaySseEvent, ...] = (
ENGINE_GATEWAY_SSE_TOKEN,
ENGINE_GATEWAY_SSE_DONE,
ENGINE_GATEWAY_SSE_ERROR,
)
class EngineMessage(BaseModel):
role: EngineMessageRole
content: str
cache: bool = False
class GenerateRequest(BaseModel):
ai_role: AIRole = "client"
messages: list[EngineMessage]
model: Optional[str] = None
max_tokens: int = 1024
temperature: float = 0.7
structured_schema: Optional[dict[str, Any]] = None
session_id: Optional[str] = None
metadata: dict[str, Any] = Field(default_factory=dict)
class StreamRequest(GenerateRequest):
"""SSE stream request for live client AI turns."""
class GenerateResponse(BaseModel):
text: str
model: str
provider: str
tokens_in: int = 0
tokens_out: int = 0
cost_usd: float = 0.0
inference_geo: Optional[str] = None
structured: Optional[dict[str, Any]] = None
class StreamTokenEvent(BaseModel):
text: str
class StreamDoneEvent(BaseModel):
provider: str
model: str
tokens_in: int = 0
tokens_out: int = 0
cost_usd: float = 0.0
turns: int = 0
class StreamErrorEvent(BaseModel):
detail: str
def sse_frame(event: EngineGatewaySseEvent, payload: BaseModel | dict[str, Any]) -> str:
if isinstance(payload, BaseModel):
body = payload.model_dump()
else:
body = payload
return f"event: {event}\ndata: {json.dumps(body, ensure_ascii=False)}\n\n"