import { isReimbursementCreationIntent } from './workbenchAiApplicationGateModel.js' import { buildRuleFallbackWorkbenchAiIntentPlan, isLowConfidenceTravelApplicationPlan, normalizeWorkbenchAiIntentPlan, resolveExecutableTravelApplicationPlan } from './workbenchAiIntentPlannerModel.js' import { buildInitialModelPlanningThinkingEvents, buildModelPlanningProgressSchedule, mergeWorkbenchAiThinkingEvents } from './workbenchAiPlanningThinkingModel.js' export function useWorkbenchAiIntentExecution(options) { const { actionRouter, activeConversationTitle, activateInlineConversation, applicationFlow, assistantDraft, clearAiModeFiles, closeWorkbenchDatePicker, conversationId, conversationMessages, createInlineMessage, expenseFlow, inlineConversationAutoScrollPinned, persistCurrentConversation, removeWorkbenchDateTag, replaceInlineMessage, resolveInlineThinkingEvents, scrollInlineConversationToBottom, searchConversationId, sending, stewardFlow } = options function isModelPlannedReimbursementTask(modelPlan = {}) { const tasks = Array.isArray(modelPlan?.tasks) ? modelPlan.tasks : [] return tasks.some((task) => { const taskType = String(task?.task_type || task?.taskType || '').trim() const assignedAgent = String(task?.assigned_agent || task?.assignedAgent || '').trim() return taskType === 'reimbursement' || assignedAgent === 'reimbursement_assistant' }) } function updateModelPlanningThinkingEvent(messageId, event) { const message = conversationMessages.value.find((item) => item.id === messageId) if (!message) { return } const currentPlan = message.stewardPlan || {} message.stewardPlan = { ...currentPlan, streamStatus: 'streaming', thinkingEvents: mergeWorkbenchAiThinkingEvents(resolveInlineThinkingEvents(message), [event]) } persistCurrentConversation() scrollInlineConversationToBottom({ force: inlineConversationAutoScrollPinned.value }) } function startModelPlanningProgressUpdates(messageId) { const timerIds = buildModelPlanningProgressSchedule().map(({ delayMs, event }) => ( globalThis.setTimeout(() => { updateModelPlanningThinkingEvent(messageId, event) }, delayMs) )) return () => { timerIds.forEach((timerId) => globalThis.clearTimeout(timerId)) } } function startModelPlanningConversation(cleanPrompt, entry = {}) { if (conversationId.value === searchConversationId) { conversationId.value = '' conversationMessages.value = [] activeConversationTitle.value = '' } activateInlineConversation({ title: entry.label || cleanPrompt.slice(0, 18) || '新对话' }) inlineConversationAutoScrollPinned.value = true conversationMessages.value.push(createInlineMessage('user', cleanPrompt)) assistantDraft.value = '' removeWorkbenchDateTag() closeWorkbenchDatePicker() clearAiModeFiles() const pendingMessage = createInlineMessage('assistant', '正在识别意图,准备拆解申请、报销和附件任务。', { pending: true, stewardPlan: { streamStatus: 'streaming', thinkingEvents: buildInitialModelPlanningThinkingEvents() } }) conversationMessages.value.push(pendingMessage) scrollInlineConversationToBottom() persistCurrentConversation() return pendingMessage } function buildModelPlannedNextTaskAction(remainingTasks = []) { const tasks = Array.isArray(remainingTasks) ? remainingTasks : [] const nextTask = tasks[0] if (!nextTask || typeof nextTask !== 'object') { return null } const taskType = String(nextTask.task_type || nextTask.taskType || '').trim() const assignedAgent = String(nextTask.assigned_agent || nextTask.assignedAgent || '').trim() const isApplication = taskType === 'expense_application' || assignedAgent === 'application_assistant' const isReimbursement = taskType === 'reimbursement' || assignedAgent === 'reimbursement_assistant' if (!isApplication && !isReimbursement) { return null } const ontologyFields = nextTask.ontology_fields || nextTask.ontologyFields || {} const flowId = isApplication ? 'travel_application' : 'travel_reimbursement' const taskLabel = isApplication ? '出差申请' : '费用报销' return { label: `继续处理${taskLabel}`, action_type: 'steward_continue_next_task', payload: { steward_confirm_flow: true, flow_id: flowId, steward_current_task: nextTask, expense_type: String(ontologyFields.expense_type || 'travel').trim() || 'travel', expense_type_label: String(ontologyFields.expense_type_label || '差旅费').trim() || '差旅费', ontology_fields: ontologyFields, original_message: String(nextTask.summary || nextTask.title || `继续处理${taskLabel}`).trim(), steward_remaining_tasks: tasks.slice(1) } } } function startModelPlannedNextTask(remainingTasks = []) { const nextTaskAction = buildModelPlannedNextTaskAction(remainingTasks) if (nextTaskAction) { actionRouter.handleInlineSuggestedAction(nextTaskAction) } } function startModelPlannedApplicationPreview(travelApplicationRequest, plannerPendingMessage = null) { void applicationFlow.startAiApplicationPreview( travelApplicationRequest.expenseType, travelApplicationRequest.expenseTypeLabel, travelApplicationRequest.sourceText, { userMessage: travelApplicationRequest.sourceText, pushUserMessage: !plannerPendingMessage, pendingMessageId: plannerPendingMessage?.id, ontologyFields: travelApplicationRequest.ontologyFields, autoSubmit: travelApplicationRequest.autoSubmit, autoSaveDraft: travelApplicationRequest.autoSaveDraft, requestedSubmit: travelApplicationRequest.requestedSubmit, submitRequiresConfirmation: travelApplicationRequest.submitRequiresConfirmation, stewardRemainingTasks: travelApplicationRequest.stewardRemainingTasks, onPreviewReadyForNextTask: startModelPlannedNextTask, onApplicationActionCompleted: startModelPlannedNextTask } ) } function buildLowConfidenceTravelApplicationConfirmationText(request, plan) { const fields = request.ontologyFields || {} const summaryParts = [] if (fields.time_range) summaryParts.push(`时间:${fields.time_range}`) if (fields.location) summaryParts.push(`地点:${fields.location}`) if (fields.reason) summaryParts.push(`事由:${fields.reason}`) if (fields.transport_mode) summaryParts.push(`交通:${fields.transport_mode}`) const summary = summaryParts.length ? `\n\n${summaryParts.join(';')}` : '' const confidenceNote = Number.isFinite(Number(plan?.confidence)) ? `(模型识别置信度较低,约 ${Math.round(Number(plan.confidence) * 100)}%)` : '(模型识别置信度较低)' return [ '### 需要确认:您是要发起出差申请吗?', '', `小财管家把这句话理解成了“发起差旅申请”${confidenceNote},为避免误操作,先请您确认。`, summary, '', '点击下方「确认发起出差申请」即可继续;如果理解有误,请补充说明您的实际需求。' ].filter(Boolean).join('\n') } function startModelPlannedTravelApplicationConfirmation(travelApplicationRequest, plan, plannerPendingMessage) { const confirmAction = { label: '确认发起出差申请', description: '根据上面识别到的信息生成出差申请预览。', icon: 'mdi mdi-check-circle-outline', action_type: 'ai_application_confirm_intent', payload: { ontologyFields: travelApplicationRequest.ontologyFields, sourceText: travelApplicationRequest.sourceText, autoSubmit: travelApplicationRequest.autoSubmit, autoSaveDraft: travelApplicationRequest.autoSaveDraft, requestedSubmit: travelApplicationRequest.requestedSubmit, submitRequiresConfirmation: travelApplicationRequest.submitRequiresConfirmation, stewardRemainingTasks: travelApplicationRequest.stewardRemainingTasks } } replaceInlineMessage(plannerPendingMessage.id, createInlineMessage( 'assistant', buildLowConfidenceTravelApplicationConfirmationText(travelApplicationRequest, plan), { id: plannerPendingMessage.id, suggestedActions: [confirmAction], stewardPlan: { streamStatus: 'completed', thinkingEvents: resolveInlineThinkingEvents(plannerPendingMessage) .map((item) => ({ ...item, status: 'completed' })) } } )) persistCurrentConversation() scrollInlineConversationToBottom({ force: inlineConversationAutoScrollPinned.value }) } async function executeModelPlannedWorkbenchIntent(cleanPrompt, entry = {}, files = []) { let intentPlan = null let modelPlan = null const plannerPendingMessage = startModelPlanningConversation(cleanPrompt, entry) const stopPlanningProgressUpdates = startModelPlanningProgressUpdates(plannerPendingMessage.id) sending.value = true try { modelPlan = await stewardFlow.resolveInlineExecutionPlan(cleanPrompt, entry, files, { pendingMessageId: plannerPendingMessage.id }) intentPlan = normalizeWorkbenchAiIntentPlan(modelPlan, { prompt: cleanPrompt }) } catch (error) { console.warn('AI mode intent planner failed, using local fallback:', error) const ruleRequest = resolveExecutableTravelApplicationPlan( buildRuleFallbackWorkbenchAiIntentPlan(cleanPrompt) ) if (ruleRequest) { sending.value = false startModelPlannedApplicationPreview(ruleRequest, plannerPendingMessage) return } } finally { stopPlanningProgressUpdates() sending.value = false } const travelApplicationRequest = resolveExecutableTravelApplicationPlan(intentPlan) if (travelApplicationRequest) { if (isLowConfidenceTravelApplicationPlan(intentPlan)) { startModelPlannedTravelApplicationConfirmation(travelApplicationRequest, intentPlan, plannerPendingMessage) } else { startModelPlannedApplicationPreview(travelApplicationRequest, plannerPendingMessage) } return } if (isModelPlannedReimbursementTask(modelPlan) || isReimbursementCreationIntent(cleanPrompt)) { replaceInlineMessage(plannerPendingMessage.id, createInlineMessage( 'assistant', '已识别为报销任务,正在进入报销流程。', { id: plannerPendingMessage.id, stewardPlan: { streamStatus: 'completed', thinkingEvents: resolveInlineThinkingEvents(plannerPendingMessage) .map((item) => ({ ...item, status: 'completed' })) } } )) void expenseFlow.startAiReimbursementAssociationGate(cleanPrompt, entry.label || cleanPrompt) return } void stewardFlow.requestInlineAssistantReply(cleanPrompt, entry, files, { pendingMessageId: plannerPendingMessage.id }) } return { executeModelPlannedWorkbenchIntent, startModelPlannedNextTask } }