@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,85 @@
1
+ import type { AgentLoopStrategy } from '../types'
2
+
3
+ /**
4
+ * Creates a strategy that continues for a maximum number of iterations
5
+ *
6
+ * @param max - Maximum number of iterations to allow
7
+ * @returns AgentLoopStrategy that stops after max iterations
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const stream = chat({
12
+ * adapter: openai(),
13
+ * model: "gpt-4o",
14
+ * messages: [...],
15
+ * tools: [weatherTool],
16
+ * agentLoopStrategy: maxIterations(3), // Max 3 iterations
17
+ * });
18
+ * ```
19
+ */
20
+ export function maxIterations(max: number): AgentLoopStrategy {
21
+ return ({ iterationCount }) => iterationCount < max
22
+ }
23
+
24
+ /**
25
+ * Creates a strategy that continues until a specific finish reason is encountered
26
+ *
27
+ * @param stopReasons - Finish reasons that should stop the loop
28
+ * @returns AgentLoopStrategy that stops on specific finish reasons
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const stream = chat({
33
+ * adapter: openai(),
34
+ * model: "gpt-4o",
35
+ * messages: [...],
36
+ * tools: [weatherTool],
37
+ * agentLoopStrategy: untilFinishReason(["stop", "length"]),
38
+ * });
39
+ * ```
40
+ */
41
+ export function untilFinishReason(
42
+ stopReasons: Array<string>,
43
+ ): AgentLoopStrategy {
44
+ return ({ finishReason, iterationCount }) => {
45
+ // Always allow at least one iteration
46
+ if (iterationCount === 0) return true
47
+
48
+ // Stop if we hit a stop reason
49
+ if (finishReason && stopReasons.includes(finishReason)) {
50
+ return false
51
+ }
52
+
53
+ // Otherwise continue
54
+ return true
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Creates a strategy that combines multiple strategies with AND logic
60
+ * All strategies must return true to continue
61
+ *
62
+ * @param strategies - Array of strategies to combine
63
+ * @returns AgentLoopStrategy that continues only if all strategies return true
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * const stream = chat({
68
+ * adapter: openai(),
69
+ * model: "gpt-4o",
70
+ * messages: [...],
71
+ * tools: [weatherTool],
72
+ * agentLoopStrategy: combineStrategies([
73
+ * maxIterations(10),
74
+ * ({ messages }) => messages.length < 100,
75
+ * ]),
76
+ * });
77
+ * ```
78
+ */
79
+ export function combineStrategies(
80
+ strategies: Array<AgentLoopStrategy>,
81
+ ): AgentLoopStrategy {
82
+ return (state) => {
83
+ return strategies.every((strategy) => strategy(state))
84
+ }
85
+ }
@@ -0,0 +1,35 @@
1
+ import type { AIAdapter, ChatStreamOptionsUnion } from '../types'
2
+
3
+ export function chatOptions<
4
+ TAdapter extends AIAdapter<any, any, any, any, any>,
5
+ const TModel extends TAdapter extends AIAdapter<
6
+ infer Models,
7
+ any,
8
+ any,
9
+ any,
10
+ any
11
+ >
12
+ ? Models[number]
13
+ : string,
14
+ >(
15
+ options: Omit<
16
+ ChatStreamOptionsUnion<TAdapter>,
17
+ 'providerOptions' | 'model' | 'messages' | 'abortController'
18
+ > & {
19
+ adapter: TAdapter
20
+ model: TModel
21
+ providerOptions?: TAdapter extends AIAdapter<
22
+ any,
23
+ any,
24
+ any,
25
+ any,
26
+ infer ModelProviderOptions
27
+ >
28
+ ? TModel extends keyof ModelProviderOptions
29
+ ? ModelProviderOptions[TModel]
30
+ : never
31
+ : never
32
+ },
33
+ ): typeof options {
34
+ return options
35
+ }
@@ -0,0 +1,63 @@
1
+ import type {
2
+ AIAdapter,
3
+ ConstrainedModelMessage,
4
+ Modality,
5
+ ModelMessage,
6
+ } from '../types'
7
+
8
+ /**
9
+ * Type-safe helper to create a messages array constrained by a model's supported modalities.
10
+ *
11
+ * This function provides compile-time checking that your messages only contain
12
+ * content types supported by the specified model. It's particularly useful when
13
+ * combining typed messages with untyped data (like from request.json()).
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { messages, chat } from '@tanstack/ai'
18
+ * import { openai } from '@tanstack/ai-openai'
19
+ *
20
+ * const adapter = openai()
21
+ *
22
+ * // This will error at compile time because gpt-4o only supports text+image
23
+ * const msgs = messages({ adapter, model: 'gpt-4o' }, [
24
+ * {
25
+ * role: 'user',
26
+ * content: [
27
+ * { type: 'video', source: { type: 'url', value: '...' } } // Error!
28
+ * ]
29
+ * }
30
+ * ])
31
+ * ```
32
+ */
33
+ export function messages<
34
+ TAdapter extends AIAdapter<any, any, any, any, any, any>,
35
+ const TModel extends TAdapter extends AIAdapter<
36
+ infer Models,
37
+ any,
38
+ any,
39
+ any,
40
+ any,
41
+ any
42
+ >
43
+ ? Models[number]
44
+ : string,
45
+ >(
46
+ _options: { adapter: TAdapter; model: TModel },
47
+ msgs: TAdapter extends AIAdapter<
48
+ any,
49
+ any,
50
+ any,
51
+ any,
52
+ any,
53
+ infer ModelInputModalities
54
+ >
55
+ ? TModel extends keyof ModelInputModalities
56
+ ? ModelInputModalities[TModel] extends ReadonlyArray<Modality>
57
+ ? Array<ConstrainedModelMessage<ModelInputModalities[TModel]>>
58
+ : Array<ModelMessage>
59
+ : Array<ModelMessage>
60
+ : Array<ModelMessage>,
61
+ ): typeof msgs {
62
+ return msgs
63
+ }
@@ -0,0 +1,116 @@
1
+ import type { StreamChunk } from '../types'
2
+
3
+ /**
4
+ * Convert a StreamChunk async iterable to a ReadableStream in Server-Sent Events format
5
+ *
6
+ * This creates a ReadableStream that emits chunks in SSE format:
7
+ * - Each chunk is prefixed with "data: "
8
+ * - Each chunk is followed by "\n\n"
9
+ * - Stream ends with "data: [DONE]\n\n"
10
+ *
11
+ * @param stream - AsyncIterable of StreamChunks from chat()
12
+ * @param abortController - Optional AbortController to abort when stream is cancelled
13
+ * @returns ReadableStream in Server-Sent Events format
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const stream = chat({ adapter: openai(), model: "gpt-4o", messages: [...] });
18
+ * const readableStream = toServerSentEventsStream(stream);
19
+ * // Use with Response, or any API that accepts ReadableStream
20
+ * ```
21
+ */
22
+ export function toServerSentEventsStream(
23
+ stream: AsyncIterable<StreamChunk>,
24
+ abortController?: AbortController,
25
+ ): ReadableStream<Uint8Array> {
26
+ const encoder = new TextEncoder()
27
+
28
+ return new ReadableStream({
29
+ async start(controller) {
30
+ try {
31
+ for await (const chunk of stream) {
32
+ // Check if stream was cancelled/aborted
33
+ if (abortController?.signal.aborted) {
34
+ break
35
+ }
36
+
37
+ // Send each chunk as Server-Sent Events format
38
+ controller.enqueue(
39
+ encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`),
40
+ )
41
+ }
42
+
43
+ // Send completion marker
44
+ controller.enqueue(encoder.encode('data: [DONE]\n\n'))
45
+ controller.close()
46
+ } catch (error: any) {
47
+ // Don't send error if aborted
48
+ if (abortController?.signal.aborted) {
49
+ controller.close()
50
+ return
51
+ }
52
+
53
+ // Send error chunk
54
+ controller.enqueue(
55
+ encoder.encode(
56
+ `data: ${JSON.stringify({
57
+ type: 'error',
58
+ error: {
59
+ message: error.message || 'Unknown error occurred',
60
+ code: error.code,
61
+ },
62
+ })}\n\n`,
63
+ ),
64
+ )
65
+ controller.close()
66
+ }
67
+ },
68
+ cancel() {
69
+ // When the ReadableStream is cancelled (e.g., client disconnects),
70
+ // abort the underlying stream
71
+ if (abortController) {
72
+ abortController.abort()
73
+ }
74
+ },
75
+ })
76
+ }
77
+
78
+ /**
79
+ * Create a streaming HTTP response from a StreamChunk async iterable
80
+ * Includes proper headers for Server-Sent Events
81
+ *
82
+ * @param stream - AsyncIterable of StreamChunks from chat()
83
+ * @param init - Optional Response initialization options
84
+ * @param abortController - Optional AbortController to abort when client disconnects
85
+ * @returns Response object with SSE headers and streaming body
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * export async function POST(request: Request) {
90
+ * const { messages } = await request.json();
91
+ * const abortController = new AbortController();
92
+ * const stream = chat({
93
+ * adapter: openai(),
94
+ * model: "gpt-4o",
95
+ * messages,
96
+ * options: { abortSignal: abortController.signal }
97
+ * });
98
+ * return toStreamResponse(stream, undefined, abortController);
99
+ * }
100
+ * ```
101
+ */
102
+ export function toStreamResponse(
103
+ stream: AsyncIterable<StreamChunk>,
104
+ init?: ResponseInit & { abortController?: AbortController },
105
+ ): Response {
106
+ const { headers, abortController, ...responseInit } = init ?? {}
107
+ return new Response(toServerSentEventsStream(stream, abortController), {
108
+ ...responseInit,
109
+ headers: {
110
+ 'Content-Type': 'text/event-stream',
111
+ 'Cache-Control': 'no-cache',
112
+ Connection: 'keep-alive',
113
+ ...(headers || {}),
114
+ },
115
+ })
116
+ }