- useWorkbenchAiApplicationPreviewFlow/useWorkbenchAiActionRouter/useWorkbenchAiCommandIntents 支持 task1 完成后自动推进 task2,确认按钮直接拉起申请预览,草稿/提交成功后继续推进下一 task - workbenchAiIntentPlannerModel/workbenchAiMessageModel/workbenchAiCommandIntentModel 适配多 task 意图规划与消息结构 - aiApplicationPreviewActions/aiApplicationPrecheckModel/aiExpenseDraftModel/aiWorkbenchConversationStore 草稿与会话存储适配 - PersonalWorkbenchAiMode 与样式适配,更新 preview-actions/expense-draft/conversation-store/fast-preview/action-router/command-intent/intent-planner 测试
144 lines
5.8 KiB
JavaScript
144 lines
5.8 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { readFileSync } from 'node:fs'
|
|
import test from 'node:test'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
import {
|
|
buildWorkbenchDocumentCommandFollowupGuidance,
|
|
buildWorkbenchDraftDeletionGuidance,
|
|
isWorkbenchDraftDeletionIntent,
|
|
resolveLatestWorkbenchDocumentCommandContext,
|
|
resolveLatestWorkbenchDraftPayload
|
|
} from '../src/composables/workbenchAiMode/workbenchAiCommandIntentModel.js'
|
|
|
|
const personalWorkbenchAiModeScript = readFileSync(
|
|
fileURLToPath(new URL('../src/composables/workbenchAiMode/usePersonalWorkbenchAiMode.js', import.meta.url)),
|
|
'utf8'
|
|
)
|
|
const commandIntentsScript = readFileSync(
|
|
fileURLToPath(new URL('../src/composables/workbenchAiMode/useWorkbenchAiCommandIntents.js', import.meta.url)),
|
|
'utf8'
|
|
)
|
|
|
|
test('workbench command intent detects draft deletion phrases without broad delete matching', () => {
|
|
assert.equal(isWorkbenchDraftDeletionIntent('删除草稿'), true)
|
|
assert.equal(isWorkbenchDraftDeletionIntent('把刚才保存的草稿删掉'), true)
|
|
assert.equal(isWorkbenchDraftDeletionIntent('删除这个申请单'), true)
|
|
assert.equal(isWorkbenchDraftDeletionIntent('删除附件'), false)
|
|
assert.equal(isWorkbenchDraftDeletionIntent('草稿还要补什么'), false)
|
|
})
|
|
|
|
test('workbench command intent resolves latest draft payload from conversation context', () => {
|
|
const payload = resolveLatestWorkbenchDraftPayload([
|
|
{
|
|
role: 'assistant',
|
|
draftPayload: {
|
|
claim_id: 'old-draft',
|
|
claim_no: 'AOLD001',
|
|
status: 'draft'
|
|
}
|
|
},
|
|
{
|
|
role: 'assistant',
|
|
suggestedActions: [{
|
|
action_type: 'open_application_detail',
|
|
payload: {
|
|
claim_id: 'latest-draft',
|
|
claim_no: 'ALATEST1',
|
|
status: 'draft'
|
|
}
|
|
}]
|
|
}
|
|
])
|
|
|
|
assert.deepEqual(payload, {
|
|
claimId: 'latest-draft',
|
|
claimNo: 'ALATEST1',
|
|
status: 'draft',
|
|
documentType: 'application'
|
|
})
|
|
})
|
|
|
|
test('workbench command intent skips submitted documents when deleting draft', () => {
|
|
const payload = resolveLatestWorkbenchDraftPayload([
|
|
{
|
|
role: 'assistant',
|
|
draftPayload: {
|
|
claim_id: 'submitted-application',
|
|
claim_no: 'ASUBMIT1',
|
|
status: 'submitted'
|
|
}
|
|
}
|
|
])
|
|
|
|
assert.equal(payload, null)
|
|
})
|
|
|
|
test('workbench draft deletion guidance opens detail instead of deleting directly', () => {
|
|
const guidance = buildWorkbenchDraftDeletionGuidance({
|
|
claimId: 'latest-draft',
|
|
claimNo: 'ALATEST1',
|
|
documentType: 'application'
|
|
})
|
|
|
|
assert.match(guidance.content, /已识别到您想删除草稿/)
|
|
assert.match(guidance.content, /不会直接替您删除/)
|
|
assert.equal(guidance.suggestedActions.length, 1)
|
|
assert.equal(guidance.suggestedActions[0].action_type, 'open_application_detail')
|
|
assert.equal(guidance.suggestedActions[0].payload.claim_id, 'latest-draft')
|
|
assert.equal(guidance.suggestedActions[0].payload.claim_no, 'ALATEST1')
|
|
})
|
|
|
|
test('workbench command intent reuses previous approval candidates for follow-up approval command', () => {
|
|
const context = resolveLatestWorkbenchDocumentCommandContext([
|
|
{
|
|
role: 'assistant',
|
|
content: [
|
|
'### 已查询到相关单据',
|
|
'',
|
|
'<article class="ai-document-card ai-document-card--application ai-document-card--approval-task is-pending">',
|
|
'<a class="ai-document-card__action" href="#ai-open-document-detail:claim_id%3Dapproval-1%26claim_no%3DAP-APPROVAL-001">查看详情</a>',
|
|
'</article>',
|
|
'<article class="ai-document-card ai-document-card--reimbursement ai-document-card--approval-task is-pending">',
|
|
'<a class="ai-document-card__action" href="#ai-open-document-detail:claim_id%3Dapproval-2%26claim_no%3DRE-APPROVAL-002">查看详情</a>',
|
|
'</article>'
|
|
].join('\n')
|
|
}
|
|
], { action: 'approve', safetyLevel: 'confirm_required' })
|
|
|
|
assert.equal(context?.candidates.length, 2)
|
|
assert.deepEqual(context.candidates[0], {
|
|
claimId: 'approval-1',
|
|
claimNo: 'AP-APPROVAL-001',
|
|
documentType: 'application',
|
|
actionLabel: '查看详情'
|
|
})
|
|
|
|
const guidance = buildWorkbenchDocumentCommandFollowupGuidance(context, { action: 'approve' })
|
|
assert.match(guidance.content, /已接上刚才查询到的待审单据/)
|
|
assert.match(guidance.content, /AP-APPROVAL-001/)
|
|
assert.match(guidance.content, /RE-APPROVAL-002/)
|
|
assert.equal(guidance.suggestedActions.length, 2)
|
|
assert.equal(guidance.suggestedActions[0].action_type, 'open_application_detail')
|
|
assert.equal(guidance.suggestedActions[0].payload.claim_id, 'approval-1')
|
|
assert.equal(guidance.suggestedActions[0].payload.command_action, 'approve')
|
|
})
|
|
|
|
test('workbench draft deletion intent is wired before draft slot continuation', () => {
|
|
assert.match(commandIntentsScript, /isWorkbenchDraftDeletionIntent/)
|
|
assert.match(commandIntentsScript, /resolveLatestWorkbenchDocumentCommandContext/)
|
|
assert.match(commandIntentsScript, /buildWorkbenchDocumentCommandFollowupGuidance/)
|
|
assert.match(commandIntentsScript, /function handleInlineDraftDeletionIntent\(cleanPrompt, entry = \{\}\)/)
|
|
assert.match(commandIntentsScript, /resolveLatestWorkbenchDraftPayload\(conversationMessages\.value\)/)
|
|
assert.match(commandIntentsScript, /buildWorkbenchDraftDeletionGuidance\(draftPayload\)/)
|
|
assert.match(personalWorkbenchAiModeScript, /useWorkbenchAiCommandIntents/)
|
|
|
|
const startIndex = personalWorkbenchAiModeScript.indexOf('function startInlineConversation')
|
|
const startBlock = personalWorkbenchAiModeScript.slice(startIndex)
|
|
const deleteIntentIndex = startBlock.indexOf('if (commandIntents.handleInlineDraftDeletionIntent(cleanPrompt, entry))')
|
|
const draftContinuationIndex = startBlock.indexOf('if (aiExpenseDraft.value && !isAiExpenseDraftComplete(aiExpenseDraft.value))')
|
|
|
|
assert.ok(deleteIntentIndex >= 0)
|
|
assert.ok(draftContinuationIndex > deleteIntentIndex)
|
|
})
|