@tanstack/ai 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/base-adapter.d.ts +35 -0
- package/dist/esm/base-adapter.js +12 -0
- package/dist/esm/base-adapter.js.map +1 -0
- package/dist/esm/core/chat-common-options.d.ts +52 -0
- package/dist/esm/core/chat.d.ts +30 -0
- package/dist/esm/core/chat.js +533 -0
- package/dist/esm/core/chat.js.map +1 -0
- package/dist/esm/core/embedding.d.ts +8 -0
- package/dist/esm/core/embedding.js +33 -0
- package/dist/esm/core/embedding.js.map +1 -0
- package/dist/esm/core/summarize.d.ts +9 -0
- package/dist/esm/core/summarize.js +36 -0
- package/dist/esm/core/summarize.js.map +1 -0
- package/dist/esm/event-client.d.ts +311 -0
- package/dist/esm/event-client.js +62 -0
- package/dist/esm/event-client.js.map +1 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.js +50 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/message-converters.d.ts +52 -0
- package/dist/esm/message-converters.js +162 -0
- package/dist/esm/message-converters.js.map +1 -0
- package/dist/esm/stream/index.d.ts +11 -0
- package/dist/esm/stream/json-parser.d.ts +38 -0
- package/dist/esm/stream/json-parser.js +28 -0
- package/dist/esm/stream/json-parser.js.map +1 -0
- package/dist/esm/stream/message-updaters.d.ts +44 -0
- package/dist/esm/stream/message-updaters.js +141 -0
- package/dist/esm/stream/message-updaters.js.map +1 -0
- package/dist/esm/stream/processor.d.ts +242 -0
- package/dist/esm/stream/processor.js +693 -0
- package/dist/esm/stream/processor.js.map +1 -0
- package/dist/esm/stream/strategies.d.ts +43 -0
- package/dist/esm/stream/strategies.js +54 -0
- package/dist/esm/stream/strategies.js.map +1 -0
- package/dist/esm/stream/types.d.ts +71 -0
- package/dist/esm/tools/tool-calls.d.ts +112 -0
- package/dist/esm/tools/tool-calls.js +302 -0
- package/dist/esm/tools/tool-calls.js.map +1 -0
- package/dist/esm/tools/tool-definition.d.ts +125 -0
- package/dist/esm/tools/tool-definition.js +25 -0
- package/dist/esm/tools/tool-definition.js.map +1 -0
- package/dist/esm/tools/zod-converter.d.ts +30 -0
- package/dist/esm/tools/zod-converter.js +36 -0
- package/dist/esm/tools/zod-converter.js.map +1 -0
- package/dist/esm/types.d.ts +619 -0
- package/dist/esm/utilities/agent-loop-strategies.d.ts +59 -0
- package/dist/esm/utilities/agent-loop-strategies.js +23 -0
- package/dist/esm/utilities/agent-loop-strategies.js.map +1 -0
- package/dist/esm/utilities/chat-options.d.ts +6 -0
- package/dist/esm/utilities/chat-options.js +7 -0
- package/dist/esm/utilities/chat-options.js.map +1 -0
- package/dist/esm/utilities/messages.d.ts +30 -0
- package/dist/esm/utilities/messages.js +7 -0
- package/dist/esm/utilities/messages.js.map +1 -0
- package/dist/esm/utilities/stream-to-response.d.ts +48 -0
- package/dist/esm/utilities/stream-to-response.js +62 -0
- package/dist/esm/utilities/stream-to-response.js.map +1 -0
- package/package.json +65 -0
- package/src/base-adapter.ts +85 -0
- package/src/core/chat-common-options.ts +55 -0
- package/src/core/chat.ts +771 -0
- package/src/core/embedding.ts +54 -0
- package/src/core/summarize.ts +56 -0
- package/src/event-client.ts +389 -0
- package/src/index.ts +68 -0
- package/src/message-converters.ts +285 -0
- package/src/stream/index.ts +41 -0
- package/src/stream/json-parser.ts +58 -0
- package/src/stream/message-updaters.ts +275 -0
- package/src/stream/processor.ts +1092 -0
- package/src/stream/strategies.ts +78 -0
- package/src/stream/types.ts +94 -0
- package/src/tools/tool-calls.ts +471 -0
- package/src/tools/tool-definition.ts +206 -0
- package/src/tools/zod-converter.ts +85 -0
- package/src/types.ts +872 -0
- package/src/utilities/agent-loop-strategies.ts +85 -0
- package/src/utilities/chat-options.ts +35 -0
- package/src/utilities/messages.ts +63 -0
- package/src/utilities/stream-to-response.ts +116 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-loop-strategies.js","sources":["../../../src/utilities/agent-loop-strategies.ts"],"sourcesContent":["import type { AgentLoopStrategy } from '../types'\n\n/**\n * Creates a strategy that continues for a maximum number of iterations\n *\n * @param max - Maximum number of iterations to allow\n * @returns AgentLoopStrategy that stops after max iterations\n *\n * @example\n * ```typescript\n * const stream = chat({\n * adapter: openai(),\n * model: \"gpt-4o\",\n * messages: [...],\n * tools: [weatherTool],\n * agentLoopStrategy: maxIterations(3), // Max 3 iterations\n * });\n * ```\n */\nexport function maxIterations(max: number): AgentLoopStrategy {\n return ({ iterationCount }) => iterationCount < max\n}\n\n/**\n * Creates a strategy that continues until a specific finish reason is encountered\n *\n * @param stopReasons - Finish reasons that should stop the loop\n * @returns AgentLoopStrategy that stops on specific finish reasons\n *\n * @example\n * ```typescript\n * const stream = chat({\n * adapter: openai(),\n * model: \"gpt-4o\",\n * messages: [...],\n * tools: [weatherTool],\n * agentLoopStrategy: untilFinishReason([\"stop\", \"length\"]),\n * });\n * ```\n */\nexport function untilFinishReason(\n stopReasons: Array<string>,\n): AgentLoopStrategy {\n return ({ finishReason, iterationCount }) => {\n // Always allow at least one iteration\n if (iterationCount === 0) return true\n\n // Stop if we hit a stop reason\n if (finishReason && stopReasons.includes(finishReason)) {\n return false\n }\n\n // Otherwise continue\n return true\n }\n}\n\n/**\n * Creates a strategy that combines multiple strategies with AND logic\n * All strategies must return true to continue\n *\n * @param strategies - Array of strategies to combine\n * @returns AgentLoopStrategy that continues only if all strategies return true\n *\n * @example\n * ```typescript\n * const stream = chat({\n * adapter: openai(),\n * model: \"gpt-4o\",\n * messages: [...],\n * tools: [weatherTool],\n * agentLoopStrategy: combineStrategies([\n * maxIterations(10),\n * ({ messages }) => messages.length < 100,\n * ]),\n * });\n * ```\n */\nexport function combineStrategies(\n strategies: Array<AgentLoopStrategy>,\n): AgentLoopStrategy {\n return (state) => {\n return strategies.every((strategy) => strategy(state))\n }\n}\n"],"names":[],"mappings":"AAmBO,SAAS,cAAc,KAAgC;AAC5D,SAAO,CAAC,EAAE,qBAAqB,iBAAiB;AAClD;AAmBO,SAAS,kBACd,aACmB;AACnB,SAAO,CAAC,EAAE,cAAc,qBAAqB;AAE3C,QAAI,mBAAmB,EAAG,QAAO;AAGjC,QAAI,gBAAgB,YAAY,SAAS,YAAY,GAAG;AACtD,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,EACT;AACF;AAuBO,SAAS,kBACd,YACmB;AACnB,SAAO,CAAC,UAAU;AAChB,WAAO,WAAW,MAAM,CAAC,aAAa,SAAS,KAAK,CAAC;AAAA,EACvD;AACF;"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AIAdapter, ChatStreamOptionsUnion } from '../types.js';
|
|
2
|
+
export declare function chatOptions<TAdapter extends AIAdapter<any, any, any, any, any>, const TModel extends TAdapter extends AIAdapter<infer Models, any, any, any, any> ? Models[number] : string>(options: Omit<ChatStreamOptionsUnion<TAdapter>, 'providerOptions' | 'model' | 'messages' | 'abortController'> & {
|
|
3
|
+
adapter: TAdapter;
|
|
4
|
+
model: TModel;
|
|
5
|
+
providerOptions?: TAdapter extends AIAdapter<any, any, any, any, infer ModelProviderOptions> ? TModel extends keyof ModelProviderOptions ? ModelProviderOptions[TModel] : never : never;
|
|
6
|
+
}): typeof options;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-options.js","sources":["../../../src/utilities/chat-options.ts"],"sourcesContent":["import type { AIAdapter, ChatStreamOptionsUnion } from '../types'\n\nexport function chatOptions<\n TAdapter extends AIAdapter<any, any, any, any, any>,\n const TModel extends TAdapter extends AIAdapter<\n infer Models,\n any,\n any,\n any,\n any\n >\n ? Models[number]\n : string,\n>(\n options: Omit<\n ChatStreamOptionsUnion<TAdapter>,\n 'providerOptions' | 'model' | 'messages' | 'abortController'\n > & {\n adapter: TAdapter\n model: TModel\n providerOptions?: TAdapter extends AIAdapter<\n any,\n any,\n any,\n any,\n infer ModelProviderOptions\n >\n ? TModel extends keyof ModelProviderOptions\n ? ModelProviderOptions[TModel]\n : never\n : never\n },\n): typeof options {\n return options\n}\n"],"names":[],"mappings":"AAEO,SAAS,YAYd,SAkBgB;AAChB,SAAO;AACT;"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AIAdapter, ConstrainedModelMessage, Modality, ModelMessage } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Type-safe helper to create a messages array constrained by a model's supported modalities.
|
|
4
|
+
*
|
|
5
|
+
* This function provides compile-time checking that your messages only contain
|
|
6
|
+
* content types supported by the specified model. It's particularly useful when
|
|
7
|
+
* combining typed messages with untyped data (like from request.json()).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { messages, chat } from '@tanstack/ai'
|
|
12
|
+
* import { openai } from '@tanstack/ai-openai'
|
|
13
|
+
*
|
|
14
|
+
* const adapter = openai()
|
|
15
|
+
*
|
|
16
|
+
* // This will error at compile time because gpt-4o only supports text+image
|
|
17
|
+
* const msgs = messages({ adapter, model: 'gpt-4o' }, [
|
|
18
|
+
* {
|
|
19
|
+
* role: 'user',
|
|
20
|
+
* content: [
|
|
21
|
+
* { type: 'video', source: { type: 'url', value: '...' } } // Error!
|
|
22
|
+
* ]
|
|
23
|
+
* }
|
|
24
|
+
* ])
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function messages<TAdapter extends AIAdapter<any, any, any, any, any, any>, const TModel extends TAdapter extends AIAdapter<infer Models, any, any, any, any, any> ? Models[number] : string>(_options: {
|
|
28
|
+
adapter: TAdapter;
|
|
29
|
+
model: TModel;
|
|
30
|
+
}, msgs: TAdapter extends AIAdapter<any, any, any, any, any, infer ModelInputModalities> ? TModel extends keyof ModelInputModalities ? ModelInputModalities[TModel] extends ReadonlyArray<Modality> ? Array<ConstrainedModelMessage<ModelInputModalities[TModel]>> : Array<ModelMessage> : Array<ModelMessage> : Array<ModelMessage>): typeof msgs;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sources":["../../../src/utilities/messages.ts"],"sourcesContent":["import type {\n AIAdapter,\n ConstrainedModelMessage,\n Modality,\n ModelMessage,\n} from '../types'\n\n/**\n * Type-safe helper to create a messages array constrained by a model's supported modalities.\n *\n * This function provides compile-time checking that your messages only contain\n * content types supported by the specified model. It's particularly useful when\n * combining typed messages with untyped data (like from request.json()).\n *\n * @example\n * ```typescript\n * import { messages, chat } from '@tanstack/ai'\n * import { openai } from '@tanstack/ai-openai'\n *\n * const adapter = openai()\n *\n * // This will error at compile time because gpt-4o only supports text+image\n * const msgs = messages({ adapter, model: 'gpt-4o' }, [\n * {\n * role: 'user',\n * content: [\n * { type: 'video', source: { type: 'url', value: '...' } } // Error!\n * ]\n * }\n * ])\n * ```\n */\nexport function messages<\n TAdapter extends AIAdapter<any, any, any, any, any, any>,\n const TModel extends TAdapter extends AIAdapter<\n infer Models,\n any,\n any,\n any,\n any,\n any\n >\n ? Models[number]\n : string,\n>(\n _options: { adapter: TAdapter; model: TModel },\n msgs: TAdapter extends AIAdapter<\n any,\n any,\n any,\n any,\n any,\n infer ModelInputModalities\n >\n ? TModel extends keyof ModelInputModalities\n ? ModelInputModalities[TModel] extends ReadonlyArray<Modality>\n ? Array<ConstrainedModelMessage<ModelInputModalities[TModel]>>\n : Array<ModelMessage>\n : Array<ModelMessage>\n : Array<ModelMessage>,\n): typeof msgs {\n return msgs\n}\n"],"names":[],"mappings":"AAgCO,SAAS,SAad,UACA,MAca;AACb,SAAO;AACT;"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { StreamChunk } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a StreamChunk async iterable to a ReadableStream in Server-Sent Events format
|
|
4
|
+
*
|
|
5
|
+
* This creates a ReadableStream that emits chunks in SSE format:
|
|
6
|
+
* - Each chunk is prefixed with "data: "
|
|
7
|
+
* - Each chunk is followed by "\n\n"
|
|
8
|
+
* - Stream ends with "data: [DONE]\n\n"
|
|
9
|
+
*
|
|
10
|
+
* @param stream - AsyncIterable of StreamChunks from chat()
|
|
11
|
+
* @param abortController - Optional AbortController to abort when stream is cancelled
|
|
12
|
+
* @returns ReadableStream in Server-Sent Events format
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const stream = chat({ adapter: openai(), model: "gpt-4o", messages: [...] });
|
|
17
|
+
* const readableStream = toServerSentEventsStream(stream);
|
|
18
|
+
* // Use with Response, or any API that accepts ReadableStream
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function toServerSentEventsStream(stream: AsyncIterable<StreamChunk>, abortController?: AbortController): ReadableStream<Uint8Array>;
|
|
22
|
+
/**
|
|
23
|
+
* Create a streaming HTTP response from a StreamChunk async iterable
|
|
24
|
+
* Includes proper headers for Server-Sent Events
|
|
25
|
+
*
|
|
26
|
+
* @param stream - AsyncIterable of StreamChunks from chat()
|
|
27
|
+
* @param init - Optional Response initialization options
|
|
28
|
+
* @param abortController - Optional AbortController to abort when client disconnects
|
|
29
|
+
* @returns Response object with SSE headers and streaming body
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* export async function POST(request: Request) {
|
|
34
|
+
* const { messages } = await request.json();
|
|
35
|
+
* const abortController = new AbortController();
|
|
36
|
+
* const stream = chat({
|
|
37
|
+
* adapter: openai(),
|
|
38
|
+
* model: "gpt-4o",
|
|
39
|
+
* messages,
|
|
40
|
+
* options: { abortSignal: abortController.signal }
|
|
41
|
+
* });
|
|
42
|
+
* return toStreamResponse(stream, undefined, abortController);
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function toStreamResponse(stream: AsyncIterable<StreamChunk>, init?: ResponseInit & {
|
|
47
|
+
abortController?: AbortController;
|
|
48
|
+
}): Response;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function toServerSentEventsStream(stream, abortController) {
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
return new ReadableStream({
|
|
4
|
+
async start(controller) {
|
|
5
|
+
try {
|
|
6
|
+
for await (const chunk of stream) {
|
|
7
|
+
if (abortController?.signal.aborted) {
|
|
8
|
+
break;
|
|
9
|
+
}
|
|
10
|
+
controller.enqueue(
|
|
11
|
+
encoder.encode(`data: ${JSON.stringify(chunk)}
|
|
12
|
+
|
|
13
|
+
`)
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
17
|
+
controller.close();
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (abortController?.signal.aborted) {
|
|
20
|
+
controller.close();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
controller.enqueue(
|
|
24
|
+
encoder.encode(
|
|
25
|
+
`data: ${JSON.stringify({
|
|
26
|
+
type: "error",
|
|
27
|
+
error: {
|
|
28
|
+
message: error.message || "Unknown error occurred",
|
|
29
|
+
code: error.code
|
|
30
|
+
}
|
|
31
|
+
})}
|
|
32
|
+
|
|
33
|
+
`
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
controller.close();
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
cancel() {
|
|
40
|
+
if (abortController) {
|
|
41
|
+
abortController.abort();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function toStreamResponse(stream, init) {
|
|
47
|
+
const { headers, abortController, ...responseInit } = init ?? {};
|
|
48
|
+
return new Response(toServerSentEventsStream(stream, abortController), {
|
|
49
|
+
...responseInit,
|
|
50
|
+
headers: {
|
|
51
|
+
"Content-Type": "text/event-stream",
|
|
52
|
+
"Cache-Control": "no-cache",
|
|
53
|
+
Connection: "keep-alive",
|
|
54
|
+
...headers || {}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
toServerSentEventsStream,
|
|
60
|
+
toStreamResponse
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=stream-to-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-to-response.js","sources":["../../../src/utilities/stream-to-response.ts"],"sourcesContent":["import type { StreamChunk } from '../types'\n\n/**\n * Convert a StreamChunk async iterable to a ReadableStream in Server-Sent Events format\n *\n * This creates a ReadableStream that emits chunks in SSE format:\n * - Each chunk is prefixed with \"data: \"\n * - Each chunk is followed by \"\\n\\n\"\n * - Stream ends with \"data: [DONE]\\n\\n\"\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param abortController - Optional AbortController to abort when stream is cancelled\n * @returns ReadableStream in Server-Sent Events format\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openai(), model: \"gpt-4o\", messages: [...] });\n * const readableStream = toServerSentEventsStream(stream);\n * // Use with Response, or any API that accepts ReadableStream\n * ```\n */\nexport function toServerSentEventsStream(\n stream: AsyncIterable<StreamChunk>,\n abortController?: AbortController,\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const chunk of stream) {\n // Check if stream was cancelled/aborted\n if (abortController?.signal.aborted) {\n break\n }\n\n // Send each chunk as Server-Sent Events format\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n )\n }\n\n // Send completion marker\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'))\n controller.close()\n } catch (error: any) {\n // Don't send error if aborted\n if (abortController?.signal.aborted) {\n controller.close()\n return\n }\n\n // Send error chunk\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({\n type: 'error',\n error: {\n message: error.message || 'Unknown error occurred',\n code: error.code,\n },\n })}\\n\\n`,\n ),\n )\n controller.close()\n }\n },\n cancel() {\n // When the ReadableStream is cancelled (e.g., client disconnects),\n // abort the underlying stream\n if (abortController) {\n abortController.abort()\n }\n },\n })\n}\n\n/**\n * Create a streaming HTTP response from a StreamChunk async iterable\n * Includes proper headers for Server-Sent Events\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param init - Optional Response initialization options\n * @param abortController - Optional AbortController to abort when client disconnects\n * @returns Response object with SSE headers and streaming body\n *\n * @example\n * ```typescript\n * export async function POST(request: Request) {\n * const { messages } = await request.json();\n * const abortController = new AbortController();\n * const stream = chat({\n * adapter: openai(),\n * model: \"gpt-4o\",\n * messages,\n * options: { abortSignal: abortController.signal }\n * });\n * return toStreamResponse(stream, undefined, abortController);\n * }\n * ```\n */\nexport function toStreamResponse(\n stream: AsyncIterable<StreamChunk>,\n init?: ResponseInit & { abortController?: AbortController },\n): Response {\n const { headers, abortController, ...responseInit } = init ?? {}\n return new Response(toServerSentEventsStream(stream, abortController), {\n ...responseInit,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n ...(headers || {}),\n },\n })\n}\n"],"names":[],"mappings":"AAqBO,SAAS,yBACd,QACA,iBAC4B;AAC5B,QAAM,UAAU,IAAI,YAAA;AAEpB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,MAAM,YAAY;AACtB,UAAI;AACF,yBAAiB,SAAS,QAAQ;AAEhC,cAAI,iBAAiB,OAAO,SAAS;AACnC;AAAA,UACF;AAGA,qBAAW;AAAA,YACT,QAAQ,OAAO,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA;AAAA,CAAM;AAAA,UAAA;AAAA,QAEvD;AAGA,mBAAW,QAAQ,QAAQ,OAAO,kBAAkB,CAAC;AACrD,mBAAW,MAAA;AAAA,MACb,SAAS,OAAY;AAEnB,YAAI,iBAAiB,OAAO,SAAS;AACnC,qBAAW,MAAA;AACX;AAAA,QACF;AAGA,mBAAW;AAAA,UACT,QAAQ;AAAA,YACN,SAAS,KAAK,UAAU;AAAA,cACtB,MAAM;AAAA,cACN,OAAO;AAAA,gBACL,SAAS,MAAM,WAAW;AAAA,gBAC1B,MAAM,MAAM;AAAA,cAAA;AAAA,YACd,CACD,CAAC;AAAA;AAAA;AAAA,UAAA;AAAA,QACJ;AAEF,mBAAW,MAAA;AAAA,MACb;AAAA,IACF;AAAA,IACA,SAAS;AAGP,UAAI,iBAAiB;AACnB,wBAAgB,MAAA;AAAA,MAClB;AAAA,IACF;AAAA,EAAA,CACD;AACH;AA0BO,SAAS,iBACd,QACA,MACU;AACV,QAAM,EAAE,SAAS,iBAAiB,GAAG,aAAA,IAAiB,QAAQ,CAAA;AAC9D,SAAO,IAAI,SAAS,yBAAyB,QAAQ,eAAe,GAAG;AAAA,IACrE,GAAG;AAAA,IACH,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,YAAY;AAAA,MACZ,GAAI,WAAW,CAAA;AAAA,IAAC;AAAA,EAClB,CACD;AACH;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tanstack/ai",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Core TanStack AI library - Open source AI SDK",
|
|
5
|
+
"author": "Tanner Linsley",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/TanStack/ai.git",
|
|
10
|
+
"directory": "packages/typescript/ai"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"module": "./dist/esm/index.js",
|
|
14
|
+
"types": "./dist/esm/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/esm/index.d.ts",
|
|
18
|
+
"import": "./dist/esm/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./event-client": {
|
|
21
|
+
"types": "./dist/esm/event-client.d.ts",
|
|
22
|
+
"import": "./dist/esm/event-client.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"src"
|
|
32
|
+
],
|
|
33
|
+
"keywords": [
|
|
34
|
+
"ai",
|
|
35
|
+
"tanstack",
|
|
36
|
+
"sdk",
|
|
37
|
+
"llm",
|
|
38
|
+
"chat",
|
|
39
|
+
"embeddings"
|
|
40
|
+
],
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@tanstack/devtools-event-client": "^0.3.5",
|
|
43
|
+
"partial-json": "^0.1.7"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@alcyone-labs/zod-to-json-schema": "^4.0.0",
|
|
47
|
+
"zod": "^3.0.0 || ^4.0.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@vitest/coverage-v8": "4.0.14",
|
|
51
|
+
"zod": "^4.1.13"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "vite build",
|
|
55
|
+
"clean": "premove ./build ./dist",
|
|
56
|
+
"lint:fix": "eslint ./src --fix",
|
|
57
|
+
"test:build": "publint --strict",
|
|
58
|
+
"test:coverage": "vitest run --coverage",
|
|
59
|
+
"test:coverage:watch": "vitest --coverage --watch",
|
|
60
|
+
"test:eslint": "eslint ./src",
|
|
61
|
+
"test:lib": "vitest",
|
|
62
|
+
"test:lib:dev": "pnpm test:lib --watch",
|
|
63
|
+
"test:types": "tsc"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AIAdapter,
|
|
3
|
+
AIAdapterConfig,
|
|
4
|
+
ChatOptions,
|
|
5
|
+
DefaultMessageMetadataByModality,
|
|
6
|
+
EmbeddingOptions,
|
|
7
|
+
EmbeddingResult,
|
|
8
|
+
Modality,
|
|
9
|
+
StreamChunk,
|
|
10
|
+
SummarizationOptions,
|
|
11
|
+
SummarizationResult,
|
|
12
|
+
} from './types'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Base adapter class with support for endpoint-specific models and provider options.
|
|
16
|
+
*
|
|
17
|
+
* Generic parameters:
|
|
18
|
+
* - TChatModels: Models that support chat/text completion
|
|
19
|
+
* - TEmbeddingModels: Models that support embeddings
|
|
20
|
+
* - TChatProviderOptions: Provider-specific options for chat endpoint
|
|
21
|
+
* - TEmbeddingProviderOptions: Provider-specific options for embedding endpoint
|
|
22
|
+
* - TModelProviderOptionsByName: Provider-specific options for model by name
|
|
23
|
+
* - TModelInputModalitiesByName: Map from model name to its supported input modalities
|
|
24
|
+
* - TMessageMetadataByModality: Map from modality type to adapter-specific metadata types
|
|
25
|
+
*/
|
|
26
|
+
export abstract class BaseAdapter<
|
|
27
|
+
TChatModels extends ReadonlyArray<string> = ReadonlyArray<string>,
|
|
28
|
+
TEmbeddingModels extends ReadonlyArray<string> = ReadonlyArray<string>,
|
|
29
|
+
TChatProviderOptions extends Record<string, any> = Record<string, any>,
|
|
30
|
+
TEmbeddingProviderOptions extends Record<string, any> = Record<string, any>,
|
|
31
|
+
TModelProviderOptionsByName extends Record<string, any> = Record<string, any>,
|
|
32
|
+
TModelInputModalitiesByName extends Record<
|
|
33
|
+
string,
|
|
34
|
+
ReadonlyArray<Modality>
|
|
35
|
+
> = Record<string, ReadonlyArray<Modality>>,
|
|
36
|
+
TMessageMetadataByModality extends {
|
|
37
|
+
image: unknown
|
|
38
|
+
audio: unknown
|
|
39
|
+
video: unknown
|
|
40
|
+
document: unknown
|
|
41
|
+
} = DefaultMessageMetadataByModality,
|
|
42
|
+
> implements
|
|
43
|
+
AIAdapter<
|
|
44
|
+
TChatModels,
|
|
45
|
+
TEmbeddingModels,
|
|
46
|
+
TChatProviderOptions,
|
|
47
|
+
TEmbeddingProviderOptions,
|
|
48
|
+
TModelProviderOptionsByName,
|
|
49
|
+
TModelInputModalitiesByName,
|
|
50
|
+
TMessageMetadataByModality
|
|
51
|
+
>
|
|
52
|
+
{
|
|
53
|
+
abstract name: string
|
|
54
|
+
abstract models: TChatModels
|
|
55
|
+
embeddingModels?: TEmbeddingModels
|
|
56
|
+
protected config: AIAdapterConfig
|
|
57
|
+
|
|
58
|
+
// These properties are used for type inference only, never assigned at runtime
|
|
59
|
+
_providerOptions?: TChatProviderOptions
|
|
60
|
+
_chatProviderOptions?: TChatProviderOptions
|
|
61
|
+
_embeddingProviderOptions?: TEmbeddingProviderOptions
|
|
62
|
+
// Type-only map; concrete adapters should override this with a precise type
|
|
63
|
+
_modelProviderOptionsByName!: TModelProviderOptionsByName
|
|
64
|
+
// Type-only map for model input modalities; concrete adapters should override this
|
|
65
|
+
_modelInputModalitiesByName?: TModelInputModalitiesByName
|
|
66
|
+
// Type-only map for message metadata types; concrete adapters should override this
|
|
67
|
+
_messageMetadataByModality?: TMessageMetadataByModality
|
|
68
|
+
|
|
69
|
+
constructor(config: AIAdapterConfig = {}) {
|
|
70
|
+
this.config = config
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
abstract chatStream(options: ChatOptions): AsyncIterable<StreamChunk>
|
|
74
|
+
|
|
75
|
+
abstract summarize(
|
|
76
|
+
options: SummarizationOptions,
|
|
77
|
+
): Promise<SummarizationResult>
|
|
78
|
+
abstract createEmbeddings(options: EmbeddingOptions): Promise<EmbeddingResult>
|
|
79
|
+
|
|
80
|
+
protected generateId(): string {
|
|
81
|
+
return `${this.name}-${Date.now()}-${Math.random()
|
|
82
|
+
.toString(36)
|
|
83
|
+
.substring(7)}`
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common options shared across different AI provider implementations.
|
|
3
|
+
* These options represent the standard parameters that work across OpenAI, Anthropic, and Gemini.
|
|
4
|
+
*/
|
|
5
|
+
export interface CommonOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Controls the randomness of the output.
|
|
8
|
+
* Higher values (e.g., 0.8) make output more random, lower values (e.g., 0.2) make it more focused and deterministic.
|
|
9
|
+
* Range: [0.0, 2.0]
|
|
10
|
+
*
|
|
11
|
+
* Note: Generally recommended to use either temperature or topP, but not both.
|
|
12
|
+
*
|
|
13
|
+
* Provider usage:
|
|
14
|
+
* - OpenAI: `temperature` (number) - in text.top_p field
|
|
15
|
+
* - Anthropic: `temperature` (number) - ranges from 0.0 to 1.0, default 1.0
|
|
16
|
+
* - Gemini: `generationConfig.temperature` (number) - ranges from 0.0 to 2.0
|
|
17
|
+
*/
|
|
18
|
+
temperature?: number
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Nucleus sampling parameter. An alternative to temperature sampling.
|
|
22
|
+
* The model considers the results of tokens with topP probability mass.
|
|
23
|
+
* For example, 0.1 means only tokens comprising the top 10% probability mass are considered.
|
|
24
|
+
*
|
|
25
|
+
* Note: Generally recommended to use either temperature or topP, but not both.
|
|
26
|
+
*
|
|
27
|
+
* Provider usage:
|
|
28
|
+
* - OpenAI: `text.top_p` (number)
|
|
29
|
+
* - Anthropic: `top_p` (number | null)
|
|
30
|
+
* - Gemini: `generationConfig.topP` (number)
|
|
31
|
+
*/
|
|
32
|
+
topP?: number
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The maximum number of tokens to generate in the response.
|
|
36
|
+
*
|
|
37
|
+
* Provider usage:
|
|
38
|
+
* - OpenAI: `max_output_tokens` (number) - includes visible output and reasoning tokens
|
|
39
|
+
* - Anthropic: `max_tokens` (number, required) - range x >= 1
|
|
40
|
+
* - Gemini: `generationConfig.maxOutputTokens` (number)
|
|
41
|
+
*/
|
|
42
|
+
maxTokens?: number
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Additional metadata to attach to the request.
|
|
46
|
+
* Can be used for tracking, debugging, or passing custom information.
|
|
47
|
+
* Structure and constraints vary by provider.
|
|
48
|
+
*
|
|
49
|
+
* Provider usage:
|
|
50
|
+
* - OpenAI: `metadata` (Record<string, string>) - max 16 key-value pairs, keys max 64 chars, values max 512 chars
|
|
51
|
+
* - Anthropic: `metadata` (Record<string, any>) - includes optional user_id (max 256 chars)
|
|
52
|
+
* - Gemini: Not directly available in TextProviderOptions
|
|
53
|
+
*/
|
|
54
|
+
metadata?: Record<string, any>
|
|
55
|
+
}
|