@assistant-ui/core 0.2.9 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/model-context/tool.d.ts +1 -1
- package/dist/model-context/tool.js +1 -1
- package/dist/model-context/tool.js.map +1 -1
- package/dist/model-context/types.js +17 -2
- package/dist/model-context/types.js.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
- package/dist/react/adapters/LocalStorageThreadListAdapter.js +12 -2
- package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
- package/dist/react/client/Tools.d.ts.map +1 -1
- package/dist/react/client/Tools.js +9 -3
- package/dist/react/client/Tools.js.map +1 -1
- package/dist/react/index.d.ts +7 -3
- package/dist/react/index.js +6 -2
- package/dist/react/model-context/define-mcp-toolkit.d.ts +12 -0
- package/dist/react/model-context/define-mcp-toolkit.d.ts.map +1 -0
- package/dist/react/model-context/define-mcp-toolkit.js +14 -0
- package/dist/react/model-context/define-mcp-toolkit.js.map +1 -0
- package/dist/react/model-context/define-toolkit.d.ts +4 -3
- package/dist/react/model-context/define-toolkit.d.ts.map +1 -1
- package/dist/react/model-context/define-toolkit.js +1 -14
- package/dist/react/model-context/define-toolkit.js.map +1 -1
- package/dist/react/model-context/hitl.d.ts +8 -4
- package/dist/react/model-context/hitl.d.ts.map +1 -1
- package/dist/react/model-context/hitl.js +9 -5
- package/dist/react/model-context/hitl.js.map +1 -1
- package/dist/react/model-context/makeAssistantTool.d.ts +8 -0
- package/dist/react/model-context/makeAssistantTool.d.ts.map +1 -1
- package/dist/react/model-context/makeAssistantTool.js +4 -0
- package/dist/react/model-context/makeAssistantTool.js.map +1 -1
- package/dist/react/model-context/makeAssistantToolUI.d.ts +8 -0
- package/dist/react/model-context/makeAssistantToolUI.d.ts.map +1 -1
- package/dist/react/model-context/makeAssistantToolUI.js +4 -0
- package/dist/react/model-context/makeAssistantToolUI.js.map +1 -1
- package/dist/react/model-context/provider-tool.d.ts +15 -0
- package/dist/react/model-context/provider-tool.d.ts.map +1 -0
- package/dist/react/model-context/provider-tool.js +12 -0
- package/dist/react/model-context/provider-tool.js.map +1 -0
- package/dist/react/model-context/stub-tool.d.ts +12 -0
- package/dist/react/model-context/stub-tool.d.ts.map +1 -0
- package/dist/react/model-context/stub-tool.js +15 -0
- package/dist/react/model-context/stub-tool.js.map +1 -0
- package/dist/react/model-context/toolbox.d.ts +62 -15
- package/dist/react/model-context/toolbox.d.ts.map +1 -1
- package/dist/react/model-context/toolbox.js +19 -1
- package/dist/react/model-context/toolbox.js.map +1 -1
- package/dist/react/model-context/useAssistantTool.d.ts +11 -1
- package/dist/react/model-context/useAssistantTool.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantTool.js +12 -6
- package/dist/react/model-context/useAssistantTool.js.map +1 -1
- package/dist/react/model-context/useAssistantToolUI.d.ts +13 -4
- package/dist/react/model-context/useAssistantToolUI.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantToolUI.js +6 -3
- package/dist/react/model-context/useAssistantToolUI.js.map +1 -1
- package/dist/react/model-context/useAuiToolOverrides.d.ts +22 -0
- package/dist/react/model-context/useAuiToolOverrides.d.ts.map +1 -0
- package/dist/react/model-context/useAuiToolOverrides.js +31 -0
- package/dist/react/model-context/useAuiToolOverrides.js.map +1 -0
- package/dist/react/primitives/part/PartMessages.d.ts +13 -11
- package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
- package/dist/react/primitives/part/PartMessages.js +13 -11
- package/dist/react/primitives/part/PartMessages.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +1 -0
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +28 -0
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +9 -2
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.d.ts +2 -0
- package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.js +6 -0
- package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +1 -0
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-adapter.d.ts +2 -0
- package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +1 -0
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +5 -0
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
- package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts +1 -0
- package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/adapter/in-memory.js +3 -0
- package/dist/runtimes/remote-thread-list/adapter/in-memory.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +1 -0
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -0
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
- package/dist/store/scopes/thread-list-item.d.ts +1 -0
- package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/model-context/tool.ts +1 -1
- package/src/model-context/types.ts +21 -3
- package/src/react/adapters/LocalStorageThreadListAdapter.tsx +15 -2
- package/src/react/client/Tools.ts +22 -7
- package/src/react/index.ts +14 -3
- package/src/react/model-context/define-mcp-toolkit.ts +16 -0
- package/src/react/model-context/define-toolkit.test.ts +92 -4
- package/src/react/model-context/define-toolkit.ts +21 -3
- package/src/react/model-context/hitl.ts +10 -5
- package/src/react/model-context/makeAssistantTool.ts +8 -0
- package/src/react/model-context/makeAssistantToolUI.ts +8 -0
- package/src/react/model-context/provider-tool.ts +30 -0
- package/src/react/model-context/stub-tool.ts +14 -0
- package/src/react/model-context/toolbox.test.ts +182 -0
- package/src/react/model-context/toolbox.ts +189 -21
- package/src/react/model-context/useAssistantTool.ts +28 -8
- package/src/react/model-context/useAssistantToolUI.ts +13 -4
- package/src/react/model-context/useAuiToolOverrides.ts +38 -0
- package/src/react/primitives/part/PartMessages.tsx +13 -11
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +43 -0
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +9 -0
- package/src/runtime/api/thread-list-item-runtime.ts +15 -0
- package/src/runtime/interfaces/thread-list-runtime-core.ts +4 -0
- package/src/runtimes/external-store/external-store-adapter.ts +7 -0
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +13 -0
- package/src/runtimes/remote-thread-list/adapter/in-memory.ts +4 -0
- package/src/runtimes/remote-thread-list/types.ts +4 -0
- package/src/store/clients/model-context-client.test.ts +87 -2
- package/src/store/runtime-clients/thread-list-item-runtime-client.ts +1 -0
- package/src/store/scopes/thread-list-item.ts +1 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-custom-metadata.test.ts +69 -1
- package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { makeToolCallTextComponent } from "./toolbox";
|
|
3
|
+
|
|
4
|
+
describe("makeToolCallTextComponent", () => {
|
|
5
|
+
it("renders static running and complete text", () => {
|
|
6
|
+
const Render = makeToolCallTextComponent({
|
|
7
|
+
running: "Searching...",
|
|
8
|
+
complete: "Done searching",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
expect(
|
|
12
|
+
Render({
|
|
13
|
+
type: "tool-call",
|
|
14
|
+
toolCallId: "call-1",
|
|
15
|
+
toolName: "search",
|
|
16
|
+
args: {},
|
|
17
|
+
argsText: "{}",
|
|
18
|
+
status: { type: "running" },
|
|
19
|
+
addResult: () => {},
|
|
20
|
+
resume: () => {},
|
|
21
|
+
respondToApproval: () => {},
|
|
22
|
+
}),
|
|
23
|
+
).toBe("Searching...");
|
|
24
|
+
|
|
25
|
+
expect(
|
|
26
|
+
Render({
|
|
27
|
+
type: "tool-call",
|
|
28
|
+
toolCallId: "call-1",
|
|
29
|
+
toolName: "search",
|
|
30
|
+
args: {},
|
|
31
|
+
argsText: "{}",
|
|
32
|
+
result: "ok",
|
|
33
|
+
status: { type: "complete" },
|
|
34
|
+
addResult: () => {},
|
|
35
|
+
resume: () => {},
|
|
36
|
+
respondToApproval: () => {},
|
|
37
|
+
}),
|
|
38
|
+
).toBe("Done searching");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("passes args and result to dynamic text functions", () => {
|
|
42
|
+
const Render = makeToolCallTextComponent<
|
|
43
|
+
{ query: string },
|
|
44
|
+
{ count: number }
|
|
45
|
+
>({
|
|
46
|
+
running: ({ args }) => `Searching ${args.query}...`,
|
|
47
|
+
complete: ({ args, result }) =>
|
|
48
|
+
`Found ${result?.count ?? 0} results for ${args.query}`,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(
|
|
52
|
+
Render({
|
|
53
|
+
type: "tool-call",
|
|
54
|
+
toolCallId: "call-1",
|
|
55
|
+
toolName: "search",
|
|
56
|
+
args: { query: "docs" },
|
|
57
|
+
argsText: '{"query":"docs"}',
|
|
58
|
+
status: { type: "running" },
|
|
59
|
+
addResult: () => {},
|
|
60
|
+
resume: () => {},
|
|
61
|
+
respondToApproval: () => {},
|
|
62
|
+
}),
|
|
63
|
+
).toBe("Searching docs...");
|
|
64
|
+
|
|
65
|
+
expect(
|
|
66
|
+
Render({
|
|
67
|
+
type: "tool-call",
|
|
68
|
+
toolCallId: "call-1",
|
|
69
|
+
toolName: "search",
|
|
70
|
+
args: { query: "docs" },
|
|
71
|
+
argsText: '{"query":"docs"}',
|
|
72
|
+
result: { count: 3 },
|
|
73
|
+
status: { type: "complete" },
|
|
74
|
+
addResult: () => {},
|
|
75
|
+
resume: () => {},
|
|
76
|
+
respondToApproval: () => {},
|
|
77
|
+
}),
|
|
78
|
+
).toBe("Found 3 results for docs");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("treats missing status as complete", () => {
|
|
82
|
+
const Render = makeToolCallTextComponent({
|
|
83
|
+
running: "Running",
|
|
84
|
+
complete: "Complete",
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(
|
|
88
|
+
Render({
|
|
89
|
+
type: "tool-call",
|
|
90
|
+
toolCallId: "call-1",
|
|
91
|
+
toolName: "search",
|
|
92
|
+
args: {},
|
|
93
|
+
argsText: "{}",
|
|
94
|
+
addResult: () => {},
|
|
95
|
+
resume: () => {},
|
|
96
|
+
respondToApproval: () => {},
|
|
97
|
+
}),
|
|
98
|
+
).toBe("Complete");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("treats incomplete status as a terminal state", () => {
|
|
102
|
+
const Render = makeToolCallTextComponent({
|
|
103
|
+
running: "Searching...",
|
|
104
|
+
complete: "Finished",
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(
|
|
108
|
+
Render({
|
|
109
|
+
type: "tool-call",
|
|
110
|
+
toolCallId: "call-1",
|
|
111
|
+
toolName: "search",
|
|
112
|
+
args: {},
|
|
113
|
+
argsText: "{}",
|
|
114
|
+
status: { type: "incomplete", reason: "error" },
|
|
115
|
+
addResult: () => {},
|
|
116
|
+
resume: () => {},
|
|
117
|
+
respondToApproval: () => {},
|
|
118
|
+
}),
|
|
119
|
+
).toBe("Finished");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("treats requires-action status as running", () => {
|
|
123
|
+
const Render = makeToolCallTextComponent({
|
|
124
|
+
running: "Waiting for approval...",
|
|
125
|
+
complete: "Done",
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(
|
|
129
|
+
Render({
|
|
130
|
+
type: "tool-call",
|
|
131
|
+
toolCallId: "call-1",
|
|
132
|
+
toolName: "confirm",
|
|
133
|
+
args: {},
|
|
134
|
+
argsText: "{}",
|
|
135
|
+
status: { type: "requires-action", reason: "interrupt" },
|
|
136
|
+
addResult: () => {},
|
|
137
|
+
resume: () => {},
|
|
138
|
+
respondToApproval: () => {},
|
|
139
|
+
}),
|
|
140
|
+
).toBe("Waiting for approval...");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("renders null for terminal status when only running text is provided", () => {
|
|
144
|
+
const Render = makeToolCallTextComponent({
|
|
145
|
+
running: "Searching...",
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(
|
|
149
|
+
Render({
|
|
150
|
+
type: "tool-call",
|
|
151
|
+
toolCallId: "call-1",
|
|
152
|
+
toolName: "search",
|
|
153
|
+
args: {},
|
|
154
|
+
argsText: "{}",
|
|
155
|
+
status: { type: "complete" },
|
|
156
|
+
addResult: () => {},
|
|
157
|
+
resume: () => {},
|
|
158
|
+
respondToApproval: () => {},
|
|
159
|
+
}),
|
|
160
|
+
).toBeNull();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("renders null for running status when only complete text is provided", () => {
|
|
164
|
+
const Render = makeToolCallTextComponent({
|
|
165
|
+
complete: "Done",
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
expect(
|
|
169
|
+
Render({
|
|
170
|
+
type: "tool-call",
|
|
171
|
+
toolCallId: "call-1",
|
|
172
|
+
toolName: "search",
|
|
173
|
+
args: {},
|
|
174
|
+
argsText: "{}",
|
|
175
|
+
status: { type: "running" },
|
|
176
|
+
addResult: () => {},
|
|
177
|
+
resume: () => {},
|
|
178
|
+
respondToApproval: () => {},
|
|
179
|
+
}),
|
|
180
|
+
).toBeNull();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
Tool,
|
|
3
|
+
ToolCallReader,
|
|
4
|
+
ToolDeclaration,
|
|
5
|
+
ToolModelOutputFunction,
|
|
6
|
+
} from "assistant-stream";
|
|
7
|
+
import type { ReactNode } from "react";
|
|
8
|
+
import type {
|
|
9
|
+
ToolCallMessagePartComponent,
|
|
10
|
+
ToolCallMessagePartProps,
|
|
11
|
+
} from "../types/MessagePartComponentTypes";
|
|
3
12
|
|
|
4
13
|
/**
|
|
5
14
|
* Resolves whether a tool's UI should be presented standalone (outside the
|
|
@@ -20,23 +29,96 @@ export const isStandaloneToolDisplay = (
|
|
|
20
29
|
type WithRender<T, TArgs extends Record<string, unknown>, TResult> = T extends {
|
|
21
30
|
type: "frontend" | "human";
|
|
22
31
|
}
|
|
23
|
-
? T &
|
|
32
|
+
? T &
|
|
33
|
+
(T extends { type: "frontend" }
|
|
34
|
+
?
|
|
35
|
+
| { render: ToolCallMessagePartComponent<TArgs, TResult> }
|
|
36
|
+
| {
|
|
37
|
+
render?: ToolCallMessagePartComponent<TArgs, TResult>;
|
|
38
|
+
renderText: ToolCallText<TArgs, TResult>;
|
|
39
|
+
}
|
|
40
|
+
: { render: ToolCallMessagePartComponent<TArgs, TResult> })
|
|
24
41
|
: T & {
|
|
25
42
|
render?: ToolCallMessagePartComponent<TArgs, TResult> | undefined;
|
|
43
|
+
renderText?: ToolCallText<TArgs, TResult> | undefined;
|
|
26
44
|
};
|
|
27
45
|
|
|
46
|
+
type ToolParameters<TArgs extends Record<string, unknown>> =
|
|
47
|
+
ToolDeclaration<TArgs>["parameters"];
|
|
48
|
+
|
|
49
|
+
// ToolExecutionContext is not re-exported from assistant-stream's public entry.
|
|
50
|
+
type ToolExecuteContext = Parameters<
|
|
51
|
+
NonNullable<ToolDeclaration["execute"]>
|
|
52
|
+
>[1];
|
|
53
|
+
|
|
54
|
+
type ToolExecute<TArgs extends Record<string, unknown>, TResult> = (
|
|
55
|
+
args: TArgs,
|
|
56
|
+
context: ToolExecuteContext,
|
|
57
|
+
) => TResult | Promise<TResult>;
|
|
58
|
+
|
|
59
|
+
type ToolStreamCall<TArgs extends Record<string, unknown>, TResult> = (
|
|
60
|
+
reader: ToolCallReader<TArgs, TResult>,
|
|
61
|
+
context: ToolExecuteContext,
|
|
62
|
+
) => void;
|
|
63
|
+
|
|
64
|
+
type ToolCallRunningText<TArgs extends Record<string, unknown>> =
|
|
65
|
+
| ReactNode
|
|
66
|
+
| ((options: { args: TArgs }) => ReactNode);
|
|
67
|
+
|
|
68
|
+
type ToolCallCompleteText<TArgs extends Record<string, unknown>, TResult> =
|
|
69
|
+
| ReactNode
|
|
70
|
+
| ((options: { args: TArgs; result: TResult | undefined }) => ReactNode);
|
|
71
|
+
|
|
72
|
+
export type ToolCallText<TArgs extends Record<string, unknown>, TResult> =
|
|
73
|
+
| {
|
|
74
|
+
running: ToolCallRunningText<TArgs>;
|
|
75
|
+
complete?: ToolCallCompleteText<TArgs, TResult> | undefined;
|
|
76
|
+
}
|
|
77
|
+
| {
|
|
78
|
+
running?: ToolCallRunningText<TArgs> | undefined;
|
|
79
|
+
complete: ToolCallCompleteText<TArgs, TResult>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const resolveToolCallText = <TArgs extends Record<string, unknown>, TResult>(
|
|
83
|
+
text: ToolCallText<TArgs, TResult>,
|
|
84
|
+
part: ToolCallMessagePartProps<TArgs, TResult>,
|
|
85
|
+
): ReactNode => {
|
|
86
|
+
const isRunning =
|
|
87
|
+
part.status?.type === "running" || part.status?.type === "requires-action";
|
|
88
|
+
|
|
89
|
+
if (!isRunning) {
|
|
90
|
+
const value = text.complete;
|
|
91
|
+
if (typeof value !== "function") return value ?? null;
|
|
92
|
+
return value({ args: part.args, result: part.result });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const value = text.running;
|
|
96
|
+
if (typeof value !== "function") return value ?? null;
|
|
97
|
+
return value({ args: part.args });
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const makeToolCallTextComponent = <
|
|
101
|
+
TArgs extends Record<string, unknown>,
|
|
102
|
+
TResult,
|
|
103
|
+
>(
|
|
104
|
+
text: ToolCallText<TArgs, TResult>,
|
|
105
|
+
): ToolCallMessagePartComponent<TArgs, TResult> => {
|
|
106
|
+
return function ToolCallTextComponent(part) {
|
|
107
|
+
return resolveToolCallText(text, part);
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
28
111
|
/**
|
|
29
112
|
* Tool definition accepted by the React tool registry.
|
|
30
113
|
*
|
|
31
|
-
* Extends the core tool contract with
|
|
32
|
-
*
|
|
33
|
-
* browser and require
|
|
34
|
-
* tools execute server-side and may omit a renderer.
|
|
35
|
-
* required for frontend and human tools and optional for backend tools.
|
|
114
|
+
* Extends the core tool contract with tool-call display options. Human tools
|
|
115
|
+
* rely on `render` to collect input from the user. Frontend tools execute in
|
|
116
|
+
* the browser and require either `render` or `renderText` for their progress
|
|
117
|
+
* and result. Backend tools execute server-side and may omit a renderer.
|
|
36
118
|
*/
|
|
37
119
|
export type ToolDefinition<
|
|
38
|
-
TArgs extends Record<string, unknown>,
|
|
39
|
-
TResult,
|
|
120
|
+
TArgs extends Record<string, unknown> = Record<string, unknown>,
|
|
121
|
+
TResult = unknown,
|
|
40
122
|
> = WithRender<Tool<TArgs, TResult>, TArgs, TResult>;
|
|
41
123
|
|
|
42
124
|
/**
|
|
@@ -63,19 +145,96 @@ export type Toolkit = Record<string, ToolDefinition<any, any>>;
|
|
|
63
145
|
* A tool as authored, before the build splits it: like {@link ToolDefinition}
|
|
64
146
|
* but it may declare `description`, `parameters`, and a server-side `execute`
|
|
65
147
|
* alongside its `render`. The `type` field is **not** authored — the
|
|
66
|
-
* `"use generative"` compiler infers it (`execute:
|
|
67
|
-
*
|
|
68
|
-
*
|
|
148
|
+
* `"use generative"` compiler infers it (`execute: hitlTool()` → human;
|
|
149
|
+
* `execute: providerTool(...)` → provider; `execute` with a `"use client"`
|
|
150
|
+
* directive → frontend; otherwise backend) and writes it back — so declaring it
|
|
151
|
+
* here is a type error.
|
|
69
152
|
*/
|
|
70
|
-
|
|
153
|
+
type OverrideOptionalField<
|
|
154
|
+
T,
|
|
155
|
+
TKey extends keyof T,
|
|
156
|
+
TValue,
|
|
157
|
+
> = undefined extends T[TKey]
|
|
158
|
+
? // Preserve `?: undefined` fields (for variants that explicitly disallow a
|
|
159
|
+
// callback) instead of widening them to accept the override value.
|
|
160
|
+
Exclude<T[TKey], undefined> extends never
|
|
161
|
+
? { [K in TKey]?: undefined }
|
|
162
|
+
: { [K in TKey]?: TValue | undefined }
|
|
163
|
+
: { [K in TKey]: TValue };
|
|
164
|
+
|
|
165
|
+
type OverrideToolDeclarationCallbacks<
|
|
166
|
+
T extends { streamCall?: unknown },
|
|
167
|
+
TArgs extends Record<string, unknown>,
|
|
168
|
+
TResult,
|
|
169
|
+
> = Omit<
|
|
170
|
+
T,
|
|
171
|
+
| "type"
|
|
172
|
+
| "execute"
|
|
173
|
+
| "toModelOutput"
|
|
174
|
+
| "experimental_onSchemaValidationError"
|
|
175
|
+
| "streamCall"
|
|
176
|
+
> & {
|
|
177
|
+
type?: never;
|
|
178
|
+
} & ("execute" extends keyof T
|
|
179
|
+
? OverrideOptionalField<T, "execute", ToolExecute<NoInfer<TArgs>, TResult>>
|
|
180
|
+
: {}) &
|
|
181
|
+
("toModelOutput" extends keyof T
|
|
182
|
+
? OverrideOptionalField<
|
|
183
|
+
T,
|
|
184
|
+
"toModelOutput",
|
|
185
|
+
ToolModelOutputFunction<NoInfer<TArgs>, NoInfer<TResult>>
|
|
186
|
+
>
|
|
187
|
+
: {}) &
|
|
188
|
+
("experimental_onSchemaValidationError" extends keyof T
|
|
189
|
+
? OverrideOptionalField<
|
|
190
|
+
T,
|
|
191
|
+
"experimental_onSchemaValidationError",
|
|
192
|
+
(
|
|
193
|
+
args: unknown,
|
|
194
|
+
context: ToolExecuteContext,
|
|
195
|
+
) => NoInfer<TResult> | Promise<NoInfer<TResult>>
|
|
196
|
+
>
|
|
197
|
+
: {}) &
|
|
198
|
+
OverrideOptionalField<
|
|
199
|
+
T,
|
|
200
|
+
"streamCall",
|
|
201
|
+
ToolStreamCall<TArgs, NoInfer<TResult>>
|
|
202
|
+
>;
|
|
203
|
+
|
|
204
|
+
// Keep the authored shape tied to ToolDeclaration's union variants while
|
|
205
|
+
// overriding callback fields to avoid inference pollution.
|
|
206
|
+
type ToolkitDefinitionInput<
|
|
71
207
|
TArgs extends Record<string, unknown>,
|
|
72
208
|
TResult,
|
|
73
209
|
> = WithRender<
|
|
74
|
-
|
|
210
|
+
ToolDeclaration<TArgs, TResult> extends infer T
|
|
211
|
+
? T extends { streamCall?: unknown }
|
|
212
|
+
? OverrideToolDeclarationCallbacks<T, TArgs, TResult>
|
|
213
|
+
: never
|
|
214
|
+
: never,
|
|
75
215
|
TArgs,
|
|
76
216
|
TResult
|
|
77
|
-
|
|
78
|
-
|
|
217
|
+
>;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* A single entry in a {@link ToolkitDefinition}.
|
|
221
|
+
*
|
|
222
|
+
* Either authored inline (whose `type` the compiler infers) or an already-formed
|
|
223
|
+
* {@link ToolDefinition} produced by a factory whose own build splits it across
|
|
224
|
+
* targets — e.g. `new JSONGenerativeUI({ library }).present()`. The factory case
|
|
225
|
+
* carries a `type`, so it can only match the {@link ToolDefinition} arm of this
|
|
226
|
+
* union.
|
|
227
|
+
*/
|
|
228
|
+
export type ToolkitDefinitionEntry<
|
|
229
|
+
TArgs extends Record<string, unknown> = Record<string, unknown>,
|
|
230
|
+
TResult = unknown,
|
|
231
|
+
> = ToolkitDefinitionInput<TArgs, TResult> | ToolDefinition<any, any>;
|
|
232
|
+
|
|
233
|
+
export type ToolkitDefinitionEntryWithParameters<
|
|
234
|
+
TArgs extends Record<string, unknown> = Record<string, unknown>,
|
|
235
|
+
TResult = unknown,
|
|
236
|
+
> = ToolkitDefinitionInput<TArgs, TResult> & {
|
|
237
|
+
parameters: NonNullable<ToolParameters<TArgs>>;
|
|
79
238
|
};
|
|
80
239
|
|
|
81
240
|
/**
|
|
@@ -83,10 +242,19 @@ export type ToolkitDeclarationDefinition<
|
|
|
83
242
|
* {@link defineToolkit}. Backend entries may carry their server `execute` here;
|
|
84
243
|
* the canonical {@link Toolkit} keeps those fields `undefined`.
|
|
85
244
|
*/
|
|
86
|
-
export type
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
245
|
+
export type ToolkitDefinition<
|
|
246
|
+
TArgsByName extends {
|
|
247
|
+
[K in keyof TArgsByName]: Record<string, unknown>;
|
|
248
|
+
} = Record<string, any>,
|
|
249
|
+
TResultByName extends { [K in keyof TArgsByName]: unknown } = {
|
|
250
|
+
[K in keyof TArgsByName]: any;
|
|
251
|
+
},
|
|
252
|
+
> = {
|
|
253
|
+
[K in keyof TArgsByName]: ToolkitDefinitionEntry<
|
|
254
|
+
TArgsByName[K],
|
|
255
|
+
TResultByName[K]
|
|
256
|
+
>;
|
|
257
|
+
};
|
|
90
258
|
|
|
91
259
|
/** Configuration for the {@link Tools} resource. */
|
|
92
260
|
export type ToolsConfig = {
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
1
|
+
import { useEffect, useMemo } from "react";
|
|
2
2
|
import { useAui } from "@assistant-ui/store";
|
|
3
3
|
import type { ToolCallMessagePartComponent } from "../types/MessagePartComponentTypes";
|
|
4
4
|
import type { AssistantToolProps as CoreAssistantToolProps } from "../..";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
isStandaloneToolDisplay,
|
|
7
|
+
makeToolCallTextComponent,
|
|
8
|
+
type ToolCallText,
|
|
9
|
+
} from "./toolbox";
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* Props used to register a tool from React.
|
|
13
|
+
*
|
|
14
|
+
* @deprecated Use a toolkit with `Tools({ toolkit })` and register it via
|
|
15
|
+
* `useAui({ tools: Tools({ toolkit }) })` instead. See
|
|
16
|
+
* https://assistant-ui.com/docs/migrations/toolkit-tools.
|
|
9
17
|
*/
|
|
10
18
|
export type AssistantToolProps<
|
|
11
19
|
TArgs extends Record<string, unknown>,
|
|
@@ -13,6 +21,8 @@ export type AssistantToolProps<
|
|
|
13
21
|
> = CoreAssistantToolProps<TArgs, TResult> & {
|
|
14
22
|
/** Component used to render calls to this tool in assistant messages. */
|
|
15
23
|
render?: ToolCallMessagePartComponent<TArgs, TResult> | undefined;
|
|
24
|
+
/** Lightweight text rendered while a tool call is running or complete. */
|
|
25
|
+
renderText?: ToolCallText<TArgs, TResult> | undefined;
|
|
16
26
|
};
|
|
17
27
|
|
|
18
28
|
/**
|
|
@@ -28,6 +38,10 @@ export type AssistantToolProps<
|
|
|
28
38
|
*
|
|
29
39
|
* @param tool - Tool definition and name to register.
|
|
30
40
|
*
|
|
41
|
+
* @deprecated Use a toolkit with `Tools({ toolkit })` and register it via
|
|
42
|
+
* `useAui({ tools: Tools({ toolkit }) })` instead. See
|
|
43
|
+
* https://assistant-ui.com/docs/migrations/toolkit-tools.
|
|
44
|
+
*
|
|
31
45
|
* @example
|
|
32
46
|
* ```tsx
|
|
33
47
|
* const weatherTool = {
|
|
@@ -54,16 +68,22 @@ export const useAssistantTool = <
|
|
|
54
68
|
const aui = useAui();
|
|
55
69
|
|
|
56
70
|
const standalone = isStandaloneToolDisplay(tool);
|
|
71
|
+
const renderTextComponent = useMemo(
|
|
72
|
+
() =>
|
|
73
|
+
tool.renderText ? makeToolCallTextComponent(tool.renderText) : undefined,
|
|
74
|
+
[tool.renderText],
|
|
75
|
+
);
|
|
76
|
+
const render = tool.render ?? renderTextComponent;
|
|
57
77
|
|
|
58
78
|
useEffect(() => {
|
|
59
|
-
if (!
|
|
60
|
-
return aui.tools().setToolUI(tool.toolName,
|
|
61
|
-
}, [aui, tool.toolName,
|
|
79
|
+
if (!render) return undefined;
|
|
80
|
+
return aui.tools().setToolUI(tool.toolName, render, { standalone });
|
|
81
|
+
}, [aui, tool.toolName, render, standalone]);
|
|
62
82
|
|
|
63
83
|
useEffect(() => {
|
|
64
|
-
// `render` and `display` are client-only presentation
|
|
65
|
-
// reach the model.
|
|
66
|
-
const { toolName, render, display, ...rest } = tool;
|
|
84
|
+
// `render`, `renderText`, and `display` are client-only presentation
|
|
85
|
+
// concerns and never reach the model.
|
|
86
|
+
const { toolName, render, renderText, display, ...rest } = tool;
|
|
67
87
|
const context = {
|
|
68
88
|
tools: {
|
|
69
89
|
[toolName]: rest,
|
|
@@ -2,7 +2,13 @@ import { useEffect } from "react";
|
|
|
2
2
|
import { useAui } from "@assistant-ui/store";
|
|
3
3
|
import type { ToolCallMessagePartComponent } from "../types/MessagePartComponentTypes";
|
|
4
4
|
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Props used to register a renderer for tool-call message parts.
|
|
7
|
+
*
|
|
8
|
+
* @deprecated Put `render`/`renderText` on the matching toolkit entry, or use
|
|
9
|
+
* `MessagePrimitive.Parts` inline tool render overrides for per-message UI.
|
|
10
|
+
* See https://assistant-ui.com/docs/migrations/toolkit-tools.
|
|
11
|
+
*/
|
|
6
12
|
export type AssistantToolUIProps<TArgs, TResult> = {
|
|
7
13
|
/** Name of the tool whose calls should use this renderer. */
|
|
8
14
|
toolName: string;
|
|
@@ -19,11 +25,14 @@ export type AssistantToolUIProps<TArgs, TResult> = {
|
|
|
19
25
|
/**
|
|
20
26
|
* Registers a tool-call renderer while the component is mounted.
|
|
21
27
|
*
|
|
22
|
-
* This only affects rendering. Pair it with {@link
|
|
23
|
-
*
|
|
24
|
-
* definition to the model.
|
|
28
|
+
* This only affects rendering. Pair it with {@link Tools} or a backend tool
|
|
29
|
+
* registry to expose the actual tool definition to the model.
|
|
25
30
|
*
|
|
26
31
|
* @param tool - Tool renderer registration, or `null` to skip registration.
|
|
32
|
+
*
|
|
33
|
+
* @deprecated Put `render`/`renderText` on the matching toolkit entry, or use
|
|
34
|
+
* `MessagePrimitive.Parts` inline tool render overrides for per-message UI.
|
|
35
|
+
* See https://assistant-ui.com/docs/migrations/toolkit-tools.
|
|
27
36
|
*/
|
|
28
37
|
export const useAssistantToolUI = (
|
|
29
38
|
tool: AssistantToolUIProps<any, any> | null,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { useAui } from "@assistant-ui/store";
|
|
3
|
+
import type { Tool } from "assistant-stream";
|
|
4
|
+
|
|
5
|
+
type AuiToolOverride<
|
|
6
|
+
TArgs extends Record<string, unknown> = Record<string, unknown>,
|
|
7
|
+
TResult = unknown,
|
|
8
|
+
> = Partial<Tool<TArgs, TResult>>;
|
|
9
|
+
|
|
10
|
+
type AuiToolOverrides = Record<string, AuiToolOverride<any, any>>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Overrides toolkit entries for the current assistant scope.
|
|
14
|
+
*
|
|
15
|
+
* This is intended for dynamic local-state tools whose model-facing contract is
|
|
16
|
+
* declared in a `"use generative"` toolkit file with `execute: stubTool()`, but
|
|
17
|
+
* whose actual executor must close over React state in the mounted component.
|
|
18
|
+
* Keep the override keys stable after mount; dynamic key addition/removal is not
|
|
19
|
+
* currently observed.
|
|
20
|
+
* Overrides are registered at priority 1000, above toolkit defaults. Only one
|
|
21
|
+
* mounted override provider may define a given tool name at a time.
|
|
22
|
+
*
|
|
23
|
+
* @deprecated Experimental, API may change.
|
|
24
|
+
*/
|
|
25
|
+
export function useAuiToolOverrides(overrides: AuiToolOverrides): void {
|
|
26
|
+
const aui = useAui();
|
|
27
|
+
const overridesRef = useRef(overrides);
|
|
28
|
+
overridesRef.current = overrides;
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
return aui.modelContext().register({
|
|
32
|
+
getModelContext: () => ({
|
|
33
|
+
priority: 1000,
|
|
34
|
+
tools: overridesRef.current as Record<string, Tool<any, any>>,
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
}, [aui]);
|
|
38
|
+
}
|
|
@@ -34,17 +34,19 @@ const usePartMessages = (): readonly ThreadMessage[] | undefined => {
|
|
|
34
34
|
*
|
|
35
35
|
* @example
|
|
36
36
|
* ```tsx
|
|
37
|
-
* const
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
37
|
+
* const toolkit = {
|
|
38
|
+
* invoke_sub_agent: {
|
|
39
|
+
* type: "backend",
|
|
40
|
+
* render: () => (
|
|
41
|
+
* <PartPrimitive.Messages>
|
|
42
|
+
* {({ message }) => {
|
|
43
|
+
* if (message.role === "user") return <MyUserMessage />;
|
|
44
|
+
* return <MyAssistantMessage />;
|
|
45
|
+
* }}
|
|
46
|
+
* </PartPrimitive.Messages>
|
|
47
|
+
* ),
|
|
48
|
+
* },
|
|
49
|
+
* } satisfies Toolkit;
|
|
48
50
|
* ```
|
|
49
51
|
*/
|
|
50
52
|
export const PartPrimitiveMessagesImpl: FC<PartPrimitiveMessages.Props> = ({
|
|
@@ -508,6 +508,49 @@ export class RemoteThreadListThreadListRuntimeCore
|
|
|
508
508
|
});
|
|
509
509
|
}
|
|
510
510
|
|
|
511
|
+
public updateCustom(
|
|
512
|
+
threadIdOrRemoteId: string,
|
|
513
|
+
custom: Record<string, unknown> | undefined,
|
|
514
|
+
): Promise<void> {
|
|
515
|
+
const data = this.getItemById(threadIdOrRemoteId);
|
|
516
|
+
if (!data) throw new Error("Thread not found");
|
|
517
|
+
if (data.status === "new") throw new Error("Thread is not yet initialized");
|
|
518
|
+
|
|
519
|
+
if (!this._options.adapter.updateCustom) {
|
|
520
|
+
throw new Error(
|
|
521
|
+
"Remote thread list adapter does not support updating custom metadata",
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return this._state.optimisticUpdate({
|
|
526
|
+
execute: async () => {
|
|
527
|
+
const { remoteId } = await data.initializeTask;
|
|
528
|
+
const adapter = this._options.adapter;
|
|
529
|
+
if (!adapter.updateCustom) {
|
|
530
|
+
throw new Error(
|
|
531
|
+
"Remote thread list adapter does not support updating custom metadata",
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
return adapter.updateCustom(remoteId, custom);
|
|
535
|
+
},
|
|
536
|
+
optimistic: (state) => {
|
|
537
|
+
const data = getThreadData(state, threadIdOrRemoteId);
|
|
538
|
+
if (!data) return state;
|
|
539
|
+
|
|
540
|
+
return {
|
|
541
|
+
...state,
|
|
542
|
+
threadData: {
|
|
543
|
+
...state.threadData,
|
|
544
|
+
[data.id]: {
|
|
545
|
+
...data,
|
|
546
|
+
custom,
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
};
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
511
554
|
private async _ensureThreadIsNotMain(threadId: string) {
|
|
512
555
|
if (threadId === this.newThreadId)
|
|
513
556
|
throw new Error("Cannot ensure new thread is not main");
|