import inspect from app.schemas.rag import RAGAskRequest from app.services.pmflow_ai_answer_verifier_registry import PMFlowAIAnswerVerifierRegistry from app.services.pmflow_ai_static_contract_registry import PMFlowAIStaticContractRegistry from app.services.pmflow_ai_turn_runtime_service import PMFlowAITurnRuntimeService def _service() -> PMFlowAITurnRuntimeService: return PMFlowAITurnRuntimeService(object(), tenant_id=1) def _assistant_identity_state(): return _service().build_initial_turn_state( request=RAGAskRequest(question="说明你的能力边界"), route_plan={}, turn_hypothesis={ "normalized_goal": "assistant_identity", "desired_outcome": "说明助手身份和能力边界", "answer_shape": "general_answer", "goal_frames": [ { "frame_id": "assistant-profile", "operation_kind": "explain_assistant", "target_scope": { "scope_type": "current_space", "binding_strength": "surface", "source": "turn_goal_assessment", }, "target_object_requirement": "none", "source_policy": "none", "answer_shape": "general_answer", "confidence": 0.9, } ], "operation_gaps": [], }, ) def test_packet_ah_static_contract_verifier_rejects_mismatched_final_answer() -> None: state = _assistant_identity_state() payload = { "_runtime_payload_kind": "final_answer", "answer": "我还没有拿到足够证据来做这轮判断。", "customer_evidence_packet": {"source_summary": []}, } failure = PMFlowAIAnswerVerifierRegistry().verify_payload(turn_state=state, payload=payload) assert failure is not None assert failure.failure_type == "static_contract_mismatch" assert failure.next_action_hint == "synthesize_answer" assert failure.state_patch["needs_internal_evidence"] is False def test_packet_ah_static_contract_verifier_accepts_registered_contract_payload() -> None: state = _assistant_identity_state() contract = PMFlowAIStaticContractRegistry().resolve_for_turn_state(state) assert contract is not None payload = { "_runtime_payload_kind": "final_answer", "answer": "我是 PMFlow AI,是 PMFlow 里的项目操作副驾。", "customer_evidence_packet": {"source_summary": list(contract.source_summary)}, "answer_runtime_packet": { "objective": {"evidence_to_use": list(contract.source_summary)}, "plan": {"draft_sources": ["产品能力或披露边界合同"]}, "draft": {"source_renderer": "产品能力或披露边界合同"}, }, } failure = _service()._verify_runtime_payload(turn_state=state, payload=payload) assert failure is None def test_packet_ah_runtime_payload_verifier_delegates_to_registry() -> None: source = inspect.getsource(PMFlowAITurnRuntimeService._verify_runtime_payload) assert "PMFlowAIAnswerVerifierRegistry" in source assert "wrong_source_or_missing_project_operating_state" not in source assert "goal_fulfillment_missing_project_operating_facets" not in source assert "interaction_not_user_actionable" not in source