- assistantSessionScope.js:新增 ASSISTANT_SCOPE_ACTION_FILL_COMPOSER 常量 - assistantSuggestedActionPrefill.js:识别 fill_composer 与 payload.fill_text - stewardPlanModel.js:normalizeStewardPlan 透传 suggestedPrompts; buildStewardPlanMessageText / buildStewardSuggestedActions 新增 off_topic 分支,按钮填充输入框不提交 - useStewardPlanFlow.js:isPendingStewardActionMessage 排除 off_topic - steward-plan-off-topic.test.mjs:覆盖 normalize/文案/按钮/兼容路径
181 lines
6.3 KiB
JavaScript
181 lines
6.3 KiB
JavaScript
import assert from 'node:assert/strict'
|
||
import test from 'node:test'
|
||
|
||
import {
|
||
ASSISTANT_SCOPE_ACTION_FILL_COMPOSER
|
||
} from '../src/utils/assistantSessionScope.js'
|
||
import {
|
||
buildStewardPlanMessageText,
|
||
buildStewardSuggestedActions,
|
||
isOffTopicStewardPlan,
|
||
normalizeStewardPlan
|
||
} from '../src/views/scripts/stewardPlanModel.js'
|
||
import {
|
||
resolveSuggestedActionPrefill
|
||
} from '../src/utils/assistantSuggestedActionPrefill.js'
|
||
|
||
const OFF_TOPIC_PLAN = {
|
||
plan_id: 'steward-plan-off-topic',
|
||
plan_status: 'off_topic',
|
||
next_action: 'none',
|
||
tasks: [],
|
||
attachment_groups: [],
|
||
confirmation_groups: [],
|
||
candidate_flows: [],
|
||
summary: '这看起来跟财务任务没什么关系...',
|
||
suggested_prompts: [
|
||
'我想要申请明天去北京出差3天,支撑客户现场实施',
|
||
'我要报销上周去上海的高铁票',
|
||
'差旅住宿标准是多少'
|
||
],
|
||
thinking_events: [
|
||
{
|
||
event_id: 'off-topic-explain',
|
||
stage: 'intent_review',
|
||
title: '未识别到财务业务意图',
|
||
content: '用户输入的内容没有匹配到费用申请或费用报销相关的业务信号。',
|
||
status: 'completed'
|
||
}
|
||
]
|
||
}
|
||
|
||
test('normalizeStewardPlan passes suggested_prompts through as trimmed strings', () => {
|
||
const normalized = normalizeStewardPlan({
|
||
plan_id: 'p-1',
|
||
plan_status: 'off_topic',
|
||
suggested_prompts: [
|
||
' 申请出差 ',
|
||
'',
|
||
null,
|
||
'报销高铁票'
|
||
]
|
||
})
|
||
assert.deepEqual(normalized.suggestedPrompts, ['申请出差', '报销高铁票'])
|
||
})
|
||
|
||
test('normalizeStewardPlan falls back to empty suggested prompts when missing', () => {
|
||
const normalized = normalizeStewardPlan({ plan_id: 'p-2', plan_status: 'off_topic' })
|
||
assert.deepEqual(normalized.suggestedPrompts, [])
|
||
})
|
||
|
||
test('isOffTopicStewardPlan returns true only for off_topic plan status', () => {
|
||
assert.equal(isOffTopicStewardPlan({ plan_status: 'off_topic' }), true)
|
||
assert.equal(isOffTopicStewardPlan({ plan_status: 'needs_flow_confirmation' }), false)
|
||
assert.equal(isOffTopicStewardPlan({}), false)
|
||
})
|
||
|
||
test('buildStewardPlanMessageText renders friendly off_topic guidance', () => {
|
||
const text = buildStewardPlanMessageText(OFF_TOPIC_PLAN)
|
||
assert.match(text, /小财管家没看懂这件事/)
|
||
// 推荐话术本身不在正文里展示,而是作为按钮单独渲染,避免重复。
|
||
for (const prompt of OFF_TOPIC_PLAN.suggested_prompts) {
|
||
assert.equal(text.includes(prompt), false, `正文不应包含推荐话术:${prompt}`)
|
||
}
|
||
})
|
||
|
||
test('buildStewardPlanMessageText keeps off_topic branch ahead of pending flow branch', () => {
|
||
// 即使 summary 缺省,也走 off_topic 分支而非默认任务文案
|
||
const text = buildStewardPlanMessageText({
|
||
plan_id: 'p-off-topic-default',
|
||
plan_status: 'off_topic',
|
||
next_action: 'none',
|
||
suggested_prompts: ['申请出差']
|
||
})
|
||
assert.match(text, /小财管家没看懂这件事/)
|
||
assert.match(text, /费用申请.*费用报销|费用报销.*费用申请/)
|
||
})
|
||
|
||
test('buildStewardSuggestedActions returns fill_composer actions for off_topic plan', () => {
|
||
const actions = buildStewardSuggestedActions(OFF_TOPIC_PLAN)
|
||
assert.equal(actions.length, OFF_TOPIC_PLAN.suggested_prompts.length)
|
||
for (const action of actions) {
|
||
assert.equal(action.action_type, ASSISTANT_SCOPE_ACTION_FILL_COMPOSER)
|
||
assert.equal(typeof action.payload.fill_text, 'string')
|
||
assert.ok(action.payload.fill_text.length > 0)
|
||
assert.equal(action.payload.steward_plan_id, OFF_TOPIC_PLAN.plan_id)
|
||
}
|
||
})
|
||
|
||
test('buildStewardSuggestedActions truncates long off_topic prompt labels', () => {
|
||
const longPrompt = '我想要申请明天去北京出差3天,支撑客户现场实施,需要预订酒店和高铁票'
|
||
const actions = buildStewardSuggestedActions({
|
||
plan_id: 'p-long',
|
||
plan_status: 'off_topic',
|
||
suggested_prompts: [longPrompt]
|
||
})
|
||
assert.equal(actions.length, 1)
|
||
assert.ok(actions[0].label.length <= 27) // 24 字符 + "..."
|
||
assert.ok(actions[0].label.endsWith('...'))
|
||
// payload.fill_text 必须保留完整话术,不被截断
|
||
assert.equal(actions[0].payload.fill_text, longPrompt)
|
||
})
|
||
|
||
test('buildStewardSuggestedActions keeps short off_topic prompt labels intact', () => {
|
||
const shortPrompt = '差旅住宿标准是多少'
|
||
const actions = buildStewardSuggestedActions({
|
||
plan_id: 'p-short',
|
||
plan_status: 'off_topic',
|
||
suggested_prompts: [shortPrompt]
|
||
})
|
||
assert.equal(actions[0].label, shortPrompt)
|
||
assert.equal(actions[0].payload.fill_text, shortPrompt)
|
||
})
|
||
|
||
test('buildStewardSuggestedActions returns empty array for off_topic plan without prompts', () => {
|
||
const actions = buildStewardSuggestedActions({
|
||
plan_id: 'p-empty',
|
||
plan_status: 'off_topic',
|
||
suggested_prompts: []
|
||
})
|
||
assert.deepEqual(actions, [])
|
||
})
|
||
|
||
test('off_topic fill_composer action is resolved as composer prefill (fill not submit)', () => {
|
||
const actions = buildStewardSuggestedActions(OFF_TOPIC_PLAN)
|
||
const firstAction = actions[0]
|
||
const prefill = resolveSuggestedActionPrefill(firstAction)
|
||
assert.equal(prefill, OFF_TOPIC_PLAN.suggested_prompts[0])
|
||
})
|
||
|
||
test('resolveSuggestedActionPrefill reads payload.fill_text directly', () => {
|
||
assert.equal(
|
||
resolveSuggestedActionPrefill({
|
||
action_type: ASSISTANT_SCOPE_ACTION_FILL_COMPOSER,
|
||
payload: { fill_text: '帮我申请下周出差' }
|
||
}),
|
||
'帮我申请下周出差'
|
||
)
|
||
// 空字符串/空白应被忽略
|
||
assert.equal(
|
||
resolveSuggestedActionPrefill({
|
||
action_type: ASSISTANT_SCOPE_ACTION_FILL_COMPOSER,
|
||
payload: { fill_text: ' ' }
|
||
}),
|
||
''
|
||
)
|
||
})
|
||
|
||
test('off_topic branch does not break pending flow confirmation actions', () => {
|
||
// pending flow 不应被 off_topic 分支拦截
|
||
const actions = buildStewardSuggestedActions({
|
||
plan_id: 'steward-plan-pending-flow',
|
||
plan_status: 'needs_flow_confirmation',
|
||
next_action: 'confirm_flow',
|
||
pending_flow_confirmation: {
|
||
status: 'pending',
|
||
reason: '缺少申请或报销动作词。',
|
||
candidate_flows: [
|
||
{
|
||
flow_id: 'travel_application',
|
||
label: '补办出差申请',
|
||
confidence: 0.6,
|
||
ontology_fields: { location: '上海' },
|
||
missing_fields: []
|
||
}
|
||
]
|
||
}
|
||
})
|
||
assert.equal(actions.length, 1)
|
||
assert.equal(actions[0].payload.flow_id, 'travel_application')
|
||
})
|