feat(tool): 优化 MySQL 工具命名并添加前端渲染组件
- 将工具 name_or_callable 从中文改为英文 (mysql_query, mysql_describe_table, mysql_list_tables) - 添加 MySQL 工具结果的前端渲染组件,提升用户体验
This commit is contained in:
parent
d66aa64436
commit
ed849a2e5c
@ -52,9 +52,9 @@ class TableListModel(BaseModel):
|
||||
pass
|
||||
|
||||
|
||||
@tool(name_or_callable="查询表名及说明", args_schema=TableListModel)
|
||||
@tool(name_or_callable="mysql_list_tables", args_schema=TableListModel)
|
||||
def mysql_list_tables() -> str:
|
||||
"""获取数据库中的所有表名
|
||||
"""【查询表名及说明】获取数据库中的所有表名
|
||||
|
||||
这个工具用来列出当前数据库中所有的表名,帮助你了解数据库的结构。
|
||||
"""
|
||||
@ -107,9 +107,9 @@ class TableDescribeModel(BaseModel):
|
||||
table_name: str = Field(description="要查询的表名", example="users")
|
||||
|
||||
|
||||
@tool(name_or_callable="描述表", args_schema=TableDescribeModel)
|
||||
@tool(name_or_callable="mysql_describe_table", args_schema=TableDescribeModel)
|
||||
def mysql_describe_table(table_name: Annotated[str, "要查询结构的表名"]) -> str:
|
||||
"""获取指定表的详细结构信息
|
||||
"""【描述表】获取指定表的详细结构信息
|
||||
|
||||
这个工具用来查看表的字段信息、数据类型、是否允许NULL、默认值、键类型等。
|
||||
帮助你了解表的结构,以便编写正确的SQL查询。
|
||||
@ -203,12 +203,12 @@ class QueryModel(BaseModel):
|
||||
timeout: int | None = Field(default=60, description="查询超时时间(秒),默认60秒,最大600秒", ge=1, le=600)
|
||||
|
||||
|
||||
@tool(name_or_callable="执行 SQL 查询", args_schema=QueryModel)
|
||||
@tool(name_or_callable="mysql_query", args_schema=QueryModel)
|
||||
def mysql_query(
|
||||
sql: Annotated[str, "要执行的SQL查询语句(只能是SELECT语句)"],
|
||||
timeout: Annotated[int | None, "查询超时时间(秒),默认60秒,最大600秒"] = 60,
|
||||
) -> str:
|
||||
"""执行只读的SQL查询语句
|
||||
"""【执行 SQL 查询】执行只读的SQL查询语句
|
||||
|
||||
这个工具用来执行SQL查询并返回结果。支持复杂的SELECT查询,包括JOIN、GROUP BY等。
|
||||
注意:只能执行查询操作,不能修改数据。
|
||||
|
||||
@ -38,6 +38,15 @@
|
||||
<!-- 编辑文件 -->
|
||||
<EditFileTool v-else-if="isEditFileResult" :tool-call="toolCall" />
|
||||
|
||||
<!-- MySQL 查询 -->
|
||||
<MysqlQueryTool v-else-if="isMysqlQueryResult" :tool-call="toolCall" />
|
||||
|
||||
<!-- MySQL 描述表 -->
|
||||
<MysqlDescribeTableTool v-else-if="isMysqlDescribeTableResult" :tool-call="toolCall" />
|
||||
|
||||
<!-- MySQL 列出表 -->
|
||||
<MysqlListTablesTool v-else-if="isMysqlListTablesResult" :tool-call="toolCall" />
|
||||
|
||||
<!-- 默认展示 -->
|
||||
<BaseToolCall v-else :tool-call="toolCall" />
|
||||
</template>
|
||||
@ -61,6 +70,9 @@ import ListDirectoryTool from './tools/ListDirectoryTool.vue'
|
||||
import SearchFileContentTool from './tools/SearchFileContentTool.vue'
|
||||
import GlobTool from './tools/GlobTool.vue'
|
||||
import EditFileTool from './tools/EditFileTool.vue'
|
||||
import MysqlQueryTool from './tools/MysqlQueryTool.vue'
|
||||
import MysqlDescribeTableTool from './tools/MysqlDescribeTableTool.vue'
|
||||
import MysqlListTablesTool from './tools/MysqlListTablesTool.vue'
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
@ -170,6 +182,18 @@ const isEditFileResult = computed(() => {
|
||||
return toolName.value === 'edit_file' || toolName.value === 'replace'
|
||||
})
|
||||
|
||||
const isMysqlQueryResult = computed(() => {
|
||||
return toolName.value === 'mysql_query'
|
||||
})
|
||||
|
||||
const isMysqlDescribeTableResult = computed(() => {
|
||||
return toolName.value === 'mysql_describe_table'
|
||||
})
|
||||
|
||||
const isMysqlListTablesResult = computed(() => {
|
||||
return toolName.value === 'mysql_list_tables'
|
||||
})
|
||||
|
||||
// 处理知识图谱刷新
|
||||
const graphToolCallRef = ref(null)
|
||||
const refreshGraph = () => {
|
||||
|
||||
@ -15,3 +15,6 @@ 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'
|
||||
export { default as MysqlQueryTool } from './tools/MysqlQueryTool.vue'
|
||||
export { default as MysqlDescribeTableTool } from './tools/MysqlDescribeTableTool.vue'
|
||||
export { default as MysqlListTablesTool } from './tools/MysqlListTablesTool.vue'
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<BaseToolCall :tool-call="toolCall" :default-expanded="true" :hide-params="true">
|
||||
<template #header-success>
|
||||
<span class="sep-header">
|
||||
<span class="keywords">描述表结构:</span>
|
||||
<span class="description code">{{ extractTableName(toolCall.args || toolCall.function?.arguments) }}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #result="{ resultContent }">
|
||||
<div class="mysql-result">
|
||||
<pre class="result-text">{{ formatResult(resultContent) }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
</BaseToolCall>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import BaseToolCall from '../BaseToolCall.vue'
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const formatResult = (content) => {
|
||||
if (!content) return ''
|
||||
|
||||
if (typeof content === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(content)
|
||||
return JSON.stringify(parsed, null, 2)
|
||||
} catch {
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof content === 'object') {
|
||||
return JSON.stringify(content, null, 2)
|
||||
}
|
||||
|
||||
return String(content)
|
||||
}
|
||||
|
||||
const extractTableName = (args) => {
|
||||
if (!args) return ''
|
||||
|
||||
let parsedArgs = args
|
||||
if (typeof args === 'string') {
|
||||
try {
|
||||
parsedArgs = JSON.parse(args)
|
||||
} catch {
|
||||
return args
|
||||
}
|
||||
}
|
||||
|
||||
return parsedArgs?.table_name || ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.mysql-result {
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
|
||||
.result-text {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: var(--gray-700);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
background: var(--gray-50);
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<BaseToolCall :tool-call="toolCall" :default-expanded="true" :hide-params="true">
|
||||
<template #header-success>
|
||||
<span class="sep-header">
|
||||
<span class="keywords">列出数据库表</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #result="{ resultContent }">
|
||||
<div class="mysql-result">
|
||||
<pre class="result-text">{{ formatResult(resultContent) }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
</BaseToolCall>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import BaseToolCall from '../BaseToolCall.vue'
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const formatResult = (content) => {
|
||||
if (!content) return ''
|
||||
|
||||
if (typeof content === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(content)
|
||||
return JSON.stringify(parsed, null, 2)
|
||||
} catch {
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof content === 'object') {
|
||||
return JSON.stringify(content, null, 2)
|
||||
}
|
||||
|
||||
return String(content)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.mysql-result {
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
|
||||
.result-text {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: var(--gray-700);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
background: var(--gray-50);
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
119
web/src/components/ToolCallingResult/tools/MysqlQueryTool.vue
Normal file
119
web/src/components/ToolCallingResult/tools/MysqlQueryTool.vue
Normal file
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<BaseToolCall :tool-call="toolCall" :default-expanded="true">
|
||||
<template #header-success>
|
||||
<span class="sep-header">
|
||||
<span class="keywords">执行SQL查询:</span>
|
||||
<span class="description">{{ truncateSql(extractSql(toolCall.args || toolCall.function?.arguments)) }}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #params="{ args }">
|
||||
<div class="mysql-params">
|
||||
<pre class="sql-text">{{ extractSql(args) }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #result="{ resultContent }">
|
||||
<div class="mysql-result">
|
||||
<pre class="result-text">{{ formatResult(resultContent) }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
</BaseToolCall>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import BaseToolCall from '../BaseToolCall.vue'
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const formatResult = (content) => {
|
||||
if (!content) return ''
|
||||
|
||||
// 如果是字符串,尝试解析为 JSON 后再转为格式化字符串
|
||||
if (typeof content === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(content)
|
||||
return JSON.stringify(parsed, null, 2)
|
||||
} catch {
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是对象,直接格式化
|
||||
if (typeof content === 'object') {
|
||||
return JSON.stringify(content, null, 2)
|
||||
}
|
||||
|
||||
return String(content)
|
||||
}
|
||||
|
||||
const extractSql = (args) => {
|
||||
if (!args) return ''
|
||||
|
||||
// 解析 args(可能是字符串或对象)
|
||||
let parsedArgs = args
|
||||
if (typeof args === 'string') {
|
||||
try {
|
||||
parsedArgs = JSON.parse(args)
|
||||
} catch {
|
||||
return args
|
||||
}
|
||||
}
|
||||
|
||||
// 提取 sql 字段
|
||||
const sql = parsedArgs?.sql || parsedArgs?.query
|
||||
return sql || JSON.stringify(parsedArgs, null, 2)
|
||||
}
|
||||
|
||||
const truncateSql = (sql, maxLength = 50) => {
|
||||
if (!sql) return ''
|
||||
// 移除换行符和多余空格
|
||||
const singleLine = sql.replace(/\s+/g, ' ').trim()
|
||||
if (singleLine.length <= maxLength) return singleLine
|
||||
return singleLine.slice(0, maxLength) + '...'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.mysql-params {
|
||||
.sql-text {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
color: var(--gray-800);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
padding: 6px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.mysql-result {
|
||||
// background: var(--gray-0);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
|
||||
.result-text {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: var(--gray-700);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
background: var(--gray-50);
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user