세션 계약과 메모리 경계 보강
This commit is contained in:
parent
391639c1de
commit
2bb052f624
12 changed files with 836 additions and 116 deletions
|
|
@ -11,27 +11,26 @@ import re
|
|||
from collections import Counter
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Literal, Optional
|
||||
from typing import Literal, Optional, cast
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from . import turn_runtime
|
||||
from .config import settings
|
||||
from .services import session_metrics
|
||||
from .stage_contract import (
|
||||
ReviewPhaseKey,
|
||||
StageLabel,
|
||||
review_phase_key,
|
||||
stage_label as _normalize_stage_label,
|
||||
stage_label_or_none,
|
||||
)
|
||||
from .store import InProcSession, TurnRecord
|
||||
|
||||
StageLabel = Literal["라포", "탐색", "개입", "정리"]
|
||||
WorksheetSpeaker = Literal["learner", "client"]
|
||||
WorksheetItemSpec = tuple[str, str, list[str], WorksheetSpeaker | None]
|
||||
WorksheetSectionSpec = tuple[str, str, list[WorksheetItemSpec]]
|
||||
|
||||
LEARNER_VISIBLE_AI_ROLE = "counselor"
|
||||
_PHASE_KEY_BY_LABEL = {
|
||||
"라포": "rapport",
|
||||
"탐색": "explore",
|
||||
"개입": "intervene",
|
||||
"정리": "closing",
|
||||
}
|
||||
|
||||
|
||||
class LearnerSessionSummary(BaseModel):
|
||||
|
|
@ -204,8 +203,8 @@ class ReviewTurn(BaseModel):
|
|||
|
||||
|
||||
class ReviewPhaseSegment(BaseModel):
|
||||
key: str
|
||||
label: str
|
||||
key: ReviewPhaseKey
|
||||
label: StageLabel
|
||||
weight: float
|
||||
|
||||
|
||||
|
|
@ -330,6 +329,9 @@ class SessionTeacherReviewStatus(BaseModel):
|
|||
reviewerId: str | None = None
|
||||
reviewedAt: str | None = None
|
||||
updatedAt: str | None = None
|
||||
worksheetStatus: Literal["pending", "approved", "changes_requested", "rejected"] = "pending"
|
||||
worksheetNote: str = ""
|
||||
worksheetReviewedAt: str | None = None
|
||||
|
||||
|
||||
class SessionReviewResponse(BaseModel):
|
||||
|
|
@ -338,7 +340,7 @@ class SessionReviewResponse(BaseModel):
|
|||
date: str
|
||||
durationLabel: str
|
||||
durationSeconds: int
|
||||
reachedPhase: str
|
||||
reachedPhase: StageLabel
|
||||
sessionSignal: str
|
||||
supervisorState: str
|
||||
supervisorName: str
|
||||
|
|
@ -385,8 +387,8 @@ class SessionReviewReadInput:
|
|||
now_ts: float | None = None
|
||||
|
||||
|
||||
def stage_label(stage: object) -> str:
|
||||
return turn_runtime.stage_label(stage)
|
||||
def stage_label(stage: object) -> StageLabel:
|
||||
return cast(StageLabel, _normalize_stage_label(stage))
|
||||
|
||||
|
||||
def iso(ts: float | None) -> str | None:
|
||||
|
|
@ -658,7 +660,7 @@ def _client_name(raw: str) -> str:
|
|||
return name or raw.strip() or "내담자"
|
||||
|
||||
|
||||
def _review_summary(*, client_name: str, reached_phase: str, turns: list[ReviewTurn]) -> str:
|
||||
def _review_summary(*, client_name: str, reached_phase: StageLabel, turns: list[ReviewTurn]) -> str:
|
||||
if not turns:
|
||||
return (
|
||||
"아직 실제 발화가 없어 리뷰를 만들 수 없습니다. 회기를 진행한 뒤 종료하면 "
|
||||
|
|
@ -674,11 +676,11 @@ def _review_summary(*, client_name: str, reached_phase: str, turns: list[ReviewT
|
|||
)
|
||||
|
||||
|
||||
def _phase_segments(stage_labels: list[str]) -> list[ReviewPhaseSegment]:
|
||||
def _phase_segments(stage_labels: list[StageLabel]) -> list[ReviewPhaseSegment]:
|
||||
counts = Counter(stage_labels)
|
||||
return [
|
||||
ReviewPhaseSegment(
|
||||
key=_PHASE_KEY_BY_LABEL.get(label, label),
|
||||
key=review_phase_key(label),
|
||||
label=label,
|
||||
weight=float(count),
|
||||
)
|
||||
|
|
@ -959,6 +961,15 @@ def saved_case_worksheet_from_payload(payload: dict[str, object] | None) -> Revi
|
|||
return worksheet.model_copy(update={"status": "saved_by_learner"})
|
||||
|
||||
|
||||
def _worksheet_review_status_value(
|
||||
value: object,
|
||||
) -> Literal["pending", "approved", "changes_requested", "rejected"]:
|
||||
raw = str(value or "pending")
|
||||
if raw in {"approved", "changes_requested", "rejected"}:
|
||||
return raw # type: ignore[return-value]
|
||||
return "pending"
|
||||
|
||||
|
||||
def _worksheet_share_highlights(worksheet: ReviewCaseWorksheet, *, limit: int = 4) -> list[dict[str, str]]:
|
||||
highlights: list[dict[str, str]] = []
|
||||
for section in worksheet.sections:
|
||||
|
|
@ -1342,6 +1353,9 @@ def build_session_review(read_input: SessionReviewReadInput) -> SessionReviewRes
|
|||
reviewerId=str(review_status.get("reviewer_id") or "") or None,
|
||||
reviewedAt=str(review_status.get("reviewed_at") or "") or None,
|
||||
updatedAt=str(review_status.get("updated_at") or "") or None,
|
||||
worksheetStatus=_worksheet_review_status_value(review_status.get("worksheet_status")),
|
||||
worksheetNote=str(review_status.get("worksheet_note") or ""),
|
||||
worksheetReviewedAt=str(review_status.get("worksheet_reviewed_at") or "") or None,
|
||||
)
|
||||
|
||||
return SessionReviewResponse(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue