refactor(ui,server): 移除冗余的 toolCalls 兼容字段

删除从 parts 中提取的冗余 toolCalls 数组:
- 移除 ToolCallInfo 类型定义
- 移除 Message.toolCalls 字段
- 移除 ToolCallsDisplay/ToolCallItem 组件
- 简化 sessions.ts 消息构建逻辑
This commit is contained in:
2025-12-17 21:46:28 +08:00
parent c6dd3695e5
commit 2ff74e446c
4 changed files with 12 additions and 168 deletions
-15
View File
@@ -20,19 +20,6 @@ export type ToolStatus = 'pending' | 'running' | 'completed' | 'error';
/** @deprecated 使用 ToolStatus 代替 */
export type ToolCallStatus = ToolStatus;
/**
* 工具调用信息
*/
export interface ToolCallInfo {
id: string;
name: string;
arguments: Record<string, unknown>;
status: ToolStatus;
result?: unknown;
error?: string;
duration?: number; // 执行时长 ms
}
// ============ 消息 Parts 相关 ============
/**
@@ -128,8 +115,6 @@ export interface Message {
parts: MessagePart[];
/** 所有文本拼接(兼容字段) */
content?: string;
/** 所有工具调用(兼容字段) */
toolCalls?: ToolCallInfo[];
/** 是否包含推理过程 */
hasReasoning?: boolean;
/** 推理内容 */
+10 -121
View File
@@ -23,7 +23,7 @@ import { fadeInUp, smoothTransition } from '../utils/animations';
import { getAgentDisplayName } from '../utils/agent';
import { Markdown } from './Markdown';
import { FileMentionText } from './FileMentionTag';
import type { Message, ToolCallInfo, ToolStatus, ToolMessagePart, QuestionMessagePart, FileDiffInfo } from '../api/types.js';
import type { Message, ToolStatus, ToolMessagePart, QuestionMessagePart, FileDiffInfo } from '../api/types.js';
import { AskUserQuestion } from './AskUserQuestion.js';
interface ChatMessageProps {
@@ -117,22 +117,17 @@ export const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>(
);
}
// 回退:使用旧的 content + toolCalls 字段
// 回退:使用 content 字段(无 parts 时)
return (
<>
{!isUser && message.toolCalls && message.toolCalls.length > 0 && (
<ToolCallsDisplay toolCalls={message.toolCalls} />
<div className="message-content text-fg-secondary">
{isUser ? (
<div>
<FileMentionText text={message.content ?? ''} />
</div>
) : (
<Markdown content={message.content ?? ''} />
)}
<div className="message-content text-fg-secondary">
{isUser ? (
<div>
<FileMentionText text={message.content ?? ''} />
</div>
) : (
<Markdown content={message.content ?? ''} />
)}
</div>
</>
</div>
);
};
@@ -384,109 +379,3 @@ function formatDuration(ms?: number): string {
return `${(ms / 1000).toFixed(1)}s`;
}
/**
* 工具调用列表容器
*/
interface ToolCallsDisplayProps {
toolCalls: ToolCallInfo[];
}
function ToolCallsDisplay({ toolCalls }: ToolCallsDisplayProps) {
return (
<div className="mb-3 space-y-2">
{toolCalls.map((toolCall) => (
<ToolCallItem key={toolCall.id} toolCall={toolCall} />
))}
</div>
);
}
/**
* 单个工具调用项
*/
interface ToolCallItemProps {
toolCall: ToolCallInfo;
}
function ToolCallItem({ toolCall }: ToolCallItemProps) {
const [expanded, setExpanded] = useState(false);
const hasDetails =
Object.keys(toolCall.arguments).length > 0 ||
toolCall.result !== undefined ||
toolCall.error !== undefined;
return (
<div className="border border-line rounded-lg overflow-hidden bg-surface-subtle/30">
{/* 头部:工具名称、状态、时长 */}
<button
onClick={() => hasDetails && setExpanded(!expanded)}
className={cn(
'w-full flex items-center gap-2 px-3 py-2 text-sm',
hasDetails && 'hover:bg-surface-muted/50 cursor-pointer',
!hasDetails && 'cursor-default'
)}
>
<Wrench size={14} className="text-fg-muted flex-shrink-0" />
<span className="font-mono text-fg-secondary flex-1 text-left truncate">
{toolCall.name}
</span>
{getStatusIcon(toolCall.status)}
{toolCall.duration && (
<span className="text-xs text-fg-subtle">{formatDuration(toolCall.duration)}</span>
)}
{hasDetails && (
<span className="text-fg-subtle">
{expanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
</span>
)}
</button>
{/* 展开的详情 */}
<AnimatePresence>
{expanded && hasDetails && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.2 }}
className="border-t border-line overflow-hidden"
>
<div className="px-3 py-2 space-y-2 text-xs">
{/* 参数 */}
{Object.keys(toolCall.arguments).length > 0 && (
<div>
<div className="text-fg-subtle mb-1">Arguments:</div>
<pre className="bg-surface-base rounded p-2 overflow-x-auto text-fg-muted max-h-48 overflow-y-auto">
{JSON.stringify(toolCall.arguments, null, 2)}
</pre>
</div>
)}
{/* 结果 */}
{toolCall.result !== undefined && (
<div>
<div className="text-fg-subtle mb-1">Result:</div>
<pre className="bg-surface-base rounded p-2 overflow-x-auto text-green-400 max-h-48 overflow-y-auto">
{typeof toolCall.result === 'string'
? toolCall.result
: JSON.stringify(toolCall.result, null, 2)}
</pre>
</div>
)}
{/* 错误 */}
{toolCall.error && (
<div>
<div className="text-red-400 mb-1">Error:</div>
<pre className="bg-surface-base rounded p-2 overflow-x-auto text-red-300 max-h-48 overflow-y-auto">
{toolCall.error}
</pre>
</div>
)}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}