多段激活后无需再带工单号;含空运群回复走机器人;复制补问清单时空货好时间不再吃成下一行编号。 Co-authored-by: Cursor <cursoragent@cursor.com>
535 lines
21 KiB
Python
535 lines
21 KiB
Python
"""
|
|
系统异常:话术、判定边界、无单不落库、暂停、告警卡、重试清列。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
from datetime import datetime
|
|
from zoneinfo import ZoneInfo
|
|
|
|
from agent.ledger.memory_ledger import MemoryLedger
|
|
from agent.policy import inquiry_copy as copy
|
|
from agent.policy.system_exception import (
|
|
STEP_GROUP,
|
|
STEP_LLM,
|
|
STEP_LLM_NO_TICKET,
|
|
STEP_TMS_CABIN,
|
|
STEP_TMS_QUOTE,
|
|
TYPE_GROUP,
|
|
TYPE_LLM,
|
|
TYPE_TMS,
|
|
SystemExceptionEvent,
|
|
call_with_retries,
|
|
confirm_system_exception,
|
|
confirm_tms_quote_if_tech,
|
|
extract_work_order_nos,
|
|
is_tms_tech_failure,
|
|
query_tms_resilient,
|
|
recover_tms_query_from_ledger,
|
|
should_mark_ticket,
|
|
ticket_is_paused,
|
|
)
|
|
|
|
|
|
class TestSystemExceptionCopy(unittest.TestCase):
|
|
def test_fallback_three_types(self) -> None:
|
|
self.assertEqual(copy.system_exception_fallback(TYPE_LLM), "大模型服务异常,IT排查中...")
|
|
self.assertEqual(copy.system_exception_fallback(TYPE_TMS), "TMS接口异常,IT运维排查中...")
|
|
self.assertEqual(
|
|
copy.system_exception_fallback(TYPE_GROUP, names=["赵六"]),
|
|
"无法添加 赵六 同事,请联系管理员检查企业微信配置。",
|
|
)
|
|
self.assertIn("李四、王五", copy.system_exception_fallback(TYPE_GROUP, names=["李四", "王五"]))
|
|
|
|
def test_recovery_four_steps(self) -> None:
|
|
self.assertEqual(copy.system_exception_recovery(STEP_TMS_QUOTE), copy.SYS_EXC_RECOVERY_TMS)
|
|
self.assertEqual(copy.system_exception_recovery(STEP_LLM), copy.SYS_EXC_RECOVERY_LLM)
|
|
self.assertEqual(copy.system_exception_recovery(STEP_TMS_CABIN), copy.SYS_EXC_RECOVERY_CABIN)
|
|
self.assertEqual(copy.system_exception_recovery(copy.STEP_TMS_LOCK), copy.SYS_EXC_RECOVERY_CABIN)
|
|
self.assertEqual(copy.system_exception_recovery(copy.STEP_TMS_RELEASE), copy.SYS_EXC_RECOVERY_CABIN)
|
|
self.assertEqual(copy.system_exception_recovery(STEP_GROUP), copy.SYS_EXC_RECOVERY_GROUP)
|
|
|
|
def test_impact_by_step(self) -> None:
|
|
self.assertEqual(copy.system_exception_impact(STEP_LLM_NO_TICKET), copy.IMPACT_LLM_NO_TICKET)
|
|
self.assertEqual(copy.system_exception_impact(STEP_LLM), copy.IMPACT_LLM)
|
|
self.assertEqual(copy.system_exception_impact(STEP_TMS_QUOTE), copy.IMPACT_TMS_QUOTE)
|
|
self.assertEqual(copy.system_exception_impact(STEP_TMS_CABIN), copy.IMPACT_TMS_CABIN)
|
|
self.assertEqual(copy.system_exception_impact(STEP_GROUP), copy.IMPACT_GROUP)
|
|
|
|
def test_cabin_impact_lock_and_release_are_separate(self) -> None:
|
|
"""锁舱失败和释放失败要分开说,不能再写成「锁舱/释放暂停」。"""
|
|
self.assertEqual(
|
|
copy.system_exception_impact(copy.STEP_TMS_LOCK),
|
|
"锁舱失败,工单状态保持不变",
|
|
)
|
|
self.assertEqual(
|
|
copy.system_exception_impact(copy.STEP_TMS_RELEASE),
|
|
"释放舱位失败,工单状态保持不变",
|
|
)
|
|
|
|
def test_alert_title_never_has_test_prefix(self) -> None:
|
|
"""系统异常通知标题不要 [test] / 【test】。"""
|
|
payload = copy.system_exception_alert_payload(
|
|
work_order_no="WO202609210002",
|
|
exception_type=TYPE_TMS,
|
|
service="空运锁舱",
|
|
error_code="LOCK_FAIL",
|
|
step=copy.STEP_TMS_LOCK,
|
|
occurred_at="2026-09-21 16:00:00",
|
|
test_prefix=True,
|
|
)
|
|
title = payload["template_card"]["main_title"]["title"]
|
|
self.assertEqual(title, "系统异常通知")
|
|
self.assertNotIn("[test]", title)
|
|
self.assertNotIn("【test】", title)
|
|
text = copy.system_exception_alert_text(
|
|
work_order_no="WO202609210002",
|
|
exception_type=TYPE_TMS,
|
|
service="空运锁舱",
|
|
error_code="LOCK_FAIL",
|
|
step=copy.STEP_TMS_LOCK,
|
|
occurred_at="2026-09-21 16:00:00",
|
|
test_prefix=True,
|
|
)
|
|
self.assertTrue(text.startswith("询价机器人系统告警"))
|
|
self.assertNotIn("[test]", text)
|
|
self.assertNotIn("【test】", text)
|
|
|
|
def test_lock_fail_copy_has_operator_reason_and_it_notice(self) -> None:
|
|
text = copy.air_lock_fail(
|
|
work_order_no="WO202609210001",
|
|
operator="吴季林",
|
|
reason="该航班日期不存在可锁定的配载记录",
|
|
)
|
|
self.assertEqual(
|
|
text,
|
|
"工单WO202609210001 空运锁舱失败。\n"
|
|
"操作人:吴季林\n"
|
|
"锁舱失败的原因:该航班日期不存在可锁定的配载记录\n"
|
|
"已通知IT运维排查,请稍等。",
|
|
)
|
|
|
|
def test_release_fail_copy_has_operator_reason_and_it_notice(self) -> None:
|
|
text = copy.air_release_fail(
|
|
work_order_no="WO202609210001",
|
|
operator="吴季林",
|
|
reason="舱位不存在",
|
|
)
|
|
self.assertEqual(
|
|
text,
|
|
"工单WO202609210001 空运释放舱位失败。\n"
|
|
"操作人:吴季林\n"
|
|
"释放舱位失败的原因:舱位不存在\n"
|
|
"已通知IT运维排查,请稍等。",
|
|
)
|
|
|
|
def test_cabin_fail_reason_keeps_tms_text(self) -> None:
|
|
self.assertEqual(
|
|
copy.air_cabin_fail_reason(
|
|
tms_reason="该航班日期不存在可锁定的配载记录",
|
|
flight_date="2026-09-20",
|
|
today="2026-09-21",
|
|
),
|
|
"该航班日期不存在可锁定的配载记录",
|
|
)
|
|
self.assertNotIn(
|
|
"报价选项不存在",
|
|
copy.air_cabin_fail_reason(
|
|
tms_reason="该航班日期不存在可锁定的配载记录",
|
|
flight_date="2026-09-20",
|
|
today="2026-09-21",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
copy.air_cabin_fail_reason(
|
|
tms_reason="",
|
|
flight_date="2026-09-20",
|
|
today="2026-09-21",
|
|
),
|
|
"",
|
|
)
|
|
|
|
|
|
class TestSystemExceptionRules(unittest.TestCase):
|
|
def test_tms_no_price_is_not_tech(self) -> None:
|
|
self.assertFalse(
|
|
is_tms_tech_failure(
|
|
{"ok": True, "classification": "TMS_NO_QUOTE_1002", "has_price": False}
|
|
)
|
|
)
|
|
self.assertFalse(
|
|
is_tms_tech_failure({"ok": False, "classification": "TMS_QUERY_INCOMPLETE"})
|
|
)
|
|
self.assertTrue(
|
|
is_tms_tech_failure({"ok": False, "classification": "TMS_TECH_FAILURE"})
|
|
)
|
|
self.assertTrue(is_tms_tech_failure({"ok": False, "error": "timeout"}))
|
|
|
|
def test_closed_or_blank_not_marked(self) -> None:
|
|
self.assertFalse(should_mark_ticket(work_order_no="", ticket_status=""))
|
|
self.assertFalse(should_mark_ticket(work_order_no="WO1", ticket_status="已关闭"))
|
|
self.assertTrue(should_mark_ticket(work_order_no="WO1", ticket_status="询价中"))
|
|
self.assertTrue(should_mark_ticket(work_order_no="WO1", ticket_status="转人工"))
|
|
self.assertTrue(should_mark_ticket(work_order_no="WO1", ticket_status="已成交"))
|
|
|
|
def test_extract_work_order_nos(self) -> None:
|
|
self.assertEqual(extract_work_order_nos("请看 WO202609210001"), ["WO202609210001"])
|
|
self.assertEqual(extract_work_order_nos("虚拟 VT202609210001 不算"), [])
|
|
|
|
def test_retries_succeed_on_second(self) -> None:
|
|
hits = {"n": 0}
|
|
|
|
def flaky() -> dict:
|
|
hits["n"] += 1
|
|
if hits["n"] < 2:
|
|
return {"ok": False}
|
|
return {"ok": True, "value": 1}
|
|
|
|
out = call_with_retries(flaky, is_ok=lambda x: bool(x.get("ok")), attempts=3)
|
|
self.assertTrue(out.get("ok"))
|
|
self.assertEqual(hits["n"], 2)
|
|
|
|
def test_retries_keep_last_failure(self) -> None:
|
|
def always_fail() -> dict:
|
|
return {"ok": False, "classification": "TMS_TECH_FAILURE"}
|
|
|
|
out = call_with_retries(always_fail, is_ok=lambda x: bool(x.get("ok")), attempts=3)
|
|
self.assertFalse(out.get("ok"))
|
|
self.assertEqual(out.get("classification"), "TMS_TECH_FAILURE")
|
|
|
|
|
|
class TestTmsTimeoutRecoverPriced(unittest.TestCase):
|
|
"""查到价但智能体超时:主账已有价则按成功续办,不标 TMS异常。"""
|
|
|
|
def setUp(self) -> None:
|
|
self.ledger = MemoryLedger()
|
|
created = self.ledger.create_ticket(
|
|
sender_id="sales1",
|
|
business_line="AIR",
|
|
facts={"起运港": "南京", "目的港": "吉隆坡", "品名": "茶叶"},
|
|
)
|
|
self.wo = str(created.get("work_order_no") or "")
|
|
self.ledger.upsert_quote(
|
|
work_order_no=self.wo,
|
|
quote={
|
|
"total": "CNY 5001.00",
|
|
"currency": "CNY",
|
|
"source": "TMS",
|
|
"source_label": "TMS标准报价",
|
|
},
|
|
to_status="已报价",
|
|
)
|
|
|
|
def test_recover_from_ledger_when_already_quoted(self) -> None:
|
|
got = recover_tms_query_from_ledger(self.ledger, work_order_no=self.wo)
|
|
self.assertIsNotNone(got)
|
|
assert got is not None
|
|
self.assertTrue(got.get("ok"))
|
|
self.assertTrue(got.get("has_price"))
|
|
self.assertTrue(got.get("recovered"))
|
|
self.assertIn("5001", str((got.get("quote") or {}).get("total") or ""))
|
|
|
|
def test_query_resilient_recovers_after_tech_fail(self) -> None:
|
|
self.ledger.query_tms = ( # type: ignore[method-assign]
|
|
lambda **_kwargs: {
|
|
"ok": False,
|
|
"classification": "TMS_TECH_FAILURE",
|
|
"has_price": False,
|
|
"quote": {},
|
|
}
|
|
)
|
|
out = query_tms_resilient(
|
|
self.ledger, work_order_no=self.wo, facts={"起运港": "南京", "目的港": "吉隆坡"}
|
|
)
|
|
self.assertTrue(out.get("ok"))
|
|
self.assertTrue(out.get("has_price"))
|
|
self.assertTrue(out.get("recovered"))
|
|
self.assertEqual(out.get("classification"), "HAS_PRICE")
|
|
|
|
def test_confirm_skips_mark_when_ledger_priced(self) -> None:
|
|
replies: list[str] = []
|
|
marked = confirm_tms_quote_if_tech(
|
|
ledger=self.ledger,
|
|
reply=replies.append,
|
|
work_order_no=self.wo,
|
|
tms={"ok": False, "classification": "TMS_TECH_FAILURE"},
|
|
sender_id="sales1",
|
|
ticket_status="询价中",
|
|
)
|
|
self.assertFalse(marked)
|
|
self.assertEqual(replies, [])
|
|
snap = self.ledger.get_for_agent(work_order_no=self.wo, sender_id="sales1")
|
|
self.assertFalse(str(snap.get("system_exception") or "").strip())
|
|
|
|
def test_true_tech_fail_still_marks_when_no_price(self) -> None:
|
|
created = self.ledger.create_ticket(
|
|
sender_id="sales2",
|
|
business_line="AIR",
|
|
facts={"起运港": "南京", "目的港": "吉隆坡", "品名": "茶叶"},
|
|
)
|
|
wo2 = str(created.get("work_order_no") or "")
|
|
replies: list[str] = []
|
|
marked = confirm_tms_quote_if_tech(
|
|
ledger=self.ledger,
|
|
reply=replies.append,
|
|
work_order_no=wo2,
|
|
tms={"ok": False, "classification": "TMS_TECH_FAILURE"},
|
|
sender_id="sales2",
|
|
ticket_status="询价中",
|
|
)
|
|
self.assertTrue(marked)
|
|
self.assertTrue(any("TMS接口异常" in x for x in replies))
|
|
|
|
|
|
class TestConfirmSystemException(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.ledger = MemoryLedger()
|
|
created = self.ledger.create_ticket(
|
|
sender_id="sales1",
|
|
business_line="SEA",
|
|
facts={"起运港": "上海", "目的港": "洛杉矶", "品名": "衣服"},
|
|
)
|
|
self.wo = str(created.get("work_order_no") or "")
|
|
self.ledger.upsert_exception_notify_user({"name": "运维甲", "wecomId": "ops-a"})
|
|
|
|
def test_no_ticket_replies_and_alerts_but_does_not_create(self) -> None:
|
|
replies: list[str] = []
|
|
queued: list[dict] = []
|
|
|
|
def enqueue(*, touser, content, dedupe_key, payload):
|
|
queued.append({"touser": touser, "payload": payload, "content": content})
|
|
return True, "ok"
|
|
|
|
before = len(self.ledger._tickets)
|
|
out = confirm_system_exception(
|
|
event=SystemExceptionEvent(
|
|
exception_type=TYPE_LLM,
|
|
step=STEP_LLM_NO_TICKET,
|
|
reason="模型返回空结果",
|
|
service="大模型识别",
|
|
error_code="EMPTY",
|
|
),
|
|
ledger=self.ledger,
|
|
reply=replies.append,
|
|
enqueue=enqueue,
|
|
notify_ready=True,
|
|
now=datetime(2026, 9, 21, 10, 0, 0, tzinfo=ZoneInfo("Asia/Shanghai")),
|
|
)
|
|
self.assertTrue(out.get("ok"))
|
|
self.assertFalse(out.get("marked"))
|
|
self.assertEqual(len(self.ledger._tickets), before)
|
|
self.assertEqual(replies[0], copy.SYS_EXC_LLM_FALLBACK)
|
|
self.assertEqual(queued[0]["touser"], "ops-a")
|
|
card = queued[0]["payload"]["template_card"]
|
|
self.assertEqual(card["source"]["desc"], "询价机器人系统告警")
|
|
self.assertEqual(card["main_title"]["desc"], "关联工单 无")
|
|
values = {row["keyname"]: row["value"] for row in card["horizontal_content_list"]}
|
|
self.assertEqual(values["影响范围"], copy.IMPACT_LLM_NO_TICKET)
|
|
self.assertEqual(card["main_title"]["title"], "系统异常通知")
|
|
self.assertNotIn("[test]", card["main_title"]["title"])
|
|
self.assertNotIn("[test]", queued[0]["content"])
|
|
|
|
def test_mark_ticket_and_pause(self) -> None:
|
|
replies: list[str] = []
|
|
out = confirm_system_exception(
|
|
event=SystemExceptionEvent(
|
|
exception_type=TYPE_TMS,
|
|
step=STEP_TMS_QUOTE,
|
|
reason="TMS /quote/v2 接口调用失败",
|
|
service="TMS报价",
|
|
error_code="QUOTE_CALCULATION",
|
|
work_order_no=self.wo,
|
|
ticket_status="询价中",
|
|
),
|
|
ledger=self.ledger,
|
|
reply=replies.append,
|
|
enqueue=lambda **kwargs: (True, "ok"),
|
|
notify_ready=False,
|
|
)
|
|
self.assertTrue(out.get("marked"))
|
|
ticket = self.ledger.get_ticket(work_order_no=self.wo)
|
|
self.assertEqual(ticket.status, "询价中")
|
|
self.assertEqual(ticket.system_exception, TYPE_TMS)
|
|
self.assertEqual(ticket.system_exception_reason, "TMS /quote/v2 接口调用失败")
|
|
self.assertTrue(ticket_is_paused(ticket))
|
|
self.assertEqual(replies[0], copy.SYS_EXC_TMS_FALLBACK)
|
|
|
|
def test_air_lock_alert_impact_keeps_status(self) -> None:
|
|
queued: list[dict] = []
|
|
|
|
def enqueue(*, touser, content, dedupe_key, payload):
|
|
queued.append({"payload": payload, "content": content})
|
|
return True, "ok"
|
|
|
|
confirm_system_exception(
|
|
event=SystemExceptionEvent(
|
|
exception_type=TYPE_TMS,
|
|
step=copy.STEP_TMS_LOCK,
|
|
reason="空运锁舱接口调用失败",
|
|
service="空运锁舱",
|
|
error_code="LOCK_FAIL",
|
|
work_order_no=self.wo,
|
|
ticket_status="已报价",
|
|
),
|
|
ledger=self.ledger,
|
|
reply=lambda _t: None,
|
|
enqueue=enqueue,
|
|
notify_ready=True,
|
|
)
|
|
card = queued[0]["payload"]["template_card"]
|
|
values = {row["keyname"]: row["value"] for row in card["horizontal_content_list"]}
|
|
self.assertEqual(values["报错原因"], "LOCK_FAIL")
|
|
self.assertNotIn("错误码", values)
|
|
self.assertIn("报错原因:LOCK_FAIL", queued[0]["content"])
|
|
self.assertEqual(values["影响范围"], "锁舱失败,工单状态保持不变")
|
|
self.assertEqual(card["main_title"]["title"], "系统异常通知")
|
|
|
|
def test_air_release_alert_impact_keeps_status(self) -> None:
|
|
queued: list[dict] = []
|
|
|
|
def enqueue(*, touser, content, dedupe_key, payload):
|
|
queued.append({"payload": payload})
|
|
return True, "ok"
|
|
|
|
confirm_system_exception(
|
|
event=SystemExceptionEvent(
|
|
exception_type=TYPE_TMS,
|
|
step=copy.STEP_TMS_RELEASE,
|
|
reason="空运释放舱位接口调用失败",
|
|
service="空运舱位",
|
|
error_code="RELEASE_FAIL",
|
|
work_order_no=self.wo,
|
|
ticket_status="已锁舱",
|
|
),
|
|
ledger=self.ledger,
|
|
reply=lambda _t: None,
|
|
enqueue=enqueue,
|
|
notify_ready=True,
|
|
)
|
|
card = queued[0]["payload"]["template_card"]
|
|
values = {row["keyname"]: row["value"] for row in card["horizontal_content_list"]}
|
|
self.assertEqual(values["影响范围"], "释放舱位失败,工单状态保持不变")
|
|
|
|
def test_closed_ticket_not_marked(self) -> None:
|
|
self.ledger.transition(work_order_no=self.wo, to_status="已关闭")
|
|
out = confirm_system_exception(
|
|
event=SystemExceptionEvent(
|
|
exception_type=TYPE_TMS,
|
|
step=STEP_TMS_QUOTE,
|
|
reason="x",
|
|
service="TMS报价",
|
|
error_code="TIMEOUT",
|
|
work_order_no=self.wo,
|
|
ticket_status="已关闭",
|
|
),
|
|
ledger=self.ledger,
|
|
reply=lambda _t: None,
|
|
notify_ready=False,
|
|
)
|
|
self.assertFalse(out.get("marked"))
|
|
ticket = self.ledger.get_ticket(work_order_no=self.wo)
|
|
self.assertFalse(ticket_is_paused(ticket))
|
|
|
|
def test_later_ticket_does_not_inherit_pre_ticket_exception(self) -> None:
|
|
confirm_system_exception(
|
|
event=SystemExceptionEvent(
|
|
exception_type=TYPE_LLM,
|
|
step=STEP_LLM_NO_TICKET,
|
|
reason="空结果",
|
|
service="大模型识别",
|
|
error_code="EMPTY",
|
|
),
|
|
ledger=self.ledger,
|
|
reply=lambda _t: None,
|
|
notify_ready=False,
|
|
)
|
|
ticket = self.ledger.get_ticket(work_order_no=self.wo)
|
|
self.assertFalse(ticket_is_paused(ticket))
|
|
|
|
def test_admin_retry_tms_success_clears(self) -> None:
|
|
self.ledger.mark_system_exception(
|
|
work_order_no=self.wo,
|
|
system_exception=TYPE_TMS,
|
|
system_exception_reason="TMS /quote/v2 接口调用失败",
|
|
step=STEP_TMS_QUOTE,
|
|
payload={"conversation_kind": "private", "conversation_target": "sales1"},
|
|
)
|
|
from agent.policy.system_exception import run_admin_retry
|
|
|
|
talks: list[str] = []
|
|
|
|
def fake_send(*, kind, target, text, extra=None):
|
|
talks.append(text)
|
|
|
|
import agent.policy.system_exception as se
|
|
|
|
orig = se._send_to_conversation
|
|
se._send_to_conversation = fake_send
|
|
try:
|
|
out = run_admin_retry(self.wo, ledger=self.ledger)
|
|
finally:
|
|
se._send_to_conversation = orig
|
|
self.assertTrue(out.get("ok"))
|
|
ticket = self.ledger.get_ticket(work_order_no=self.wo)
|
|
self.assertFalse(ticket_is_paused(ticket))
|
|
self.assertEqual(ticket.status, "已报价")
|
|
self.assertTrue(ticket.quote)
|
|
self.assertTrue(any("TMS已恢复" in x for x in talks))
|
|
joined = "\n".join(talks)
|
|
self.assertTrue("USD" in joined or "拉产品" in joined or "已报价" in joined)
|
|
|
|
def test_admin_retry_cabin_lock_runs_lock(self) -> None:
|
|
self.ledger.mark_system_exception(
|
|
work_order_no=self.wo,
|
|
system_exception=TYPE_TMS,
|
|
system_exception_reason="空运锁舱接口调用失败",
|
|
step=STEP_TMS_CABIN,
|
|
payload={
|
|
"conversation_kind": "group",
|
|
"conversation_target": "chat-air",
|
|
"extra": {"option_no": "AIR-OPT-01", "instruction": "锁舱"},
|
|
},
|
|
)
|
|
hits = {"n": 0}
|
|
orig_lock = self.ledger.lock_cabin
|
|
|
|
def counted(**kwargs):
|
|
hits["n"] += 1
|
|
return orig_lock(**kwargs)
|
|
|
|
self.ledger.lock_cabin = counted # type: ignore[method-assign]
|
|
from agent.policy.system_exception import run_admin_retry
|
|
|
|
talks: list[str] = []
|
|
import agent.policy.system_exception as se
|
|
|
|
orig = se._send_to_conversation
|
|
se._send_to_conversation = lambda **kwargs: talks.append(str(kwargs.get("text") or ""))
|
|
try:
|
|
out = run_admin_retry(self.wo, ledger=self.ledger)
|
|
finally:
|
|
se._send_to_conversation = orig
|
|
self.assertTrue(out.get("ok"))
|
|
self.assertGreaterEqual(hits["n"], 1)
|
|
ticket = self.ledger.get_ticket(work_order_no=self.wo)
|
|
self.assertFalse(ticket_is_paused(ticket))
|
|
self.assertTrue(any("空运舱位接口已恢复" in x for x in talks))
|
|
|
|
def test_extract_tech_fail_when_model_errors(self) -> None:
|
|
from agent.llm.extract_text import extract_inquiry_snapshot
|
|
|
|
class Boom:
|
|
ok = False
|
|
error = "timeout"
|
|
tool_calls = []
|
|
content = ""
|
|
|
|
snap = extract_inquiry_snapshot("上海到洛杉矶衣服", allow_b=True, chat_fn=lambda **_k: Boom())
|
|
self.assertTrue(snap.get("tech_fail"))
|
|
self.assertEqual(snap.get("tech_error"), "timeout")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|