feat(web): AI 工作台意图规划与规划思考模型

- 新增 workbenchAiIntentPlannerModel,基于 LLM function_call 解析建单/草稿/提交意图,区分 model 与 rule_fallback 来源
- 新增 workbenchAiPlanningThinkingModel 合并规划思考事件流,按 eventId 去重合并
- application gate/preview 模型接入意图规划,usePersonalWorkbenchAiMode/useWorkbenchAiStewardFlow/useWorkbenchAiActionRouter 链路适配,支持上下文提交
- steward 服务与 stewardPlanModel 适配新动作结构,receipt-folder-view 微调样式
- 新增 intent-planner-model/application-context-submit/steward-actions-service 测试,更新 gate-model/action-router/plan-message-copy/fast-preview 测试
This commit is contained in:
caoxiaozhu
2026-06-24 21:58:46 +08:00
parent 5311c99d69
commit bc560145a4
18 changed files with 1914 additions and 38 deletions

View File

@@ -1,6 +1,7 @@
import {
buildApplicationTemplatePreview,
buildLocalApplicationPreview,
buildModelRefinedApplicationPreview,
normalizeApplicationPreview
} from '../../utils/expenseApplicationPreview.js'
import {
@@ -187,11 +188,74 @@ export function normalizeInlineApplicationTypeLabel(expenseTypeLabel, fallback =
return `${label}申请`
}
export function buildInlineApplicationPreview(expenseTypeLabel, sourceText = '', currentUser = {}) {
function buildOntologyTimeRangeFromCanonical(value = '') {
const raw = String(value || '').trim()
if (!raw) {
return {}
}
const normalized = raw.replace(/\s+/g, ' ')
const [startDate, endDate] = normalized
.split(/\s*(?:至|到|~|--|—)\s*/)
.map((item) => item.trim())
.filter(Boolean)
if (/^20\d{2}-\d{1,2}-\d{1,2}$/.test(startDate || '')) {
return {
raw,
start_date: startDate,
end_date: /^20\d{2}-\d{1,2}-\d{1,2}$/.test(endDate || '') ? endDate : startDate
}
}
return { raw }
}
function buildEntityFromCanonicalField(type, value) {
const normalizedValue = String(value || '').trim()
if (!type || !normalizedValue) {
return null
}
return {
type,
value: normalizedValue,
normalized_value: normalizedValue
}
}
function buildApplicationOntologyFromCanonicalFields(fields = {}) {
const source = fields && typeof fields === 'object' ? fields : {}
const entityKeys = [
'expense_type',
'location',
'reason',
'amount',
'transport_mode',
'customer_name',
'merchant_name',
'department_name',
'employee_name',
'employee_no'
]
return {
parse_strategy: 'llm_primary',
time_range: buildOntologyTimeRangeFromCanonical(source.time_range),
entities: entityKeys
.map((key) => buildEntityFromCanonicalField(key, source[key]))
.filter(Boolean)
}
}
export function buildInlineApplicationPreview(expenseTypeLabel, sourceText = '', currentUser = {}, options = {}) {
const rawText = String(sourceText || '').trim()
const preview = rawText
const basePreview = rawText
? buildLocalApplicationPreview(rawText, currentUser)
: buildApplicationTemplatePreview(currentUser)
const preview = options.ontologyFields && typeof options.ontologyFields === 'object'
? buildModelRefinedApplicationPreview(
basePreview,
buildApplicationOntologyFromCanonicalFields(options.ontologyFields),
rawText,
currentUser
)
: basePreview
const normalized = normalizeApplicationPreview(preview)
return normalizeApplicationPreview({
...normalized,