refactor(ui,server): 移除冗余的 toolCalls 兼容字段
删除从 parts 中提取的冗余 toolCalls 数组: - 移除 ToolCallInfo 类型定义 - 移除 Message.toolCalls 字段 - 移除 ToolCallsDisplay/ToolCallItem 组件 - 简化 sessions.ts 消息构建逻辑
This commit is contained in:
@@ -8,12 +8,11 @@ import { Hono } from 'hono';
|
|||||||
import { getSessionManager } from '../session/manager.js';
|
import { getSessionManager } from '../session/manager.js';
|
||||||
import {
|
import {
|
||||||
CreateSessionInputSchema,
|
CreateSessionInputSchema,
|
||||||
type ToolCallInfo,
|
|
||||||
type Message,
|
type Message,
|
||||||
type MessagePart,
|
type MessagePart,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import type { MessageInfo, Part, ToolPart, ApiPart } from '@ai-assistant/core';
|
import type { MessageInfo, Part, ApiPart } from '@ai-assistant/core';
|
||||||
import { MessageStorage, PartStorage, partsToApiFormat, getToolInput, getToolDuration } from '@ai-assistant/core';
|
import { MessageStorage, PartStorage, partsToApiFormat } from '@ai-assistant/core';
|
||||||
|
|
||||||
export const sessionsRouter = new Hono();
|
export const sessionsRouter = new Hono();
|
||||||
|
|
||||||
@@ -187,19 +186,6 @@ sessionsRouter.get('/:id/messages', async (c) => {
|
|||||||
.map((p) => p.text ?? '')
|
.map((p) => p.text ?? '')
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
// 兼容字段:提取工具调用(使用 Core 工具函数)
|
|
||||||
const toolCalls: ToolCallInfo[] = parts
|
|
||||||
.filter((p): p is ToolPart => p.type === 'tool')
|
|
||||||
.map((p) => ({
|
|
||||||
id: p.toolCallId ?? '',
|
|
||||||
name: p.toolName ?? '',
|
|
||||||
arguments: getToolInput(p),
|
|
||||||
status: p.state.status,
|
|
||||||
result: p.state.status === 'completed' ? (p.state as { output: unknown }).output : undefined,
|
|
||||||
error: p.state.status === 'error' ? (p.state as { error: string }).error : undefined,
|
|
||||||
duration: getToolDuration(p),
|
|
||||||
}));
|
|
||||||
|
|
||||||
messages.push({
|
messages.push({
|
||||||
id: msgInfo.id,
|
id: msgInfo.id,
|
||||||
sessionId: msgInfo.sessionId,
|
sessionId: msgInfo.sessionId,
|
||||||
@@ -207,7 +193,6 @@ sessionsRouter.get('/:id/messages', async (c) => {
|
|||||||
timestamp: new Date(msgInfo.createdAt).toISOString(),
|
timestamp: new Date(msgInfo.createdAt).toISOString(),
|
||||||
parts: messageParts,
|
parts: messageParts,
|
||||||
content: textContent || undefined,
|
content: textContent || undefined,
|
||||||
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -349,19 +349,6 @@ export interface ReasoningMessagePart {
|
|||||||
*/
|
*/
|
||||||
export type MessagePart = TextMessagePart | ToolMessagePart | ReasoningMessagePart;
|
export type MessagePart = TextMessagePart | ToolMessagePart | ReasoningMessagePart;
|
||||||
|
|
||||||
/**
|
|
||||||
* 工具调用信息(兼容字段,合并后)
|
|
||||||
*/
|
|
||||||
export interface ToolCallInfo {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
arguments: Record<string, unknown>;
|
|
||||||
status: ToolStatus;
|
|
||||||
result?: unknown;
|
|
||||||
error?: string;
|
|
||||||
duration?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息格式(包含有序 Parts)
|
* 消息格式(包含有序 Parts)
|
||||||
*
|
*
|
||||||
@@ -378,8 +365,6 @@ export interface Message {
|
|||||||
parts: MessagePart[];
|
parts: MessagePart[];
|
||||||
/** 所有文本拼接(兼容字段) */
|
/** 所有文本拼接(兼容字段) */
|
||||||
content?: string;
|
content?: string;
|
||||||
/** 所有工具调用(兼容字段) */
|
|
||||||
toolCalls?: ToolCallInfo[];
|
|
||||||
metadata?: {
|
metadata?: {
|
||||||
model?: string;
|
model?: string;
|
||||||
stepCount?: number;
|
stepCount?: number;
|
||||||
|
|||||||
@@ -20,19 +20,6 @@ export type ToolStatus = 'pending' | 'running' | 'completed' | 'error';
|
|||||||
/** @deprecated 使用 ToolStatus 代替 */
|
/** @deprecated 使用 ToolStatus 代替 */
|
||||||
export type ToolCallStatus = 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 相关 ============
|
// ============ 消息 Parts 相关 ============
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,8 +115,6 @@ export interface Message {
|
|||||||
parts: MessagePart[];
|
parts: MessagePart[];
|
||||||
/** 所有文本拼接(兼容字段) */
|
/** 所有文本拼接(兼容字段) */
|
||||||
content?: string;
|
content?: string;
|
||||||
/** 所有工具调用(兼容字段) */
|
|
||||||
toolCalls?: ToolCallInfo[];
|
|
||||||
/** 是否包含推理过程 */
|
/** 是否包含推理过程 */
|
||||||
hasReasoning?: boolean;
|
hasReasoning?: boolean;
|
||||||
/** 推理内容 */
|
/** 推理内容 */
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { fadeInUp, smoothTransition } from '../utils/animations';
|
|||||||
import { getAgentDisplayName } from '../utils/agent';
|
import { getAgentDisplayName } from '../utils/agent';
|
||||||
import { Markdown } from './Markdown';
|
import { Markdown } from './Markdown';
|
||||||
import { FileMentionText } from './FileMentionTag';
|
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';
|
import { AskUserQuestion } from './AskUserQuestion.js';
|
||||||
|
|
||||||
interface ChatMessageProps {
|
interface ChatMessageProps {
|
||||||
@@ -117,22 +117,17 @@ export const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回退:使用旧的 content + toolCalls 字段
|
// 回退:使用 content 字段(无 parts 时)
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="message-content text-fg-secondary">
|
||||||
{!isUser && message.toolCalls && message.toolCalls.length > 0 && (
|
{isUser ? (
|
||||||
<ToolCallsDisplay toolCalls={message.toolCalls} />
|
<div>
|
||||||
|
<FileMentionText text={message.content ?? ''} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Markdown content={message.content ?? ''} />
|
||||||
)}
|
)}
|
||||||
<div className="message-content text-fg-secondary">
|
</div>
|
||||||
{isUser ? (
|
|
||||||
<div>
|
|
||||||
<FileMentionText text={message.content ?? ''} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Markdown content={message.content ?? ''} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -384,109 +379,3 @@ function formatDuration(ms?: number): string {
|
|||||||
return `${(ms / 1000).toFixed(1)}s`;
|
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user