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

131 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
},
],
}
}