159 lines
4.8 KiB
JavaScript
159 lines
4.8 KiB
JavaScript
|
|
import {
|
||
|
|
buildStewardPlanMessageText,
|
||
|
|
buildStewardPlanRequest,
|
||
|
|
buildStewardSuggestedActions,
|
||
|
|
normalizeStewardPlan
|
||
|
|
} from './stewardPlanModel.js'
|
||
|
|
import { SESSION_TYPE_STEWARD } from './travelReimbursementConversationModel.js'
|
||
|
|
|
||
|
|
export function useStewardPlanFlow({
|
||
|
|
activeSessionType,
|
||
|
|
attachedFiles,
|
||
|
|
composerDraft,
|
||
|
|
currentUser,
|
||
|
|
fileInputRef,
|
||
|
|
messages,
|
||
|
|
createMessage,
|
||
|
|
fetchStewardPlan,
|
||
|
|
fetchStewardPlanStream,
|
||
|
|
nextTick,
|
||
|
|
persistSessionState,
|
||
|
|
replaceMessage,
|
||
|
|
scrollToBottom,
|
||
|
|
adjustComposerTextareaHeight,
|
||
|
|
submitting,
|
||
|
|
reviewActionBusy,
|
||
|
|
sessionSwitchBusy,
|
||
|
|
toast
|
||
|
|
}) {
|
||
|
|
function isStewardSession() {
|
||
|
|
return String(activeSessionType.value || '').trim() === SESSION_TYPE_STEWARD
|
||
|
|
}
|
||
|
|
|
||
|
|
function clearStewardThinkingTimers() {
|
||
|
|
// 保留给页面卸载调用;流式版不再使用前端延时器。
|
||
|
|
}
|
||
|
|
|
||
|
|
async function submitStewardPlan(options = {}) {
|
||
|
|
if (!isStewardSession()) return false
|
||
|
|
if (submitting.value || reviewActionBusy.value || sessionSwitchBusy.value) return true
|
||
|
|
|
||
|
|
const rawText = String(options.rawText ?? composerDraft.value ?? '').trim()
|
||
|
|
const files = Array.from(options.files ?? attachedFiles.value ?? [])
|
||
|
|
if (!rawText && !files.length) return true
|
||
|
|
|
||
|
|
const fileNames = files.map((file) => file.name).filter(Boolean)
|
||
|
|
const userText = String(options.userText || rawText || `我上传了 ${fileNames.length} 份附件,请小财管家先归集任务。`).trim()
|
||
|
|
submitting.value = true
|
||
|
|
|
||
|
|
if (!options.skipUserMessage) {
|
||
|
|
messages.value.push(createMessage('user', userText, fileNames))
|
||
|
|
}
|
||
|
|
const pendingPlan = normalizeStewardPlan({
|
||
|
|
plan_status: 'streaming',
|
||
|
|
summary: '',
|
||
|
|
thinking_events: []
|
||
|
|
})
|
||
|
|
const pendingMessage = createMessage('assistant', '', [], {
|
||
|
|
assistantName: '小财管家',
|
||
|
|
meta: ['小财管家', '流式分析中'],
|
||
|
|
stewardPlan: {
|
||
|
|
...pendingPlan,
|
||
|
|
streamStatus: 'streaming'
|
||
|
|
}
|
||
|
|
})
|
||
|
|
messages.value.push(pendingMessage)
|
||
|
|
composerDraft.value = ''
|
||
|
|
nextTick(() => {
|
||
|
|
adjustComposerTextareaHeight()
|
||
|
|
scrollToBottom()
|
||
|
|
})
|
||
|
|
|
||
|
|
try {
|
||
|
|
const requestPayload = buildStewardPlanRequest({
|
||
|
|
rawText,
|
||
|
|
files,
|
||
|
|
currentUser: currentUser.value || {}
|
||
|
|
})
|
||
|
|
const plan = await fetchPlanWithStreaming(pendingMessage.id, requestPayload)
|
||
|
|
const normalizedPlan = normalizeStewardPlan(plan, {
|
||
|
|
visibleThinkingEventCount: Number.MAX_SAFE_INTEGER
|
||
|
|
})
|
||
|
|
replaceMessage(pendingMessage.id, createMessage('assistant', buildStewardPlanMessageText(plan), [], {
|
||
|
|
assistantName: '小财管家',
|
||
|
|
meta: ['小财管家', '等待确认'],
|
||
|
|
stewardPlan: {
|
||
|
|
...normalizedPlan,
|
||
|
|
streamStatus: 'completed'
|
||
|
|
},
|
||
|
|
suggestedActions: buildStewardSuggestedActions(plan)
|
||
|
|
}))
|
||
|
|
persistSessionState()
|
||
|
|
nextTick(scrollToBottom)
|
||
|
|
} catch (error) {
|
||
|
|
replaceMessage(pendingMessage.id, createMessage('assistant', error?.message || '小财管家规划失败,请稍后重试。', [], {
|
||
|
|
assistantName: '小财管家',
|
||
|
|
meta: ['小财管家', '规划失败']
|
||
|
|
}))
|
||
|
|
toast(error?.message || '小财管家规划失败,请稍后重试。')
|
||
|
|
persistSessionState()
|
||
|
|
} finally {
|
||
|
|
submitting.value = false
|
||
|
|
if (fileInputRef.value) {
|
||
|
|
fileInputRef.value.value = ''
|
||
|
|
}
|
||
|
|
nextTick(() => {
|
||
|
|
adjustComposerTextareaHeight()
|
||
|
|
scrollToBottom()
|
||
|
|
})
|
||
|
|
}
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
function fetchPlanWithStreaming(messageId, requestPayload) {
|
||
|
|
if (typeof fetchStewardPlanStream === 'function') {
|
||
|
|
return fetchStewardPlanStream(requestPayload, {
|
||
|
|
onEvent: (event) => handleStreamEvent(messageId, event)
|
||
|
|
}, {
|
||
|
|
timeoutMs: 20000,
|
||
|
|
timeoutMessage: '小财管家任务规划超时,请稍后重试。'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
return fetchStewardPlan(requestPayload, {
|
||
|
|
timeoutMs: 16000,
|
||
|
|
timeoutMessage: '小财管家任务规划超时,请稍后重试。'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
function handleStreamEvent(messageId, event) {
|
||
|
|
if (event.event !== 'thinking') {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
const message = messages.value.find((item) => item.id === messageId)
|
||
|
|
if (!message?.stewardPlan) return
|
||
|
|
const existingEvents = Array.isArray(message.stewardPlan.thinkingEvents)
|
||
|
|
? message.stewardPlan.thinkingEvents
|
||
|
|
: []
|
||
|
|
const normalizedPlan = normalizeStewardPlan({
|
||
|
|
...message.stewardPlan,
|
||
|
|
thinking_events: [...existingEvents, event.data]
|
||
|
|
}, {
|
||
|
|
visibleThinkingEventCount: existingEvents.length + 1
|
||
|
|
})
|
||
|
|
message.stewardPlan = {
|
||
|
|
...message.stewardPlan,
|
||
|
|
...normalizedPlan,
|
||
|
|
streamStatus: 'streaming'
|
||
|
|
}
|
||
|
|
persistSessionState()
|
||
|
|
nextTick(scrollToBottom)
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
isStewardSession,
|
||
|
|
submitStewardPlan,
|
||
|
|
clearStewardThinkingTimers
|
||
|
|
}
|
||
|
|
}
|