diff --git a/apps/www/components/docs/ParametersTable.tsx b/apps/www/components/docs/ParametersTable.tsx index ff30806572..14365ad74f 100644 --- a/apps/www/components/docs/ParametersTable.tsx +++ b/apps/www/components/docs/ParametersTable.tsx @@ -51,7 +51,7 @@ const ParametersList = ({ }; const ParametersBox: FC = ({ type, parameters }) => { return ( -
+
{!!type && (

{type} diff --git a/apps/www/pages/docs/advanced/ToolRenderers.mdx b/apps/www/pages/docs/advanced/ToolRenderers.mdx new file mode 100644 index 0000000000..e57de361aa --- /dev/null +++ b/apps/www/pages/docs/advanced/ToolRenderers.mdx @@ -0,0 +1,96 @@ +--- +title: Tool Renderers +--- + +You can show a custom UI when a tool is called to let the user know what is happening. + +### Tool Renderer Components + +```tsx +import { makeToolRenderer } from '@assistant-ui/react/experimental'; + +type WebSearchArgs = { + query: string; +}; + +type WebSearchResult = { + title: string; + description: string; + url: string; +}; + +const WebSearch = makeToolRenderer({ + name: 'web_search', + render: ({ part, status }) => { + return ( +

web_search({part.args.query})

+ ); + }, +}); +``` + +You can put this component anywhere in your app inside the `` component. + +```tsx {1, 8} +import { useWebSearch } from '@/tools/useWebSearch'; + +const MyApp = () => { + ... + return ( + + ... + + ... + + ); +}; +``` + +### Tool Renderer Hooks + + +```tsx +import { makeToolRenderer } from '@assistant-ui/react/experimental'; + +type WebSearchArgs = { + query: string; +}; + +type WebSearchResult = { + title: string; + description: string; + url: string; +}; + +const useWebSearch = makeToolRenderer({ + name: 'web_search', + render: ({ part, status }) => { + return ( +

web_search({part.args.query})

+ ); + }, +}); +``` + + +You can use this hook anywhere in your app inside the `` component. + +```tsx {1, 4} +import { useWebSearch } from '@/tools/useWebSearch'; + +const MyComponent = () => { + useWebSearch(); + + ... +}; + +const MyApp = () => { + ... + return ( + + ... + + ... + + ); +}; \ No newline at end of file diff --git a/apps/www/pages/docs/advanced/_meta.tsx b/apps/www/pages/docs/advanced/_meta.tsx index 85862fee35..c947079b4c 100644 --- a/apps/www/pages/docs/advanced/_meta.tsx +++ b/apps/www/pages/docs/advanced/_meta.tsx @@ -1,4 +1,5 @@ export default { + ToolRenderers: "Tool Renderers", Branching: "Branching", Editing: "Editing", }; diff --git a/apps/www/pages/reference/runtime.mdx b/apps/www/pages/reference/runtime.mdx index ba6f6e3368..08ed5727dc 100644 --- a/apps/www/pages/reference/runtime.mdx +++ b/apps/www/pages/reference/runtime.mdx @@ -27,6 +27,7 @@ const MyApp = () => { #### Properties { description: "The runtime to provide to the rest of your app.", children: [ { + type: "AssistantRuntime", parameters: [ { name: "messages", diff --git a/packages/react/src/experimental.ts b/packages/react/src/experimental.ts index d1b8f6421e..45b71d9148 100644 --- a/packages/react/src/experimental.ts +++ b/packages/react/src/experimental.ts @@ -22,5 +22,13 @@ export type { export * from "./context"; export { useAssistantInstructions } from "./model-config/useAssistantInstructions"; -export { useAssistantTool } from "./model-config/useAssistantTool"; -export { useAssistantToolRenderer } from "./model-config/useAssistantToolRenderer"; +export { + useAssistantTool, + type AssistantToolProps, +} from "./model-config/useAssistantTool"; +export { + useAssistantToolRenderer, + type AssistantToolRendererProps, +} from "./model-config/useAssistantToolRenderer"; +export { makeTool } from "./model-config/makeTool"; +export { makeToolRenderer } from "./model-config/makeToolRenderer"; diff --git a/packages/react/src/model-config/makeTool.tsx b/packages/react/src/model-config/makeTool.tsx new file mode 100644 index 0000000000..03be95cc37 --- /dev/null +++ b/packages/react/src/model-config/makeTool.tsx @@ -0,0 +1,12 @@ +"use client"; +import { type AssistantToolProps, useAssistantTool } from "./useAssistantTool"; + +export const makeTool = ( + tool: AssistantToolProps, +) => { + const Tool = () => { + useAssistantTool(tool); + return null; + }; + return Tool; +}; diff --git a/packages/react/src/model-config/makeToolRenderer.tsx b/packages/react/src/model-config/makeToolRenderer.tsx new file mode 100644 index 0000000000..9e55371348 --- /dev/null +++ b/packages/react/src/model-config/makeToolRenderer.tsx @@ -0,0 +1,15 @@ +"use client"; +import { + type AssistantToolRendererProps, + useAssistantToolRenderer, +} from "./useAssistantToolRenderer"; + +export const makeToolRenderer = ( + tool: AssistantToolRendererProps, +) => { + const ToolRenderer = () => { + useAssistantToolRenderer(tool); + return null; + }; + return ToolRenderer; +}; diff --git a/packages/react/src/model-config/useAssistantTool.tsx b/packages/react/src/model-config/useAssistantTool.tsx index 15addf75a3..18d822af22 100644 --- a/packages/react/src/model-config/useAssistantTool.tsx +++ b/packages/react/src/model-config/useAssistantTool.tsx @@ -5,13 +5,13 @@ import { useAssistantContext } from "../context/AssistantContext"; import type { ToolCallContentPartComponent } from "../primitives/message/ContentPartComponentTypes"; import type { Tool } from "../utils/ModelConfigTypes"; -export type UseAssistantTool = Tool & { +export type AssistantToolProps = Tool & { name: string; render?: ToolCallContentPartComponent; }; export const useAssistantTool = ( - tool: UseAssistantTool, + tool: AssistantToolProps, ) => { const { useModelConfig, useToolRenderers } = useAssistantContext(); const registerModelConfigProvider = useModelConfig( diff --git a/packages/react/src/model-config/useAssistantToolRenderer.tsx b/packages/react/src/model-config/useAssistantToolRenderer.tsx index 172f7ad111..7fe477c1c5 100644 --- a/packages/react/src/model-config/useAssistantToolRenderer.tsx +++ b/packages/react/src/model-config/useAssistantToolRenderer.tsx @@ -3,14 +3,14 @@ import { useEffect } from "react"; import { useAssistantContext } from "../context/AssistantContext"; import type { ToolCallContentPartComponent } from "../primitives/message/ContentPartComponentTypes"; -type UseAssistantToolRenderer = { +export type AssistantToolRendererProps = { name: string; render: ToolCallContentPartComponent; }; export const useAssistantToolRenderer = ( // biome-ignore lint/suspicious/noExplicitAny: intentional any - tool: UseAssistantToolRenderer | null, + tool: AssistantToolRendererProps | null, ) => { const { useToolRenderers } = useAssistantContext(); const setToolRenderer = useToolRenderers((s) => s.setToolRenderer);