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

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

@ -21,7 +21,8 @@ from typing import TYPE_CHECKING, Any, Literal, Optional
from pydantic import BaseModel, Field, field_validator
from ..config import settings
from ..engine_client import EngineClient, EngineError, EngineMessage, GenerateRequest, GenerateResponse
from ..contracts.engine_gateway import structured_payload_from_response
from ..engine_client import EngineClient, EngineError, EngineMessage, GenerateRequest
from ..paths import repo_root, repo_path
from ..session_read_model import StageLabel, stage_label_or_none
from . import guardrail
@ -433,30 +434,6 @@ def _schema() -> dict[str, Any]:
}
def _structured_payload(resp: GenerateResponse) -> Optional[dict[str, Any]]:
if isinstance(resp.structured, dict):
return resp.structured
raw = (resp.text or "").strip()
if not raw:
return None
if raw.startswith("```"):
raw = raw.split("```", 2)[1] if raw.count("```") >= 2 else raw.strip("`")
if raw.lstrip().lower().startswith("json"):
raw = raw.lstrip()[4:]
try:
data = json.loads(raw)
return data if isinstance(data, dict) else None
except (json.JSONDecodeError, ValueError):
start, end = raw.find("{"), raw.rfind("}")
if 0 <= start < end:
try:
data = json.loads(raw[start : end + 1])
return data if isinstance(data, dict) else None
except (json.JSONDecodeError, ValueError):
return None
return None
def _clip(value: Any, limit: int) -> Optional[str]:
text = str(value or "").strip()
if not text:
@ -674,7 +651,7 @@ async def generate_live_coaching(
inference_geo=resp.inference_geo,
latency_ms=latency_ms,
)
payload = _structured_payload(resp)
payload = structured_payload_from_response(resp)
if payload is None:
return _fallback_suggestion(
item,