Files
life-echo/api/tests/test_story_route_payload.py
yangshilin e1341c6d18 feat:
1. 建立问题库大纲,对应每个人生阶段槽位
2. 鼓励使用更生活化的交流语言共情与总结
3. 降低评审模型可能发生截断的概率
4. 成稿质量维度强化情感表达和上下文连贯性
2026-04-09 15:32:35 +08:00

161 lines
4.6 KiB
Python
Raw 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.
"""Story 路由候选 JSON排序、summary 优先、预算降级。"""
from __future__ import annotations
import json
from datetime import datetime, timezone
from types import SimpleNamespace
from app.agents.memoir.story_route_payload import (
build_route_candidate_json,
build_route_candidate_rows,
sort_stories_for_route,
_truncate_body_for_route,
)
from app.core.config import Settings
def _story(**kwargs):
defaults = dict(
id="s-default",
title="T",
summary=None,
canonical_markdown="",
updated_at=None,
chapter_links=[],
)
defaults.update(kwargs)
return SimpleNamespace(**defaults)
def test_sort_has_summary_first_then_recency():
older = _story(
id="old",
summary="x" * 40,
updated_at=datetime(2020, 1, 1, tzinfo=timezone.utc),
)
newer = _story(
id="new",
summary="",
canonical_markdown="body",
updated_at=datetime(2025, 1, 1, tzinfo=timezone.utc),
)
meta = {
"old": {"char_count": 10, "version_count": 1},
"new": {"char_count": 20, "version_count": 2},
}
out = sort_stories_for_route([newer, older], meta, summary_min_chars=30)
assert [s.id for s in out] == ["old", "new"]
def test_sort_tiebreak_version_then_char_then_id():
t = datetime(2024, 6, 1, tzinfo=timezone.utc)
a = _story(id="a", summary="", canonical_markdown="a", updated_at=t)
b = _story(id="b", summary="", canonical_markdown="bb", updated_at=t)
meta = {
"a": {"char_count": 100, "version_count": 1},
"b": {"char_count": 50, "version_count": 3},
}
out = sort_stories_for_route([a, b], meta, summary_min_chars=30)
assert [s.id for s in out] == ["b", "a"]
def test_summary_sufficient_omits_body():
s = _story(
id="1",
summary="" * 40,
canonical_markdown="正文" * 500,
)
settings = Settings()
rows = build_route_candidate_rows(
[s], {"1": {"char_count": 10, "version_count": 1}}, settings
)
assert "summary" in rows[0]
assert "body_for_route" not in rows[0]
def test_short_summary_falls_back_to_body():
s = _story(
id="1",
summary="",
canonical_markdown="唯一的正文用于路由",
)
settings = Settings()
rows = build_route_candidate_rows(
[s], {"1": {"char_count": 20, "version_count": 1}}, settings
)
assert "summary" not in rows[0]
assert rows[0].get("body_for_route")
def test_long_body_uses_head_tail():
md = "" * 3000
out = _truncate_body_for_route(
md,
body_max_chars=1600,
head_chars=100,
tail_chars=100,
)
assert "中间省略" in out
assert len(out) < len(md)
def test_total_budget_downgrades_tail_rows(monkeypatch):
settings = Settings()
monkeypatch.setattr(settings, "story_route_candidate_total_max_chars", 800)
monkeypatch.setattr(settings, "story_route_index_preview_chars", 40)
stories = [
_story(
id="1",
summary="",
canonical_markdown="A" * 400,
updated_at=datetime(2025, 1, 2, tzinfo=timezone.utc),
),
_story(
id="2",
summary="",
canonical_markdown="B" * 400,
updated_at=datetime(2025, 1, 1, tzinfo=timezone.utc),
),
]
meta = {
"1": {"char_count": 400, "version_count": 1},
"2": {"char_count": 400, "version_count": 1},
}
payload = build_route_candidate_json(stories, meta, settings)
data = json.loads(payload)
assert any("preview" in row and "body_for_route" not in row for row in data)
def test_opening_snippet_when_no_summary_but_body():
s = _story(
id="1",
summary="",
canonical_markdown="我在山东潍坊长大,小时候常和伙伴在河边玩。" * 2,
)
settings = Settings()
rows = build_route_candidate_rows(
[s], {"1": {"char_count": 80, "version_count": 1}}, settings
)
assert "opening_snippet" in rows[0]
assert "潍坊" in rows[0]["opening_snippet"]
def test_json_includes_core_fields():
s = _story(
id="x1",
title="标题",
summary="y" * 40,
updated_at=datetime(2024, 1, 1, tzinfo=timezone.utc),
)
settings = Settings()
js = build_route_candidate_json(
[s], {"x1": {"char_count": 5, "version_count": 2}}, settings
)
row = json.loads(js)[0]
assert row["id"] == "x1"
assert row["title"] == "标题"
assert row["version_count"] == 2
assert row["char_count"] == 5
assert "updated_at" in row