한신대 피드백 개선팩 반영
This commit is contained in:
parent
5a9c110c11
commit
6b6241f468
25 changed files with 1247 additions and 94 deletions
|
|
@ -92,9 +92,11 @@ class CaptureGenerateEngine:
|
|||
self.payload: dict[str, Any] | None = None
|
||||
self._payload_builder = EngineClient(base_url="http://engine.test")
|
||||
self.text = text
|
||||
self.requests: list[Any] = []
|
||||
|
||||
async def generate(self, req):
|
||||
self.request = req
|
||||
self.requests.append(req)
|
||||
self.payload = self._payload_builder._payload(req)
|
||||
return GenerateResponse(
|
||||
text=self.text,
|
||||
|
|
@ -283,6 +285,26 @@ class OrchestratorMaskingGateTest(unittest.IsolatedAsyncioTestCase):
|
|||
self.assertIn("자동적 사고", blob)
|
||||
self.assertIn("행동활성화", blob)
|
||||
|
||||
def test_prepare_turn_maps_recent_turns_from_client_ai_perspective(self) -> None:
|
||||
ctx = orchestrator.prepare_turn(
|
||||
session_id="history-session",
|
||||
case_id="history-case",
|
||||
card=persona.P1,
|
||||
state=_initial_state(),
|
||||
learner_text="그 말을 듣고 어떤 생각이 들었나요?",
|
||||
memory=orchestrator.TurnMemory(
|
||||
recent_turns=[
|
||||
{"speaker": "counselor", "text": "왜 상담에 오게 됐나요?"},
|
||||
{"speaker": "client", "text": "엄마가 가보라고 해서요."},
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
recent = ctx.messages[-3:-1]
|
||||
self.assertEqual([message.role for message in recent], ["user", "assistant"])
|
||||
self.assertEqual(recent[0].content, "왜 상담에 오게 됐나요?")
|
||||
self.assertEqual(recent[1].content, "엄마가 가보라고 해서요.")
|
||||
|
||||
async def test_run_turn_generate_sends_only_masked_engine_payload(self) -> None:
|
||||
ctx = _prepare_context()
|
||||
engine = CaptureGenerateEngine()
|
||||
|
|
@ -395,6 +417,78 @@ class OrchestratorMaskingGateTest(unittest.IsolatedAsyncioTestCase):
|
|||
self.assertNotIn("[NAME]", result.client_reply or "")
|
||||
self.assertIn("그 이름은 그대로고", result.client_reply or "")
|
||||
|
||||
async def test_run_turn_generate_retries_once_after_role_meta_reply(self) -> None:
|
||||
class SequenceEngine(CaptureGenerateEngine):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("")
|
||||
self.responses = [
|
||||
"내담자 역할로 응답하겠습니다. 엄마가 가보라고 해서요.",
|
||||
"엄마가 그냥 가보라고 해서 왔어요.",
|
||||
]
|
||||
|
||||
async def generate(self, req):
|
||||
self.request = req
|
||||
self.requests.append(req)
|
||||
self.payload = self._payload_builder._payload(req)
|
||||
text = self.responses.pop(0)
|
||||
return GenerateResponse(
|
||||
text=text,
|
||||
model="fake-model",
|
||||
provider="fake-provider",
|
||||
tokens_in=3,
|
||||
tokens_out=4,
|
||||
cost_usd=0.0,
|
||||
)
|
||||
|
||||
ctx = orchestrator.prepare_turn(
|
||||
session_id="quality-session",
|
||||
case_id="quality-case",
|
||||
card=persona.P1,
|
||||
state=_initial_state(),
|
||||
learner_text="어머니가 오라고 하셨군요. 지금은 어떤 마음인가요?",
|
||||
)
|
||||
engine = SequenceEngine()
|
||||
|
||||
result = await orchestrator.run_turn_generate(ctx, engine) # type: ignore[arg-type]
|
||||
|
||||
self.assertEqual(len(engine.requests), 2)
|
||||
self.assertEqual(result.client_reply, "엄마가 그냥 가보라고 해서 왔어요.")
|
||||
self.assertFalse(result.safety_flagged)
|
||||
|
||||
async def test_run_turn_generate_returns_retryable_error_without_saving_bad_fallback(self) -> None:
|
||||
class AlwaysBadEngine(CaptureGenerateEngine):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("")
|
||||
|
||||
async def generate(self, req):
|
||||
self.request = req
|
||||
self.requests.append(req)
|
||||
self.payload = self._payload_builder._payload(req)
|
||||
return GenerateResponse(
|
||||
text="AI로서 내담자 역할로 응답하겠습니다.",
|
||||
model="fake-model",
|
||||
provider="fake-provider",
|
||||
tokens_in=3,
|
||||
tokens_out=4,
|
||||
cost_usd=0.0,
|
||||
)
|
||||
|
||||
ctx = orchestrator.prepare_turn(
|
||||
session_id="quality-session",
|
||||
case_id="quality-case",
|
||||
card=persona.P1,
|
||||
state=_initial_state(),
|
||||
learner_text="지금 이 자리에서 가장 말하기 어려운 게 뭔가요?",
|
||||
)
|
||||
engine = AlwaysBadEngine()
|
||||
|
||||
result = await orchestrator.run_turn_generate(ctx, engine) # type: ignore[arg-type]
|
||||
|
||||
self.assertEqual(len(engine.requests), 2)
|
||||
self.assertIsNone(result.client_reply)
|
||||
self.assertTrue(result.safety_flagged)
|
||||
self.assertEqual(getattr(result, "output_error", None), "client_reply_quality_retryable")
|
||||
|
||||
async def test_run_turn_stream_humanizes_split_masked_placeholder_reply(self) -> None:
|
||||
ctx = orchestrator.prepare_turn(
|
||||
session_id="masking-session",
|
||||
|
|
@ -422,6 +516,38 @@ class OrchestratorMaskingGateTest(unittest.IsolatedAsyncioTestCase):
|
|||
self.assertIn("그 이름은 그대로고요.", streamed)
|
||||
self.assertEqual(events[-1].event, "done")
|
||||
|
||||
async def test_run_turn_stream_buffers_role_meta_reply_without_token_leak(self) -> None:
|
||||
ctx = orchestrator.prepare_turn(
|
||||
session_id="quality-stream-session",
|
||||
case_id="quality-stream-case",
|
||||
card=persona.P1,
|
||||
state=_initial_state(),
|
||||
learner_text="어머니가 오라고 하셨군요. 지금은 어떤 마음인가요?",
|
||||
)
|
||||
engine = CaptureStreamEngine(
|
||||
chunks=[
|
||||
"내담자 ",
|
||||
"역할로 응답하겠습니다. 엄마가 가보라고 해서요.",
|
||||
]
|
||||
)
|
||||
|
||||
events = [
|
||||
event
|
||||
async for event in orchestrator.run_turn_stream(
|
||||
ctx,
|
||||
engine, # type: ignore[arg-type]
|
||||
)
|
||||
]
|
||||
|
||||
self.assertEqual([event.event for event in events], ["safety", "done"])
|
||||
self.assertEqual(
|
||||
"".join(str(event.data.get("text", "")) for event in events if event.event == "token"),
|
||||
"",
|
||||
)
|
||||
self.assertEqual(events[0].data["reason"], "client_reply_quality_retryable")
|
||||
self.assertTrue(events[-1].data["safety_flagged"])
|
||||
self.assertEqual(events[-1].data["output_error"], "client_reply_quality_retryable")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue