fix:
1. 修复安卓部分机型顶部安全区遮挡回忆录标题的问题; 2. 降低封面图生成阈值和展示逻辑,独立封面图未生成时,使用正文图; 3. 去掉“嗯。”生硬回答,去掉不合理段首承接词; 4. 新增章节封面所需最少插图数的配置项
This commit is contained in:
57
api/tests/test_chapter_cover_fallback_inline.py
Normal file
57
api/tests/test_chapter_cover_fallback_inline.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""章节封面:无 cover_asset_id 时用正文首张 asset:// 作列表封面。"""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from app.features.memoir.helpers import chapter_cover_to_dict
|
||||
|
||||
|
||||
def test_cover_falls_back_to_first_inline_asset_url() -> None:
|
||||
ch = MagicMock(spec=[])
|
||||
ch.canonical_markdown = "正文\n\n"
|
||||
ch.cover_asset_id = None
|
||||
ch.story_links = []
|
||||
ch.images = []
|
||||
m = chapter_cover_to_dict(
|
||||
ch,
|
||||
asset_url_map={"img-1": "https://cos.example.com/signed-1"},
|
||||
markdown_for_response=None,
|
||||
)
|
||||
assert m is not None
|
||||
assert m["url"] == "https://cos.example.com/signed-1"
|
||||
assert m["status"] == "completed"
|
||||
|
||||
|
||||
def test_cover_from_reading_segments_when_canonical_has_no_asset() -> None:
|
||||
"""分段快照里有 asset://,章节 canonical 未带图时仍能出封面 URL。"""
|
||||
ch = MagicMock(spec=[])
|
||||
ch.canonical_markdown = "只有文字没有图" * 20
|
||||
ch.cover_asset_id = None
|
||||
ch.story_links = []
|
||||
ch.images = []
|
||||
ch.reading_segments_json = [
|
||||
{"story_id": "s1", "body_markdown": ""}
|
||||
]
|
||||
m = chapter_cover_to_dict(
|
||||
ch,
|
||||
asset_url_map={"seg-1": "https://cos.example.com/seg"},
|
||||
markdown_for_response=None,
|
||||
)
|
||||
assert m is not None
|
||||
assert m["url"] == "https://cos.example.com/seg"
|
||||
|
||||
|
||||
def test_cover_prefers_cover_asset_id_over_inline() -> None:
|
||||
ch = MagicMock(spec=[])
|
||||
ch.canonical_markdown = ""
|
||||
ch.cover_asset_id = "cover-99"
|
||||
ch.story_links = []
|
||||
ch.images = []
|
||||
m = chapter_cover_to_dict(
|
||||
ch,
|
||||
asset_url_map={
|
||||
"inline-1": "https://cos.example.com/inline",
|
||||
"cover-99": "https://cos.example.com/cover",
|
||||
},
|
||||
markdown_for_response=None,
|
||||
)
|
||||
assert m["url"] == "https://cos.example.com/cover"
|
||||
54
api/tests/test_cover_eligibility_effective_markdown.py
Normal file
54
api/tests/test_cover_eligibility_effective_markdown.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""封面闸门:canonical 未落库时须用物化正文计数 asset://。"""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from app.features.memoir.cover_eligibility import (
|
||||
chapter_eligible_for_cover_by_inline_body_image_count,
|
||||
chapter_needs_cover_enqueue,
|
||||
count_chapter_inline_body_images,
|
||||
effective_chapter_markdown_for_cover_gates,
|
||||
)
|
||||
|
||||
|
||||
def test_effective_markdown_falls_back_to_materialize_when_canonical_empty() -> None:
|
||||
ch = MagicMock()
|
||||
ch.canonical_markdown = ""
|
||||
ch.story_links = [MagicMock()]
|
||||
with patch(
|
||||
"app.features.memoir.chapter_markdown_compose.materialize_chapter_markdown_from_loaded_chapter",
|
||||
return_value="正文\n\n",
|
||||
):
|
||||
assert "asset://" in effective_chapter_markdown_for_cover_gates(ch)
|
||||
|
||||
|
||||
def test_count_uses_effective_when_canonical_empty() -> None:
|
||||
ch = MagicMock()
|
||||
ch.canonical_markdown = ""
|
||||
ch.story_links = [MagicMock()]
|
||||
with patch(
|
||||
"app.features.memoir.chapter_markdown_compose.materialize_chapter_markdown_from_loaded_chapter",
|
||||
return_value="",
|
||||
):
|
||||
assert count_chapter_inline_body_images(ch) == 1
|
||||
|
||||
|
||||
def test_eligible_with_explicit_markdown_override() -> None:
|
||||
ch = MagicMock()
|
||||
ch.canonical_markdown = ""
|
||||
assert chapter_eligible_for_cover_by_inline_body_image_count(
|
||||
ch, markdown=""
|
||||
)
|
||||
|
||||
|
||||
def test_needs_cover_enqueue_uses_materialized_body() -> None:
|
||||
ch = MagicMock()
|
||||
ch.canonical_markdown = ""
|
||||
ch.cover_asset_id = None
|
||||
ch.story_links = [MagicMock(story=MagicMock())]
|
||||
link = ch.story_links[0]
|
||||
link.story = MagicMock()
|
||||
with patch(
|
||||
"app.features.memoir.chapter_markdown_compose.materialize_chapter_markdown_from_loaded_chapter",
|
||||
return_value="故事\n\n",
|
||||
):
|
||||
assert chapter_needs_cover_enqueue(ch) is True
|
||||
@@ -3,6 +3,7 @@
|
||||
from app.agents.chat.reply_limits import (
|
||||
nonempty_segments_or_fallback,
|
||||
segments_from_llm_response,
|
||||
strip_leading_en_period_ack_for_chat,
|
||||
strip_markdown_for_chat,
|
||||
strip_parenthetical_asides_for_chat,
|
||||
)
|
||||
@@ -58,3 +59,15 @@ def test_segments_strip_parentheticals_before_split():
|
||||
|
||||
def test_strip_parenthetical_multiple_passes():
|
||||
assert strip_parenthetical_asides_for_chat("a(一)b(二)c") == "abc"
|
||||
|
||||
|
||||
def test_strip_leading_en_period_ack():
|
||||
assert strip_leading_en_period_ack_for_chat("嗯。后面正文") == "后面正文"
|
||||
assert strip_leading_en_period_ack_for_chat("嗯嗯。后面") == "后面"
|
||||
assert strip_leading_en_period_ack_for_chat(" 嗯。 第二句") == "第二句"
|
||||
assert strip_leading_en_period_ack_for_chat("句中嗯。不打头") == "句中嗯。不打头"
|
||||
|
||||
|
||||
def test_segments_strip_leading_en_ack():
|
||||
assert segments_from_llm_response("嗯。只有一句", max_segments=3) == ["只有一句"]
|
||||
assert segments_from_llm_response("嗯。A[SPLIT]嗯。B", max_segments=3) == ["A", "B"]
|
||||
|
||||
Reference in New Issue
Block a user