feat: 添加更多工具解析组件
This commit is contained in:
parent
d47a66ea88
commit
b589521775
@ -126,7 +126,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="!attachmentCount" class="empty">
|
||||
<p>暂无附件</p>
|
||||
<p>暂无附件,支持 txt/md/docx/html 格式 ≤ 5 MB</p>
|
||||
<a-button type="primary" @click="triggerUpload" :loading="isUploading">上传附件</a-button>
|
||||
</div>
|
||||
<div v-else class="file-tree-container attachment-tree">
|
||||
@ -827,7 +827,7 @@ const stopResize = () => {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--gray-150);
|
||||
transition: all 0.15s ease;
|
||||
|
||||
@ -305,11 +305,24 @@ const formatResultData = (data) => {
|
||||
|
||||
:deep(.tag) {
|
||||
font-size: 12px;
|
||||
color: var(--gray-600);
|
||||
background-color: var(--gray-100);
|
||||
padding: 0px 4px;
|
||||
color: var(--gray-800);
|
||||
background-color: var(--gray-50);
|
||||
padding: 4px 4px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
|
||||
&.tag-yes {
|
||||
color: var(--main-500);
|
||||
}
|
||||
|
||||
&.success {
|
||||
color: var(--color-success-500);
|
||||
background-color: var(--color-success-50);
|
||||
}
|
||||
&.error {
|
||||
color: var(--color-error-500);
|
||||
background-color: var(--color-error-50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,21 @@
|
||||
<!-- 写文件 -->
|
||||
<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>
|
||||
@ -41,6 +56,11 @@ 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: {
|
||||
@ -130,6 +150,26 @@ 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 = () => {
|
||||
|
||||
@ -10,3 +10,8 @@ export { default as CalculatorTool } from './tools/CalculatorTool.vue'
|
||||
export { default as TodoListTool } from './tools/TodoListTool.vue'
|
||||
export { default as ImageTool } from './tools/ImageTool.vue'
|
||||
export { default as WriteFileTool } from './tools/WriteFileTool.vue'
|
||||
export { default as ReadFileTool } from './tools/ReadFileTool.vue'
|
||||
export { default as ListDirectoryTool } from './tools/ListDirectoryTool.vue'
|
||||
export { default as SearchFileContentTool } from './tools/SearchFileContentTool.vue'
|
||||
export { default as GlobTool } from './tools/GlobTool.vue'
|
||||
export { default as EditFileTool } from './tools/EditFileTool.vue'
|
||||
|
||||
55
web/src/components/ToolCallingResult/tools/EditFileTool.vue
Normal file
55
web/src/components/ToolCallingResult/tools/EditFileTool.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<BaseToolCall :tool-call="toolCall">
|
||||
<template #header>
|
||||
<div class="sep-header">
|
||||
<span class="note">{{ toolCallName }}</span>
|
||||
<span class="separator" v-if="filePath">|</span>
|
||||
<span class="description">
|
||||
{{ filePath }}
|
||||
<span class="tag success" v-if="addedLines > 0">+{{ addedLines }}</span>
|
||||
<span class="tag error" v-if="removedLines > 0">-{{ removedLines }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</BaseToolCall>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import BaseToolCall from '../BaseToolCall.vue'
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const toolCallName = computed(() => props.toolCall.name || props.toolCall.function?.name || 'edit_file')
|
||||
|
||||
const parsedArgs = computed(() => {
|
||||
const args = props.toolCall.args || props.toolCall.function?.arguments
|
||||
if (!args) return {}
|
||||
if (typeof args === 'object') return args
|
||||
try {
|
||||
return JSON.parse(args)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
|
||||
const filePath = computed(() => {
|
||||
const path = parsedArgs.value.file_path || ''
|
||||
return path.startsWith('/') ? path.slice(1) : path
|
||||
})
|
||||
|
||||
const addedLines = computed(() => {
|
||||
const newStr = parsedArgs.value.new_string || ''
|
||||
return newStr ? String(newStr).split('\n').length : 0
|
||||
})
|
||||
|
||||
const removedLines = computed(() => {
|
||||
const oldStr = parsedArgs.value.old_string || ''
|
||||
return oldStr ? String(oldStr).split('\n').length : 0
|
||||
})
|
||||
</script>
|
||||
38
web/src/components/ToolCallingResult/tools/GlobTool.vue
Normal file
38
web/src/components/ToolCallingResult/tools/GlobTool.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<BaseToolCall :tool-call="toolCall">
|
||||
<template #header>
|
||||
<div class="sep-header">
|
||||
<span class="note">glob</span>
|
||||
<span class="separator" v-if="pattern">|</span>
|
||||
<span class="description">{{ pattern }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</BaseToolCall>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import BaseToolCall from '../BaseToolCall.vue'
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const parsedArgs = computed(() => {
|
||||
const args = props.toolCall.args || props.toolCall.function?.arguments
|
||||
if (!args) return {}
|
||||
if (typeof args === 'object') return args
|
||||
try {
|
||||
return JSON.parse(args)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
|
||||
const pattern = computed(() => parsedArgs.value.pattern || '')
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<BaseToolCall :tool-call="toolCall" :hide-params="true">
|
||||
<template #header>
|
||||
<div class="sep-header">
|
||||
<span class="note">{{ toolCallName }}</span>
|
||||
<span class="separator" v-if="dirPath">|</span>
|
||||
<span class="description">{{ dirPath }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</BaseToolCall>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import BaseToolCall from '../BaseToolCall.vue'
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const toolCallName = computed(
|
||||
() => props.toolCall.name || props.toolCall.function?.name || 'list_directory'
|
||||
)
|
||||
|
||||
const parsedArgs = computed(() => {
|
||||
const args = props.toolCall.args || props.toolCall.function?.arguments
|
||||
if (!args) return {}
|
||||
if (typeof args === 'object') return args
|
||||
try {
|
||||
return JSON.parse(args)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
|
||||
const dirPath = computed(() => {
|
||||
return parsedArgs.value.dir_path || parsedArgs.value.path || ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
62
web/src/components/ToolCallingResult/tools/ReadFileTool.vue
Normal file
62
web/src/components/ToolCallingResult/tools/ReadFileTool.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<BaseToolCall :tool-call="toolCall">
|
||||
<template #header>
|
||||
<div class="sep-header">
|
||||
<span class="note">read_file</span>
|
||||
<span class="separator" v-if="filePath">|</span>
|
||||
<span class="description">
|
||||
{{ filePath }}
|
||||
<span class="tag" v-if="lineRange">{{ lineRange }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</BaseToolCall>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import BaseToolCall from '../BaseToolCall.vue'
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const parsedArgs = computed(() => {
|
||||
const args = props.toolCall.args || props.toolCall.function?.arguments
|
||||
if (!args) return {}
|
||||
if (typeof args === 'object') return args
|
||||
try {
|
||||
return JSON.parse(args)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
|
||||
const filePath = computed(() => {
|
||||
const path = parsedArgs.value.file_path || ''
|
||||
return path.startsWith('/') ? path.slice(1) : path
|
||||
})
|
||||
|
||||
const lineRange = computed(() => {
|
||||
const offset = parsedArgs.value.offset
|
||||
const limit = parsedArgs.value.limit
|
||||
if (offset !== undefined && limit !== undefined) {
|
||||
return `Lines ${offset}-${Number(offset) + Number(limit)}`
|
||||
} else if (limit !== undefined) {
|
||||
return `First ${limit} lines`
|
||||
}
|
||||
return ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sep-header {
|
||||
.tag {
|
||||
color: var(--color-primary-600);
|
||||
background-color: var(--color-primary-50);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<BaseToolCall :tool-call="toolCall">
|
||||
<template #header>
|
||||
<div class="sep-header">
|
||||
<span class="note">search_file_content</span>
|
||||
<span class="separator" v-if="pattern">|</span>
|
||||
<span class="keywords">{{ pattern }}</span>
|
||||
<span class="separator" v-if="dirPath">|</span>
|
||||
<span class="description">{{ dirPath }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</BaseToolCall>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import BaseToolCall from '../BaseToolCall.vue'
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const parsedArgs = computed(() => {
|
||||
const args = props.toolCall.args || props.toolCall.function?.arguments
|
||||
if (!args) return {}
|
||||
if (typeof args === 'object') return args
|
||||
try {
|
||||
return JSON.parse(args)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
|
||||
const pattern = computed(() => parsedArgs.value.pattern || '')
|
||||
const dirPath = computed(() => parsedArgs.value.dir_path || '')
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@ -147,17 +147,17 @@ const todoListData = (content) => {
|
||||
.todo-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.todo-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
background: var(--gray-25);
|
||||
padding: 4px 8px;
|
||||
// background: var(--gray-10);
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--gray-150);
|
||||
// border: 1px solid var(--gray-150);
|
||||
|
||||
.todo-status {
|
||||
flex-shrink: 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user