@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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/dist/esm/base-adapter.d.ts +35 -0
  4. package/dist/esm/base-adapter.js +12 -0
  5. package/dist/esm/base-adapter.js.map +1 -0
  6. package/dist/esm/core/chat-common-options.d.ts +52 -0
  7. package/dist/esm/core/chat.d.ts +30 -0
  8. package/dist/esm/core/chat.js +533 -0
  9. package/dist/esm/core/chat.js.map +1 -0
  10. package/dist/esm/core/embedding.d.ts +8 -0
  11. package/dist/esm/core/embedding.js +33 -0
  12. package/dist/esm/core/embedding.js.map +1 -0
  13. package/dist/esm/core/summarize.d.ts +9 -0
  14. package/dist/esm/core/summarize.js +36 -0
  15. package/dist/esm/core/summarize.js.map +1 -0
  16. package/dist/esm/event-client.d.ts +311 -0
  17. package/dist/esm/event-client.js +62 -0
  18. package/dist/esm/event-client.js.map +1 -0
  19. package/dist/esm/index.d.ts +16 -0
  20. package/dist/esm/index.js +50 -0
  21. package/dist/esm/index.js.map +1 -0
  22. package/dist/esm/message-converters.d.ts +52 -0
  23. package/dist/esm/message-converters.js +162 -0
  24. package/dist/esm/message-converters.js.map +1 -0
  25. package/dist/esm/stream/index.d.ts +11 -0
  26. package/dist/esm/stream/json-parser.d.ts +38 -0
  27. package/dist/esm/stream/json-parser.js +28 -0
  28. package/dist/esm/stream/json-parser.js.map +1 -0
  29. package/dist/esm/stream/message-updaters.d.ts +44 -0
  30. package/dist/esm/stream/message-updaters.js +141 -0
  31. package/dist/esm/stream/message-updaters.js.map +1 -0
  32. package/dist/esm/stream/processor.d.ts +242 -0
  33. package/dist/esm/stream/processor.js +693 -0
  34. package/dist/esm/stream/processor.js.map +1 -0
  35. package/dist/esm/stream/strategies.d.ts +43 -0
  36. package/dist/esm/stream/strategies.js +54 -0
  37. package/dist/esm/stream/strategies.js.map +1 -0
  38. package/dist/esm/stream/types.d.ts +71 -0
  39. package/dist/esm/tools/tool-calls.d.ts +112 -0
  40. package/dist/esm/tools/tool-calls.js +302 -0
  41. package/dist/esm/tools/tool-calls.js.map +1 -0
  42. package/dist/esm/tools/tool-definition.d.ts +125 -0
  43. package/dist/esm/tools/tool-definition.js +25 -0
  44. package/dist/esm/tools/tool-definition.js.map +1 -0
  45. package/dist/esm/tools/zod-converter.d.ts +30 -0
  46. package/dist/esm/tools/zod-converter.js +36 -0
  47. package/dist/esm/tools/zod-converter.js.map +1 -0
  48. package/dist/esm/types.d.ts +619 -0
  49. package/dist/esm/utilities/agent-loop-strategies.d.ts +59 -0
  50. package/dist/esm/utilities/agent-loop-strategies.js +23 -0
  51. package/dist/esm/utilities/agent-loop-strategies.js.map +1 -0
  52. package/dist/esm/utilities/chat-options.d.ts +6 -0
  53. package/dist/esm/utilities/chat-options.js +7 -0
  54. package/dist/esm/utilities/chat-options.js.map +1 -0
  55. package/dist/esm/utilities/messages.d.ts +30 -0
  56. package/dist/esm/utilities/messages.js +7 -0
  57. package/dist/esm/utilities/messages.js.map +1 -0
  58. package/dist/esm/utilities/stream-to-response.d.ts +48 -0
  59. package/dist/esm/utilities/stream-to-response.js +62 -0
  60. package/dist/esm/utilities/stream-to-response.js.map +1 -0
  61. package/package.json +65 -0
  62. package/src/base-adapter.ts +85 -0
  63. package/src/core/chat-common-options.ts +55 -0
  64. package/src/core/chat.ts +771 -0
  65. package/src/core/embedding.ts +54 -0
  66. package/src/core/summarize.ts +56 -0
  67. package/src/event-client.ts +389 -0
  68. package/src/index.ts +68 -0
  69. package/src/message-converters.ts +285 -0
  70. package/src/stream/index.ts +41 -0
  71. package/src/stream/json-parser.ts +58 -0
  72. package/src/stream/message-updaters.ts +275 -0
  73. package/src/stream/processor.ts +1092 -0
  74. package/src/stream/strategies.ts +78 -0
  75. package/src/stream/types.ts +94 -0
  76. package/src/tools/tool-calls.ts +471 -0
  77. package/src/tools/tool-definition.ts +206 -0
  78. package/src/tools/zod-converter.ts +85 -0
  79. package/src/types.ts +872 -0
  80. package/src/utilities/agent-loop-strategies.ts +85 -0
  81. package/src/utilities/chat-options.ts +35 -0
  82. package/src/utilities/messages.ts +63 -0
  83. 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,7 @@
1
+ function chatOptions(options) {
2
+ return options;
3
+ }
4
+ export {
5
+ chatOptions
6
+ };
7
+ //# sourceMappingURL=chat-options.js.map
@@ -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,7 @@
1
+ function messages(_options, msgs) {
2
+ return msgs;
3
+ }
4
+ export {
5
+ messages
6
+ };
7
+ //# sourceMappingURL=messages.js.map
@@ -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
+ }