408 lines
21 KiB
Python
408 lines
21 KiB
Python
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from app.services.analysis_packet_builder import build_analysis_packet
|
||
from app.services.integrations.feishu_integration_service.mixin_receive_routing import (
|
||
FeishuReceiveRoutingMixin,
|
||
)
|
||
|
||
|
||
def _render_text(packet: dict[str, object]) -> str:
|
||
return FeishuReceiveRoutingMixin._build_feishu_qa_analysis_packet_reply_text(packet)
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("case_name", "input_payload", "expected_fragments", "forbidden_fragments"),
|
||
[
|
||
(
|
||
"project_fit_inventory_responses_api",
|
||
{
|
||
"route_decision": {"route_mode": "external", "effective_source_policy": "external"},
|
||
"execution_recipe": {
|
||
"primary_lane": "external",
|
||
"fit_brief_required": True,
|
||
"external_evidence_required": True,
|
||
"plan_handoff_mode": "offer_after_answer",
|
||
},
|
||
"fit_brief": {"project_summary": "采购经理每日实时库存上报服务"},
|
||
"external_recommendation": {
|
||
"retrieval_status": "evidence_found",
|
||
"impact_status": "indirect_only",
|
||
},
|
||
"recommended_next_step": "先确认它在当前项目里具体承接哪个节点。",
|
||
"project_snapshot": {
|
||
"project_name": "采购经理每日实时库存上报服务",
|
||
"tasks": [
|
||
{
|
||
"id": 1,
|
||
"title": "每日固定时段生成并推送结构化摘要卡片",
|
||
"description": "每日凌晨触发全量计算,生成含标题、总水位摘要、TOP10表格的结构化文本卡片,并在上午10点前推送。",
|
||
},
|
||
{
|
||
"id": 2,
|
||
"title": "WMS库存快照自动拉取与清洗",
|
||
"description": "从WMS系统每小时拉取库存快照,按可用/在途/冻结三态聚合,清洗异常值后写入中台临时表。",
|
||
},
|
||
],
|
||
"signals": [
|
||
{
|
||
"id": 11,
|
||
"title": "外部信号源 - WMS实时库存接口",
|
||
"summary": "提供各仓库每小时更新的库存快照,作为水位与缺货计算的原始数据源。",
|
||
}
|
||
],
|
||
"analysis_object_refs": {
|
||
"fit_candidate_task_ref": {"kind": "task", "id": 1, "title": "每日固定时段生成并推送结构化摘要卡片"},
|
||
"fit_rejected_task_ref": {"kind": "task", "id": 2, "title": "WMS库存快照自动拉取与清洗"},
|
||
"primary_anchor_signal_ref": {"kind": "signal", "id": 11, "title": "外部信号源 - WMS实时库存接口"},
|
||
"primary_dependent_task_ref": {"kind": "task", "id": 2, "title": "WMS库存快照自动拉取与清洗"},
|
||
},
|
||
},
|
||
},
|
||
[
|
||
"每日固定时段生成并推送结构化摘要卡片",
|
||
"WMS库存快照自动拉取与清洗",
|
||
"先在这里试更稳",
|
||
"先定清楚它是给每日固定时段生成并推送结构化摘要卡片补说明",
|
||
],
|
||
["当前项目:", "先说我的判断", "到底承担什么角色"],
|
||
),
|
||
(
|
||
"project_fit_generic_titles_prefers_explicit_refs",
|
||
{
|
||
"route_decision": {"route_mode": "external", "effective_source_policy": "external"},
|
||
"execution_recipe": {
|
||
"primary_lane": "external",
|
||
"fit_brief_required": True,
|
||
"external_evidence_required": True,
|
||
"plan_handoff_mode": "offer_after_answer",
|
||
},
|
||
"fit_brief": {"project_summary": "库存异常播报助手"},
|
||
"external_recommendation": {
|
||
"retrieval_status": "evidence_found",
|
||
"impact_status": "indirect_only",
|
||
},
|
||
"recommended_next_step": "先确认它在项目里具体承接哪个节点。",
|
||
"project_snapshot": {
|
||
"project_name": "库存异常播报助手",
|
||
"tasks": [
|
||
{
|
||
"id": 21,
|
||
"title": "任务A",
|
||
"description": "每 15 分钟从仓储中台拉取异常库存快照,清洗后写入临时表供后续核算使用。",
|
||
},
|
||
{
|
||
"id": 22,
|
||
"title": "任务B",
|
||
"description": "根据前序结果生成发给值班同学的异常播报文本,并支持继续追问异常原因。",
|
||
},
|
||
],
|
||
"analysis_object_refs": {
|
||
"fit_candidate_task_ref": {"kind": "task", "id": 22, "title": "任务B"},
|
||
"fit_rejected_task_ref": {"kind": "task", "id": 21, "title": "任务A"},
|
||
},
|
||
},
|
||
},
|
||
["任务B", "任务A", "异常播报文本", "先在这里试更稳"],
|
||
["status_summary", "外部推荐"],
|
||
),
|
||
(
|
||
"project_fit_direct_match_inventory",
|
||
{
|
||
"route_decision": {"route_mode": "external", "effective_source_policy": "external"},
|
||
"execution_recipe": {
|
||
"primary_lane": "external",
|
||
"fit_brief_required": True,
|
||
"external_evidence_required": True,
|
||
"plan_handoff_mode": "offer_after_answer",
|
||
},
|
||
"fit_brief": {"project_summary": "采购经理每日实时库存上报服务"},
|
||
"external_recommendation": {
|
||
"retrieval_status": "evidence_found",
|
||
"impact_status": "direct_match",
|
||
"affected_signal_sources": ["外部信号源 - WMS实时库存接口"],
|
||
},
|
||
"recommended_next_step": "直接看它会影响哪条下游任务,再决定要不要进 plan。",
|
||
"project_snapshot": {
|
||
"project_name": "采购经理每日实时库存上报服务",
|
||
"tasks": [
|
||
{
|
||
"id": 31,
|
||
"title": "WMS库存快照自动拉取与清洗",
|
||
"description": "从WMS系统每小时拉取库存快照,按可用/在途/冻结三态聚合,清洗异常值后写入中台临时表。",
|
||
}
|
||
],
|
||
"signals": [
|
||
{
|
||
"id": 41,
|
||
"title": "外部信号源 - WMS实时库存接口",
|
||
"summary": "提供各仓库每小时更新的库存快照,作为水位与缺货计算的原始数据源。",
|
||
}
|
||
],
|
||
"analysis_object_refs": {
|
||
"primary_anchor_signal_ref": {"kind": "signal", "id": 41, "title": "外部信号源 - WMS实时库存接口"},
|
||
"primary_dependent_task_ref": {"kind": "task", "id": 31, "title": "WMS库存快照自动拉取与清洗"},
|
||
},
|
||
},
|
||
},
|
||
["WMS库存快照自动拉取与清洗", "外部信号源 - WMS实时库存接口"],
|
||
["当前项目:", "外部判断:"],
|
||
),
|
||
(
|
||
"external_verifier_blocked_inventory",
|
||
{
|
||
"route_decision": {"route_mode": "external", "effective_source_policy": "external"},
|
||
"external_recommendation": {
|
||
"retrieval_status": "verifier_blocked",
|
||
"impact_status": "no_direct_impact",
|
||
"external_action": "先修正证据质量,再决定是否继续外部推荐。",
|
||
},
|
||
"recommended_next_step": "先修正证据质量,再决定是否继续外部推荐。",
|
||
"project_snapshot": {
|
||
"project_name": "Alpha",
|
||
"tasks": [
|
||
{
|
||
"id": 51,
|
||
"title": "每日固定时段生成并推送结构化摘要卡片",
|
||
"description": "每日凌晨触发全量计算,生成结构化文本卡片,并在上午10点前推送。",
|
||
}
|
||
],
|
||
"signals": [
|
||
{
|
||
"id": 61,
|
||
"title": "外部信号源 - WMS实时库存接口",
|
||
"summary": "提供各仓库每小时更新的库存快照,作为水位与缺货计算的原始数据源。",
|
||
}
|
||
],
|
||
"analysis_object_refs": {
|
||
"primary_anchor_signal_ref": {"kind": "signal", "id": 61, "title": "外部信号源 - WMS实时库存接口"},
|
||
"primary_dependent_task_ref": {"kind": "task", "id": 51, "title": "每日固定时段生成并推送结构化摘要卡片"},
|
||
},
|
||
},
|
||
},
|
||
["外部信号源 - WMS实时库存接口", "每日固定时段生成并推送结构化摘要卡片", "核实清楚"],
|
||
["外部推荐", "证据质量"],
|
||
),
|
||
(
|
||
"external_direct_match_inventory",
|
||
{
|
||
"route_decision": {"route_mode": "external", "effective_source_policy": "external"},
|
||
"external_recommendation": {
|
||
"retrieval_status": "evidence_found",
|
||
"impact_status": "direct_match",
|
||
"affected_signal_sources": ["外部信号源 - WMS实时库存接口"],
|
||
},
|
||
"recommended_next_step": "直接看它会影响哪条下游任务,再决定要不要进 plan。",
|
||
"project_snapshot": {
|
||
"project_name": "采购经理每日实时库存上报服务",
|
||
"tasks": [
|
||
{
|
||
"id": 71,
|
||
"title": "WMS库存快照自动拉取与清洗",
|
||
"description": "从WMS系统每小时拉取库存快照,按可用/在途/冻结三态聚合,清洗异常值后写入中台临时表。",
|
||
}
|
||
],
|
||
"signals": [
|
||
{
|
||
"id": 72,
|
||
"title": "外部信号源 - WMS实时库存接口",
|
||
"summary": "提供各仓库每小时更新的库存快照,作为水位与缺货计算的原始数据源。",
|
||
}
|
||
],
|
||
"analysis_object_refs": {
|
||
"primary_anchor_signal_ref": {"kind": "signal", "id": 72, "title": "外部信号源 - WMS实时库存接口"},
|
||
"primary_dependent_task_ref": {"kind": "task", "id": 71, "title": "WMS库存快照自动拉取与清洗"},
|
||
},
|
||
},
|
||
},
|
||
["WMS库存快照自动拉取与清洗", "能对上的外部来源是外部信号源 - WMS实时库存接口", "不是泛泛相关"],
|
||
["status_summary", "当前项目:"],
|
||
),
|
||
(
|
||
"external_indirect_only_prefers_explanation_layer",
|
||
{
|
||
"route_decision": {"route_mode": "external", "effective_source_policy": "external"},
|
||
"external_recommendation": {
|
||
"retrieval_status": "evidence_found",
|
||
"impact_status": "indirect_only",
|
||
},
|
||
"recommended_next_step": "先确认它是不是只用在摘要卡片之后。",
|
||
"project_snapshot": {
|
||
"project_name": "采购经理每日实时库存上报服务",
|
||
"tasks": [
|
||
{
|
||
"id": 81,
|
||
"title": "每日固定时段生成并推送结构化摘要卡片",
|
||
"description": "每日凌晨触发全量计算,生成结构化文本卡片,并在上午10点前推送。",
|
||
},
|
||
{
|
||
"id": 82,
|
||
"title": "WMS库存快照自动拉取与清洗",
|
||
"description": "从WMS系统每小时拉取库存快照并写入中台临时表。",
|
||
},
|
||
],
|
||
"analysis_object_refs": {
|
||
"fit_candidate_task_ref": {"kind": "task", "id": 81, "title": "每日固定时段生成并推送结构化摘要卡片"},
|
||
"fit_rejected_task_ref": {"kind": "task", "id": 82, "title": "WMS库存快照自动拉取与清洗"},
|
||
},
|
||
},
|
||
},
|
||
["背景相关", "每日固定时段生成并推送结构化摘要卡片", "WMS库存快照自动拉取与清洗"],
|
||
["先说我的判断", "外部判断:"],
|
||
),
|
||
(
|
||
"internal_status_model_router",
|
||
{
|
||
"route_decision": {"route_mode": "internal", "effective_source_policy": "internal"},
|
||
"answer": "当前活动引擎是 Qwen 低阶引擎。",
|
||
"query_type": "status",
|
||
"evidence_pack": {"summary": {"conclusion": "当前活动引擎是 Qwen 低阶引擎。"}},
|
||
"internal_context_packet": {
|
||
"operation_contract": {"query_type": "status", "answer_contract": "status_summary"},
|
||
"target_resolution": {"primary_target_label": "模型路由配置"},
|
||
"query_plan": {"answer_contract": "status_summary"},
|
||
},
|
||
},
|
||
["当前活动引擎是 Qwen 低阶引擎", "模型路由配置", "当前情况回答"],
|
||
["status_summary", "泛化建议"],
|
||
),
|
||
(
|
||
"internal_status_with_uncertainty",
|
||
{
|
||
"route_decision": {"route_mode": "internal", "effective_source_policy": "internal"},
|
||
"answer": "当前活动引擎是 Qwen 低阶引擎。",
|
||
"query_type": "status",
|
||
"evidence_pack": {
|
||
"summary": {
|
||
"conclusion": "当前活动引擎是 Qwen 低阶引擎。",
|
||
"uncertainties": ["尚未确认是否已经切到最新灰度配置。"],
|
||
}
|
||
},
|
||
"internal_context_packet": {
|
||
"operation_contract": {"query_type": "status", "answer_contract": "status_summary"},
|
||
"target_resolution": {"primary_target_label": "模型路由配置"},
|
||
"query_plan": {"answer_contract": "status_summary"},
|
||
},
|
||
},
|
||
["当前活动引擎是 Qwen 低阶引擎", "模型路由配置", "先不展开方案"],
|
||
["status_summary", "contract"],
|
||
),
|
||
(
|
||
"followup_route_clarify",
|
||
{
|
||
"route_decision": {"route_mode": "uncertain", "effective_source_policy": "uncertain"},
|
||
"answer": "你是想问外部公开事实,还是系统内部配置/项目对象?",
|
||
"clarification_code": "query_route_clarify",
|
||
"anchor_kind": "reply_thread",
|
||
"meta_followup_kind": "route_selection",
|
||
"evidence_pack": {
|
||
"summary": {
|
||
"conclusion": "需要先确认查询来源。",
|
||
"uncertainties": ["内部还是外部"],
|
||
}
|
||
},
|
||
},
|
||
["需要先确认查询来源"],
|
||
["当前项目:", "status_summary"],
|
||
),
|
||
(
|
||
"feishu_render_fit_indirect",
|
||
{
|
||
"route_decision": {"route_mode": "external", "effective_source_policy": "external"},
|
||
"execution_recipe": {
|
||
"primary_lane": "external",
|
||
"fit_brief_required": True,
|
||
"plan_handoff_mode": "offer_after_answer",
|
||
},
|
||
"fit_brief": {"project_summary": "采购经理每日实时库存上报服务"},
|
||
"external_recommendation": {"retrieval_status": "evidence_found", "impact_status": "indirect_only"},
|
||
"recommended_next_step": "先确认它是不是只用在摘要卡片之后。",
|
||
"project_snapshot": {
|
||
"project_name": "采购经理每日实时库存上报服务",
|
||
"tasks": [
|
||
{
|
||
"id": 91,
|
||
"title": "每日固定时段生成并推送结构化摘要卡片",
|
||
"description": "每日凌晨触发全量计算,生成结构化文本卡片,并在上午10点前推送。",
|
||
},
|
||
{
|
||
"id": 92,
|
||
"title": "WMS库存快照自动拉取与清洗",
|
||
"description": "从WMS系统每小时拉取库存快照并写入中台临时表。",
|
||
},
|
||
],
|
||
"analysis_object_refs": {
|
||
"fit_candidate_task_ref": {"kind": "task", "id": 91, "title": "每日固定时段生成并推送结构化摘要卡片"},
|
||
"fit_rejected_task_ref": {"kind": "task", "id": 92, "title": "WMS库存快照自动拉取与清洗"},
|
||
},
|
||
},
|
||
},
|
||
["每日固定时段生成并推送结构化摘要卡片", "WMS库存快照自动拉取与清洗"],
|
||
["当前项目:", "先说我的判断"],
|
||
),
|
||
(
|
||
"feishu_render_verifier_blocked",
|
||
{
|
||
"route_decision": {"route_mode": "external", "effective_source_policy": "external"},
|
||
"external_recommendation": {"retrieval_status": "verifier_blocked", "impact_status": "no_direct_impact"},
|
||
"recommended_next_step": "先修正证据质量,再决定是否继续外部推荐。",
|
||
"project_snapshot": {
|
||
"project_name": "Alpha",
|
||
"tasks": [
|
||
{
|
||
"id": 101,
|
||
"title": "每日固定时段生成并推送结构化摘要卡片",
|
||
"description": "每日凌晨触发全量计算,生成结构化文本卡片,并在上午10点前推送。",
|
||
}
|
||
],
|
||
"signals": [
|
||
{
|
||
"id": 102,
|
||
"title": "外部信号源 - WMS实时库存接口",
|
||
"summary": "提供各仓库每小时更新的库存快照,作为水位与缺货计算的原始数据源。",
|
||
}
|
||
],
|
||
"analysis_object_refs": {
|
||
"primary_anchor_signal_ref": {"kind": "signal", "id": 102, "title": "外部信号源 - WMS实时库存接口"},
|
||
"primary_dependent_task_ref": {"kind": "task", "id": 101, "title": "每日固定时段生成并推送结构化摘要卡片"},
|
||
},
|
||
},
|
||
},
|
||
["核实清楚", "每日固定时段生成并推送结构化摘要卡片"],
|
||
["外部推荐", "证据质量"],
|
||
),
|
||
(
|
||
"feishu_render_internal_status",
|
||
{
|
||
"route_decision": {"route_mode": "internal", "effective_source_policy": "internal"},
|
||
"answer": "当前活动引擎是 Qwen 低阶引擎。",
|
||
"query_type": "status",
|
||
"evidence_pack": {"summary": {"conclusion": "当前活动引擎是 Qwen 低阶引擎。"}},
|
||
"internal_context_packet": {
|
||
"operation_contract": {"query_type": "status", "answer_contract": "status_summary"},
|
||
"target_resolution": {"primary_target_label": "模型路由配置"},
|
||
"query_plan": {"answer_contract": "status_summary"},
|
||
},
|
||
},
|
||
["当前活动引擎是 Qwen 低阶引擎", "模型路由配置"],
|
||
["status_summary", "泛化建议"],
|
||
),
|
||
],
|
||
)
|
||
def test_analysis_packet_golden_v2_cases(
|
||
case_name: str,
|
||
input_payload: dict[str, object],
|
||
expected_fragments: list[str],
|
||
forbidden_fragments: list[str],
|
||
) -> None:
|
||
_ = case_name
|
||
packet = build_analysis_packet(**input_payload)
|
||
assert packet["packet_type"]
|
||
rendered = _render_text(packet)
|
||
assert rendered
|
||
for fragment in expected_fragments:
|
||
assert fragment in rendered
|
||
for fragment in forbidden_fragments:
|
||
assert fragment not in rendered
|