from __future__ import annotations from app.schemas.pmflow_ai_turn_runtime import PMFlowAITurnState from app.schemas.rag import RAGAskRequest from app.services.pmflow_ai_turn_runtime_service import PMFlowAITurnRuntimeService from app.services.pmflow_ai_turn_state_reviser import PMFlowAITurnStateReviser def test_packet_e_goal_frames_drive_working_state_without_legacy_decision_fields() -> None: state = PMFlowAITurnRuntimeService(object(), tenant_id=1).build_initial_turn_state( request=RAGAskRequest(question="现在最新的 Agent 应用机会是什么"), route_plan={"legacy_policy": "internal_only"}, turn_hypothesis={ "normalized_goal": "判断当前最新 Agent 应用机会", "desired_outcome": "给出机会、风险和下一步", "goal_frames": [ { "frame_id": "agent-opportunity", "operation_kind": "judge_direction", "target_scope": { "scope_type": "external_public", "binding_strength": "surface", "source": "turn_goal_assessment", }, "target_object_requirement": "none", "answer_shape": "strategic_judgement", "confidence": 0.82, } ], "operation_gaps": [ { "key": "fresh_public_market_evidence", "gap_type": "freshness_gap", "blocking": False, "user_actionable": False, "system_action_hint": "external_search", "reason": "需要当前公开市场证据。", } ], "can_answer_with_assumptions": True, "primary_object": {"binding_strength": "none"}, }, ) decision = PMFlowAITurnRuntimeService(object(), tenant_id=1).assess_sufficiency(state) assert state.selected_frame is not None assert state.selected_frame.operation_kind == "judge_direction" assert state.answer_state.answer_shape == "strategic_judgement" assert [gap.key for gap in state.operation_gaps] == ["fresh_public_market_evidence"] assert decision.next_action_type == "external_search" def test_packet_e_goal_frames_beat_conflicting_legacy_fields() -> None: state = PMFlowAITurnRuntimeService(object(), tenant_id=1).build_initial_turn_state( request=RAGAskRequest(question="暂存想法里有没有值得推进的"), route_plan={}, turn_hypothesis={ "normalized_goal": "列出暂存想法", "answer_shape": "status_answer", "decision_type": "project_status", "evidence_needs": ["project_status"], "goal_frames": [ { "frame_id": "saved-ideas", "operation_kind": "list_collection", "target_scope": { "scope_type": "current_space", "binding_strength": "surface", "source": "turn_goal_assessment", }, "target_collection": { "object_type": "saved_idea", "required": True, }, "target_object_requirement": "none", "answer_shape": "fact_list", "confidence": 0.86, } ], "operation_gaps": [ { "key": "saved_ideas", "gap_type": "system_evidence_gap", "system_action_hint": "inspect_saved_ideas", "reason": "需要读取暂存想法集合。", } ], "can_answer_with_assumptions": True, }, ) action = PMFlowAITurnRuntimeService(object(), tenant_id=1).select_next_minimal_action(state) assert state.selected_frame is not None assert state.selected_frame.operation_kind == "list_collection" assert state.answer_state.answer_shape == "fact_list" assert action is not None assert action.action_type == "inspect_saved_ideas" def test_packet_e_user_actionable_operation_gap_controls_clarification_gate() -> None: state = PMFlowAITurnRuntimeService(object(), tenant_id=1).build_initial_turn_state( request=RAGAskRequest(question="帮我处理一下"), route_plan={}, turn_hypothesis={ "normalized_goal": "确认执行动作边界", "goal_frames": [ { "frame_id": "execution-boundary", "operation_kind": "guide_execution", "target_scope": { "scope_type": "current_space", "binding_strength": "none", "source": "turn_goal_assessment", }, "target_object_requirement": "authorization", "answer_shape": "execution_guidance", "confidence": 0.7, } ], "operation_gaps": [ { "key": "execution_boundary_required", "gap_type": "user_clarification_gap", "blocking": True, "user_actionable": True, "customer_question": "请补充要处理的对象和动作边界。", "reason": "执行前缺少对象和动作边界。", } ], "can_answer_with_assumptions": False, }, ) decision = PMFlowAITurnRuntimeService(object(), tenant_id=1).assess_sufficiency(state) assert decision.must_ask_user is True assert decision.next_action_type == "ask_clarification" assert state.operation_gaps[0].user_actionable is True def test_packet_e_reviser_accepts_direct_goal_frame_payload() -> None: state = PMFlowAITurnState() PMFlowAITurnStateReviser().initialize_working_state( turn_state=state, hypothesis={ "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", "answer_shape": "general_answer", "confidence": 0.9, } ], "operation_gaps": [], "decision_type": "project_status", "evidence_needs": ["project_status"], }, ) assert state.selected_frame is not None assert state.selected_frame.operation_kind == "explain_assistant" assert state.operation_gaps == []