Show in-progress tool calls

This commit is contained in:
2026-06-05 22:20:56 -07:00
parent f71b69ca8b
commit fccc8110f4
14 changed files with 382 additions and 177 deletions

View File

@@ -435,7 +435,7 @@ type ToolLogMetadata = {
kind: "tool_call";
toolCallId?: string;
toolName?: string;
status?: "completed" | "failed";
status?: "initiated" | "completed" | "failed";
summary?: string;
args?: Record<string, unknown>;
startedAt?: string;
@@ -461,28 +461,48 @@ function isDisplayableMessage(message: Message) {
}
function buildOptimisticToolMessage(event: ToolCallEvent): Message {
const metadata: ToolLogMetadata = {
kind: "tool_call",
toolCallId: event.toolCallId,
toolName: event.name,
status: event.status,
summary: event.summary,
args: event.args,
startedAt: event.startedAt,
error: event.error ?? null,
resultPreview: event.resultPreview ?? null,
};
if (event.completedAt) metadata.completedAt = event.completedAt;
if (typeof event.durationMs === "number") metadata.durationMs = event.durationMs;
return {
id: `temp-tool-${event.toolCallId}`,
createdAt: event.completedAt ?? new Date().toISOString(),
createdAt: event.completedAt ?? event.startedAt ?? new Date().toISOString(),
role: "tool",
content: event.summary,
name: event.name,
metadata: {
kind: "tool_call",
toolCallId: event.toolCallId,
toolName: event.name,
status: event.status,
summary: event.summary,
args: event.args,
startedAt: event.startedAt,
completedAt: event.completedAt,
durationMs: event.durationMs,
error: event.error ?? null,
resultPreview: event.resultPreview ?? null,
} satisfies ToolLogMetadata,
metadata,
};
}
function upsertOptimisticToolMessage(messages: Message[], event: ToolCallEvent, assistantMessagePrefix: string) {
const toolMessage = buildOptimisticToolMessage(event);
const existingIndex = messages.findIndex(
(message) => asToolLogMetadata(message.metadata)?.toolCallId === event.toolCallId || message.id === `temp-tool-${event.toolCallId}`
);
if (existingIndex >= 0) {
return messages.map((message, index) => (index === existingIndex ? { ...toolMessage, id: message.id } : message));
}
const assistantIndex = messages.findIndex(
(message, index, all) => index === all.length - 1 && message.id.startsWith(assistantMessagePrefix)
);
if (assistantIndex < 0) return messages.concat(toolMessage);
return [...messages.slice(0, assistantIndex), toolMessage, ...messages.slice(assistantIndex)];
}
type ModelComboboxProps = {
options: string[];
value: string;
@@ -2093,33 +2113,10 @@ export default function App() {
setPendingChatStates((current) => {
const pendingState = current[chatId];
if (!pendingState) return current;
if (
pendingState.messages.some(
(message) =>
asToolLogMetadata(message.metadata)?.toolCallId === payload.toolCallId || message.id === `temp-tool-${payload.toolCallId}`
)
) {
return current;
}
const toolMessage = buildOptimisticToolMessage(payload);
const assistantIndex = pendingState.messages.findIndex(
(message, index, all) => index === all.length - 1 && message.id.startsWith("temp-assistant-")
);
if (assistantIndex < 0) {
return {
...current,
[chatId]: { messages: pendingState.messages.concat(toolMessage) },
};
}
return {
...current,
[chatId]: {
messages: [
...pendingState.messages.slice(0, assistantIndex),
toolMessage,
...pendingState.messages.slice(assistantIndex),
],
messages: upsertOptimisticToolMessage(pendingState.messages, payload, "temp-assistant-"),
},
};
});
@@ -2359,30 +2356,10 @@ export default function App() {
setPendingChatStates((current) => {
const pendingState = current[chatId];
if (!pendingState) return current;
if (
pendingState.messages.some(
(message) =>
asToolLogMetadata(message.metadata)?.toolCallId === payload.toolCallId || message.id === `temp-tool-${payload.toolCallId}`
)
) {
return current;
}
const toolMessage = buildOptimisticToolMessage(payload);
const assistantIndex = pendingState.messages.findIndex(
(message, index, all) => index === all.length - 1 && message.id.startsWith("temp-assistant-")
);
if (assistantIndex < 0) {
return { ...current, [chatId]: { messages: pendingState.messages.concat(toolMessage) } };
}
return {
...current,
[chatId]: {
messages: [
...pendingState.messages.slice(0, assistantIndex),
toolMessage,
...pendingState.messages.slice(assistantIndex),
],
messages: upsertOptimisticToolMessage(pendingState.messages, payload, "temp-assistant-"),
},
};
});
@@ -2649,25 +2626,7 @@ export default function App() {
{
onToolCall: (payload) => {
setQuickQuestionMessages((current) => {
if (
current.some(
(message) =>
asToolLogMetadata(message.metadata)?.toolCallId === payload.toolCallId || message.id === `temp-tool-${payload.toolCallId}`
)
) {
return current;
}
const toolMessage = buildOptimisticToolMessage(payload);
const assistantIndex = current.findIndex(
(message, index, all) => index === all.length - 1 && message.id.startsWith("temp-assistant-quick-")
);
if (assistantIndex < 0) return current.concat(toolMessage);
return [
...current.slice(0, assistantIndex),
toolMessage,
...current.slice(assistantIndex),
];
return upsertOptimisticToolMessage(current, payload, "temp-assistant-quick-");
});
},
onDelta: (payload) => {