hololake-system-architecture/product-source/hololake-platform/src/lib/aiAgentMessageState.ts

131 lines
4.7 KiB
TypeScript
Raw Normal View History

import type { Dispatch, SetStateAction } from 'react'
import type { AiAgentMessage } from './aiAgentConversation'
import type { AppLocale } from './i18n'
export interface ToolInvocation {
tool: string
input?: string
}
export function updateMessage(
setMessages: Dispatch<SetStateAction<AiAgentMessage[]>>,
messageId: string,
updater: (message: AiAgentMessage) => AiAgentMessage,
): void {
setMessages((current) => current.map((message) => (message.id === messageId ? updater(message) : message)))
}
export function markReasoningDone(
setMessages: Dispatch<SetStateAction<AiAgentMessage[]>>,
messageId: string,
): void {
updateMessage(setMessages, messageId, (message) => (
message.reasoningDone ? message : { ...message, reasoningDone: true }
))
}
type ToolActionStatus = 'pending' | 'done' | 'error'
function parsedToolInput(input?: string): Record<string, unknown> {
if (!input) return {}
try {
const parsed: unknown = JSON.parse(input)
return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
? parsed as Record<string, unknown>
: {}
} catch {
return {}
}
}
function toolDetail(input?: string): string | undefined {
const parsed = parsedToolInput(input)
for (const key of ['purpose', 'query', 'url', 'path', 'file_path']) {
const value = parsed[key]
if (typeof value === 'string' && value.trim()) return value.trim()
}
return undefined
}
function chineseToolVerb(toolName: string, status: ToolActionStatus): string {
const verbs: Record<string, [string, string, string]> = {
magic_brush: ['正在执行', '已完成', '执行失败'],
search_web: ['正在联网搜索', '已联网搜索', '联网搜索失败'],
read_web_page: ['正在浏览网页', '已浏览网页', '网页浏览失败'],
read_guanghu_url: ['正在读取光湖页面', '已读取光湖页面', '光湖页面读取失败'],
search_notes: ['正在搜索知识库', '已搜索知识库', '知识库搜索失败'],
get_vault_context: ['正在了解知识库', '已读取知识库概况', '知识库概况读取失败'],
get_note: ['正在读取页面', '已读取页面', '页面读取失败'],
open_note: ['正在打开页面', '已打开页面', '页面打开失败'],
create_note: ['正在新建页面', '已新建页面', '页面新建失败'],
edit_note: ['正在写入页面', '已写入页面', '页面写入失败'],
delete_note: ['正在删除页面', '已删除页面', '页面删除失败'],
get_fifth_domain_wake_route: ['正在读取第五域路径', '已读取第五域路径', '第五域路径读取失败'],
get_current_time: ['正在校准当前时间', '已校准当前时间', '当前时间校准失败'],
Bash: ['正在运行命令', '已运行命令', '命令运行失败'],
Write: ['正在写入文件', '已写入文件', '文件写入失败'],
Edit: ['正在编辑文件', '已编辑文件', '文件编辑失败'],
Read: ['正在读取文件', '已读取文件', '文件读取失败'],
Glob: ['正在查找文件', '已查找文件', '文件查找失败'],
Grep: ['正在搜索内容', '已搜索内容', '内容搜索失败'],
}
const index = status === 'pending' ? 0 : status === 'done' ? 1 : 2
return verbs[toolName]?.[index] ?? (status === 'pending' ? `正在调用 ${toolName}` : status === 'done' ? `已调用 ${toolName}` : `${toolName} 调用失败`)
}
export function formatToolLabel(
toolName: string,
input?: string,
locale: AppLocale = 'en',
status: ToolActionStatus = 'pending',
): string {
if (locale.startsWith('zh')) {
const detail = toolDetail(input)
const verb = chineseToolVerb(toolName, status)
return detail ? `${verb}${detail}` : verb
}
if (toolName === 'Bash') {
return 'Ran shell command'
}
if (toolName === 'Write') return 'Wrote file'
if (toolName === 'Edit') return 'Edited file'
return toolName
}
export function updateToolAction(
message: AiAgentMessage,
toolName: string,
toolId: string,
input?: string,
locale: AppLocale = 'en',
): AiAgentMessage {
const existing = message.actions.find((action) => action.toolId === toolId)
if (existing) {
return {
...message,
actions: message.actions.map((action) => (
action.toolId === toolId
? {
...action,
input: input ?? action.input,
label: formatToolLabel(toolName, input ?? action.input, locale),
}
: action
)),
}
}
return {
...message,
actions: [
...message.actions,
{
tool: toolName,
toolId,
label: formatToolLabel(toolName, input, locale),
status: 'pending' as const,
input,
},
],
}
}