Implement runtime tool ID and display name mapping in DuplexPipeline. Enhance Assistants and ToolLibrary components to utilize new mappings for improved tool identification and display. Update DebugDrawer to reflect changes in tool display names during interactions.

This commit is contained in:
Xin Wang
2026-02-27 15:50:43 +08:00
parent 0f1165af64
commit b035e023c4
4 changed files with 126 additions and 8 deletions

View File

@@ -2913,6 +2913,7 @@ export const DebugDrawer: React.FC<{
const toolCall = payload?.tool_call || {};
const toolCallId = String(toolCall?.id || '').trim();
const toolName = String(toolCall?.function?.name || toolCall?.name || 'unknown_tool');
const toolDisplayName = String(payload?.tool_display_name || toolCall?.displayName || toolName);
const executor = String(toolCall?.executor || 'server').toLowerCase();
const rawArgs = String(toolCall?.function?.arguments || '');
const argText = rawArgs.length > 160 ? `${rawArgs.slice(0, 160)}...` : rawArgs;
@@ -2920,7 +2921,7 @@ export const DebugDrawer: React.FC<{
...prev,
{
role: 'tool',
text: `call ${toolName} executor=${executor}${argText ? ` args=${argText}` : ''}`,
text: `call ${toolDisplayName} executor=${executor}${argText ? ` args=${argText}` : ''}`,
},
]);
if (executor === 'client' && toolCallId && ws.readyState === WebSocket.OPEN) {
@@ -2950,8 +2951,8 @@ export const DebugDrawer: React.FC<{
const statusMessage = String(resultPayload?.status?.message || 'error');
const resultText =
statusCode === 200 && typeof resultPayload?.output?.result === 'number'
? `result ${toolName} = ${resultPayload.output.result}`
: `result ${toolName} status=${statusCode} ${statusMessage}`;
? `result ${toolDisplayName} = ${resultPayload.output.result}`
: `result ${toolDisplayName} status=${statusCode} ${statusMessage}`;
setMessages((prev) => [
...prev,
{
@@ -3025,14 +3026,15 @@ export const DebugDrawer: React.FC<{
if (type === 'assistant.tool_result') {
const result = payload?.result || {};
const toolName = String(result?.name || 'unknown_tool');
const toolDisplayName = String(payload?.tool_display_name || toolName);
const statusCode = Number(result?.status?.code || 500);
const statusMessage = String(result?.status?.message || 'error');
const source = String(payload?.source || 'server');
const output = result?.output;
const resultText =
statusCode === 200
? `result ${toolName} source=${source} ${JSON.stringify(output)}`
: `result ${toolName} source=${source} status=${statusCode} ${statusMessage}`;
? `result ${toolDisplayName} source=${source} ${JSON.stringify(output)}`
: `result ${toolDisplayName} source=${source} status=${statusCode} ${statusMessage}`;
setMessages((prev) => [...prev, { role: 'tool', text: resultText }]);
return;
}

View File

@@ -179,6 +179,7 @@ export const ToolLibraryPage: React.FC = () => {
const [editingTool, setEditingTool] = useState<Tool | null>(null);
const [toolName, setToolName] = useState('');
const [toolId, setToolId] = useState('');
const [toolDesc, setToolDesc] = useState('');
const [toolCategory, setToolCategory] = useState<'system' | 'query'>('system');
const [toolIcon, setToolIcon] = useState('Wrench');
@@ -209,6 +210,7 @@ export const ToolLibraryPage: React.FC = () => {
const openAdd = () => {
setEditingTool(null);
setToolName('');
setToolId('');
setToolDesc('');
setToolCategory('system');
setToolIcon('Wrench');
@@ -224,6 +226,7 @@ export const ToolLibraryPage: React.FC = () => {
const openEdit = (tool: Tool) => {
setEditingTool(tool);
setToolName(tool.name);
setToolId(tool.id);
setToolDesc(tool.description || '');
setToolCategory(tool.category);
setToolIcon(tool.icon || 'Wrench');
@@ -314,6 +317,10 @@ export const ToolLibraryPage: React.FC = () => {
alert('请填写工具名称');
return;
}
if (!editingTool && toolId.trim() && !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(toolId.trim())) {
alert('工具 ID 不合法,请使用字母/数字/下划线,且不能以数字开头');
return;
}
try {
setSaving(true);
@@ -369,6 +376,7 @@ export const ToolLibraryPage: React.FC = () => {
setTools((prev) => prev.map((item) => (item.id === updated.id ? updated : item)));
} else {
const created = await createTool({
id: toolId.trim() || undefined,
name: toolName.trim(),
description: toolDesc,
category: toolCategory,
@@ -542,6 +550,19 @@ export const ToolLibraryPage: React.FC = () => {
/>
</div>
<div className="space-y-1.5">
<label className="text-[10px] font-black text-muted-foreground uppercase tracking-widest block"> ID ()</label>
<Input
value={toolId}
onChange={(e) => setToolId(e.target.value)}
placeholder="例如: voice_message_prompt留空自动生成"
disabled={Boolean(editingTool)}
/>
<p className="text-[11px] text-muted-foreground">
{editingTool ? '已创建工具的 ID 不可修改。' : '建议客户端工具填写稳定 ID避免随机 tool_xxx 导致前端无法识别。'}
</p>
</div>
<div className="space-y-1.5">
<label className="text-[10px] font-black text-muted-foreground uppercase tracking-widest block"> ( AI )</label>
<textarea