feat(ai): 优化内置工具目录检索与参数提示

- 为内置工具目录增加关键词搜索和结果计数

- 参数提示补充类型、默认值、枚举和示例信息

- 补充目录渲染和参数摘要提取测试
This commit is contained in:
Syngnat
2026-06-11 22:29:37 +08:00
parent 6f4e80c749
commit cba8ff394c
4 changed files with 304 additions and 34 deletions

View File

@@ -5,16 +5,18 @@ import { describe, expect, it } from 'vitest';
import AIBuiltinToolsCatalog from './AIBuiltinToolsCatalog';
import { buildOverlayWorkbenchTheme } from '../../utils/overlayWorkbenchTheme';
const renderCatalog = () => (
<AIBuiltinToolsCatalog
darkMode={false}
overlayTheme={buildOverlayWorkbenchTheme(false)}
cardBg="#fff"
cardBorder="rgba(0,0,0,0.08)"
/>
);
describe('AIBuiltinToolsCatalog', () => {
it('renders the workspace flows, snapshot tools, and local saved-sql discovery tools', () => {
const markup = renderToStaticMarkup(
<AIBuiltinToolsCatalog
darkMode={false}
overlayTheme={buildOverlayWorkbenchTheme(false)}
cardBg="#fff"
cardBorder="rgba(0,0,0,0.08)"
/>,
);
const markup = renderToStaticMarkup(renderCatalog());
expect(markup).toContain('字段反查表');
expect(markup).toContain('get_all_columns');
@@ -111,6 +113,11 @@ describe('AIBuiltinToolsCatalog', () => {
expect(markup).toContain('理解样例数据');
expect(markup).toContain('preview_table_rows');
expect(markup).toContain('参数提示');
expect(markup).toContain('搜索工具、流程或参数');
expect(markup).toContain('当前显示');
expect(markup).toContain('类型string');
expect(markup).toContain('默认160');
expect(markup).toContain('示例:');
expect(markup).toContain('filePath');
expect(markup).toContain('正文预览最多返回多少字符');
});

View File

@@ -1,9 +1,11 @@
import React from 'react';
import { ToolOutlined } from '@ant-design/icons';
import React, { useState } from 'react';
import { SearchOutlined, ToolOutlined } from '@ant-design/icons';
import {
BUILTIN_TOOL_FLOWS,
describeBuiltinToolParameters,
filterBuiltinToolFlows,
filterBuiltinTools,
} from '../../utils/aiBuiltinToolCatalog';
import { BUILTIN_AI_TOOL_INFO } from '../../utils/aiToolRegistry';
import type { OverlayWorkbenchTheme } from '../../utils/overlayWorkbenchTheme';
@@ -20,31 +22,98 @@ export const AIBuiltinToolsCatalog: React.FC<AIBuiltinToolsCatalogProps> = ({
overlayTheme,
cardBg,
cardBorder,
}) => (
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<div style={{ fontSize: 13, color: overlayTheme.mutedText, marginBottom: 4 }}>
AI
</div>
<div style={{ display: 'grid', gap: 8 }}>
{BUILTIN_TOOL_FLOWS.map((flow) => (
<div
key={flow.title}
}) => {
const [searchText, setSearchText] = useState('');
const visibleFlows = filterBuiltinToolFlows(BUILTIN_TOOL_FLOWS, searchText);
const visibleTools = filterBuiltinTools(BUILTIN_AI_TOOL_INFO, searchText);
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<div style={{ fontSize: 13, color: overlayTheme.mutedText, marginBottom: 4 }}>
AI
</div>
<label
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
padding: '8px 10px',
borderRadius: 10,
border: `1px solid ${cardBorder}`,
background: darkMode ? 'rgba(255,255,255,0.03)' : 'rgba(255,255,255,0.78)',
}}
>
<SearchOutlined style={{ color: overlayTheme.mutedText }} />
<input
aria-label="搜索内置工具"
value={searchText}
onChange={(event) => setSearchText(event.target.value)}
placeholder="搜索工具、流程或参数,例如 mcp / lineLimit / allowMutating / 事务"
style={{
fontSize: 12,
color: overlayTheme.mutedText,
padding: '10px 12px',
borderRadius: 10,
flex: 1,
border: 'none',
outline: 'none',
background: 'transparent',
color: overlayTheme.titleText,
fontSize: 13,
}}
/>
{searchText && (
<button
type="button"
onClick={() => setSearchText('')}
style={{
border: 'none',
background: 'transparent',
color: overlayTheme.mutedText,
cursor: 'pointer',
fontSize: 12,
}}
>
</button>
)}
</label>
<div style={{ fontSize: 12, color: overlayTheme.mutedText }}>
{visibleFlows.length}/{BUILTIN_TOOL_FLOWS.length} {visibleTools.length}/{BUILTIN_AI_TOOL_INFO.length}
</div>
{visibleFlows.length > 0 && (
<div style={{ display: 'grid', gap: 8 }}>
{visibleFlows.map((flow) => (
<div
key={flow.title}
style={{
fontSize: 12,
color: overlayTheme.mutedText,
padding: '10px 12px',
borderRadius: 10,
background: cardBg,
border: `1px solid ${cardBorder}`,
}}
>
<div style={{ fontWeight: 700, color: overlayTheme.titleText }}>{flow.title}</div>
<div style={{ marginTop: 4, fontFamily: 'var(--gn-font-mono)' }}>{flow.steps}</div>
<div style={{ marginTop: 4, opacity: 0.8, lineHeight: 1.6 }}>{flow.description}</div>
</div>
))}
</div>
)}
{visibleTools.length === 0 && (
<div
style={{
padding: '18px 16px',
borderRadius: 14,
border: `1px dashed ${cardBorder}`,
background: cardBg,
border: `1px solid ${cardBorder}`,
color: overlayTheme.mutedText,
fontSize: 13,
lineHeight: 1.7,
}}
>
<div style={{ fontWeight: 700, color: overlayTheme.titleText }}>{flow.title}</div>
<div style={{ marginTop: 4, fontFamily: 'var(--gn-font-mono)' }}>{flow.steps}</div>
<div style={{ marginTop: 4, opacity: 0.8, lineHeight: 1.6 }}>{flow.description}</div>
mcpschema
</div>
))}
</div>
{BUILTIN_AI_TOOL_INFO.map((tool) => {
)}
{visibleTools.map((tool) => {
const parameterDetails = describeBuiltinToolParameters(tool);
return (
<div
@@ -104,6 +173,9 @@ export const AIBuiltinToolsCatalog: React.FC<AIBuiltinToolsCatalogProps> = ({
>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
<code style={{ fontFamily: 'var(--gn-font-mono)', fontSize: 12 }}>{item.name}</code>
<span style={{ fontSize: 11, color: overlayTheme.mutedText }}>
{item.typeLabel}
</span>
<span
style={{
padding: '1px 8px',
@@ -123,10 +195,20 @@ export const AIBuiltinToolsCatalog: React.FC<AIBuiltinToolsCatalogProps> = ({
{item.enumValues.join(' / ')}
</span>
)}
{item.defaultValue && (
<span style={{ fontSize: 11, color: overlayTheme.mutedText }}>
{item.defaultValue}
</span>
)}
</div>
{item.description && (
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.6 }}>{item.description}</div>
)}
{item.exampleValue && (
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.6 }}>
<code style={{ fontFamily: 'var(--gn-font-mono)' }}>{item.exampleValue}</code>
</div>
)}
</div>
))}
</div>
@@ -134,8 +216,9 @@ export const AIBuiltinToolsCatalog: React.FC<AIBuiltinToolsCatalogProps> = ({
)}
</div>
);
})}
</div>
);
})}
</div>
);
};
export default AIBuiltinToolsCatalog;