Files
meida_backend/tests/unit/test_report_profile.py
xsl 7bcbb8d40b
CI / lint / typecheck / import-linter / tests / secrets (push) Canceled after 0s
feat(agent,report): 会话持久化与画像注入、报告反馈与预约留资——PM/开发双Agent迭代1-3
- agent: ag_conversation/ag_message 表(迁移0007),上下文组装注入最近报告画像摘要
  (report.profile.latest_profile_summary 经 apps 层闭包注入,同层不互调),历史带最近
  AGENT_HISTORY_TURNS=10 轮;POST/GET /agent/conversations 与消息历史接口
- agent 修复:ChatRequest.conversation_id 改 str(雪花ID超2^53,JS Number丢精度);
  _build_context 挪进 reserve commit 后同一 try,异常走 refund 兜底
- report: rp_feedback(维度反馈 upsert,认同率/修正率 SQL 可算,T-P3-21 最小版——
  L1 记忆未建,不回写结论)+ rp_reservation(预约留资幂等,T-P3-22,预约率口径
  行数/full_report.card_view 去重曝光,门槛≥8%),迁移0008
- 测试:全量 606 passed;ruff/mypy/lint-imports 全绿;E2E 真实 deepseek-v4-flash 验证
  画像注入与历史携带
2026-09-11 21:50:58 +08:00

84 lines
3.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""report.profile.latest_profile_summary:repo 打桩,验证摘要选取纪律。"""
from __future__ import annotations
import json
from types import SimpleNamespace
from typing import Any
import pytest
from packages.report import repo
from packages.report.profile import latest_profile_summary
_DIAGNOSIS = {
"dimensions": [
{
"key": "body_shape",
"label": "体型",
"status": "ok",
"value": "梨形",
"confidence": 0.9,
"evidence": ["问卷自报肩窄胯宽"],
},
{"key": "style_impression", "status": "skipped", "value": None},
],
"rule_hits": [
{"rule_key": "pear_lower_dark", "dimension": "body_shape", "suggestion": "下装选深色"}
],
}
def _report(report_id: int, *, status: str, result: dict[str, Any]) -> Any:
return SimpleNamespace(report_id=report_id, status=status, result=result)
@pytest.fixture
def stub_list(monkeypatch: pytest.MonkeyPatch) -> dict[str, Any]:
state: dict[str, Any] = {"reports": []}
async def fake_list(session: Any, *, user_id: int, limit: int = 20) -> list[Any]:
state["limit"] = limit
return state["reports"]
monkeypatch.setattr(repo, "list_reports", fake_list)
return state
class TestLatestProfileSummary:
async def test_no_reports_returns_none(self, stub_list: dict[str, Any]) -> None:
assert await latest_profile_summary(None, user_id=1, max_chars=800) is None # type: ignore[arg-type]
async def test_succeeded_report_with_diagnosis(self, stub_list: dict[str, Any]) -> None:
stub_list["reports"] = [_report(1, status="succeeded", result={"diagnosis": _DIAGNOSIS})]
summary = await latest_profile_summary(None, user_id=1, max_chars=800) # type: ignore[arg-type]
assert summary is not None
payload = json.loads(summary)
values = {d["key"]: d["value"] for d in payload["dimensions"]}
assert values["body_shape"] == "梨形"
# skipped 维度不进素材(防幻觉第一道闸)
assert "style_impression" not in values
assert payload["rule_hits"][0]["rule_key"] == "pear_lower_dark"
async def test_latest_failed_falls_back_to_earlier_succeeded(
self, stub_list: dict[str, Any]
) -> None:
stub_list["reports"] = [
_report(2, status="failed", result={}),
_report(1, status="succeeded", result={"diagnosis": _DIAGNOSIS}),
]
summary = await latest_profile_summary(None, user_id=1, max_chars=800) # type: ignore[arg-type]
assert summary is not None and "梨形" in summary
async def test_succeeded_without_diagnosis_is_skipped(self, stub_list: dict[str, Any]) -> None:
stub_list["reports"] = [_report(1, status="succeeded", result={"vision": {}})]
assert await latest_profile_summary(None, user_id=1, max_chars=800) is None # type: ignore[arg-type]
async def test_budget_is_forwarded(self, stub_list: dict[str, Any]) -> None:
"""max_chars 收紧时低优先级维度被裁、硬结论保留(复用 copy 的裁剪纪律)。"""
stub_list["reports"] = [_report(1, status="succeeded", result={"diagnosis": _DIAGNOSIS})]
summary = await latest_profile_summary(None, user_id=1, max_chars=60) # type: ignore[arg-type]
assert summary is not None
assert len(summary) > 60 # 硬结论永不裁,载荷可超预算(copy.py 既定语义)
assert "梨形" in summary