重构: CR-11 composables+ToolCard 健壮性 6 子项
⑦ useAiConversations JSON.parse 逐条 try/catch 容错(坏条降级不清空整对话);⑧ ToolCard 去 as any + argString 安全取字段;⑨ useAiSend approveToolCall 复用 findToolCall;⑩ useAiEvents 补 AiHeartbeat case 防穿透;⑪ approveHumanApproval 签名收敛(decisions:string[] + select_type 判单/多选)+ ProjectDetail 调用方同步(单选包数组/多选直传);⑫ ToolCard formatBytes/formatToolName/argString 兜底。批5 ww1mh6mry,vue-tsc 0
This commit is contained in:
@@ -74,15 +74,27 @@ export async function switchConversation(id: string) {
|
||||
content: m.content || '',
|
||||
model: m.model,
|
||||
timestamp: Date.now(),
|
||||
toolCalls: m.tool_calls?.map((tc: any) => ({
|
||||
id: tc.id,
|
||||
name: tc.function?.name || '',
|
||||
args: typeof tc.function?.arguments === 'string'
|
||||
? JSON.parse(tc.function.arguments || '{}')
|
||||
: tc.function?.arguments || {},
|
||||
status: 'completed' as const,
|
||||
result: toolResultMap.get(tc.id),
|
||||
})),
|
||||
toolCalls: m.tool_calls?.map((tc: any): AiToolCallInfo => {
|
||||
// 逐条容错:单条坏 arguments 仅降级为空对象,不影响整条对话回填
|
||||
let args: unknown = {}
|
||||
const rawArgs = tc.function?.arguments
|
||||
if (typeof rawArgs === 'string') {
|
||||
try {
|
||||
args = rawArgs ? JSON.parse(rawArgs) : {}
|
||||
} catch {
|
||||
args = {}
|
||||
}
|
||||
} else if (rawArgs && typeof rawArgs === 'object') {
|
||||
args = rawArgs
|
||||
}
|
||||
return {
|
||||
id: tc.id,
|
||||
name: tc.function?.name || '',
|
||||
args,
|
||||
status: 'completed' as const,
|
||||
result: toolResultMap.get(tc.id),
|
||||
}
|
||||
}),
|
||||
}))
|
||||
} catch {
|
||||
state.messages = []
|
||||
|
||||
@@ -127,6 +127,10 @@ export function handleEvent(event: AiChatEvent) {
|
||||
break
|
||||
}
|
||||
|
||||
case 'AiHeartbeat':
|
||||
// 心跳事件:仅维持看门狗(已在上方 reset),无需额外处理;显式 case 防 switch 穿透
|
||||
break
|
||||
|
||||
case 'AiToolCallStarted': {
|
||||
const info: AiToolCallInfo = {
|
||||
id: event.id,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { aiApi } from '../../api'
|
||||
import { useAppSettingsStore } from '../../stores/appSettings'
|
||||
import { state } from '../../stores/ai'
|
||||
import { resetStreamWatchdog, clearStreamWatchdog } from './useAiStream'
|
||||
import { startListener } from './useAiEvents'
|
||||
import { startListener, findToolCall } from './useAiEvents'
|
||||
import { nextMsgId } from './aiShared'
|
||||
|
||||
const appSettings = useAppSettingsStore()
|
||||
@@ -81,9 +81,8 @@ async function sendMessage(text: string, skill?: string) {
|
||||
/** 工具审批:乐观置 running,IPC 失败时改 completed+错误文案(不回滚避免卡按钮) */
|
||||
async function approveToolCall(toolCallId: string, approved: boolean) {
|
||||
// 乐观置运行中,禁用审批按钮防重复点击(后端事件回来后转 completed/rejected)
|
||||
const tc = state.messages
|
||||
.flatMap(m => m.toolCalls || [])
|
||||
.find(t => t.id === toolCallId)
|
||||
// 复用 findToolCall(反向扫描)避免重复实现查找逻辑
|
||||
const tc = findToolCall(toolCallId)
|
||||
if (tc) tc.status = 'running'
|
||||
// 重启看门狗覆盖审批执行→续生成窗口(useAiEvents.ts:162 AiApprovalRequired 已 clear,
|
||||
// 审批态无心跳兜底;approve 后后端要跑工具+续生成,期间任何后端异常不回则按钮永久 running。
|
||||
|
||||
Reference in New Issue
Block a user