Files
assistant-ui--assistant-ui/packages/react-ai-sdk/src/frontendTools.ts
T
wehub-resource-sync e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:40:13 +08:00

81 lines
2.4 KiB
TypeScript

import { jsonSchema, type ToolSet } from "ai";
import type { ToolJSONSchema } from "assistant-stream";
import { unwrapModelContentEnvelope } from "./modelContentEnvelope";
import { toAISDKContent, toAISDKDefaultOutput } from "./toolOutputConversion";
/** Frontend tool definitions uploaded by AssistantChatTransport. */
export type FrontendTools = Record<string, ToolJSONSchema>;
export const defaultToModelOutput = ({ output }: { output: unknown }) => {
const { result, modelContent } = unwrapModelContentEnvelope(output);
if (modelContent !== undefined) {
return toAISDKContent(modelContent);
}
return toAISDKDefaultOutput(result);
};
const isPlainObject = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null && !Array.isArray(value);
function validateFrontendTool(
name: string,
tool: unknown,
): asserts tool is ToolJSONSchema {
if (!isPlainObject(tool)) {
throw new Error(
`frontendTools() expected tool "${name}" to be an object with a JSON Schema parameters object.`,
);
}
if (!isPlainObject(tool.parameters)) {
throw new Error(
`frontendTools() expected tool "${name}" to include a JSON Schema parameters object.`,
);
}
if (tool.description !== undefined && typeof tool.description !== "string") {
throw new Error(
`frontendTools() expected tool "${name}" description to be a string.`,
);
}
if (
tool.providerOptions !== undefined &&
!isPlainObject(tool.providerOptions)
) {
throw new Error(
`frontendTools() expected tool "${name}" providerOptions to be an object.`,
);
}
}
function validateFrontendTools(tools: unknown): asserts tools is FrontendTools {
if (!isPlainObject(tools)) {
throw new Error(
"frontendTools() expected tools to be an object keyed by tool name.",
);
}
for (const [name, tool] of Object.entries(tools)) {
validateFrontendTool(name, tool);
}
}
export const frontendTools = (tools: FrontendTools): ToolSet => {
validateFrontendTools(tools);
return Object.fromEntries(
Object.entries(tools).map(([name, tool]) => [
name,
{
...(tool.description !== undefined && {
description: tool.description,
}),
inputSchema: jsonSchema(tool.parameters),
toModelOutput: defaultToModelOutput,
...(tool.providerOptions && { providerOptions: tool.providerOptions }),
},
]),
) as ToolSet;
};