@tanstack/ai-client 0.29.2 → 0.30.0
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/README.md +11 -15
- package/dist/esm/ui/selectors.d.ts +11 -0
- package/dist/esm/ui/selectors.js +106 -0
- package/dist/esm/ui/selectors.js.map +1 -0
- package/dist/esm/ui/types.d.ts +103 -0
- package/dist/esm/ui.d.ts +3 -0
- package/dist/esm/ui.js +2 -0
- package/package.json +6 -2
- package/src/ui/selectors.ts +178 -0
- package/src/ui/types.ts +243 -0
- package/src/ui.ts +37 -0
package/README.md
CHANGED
|
@@ -61,12 +61,8 @@ framework bindings as your app needs them.
|
|
|
61
61
|
|
|
62
62
|
- [Overview](https://tanstack.com/ai/latest/docs/getting-started/overview) -
|
|
63
63
|
what TanStack AI is and how the packages fit together.
|
|
64
|
-
- [Quick Start
|
|
65
|
-
add streaming chat
|
|
66
|
-
- [Quick Start: Vue](https://tanstack.com/ai/latest/docs/getting-started/quick-start-vue) -
|
|
67
|
-
build with Vue composables.
|
|
68
|
-
- [Quick Start: Svelte](https://tanstack.com/ai/latest/docs/getting-started/quick-start-svelte) -
|
|
69
|
-
build with Svelte 5 runes.
|
|
64
|
+
- [Quick Start](https://tanstack.com/ai/latest/docs/getting-started/quick-start) -
|
|
65
|
+
add streaming chat. Pick your framework at the top of the page.
|
|
70
66
|
- [Quick Start: Server Only](https://tanstack.com/ai/latest/docs/getting-started/quick-start-server) -
|
|
71
67
|
use TanStack AI from a server endpoint, script, or backend service.
|
|
72
68
|
- [TanStack AI vs Vercel AI SDK](https://tanstack.com/ai/latest/docs/comparison/vercel-ai-sdk) -
|
|
@@ -226,15 +222,15 @@ on.
|
|
|
226
222
|
|
|
227
223
|
## Framework Packages
|
|
228
224
|
|
|
229
|
-
| Package
|
|
230
|
-
|
|
|
231
|
-
| [`@tanstack/ai-client`](https://tanstack.com/ai/latest/docs/api/ai-client)
|
|
232
|
-
| [`@tanstack/ai-react`](https://tanstack.com/ai/latest/docs/api/ai-react)
|
|
233
|
-
| [`@tanstack/ai-solid`](https://tanstack.com/ai/latest/docs/api/ai-solid)
|
|
234
|
-
| [`@tanstack/ai-vue`](https://tanstack.com/ai/latest/docs/api/ai-vue)
|
|
235
|
-
| [`@tanstack/ai-svelte`](https://tanstack.com/ai/latest/docs/api/ai-svelte)
|
|
236
|
-
| [`@tanstack/ai-preact`](https://tanstack.com/ai/latest/docs/api/ai-preact)
|
|
237
|
-
| `@tanstack/ai-react
|
|
225
|
+
| Package | What it provides |
|
|
226
|
+
| ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
227
|
+
| [`@tanstack/ai-client`](https://tanstack.com/ai/latest/docs/api/ai-client) | Headless chat, realtime, and generation clients |
|
|
228
|
+
| [`@tanstack/ai-react`](https://tanstack.com/ai/latest/docs/api/ai-react) | React hooks including `useChat`, `useRealtimeChat`, and generation hooks |
|
|
229
|
+
| [`@tanstack/ai-solid`](https://tanstack.com/ai/latest/docs/api/ai-solid) | Solid hooks for chat and generations |
|
|
230
|
+
| [`@tanstack/ai-vue`](https://tanstack.com/ai/latest/docs/api/ai-vue) | Vue composables for chat and generations |
|
|
231
|
+
| [`@tanstack/ai-svelte`](https://tanstack.com/ai/latest/docs/api/ai-svelte) | Svelte 5 factories for chat and generations |
|
|
232
|
+
| [`@tanstack/ai-preact`](https://tanstack.com/ai/latest/docs/api/ai-preact) | Preact hooks for chat |
|
|
233
|
+
| `@tanstack/ai-react/ui`, `@tanstack/ai-solid/ui`, `@tanstack/ai-vue/ui`, `@tanstack/ai-svelte/ui` | Headless UI components for chat interfaces. `@tanstack/ai-react-ui`, `@tanstack/ai-solid-ui`, and `@tanstack/ai-vue-ui` are deprecated. |
|
|
238
234
|
|
|
239
235
|
## Advanced Docs
|
|
240
236
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { UIMessage } from '../types.js';
|
|
2
|
+
import { ChatUIInterrupt, ChatUIPartKey, ChatUISelectInput, ChatUISelectedMessage, ChatUISelectedPart, ChatUISelection } from './types.js';
|
|
3
|
+
export declare function partTypeToKey(type: string): ChatUIPartKey | string;
|
|
4
|
+
export declare function collectInlineToolNames(toolInterrupts?: Record<string, unknown>, mappedToolNames?: ReadonlyArray<string>): Array<string>;
|
|
5
|
+
export declare function resolveInterruptComponent(interrupt: ChatUIInterrupt, interruptsMap: {
|
|
6
|
+
tools?: Record<string, unknown>;
|
|
7
|
+
generic?: Record<string, unknown>;
|
|
8
|
+
} | undefined): unknown;
|
|
9
|
+
export declare function selectChatUI(input: ChatUISelectInput): ChatUISelection;
|
|
10
|
+
export declare function automaticPartsForMessage(selected: ChatUISelectedMessage): Array<ChatUISelectedPart>;
|
|
11
|
+
export declare function selectMessageUI(message: UIMessage, input: Omit<ChatUISelectInput, 'messages'>): ChatUISelectedMessage;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
//#region src/ui/selectors.ts
|
|
2
|
+
var PART_KEY_BY_TYPE = {
|
|
3
|
+
text: "text",
|
|
4
|
+
image: "image",
|
|
5
|
+
audio: "audio",
|
|
6
|
+
video: "video",
|
|
7
|
+
document: "document",
|
|
8
|
+
thinking: "thinking",
|
|
9
|
+
"tool-call": "toolCall",
|
|
10
|
+
"tool-result": "toolResult",
|
|
11
|
+
"structured-output": "structuredOutput",
|
|
12
|
+
"ui-resource": "uiResource"
|
|
13
|
+
};
|
|
14
|
+
function partTypeToKey(type) {
|
|
15
|
+
const mapped = PART_KEY_BY_TYPE[type];
|
|
16
|
+
if (mapped) return mapped;
|
|
17
|
+
return type.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
18
|
+
}
|
|
19
|
+
function collectInlineToolNames(toolInterrupts, mappedToolNames) {
|
|
20
|
+
const listPlaced = new Set(Object.keys(toolInterrupts ?? {}));
|
|
21
|
+
return (mappedToolNames ?? []).filter((name) => !listPlaced.has(name));
|
|
22
|
+
}
|
|
23
|
+
function resolveInterruptComponent(interrupt, interruptsMap) {
|
|
24
|
+
if (!interruptsMap) return void 0;
|
|
25
|
+
if (interrupt.kind === "tool-approval") return interruptsMap.tools?.[interrupt.toolName];
|
|
26
|
+
const generic = interruptsMap.generic;
|
|
27
|
+
if (!generic) return void 0;
|
|
28
|
+
const definitionId = "definitionId" in interrupt && typeof interrupt.definitionId === "string" ? interrupt.definitionId : void 0;
|
|
29
|
+
if (definitionId && definitionId !== "fallback") {
|
|
30
|
+
const registered = generic[definitionId];
|
|
31
|
+
if (registered) return registered;
|
|
32
|
+
}
|
|
33
|
+
return generic.fallback;
|
|
34
|
+
}
|
|
35
|
+
function isToolCallPart(part) {
|
|
36
|
+
return part.type === "tool-call";
|
|
37
|
+
}
|
|
38
|
+
function isToolResultPart(part) {
|
|
39
|
+
return part.type === "tool-result";
|
|
40
|
+
}
|
|
41
|
+
function isToolApproval(interrupt) {
|
|
42
|
+
return interrupt.kind === "tool-approval";
|
|
43
|
+
}
|
|
44
|
+
function selectChatUI(input) {
|
|
45
|
+
const messages = input.messages;
|
|
46
|
+
const interrupts = input.interrupts ?? [];
|
|
47
|
+
const inlineToolNames = new Set(input.inlineToolNames ?? []);
|
|
48
|
+
const resultsByCallId = /* @__PURE__ */ new Map();
|
|
49
|
+
const callIds = /* @__PURE__ */ new Set();
|
|
50
|
+
for (const message of messages) for (const part of message.parts) {
|
|
51
|
+
if (isToolCallPart(part)) callIds.add(part.id);
|
|
52
|
+
if (isToolResultPart(part)) resultsByCallId.set(part.toolCallId, part);
|
|
53
|
+
}
|
|
54
|
+
const approvalByCallId = /* @__PURE__ */ new Map();
|
|
55
|
+
for (const interrupt of interrupts) if (isToolApproval(interrupt)) approvalByCallId.set(interrupt.toolCallId, interrupt);
|
|
56
|
+
return {
|
|
57
|
+
messages: messages.map((message) => ({
|
|
58
|
+
message,
|
|
59
|
+
parts: message.parts.map((part) => toSelectedPart(part, resultsByCallId, approvalByCallId, callIds))
|
|
60
|
+
})),
|
|
61
|
+
interrupts: interrupts.filter((interrupt) => {
|
|
62
|
+
if (!isToolApproval(interrupt)) return true;
|
|
63
|
+
if (!inlineToolNames.has(interrupt.toolName)) return true;
|
|
64
|
+
return !callIds.has(interrupt.toolCallId);
|
|
65
|
+
})
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function toSelectedPart(part, resultsByCallId, approvalByCallId, callIds) {
|
|
69
|
+
if (isToolCallPart(part)) {
|
|
70
|
+
const result = resultsByCallId.get(part.id);
|
|
71
|
+
const interrupt = approvalByCallId.get(part.id);
|
|
72
|
+
return {
|
|
73
|
+
key: "toolCall",
|
|
74
|
+
part,
|
|
75
|
+
...result ? { result } : {},
|
|
76
|
+
...interrupt ? { interrupt } : {},
|
|
77
|
+
input: part.input,
|
|
78
|
+
output: part.output
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (isToolResultPart(part)) return {
|
|
82
|
+
key: "toolResult",
|
|
83
|
+
part,
|
|
84
|
+
matched: callIds.has(part.toolCallId)
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
key: partTypeToKey(part.type),
|
|
88
|
+
part
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function automaticPartsForMessage(selected) {
|
|
92
|
+
return selected.parts.filter((part) => !(part.key === "toolResult" && part.matched));
|
|
93
|
+
}
|
|
94
|
+
function selectMessageUI(message, input) {
|
|
95
|
+
return selectChatUI({
|
|
96
|
+
...input,
|
|
97
|
+
messages: [message]
|
|
98
|
+
}).messages[0] ?? {
|
|
99
|
+
message,
|
|
100
|
+
parts: []
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { automaticPartsForMessage, collectInlineToolNames, partTypeToKey, resolveInterruptComponent, selectChatUI, selectMessageUI };
|
|
105
|
+
|
|
106
|
+
//# sourceMappingURL=selectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectors.js","names":[],"sources":["../../../src/ui/selectors.ts"],"sourcesContent":["import type {\n MessagePart,\n ToolApprovalInterrupt,\n ToolCallPart,\n ToolResultPart,\n UIMessage,\n} from '../types'\nimport type {\n ChatUIInterrupt,\n ChatUIPartKey,\n ChatUISelectInput,\n ChatUISelectedMessage,\n ChatUISelectedPart,\n ChatUISelection,\n} from './types'\n\nconst PART_KEY_BY_TYPE: Record<string, ChatUIPartKey> = {\n text: 'text',\n image: 'image',\n audio: 'audio',\n video: 'video',\n document: 'document',\n thinking: 'thinking',\n 'tool-call': 'toolCall',\n 'tool-result': 'toolResult',\n 'structured-output': 'structuredOutput',\n 'ui-resource': 'uiResource',\n}\n\nexport function partTypeToKey(type: string): ChatUIPartKey | string {\n const mapped = PART_KEY_BY_TYPE[type]\n if (mapped) return mapped\n return type.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase())\n}\n\nexport function collectInlineToolNames(\n toolInterrupts?: Record<string, unknown>,\n mappedToolNames?: ReadonlyArray<string>,\n): Array<string> {\n const listPlaced = new Set(Object.keys(toolInterrupts ?? {}))\n return (mappedToolNames ?? []).filter((name) => !listPlaced.has(name))\n}\n\nexport function resolveInterruptComponent(\n interrupt: ChatUIInterrupt,\n interruptsMap:\n | {\n tools?: Record<string, unknown>\n generic?: Record<string, unknown>\n }\n | undefined,\n): unknown {\n if (!interruptsMap) return undefined\n if (interrupt.kind === 'tool-approval') {\n return interruptsMap.tools?.[interrupt.toolName]\n }\n const generic = interruptsMap.generic\n if (!generic) return undefined\n const definitionId =\n 'definitionId' in interrupt && typeof interrupt.definitionId === 'string'\n ? interrupt.definitionId\n : undefined\n if (definitionId && definitionId !== 'fallback') {\n const registered = generic[definitionId]\n if (registered) return registered\n }\n return generic.fallback\n}\n\nfunction isToolCallPart(part: MessagePart): part is ToolCallPart {\n return part.type === 'tool-call'\n}\n\nfunction isToolResultPart(part: MessagePart): part is ToolResultPart {\n return part.type === 'tool-result'\n}\n\nfunction isToolApproval(\n interrupt: ChatUIInterrupt,\n): interrupt is ToolApprovalInterrupt {\n return interrupt.kind === 'tool-approval'\n}\n\nexport function selectChatUI(input: ChatUISelectInput): ChatUISelection {\n const messages = input.messages\n const interrupts = input.interrupts ?? []\n const inlineToolNames = new Set(input.inlineToolNames ?? [])\n\n const resultsByCallId = new Map<string, ToolResultPart>()\n const callIds = new Set<string>()\n for (const message of messages) {\n for (const part of message.parts) {\n if (isToolCallPart(part)) callIds.add(part.id)\n if (isToolResultPart(part)) resultsByCallId.set(part.toolCallId, part)\n }\n }\n\n const approvalByCallId = new Map<string, ToolApprovalInterrupt>()\n for (const interrupt of interrupts) {\n if (isToolApproval(interrupt)) {\n approvalByCallId.set(interrupt.toolCallId, interrupt)\n }\n }\n\n const selectedMessages: Array<ChatUISelectedMessage> = messages.map(\n (message) => ({\n message,\n parts: message.parts.map((part) =>\n toSelectedPart(part, resultsByCallId, approvalByCallId, callIds),\n ),\n }),\n )\n\n return {\n messages: selectedMessages,\n interrupts: interrupts.filter((interrupt) => {\n if (!isToolApproval(interrupt)) return true\n if (!inlineToolNames.has(interrupt.toolName)) return true\n return !callIds.has(interrupt.toolCallId)\n }),\n }\n}\n\nfunction toSelectedPart(\n part: MessagePart,\n resultsByCallId: Map<string, ToolResultPart>,\n approvalByCallId: Map<string, ToolApprovalInterrupt>,\n callIds: Set<string>,\n): ChatUISelectedPart {\n if (isToolCallPart(part)) {\n const result = resultsByCallId.get(part.id)\n const interrupt = approvalByCallId.get(part.id)\n return {\n key: 'toolCall',\n part,\n ...(result ? { result } : {}),\n ...(interrupt ? { interrupt } : {}),\n input: part.input,\n output: part.output,\n }\n }\n\n if (isToolResultPart(part)) {\n return {\n key: 'toolResult',\n part,\n matched: callIds.has(part.toolCallId),\n }\n }\n\n return {\n key: partTypeToKey(part.type) as Exclude<\n ChatUIPartKey,\n 'toolCall' | 'toolResult'\n >,\n part,\n }\n}\n\nexport function automaticPartsForMessage(\n selected: ChatUISelectedMessage,\n): Array<ChatUISelectedPart> {\n return selected.parts.filter(\n (part) => !(part.key === 'toolResult' && part.matched),\n )\n}\n\nexport function selectMessageUI(\n message: UIMessage,\n input: Omit<ChatUISelectInput, 'messages'>,\n): ChatUISelectedMessage {\n return (\n selectChatUI({ ...input, messages: [message] }).messages[0] ?? {\n message,\n parts: [],\n }\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { AnyClientTool, InferSchemaType, InferToolInput, InferToolOutput, InterruptDefinition, SchemaInput } from '@tanstack/ai/client';
|
|
2
|
+
import { ChatInterrupt, MessagePart, RegisteredGenericInterrupt, StructuredOutputPart, ToolApprovalInterrupt, ToolCallPart, ToolResultPart, UIMessage } from '../types.js';
|
|
3
|
+
export type ChatUIPartKey = 'text' | 'image' | 'audio' | 'video' | 'document' | 'thinking' | 'toolCall' | 'toolResult' | 'structuredOutput' | 'uiResource';
|
|
4
|
+
export type ChatUIPartTypeByKey = {
|
|
5
|
+
text: 'text';
|
|
6
|
+
image: 'image';
|
|
7
|
+
audio: 'audio';
|
|
8
|
+
video: 'video';
|
|
9
|
+
document: 'document';
|
|
10
|
+
thinking: 'thinking';
|
|
11
|
+
toolCall: 'tool-call';
|
|
12
|
+
toolResult: 'tool-result';
|
|
13
|
+
structuredOutput: 'structured-output';
|
|
14
|
+
uiResource: 'ui-resource';
|
|
15
|
+
};
|
|
16
|
+
export type ChatUIPartOf<TOptions, TKey extends ChatUIPartKey = ChatUIPartKey> = TKey extends ChatUIPartKey ? Extract<MessagePart<ChatUIToolsOf<TOptions>, ChatUIData<TOptions>>, {
|
|
17
|
+
type: ChatUIPartTypeByKey[TKey];
|
|
18
|
+
}> : never;
|
|
19
|
+
export type ChatUIToolsOf<TOptions> = TOptions extends {
|
|
20
|
+
tools: infer TTools;
|
|
21
|
+
} ? TTools extends ReadonlyArray<AnyClientTool> ? TTools : any : any;
|
|
22
|
+
export type ChatUIInterruptsOf<TOptions> = TOptions extends {
|
|
23
|
+
interrupts: infer TInterrupts;
|
|
24
|
+
} ? TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> ? TInterrupts : readonly [] : readonly [];
|
|
25
|
+
export type ChatUISchemaOf<TOptions> = TOptions extends {
|
|
26
|
+
outputSchema: infer TSchema;
|
|
27
|
+
} ? TSchema extends SchemaInput ? TSchema : undefined : undefined;
|
|
28
|
+
export type ChatUIData<TOptions> = TOptions extends {
|
|
29
|
+
outputSchema: infer TSchema;
|
|
30
|
+
} ? TSchema extends SchemaInput ? InferSchemaType<TSchema> : unknown : unknown;
|
|
31
|
+
export type ChatUIToolName<TOptions> = ChatUIToolsOf<TOptions>[number] extends infer TTool ? TTool extends AnyClientTool ? TTool['name'] : string : string;
|
|
32
|
+
export type ChatUIRegisteredInterruptId<TOptions> = ChatUIInterruptsOf<TOptions>[number] extends infer TDefinition ? TDefinition extends InterruptDefinition<infer TId, any, any, any> ? TId : string : string;
|
|
33
|
+
export type ChatUIHasNamedTools<TOptions> = [ChatUIToolName<TOptions>] extends [
|
|
34
|
+
never
|
|
35
|
+
] ? false : [string] extends [ChatUIToolName<TOptions>] ? false : true;
|
|
36
|
+
export type ChatUIHasNamedInterrupts<TOptions> = [
|
|
37
|
+
ChatUIRegisteredInterruptId<TOptions>
|
|
38
|
+
] extends [never] ? false : [string] extends [ChatUIRegisteredInterruptId<TOptions>] ? false : true;
|
|
39
|
+
export type ChatUINamedInterruptId<TOptions> = Exclude<ChatUIRegisteredInterruptId<TOptions>, 'fallback'>;
|
|
40
|
+
export type ChatUIInterruptName<TOptions> = (ChatUIHasNamedTools<TOptions> extends true ? ChatUIToolName<TOptions> : never) | (ChatUIHasNamedInterrupts<TOptions> extends true ? ChatUINamedInterruptId<TOptions> : never);
|
|
41
|
+
type ToolByName<TOptions, TName> = Extract<ChatUIToolsOf<TOptions> extends ReadonlyArray<infer TTool> ? TTool : never, {
|
|
42
|
+
name: TName;
|
|
43
|
+
}>;
|
|
44
|
+
export type ChatUIInterrupt = ChatInterrupt | ToolApprovalInterrupt;
|
|
45
|
+
export type ChatUIToolApproval<TOptions, TName extends ChatUIToolName<TOptions> = ChatUIToolName<TOptions>> = Extract<ChatInterrupt<ChatUIToolsOf<TOptions>, ChatUIInterruptsOf<TOptions>>, {
|
|
46
|
+
kind: 'tool-approval';
|
|
47
|
+
toolName: TName;
|
|
48
|
+
}>;
|
|
49
|
+
export type ChatUIToolPart<TOptions, TName extends ChatUIToolName<TOptions> = ChatUIToolName<TOptions>> = {
|
|
50
|
+
key: 'toolCall';
|
|
51
|
+
part: Extract<ToolCallPart<ChatUIToolsOf<TOptions>>, {
|
|
52
|
+
name: TName;
|
|
53
|
+
}>;
|
|
54
|
+
result?: ToolResultPart;
|
|
55
|
+
interrupt?: Extract<ChatInterrupt<ChatUIToolsOf<TOptions>, ChatUIInterruptsOf<TOptions>>, {
|
|
56
|
+
kind: 'tool-approval';
|
|
57
|
+
toolName: TName;
|
|
58
|
+
}>;
|
|
59
|
+
input?: InferToolInput<ToolByName<TOptions, TName>>;
|
|
60
|
+
output?: InferToolOutput<ToolByName<TOptions, TName>>;
|
|
61
|
+
};
|
|
62
|
+
export type RegisteredUIInterrupt<TOptions, TId extends ChatUIRegisteredInterruptId<TOptions> = ChatUIRegisteredInterruptId<TOptions>> = Extract<RegisteredGenericInterrupt<ChatUIInterruptsOf<TOptions>>, {
|
|
63
|
+
definitionId: TId;
|
|
64
|
+
}>;
|
|
65
|
+
export type ChatUIInterruptOf<TOptions, TName extends ChatUIInterruptName<TOptions> = never> = [TName] extends [never] ? ChatUIInterrupt : TName extends (ChatUIHasNamedTools<TOptions> extends true ? ChatUIToolName<TOptions> : never) ? ChatUIToolApproval<TOptions, TName> : TName extends ChatUINamedInterruptId<TOptions> ? RegisteredUIInterrupt<TOptions, TName> : ChatUIInterrupt;
|
|
66
|
+
export type ChatUISelectedToolPart = {
|
|
67
|
+
key: 'toolCall';
|
|
68
|
+
part: ToolCallPart;
|
|
69
|
+
result?: ToolResultPart;
|
|
70
|
+
interrupt?: ChatUIInterrupt;
|
|
71
|
+
input?: unknown;
|
|
72
|
+
output?: unknown;
|
|
73
|
+
};
|
|
74
|
+
export type ChatUISelectedResultPart = {
|
|
75
|
+
key: 'toolResult';
|
|
76
|
+
part: ToolResultPart;
|
|
77
|
+
matched: boolean;
|
|
78
|
+
};
|
|
79
|
+
export type ChatUISelectedGenericPart = {
|
|
80
|
+
key: Exclude<ChatUIPartKey, 'toolCall' | 'toolResult'>;
|
|
81
|
+
part: MessagePart;
|
|
82
|
+
};
|
|
83
|
+
export type ChatUISelectedPart = ChatUISelectedToolPart | ChatUISelectedResultPart | ChatUISelectedGenericPart;
|
|
84
|
+
export type ChatUISelectedPartOf<TOptions, TKey extends ChatUIPartKey = ChatUIPartKey> = TKey extends 'toolCall' ? ChatUIToolPart<TOptions> : TKey extends 'toolResult' ? ChatUISelectedResultPart : {
|
|
85
|
+
key: TKey;
|
|
86
|
+
part: ChatUIPartOf<TOptions, TKey>;
|
|
87
|
+
};
|
|
88
|
+
export type ChatUISelectedMessage = {
|
|
89
|
+
message: UIMessage;
|
|
90
|
+
parts: Array<ChatUISelectedPart>;
|
|
91
|
+
};
|
|
92
|
+
export type ChatUISelection = {
|
|
93
|
+
messages: Array<ChatUISelectedMessage>;
|
|
94
|
+
interrupts: Array<ChatUIInterrupt>;
|
|
95
|
+
};
|
|
96
|
+
export type ChatUISelectInput = {
|
|
97
|
+
messages: ReadonlyArray<UIMessage>;
|
|
98
|
+
interrupts?: ReadonlyArray<ChatUIInterrupt>;
|
|
99
|
+
inlineToolNames?: ReadonlyArray<string>;
|
|
100
|
+
};
|
|
101
|
+
export type ChatUIMessages<TOptions> = Array<UIMessage<ChatUIToolsOf<TOptions>, ChatUIData<TOptions>>>;
|
|
102
|
+
export type ChatUIStructuredPart<TOptions> = StructuredOutputPart<ChatUIData<TOptions>>;
|
|
103
|
+
export {};
|
package/dist/esm/ui.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { partTypeToKey, selectChatUI } from './ui/selectors.js';
|
|
2
|
+
export { automaticPartsForMessage, collectInlineToolNames, resolveInterruptComponent, selectMessageUI, } from './ui/selectors.js';
|
|
3
|
+
export type { ChatUIData, ChatUIHasNamedInterrupts, ChatUIHasNamedTools, ChatUIInterrupt, ChatUIInterruptName, ChatUIInterruptOf, ChatUIInterruptsOf, ChatUINamedInterruptId, ChatUIMessages, ChatUIPartKey, ChatUIPartOf, ChatUIPartTypeByKey, ChatUIRegisteredInterruptId, ChatUISchemaOf, ChatUISelectInput, ChatUISelectedGenericPart, ChatUISelectedMessage, ChatUISelectedPart, ChatUISelectedPartOf, ChatUISelectedResultPart, ChatUISelectedToolPart, ChatUISelection, ChatUIStructuredPart, ChatUIToolApproval, ChatUIToolName, ChatUIToolPart, ChatUIToolsOf, RegisteredUIInterrupt, } from './ui/types.js';
|
package/dist/esm/ui.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { automaticPartsForMessage, collectInlineToolNames, partTypeToKey, resolveInterruptComponent, selectChatUI, selectMessageUI } from "./ui/selectors.js";
|
|
2
|
+
export { automaticPartsForMessage, collectInlineToolNames, partTypeToKey, resolveInterruptComponent, selectChatUI, selectMessageUI };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Framework-agnostic headless client for TanStack AI chat, realtime sessions, streaming transports, and media generations.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,6 +46,10 @@
|
|
|
46
46
|
"./byok": {
|
|
47
47
|
"types": "./dist/esm/byok.d.ts",
|
|
48
48
|
"import": "./dist/esm/byok.js"
|
|
49
|
+
},
|
|
50
|
+
"./ui": {
|
|
51
|
+
"types": "./dist/esm/ui.d.ts",
|
|
52
|
+
"import": "./dist/esm/ui.js"
|
|
49
53
|
}
|
|
50
54
|
},
|
|
51
55
|
"files": [
|
|
@@ -53,7 +57,7 @@
|
|
|
53
57
|
"src"
|
|
54
58
|
],
|
|
55
59
|
"dependencies": {
|
|
56
|
-
"@tanstack/ai": "^0.52.
|
|
60
|
+
"@tanstack/ai": "^0.52.1",
|
|
57
61
|
"@tanstack/ai-event-client": "^0.11.2",
|
|
58
62
|
"@tanstack/ai-utils": "^0.4.0"
|
|
59
63
|
},
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MessagePart,
|
|
3
|
+
ToolApprovalInterrupt,
|
|
4
|
+
ToolCallPart,
|
|
5
|
+
ToolResultPart,
|
|
6
|
+
UIMessage,
|
|
7
|
+
} from '../types'
|
|
8
|
+
import type {
|
|
9
|
+
ChatUIInterrupt,
|
|
10
|
+
ChatUIPartKey,
|
|
11
|
+
ChatUISelectInput,
|
|
12
|
+
ChatUISelectedMessage,
|
|
13
|
+
ChatUISelectedPart,
|
|
14
|
+
ChatUISelection,
|
|
15
|
+
} from './types'
|
|
16
|
+
|
|
17
|
+
const PART_KEY_BY_TYPE: Record<string, ChatUIPartKey> = {
|
|
18
|
+
text: 'text',
|
|
19
|
+
image: 'image',
|
|
20
|
+
audio: 'audio',
|
|
21
|
+
video: 'video',
|
|
22
|
+
document: 'document',
|
|
23
|
+
thinking: 'thinking',
|
|
24
|
+
'tool-call': 'toolCall',
|
|
25
|
+
'tool-result': 'toolResult',
|
|
26
|
+
'structured-output': 'structuredOutput',
|
|
27
|
+
'ui-resource': 'uiResource',
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function partTypeToKey(type: string): ChatUIPartKey | string {
|
|
31
|
+
const mapped = PART_KEY_BY_TYPE[type]
|
|
32
|
+
if (mapped) return mapped
|
|
33
|
+
return type.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase())
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function collectInlineToolNames(
|
|
37
|
+
toolInterrupts?: Record<string, unknown>,
|
|
38
|
+
mappedToolNames?: ReadonlyArray<string>,
|
|
39
|
+
): Array<string> {
|
|
40
|
+
const listPlaced = new Set(Object.keys(toolInterrupts ?? {}))
|
|
41
|
+
return (mappedToolNames ?? []).filter((name) => !listPlaced.has(name))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function resolveInterruptComponent(
|
|
45
|
+
interrupt: ChatUIInterrupt,
|
|
46
|
+
interruptsMap:
|
|
47
|
+
| {
|
|
48
|
+
tools?: Record<string, unknown>
|
|
49
|
+
generic?: Record<string, unknown>
|
|
50
|
+
}
|
|
51
|
+
| undefined,
|
|
52
|
+
): unknown {
|
|
53
|
+
if (!interruptsMap) return undefined
|
|
54
|
+
if (interrupt.kind === 'tool-approval') {
|
|
55
|
+
return interruptsMap.tools?.[interrupt.toolName]
|
|
56
|
+
}
|
|
57
|
+
const generic = interruptsMap.generic
|
|
58
|
+
if (!generic) return undefined
|
|
59
|
+
const definitionId =
|
|
60
|
+
'definitionId' in interrupt && typeof interrupt.definitionId === 'string'
|
|
61
|
+
? interrupt.definitionId
|
|
62
|
+
: undefined
|
|
63
|
+
if (definitionId && definitionId !== 'fallback') {
|
|
64
|
+
const registered = generic[definitionId]
|
|
65
|
+
if (registered) return registered
|
|
66
|
+
}
|
|
67
|
+
return generic.fallback
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isToolCallPart(part: MessagePart): part is ToolCallPart {
|
|
71
|
+
return part.type === 'tool-call'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isToolResultPart(part: MessagePart): part is ToolResultPart {
|
|
75
|
+
return part.type === 'tool-result'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isToolApproval(
|
|
79
|
+
interrupt: ChatUIInterrupt,
|
|
80
|
+
): interrupt is ToolApprovalInterrupt {
|
|
81
|
+
return interrupt.kind === 'tool-approval'
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function selectChatUI(input: ChatUISelectInput): ChatUISelection {
|
|
85
|
+
const messages = input.messages
|
|
86
|
+
const interrupts = input.interrupts ?? []
|
|
87
|
+
const inlineToolNames = new Set(input.inlineToolNames ?? [])
|
|
88
|
+
|
|
89
|
+
const resultsByCallId = new Map<string, ToolResultPart>()
|
|
90
|
+
const callIds = new Set<string>()
|
|
91
|
+
for (const message of messages) {
|
|
92
|
+
for (const part of message.parts) {
|
|
93
|
+
if (isToolCallPart(part)) callIds.add(part.id)
|
|
94
|
+
if (isToolResultPart(part)) resultsByCallId.set(part.toolCallId, part)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const approvalByCallId = new Map<string, ToolApprovalInterrupt>()
|
|
99
|
+
for (const interrupt of interrupts) {
|
|
100
|
+
if (isToolApproval(interrupt)) {
|
|
101
|
+
approvalByCallId.set(interrupt.toolCallId, interrupt)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const selectedMessages: Array<ChatUISelectedMessage> = messages.map(
|
|
106
|
+
(message) => ({
|
|
107
|
+
message,
|
|
108
|
+
parts: message.parts.map((part) =>
|
|
109
|
+
toSelectedPart(part, resultsByCallId, approvalByCallId, callIds),
|
|
110
|
+
),
|
|
111
|
+
}),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
messages: selectedMessages,
|
|
116
|
+
interrupts: interrupts.filter((interrupt) => {
|
|
117
|
+
if (!isToolApproval(interrupt)) return true
|
|
118
|
+
if (!inlineToolNames.has(interrupt.toolName)) return true
|
|
119
|
+
return !callIds.has(interrupt.toolCallId)
|
|
120
|
+
}),
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function toSelectedPart(
|
|
125
|
+
part: MessagePart,
|
|
126
|
+
resultsByCallId: Map<string, ToolResultPart>,
|
|
127
|
+
approvalByCallId: Map<string, ToolApprovalInterrupt>,
|
|
128
|
+
callIds: Set<string>,
|
|
129
|
+
): ChatUISelectedPart {
|
|
130
|
+
if (isToolCallPart(part)) {
|
|
131
|
+
const result = resultsByCallId.get(part.id)
|
|
132
|
+
const interrupt = approvalByCallId.get(part.id)
|
|
133
|
+
return {
|
|
134
|
+
key: 'toolCall',
|
|
135
|
+
part,
|
|
136
|
+
...(result ? { result } : {}),
|
|
137
|
+
...(interrupt ? { interrupt } : {}),
|
|
138
|
+
input: part.input,
|
|
139
|
+
output: part.output,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (isToolResultPart(part)) {
|
|
144
|
+
return {
|
|
145
|
+
key: 'toolResult',
|
|
146
|
+
part,
|
|
147
|
+
matched: callIds.has(part.toolCallId),
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
key: partTypeToKey(part.type) as Exclude<
|
|
153
|
+
ChatUIPartKey,
|
|
154
|
+
'toolCall' | 'toolResult'
|
|
155
|
+
>,
|
|
156
|
+
part,
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function automaticPartsForMessage(
|
|
161
|
+
selected: ChatUISelectedMessage,
|
|
162
|
+
): Array<ChatUISelectedPart> {
|
|
163
|
+
return selected.parts.filter(
|
|
164
|
+
(part) => !(part.key === 'toolResult' && part.matched),
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function selectMessageUI(
|
|
169
|
+
message: UIMessage,
|
|
170
|
+
input: Omit<ChatUISelectInput, 'messages'>,
|
|
171
|
+
): ChatUISelectedMessage {
|
|
172
|
+
return (
|
|
173
|
+
selectChatUI({ ...input, messages: [message] }).messages[0] ?? {
|
|
174
|
+
message,
|
|
175
|
+
parts: [],
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
}
|
package/src/ui/types.ts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnyClientTool,
|
|
3
|
+
InferSchemaType,
|
|
4
|
+
InferToolInput,
|
|
5
|
+
InferToolOutput,
|
|
6
|
+
InterruptDefinition,
|
|
7
|
+
SchemaInput,
|
|
8
|
+
} from '@tanstack/ai/client'
|
|
9
|
+
import type {
|
|
10
|
+
ChatInterrupt,
|
|
11
|
+
MessagePart,
|
|
12
|
+
RegisteredGenericInterrupt,
|
|
13
|
+
StructuredOutputPart,
|
|
14
|
+
ToolApprovalInterrupt,
|
|
15
|
+
ToolCallPart,
|
|
16
|
+
ToolResultPart,
|
|
17
|
+
UIMessage,
|
|
18
|
+
} from '../types'
|
|
19
|
+
|
|
20
|
+
export type ChatUIPartKey =
|
|
21
|
+
| 'text'
|
|
22
|
+
| 'image'
|
|
23
|
+
| 'audio'
|
|
24
|
+
| 'video'
|
|
25
|
+
| 'document'
|
|
26
|
+
| 'thinking'
|
|
27
|
+
| 'toolCall'
|
|
28
|
+
| 'toolResult'
|
|
29
|
+
| 'structuredOutput'
|
|
30
|
+
| 'uiResource'
|
|
31
|
+
|
|
32
|
+
export type ChatUIPartTypeByKey = {
|
|
33
|
+
text: 'text'
|
|
34
|
+
image: 'image'
|
|
35
|
+
audio: 'audio'
|
|
36
|
+
video: 'video'
|
|
37
|
+
document: 'document'
|
|
38
|
+
thinking: 'thinking'
|
|
39
|
+
toolCall: 'tool-call'
|
|
40
|
+
toolResult: 'tool-result'
|
|
41
|
+
structuredOutput: 'structured-output'
|
|
42
|
+
uiResource: 'ui-resource'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type ChatUIPartOf<
|
|
46
|
+
TOptions,
|
|
47
|
+
TKey extends ChatUIPartKey = ChatUIPartKey,
|
|
48
|
+
> = TKey extends ChatUIPartKey
|
|
49
|
+
? Extract<
|
|
50
|
+
MessagePart<ChatUIToolsOf<TOptions>, ChatUIData<TOptions>>,
|
|
51
|
+
{ type: ChatUIPartTypeByKey[TKey] }
|
|
52
|
+
>
|
|
53
|
+
: never
|
|
54
|
+
|
|
55
|
+
export type ChatUIToolsOf<TOptions> = TOptions extends {
|
|
56
|
+
tools: infer TTools
|
|
57
|
+
}
|
|
58
|
+
? TTools extends ReadonlyArray<AnyClientTool>
|
|
59
|
+
? TTools
|
|
60
|
+
: any
|
|
61
|
+
: any
|
|
62
|
+
|
|
63
|
+
export type ChatUIInterruptsOf<TOptions> = TOptions extends {
|
|
64
|
+
interrupts: infer TInterrupts
|
|
65
|
+
}
|
|
66
|
+
? TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>>
|
|
67
|
+
? TInterrupts
|
|
68
|
+
: readonly []
|
|
69
|
+
: readonly []
|
|
70
|
+
|
|
71
|
+
export type ChatUISchemaOf<TOptions> = TOptions extends {
|
|
72
|
+
outputSchema: infer TSchema
|
|
73
|
+
}
|
|
74
|
+
? TSchema extends SchemaInput
|
|
75
|
+
? TSchema
|
|
76
|
+
: undefined
|
|
77
|
+
: undefined
|
|
78
|
+
|
|
79
|
+
export type ChatUIData<TOptions> = TOptions extends {
|
|
80
|
+
outputSchema: infer TSchema
|
|
81
|
+
}
|
|
82
|
+
? TSchema extends SchemaInput
|
|
83
|
+
? InferSchemaType<TSchema>
|
|
84
|
+
: unknown
|
|
85
|
+
: unknown
|
|
86
|
+
|
|
87
|
+
export type ChatUIToolName<TOptions> =
|
|
88
|
+
ChatUIToolsOf<TOptions>[number] extends infer TTool
|
|
89
|
+
? TTool extends AnyClientTool
|
|
90
|
+
? TTool['name']
|
|
91
|
+
: string
|
|
92
|
+
: string
|
|
93
|
+
|
|
94
|
+
export type ChatUIRegisteredInterruptId<TOptions> =
|
|
95
|
+
ChatUIInterruptsOf<TOptions>[number] extends infer TDefinition
|
|
96
|
+
? TDefinition extends InterruptDefinition<infer TId, any, any, any>
|
|
97
|
+
? TId
|
|
98
|
+
: string
|
|
99
|
+
: string
|
|
100
|
+
|
|
101
|
+
export type ChatUIHasNamedTools<TOptions> = [ChatUIToolName<TOptions>] extends [
|
|
102
|
+
never,
|
|
103
|
+
]
|
|
104
|
+
? false
|
|
105
|
+
: [string] extends [ChatUIToolName<TOptions>]
|
|
106
|
+
? false
|
|
107
|
+
: true
|
|
108
|
+
|
|
109
|
+
export type ChatUIHasNamedInterrupts<TOptions> = [
|
|
110
|
+
ChatUIRegisteredInterruptId<TOptions>,
|
|
111
|
+
] extends [never]
|
|
112
|
+
? false
|
|
113
|
+
: [string] extends [ChatUIRegisteredInterruptId<TOptions>]
|
|
114
|
+
? false
|
|
115
|
+
: true
|
|
116
|
+
|
|
117
|
+
export type ChatUINamedInterruptId<TOptions> = Exclude<
|
|
118
|
+
ChatUIRegisteredInterruptId<TOptions>,
|
|
119
|
+
'fallback'
|
|
120
|
+
>
|
|
121
|
+
|
|
122
|
+
export type ChatUIInterruptName<TOptions> =
|
|
123
|
+
| (ChatUIHasNamedTools<TOptions> extends true
|
|
124
|
+
? ChatUIToolName<TOptions>
|
|
125
|
+
: never)
|
|
126
|
+
| (ChatUIHasNamedInterrupts<TOptions> extends true
|
|
127
|
+
? ChatUINamedInterruptId<TOptions>
|
|
128
|
+
: never)
|
|
129
|
+
|
|
130
|
+
type ToolByName<TOptions, TName> = Extract<
|
|
131
|
+
ChatUIToolsOf<TOptions> extends ReadonlyArray<infer TTool> ? TTool : never,
|
|
132
|
+
{ name: TName }
|
|
133
|
+
>
|
|
134
|
+
|
|
135
|
+
export type ChatUIInterrupt = ChatInterrupt | ToolApprovalInterrupt
|
|
136
|
+
|
|
137
|
+
export type ChatUIToolApproval<
|
|
138
|
+
TOptions,
|
|
139
|
+
TName extends ChatUIToolName<TOptions> = ChatUIToolName<TOptions>,
|
|
140
|
+
> = Extract<
|
|
141
|
+
ChatInterrupt<ChatUIToolsOf<TOptions>, ChatUIInterruptsOf<TOptions>>,
|
|
142
|
+
{ kind: 'tool-approval'; toolName: TName }
|
|
143
|
+
>
|
|
144
|
+
|
|
145
|
+
export type ChatUIToolPart<
|
|
146
|
+
TOptions,
|
|
147
|
+
TName extends ChatUIToolName<TOptions> = ChatUIToolName<TOptions>,
|
|
148
|
+
> = {
|
|
149
|
+
key: 'toolCall'
|
|
150
|
+
part: Extract<ToolCallPart<ChatUIToolsOf<TOptions>>, { name: TName }>
|
|
151
|
+
result?: ToolResultPart
|
|
152
|
+
interrupt?: Extract<
|
|
153
|
+
ChatInterrupt<ChatUIToolsOf<TOptions>, ChatUIInterruptsOf<TOptions>>,
|
|
154
|
+
{ kind: 'tool-approval'; toolName: TName }
|
|
155
|
+
>
|
|
156
|
+
input?: InferToolInput<ToolByName<TOptions, TName>>
|
|
157
|
+
output?: InferToolOutput<ToolByName<TOptions, TName>>
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export type RegisteredUIInterrupt<
|
|
161
|
+
TOptions,
|
|
162
|
+
TId extends ChatUIRegisteredInterruptId<TOptions> =
|
|
163
|
+
ChatUIRegisteredInterruptId<TOptions>,
|
|
164
|
+
> = Extract<
|
|
165
|
+
RegisteredGenericInterrupt<ChatUIInterruptsOf<TOptions>>,
|
|
166
|
+
{ definitionId: TId }
|
|
167
|
+
>
|
|
168
|
+
|
|
169
|
+
export type ChatUIInterruptOf<
|
|
170
|
+
TOptions,
|
|
171
|
+
TName extends ChatUIInterruptName<TOptions> = never,
|
|
172
|
+
> = [TName] extends [never]
|
|
173
|
+
? ChatUIInterrupt
|
|
174
|
+
: TName extends (
|
|
175
|
+
ChatUIHasNamedTools<TOptions> extends true
|
|
176
|
+
? ChatUIToolName<TOptions>
|
|
177
|
+
: never
|
|
178
|
+
)
|
|
179
|
+
? ChatUIToolApproval<TOptions, TName>
|
|
180
|
+
: TName extends ChatUINamedInterruptId<TOptions>
|
|
181
|
+
? RegisteredUIInterrupt<TOptions, TName>
|
|
182
|
+
: ChatUIInterrupt
|
|
183
|
+
|
|
184
|
+
export type ChatUISelectedToolPart = {
|
|
185
|
+
key: 'toolCall'
|
|
186
|
+
part: ToolCallPart
|
|
187
|
+
result?: ToolResultPart
|
|
188
|
+
interrupt?: ChatUIInterrupt
|
|
189
|
+
input?: unknown
|
|
190
|
+
output?: unknown
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export type ChatUISelectedResultPart = {
|
|
194
|
+
key: 'toolResult'
|
|
195
|
+
part: ToolResultPart
|
|
196
|
+
matched: boolean
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export type ChatUISelectedGenericPart = {
|
|
200
|
+
key: Exclude<ChatUIPartKey, 'toolCall' | 'toolResult'>
|
|
201
|
+
part: MessagePart
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export type ChatUISelectedPart =
|
|
205
|
+
| ChatUISelectedToolPart
|
|
206
|
+
| ChatUISelectedResultPart
|
|
207
|
+
| ChatUISelectedGenericPart
|
|
208
|
+
|
|
209
|
+
export type ChatUISelectedPartOf<
|
|
210
|
+
TOptions,
|
|
211
|
+
TKey extends ChatUIPartKey = ChatUIPartKey,
|
|
212
|
+
> = TKey extends 'toolCall'
|
|
213
|
+
? ChatUIToolPart<TOptions>
|
|
214
|
+
: TKey extends 'toolResult'
|
|
215
|
+
? ChatUISelectedResultPart
|
|
216
|
+
: {
|
|
217
|
+
key: TKey
|
|
218
|
+
part: ChatUIPartOf<TOptions, TKey>
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export type ChatUISelectedMessage = {
|
|
222
|
+
message: UIMessage
|
|
223
|
+
parts: Array<ChatUISelectedPart>
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export type ChatUISelection = {
|
|
227
|
+
messages: Array<ChatUISelectedMessage>
|
|
228
|
+
interrupts: Array<ChatUIInterrupt>
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export type ChatUISelectInput = {
|
|
232
|
+
messages: ReadonlyArray<UIMessage>
|
|
233
|
+
interrupts?: ReadonlyArray<ChatUIInterrupt>
|
|
234
|
+
inlineToolNames?: ReadonlyArray<string>
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export type ChatUIMessages<TOptions> = Array<
|
|
238
|
+
UIMessage<ChatUIToolsOf<TOptions>, ChatUIData<TOptions>>
|
|
239
|
+
>
|
|
240
|
+
|
|
241
|
+
export type ChatUIStructuredPart<TOptions> = StructuredOutputPart<
|
|
242
|
+
ChatUIData<TOptions>
|
|
243
|
+
>
|
package/src/ui.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export { partTypeToKey, selectChatUI } from './ui/selectors'
|
|
2
|
+
export {
|
|
3
|
+
automaticPartsForMessage,
|
|
4
|
+
collectInlineToolNames,
|
|
5
|
+
resolveInterruptComponent,
|
|
6
|
+
selectMessageUI,
|
|
7
|
+
} from './ui/selectors'
|
|
8
|
+
export type {
|
|
9
|
+
ChatUIData,
|
|
10
|
+
ChatUIHasNamedInterrupts,
|
|
11
|
+
ChatUIHasNamedTools,
|
|
12
|
+
ChatUIInterrupt,
|
|
13
|
+
ChatUIInterruptName,
|
|
14
|
+
ChatUIInterruptOf,
|
|
15
|
+
ChatUIInterruptsOf,
|
|
16
|
+
ChatUINamedInterruptId,
|
|
17
|
+
ChatUIMessages,
|
|
18
|
+
ChatUIPartKey,
|
|
19
|
+
ChatUIPartOf,
|
|
20
|
+
ChatUIPartTypeByKey,
|
|
21
|
+
ChatUIRegisteredInterruptId,
|
|
22
|
+
ChatUISchemaOf,
|
|
23
|
+
ChatUISelectInput,
|
|
24
|
+
ChatUISelectedGenericPart,
|
|
25
|
+
ChatUISelectedMessage,
|
|
26
|
+
ChatUISelectedPart,
|
|
27
|
+
ChatUISelectedPartOf,
|
|
28
|
+
ChatUISelectedResultPart,
|
|
29
|
+
ChatUISelectedToolPart,
|
|
30
|
+
ChatUISelection,
|
|
31
|
+
ChatUIStructuredPart,
|
|
32
|
+
ChatUIToolApproval,
|
|
33
|
+
ChatUIToolName,
|
|
34
|
+
ChatUIToolPart,
|
|
35
|
+
ChatUIToolsOf,
|
|
36
|
+
RegisteredUIInterrupt,
|
|
37
|
+
} from './ui/types'
|