Spaces:
Sleeping
Sleeping
| import type { UIMessage } from "ai"; | |
| /** | |
| * Normalized view over an ai-sdk message part that represents a tool call. | |
| * | |
| * ai-sdk v6 replaced the single `{ type: "tool-invocation", toolInvocation }` | |
| * part with typed parts of shape `{ type: "tool-${toolName}", toolCallId, state, | |
| * input, output }`. Some of our code and persisted chats still carry the | |
| * legacy shape, so we accept both and expose a single normalized structure. | |
| */ | |
| export interface NormalizedToolPart { | |
| /** Tool name, e.g. "replaceSelection". */ | |
| toolName: string; | |
| /** "result" when the call has produced output, otherwise the in-flight state. */ | |
| state: string; | |
| /** Arguments passed to the tool (v5: part.toolInvocation.args, v6: part.input). */ | |
| input?: unknown; | |
| result?: unknown; | |
| } | |
| type AnyPart = UIMessage["parts"][number]; | |
| /** Returns true if the part represents a tool call (v5 or v6 shape). */ | |
| export function isToolPart(part: AnyPart | undefined | null): boolean { | |
| if (!part) return false; | |
| const type = (part as { type?: string }).type; | |
| if (!type) return false; | |
| return type === "tool-invocation" || type.startsWith("tool-"); | |
| } | |
| /** Extract tool name + state from either v5 or v6 tool parts. */ | |
| export function normalizeToolPart(part: AnyPart): NormalizedToolPart | null { | |
| const anyPart = part as Record<string, unknown>; | |
| const type = anyPart.type as string | undefined; | |
| if (!type) return null; | |
| if (type === "tool-invocation") { | |
| const inv = anyPart.toolInvocation as | |
| | { toolName?: string; state?: string; args?: unknown; result?: unknown } | |
| | undefined; | |
| if (!inv?.toolName) return null; | |
| return { | |
| toolName: inv.toolName, | |
| state: inv.state ?? "call", | |
| input: inv.args, | |
| result: inv.result, | |
| }; | |
| } | |
| if (type.startsWith("tool-")) { | |
| const toolName = type.slice("tool-".length); | |
| const state = (anyPart.state as string | undefined) ?? "input-available"; | |
| // Normalize v6 states into the v5 "result" marker expected by UI labels. | |
| const normalized = state === "output-available" || state === "output-error" ? "result" : state; | |
| return { | |
| toolName, | |
| state: normalized, | |
| input: anyPart.input, | |
| result: anyPart.output, | |
| }; | |
| } | |
| return null; | |
| } | |