@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/src/types.ts ADDED
@@ -0,0 +1,276 @@
1
+ import type {
2
+ AnyClientTool,
3
+ ChunkStrategy,
4
+ InferToolInput,
5
+ InferToolOutput,
6
+ ModelMessage,
7
+ StreamChunk,
8
+ } from '@tanstack/ai'
9
+ import type { ConnectionAdapter } from './connection-adapters'
10
+
11
+ /**
12
+ * Tool call states - track the lifecycle of a tool call
13
+ */
14
+ export type ToolCallState =
15
+ | 'awaiting-input' // Received start but no arguments yet
16
+ | 'input-streaming' // Partial arguments received
17
+ | 'input-complete' // All arguments received
18
+ | 'approval-requested' // Waiting for user approval
19
+ | 'approval-responded' // User has approved/denied
20
+
21
+ /**
22
+ * Tool result states - track the lifecycle of a tool result
23
+ */
24
+ export type ToolResultState =
25
+ | 'streaming' // Placeholder for future streamed output
26
+ | 'complete' // Result is complete
27
+ | 'error' // Error occurred
28
+
29
+ /**
30
+ * Message parts - building blocks of UIMessage
31
+ */
32
+ export interface TextPart {
33
+ type: 'text'
34
+ content: string
35
+ }
36
+
37
+ /**
38
+ * Helper type that creates a tool-call part for a specific tool.
39
+ * This is a conditional type to enable proper distribution over union types,
40
+ * creating a discriminated union where `name` is the discriminant.
41
+ */
42
+ type ToolCallPartForTool<T> = T extends AnyClientTool
43
+ ? {
44
+ type: 'tool-call'
45
+ id: string
46
+ name: T['name']
47
+ arguments: string // JSON string (may be incomplete)
48
+ /** Parsed tool input (typed from inputSchema) */
49
+ input?: InferToolInput<T>
50
+ state: ToolCallState
51
+ /** Approval metadata if tool requires user approval */
52
+ approval?: {
53
+ id: string // Unique approval ID
54
+ needsApproval: boolean // Always true if present
55
+ approved?: boolean // User's decision (undefined until responded)
56
+ }
57
+ /** Tool execution output (for client tools or after approval) */
58
+ output?: InferToolOutput<T>
59
+ }
60
+ : never
61
+
62
+ /**
63
+ * Fallback tool-call part type when tools are not typed
64
+ */
65
+ type UntypedToolCallPart = {
66
+ type: 'tool-call'
67
+ id: string
68
+ name: string
69
+ arguments: string
70
+ input?: any
71
+ state: ToolCallState
72
+ approval?: {
73
+ id: string
74
+ needsApproval: boolean
75
+ approved?: boolean
76
+ }
77
+ output?: any
78
+ }
79
+
80
+ /**
81
+ * Tool call part that creates a proper discriminated union.
82
+ * When TTools is typed, checking `part.name === 'toolName'` will narrow
83
+ * `part.output` to the correct type for that tool.
84
+ *
85
+ * The discriminant is `name`, so code like:
86
+ * ```ts
87
+ * if (part.name === 'recommendGuitar') {
88
+ * // part.output is now typed to the recommendGuitar tool's output
89
+ * }
90
+ * ```
91
+ */
92
+ export type ToolCallPart<TTools extends ReadonlyArray<AnyClientTool> = any> =
93
+ // Check if we have a concrete tools array (not 'any' or 'never')
94
+ [TTools] extends [never]
95
+ ? UntypedToolCallPart
96
+ : unknown extends TTools
97
+ ? UntypedToolCallPart
98
+ : TTools extends ReadonlyArray<infer Tool>
99
+ ? Tool extends AnyClientTool
100
+ ? ToolCallPartForTool<Tool>
101
+ : UntypedToolCallPart
102
+ : UntypedToolCallPart
103
+
104
+ export interface ToolResultPart {
105
+ type: 'tool-result'
106
+ toolCallId: string
107
+ content: string
108
+ state: ToolResultState
109
+ error?: string // Error message if state is "error"
110
+ }
111
+
112
+ export interface ThinkingPart {
113
+ type: 'thinking'
114
+ content: string
115
+ }
116
+
117
+ export type MessagePart<TTools extends ReadonlyArray<AnyClientTool> = any> =
118
+ | TextPart
119
+ | ToolCallPart<TTools>
120
+ | ToolResultPart
121
+ | ThinkingPart
122
+
123
+ /**
124
+ * UIMessage - Domain-specific message format optimized for building chat UIs
125
+ * Contains parts that can be text, tool calls, or tool results
126
+ */
127
+ export interface UIMessage<TTools extends ReadonlyArray<AnyClientTool> = any> {
128
+ id: string
129
+ role: 'user' | 'assistant'
130
+ parts: Array<MessagePart<TTools>>
131
+ createdAt?: Date
132
+ }
133
+
134
+ export interface ChatClientOptions<
135
+ TTools extends ReadonlyArray<AnyClientTool> = any,
136
+ > {
137
+ /**
138
+ * Connection adapter for streaming
139
+ * Use fetchServerSentEvents(), fetchHttpStream(), or stream() to create adapters
140
+ */
141
+ connection: ConnectionAdapter
142
+
143
+ /**
144
+ * Initial messages to populate the chat
145
+ */
146
+ initialMessages?: Array<UIMessage<TTools>>
147
+
148
+ /**
149
+ * Unique identifier for this chat instance
150
+ * Used for managing multiple chats
151
+ */
152
+ id?: string
153
+
154
+ /**
155
+ * Additional body parameters to send
156
+ */
157
+ body?: Record<string, any>
158
+
159
+ /**
160
+ * Callback when a response is received
161
+ */
162
+ onResponse?: (response?: Response) => void | Promise<void>
163
+
164
+ /**
165
+ * Callback when a stream chunk is received
166
+ */
167
+ onChunk?: (chunk: StreamChunk) => void
168
+
169
+ /**
170
+ * Callback when the response is finished
171
+ */
172
+ onFinish?: (message: UIMessage<TTools>) => void
173
+
174
+ /**
175
+ * Callback when an error occurs
176
+ */
177
+ onError?: (error: Error) => void
178
+
179
+ /**
180
+ * Callback when messages change
181
+ */
182
+ onMessagesChange?: (messages: Array<UIMessage<TTools>>) => void
183
+
184
+ /**
185
+ * Callback when loading state changes
186
+ */
187
+ onLoadingChange?: (isLoading: boolean) => void
188
+
189
+ /**
190
+ * Callback when error state changes
191
+ */
192
+ onErrorChange?: (error: Error | undefined) => void
193
+
194
+ /**
195
+ * Client-side tools with execution logic
196
+ * When provided, tools with execute functions will be called automatically
197
+ */
198
+ tools?: TTools
199
+
200
+ /**
201
+ * Stream processing options (optional)
202
+ * Configure chunking strategy
203
+ */
204
+ streamProcessor?: {
205
+ /**
206
+ * Strategy for when to emit text updates
207
+ * Defaults to ImmediateStrategy (every chunk)
208
+ */
209
+ chunkStrategy?: ChunkStrategy
210
+ }
211
+ }
212
+
213
+ export interface ChatRequestBody {
214
+ messages: Array<ModelMessage>
215
+ data?: Record<string, any>
216
+ }
217
+
218
+ /**
219
+ * Create a typed array of client tools with proper type inference.
220
+ * This eliminates the need for `as const` when defining tool arrays.
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * const tools = clientTools(
225
+ * myTool1.client(() => result1),
226
+ * myTool2.client(() => result2),
227
+ * )
228
+ *
229
+ * // tools is now properly typed as a tuple with literal tool names
230
+ * // This enables type narrowing when checking part.name === 'toolName'
231
+ * ```
232
+ */
233
+ export function clientTools<const T extends Array<AnyClientTool>>(
234
+ ...tools: T
235
+ ): T {
236
+ return tools
237
+ }
238
+
239
+ /**
240
+ * Helper to create typed chat client options
241
+ * Use this to get proper type inference for messages
242
+ *
243
+ * @example
244
+ * ```ts
245
+ * const tools = clientTools(myTool1, myTool2)
246
+ *
247
+ * const chatOptions = createChatClientOptions({
248
+ * connection: fetchServerSentEvents('/api/chat'),
249
+ * tools,
250
+ * })
251
+ *
252
+ * type MyMessages = InferChatMessages<typeof chatOptions>
253
+ * ```
254
+ */
255
+ export function createChatClientOptions<
256
+ const TTools extends ReadonlyArray<AnyClientTool>,
257
+ >(options: ChatClientOptions<TTools>): ChatClientOptions<TTools> {
258
+ return options
259
+ }
260
+
261
+ /**
262
+ * Extract the message type from chat options
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * const chatOptions = createChatClientOptions({
267
+ * connection: fetchServerSentEvents('/api/chat'),
268
+ * tools: [myTool1, myTool2],
269
+ * })
270
+ *
271
+ * type MyMessages = InferChatMessages<typeof chatOptions>
272
+ * // MyMessages is now Array<UIMessage<[typeof myTool1, typeof myTool2]>>
273
+ * ```
274
+ */
275
+ export type InferChatMessages<T> =
276
+ T extends ChatClientOptions<infer TTools> ? Array<UIMessage<TTools>> : never