feat: 出站补全渲染——首封e164+标注/主题行/推送派生/canon双保险/面板尾注
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -57,15 +57,15 @@ def _setup(async_db, monkeypatch, source=ConversationSource.widget, ai=None):
|
||||
sent = []
|
||||
|
||||
async def fake_lead(customer_profile, contact_info, reasoning,
|
||||
conversation_messages, intent_level=""):
|
||||
conversation_messages, intent_level="", cc_info=None):
|
||||
sent.append(("lead", dict(contact_info), intent_level))
|
||||
return True
|
||||
|
||||
async def fake_nudge(contact_info):
|
||||
async def fake_nudge(contact_info, cc_info=None):
|
||||
sent.append(("nudge", dict(contact_info), ""))
|
||||
return True
|
||||
|
||||
async def fake_fix(contact_info, count=1):
|
||||
async def fake_fix(contact_info, count=1, cc_info=None):
|
||||
sent.append(("fix", dict(contact_info), ""))
|
||||
return True
|
||||
|
||||
@@ -276,7 +276,9 @@ def test_oscillation_does_not_burn_correction_quota(async_db, monkeypatch):
|
||||
select(Evaluation).where(Evaluation.conversation_id == conv.id))).scalar_one()
|
||||
ev = asyncio.run(check())
|
||||
assert ev.correction_count == 2
|
||||
assert ev.contact_info == {"phone": "18325698745", "whatsapp": "18325698745"}
|
||||
# Task 6: 末轮裸号轮末补建 _cc(no_ip,确认流未走完) → 快照带等待态尾注
|
||||
assert ev.contact_info == {"phone": "18325698745(待确认补全号)",
|
||||
"whatsapp": "18325698745(待确认补全号)"}
|
||||
asyncio.run(manager.db.close())
|
||||
|
||||
|
||||
@@ -304,7 +306,9 @@ def test_standalone_number_overrides_uncooperative_extractor(async_db, monkeypat
|
||||
assert kinds == ["lead", "fix", "fix"], f"实际: {kinds}"
|
||||
assert sent[2][1]["phone"] == "15236541128" # 更正2 邮件内容就是裸号
|
||||
assert ev.correction_count == 2
|
||||
assert ev.contact_info == {"phone": "15236541128", "whatsapp": "15236541128"}
|
||||
# Task 6: 末轮裸号轮末补建 _cc(确认流未走完) → 快照带等待态尾注
|
||||
assert ev.contact_info == {"phone": "15236541128(待确认补全号)",
|
||||
"whatsapp": "15236541128(待确认补全号)"}
|
||||
asyncio.run(manager.db.close())
|
||||
|
||||
|
||||
@@ -1714,3 +1718,58 @@ def test_violated_closing_separated_by_space(async_db, monkeypatch):
|
||||
tail = [m["content"] for m in msgs if m["role"] == "assistant"][-1]
|
||||
assert tail == streamed # 落库=流出
|
||||
asyncio.run(manager.db.close())
|
||||
|
||||
|
||||
# ---- Task 6: 出站渲染集成(面板尾注/确认不补发/换号走更正/跨会话 e164 去重) ----
|
||||
|
||||
def test_cc_first_email_panel_tail_and_no_resend_on_confirm(async_db, monkeypatch):
|
||||
"""spec §5 邮件时序集成: 首封快照 ev.contact_info 存裸号原文+等待态尾注
|
||||
(待确认补全号)(面板同步,e164 走派生不落本体——防每轮误判换号);
|
||||
客户确认(号码未变)不补发更正;换号(canon 变化)才走现有更正通道。"""
|
||||
_sa(monkeypatch)
|
||||
ai = _RecAI(extract=[{}, {"cc_reply": {"affirm": True, "country": None}},
|
||||
{}, {}])
|
||||
(manager, conv), sent = _setup(async_db, monkeypatch, ai=ai,
|
||||
source=ConversationSource.standalone)
|
||||
|
||||
async def get_ev():
|
||||
return (await manager.db.execute(
|
||||
select(Evaluation).where(Evaluation.conversation_id == conv.id))
|
||||
).scalar_one()
|
||||
|
||||
asyncio.run(_send(manager, conv.session_id, "my whatsapp is 0501234567"))
|
||||
assert [s[0] for s in sent] == ["lead"]
|
||||
ev = asyncio.run(get_ev())
|
||||
# 面板尾注: 首封时确认流未落定(attempts=1 < 2),快照值带等待态标注;
|
||||
# 尾注全非数字,不影响后续 canon 比对
|
||||
assert ev.contact_info["whatsapp"] == "0501234567(待确认补全号)"
|
||||
|
||||
asyncio.run(_send(manager, conv.session_id, "yes that's right"))
|
||||
asyncio.run(_send(manager, conv.session_id, "and the fittings?"))
|
||||
assert [s[0] for s in sent] == ["lead"] # 确认落定(号码未变)不补发
|
||||
|
||||
asyncio.run(_send(manager, conv.session_id, "my new whatsapp is 0512345678"))
|
||||
assert [s[0] for s in sent] == ["lead", "fix"] # 换号 → 现有更正通道
|
||||
ev = asyncio.run(get_ev())
|
||||
assert ev.contact_info["whatsapp"] == "0512345678(待确认补全号)"
|
||||
asyncio.run(manager.db.close())
|
||||
|
||||
|
||||
def test_cross_session_dedup_merges_cc_e164_canon(async_db, monkeypatch):
|
||||
"""Task 6 canon 双保险(跨会话 24h 去重): A 会话留裸号(首封带补全号发信),
|
||||
B 会话同号带 + 重访 → 去重集并入 A 的 _cc.e164 canon → 回访轻提醒,
|
||||
不退化为全量询盘。"""
|
||||
_sa(monkeypatch)
|
||||
ai1 = _RecAI(extract=[{}])
|
||||
(m1, c1), sent1 = _setup(async_db, monkeypatch, ai=ai1,
|
||||
source=ConversationSource.standalone)
|
||||
asyncio.run(_send(m1, c1.session_id, "my whatsapp is 0501234567"))
|
||||
assert [s[0] for s in sent1] == ["lead"]
|
||||
asyncio.run(m1.db.close())
|
||||
|
||||
ai2 = _RecAI(extract=[{}])
|
||||
(m2, c2), sent2 = _setup(async_db, monkeypatch, ai=ai2,
|
||||
source=ConversationSource.standalone)
|
||||
asyncio.run(_send(m2, c2.session_id, "my whatsapp is +966501234567"))
|
||||
assert [s[0] for s in sent2] == ["nudge"] # e164 形态同号 → 仍判回访
|
||||
asyncio.run(m2.db.close())
|
||||
|
||||
Reference in New Issue
Block a user