@tanstack/ai-client 0.0.1
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/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/esm/chat-client.d.ts +102 -0
- package/dist/esm/chat-client.js +375 -0
- package/dist/esm/chat-client.js.map +1 -0
- package/dist/esm/connection-adapters.d.ts +124 -0
- package/dist/esm/connection-adapters.js +149 -0
- package/dist/esm/connection-adapters.js.map +1 -0
- package/dist/esm/events.d.ts +82 -0
- package/dist/esm/events.js +180 -0
- package/dist/esm/events.js.map +1 -0
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.js +29 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/tool-types.d.ts +20 -0
- package/dist/esm/types.d.ts +210 -0
- package/dist/esm/types.js +11 -0
- package/dist/esm/types.js.map +1 -0
- package/package.json +53 -0
- package/src/chat-client.ts +522 -0
- package/src/connection-adapters.ts +344 -0
- package/src/events.ts +252 -0
- package/src/index.ts +63 -0
- package/src/tool-types.ts +41 -0
- package/src/types.ts +276 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { ModelMessage, StreamChunk, UIMessage } from '@tanstack/ai';
|
|
2
|
+
/**
|
|
3
|
+
* Connection adapter interface - converts a connection into a stream of chunks
|
|
4
|
+
*/
|
|
5
|
+
export interface ConnectionAdapter {
|
|
6
|
+
/**
|
|
7
|
+
* Connect and return an async iterable of StreamChunks
|
|
8
|
+
* @param messages - The messages to send (UIMessages or ModelMessages)
|
|
9
|
+
* @param data - Additional data to send
|
|
10
|
+
* @param abortSignal - Optional abort signal for request cancellation
|
|
11
|
+
*/
|
|
12
|
+
connect: (messages: Array<UIMessage> | Array<ModelMessage>, data?: Record<string, any>, abortSignal?: AbortSignal) => AsyncIterable<StreamChunk>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Options for fetch-based connection adapters
|
|
16
|
+
*/
|
|
17
|
+
export interface FetchConnectionOptions {
|
|
18
|
+
headers?: Record<string, string> | Headers;
|
|
19
|
+
credentials?: RequestCredentials;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
body?: Record<string, any>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create a Server-Sent Events connection adapter
|
|
25
|
+
*
|
|
26
|
+
* @param url - The API endpoint URL (or a function that returns the URL)
|
|
27
|
+
* @param options - Fetch options (headers, credentials, body, etc.) or a function that returns options (can be async)
|
|
28
|
+
* @returns A connection adapter for SSE streams
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // Static URL
|
|
33
|
+
* const connection = fetchServerSentEvents('/api/chat');
|
|
34
|
+
*
|
|
35
|
+
* // Dynamic URL
|
|
36
|
+
* const connection = fetchServerSentEvents(() => `/api/chat?user=${userId}`);
|
|
37
|
+
*
|
|
38
|
+
* // With options
|
|
39
|
+
* const connection = fetchServerSentEvents('/api/chat', {
|
|
40
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // With dynamic options
|
|
44
|
+
* const connection = fetchServerSentEvents('/api/chat', () => ({
|
|
45
|
+
* headers: { 'Authorization': `Bearer ${getToken()}` }
|
|
46
|
+
* }));
|
|
47
|
+
*
|
|
48
|
+
* // With additional body data
|
|
49
|
+
* const connection = fetchServerSentEvents('/api/chat', async () => ({
|
|
50
|
+
* body: {
|
|
51
|
+
* provider: 'openai',
|
|
52
|
+
* model: 'gpt-4o',
|
|
53
|
+
* }
|
|
54
|
+
* }));
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function fetchServerSentEvents(url: string | (() => string), options?: FetchConnectionOptions | (() => FetchConnectionOptions | Promise<FetchConnectionOptions>)): ConnectionAdapter;
|
|
58
|
+
/**
|
|
59
|
+
* Create an HTTP streaming connection adapter (for raw streaming without SSE format)
|
|
60
|
+
*
|
|
61
|
+
* @param url - The API endpoint URL (or a function that returns the URL)
|
|
62
|
+
* @param options - Fetch options (headers, credentials, body, etc.) or a function that returns options (can be async)
|
|
63
|
+
* @returns A connection adapter for HTTP streams
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* // Static URL
|
|
68
|
+
* const connection = fetchHttpStream('/api/chat');
|
|
69
|
+
*
|
|
70
|
+
* // Dynamic URL
|
|
71
|
+
* const connection = fetchHttpStream(() => `/api/chat?user=${userId}`);
|
|
72
|
+
*
|
|
73
|
+
* // With options
|
|
74
|
+
* const connection = fetchHttpStream('/api/chat', {
|
|
75
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
76
|
+
* });
|
|
77
|
+
*
|
|
78
|
+
* // With dynamic options
|
|
79
|
+
* const connection = fetchHttpStream('/api/chat', () => ({
|
|
80
|
+
* headers: { 'Authorization': `Bearer ${getToken()}` }
|
|
81
|
+
* }));
|
|
82
|
+
*
|
|
83
|
+
* // With additional body data
|
|
84
|
+
* const connection = fetchHttpStream('/api/chat', async () => ({
|
|
85
|
+
* body: {
|
|
86
|
+
* provider: 'openai',
|
|
87
|
+
* model: 'gpt-4o',
|
|
88
|
+
* }
|
|
89
|
+
* }));
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function fetchHttpStream(url: string | (() => string), options?: FetchConnectionOptions | (() => FetchConnectionOptions | Promise<FetchConnectionOptions>)): ConnectionAdapter;
|
|
93
|
+
/**
|
|
94
|
+
* Create a direct stream connection adapter (for server functions or direct streams)
|
|
95
|
+
*
|
|
96
|
+
* @param streamFactory - A function that returns an async iterable of StreamChunks
|
|
97
|
+
* @returns A connection adapter for direct streams
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* // With TanStack Start server function
|
|
102
|
+
* const connection = stream(() => serverFunction({ messages }));
|
|
103
|
+
*
|
|
104
|
+
* const client = new ChatClient({ connection });
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare function stream(streamFactory: (messages: Array<ModelMessage>, data?: Record<string, any>) => AsyncIterable<StreamChunk>): ConnectionAdapter;
|
|
108
|
+
/**
|
|
109
|
+
* Create an RPC stream connection adapter (for RPC-based streaming like Cap'n Web RPC)
|
|
110
|
+
*
|
|
111
|
+
* @param rpcCall - A function that accepts messages and returns an async iterable of StreamChunks
|
|
112
|
+
* @returns A connection adapter for RPC streams
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* // With Cap'n Web RPC
|
|
117
|
+
* const connection = rpcStream((messages, data) =>
|
|
118
|
+
* api.streamMurfResponse(messages, data)
|
|
119
|
+
* );
|
|
120
|
+
*
|
|
121
|
+
* const client = new ChatClient({ connection });
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export declare function rpcStream(rpcCall: (messages: Array<ModelMessage>, data?: Record<string, any>) => AsyncIterable<StreamChunk>): ConnectionAdapter;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { convertMessagesToModelMessages } from "@tanstack/ai";
|
|
2
|
+
function mergeHeaders(customHeaders) {
|
|
3
|
+
if (!customHeaders) {
|
|
4
|
+
return {};
|
|
5
|
+
}
|
|
6
|
+
if (customHeaders instanceof Headers) {
|
|
7
|
+
const result = {};
|
|
8
|
+
customHeaders.forEach((value, key) => {
|
|
9
|
+
result[key] = value;
|
|
10
|
+
});
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
return customHeaders;
|
|
14
|
+
}
|
|
15
|
+
async function* readStreamLines(reader, abortSignal) {
|
|
16
|
+
try {
|
|
17
|
+
const decoder = new TextDecoder();
|
|
18
|
+
let buffer = "";
|
|
19
|
+
while (true) {
|
|
20
|
+
if (abortSignal?.aborted) {
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
const { done, value } = await reader.read();
|
|
24
|
+
if (done) break;
|
|
25
|
+
buffer += decoder.decode(value, { stream: true });
|
|
26
|
+
const lines = buffer.split("\n");
|
|
27
|
+
buffer = lines.pop() || "";
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
if (line.trim()) {
|
|
30
|
+
yield line;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (buffer.trim()) {
|
|
35
|
+
yield buffer;
|
|
36
|
+
}
|
|
37
|
+
} finally {
|
|
38
|
+
reader.releaseLock();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function fetchServerSentEvents(url, options = {}) {
|
|
42
|
+
return {
|
|
43
|
+
async *connect(messages, data, abortSignal) {
|
|
44
|
+
const resolvedUrl = typeof url === "function" ? url() : url;
|
|
45
|
+
const resolvedOptions = typeof options === "function" ? await options() : options;
|
|
46
|
+
const modelMessages = convertMessagesToModelMessages(messages);
|
|
47
|
+
const requestHeaders = {
|
|
48
|
+
"Content-Type": "application/json",
|
|
49
|
+
...mergeHeaders(resolvedOptions.headers)
|
|
50
|
+
};
|
|
51
|
+
const requestBody = {
|
|
52
|
+
messages: modelMessages,
|
|
53
|
+
data,
|
|
54
|
+
...resolvedOptions.body
|
|
55
|
+
};
|
|
56
|
+
const response = await fetch(resolvedUrl, {
|
|
57
|
+
method: "POST",
|
|
58
|
+
headers: requestHeaders,
|
|
59
|
+
body: JSON.stringify(requestBody),
|
|
60
|
+
credentials: resolvedOptions.credentials || "same-origin",
|
|
61
|
+
signal: abortSignal || resolvedOptions.signal
|
|
62
|
+
});
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`HTTP error! status: ${response.status} ${response.statusText}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const reader = response.body?.getReader();
|
|
69
|
+
if (!reader) {
|
|
70
|
+
throw new Error("Response body is not readable");
|
|
71
|
+
}
|
|
72
|
+
for await (const line of readStreamLines(reader, abortSignal)) {
|
|
73
|
+
const data2 = line.startsWith("data: ") ? line.slice(6) : line;
|
|
74
|
+
if (data2 === "[DONE]") continue;
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(data2);
|
|
77
|
+
yield parsed;
|
|
78
|
+
} catch (parseError) {
|
|
79
|
+
console.warn("Failed to parse SSE chunk:", data2);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function fetchHttpStream(url, options = {}) {
|
|
86
|
+
return {
|
|
87
|
+
async *connect(messages, data, abortSignal) {
|
|
88
|
+
const resolvedUrl = typeof url === "function" ? url() : url;
|
|
89
|
+
const resolvedOptions = typeof options === "function" ? await options() : options;
|
|
90
|
+
const modelMessages = convertMessagesToModelMessages(messages);
|
|
91
|
+
const requestHeaders = {
|
|
92
|
+
"Content-Type": "application/json",
|
|
93
|
+
...mergeHeaders(resolvedOptions.headers)
|
|
94
|
+
};
|
|
95
|
+
const requestBody = {
|
|
96
|
+
messages: modelMessages,
|
|
97
|
+
data,
|
|
98
|
+
...resolvedOptions.body
|
|
99
|
+
};
|
|
100
|
+
const response = await fetch(resolvedUrl, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: requestHeaders,
|
|
103
|
+
body: JSON.stringify(requestBody),
|
|
104
|
+
credentials: resolvedOptions.credentials || "same-origin",
|
|
105
|
+
signal: abortSignal || resolvedOptions.signal
|
|
106
|
+
});
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`HTTP error! status: ${response.status} ${response.statusText}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const reader = response.body?.getReader();
|
|
113
|
+
if (!reader) {
|
|
114
|
+
throw new Error("Response body is not readable");
|
|
115
|
+
}
|
|
116
|
+
for await (const line of readStreamLines(reader, abortSignal)) {
|
|
117
|
+
try {
|
|
118
|
+
const parsed = JSON.parse(line);
|
|
119
|
+
yield parsed;
|
|
120
|
+
} catch (parseError) {
|
|
121
|
+
console.warn("Failed to parse HTTP stream chunk:", line);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function stream(streamFactory) {
|
|
128
|
+
return {
|
|
129
|
+
async *connect(messages, data) {
|
|
130
|
+
const modelMessages = convertMessagesToModelMessages(messages);
|
|
131
|
+
yield* streamFactory(modelMessages, data);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function rpcStream(rpcCall) {
|
|
136
|
+
return {
|
|
137
|
+
async *connect(messages, data) {
|
|
138
|
+
const modelMessages = convertMessagesToModelMessages(messages);
|
|
139
|
+
yield* rpcCall(modelMessages, data);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
export {
|
|
144
|
+
fetchHttpStream,
|
|
145
|
+
fetchServerSentEvents,
|
|
146
|
+
rpcStream,
|
|
147
|
+
stream
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=connection-adapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-adapters.js","sources":["../../src/connection-adapters.ts"],"sourcesContent":["import { convertMessagesToModelMessages } from '@tanstack/ai'\nimport type { ModelMessage, StreamChunk, UIMessage } from '@tanstack/ai'\n\n/**\n * Merge custom headers into request headers\n */\nfunction mergeHeaders(\n customHeaders?: Record<string, string> | Headers,\n): Record<string, string> {\n if (!customHeaders) {\n return {}\n }\n if (customHeaders instanceof Headers) {\n const result: Record<string, string> = {}\n customHeaders.forEach((value, key) => {\n result[key] = value\n })\n return result\n }\n return customHeaders\n}\n\n/**\n * Read lines from a stream (newline-delimited)\n */\nasync function* readStreamLines(\n reader: ReadableStreamDefaultReader<Uint8Array>,\n abortSignal?: AbortSignal,\n): AsyncGenerator<string> {\n try {\n const decoder = new TextDecoder()\n let buffer = ''\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n while (true) {\n // Check if aborted before reading\n if (abortSignal?.aborted) {\n break\n }\n\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n\n // Keep the last incomplete line in the buffer\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n if (line.trim()) {\n yield line\n }\n }\n }\n\n // Process any remaining data in the buffer\n if (buffer.trim()) {\n yield buffer\n }\n } finally {\n reader.releaseLock()\n }\n}\n\n/**\n * Connection adapter interface - converts a connection into a stream of chunks\n */\nexport interface ConnectionAdapter {\n /**\n * Connect and return an async iterable of StreamChunks\n * @param messages - The messages to send (UIMessages or ModelMessages)\n * @param data - Additional data to send\n * @param abortSignal - Optional abort signal for request cancellation\n */\n connect: (\n messages: Array<UIMessage> | Array<ModelMessage>,\n data?: Record<string, any>,\n abortSignal?: AbortSignal,\n ) => AsyncIterable<StreamChunk>\n}\n\n/**\n * Options for fetch-based connection adapters\n */\nexport interface FetchConnectionOptions {\n headers?: Record<string, string> | Headers\n credentials?: RequestCredentials\n signal?: AbortSignal\n body?: Record<string, any>\n}\n\n/**\n * Create a Server-Sent Events connection adapter\n *\n * @param url - The API endpoint URL (or a function that returns the URL)\n * @param options - Fetch options (headers, credentials, body, etc.) or a function that returns options (can be async)\n * @returns A connection adapter for SSE streams\n *\n * @example\n * ```typescript\n * // Static URL\n * const connection = fetchServerSentEvents('/api/chat');\n *\n * // Dynamic URL\n * const connection = fetchServerSentEvents(() => `/api/chat?user=${userId}`);\n *\n * // With options\n * const connection = fetchServerSentEvents('/api/chat', {\n * headers: { 'Authorization': 'Bearer token' }\n * });\n *\n * // With dynamic options\n * const connection = fetchServerSentEvents('/api/chat', () => ({\n * headers: { 'Authorization': `Bearer ${getToken()}` }\n * }));\n *\n * // With additional body data\n * const connection = fetchServerSentEvents('/api/chat', async () => ({\n * body: {\n * provider: 'openai',\n * model: 'gpt-4o',\n * }\n * }));\n * ```\n */\nexport function fetchServerSentEvents(\n url: string | (() => string),\n options:\n | FetchConnectionOptions\n | (() => FetchConnectionOptions | Promise<FetchConnectionOptions>) = {},\n): ConnectionAdapter {\n return {\n async *connect(messages, data, abortSignal) {\n // Resolve URL and options if they are functions\n const resolvedUrl = typeof url === 'function' ? url() : url\n const resolvedOptions =\n typeof options === 'function' ? await options() : options\n\n const modelMessages = convertMessagesToModelMessages(messages)\n\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...mergeHeaders(resolvedOptions.headers),\n }\n\n // Merge body from options with messages and data\n const requestBody = {\n messages: modelMessages,\n data,\n ...resolvedOptions.body,\n }\n\n const response = await fetch(resolvedUrl, {\n method: 'POST',\n headers: requestHeaders,\n body: JSON.stringify(requestBody),\n credentials: resolvedOptions.credentials || 'same-origin',\n signal: abortSignal || resolvedOptions.signal,\n })\n\n if (!response.ok) {\n throw new Error(\n `HTTP error! status: ${response.status} ${response.statusText}`,\n )\n }\n\n // Parse Server-Sent Events format\n const reader = response.body?.getReader()\n if (!reader) {\n throw new Error('Response body is not readable')\n }\n\n for await (const line of readStreamLines(reader, abortSignal)) {\n // Handle Server-Sent Events format\n const data = line.startsWith('data: ') ? line.slice(6) : line\n\n if (data === '[DONE]') continue\n\n try {\n const parsed: StreamChunk = JSON.parse(data)\n yield parsed\n } catch (parseError) {\n // Skip non-JSON lines or malformed chunks\n console.warn('Failed to parse SSE chunk:', data)\n }\n }\n },\n }\n}\n\n/**\n * Create an HTTP streaming connection adapter (for raw streaming without SSE format)\n *\n * @param url - The API endpoint URL (or a function that returns the URL)\n * @param options - Fetch options (headers, credentials, body, etc.) or a function that returns options (can be async)\n * @returns A connection adapter for HTTP streams\n *\n * @example\n * ```typescript\n * // Static URL\n * const connection = fetchHttpStream('/api/chat');\n *\n * // Dynamic URL\n * const connection = fetchHttpStream(() => `/api/chat?user=${userId}`);\n *\n * // With options\n * const connection = fetchHttpStream('/api/chat', {\n * headers: { 'Authorization': 'Bearer token' }\n * });\n *\n * // With dynamic options\n * const connection = fetchHttpStream('/api/chat', () => ({\n * headers: { 'Authorization': `Bearer ${getToken()}` }\n * }));\n *\n * // With additional body data\n * const connection = fetchHttpStream('/api/chat', async () => ({\n * body: {\n * provider: 'openai',\n * model: 'gpt-4o',\n * }\n * }));\n * ```\n */\nexport function fetchHttpStream(\n url: string | (() => string),\n options:\n | FetchConnectionOptions\n | (() => FetchConnectionOptions | Promise<FetchConnectionOptions>) = {},\n): ConnectionAdapter {\n return {\n async *connect(messages, data, abortSignal) {\n // Resolve URL and options if they are functions\n const resolvedUrl = typeof url === 'function' ? url() : url\n const resolvedOptions =\n typeof options === 'function' ? await options() : options\n\n // Convert UIMessages to ModelMessages if needed\n const modelMessages = convertMessagesToModelMessages(messages)\n\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...mergeHeaders(resolvedOptions.headers),\n }\n\n // Merge body from options with messages and data\n const requestBody = {\n messages: modelMessages,\n data,\n ...resolvedOptions.body,\n }\n\n const response = await fetch(resolvedUrl, {\n method: 'POST',\n headers: requestHeaders,\n body: JSON.stringify(requestBody),\n credentials: resolvedOptions.credentials || 'same-origin',\n signal: abortSignal || resolvedOptions.signal,\n })\n\n if (!response.ok) {\n throw new Error(\n `HTTP error! status: ${response.status} ${response.statusText}`,\n )\n }\n\n // Parse raw HTTP stream (newline-delimited JSON)\n const reader = response.body?.getReader()\n if (!reader) {\n throw new Error('Response body is not readable')\n }\n\n for await (const line of readStreamLines(reader, abortSignal)) {\n try {\n const parsed: StreamChunk = JSON.parse(line)\n yield parsed\n } catch (parseError) {\n console.warn('Failed to parse HTTP stream chunk:', line)\n }\n }\n },\n }\n}\n\n/**\n * Create a direct stream connection adapter (for server functions or direct streams)\n *\n * @param streamFactory - A function that returns an async iterable of StreamChunks\n * @returns A connection adapter for direct streams\n *\n * @example\n * ```typescript\n * // With TanStack Start server function\n * const connection = stream(() => serverFunction({ messages }));\n *\n * const client = new ChatClient({ connection });\n * ```\n */\nexport function stream(\n streamFactory: (\n messages: Array<ModelMessage>,\n data?: Record<string, any>,\n ) => AsyncIterable<StreamChunk>,\n): ConnectionAdapter {\n return {\n async *connect(messages, data) {\n const modelMessages = convertMessagesToModelMessages(messages)\n yield* streamFactory(modelMessages, data)\n },\n }\n}\n\n/**\n * Create an RPC stream connection adapter (for RPC-based streaming like Cap'n Web RPC)\n *\n * @param rpcCall - A function that accepts messages and returns an async iterable of StreamChunks\n * @returns A connection adapter for RPC streams\n *\n * @example\n * ```typescript\n * // With Cap'n Web RPC\n * const connection = rpcStream((messages, data) =>\n * api.streamMurfResponse(messages, data)\n * );\n *\n * const client = new ChatClient({ connection });\n * ```\n */\nexport function rpcStream(\n rpcCall: (\n messages: Array<ModelMessage>,\n data?: Record<string, any>,\n ) => AsyncIterable<StreamChunk>,\n): ConnectionAdapter {\n return {\n async *connect(messages, data) {\n const modelMessages = convertMessagesToModelMessages(messages)\n // Simply yield from the RPC call\n // The RPC layer handles WebSocket transport\n yield* rpcCall(modelMessages, data)\n },\n }\n}\n"],"names":["data"],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { UIMessage } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for ChatClient event emission
|
|
4
|
+
*/
|
|
5
|
+
export declare abstract class ChatClientEventEmitter {
|
|
6
|
+
protected clientId: string;
|
|
7
|
+
constructor(clientId: string);
|
|
8
|
+
/**
|
|
9
|
+
* Protected abstract method for emitting events
|
|
10
|
+
* Implementations should handle adding clientId and timestamp
|
|
11
|
+
*/
|
|
12
|
+
protected abstract emitEvent(eventName: string, data?: Record<string, any>): void;
|
|
13
|
+
/**
|
|
14
|
+
* Emit client created event
|
|
15
|
+
*/
|
|
16
|
+
clientCreated(initialMessageCount: number): void;
|
|
17
|
+
/**
|
|
18
|
+
* Emit loading state changed event
|
|
19
|
+
*/
|
|
20
|
+
loadingChanged(isLoading: boolean): void;
|
|
21
|
+
/**
|
|
22
|
+
* Emit error state changed event
|
|
23
|
+
*/
|
|
24
|
+
errorChanged(error: string | null): void;
|
|
25
|
+
/**
|
|
26
|
+
* Emit text update events (combines processor and client events)
|
|
27
|
+
*/
|
|
28
|
+
textUpdated(streamId: string, messageId: string, content: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Emit tool call state change events (combines processor and client events)
|
|
31
|
+
*/
|
|
32
|
+
toolCallStateChanged(streamId: string, messageId: string, toolCallId: string, toolName: string, state: string, args: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Emit tool result state change event
|
|
35
|
+
*/
|
|
36
|
+
toolResultStateChanged(streamId: string, toolCallId: string, content: string, state: string, error?: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Emit thinking update event
|
|
39
|
+
*/
|
|
40
|
+
thinkingUpdated(streamId: string, messageId: string, content: string, delta?: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Emit approval requested event
|
|
43
|
+
*/
|
|
44
|
+
approvalRequested(messageId: string, toolCallId: string, toolName: string, input: any, approvalId: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Emit message appended event
|
|
47
|
+
*/
|
|
48
|
+
messageAppended(uiMessage: UIMessage): void;
|
|
49
|
+
/**
|
|
50
|
+
* Emit message sent event
|
|
51
|
+
*/
|
|
52
|
+
messageSent(messageId: string, content: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* Emit reloaded event
|
|
55
|
+
*/
|
|
56
|
+
reloaded(fromMessageIndex: number): void;
|
|
57
|
+
/**
|
|
58
|
+
* Emit stopped event
|
|
59
|
+
*/
|
|
60
|
+
stopped(): void;
|
|
61
|
+
/**
|
|
62
|
+
* Emit messages cleared event
|
|
63
|
+
*/
|
|
64
|
+
messagesCleared(): void;
|
|
65
|
+
/**
|
|
66
|
+
* Emit tool result added event
|
|
67
|
+
*/
|
|
68
|
+
toolResultAdded(toolCallId: string, toolName: string, output: any, state: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* Emit tool approval responded event
|
|
71
|
+
*/
|
|
72
|
+
toolApprovalResponded(approvalId: string, toolCallId: string, approved: boolean): void;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Default implementation of ChatClientEventEmitter
|
|
76
|
+
*/
|
|
77
|
+
export declare class DefaultChatClientEventEmitter extends ChatClientEventEmitter {
|
|
78
|
+
/**
|
|
79
|
+
* Emit an event with automatic clientId and timestamp for client/tool events
|
|
80
|
+
*/
|
|
81
|
+
protected emitEvent(eventName: string, data?: Record<string, any>): void;
|
|
82
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { aiEventClient } from "@tanstack/ai/event-client";
|
|
2
|
+
class ChatClientEventEmitter {
|
|
3
|
+
constructor(clientId) {
|
|
4
|
+
this.clientId = clientId;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Emit client created event
|
|
8
|
+
*/
|
|
9
|
+
clientCreated(initialMessageCount) {
|
|
10
|
+
this.emitEvent("client:created", {
|
|
11
|
+
initialMessageCount
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Emit loading state changed event
|
|
16
|
+
*/
|
|
17
|
+
loadingChanged(isLoading) {
|
|
18
|
+
this.emitEvent("client:loading-changed", { isLoading });
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Emit error state changed event
|
|
22
|
+
*/
|
|
23
|
+
errorChanged(error) {
|
|
24
|
+
this.emitEvent("client:error-changed", {
|
|
25
|
+
error
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Emit text update events (combines processor and client events)
|
|
30
|
+
*/
|
|
31
|
+
textUpdated(streamId, messageId, content) {
|
|
32
|
+
this.emitEvent("processor:text-updated", {
|
|
33
|
+
streamId,
|
|
34
|
+
content
|
|
35
|
+
});
|
|
36
|
+
this.emitEvent("client:assistant-message-updated", {
|
|
37
|
+
messageId,
|
|
38
|
+
content
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Emit tool call state change events (combines processor and client events)
|
|
43
|
+
*/
|
|
44
|
+
toolCallStateChanged(streamId, messageId, toolCallId, toolName, state, args) {
|
|
45
|
+
this.emitEvent("processor:tool-call-state-changed", {
|
|
46
|
+
streamId,
|
|
47
|
+
toolCallId,
|
|
48
|
+
toolName,
|
|
49
|
+
state,
|
|
50
|
+
arguments: args
|
|
51
|
+
});
|
|
52
|
+
this.emitEvent("client:tool-call-updated", {
|
|
53
|
+
messageId,
|
|
54
|
+
toolCallId,
|
|
55
|
+
toolName,
|
|
56
|
+
state,
|
|
57
|
+
arguments: args
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Emit tool result state change event
|
|
62
|
+
*/
|
|
63
|
+
toolResultStateChanged(streamId, toolCallId, content, state, error) {
|
|
64
|
+
this.emitEvent("processor:tool-result-state-changed", {
|
|
65
|
+
streamId,
|
|
66
|
+
toolCallId,
|
|
67
|
+
content,
|
|
68
|
+
state,
|
|
69
|
+
error
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Emit thinking update event
|
|
74
|
+
*/
|
|
75
|
+
thinkingUpdated(streamId, messageId, content, delta) {
|
|
76
|
+
this.emitEvent("stream:chunk:thinking", {
|
|
77
|
+
streamId,
|
|
78
|
+
messageId,
|
|
79
|
+
content,
|
|
80
|
+
delta
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Emit approval requested event
|
|
85
|
+
*/
|
|
86
|
+
approvalRequested(messageId, toolCallId, toolName, input, approvalId) {
|
|
87
|
+
this.emitEvent("client:approval-requested", {
|
|
88
|
+
messageId,
|
|
89
|
+
toolCallId,
|
|
90
|
+
toolName,
|
|
91
|
+
input,
|
|
92
|
+
approvalId
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Emit message appended event
|
|
97
|
+
*/
|
|
98
|
+
messageAppended(uiMessage) {
|
|
99
|
+
const contentPreview = uiMessage.parts.filter((p) => p.type === "text").map((p) => p.content).join(" ").substring(0, 100);
|
|
100
|
+
this.emitEvent("client:message-appended", {
|
|
101
|
+
messageId: uiMessage.id,
|
|
102
|
+
role: uiMessage.role,
|
|
103
|
+
contentPreview
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Emit message sent event
|
|
108
|
+
*/
|
|
109
|
+
messageSent(messageId, content) {
|
|
110
|
+
this.emitEvent("client:message-sent", {
|
|
111
|
+
messageId,
|
|
112
|
+
content
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Emit reloaded event
|
|
117
|
+
*/
|
|
118
|
+
reloaded(fromMessageIndex) {
|
|
119
|
+
this.emitEvent("client:reloaded", {
|
|
120
|
+
fromMessageIndex
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Emit stopped event
|
|
125
|
+
*/
|
|
126
|
+
stopped() {
|
|
127
|
+
this.emitEvent("client:stopped");
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Emit messages cleared event
|
|
131
|
+
*/
|
|
132
|
+
messagesCleared() {
|
|
133
|
+
this.emitEvent("client:messages-cleared");
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Emit tool result added event
|
|
137
|
+
*/
|
|
138
|
+
toolResultAdded(toolCallId, toolName, output, state) {
|
|
139
|
+
this.emitEvent("tool:result-added", {
|
|
140
|
+
toolCallId,
|
|
141
|
+
toolName,
|
|
142
|
+
output,
|
|
143
|
+
state
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Emit tool approval responded event
|
|
148
|
+
*/
|
|
149
|
+
toolApprovalResponded(approvalId, toolCallId, approved) {
|
|
150
|
+
this.emitEvent("tool:approval-responded", {
|
|
151
|
+
approvalId,
|
|
152
|
+
toolCallId,
|
|
153
|
+
approved
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
class DefaultChatClientEventEmitter extends ChatClientEventEmitter {
|
|
158
|
+
/**
|
|
159
|
+
* Emit an event with automatic clientId and timestamp for client/tool events
|
|
160
|
+
*/
|
|
161
|
+
emitEvent(eventName, data) {
|
|
162
|
+
if (eventName.startsWith("client:") || eventName.startsWith("tool:")) {
|
|
163
|
+
aiEventClient.emit(eventName, {
|
|
164
|
+
...data,
|
|
165
|
+
clientId: this.clientId,
|
|
166
|
+
timestamp: Date.now()
|
|
167
|
+
});
|
|
168
|
+
} else {
|
|
169
|
+
aiEventClient.emit(eventName, {
|
|
170
|
+
...data,
|
|
171
|
+
timestamp: Date.now()
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
export {
|
|
177
|
+
ChatClientEventEmitter,
|
|
178
|
+
DefaultChatClientEventEmitter
|
|
179
|
+
};
|
|
180
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sources":["../../src/events.ts"],"sourcesContent":["import { aiEventClient } from '@tanstack/ai/event-client'\nimport type { UIMessage } from './types'\n\n/**\n * Abstract base class for ChatClient event emission\n */\nexport abstract class ChatClientEventEmitter {\n protected clientId: string\n\n constructor(clientId: string) {\n this.clientId = clientId\n }\n\n /**\n * Protected abstract method for emitting events\n * Implementations should handle adding clientId and timestamp\n */\n protected abstract emitEvent(\n eventName: string,\n data?: Record<string, any>,\n ): void\n\n /**\n * Emit client created event\n */\n clientCreated(initialMessageCount: number): void {\n this.emitEvent('client:created', {\n initialMessageCount,\n })\n }\n\n /**\n * Emit loading state changed event\n */\n loadingChanged(isLoading: boolean): void {\n this.emitEvent('client:loading-changed', { isLoading })\n }\n\n /**\n * Emit error state changed event\n */\n errorChanged(error: string | null): void {\n this.emitEvent('client:error-changed', {\n error,\n })\n }\n\n /**\n * Emit text update events (combines processor and client events)\n */\n textUpdated(streamId: string, messageId: string, content: string): void {\n this.emitEvent('processor:text-updated', {\n streamId,\n content,\n })\n\n this.emitEvent('client:assistant-message-updated', {\n messageId,\n content,\n })\n }\n\n /**\n * Emit tool call state change events (combines processor and client events)\n */\n toolCallStateChanged(\n streamId: string,\n messageId: string,\n toolCallId: string,\n toolName: string,\n state: string,\n args: string,\n ): void {\n this.emitEvent('processor:tool-call-state-changed', {\n streamId,\n toolCallId,\n toolName,\n state,\n arguments: args,\n })\n\n this.emitEvent('client:tool-call-updated', {\n messageId,\n toolCallId,\n toolName,\n state,\n arguments: args,\n })\n }\n\n /**\n * Emit tool result state change event\n */\n toolResultStateChanged(\n streamId: string,\n toolCallId: string,\n content: string,\n state: string,\n error?: string,\n ): void {\n this.emitEvent('processor:tool-result-state-changed', {\n streamId,\n toolCallId,\n content,\n state,\n error,\n })\n }\n\n /**\n * Emit thinking update event\n */\n thinkingUpdated(\n streamId: string,\n messageId: string,\n content: string,\n delta?: string,\n ): void {\n this.emitEvent('stream:chunk:thinking', {\n streamId,\n messageId,\n content,\n delta,\n })\n }\n\n /**\n * Emit approval requested event\n */\n approvalRequested(\n messageId: string,\n toolCallId: string,\n toolName: string,\n input: any,\n approvalId: string,\n ): void {\n this.emitEvent('client:approval-requested', {\n messageId,\n toolCallId,\n toolName,\n input,\n approvalId,\n })\n }\n\n /**\n * Emit message appended event\n */\n messageAppended(uiMessage: UIMessage): void {\n const contentPreview = uiMessage.parts\n .filter((p) => p.type === 'text')\n .map((p) => (p as any).content)\n .join(' ')\n .substring(0, 100)\n\n this.emitEvent('client:message-appended', {\n messageId: uiMessage.id,\n role: uiMessage.role,\n contentPreview,\n })\n }\n\n /**\n * Emit message sent event\n */\n messageSent(messageId: string, content: string): void {\n this.emitEvent('client:message-sent', {\n messageId,\n content,\n })\n }\n\n /**\n * Emit reloaded event\n */\n reloaded(fromMessageIndex: number): void {\n this.emitEvent('client:reloaded', {\n fromMessageIndex,\n })\n }\n\n /**\n * Emit stopped event\n */\n stopped(): void {\n this.emitEvent('client:stopped')\n }\n\n /**\n * Emit messages cleared event\n */\n messagesCleared(): void {\n this.emitEvent('client:messages-cleared')\n }\n\n /**\n * Emit tool result added event\n */\n toolResultAdded(\n toolCallId: string,\n toolName: string,\n output: any,\n state: string,\n ): void {\n this.emitEvent('tool:result-added', {\n toolCallId,\n toolName,\n output,\n state,\n })\n }\n\n /**\n * Emit tool approval responded event\n */\n toolApprovalResponded(\n approvalId: string,\n toolCallId: string,\n approved: boolean,\n ): void {\n this.emitEvent('tool:approval-responded', {\n approvalId,\n toolCallId,\n approved,\n })\n }\n}\n\n/**\n * Default implementation of ChatClientEventEmitter\n */\nexport class DefaultChatClientEventEmitter extends ChatClientEventEmitter {\n /**\n * Emit an event with automatic clientId and timestamp for client/tool events\n */\n protected emitEvent(eventName: string, data?: Record<string, any>): void {\n // For client:* and tool:* events, automatically add clientId and timestamp\n if (eventName.startsWith('client:') || eventName.startsWith('tool:')) {\n aiEventClient.emit(eventName as any, {\n ...data,\n clientId: this.clientId,\n timestamp: Date.now(),\n })\n } else {\n // For other events (e.g., processor:*), just add timestamp\n aiEventClient.emit(eventName as any, {\n ...data,\n timestamp: Date.now(),\n })\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { ChatClient } from './chat-client.js';
|
|
2
|
+
export type { UIMessage, MessagePart, TextPart, ToolCallPart, ToolResultPart, ThinkingPart, ChatClientOptions, ChatRequestBody, InferChatMessages, } from './types.js';
|
|
3
|
+
export { clientTools, createChatClientOptions } from './types.js';
|
|
4
|
+
export type { ExtractToolNames, ExtractToolInput, ExtractToolOutput, } from './tool-types.js';
|
|
5
|
+
export type { AnyClientTool } from '@tanstack/ai';
|
|
6
|
+
export { fetchServerSentEvents, fetchHttpStream, stream, rpcStream, type ConnectionAdapter, type FetchConnectionOptions, } from './connection-adapters.js';
|
|
7
|
+
export { uiMessageToModelMessages, modelMessageToUIMessage, modelMessagesToUIMessages, convertMessagesToModelMessages, normalizeToUIMessage, generateMessageId, } from '@tanstack/ai';
|
|
8
|
+
export { StreamProcessor, ImmediateStrategy, PunctuationStrategy, BatchStrategy, WordBoundaryStrategy, CompositeStrategy, parsePartialJSON, PartialJSONParser, defaultJSONParser, type ChunkStrategy, type StreamProcessorOptions, type StreamProcessorHandlers, type StreamProcessorEvents, type InternalToolCallState, type ToolCallState, type ToolResultState, type JSONParser, type ChunkRecording, type ProcessorResult, type ProcessorState, } from '@tanstack/ai';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ChatClient } from "./chat-client.js";
|
|
2
|
+
import { clientTools, createChatClientOptions } from "./types.js";
|
|
3
|
+
import { fetchHttpStream, fetchServerSentEvents, rpcStream, stream } from "./connection-adapters.js";
|
|
4
|
+
import { BatchStrategy, CompositeStrategy, ImmediateStrategy, PartialJSONParser, PunctuationStrategy, StreamProcessor, WordBoundaryStrategy, convertMessagesToModelMessages, defaultJSONParser, generateMessageId, modelMessageToUIMessage, modelMessagesToUIMessages, normalizeToUIMessage, parsePartialJSON, uiMessageToModelMessages } from "@tanstack/ai";
|
|
5
|
+
export {
|
|
6
|
+
BatchStrategy,
|
|
7
|
+
ChatClient,
|
|
8
|
+
CompositeStrategy,
|
|
9
|
+
ImmediateStrategy,
|
|
10
|
+
PartialJSONParser,
|
|
11
|
+
PunctuationStrategy,
|
|
12
|
+
StreamProcessor,
|
|
13
|
+
WordBoundaryStrategy,
|
|
14
|
+
clientTools,
|
|
15
|
+
convertMessagesToModelMessages,
|
|
16
|
+
createChatClientOptions,
|
|
17
|
+
defaultJSONParser,
|
|
18
|
+
fetchHttpStream,
|
|
19
|
+
fetchServerSentEvents,
|
|
20
|
+
generateMessageId,
|
|
21
|
+
modelMessageToUIMessage,
|
|
22
|
+
modelMessagesToUIMessages,
|
|
23
|
+
normalizeToUIMessage,
|
|
24
|
+
parsePartialJSON,
|
|
25
|
+
rpcStream,
|
|
26
|
+
stream,
|
|
27
|
+
uiMessageToModelMessages
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AnyClientTool, InferToolInput, InferToolOutput } from '@tanstack/ai';
|
|
2
|
+
/**
|
|
3
|
+
* Extract all tool names from a tools array as a union type
|
|
4
|
+
*/
|
|
5
|
+
export type ExtractToolNames<TTools extends ReadonlyArray<AnyClientTool>> = TTools[number]['name'];
|
|
6
|
+
/**
|
|
7
|
+
* Find a tool by name in the tools array
|
|
8
|
+
*/
|
|
9
|
+
type FindTool<TTools extends ReadonlyArray<AnyClientTool>, TName extends string> = Extract<TTools[number], {
|
|
10
|
+
name: TName;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* Extract the input type for a specific tool by name
|
|
14
|
+
*/
|
|
15
|
+
export type ExtractToolInput<TTools extends ReadonlyArray<AnyClientTool>, TName extends string> = TName extends ExtractToolNames<TTools> ? InferToolInput<FindTool<TTools, TName>> : any;
|
|
16
|
+
/**
|
|
17
|
+
* Extract the output type for a specific tool by name
|
|
18
|
+
*/
|
|
19
|
+
export type ExtractToolOutput<TTools extends ReadonlyArray<AnyClientTool>, TName extends string> = TName extends ExtractToolNames<TTools> ? InferToolOutput<FindTool<TTools, TName>> : any;
|
|
20
|
+
export {};
|