落地陆运私聊文字询价:选项核对后再建单查价,并按线路拉产品进群。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
"""
|
||||
入站选路:陆运书签/本句陆运交给 LandTextInquiryFlow;海运立即建单不变。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
if ROOT not in sys.path:
|
||||
sys.path.insert(0, ROOT)
|
||||
|
||||
os.environ.setdefault("YTD_ENV", "test")
|
||||
os.environ.setdefault("REDIS_BACKEND", "memory")
|
||||
os.environ.setdefault("CHECKPOINT_BACKEND", "memory")
|
||||
os.environ.setdefault("MESSAGE_STORE_BACKEND", "memory")
|
||||
os.environ.setdefault("LLM_DATA_USAGE_CONFIRMED", "false")
|
||||
os.environ.setdefault("LLM_ALLOW_NETWORK", "false")
|
||||
os.environ.setdefault("LEDGER_BACKEND", "memory")
|
||||
|
||||
from agent.channel.queue import MemoryMessageStore, get_message_store
|
||||
from agent.channel.wecom.models import InboundMessage
|
||||
from agent.handlers.text_inquiry import handle_text_inquiry
|
||||
from agent.policy.air_text_flow import reset_air_text_flow_for_test
|
||||
from agent.policy.land_text_flow import get_land_text_flow, reset_land_text_flow_for_test
|
||||
from agent.policy.sea_text_flow import reset_sea_text_flow_for_test
|
||||
from agent.routing.decision import RouteDecision
|
||||
from agent.routing.dispatch import dispatch_inbound
|
||||
from tests.test_sea_text_inquiry import COMPLETE_SEA
|
||||
|
||||
|
||||
class LandDispatchTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
reset_sea_text_flow_for_test()
|
||||
reset_land_text_flow_for_test()
|
||||
reset_air_text_flow_for_test()
|
||||
self.store = MemoryMessageStore()
|
||||
|
||||
def _outbox_text(self) -> str:
|
||||
return "\n".join(i.content for i in self.store._outbox.values())
|
||||
|
||||
def test_handler_routes_land_to_option_sheet(self) -> None:
|
||||
msg = InboundMessage(sender_id="ld1", message_id="m-land-1", content="陆运询价")
|
||||
phase = handle_text_inquiry(
|
||||
msg,
|
||||
injected_facts={"起运港": "广州", "目的港": "深圳", "品名": "衣服"},
|
||||
injected_mode="LAND",
|
||||
store=self.store,
|
||||
)
|
||||
self.assertEqual(phase, "need_land_options")
|
||||
body = self._outbox_text()
|
||||
self.assertIn("运输类型", body)
|
||||
self.assertIn("线路类别", body)
|
||||
self.assertIn("运输分类", body)
|
||||
self.assertNotIn("请补充陆运类型:", body)
|
||||
|
||||
def test_air_owner_forwards_land_to_option_sheet(self) -> None:
|
||||
from agent.policy.air_text_flow import get_air_text_flow
|
||||
|
||||
replies: list[str] = []
|
||||
phase = get_air_text_flow().on_text(
|
||||
sender_id="ld-air",
|
||||
text="陆运询价",
|
||||
reply=lambda t, extra=None: replies.append(t) or "",
|
||||
injected_facts={"起运港": "广州", "目的港": "深圳", "品名": "衣服"},
|
||||
injected_mode="LAND",
|
||||
)
|
||||
self.assertEqual(phase, "need_land_options")
|
||||
text = "\n".join(replies)
|
||||
self.assertIn("线路类别", text)
|
||||
self.assertIn("运输分类", text)
|
||||
self.assertNotIn("请补充陆运类型:", text)
|
||||
|
||||
def test_land_session_survives_confirm(self) -> None:
|
||||
msg1 = InboundMessage(sender_id="ld2", message_id="m-l2a", content="陆运")
|
||||
facts = {
|
||||
"起运港": "广州",
|
||||
"目的港": "深圳",
|
||||
"品名": "衣服",
|
||||
"体积": "100CBM",
|
||||
"毛重": "50kg",
|
||||
}
|
||||
phase = handle_text_inquiry(
|
||||
msg1,
|
||||
injected_facts=facts,
|
||||
injected_mode="LAND",
|
||||
store=self.store,
|
||||
)
|
||||
# 补选项走同一 sender
|
||||
msg2 = InboundMessage(
|
||||
sender_id="ld2",
|
||||
message_id="m-l2b",
|
||||
content="国内运输拼车 国内长途/零担 急件",
|
||||
)
|
||||
phase = handle_text_inquiry(
|
||||
msg2,
|
||||
injected_facts=facts,
|
||||
injected_mode="LAND",
|
||||
store=self.store,
|
||||
)
|
||||
self.assertEqual(phase, "wait_confirm")
|
||||
msg3 = InboundMessage(sender_id="ld2", message_id="m-l2c", content="确定")
|
||||
phase = handle_text_inquiry(msg3, store=self.store)
|
||||
self.assertEqual(phase, "wait_collab")
|
||||
|
||||
def test_confirm_misrouted_as_other_still_creates(self) -> None:
|
||||
store = get_message_store()
|
||||
facts = {
|
||||
"起运港": "广州",
|
||||
"目的港": "深圳",
|
||||
"品名": "衣服",
|
||||
"体积": "100CBM",
|
||||
"毛重": "50kg",
|
||||
}
|
||||
handle_text_inquiry(
|
||||
InboundMessage(sender_id="ld3", message_id="m-l3a", content="陆运"),
|
||||
injected_facts=facts,
|
||||
injected_mode="LAND",
|
||||
store=store,
|
||||
)
|
||||
handle_text_inquiry(
|
||||
InboundMessage(
|
||||
sender_id="ld3",
|
||||
message_id="m-l3b",
|
||||
content="国内运输拼车 国内长途/零担 急件",
|
||||
),
|
||||
injected_facts=facts,
|
||||
injected_mode="LAND",
|
||||
store=store,
|
||||
)
|
||||
self.assertEqual(get_land_text_flow().session_of("ld3").phase, "wait_confirm")
|
||||
name = dispatch_inbound(
|
||||
InboundMessage(sender_id="ld3", message_id="m-l3c", content="确定"),
|
||||
RouteDecision(intent="ordinary_text_other"),
|
||||
)
|
||||
self.assertEqual(name, "ordinary_text_inquiry")
|
||||
sess = get_land_text_flow().session_of("ld3")
|
||||
self.assertEqual(sess.phase, "wait_collab")
|
||||
self.assertTrue((sess.work_order_no or "").startswith("WO"))
|
||||
bodies = "\n".join(i.content for i in store._outbox.values())
|
||||
self.assertIn(sess.work_order_no, bodies)
|
||||
last = list(store._outbox.values())[-1].content
|
||||
self.assertNotIn("你好,我是询价机器人", last)
|
||||
|
||||
def test_confirm_after_memory_lost_still_creates(self) -> None:
|
||||
store = get_message_store()
|
||||
facts = {
|
||||
"起运港": "广州",
|
||||
"目的港": "深圳",
|
||||
"品名": "衣服",
|
||||
"体积": "100CBM",
|
||||
"毛重": "50kg",
|
||||
}
|
||||
handle_text_inquiry(
|
||||
InboundMessage(sender_id="ld4", message_id="m-l4a", content="陆运"),
|
||||
injected_facts=facts,
|
||||
injected_mode="LAND",
|
||||
store=store,
|
||||
)
|
||||
handle_text_inquiry(
|
||||
InboundMessage(
|
||||
sender_id="ld4",
|
||||
message_id="m-l4b",
|
||||
content="国内运输拼车 国内长途/零担 急件",
|
||||
),
|
||||
injected_facts=facts,
|
||||
injected_mode="LAND",
|
||||
store=store,
|
||||
)
|
||||
self.assertEqual(get_land_text_flow().session_of("ld4").phase, "wait_confirm")
|
||||
reset_land_text_flow_for_test()
|
||||
self.assertIsNone(get_land_text_flow().session_of("ld4"))
|
||||
name = dispatch_inbound(
|
||||
InboundMessage(sender_id="ld4", message_id="m-l4c", content="确定"),
|
||||
RouteDecision(intent="ordinary_text_other"),
|
||||
)
|
||||
self.assertEqual(name, "ordinary_text_inquiry")
|
||||
sess = get_land_text_flow().session_of("ld4")
|
||||
self.assertEqual(sess.phase, "wait_collab")
|
||||
self.assertTrue((sess.work_order_no or "").startswith("WO"))
|
||||
last = list(store._outbox.values())[-1].content
|
||||
self.assertNotIn("你好,我是询价机器人", last)
|
||||
|
||||
def test_tms_retry_misrouted_as_other(self) -> None:
|
||||
from tests.test_land_text_inquiry import CountingLedger
|
||||
|
||||
class Flaky(CountingLedger):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.query_calls = 0
|
||||
|
||||
def query_tms(self, **kwargs):
|
||||
self.query_calls += 1
|
||||
if self.query_calls == 1:
|
||||
return {
|
||||
"ok": False,
|
||||
"classification": "TMS_TECH_FAILURE",
|
||||
"has_price": False,
|
||||
"quote": {},
|
||||
}
|
||||
return super().query_tms(**kwargs)
|
||||
|
||||
land = reset_land_text_flow_for_test()
|
||||
land.ledger = Flaky()
|
||||
facts = {
|
||||
"起运港": "广州",
|
||||
"目的港": "深圳",
|
||||
"品名": "衣服",
|
||||
"体积": "100CBM",
|
||||
"毛重": "50kg",
|
||||
}
|
||||
handle_text_inquiry(
|
||||
InboundMessage(sender_id="ld5", message_id="m-l5a", content="陆运"),
|
||||
injected_facts=facts,
|
||||
injected_mode="LAND",
|
||||
store=self.store,
|
||||
)
|
||||
handle_text_inquiry(
|
||||
InboundMessage(
|
||||
sender_id="ld5",
|
||||
message_id="m-l5b",
|
||||
content="国内运输拼车 国内长途/零担 急件",
|
||||
),
|
||||
injected_facts=facts,
|
||||
injected_mode="LAND",
|
||||
store=self.store,
|
||||
)
|
||||
handle_text_inquiry(
|
||||
InboundMessage(sender_id="ld5", message_id="m-l5c", content="确定"),
|
||||
store=self.store,
|
||||
)
|
||||
self.assertEqual(get_land_text_flow().session_of("ld5").phase, "tms_tech")
|
||||
wo = get_land_text_flow().session_of("ld5").work_order_no
|
||||
name = dispatch_inbound(
|
||||
InboundMessage(sender_id="ld5", message_id="m-l5d", content="确定"),
|
||||
RouteDecision(intent="ordinary_text_other"),
|
||||
)
|
||||
self.assertEqual(name, "ordinary_text_inquiry")
|
||||
sess = get_land_text_flow().session_of("ld5")
|
||||
self.assertEqual(sess.phase, "wait_collab")
|
||||
self.assertEqual(sess.work_order_no, wo)
|
||||
last = list(self.store._outbox.values())[-1].content
|
||||
self.assertNotIn("你好,我是询价机器人", last)
|
||||
|
||||
def test_sea_still_creates_immediately(self) -> None:
|
||||
msg = InboundMessage(sender_id="s-sea", message_id="m-sea-keep", content="海运询价")
|
||||
phase = handle_text_inquiry(
|
||||
msg,
|
||||
injected_facts=COMPLETE_SEA,
|
||||
injected_mode="SEA",
|
||||
store=self.store,
|
||||
)
|
||||
self.assertEqual(phase, "wait_collab")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user