carbon-tokenization / frontend /src /utils /ai-tool-parts.ts
tfrere's picture
tfrere HF Staff
feat(editor): embed studio with data files and agent-aware editing
8fc8501
Raw
History Blame Contribute Delete
2.23 kB
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;
}