동의 게이트와 런타임 안정화

This commit is contained in:
Yun Chan 2026-06-27 17:22:38 +09:00
parent 0eb7d925ed
commit 0ec266a761
34 changed files with 1186 additions and 158 deletions

View file

@ -19,6 +19,7 @@ from pydantic import BaseModel, Field
from sse_starlette.sse import EventSourceResponse
from .. import db, session_persistence, turn_runtime
from ..auth_sessions import user_has_consent
from ..config import settings
from ..deps import CurrentPrincipal, Principal, Role
from ..engine_client import EngineError, engine_client
@ -240,6 +241,7 @@ class SessionReviewResponse(BaseModel):
_RECALL_CACHE: dict[str, memory.RecallContext] = {}
# 세션별 KB 증상 행동단서(회기 1회 산출·캐시). 빈 list 캐시 = 회기 내 재시도 안 함(안정성).
_KB_CUES_CACHE: dict[str, list[str]] = {}
_RAG_WARM_SEMAPHORE = asyncio.Semaphore(1)
_LEARNER_VISIBLE_AI_ROLE = "counselor"
# ────────────────────────────────────────────────────────────────────────────
@ -409,14 +411,15 @@ async def _warm_rag_caches(session_id: str, case_id: str, card) -> None:
BGE-M3 임베더 로드(~ ) 회기 시작/ 응답을 막지 않도록 create_task로 띄운다.
warm 완료 턴은 회상/단서로 진행(graceful), 이후 턴부터 RAG 주입. 구간 비치명적.
"""
try:
_RECALL_CACHE[session_id] = await _build_start_recall(case_id=case_id, card=card)
except Exception:
pass
try:
_KB_CUES_CACHE[session_id] = await _retrieve_kb_behavior_cues(card)
except Exception:
pass
async with _RAG_WARM_SEMAPHORE:
try:
_RECALL_CACHE[session_id] = await _build_start_recall(case_id=case_id, card=card)
except Exception:
pass
try:
_KB_CUES_CACHE[session_id] = await _retrieve_kb_behavior_cues(card)
except Exception:
pass
_PHASE_KEY_BY_LABEL = {
@ -436,6 +439,14 @@ def _ensure_learner(principal: Principal) -> None:
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="only learners can use sessions")
async def _ensure_practice_consent(principal: Principal) -> None:
if principal.consent_at is not None:
return
if await user_has_consent(principal.user_id):
return
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="consent_required")
async def _load_session_or_404(
session_id: str,
principal: Principal,
@ -1149,6 +1160,7 @@ async def start_session(
) -> SessionStartResponse:
"""Start a learner-owned practice session."""
_ensure_learner(principal)
await _ensure_practice_consent(principal)
try:
catalog_persona = await get_catalog_persona(body.persona_code)