개선관리 요구사항과 Google 로그인을 완료
This commit is contained in:
parent
cc0a15b7c6
commit
2a39636163
112 changed files with 10166 additions and 527 deletions
|
|
@ -79,6 +79,8 @@ class TurnContext:
|
|||
state_before: SessionState
|
||||
learner_text_raw: str
|
||||
learner_text_masked: str = ""
|
||||
counselor_identity: Optional[str] = None
|
||||
client_identity: Optional[str] = None
|
||||
crisis: Optional[guardrail.CrisisResult] = None
|
||||
state_after: Optional[SessionState] = None
|
||||
messages: list[EngineMessage] = field(default_factory=list)
|
||||
|
|
@ -133,6 +135,7 @@ def prepare_turn(
|
|||
card: PersonaCard,
|
||||
state: SessionState,
|
||||
learner_text: str,
|
||||
learner_identity: Optional[str] = None,
|
||||
memory: Optional[TurnMemory] = None,
|
||||
theory_mode: Optional[str] = None,
|
||||
eval_rapport_signal: Optional[float] = None,
|
||||
|
|
@ -146,23 +149,42 @@ def prepare_turn(
|
|||
state_machine 의 경량 휴리스틱으로 라포 신호를 추정한다.
|
||||
"""
|
||||
turn_memory = memory or TurnMemory()
|
||||
client_identity = card.display_name
|
||||
ctx = TurnContext(
|
||||
session_id=session_id,
|
||||
case_id=case_id,
|
||||
persona=card,
|
||||
state_before=state,
|
||||
learner_text_raw=learner_text,
|
||||
counselor_identity=learner_identity,
|
||||
client_identity=client_identity,
|
||||
memory=TurnMemory(
|
||||
recall_summary=_mask_optional_text(turn_memory.recall_summary),
|
||||
pinned_facts=_mask_text_list(turn_memory.pinned_facts),
|
||||
recent_turns=_mask_recent_turns(turn_memory.recent_turns),
|
||||
recall_summary=_mask_optional_text(
|
||||
turn_memory.recall_summary,
|
||||
counselor_identity=learner_identity,
|
||||
client_identity=client_identity,
|
||||
),
|
||||
pinned_facts=_mask_text_list(
|
||||
turn_memory.pinned_facts,
|
||||
counselor_identity=learner_identity,
|
||||
client_identity=client_identity,
|
||||
),
|
||||
recent_turns=_mask_recent_turns(
|
||||
turn_memory.recent_turns,
|
||||
counselor_identity=learner_identity,
|
||||
client_identity=client_identity,
|
||||
),
|
||||
kb_behavior_cues=list(turn_memory.kb_behavior_cues or []),
|
||||
),
|
||||
theory_mode=theory_mode,
|
||||
)
|
||||
|
||||
# 1) 입력 가드레일 — PII 마스킹 + 위기분류
|
||||
mask = guardrail.mask_pii(learner_text)
|
||||
mask = guardrail.mask_role_identities(
|
||||
learner_text,
|
||||
counselor_identity=learner_identity,
|
||||
client_identity=client_identity,
|
||||
)
|
||||
ctx.learner_text_masked = mask.text_masked
|
||||
ctx.crisis = guardrail.classify_crisis(learner_text, speaker_is_persona_context=True)
|
||||
|
||||
|
|
@ -210,21 +232,51 @@ def prepare_turn(
|
|||
return ctx
|
||||
|
||||
|
||||
def _mask_optional_text(text: Optional[str]) -> Optional[str]:
|
||||
def _mask_optional_text(
|
||||
text: Optional[str],
|
||||
*,
|
||||
counselor_identity: Optional[str] = None,
|
||||
client_identity: Optional[str] = None,
|
||||
) -> Optional[str]:
|
||||
if text is None:
|
||||
return None
|
||||
return guardrail.mask_pii(text).text_masked
|
||||
return guardrail.mask_role_identities(
|
||||
text,
|
||||
counselor_identity=counselor_identity,
|
||||
client_identity=client_identity,
|
||||
).text_masked
|
||||
|
||||
|
||||
def _mask_text_list(values: Optional[list[str]]) -> list[str]:
|
||||
return [guardrail.mask_pii(value).text_masked for value in (values or [])]
|
||||
def _mask_text_list(
|
||||
values: Optional[list[str]],
|
||||
*,
|
||||
counselor_identity: Optional[str] = None,
|
||||
client_identity: Optional[str] = None,
|
||||
) -> list[str]:
|
||||
return [
|
||||
guardrail.mask_role_identities(
|
||||
value,
|
||||
counselor_identity=counselor_identity,
|
||||
client_identity=client_identity,
|
||||
).text_masked
|
||||
for value in (values or [])
|
||||
]
|
||||
|
||||
|
||||
def _mask_recent_turns(turns: Optional[list[dict[str, str]]]) -> list[dict[str, str]]:
|
||||
def _mask_recent_turns(
|
||||
turns: Optional[list[dict[str, str]]],
|
||||
*,
|
||||
counselor_identity: Optional[str] = None,
|
||||
client_identity: Optional[str] = None,
|
||||
) -> list[dict[str, str]]:
|
||||
masked: list[dict[str, str]] = []
|
||||
for turn in turns or []:
|
||||
item = dict(turn)
|
||||
item["text"] = guardrail.mask_pii(str(item.get("text", ""))).text_masked
|
||||
item["text"] = guardrail.mask_role_identities(
|
||||
str(item.get("text", "")),
|
||||
counselor_identity=counselor_identity,
|
||||
client_identity=client_identity,
|
||||
).text_masked
|
||||
masked.append(item)
|
||||
return masked
|
||||
|
||||
|
|
@ -427,6 +479,7 @@ async def run_turn_stream(
|
|||
flagged = False
|
||||
output_error: str | None = None
|
||||
stream_meta: dict[str, Any] = {}
|
||||
gateway_done = False
|
||||
previous_client_reply = _latest_client_reply(ctx.memory.recent_turns)
|
||||
if ctx.crisis is not None and ctx.crisis.escalate:
|
||||
flagged = True
|
||||
|
|
@ -471,6 +524,7 @@ async def run_turn_stream(
|
|||
payload = packet.payload
|
||||
if isinstance(payload, StreamDoneEvent):
|
||||
stream_meta = payload.model_dump()
|
||||
gateway_done = True
|
||||
break
|
||||
|
||||
payload = packet.payload
|
||||
|
|
@ -479,6 +533,12 @@ async def run_turn_stream(
|
|||
text_piece = payload.text
|
||||
accumulated += text_piece
|
||||
|
||||
if not gateway_done:
|
||||
# 토큰 일부 또는 빈 본문 뒤 연결이 끊겨도 성공 done을 합성하지 않는다.
|
||||
# 라우트는 이 error 이벤트를 전달하고 durable turn을 저장하지 않는다.
|
||||
yield StreamEvent("error", {"detail": "client_stream_incomplete"})
|
||||
return
|
||||
|
||||
scenario_leakage = rupture_scenario_director.contains_internal_scenario_leakage(
|
||||
accumulated
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue