회기 무발화 0턴 분리, 자기예측 락 불변식 및 TDD 회귀 검증 완료
Some checks failed
API contract / OpenAPI type drift (push) Failing after 3m27s
Some checks failed
API contract / OpenAPI type drift (push) Failing after 3m27s
This commit is contained in:
parent
a479db7a5a
commit
a0311c5957
100 changed files with 4884 additions and 11210 deletions
|
|
@ -21,7 +21,7 @@ from fastapi import APIRouter, HTTPException, Request, status
|
|||
from pydantic import BaseModel, Field, field_validator
|
||||
from sse_starlette.sse import EventSourceResponse
|
||||
|
||||
from .. import db, session_persistence, turn_runtime
|
||||
from .. import db, session_evaluation_repository, session_persistence, turn_runtime
|
||||
from ..auth_sessions import user_has_consent, user_onboarding_complete
|
||||
from ..config import settings
|
||||
from ..deps import CurrentPrincipal, Principal, Role
|
||||
|
|
@ -29,6 +29,7 @@ from ..engine_client import EngineError, engine_client
|
|||
from ..persona_repository import get_catalog_persona
|
||||
from ..runtime_policy import require_runtime_fallback_allowed
|
||||
from ..session_evaluation_input import enriched_masked_turns
|
||||
from ..session_turn_memory import build_turn_memory
|
||||
from ..session_evaluation_timeout import (
|
||||
session_evaluation_outer_timeout_seconds,
|
||||
session_evaluation_timeout_seconds as _session_evaluation_timeout_seconds,
|
||||
|
|
@ -50,15 +51,28 @@ from ..services import (
|
|||
session_learning_producer,
|
||||
state_machine,
|
||||
)
|
||||
from ..session_dashboard_projection import (
|
||||
LearnerDashboardResponse,
|
||||
dashboard_achievements as _dashboard_achievements,
|
||||
dashboard_feedback as _dashboard_feedback,
|
||||
dashboard_growth as _dashboard_growth,
|
||||
dashboard_overview as _dashboard_overview,
|
||||
dashboard_persona_progress as _dashboard_persona_progress,
|
||||
dashboard_training_exposure as _dashboard_training_exposure,
|
||||
)
|
||||
from ..session_projection import (
|
||||
LEARNER_VISIBLE_AI_ROLE,
|
||||
iso as _iso,
|
||||
learner_visible_turns as _learner_visible_turns,
|
||||
stage_label as _stage_label,
|
||||
)
|
||||
from ..session_read_model import (
|
||||
CaseMemoryPreview,
|
||||
CaseProgressStats,
|
||||
LearnerCaseListResponse,
|
||||
LearnerCaseSummary,
|
||||
LearnerDashboardResponse,
|
||||
LearnerSessionsResponse,
|
||||
LearnerSessionSummary,
|
||||
LEARNER_VISIBLE_AI_ROLE,
|
||||
ReviewCaseWorksheet,
|
||||
ReviewCaseWorksheetSaveRequest,
|
||||
ReviewWorksheetItem as ReviewWorksheetItem,
|
||||
|
|
@ -73,18 +87,9 @@ from ..session_read_model import (
|
|||
StageLabel,
|
||||
build_session_progress,
|
||||
build_session_review,
|
||||
dashboard_achievements as _dashboard_achievements,
|
||||
dashboard_feedback as _dashboard_feedback,
|
||||
dashboard_growth as _dashboard_growth,
|
||||
dashboard_overview as _dashboard_overview,
|
||||
dashboard_persona_progress as _dashboard_persona_progress,
|
||||
dashboard_training_exposure as _dashboard_training_exposure,
|
||||
iso as _iso,
|
||||
learner_summary as _learner_summary,
|
||||
learner_visible_turns as _learner_visible_turns,
|
||||
session_detail as _session_detail,
|
||||
session_share_payload as _session_share_payload,
|
||||
stage_label as _stage_label,
|
||||
)
|
||||
from ..store import InProcSession, TurnRecord, store
|
||||
|
||||
|
|
@ -548,12 +553,7 @@ async def _prepare_turn_context(
|
|||
state=sess.state,
|
||||
learner_text=learner_text,
|
||||
learner_identity=sess.learner_label,
|
||||
memory=orchestrator.TurnMemory(
|
||||
recall_summary=recall.recall_summary,
|
||||
pinned_facts=recall.pinned_facts,
|
||||
recent_turns=sess.recent_turns(visible_to="client"),
|
||||
kb_behavior_cues=kb_cues,
|
||||
),
|
||||
memory=build_turn_memory(sess, recall, kb_cues),
|
||||
theory_mode=sess.theory_mode,
|
||||
scenario_context=scenario_context,
|
||||
)
|
||||
|
|
@ -956,14 +956,14 @@ async def _generate_and_save_session_evaluation(sess: InProcSession) -> None:
|
|||
# 예산 뒤에 outer grace를 두어 정상 결과를 timeout error로 바꾸지 않는다.
|
||||
timeout=session_evaluation_outer_timeout_seconds(),
|
||||
)
|
||||
write = session_persistence.SessionEvaluationWrite.from_result(
|
||||
write = session_evaluation_repository.SessionEvaluationWrite.from_result(
|
||||
session_id=sess.session_id,
|
||||
learner_id=sess.learner_id,
|
||||
result=result,
|
||||
counselor_identity=sess.learner_label,
|
||||
client_identity=sess.persona.display_name,
|
||||
)
|
||||
saved = await session_persistence.save_session_evaluation(write)
|
||||
saved = await session_evaluation_repository.save_session_evaluation(write)
|
||||
if not saved:
|
||||
logger.error(
|
||||
"session evaluation save did not reach durable store: session_id=%s status=%s scope=%s",
|
||||
|
|
@ -988,7 +988,7 @@ async def _generate_and_save_session_evaluation(sess: InProcSession) -> None:
|
|||
except asyncio.TimeoutError:
|
||||
message = f"session evaluation timeout after {timeout_seconds:g}s"
|
||||
logger.exception("%s: session_id=%s", message, sess.session_id)
|
||||
write = session_persistence.SessionEvaluationWrite.from_error(
|
||||
write = session_evaluation_repository.SessionEvaluationWrite.from_error(
|
||||
session_id=sess.session_id,
|
||||
learner_id=sess.learner_id,
|
||||
scope="session_end",
|
||||
|
|
@ -997,7 +997,7 @@ async def _generate_and_save_session_evaluation(sess: InProcSession) -> None:
|
|||
counselor_identity=sess.learner_label,
|
||||
client_identity=sess.persona.display_name,
|
||||
)
|
||||
saved = await session_persistence.save_session_evaluation(write)
|
||||
saved = await session_evaluation_repository.save_session_evaluation(write)
|
||||
if not saved:
|
||||
logger.error(
|
||||
"session evaluation error save did not reach durable store: session_id=%s error=%s",
|
||||
|
|
@ -1008,7 +1008,7 @@ async def _generate_and_save_session_evaluation(sess: InProcSession) -> None:
|
|||
await _enqueue_session_review_ready_notification(sess.session_id)
|
||||
except Exception as exc:
|
||||
logger.exception("session evaluation failed: session_id=%s", sess.session_id)
|
||||
write = session_persistence.SessionEvaluationWrite.from_error(
|
||||
write = session_evaluation_repository.SessionEvaluationWrite.from_error(
|
||||
session_id=sess.session_id,
|
||||
learner_id=sess.learner_id,
|
||||
scope="session_end",
|
||||
|
|
@ -1017,7 +1017,7 @@ async def _generate_and_save_session_evaluation(sess: InProcSession) -> None:
|
|||
counselor_identity=sess.learner_label,
|
||||
client_identity=sess.persona.display_name,
|
||||
)
|
||||
saved = await session_persistence.save_session_evaluation(write)
|
||||
saved = await session_evaluation_repository.save_session_evaluation(write)
|
||||
if not saved:
|
||||
logger.error(
|
||||
"session evaluation failure record did not reach durable store: session_id=%s error=%s",
|
||||
|
|
@ -1162,7 +1162,7 @@ async def _review_ready(sess: InProcSession, principal: Principal) -> bool:
|
|||
return False
|
||||
if len(turns) != len(sess.turns):
|
||||
return False
|
||||
evaluation_record, _ = await session_persistence.load_session_evaluation(
|
||||
evaluation_record, _ = await session_evaluation_repository.load_session_evaluation(
|
||||
sess.session_id,
|
||||
principal,
|
||||
)
|
||||
|
|
@ -1774,7 +1774,7 @@ async def get_session_review(
|
|||
(
|
||||
evaluation_record,
|
||||
evaluation_durable,
|
||||
) = await session_persistence.load_session_evaluation(
|
||||
) = await session_evaluation_repository.load_session_evaluation(
|
||||
session_id,
|
||||
review_principal,
|
||||
)
|
||||
|
|
@ -2245,7 +2245,8 @@ async def end_session(
|
|||
await _end_persisted_session(sess, carry)
|
||||
invalidate_session_context_cache(session_id)
|
||||
rupture_runtime.schedule_session_scan(session_id, trigger="session_ended")
|
||||
if not was_ended:
|
||||
has_counselor_turns = any(t.speaker in ("counselor", "learner") for t in sess.turns)
|
||||
if not was_ended and has_counselor_turns:
|
||||
_schedule_session_evaluation(sess)
|
||||
|
||||
return SessionEndResponse(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue