from __future__ import annotations import unittest from app.plugins.base import PluginRegistry from app.schemas.chat import ChatRequest, FillSlotsRequest from app.schemas.debug import RoutingDebug from app.schemas.intent import IntentDefinition from app.schemas.workflow import Workflow, WorkflowStep from app.services.agent_service import AgentService from app.services.intent_registry import IntentRegistry from app.services.session_store import InMemorySessionStore from app.services.social import SocialReplyResult, SocialRouter class _FailingRouter: def route(self, text: str): # pragma: no cover - should not be called in these tests raise AssertionError(f"router should not be called for social input: {text}") def extract_slots(self, text: str, intent: IntentDefinition) -> dict[str, object]: _ = (text, intent) return {} class _FakeSocialResponder: def reply(self, text: str, session) -> SocialReplyResult: _ = (text, session) normalized = text.strip() if "你好" in normalized: text = "你好呀,我在,想聊什么都可以。" elif "名字" in normalized or "你是谁" in normalized: text = "我是一名智能座舱助手,你可以直接叫我座舱助手。" elif "天气" in normalized: text = "是啊,今天确实挺舒服的。" else: text = "我在,咱们继续聊。" return SocialReplyResult( text=text, backend="fake-cloud", model_name="fake-social", ) def _intent(intent_id: str, plugin_id: str) -> IntentDefinition: return IntentDefinition( intent_id=intent_id, plugin_id=plugin_id, domain="service" if intent_id.startswith("cs_") else "cabin", risk_level="high" if intent_id == "cs_cancel_order" else "low", required_slots=["order_id"] if intent_id == "cs_cancel_order" else [], ask_templates={"order_id": "请告诉我订单号。"} if intent_id == "cs_cancel_order" else {}, keywords=[], examples=[], ) class SocialChatTests(unittest.TestCase): def setUp(self) -> None: self.session_store = InMemorySessionStore() self.plugins = PluginRegistry() self.plugins.register( "mock.cancel_order", lambda slots: {"success": True, "message": f"已取消订单 {slots.get('order_id', '')}。"}, ) self.service = AgentService( intent_registry=IntentRegistry([_intent("cs_cancel_order", "mock.cancel_order")]), router=_FailingRouter(), plugins=self.plugins, session_store=self.session_store, social_router=SocialRouter(), social_responder=_FakeSocialResponder(), ) def test_greeting_social_reply_uses_social_responder(self) -> None: response = self.service.handle_chat( ChatRequest( session_id="sess_social_hi", user_id="user_1", input_text="你好", ) ) self.assertEqual(response.decision, "open_social") self.assertEqual(response.status, "social") self.assertIn("你好呀", response.reply_text) def test_capability_social_question_does_not_fall_into_business_intent(self) -> None: response = self.service.handle_chat( ChatRequest( session_id="sess_social_name", user_id="user_1", input_text="你叫什么名字", ) ) self.assertEqual(response.decision, "open_social") self.assertEqual(response.status, "social") self.assertNotEqual(response.reply_type, "ask_slot") self.assertIn("智能座舱助手", response.reply_text) def test_open_social_reply_uses_social_responder(self) -> None: response = self.service.handle_chat( ChatRequest( session_id="sess_social_open", user_id="user_1", input_text="今天天气真不错啊", ) ) self.assertEqual(response.decision, "open_social") self.assertEqual(response.status, "social") self.assertIn("挺舒服", response.reply_text) def test_social_turn_does_not_break_waiting_confirmation(self) -> None: session = self.session_store.get_or_create("sess_confirm", "user_1") session.current_intent = "cs_cancel_order" session.status = "waiting_confirmation" session.pending_slots = ["confirmation"] session.slots = {"order_id": "A123456"} session.routing_debug = RoutingDebug(selected_intent="cs_cancel_order", decision="execute").model_dump() session.workflow = Workflow( workflow_id="wf_sess_confirm", workflow_type="conditional", domain="service", intent_id="cs_cancel_order", status="waiting_confirmation", risk_level="high", slots={"order_id": "A123456"}, steps=[ WorkflowStep( step=1, step_id="step_confirm", intent_id="cs_cancel_order", plugin_id="mock.cancel_order", action="cancel_order", status="waiting_confirmation", slots={"order_id": "A123456"}, requires_confirmation=True, ) ], meta={ "pending_confirmation": { "step_id": "step_confirm", "intent_id": "cs_cancel_order", "detail": "确认取消订单 A123456", }, "confirmed_steps": [], "step_results": {}, }, ).model_dump() self.session_store.save(session) social_response = self.service.handle_fill_slots( FillSlotsRequest( session_id="sess_confirm", user_id="user_1", input_text="今天天气真不错啊", ) ) self.assertEqual(social_response.decision, "open_social") self.assertEqual(social_response.status, "waiting_confirmation") self.assertEqual(social_response.pending_slots, ["confirmation"]) self.assertIn("回复“确认”或“取消”即可", social_response.reply_text) confirm_response = self.service.handle_fill_slots( FillSlotsRequest( session_id="sess_confirm", user_id="user_1", input_text="确认", ) ) self.assertEqual(confirm_response.reply_type, "workflow_result") self.assertEqual(confirm_response.status, "completed") self.assertIn("已取消订单", confirm_response.reply_text) if __name__ == "__main__": unittest.main()