@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,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
|
+
}
|