ForcePilot/web/src/components/ToolCallingResult/ToolCallRenderer.vue
2026-02-03 14:03:57 +08:00

185 lines
5.5 KiB
Vue

<template>
<!-- 知识图谱查询结果 -->
<KnowledgeGraphTool v-if="isKnowledgeGraphResult" :tool-call="toolCall" ref="graphToolCallRef" />
<!-- 网页搜索 -->
<WebSearchTool v-else-if="isWebSearchResult" :tool-call="toolCall" />
<!-- 知识库 -->
<KnowledgeBaseTool v-else-if="isKnowledgeBaseResult" :tool-call="toolCall" />
<!-- 待办事项 -->
<TodoListTool v-else-if="isTodoListResult" :tool-call="toolCall" />
<!-- 计算器 -->
<CalculatorTool v-else-if="isCalculatorResult" :tool-call="toolCall" />
<!-- 图片 -->
<ImageTool v-else-if="isImageResult" :tool-call="toolCall" />
<!-- 任务分配 -->
<TaskTool v-else-if="isTaskResult" :tool-call="toolCall" />
<!-- 写文件 -->
<WriteFileTool v-else-if="isWriteFileResult" :tool-call="toolCall" />
<!-- 读文件 -->
<ReadFileTool v-else-if="isReadFileResult" :tool-call="toolCall" />
<!-- 列目录 -->
<ListDirectoryTool v-else-if="isListDirectoryResult" :tool-call="toolCall" />
<!-- 搜索文件内容 -->
<SearchFileContentTool v-else-if="isSearchFileContentResult" :tool-call="toolCall" />
<!-- Glob 搜索 -->
<GlobTool v-else-if="isGlobResult" :tool-call="toolCall" />
<!-- 编辑文件 -->
<EditFileTool v-else-if="isEditFileResult" :tool-call="toolCall" />
<!-- 默认展示 -->
<BaseToolCall v-else :tool-call="toolCall" />
</template>
<script setup>
import { computed, ref } from 'vue'
import BaseToolCall from './BaseToolCall.vue'
import { useAgentStore } from '@/stores/agent'
import { useDatabaseStore } from '@/stores/database'
import WebSearchTool from './tools/WebSearchTool.vue'
import KnowledgeBaseTool from './tools/KnowledgeBaseTool.vue'
import KnowledgeGraphTool from './tools/KnowledgeGraphTool.vue'
import CalculatorTool from './tools/CalculatorTool.vue'
import TodoListTool from './tools/TodoListTool.vue'
import ImageTool from './tools/ImageTool.vue'
import TaskTool from './tools/TaskTool.vue'
import WriteFileTool from './tools/WriteFileTool.vue'
import ReadFileTool from './tools/ReadFileTool.vue'
import ListDirectoryTool from './tools/ListDirectoryTool.vue'
import SearchFileContentTool from './tools/SearchFileContentTool.vue'
import GlobTool from './tools/GlobTool.vue'
import EditFileTool from './tools/EditFileTool.vue'
const props = defineProps({
toolCall: {
type: Object,
required: true
}
})
const agentStore = useAgentStore()
const databaseStore = useDatabaseStore()
const toolName = computed(() => props.toolCall.name || props.toolCall.function?.name || '')
const tool = computed(() => {
const toolsList = agentStore?.availableTools ? Object.values(agentStore.availableTools) : []
const tool = toolsList.find((t) => t.name === toolName.value)
return tool || null
})
const databases = computed(() => databaseStore.databases || [])
const parseData = (content) => {
if (typeof content === 'string') {
try {
return JSON.parse(content)
} catch (error) {
return content
}
}
return content
}
// 识别逻辑
const isWebSearchResult = computed(() => {
const name = toolName.value.toLowerCase()
return name.includes('tavily_search')
})
const isTaskResult = computed(() => {
let args = props.toolCall.args || props.toolCall.function?.arguments
if (typeof args === 'string') {
try {
args = JSON.parse(args)
} catch {
return false
}
}
return args && typeof args === 'object' && 'subagent_type' in args
})
const isKnowledgeBaseResult = computed(() => {
const databaseInfo = databases.value.find((db) => db.name === toolName.value)
if (databaseInfo && databaseInfo.kb_type !== 'lightrag') {
return true
}
return false
})
const isKnowledgeGraphResult = computed(() => {
const name = toolName.value.toLowerCase()
const hasGraphKeyword = name.includes('graph') || name.includes('图谱') || name.includes('kg')
const data = parseData(props.toolCall.tool_call_result?.content)
const hasBasicStructure = data && typeof data === 'object'
const hasTriples = hasBasicStructure && 'triples' in data
return (
(hasTriples && Array.isArray(data.triples) && data.triples.length > 0) ||
(hasTriples && hasGraphKeyword)
)
})
const isTodoListResult = computed(() => {
return toolName.value === 'write_todos'
})
const isCalculatorResult = computed(() => {
const name = toolName.value.toLowerCase()
return name.includes('calculator') || name.includes('calc') || name.includes('math')
})
const isImageResult = computed(() => {
const name = toolName.value.toLowerCase()
if (!name.includes('chart')) return false
const data = parseData(props.toolCall.tool_call_result?.content)
return data && typeof data === 'string' && data.startsWith('http')
})
const isWriteFileResult = computed(() => {
return toolName.value === 'write_file'
})
const isReadFileResult = computed(() => {
return toolName.value === 'read_file'
})
const isListDirectoryResult = computed(() => {
return toolName.value === 'list_directory' || toolName.value === 'ls'
})
const isSearchFileContentResult = computed(() => {
return toolName.value === 'search_file_content'
})
const isGlobResult = computed(() => {
return toolName.value === 'glob'
})
const isEditFileResult = computed(() => {
return toolName.value === 'edit_file' || toolName.value === 'replace'
})
// 处理知识图谱刷新
const graphToolCallRef = ref(null)
const refreshGraph = () => {
if (graphToolCallRef.value && typeof graphToolCallRef.value.refreshGraph === 'function') {
graphToolCallRef.value.refreshGraph()
}
}
defineExpose({ refreshGraph })
</script>
<style lang="less" scoped></style>