118 lines
4.3 KiB
Python
118 lines
4.3 KiB
Python
import time
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from app.schemas.rag import RAGAskRequest
|
|
from app.services.customer_answer_presentation_service import CustomerAnswerPresentationService
|
|
from app.services.pmflow_ai_turn_runtime_service import PMFlowAITurnRuntimeService
|
|
|
|
|
|
def _service() -> PMFlowAITurnRuntimeService:
|
|
return PMFlowAITurnRuntimeService(object(), tenant_id=1)
|
|
|
|
|
|
def _assistant_state():
|
|
return _service().build_initial_turn_state(
|
|
request=RAGAskRequest(question="who are you"),
|
|
route_plan={},
|
|
turn_hypothesis={
|
|
"normalized_goal": "assistant_identity",
|
|
"desired_outcome": "说明 PMFlow AI 身份和能力边界",
|
|
"answer_shape": "general_answer",
|
|
"goal_frames": [
|
|
{
|
|
"frame_id": "assistant-profile",
|
|
"operation_kind": "explain_assistant",
|
|
"target_scope": {"scope_type": "current_space", "binding_strength": "surface"},
|
|
"target_object_requirement": "none",
|
|
"source_policy": "none",
|
|
"answer_shape": "general_answer",
|
|
"confidence": 0.9,
|
|
}
|
|
],
|
|
"operation_gaps": [],
|
|
},
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_packet_ao_quality_gate_rewrites_internal_runtime_leak(monkeypatch) -> None:
|
|
service = _service()
|
|
state = _assistant_state()
|
|
|
|
async def bad_render(*, request, turn_state):
|
|
_ = request, turn_state
|
|
return "problem_frame: assistant_identity; evidence_findings: static contract"
|
|
|
|
monkeypatch.setattr(service, "_render_answer_from_registry", bad_render)
|
|
|
|
payload = await service._run_state_answer_synthesis(
|
|
request=RAGAskRequest(question="who are you"),
|
|
current_user=SimpleNamespace(id=1, tenant_id=1),
|
|
route_plan={},
|
|
turn_state=state,
|
|
presentation_service=CustomerAnswerPresentationService(object(), tenant_id=1),
|
|
started_at=time.perf_counter(),
|
|
)
|
|
|
|
finding_types = {item["finding_type"] for item in payload["answer_runtime_packet"]["quality_findings"]}
|
|
assert "internal_runtime_leak" in finding_types
|
|
assert "problem_frame" not in payload["answer"]
|
|
assert "evidence_findings" not in payload["answer"]
|
|
assert "PMFlow AI" in payload["answer"]
|
|
assert payload["answer_runtime_packet"]["draft"]["draft_id"] == "draft_quality_checked"
|
|
assert any(step.key == "revise_answer_quality" for step in state.runtime_steps)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_packet_ao_quality_gate_rewrites_plan_echo(monkeypatch) -> None:
|
|
service = _service()
|
|
state = _assistant_state()
|
|
|
|
async def bad_render(*, request, turn_state):
|
|
_ = request
|
|
return turn_state.answer_plan.answer_strategy
|
|
|
|
monkeypatch.setattr(service, "_render_answer_from_registry", bad_render)
|
|
|
|
payload = await service._run_state_answer_synthesis(
|
|
request=RAGAskRequest(question="who are you"),
|
|
current_user=SimpleNamespace(id=1, tenant_id=1),
|
|
route_plan={},
|
|
turn_state=state,
|
|
presentation_service=CustomerAnswerPresentationService(object(), tenant_id=1),
|
|
started_at=time.perf_counter(),
|
|
)
|
|
|
|
finding_types = {item["finding_type"] for item in payload["answer_runtime_packet"]["quality_findings"]}
|
|
assert "plan_echo" in finding_types
|
|
assert state.answer_plan.answer_strategy not in payload["answer"]
|
|
assert "项目操作副驾" in payload["answer"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_packet_ao_quality_gate_rewrites_empty_draft(monkeypatch) -> None:
|
|
service = _service()
|
|
state = _assistant_state()
|
|
|
|
async def bad_render(*, request, turn_state):
|
|
_ = request, turn_state
|
|
return ""
|
|
|
|
monkeypatch.setattr(service, "_render_answer_from_registry", bad_render)
|
|
|
|
payload = await service._run_state_answer_synthesis(
|
|
request=RAGAskRequest(question="who are you"),
|
|
current_user=SimpleNamespace(id=1, tenant_id=1),
|
|
route_plan={},
|
|
turn_state=state,
|
|
presentation_service=CustomerAnswerPresentationService(object(), tenant_id=1),
|
|
started_at=time.perf_counter(),
|
|
)
|
|
|
|
finding_types = {item["finding_type"] for item in payload["answer_runtime_packet"]["quality_findings"]}
|
|
assert "empty_answer" in finding_types
|
|
assert payload["answer"]
|
|
assert "PMFlow AI" in payload["answer"]
|