Skip to content

Commit

Permalink
feat: ToolUI addResult (#274)
Browse files Browse the repository at this point in the history
  • Loading branch information
Yonom authored Jun 21, 2024
1 parent 4820024 commit feaeadb
Show file tree
Hide file tree
Showing 26 changed files with 263 additions and 189 deletions.
2 changes: 1 addition & 1 deletion apps/www/components/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const supportedModels = [
component: ModalChat,
},
{
name: "Generative UI",
name: "Tool UI",
component: GenUI,
},
{
Expand Down
91 changes: 0 additions & 91 deletions apps/www/pages/docs/advanced/ToolRenderers.mdx

This file was deleted.

149 changes: 149 additions & 0 deletions apps/www/pages/docs/advanced/ToolUI.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
title: Tool UIs
---

You can show a custom UI when a tool is called to let the user know what is happening.

### Tool UI Components

```tsx
import { makeAssistantToolUI } from "@assistant-ui/react/experimental";

type WebSearchArgs = {
query: string;
};

type WebSearchResult = {
title: string;
description: string;
url: string;
};

const WebSearchToolUI = makeAssistantToolUI<WebSearchArgs, WebSearchResult>({
toolName: "web_search",
render: ({ part, status }) => {
return <p>web_search({part.args.query})</p>;
},
});
```

You can put this component anywhere in your app inside the `<AssistantRuntimeProvider />` component.

```tsx {1, 8}
import { WebSearchToolUI } from '@/tools/WebSearchToolUI';

const MyApp = () => {
...
return (
<AssistantRuntimeProvider runtime={runtime}>
...
<WebSearchToolUI />
...
</AssistantRuntimeProvider>
);
};
```

### Tool UI Hooks

```tsx
import { makeAssistantToolUI } from "@assistant-ui/react/experimental";

type WebSearchArgs = {
query: string;
};

type WebSearchResult = {
title: string;
description: string;
url: string;
};

const useWebSearchToolUI = makeAssistantToolUI<WebSearchArgs, WebSearchResult>({
toolName: "web_search",
render: ({ part, status }) => {
return <p>web_search({part.args.query})</p>;
},
});
```

You can use this hook anywhere in your app inside the `<AssistantRuntimeProvider />` component.

```tsx {1, 4}
import { useWebSearchToolUI } from '@/tools/useWebSearchToolUI';

const MyComponent = () => {
useWebSearchToolUI();

...
};

const MyApp = () => {
...
return (
<AssistantRuntimeProvider runtime={runtime}>
...
<MyComponent />
...
</AssistantRuntimeProvider>
);
};
```



### Inline Tool UI Hooks

If you need access to component props, you can use the `useAssistantToolUI` hook.

```tsx {1, 4}
import { useAssistantToolUI } from "@assistant-ui/react/experimental";
import { useCallback } from "react";

const MyComponent = ({ product_id }) => {
useAssistantToolUI({
toolName: "current_product_info",
render: useCallback(({ part, status }) => {
// you can access component props here
return <p>product_info({ product_id })</p>;
}, [product_id]),
})

...
};

const MyApp = () => {
...
return (
<AssistantRuntimeProvider runtime={runtime}>
...
<MyComponent product_id="123" />
...
</AssistantRuntimeProvider>
);
};
```

### Function Calling for User Input

The following example shows a `date_picker` tool that the AI can call to collect a date from the user.

```tsx {1, 4}
import { makeAssistantToolUI } from "@assistant-ui/react/experimental";
import { DatePicker } from "@/components/datepicker";

const DatePickerToolUI = makeAssistantToolUI<{}, { date: string }>({
toolName: "date_picker",
render: ({ part, status, addResult }) => {
if (part.result) {
return <p>You picked {part.result.date}</p>;
}

const handleSubmit = (date: Date) => {
addResult({ date: date.toISOString() });
};

return <DatePicker onSubmit={handleSubmit} />;
},
});
```
2 changes: 1 addition & 1 deletion apps/www/pages/docs/advanced/_meta.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const meta = {
ToolRenderers: "Tool Renderers",
ToolUI: "Tool UIs",
Branching: "Branching",
Editing: "Editing",
};
Expand Down
4 changes: 2 additions & 2 deletions apps/www/pages/reference/integrations/react-hook-form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Drop-in replacement hook for `useForm` that adds support for `@assistant-ui/reac
{
parameters: [
{
name: "renderer",
name: "render",
type: "ToolCallContentPartComponent<{ name: string; value: string; }, {}>",
description:
"The component to render when set_form_field is called.",
Expand All @@ -66,7 +66,7 @@ Drop-in replacement hook for `useForm` that adds support for `@assistant-ui/reac
{
parameters: [
{
name: "renderer",
name: "render",
type: "ToolCallContentPartComponent<{}, {}>",
description:
"The component to render when submit_form is called.",
Expand Down
6 changes: 6 additions & 0 deletions apps/www/pages/reference/runtime.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ const MyApp = () => {
required: true,
description: "A function to cancel a run.",
},
{
name: "addToolResult",
type: "(toolCallId: string, result: any) => void",
required: true,
description: "A function to add a tool result.",
},
{
name: "subscribe",
type: "(callback: () => void) => Unsubscribe",
Expand Down
6 changes: 2 additions & 4 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@
import { add } from "@/src/commands/add";
import { Command } from "commander";

import { getPackageInfo } from "./utils/get-package-info";
import packageJson from "../package.json";
import { create } from "./commands/create";

process.on("SIGINT", () => process.exit(0));
process.on("SIGTERM", () => process.exit(0));

async function main() {
const packageInfo = getPackageInfo();

const program = new Command()
.name("assistant-ui")
.description("add components and dependencies to your project")
.version(
packageInfo.version || "1.0.0",
packageJson.version || "1.0.0",
"-v, --version",
"display the version number",
);
Expand Down
14 changes: 0 additions & 14 deletions packages/cli/src/utils/get-package-info.ts

This file was deleted.

4 changes: 4 additions & 0 deletions packages/react-ai-sdk/src/rsc/VercelRSCRuntime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,8 @@ export class VercelRSCRuntime<T extends WeakKey = VercelRSCMessage>

return null;
};

addToolResult() {
throw new Error("VercelRSCRuntime does not support adding tool results");
}
}
7 changes: 7 additions & 0 deletions packages/react-ai-sdk/src/ui/VercelAIRuntime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,11 @@ export class VercelAIRuntime

return null;
};

addToolResult(toolCallId: string, result: any) {
if (!("addToolResult" in this.vercel)) {
throw new Error("VercelAIRuntime does not support adding tool results");
}
this.vercel.addToolResult({ toolCallId, result });
}
}
4 changes: 2 additions & 2 deletions packages/react-ai-sdk/src/ui/utils/VercelHelpers.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import type { UseAssistantHelpers, UseChatHelpers } from "@ai-sdk/react";
import type { UseAssistantHelpers, useChat } from "@ai-sdk/react";

export type VercelHelpers = UseChatHelpers | UseAssistantHelpers;
export type VercelHelpers = ReturnType<typeof useChat> | UseAssistantHelpers;
10 changes: 5 additions & 5 deletions packages/react-hook-form/src/useAssistantForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
type ModelConfig,
type ToolCallContentPartComponent,
useAssistantContext,
useAssistantToolRenderer,
useAssistantToolUI,
} from "@assistant-ui/react/experimental";
import { useEffect } from "react";
import {
Expand Down Expand Up @@ -101,20 +101,20 @@ export const useAssistantForm = <
}, [control, setValue, getValues, registerModelConfigProvider]);

const renderFormFieldTool = props?.assistant?.tools?.set_form_field?.render;
useAssistantToolRenderer(
useAssistantToolUI(
renderFormFieldTool
? {
name: "set_form_field",
toolName: "set_form_field",
render: renderFormFieldTool,
}
: null,
);

const renderSubmitFormTool = props?.assistant?.tools?.submit_form?.render;
useAssistantToolRenderer(
useAssistantToolUI(
renderSubmitFormTool
? {
name: "submit_form",
toolName: "submit_form",
render: renderSubmitFormTool,
}
: null,
Expand Down
Loading

0 comments on commit feaeadb

Please sign in to comment.