런타임 계약과 학습자 흐름 보강

This commit is contained in:
Yun Chan 2026-06-29 08:12:14 +09:00
parent f456b8997a
commit 206018b088
56 changed files with 4306 additions and 1008 deletions

View file

@ -24,6 +24,16 @@ ENGINE_GATEWAY_SSE_EVENTS: tuple[EngineGatewaySseEvent, ...] = (
ENGINE_GATEWAY_SSE_DONE,
ENGINE_GATEWAY_SSE_ERROR,
)
ENGINE_GATEWAY_DEFAULT_MODEL_SENTINEL = "gateway-default"
def normalize_engine_gateway_model(model: Optional[str]) -> Optional[str]:
"""Return an explicit model override, or None for gateway default routing."""
value = (model or "").strip()
if not value or value == ENGINE_GATEWAY_DEFAULT_MODEL_SENTINEL:
return None
return value
class EngineMessage(BaseModel):
@ -58,6 +68,27 @@ class GenerateResponse(BaseModel):
structured: Optional[dict[str, Any]] = None
def structured_payload_from_response(resp: GenerateResponse) -> dict[str, Any] | None:
"""Return structured output, or a JSON object embedded in legacy text."""
if isinstance(resp.structured, dict):
return resp.structured
raw = (resp.text or "").strip()
if not raw:
return None
raw = _strip_json_fence(raw)
parsed = _json_object_or_none(raw)
if parsed is not None:
return parsed
start, end = raw.find("{"), raw.rfind("}")
if 0 <= start < end:
return _json_object_or_none(raw[start : end + 1])
return None
class StreamTokenEvent(BaseModel):
text: str
@ -188,6 +219,23 @@ def _to_stream_error_event(payload: Any) -> StreamErrorEvent:
return StreamErrorEvent(detail="engine stream error")
def _strip_json_fence(value: str) -> str:
if not value.startswith("```"):
return value
text = value.split("```", 2)[1] if value.count("```") >= 2 else value.strip("`")
if text.lstrip().lower().startswith("json"):
text = text.lstrip()[4:]
return text.strip()
def _json_object_or_none(value: str) -> dict[str, Any] | None:
try:
parsed = json.loads(value)
except (json.JSONDecodeError, ValueError):
return None
return parsed if isinstance(parsed, dict) else None
def _safe_int(value: Any) -> int:
try:
return int(value or 0)