세션 계약과 메모리 경계 보강
This commit is contained in:
parent
391639c1de
commit
2bb052f624
12 changed files with 836 additions and 116 deletions
|
|
@ -45,6 +45,90 @@ class SessionMemoryPureTest(unittest.TestCase):
|
|||
self.assertIn("[ORG]", digest)
|
||||
self.assertNotIn("김서연", digest)
|
||||
|
||||
def test_session_digest_input_keeps_only_client_visible_masked_turns(self) -> None:
|
||||
digest_input = memory.build_session_digest_input(
|
||||
session_id="00000000-0000-0000-0000-00000000feed",
|
||||
case_id="00000000-0000-0000-0000-00000000ca5e",
|
||||
session_no=4,
|
||||
masked_turns=[
|
||||
{
|
||||
"speaker": "client",
|
||||
"text": "raw 김서연",
|
||||
"text_masked": "저는 [NAME]입니다.",
|
||||
"turn_id": "00000000-0000-0000-0000-000000000201",
|
||||
"visible_to": ["client", "evaluator"],
|
||||
},
|
||||
{
|
||||
"speaker": "client",
|
||||
"text": "평가자 전용 raw",
|
||||
"text_masked": "평가자 전용 masked",
|
||||
"visible_to": ["evaluator"],
|
||||
},
|
||||
{
|
||||
"speaker": "system",
|
||||
"text": "시스템 메모",
|
||||
"visible_to": ["client"],
|
||||
},
|
||||
],
|
||||
open_threads=[" 가족 이야기 이어가기 ", ""],
|
||||
)
|
||||
|
||||
self.assertEqual(digest_input.session_no, 4)
|
||||
self.assertEqual(len(digest_input.masked_turns), 1)
|
||||
self.assertEqual(digest_input.masked_turns[0].speaker, "client")
|
||||
self.assertEqual(digest_input.masked_turns[0].text, "저는 [NAME]입니다.")
|
||||
self.assertEqual(
|
||||
digest_input.masked_turns[0].turn_id,
|
||||
"00000000-0000-0000-0000-000000000201",
|
||||
)
|
||||
self.assertEqual(digest_input.open_threads, ("가족 이야기 이어가기",))
|
||||
self.assertNotIn("김서연", " ".join(turn.text for turn in digest_input.masked_turns))
|
||||
|
||||
def test_compression_messages_use_digest_contract_without_end_state(self) -> None:
|
||||
digest_input = memory.build_session_digest_input(
|
||||
session_id="00000000-0000-0000-0000-00000000feed",
|
||||
case_id="00000000-0000-0000-0000-00000000ca5e",
|
||||
session_no=5,
|
||||
masked_turns=[
|
||||
{"speaker": "client", "text": "저는 [NAME]입니다.", "visible_to": ["client"]},
|
||||
{"speaker": "counselor", "text": "그때 마음을 더 말해볼까요?", "visible_to": ["client"]},
|
||||
],
|
||||
open_threads=["다음 회기에 가족 이야기를 이어가기"],
|
||||
)
|
||||
messages = memory.build_compression_messages(memory.CompressionJob(digest_input=digest_input))
|
||||
prompt = "\n".join(message["content"] for message in messages)
|
||||
|
||||
self.assertIn("저는 [NAME]입니다.", prompt)
|
||||
self.assertIn("다음 회기에 가족 이야기를 이어가기", prompt)
|
||||
self.assertNotIn("김서연", prompt)
|
||||
self.assertNotIn("종료 상태", prompt)
|
||||
self.assertNotIn("rapport_credit", prompt)
|
||||
self.assertNotIn("evaluation", prompt)
|
||||
|
||||
def test_fallback_digest_result_uses_shared_digest_contract(self) -> None:
|
||||
digest_input = memory.build_session_digest_input(
|
||||
session_id="00000000-0000-0000-0000-00000000feed",
|
||||
case_id="00000000-0000-0000-0000-00000000ca5e",
|
||||
session_no=6,
|
||||
masked_turns=[
|
||||
{"speaker": "client", "text": "저는 [NAME]입니다.", "visible_to": ["client"]},
|
||||
],
|
||||
open_threads=["가족 이야기"],
|
||||
)
|
||||
result = memory.build_fallback_digest_result(
|
||||
digest_input,
|
||||
end_state={"stage": "탐색", "effective_openness": 0.4, "rapport_credit": 0.3},
|
||||
)
|
||||
|
||||
self.assertEqual(result.source, "fallback")
|
||||
self.assertEqual(result.session_id, "00000000-0000-0000-0000-00000000feed")
|
||||
self.assertEqual(result.case_id, "00000000-0000-0000-0000-00000000ca5e")
|
||||
self.assertEqual(result.session_no, 6)
|
||||
self.assertEqual(result.open_threads, ("가족 이야기",))
|
||||
self.assertIn("S6:", result.digest)
|
||||
self.assertIn("[NAME]", result.digest)
|
||||
self.assertNotIn("김서연", result.digest)
|
||||
|
||||
def test_extract_pinned_fact_candidates_is_conservative_and_masked(self) -> None:
|
||||
facts = memory.extract_pinned_fact_candidates(
|
||||
[
|
||||
|
|
@ -363,13 +447,35 @@ class SessionMemoryPersistenceTest(unittest.IsolatedAsyncioTestCase):
|
|||
persisted = await session_persistence.end_session(sess, carry)
|
||||
|
||||
self.assertTrue(persisted)
|
||||
summary_writes = [
|
||||
args for query, args in conn.executed if "INSERT INTO app.session_summary" in query
|
||||
]
|
||||
self.assertEqual(len(summary_writes), 1)
|
||||
(
|
||||
summary_session_id,
|
||||
summary_case_id,
|
||||
summary_session_no,
|
||||
summary_end_state,
|
||||
summary_rapport_delta,
|
||||
summary_digest,
|
||||
summary_open_threads,
|
||||
) = summary_writes[0]
|
||||
self.assertEqual(summary_session_id, sess.session_id)
|
||||
self.assertEqual(summary_case_id, case_id)
|
||||
self.assertEqual(summary_session_no, 2)
|
||||
self.assertEqual(summary_end_state, carry.end_state)
|
||||
self.assertEqual(summary_rapport_delta, 0.21)
|
||||
self.assertTrue(summary_digest.startswith("S2:"))
|
||||
self.assertIn("[NAME]", summary_digest)
|
||||
self.assertNotIn("김서연", summary_digest)
|
||||
self.assertEqual(summary_open_threads, [])
|
||||
case_updates = [
|
||||
args for query, args in conn.executed if "UPDATE app.case_profile" in query
|
||||
]
|
||||
self.assertEqual(len(case_updates), 1)
|
||||
_, _, case_digest, trajectory, alliance_level = case_updates[0]
|
||||
self.assertIn("S1: 이전 회기", case_digest)
|
||||
self.assertIn("S2:", case_digest)
|
||||
self.assertIn(summary_digest, case_digest)
|
||||
self.assertIn("[NAME]", case_digest)
|
||||
self.assertNotIn("김서연", case_digest)
|
||||
self.assertEqual(trajectory[-1]["session_no"], 2)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue