[feature] adds web_search and fetch_url tool calls

This commit is contained in:
2026-03-02 16:13:34 -08:00
parent c47646a48c
commit d5b06ce22a
12 changed files with 951 additions and 48 deletions

View File

@@ -27,6 +27,7 @@ import {
type Message,
type SearchDetail,
type SearchSummary,
type ToolCallEvent,
} from "@/lib/api";
import { useSessionAuth } from "@/hooks/use-session-auth";
import { cn } from "@/lib/utils";
@@ -139,6 +140,54 @@ function getChatModelSelection(chat: Pick<ChatSummary, "lastUsedProvider" | "las
};
}
type ToolLogMetadata = {
kind: "tool_call";
toolCallId?: string;
toolName?: string;
status?: "completed" | "failed";
summary?: string;
args?: Record<string, unknown>;
startedAt?: string;
completedAt?: string;
durationMs?: number;
error?: string | null;
resultPreview?: string | null;
};
function asToolLogMetadata(value: unknown): ToolLogMetadata | null {
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
const record = value as Record<string, unknown>;
if (record.kind !== "tool_call") return null;
return record as ToolLogMetadata;
}
function isToolCallLogMessage(message: Message) {
return asToolLogMetadata(message.metadata) !== null;
}
function buildOptimisticToolMessage(event: ToolCallEvent): Message {
return {
id: `temp-tool-${event.toolCallId}`,
createdAt: event.completedAt ?? 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,
};
}
type ModelComboboxProps = {
options: string[];
value: string;
@@ -707,6 +756,7 @@ export default function App() {
role: "user",
content,
name: null,
metadata: null,
};
const optimisticAssistantMessage: Message = {
@@ -715,6 +765,7 @@ export default function App() {
role: "assistant",
content: "",
name: null,
metadata: null,
};
setPendingChatState({
@@ -758,11 +809,13 @@ export default function App() {
}
const requestMessages: CompletionRequestMessage[] = [
...baseChat.messages.map((message) => ({
...baseChat.messages
.filter((message) => !isToolCallLogMessage(message))
.map((message) => ({
role: message.role,
content: message.content,
...(message.name ? { name: message.name } : {}),
})),
})),
{
role: "user",
content,
@@ -813,6 +866,35 @@ export default function App() {
if (payload.chatId !== chatId) return;
setPendingChatState((current) => (current ? { ...current, chatId: payload.chatId } : current));
},
onToolCall: (payload) => {
setPendingChatState((current) => {
if (!current) return current;
if (
current.messages.some(
(message) =>
asToolLogMetadata(message.metadata)?.toolCallId === payload.toolCallId || message.id === `temp-tool-${payload.toolCallId}`
)
) {
return current;
}
const toolMessage = buildOptimisticToolMessage(payload);
const assistantIndex = current.messages.findIndex(
(message, index, all) => index === all.length - 1 && message.id.startsWith("temp-assistant-")
);
if (assistantIndex < 0) {
return { ...current, messages: current.messages.concat(toolMessage) };
}
return {
...current,
messages: [
...current.messages.slice(0, assistantIndex),
toolMessage,
...current.messages.slice(assistantIndex),
],
};
});
},
onDelta: (payload) => {
if (!payload.text) return;
setPendingChatState((current) => {