@tanstack/ai-client 0.14.1 → 0.15.0
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/dist/esm/chat-client.d.ts +38 -30
- package/dist/esm/chat-client.js +56 -17
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/devtools.js +1 -1
- package/dist/esm/devtools.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/types.d.ts +47 -10
- package/dist/esm/types.js.map +1 -1
- package/package.json +3 -3
- package/src/chat-client.ts +124 -57
- package/src/devtools.ts +3 -1
- package/src/index.ts +2 -0
- package/src/types.ts +103 -12
package/src/chat-client.ts
CHANGED
|
@@ -2,7 +2,9 @@ import {
|
|
|
2
2
|
StreamProcessor,
|
|
3
3
|
convertSchemaToJsonSchema,
|
|
4
4
|
generateMessageId,
|
|
5
|
+
isStandardSchema,
|
|
5
6
|
normalizeToUIMessage,
|
|
7
|
+
parseWithStandardSchema,
|
|
6
8
|
} from '@tanstack/ai/client'
|
|
7
9
|
import { createNoOpChatDevtoolsBridge } from './devtools-noop'
|
|
8
10
|
import {
|
|
@@ -39,6 +41,37 @@ import type {
|
|
|
39
41
|
UIMessage,
|
|
40
42
|
} from './types'
|
|
41
43
|
|
|
44
|
+
type ChatClientUpdateOptionsWithoutContext<
|
|
45
|
+
TTools extends ReadonlyArray<AnyClientTool>,
|
|
46
|
+
> = {
|
|
47
|
+
connection?: ConnectionAdapter
|
|
48
|
+
fetcher?: ChatFetcher
|
|
49
|
+
/** @deprecated Use `forwardedProps` instead. */
|
|
50
|
+
body?: Record<string, any>
|
|
51
|
+
forwardedProps?: Record<string, any>
|
|
52
|
+
tools?: TTools
|
|
53
|
+
onResponse?: (response?: Response) => void | Promise<void>
|
|
54
|
+
onChunk?: (chunk: StreamChunk) => void
|
|
55
|
+
onFinish?: (message: UIMessage) => void
|
|
56
|
+
onError?: (error: Error) => void
|
|
57
|
+
onSubscriptionChange?: (isSubscribed: boolean) => void
|
|
58
|
+
onConnectionStatusChange?: (status: ConnectionStatus) => void
|
|
59
|
+
onSessionGeneratingChange?: (isGenerating: boolean) => void
|
|
60
|
+
onCustomEvent?: (
|
|
61
|
+
eventType: string,
|
|
62
|
+
data: unknown,
|
|
63
|
+
context: { toolCallId?: string },
|
|
64
|
+
) => void
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type ClientToolResult = {
|
|
68
|
+
toolCallId: string
|
|
69
|
+
tool: string
|
|
70
|
+
output: any
|
|
71
|
+
state?: 'output-available' | 'output-error'
|
|
72
|
+
errorText?: string
|
|
73
|
+
}
|
|
74
|
+
|
|
42
75
|
function resolveTransport(transport: {
|
|
43
76
|
connection?: ConnectionAdapter
|
|
44
77
|
fetcher?: ChatFetcher
|
|
@@ -54,7 +87,10 @@ function resolveTransport(transport: {
|
|
|
54
87
|
throw new Error('ChatClient: either `connection` or `fetcher` is required.')
|
|
55
88
|
}
|
|
56
89
|
|
|
57
|
-
export class ChatClient
|
|
90
|
+
export class ChatClient<
|
|
91
|
+
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
92
|
+
TContext = unknown,
|
|
93
|
+
> {
|
|
58
94
|
private readonly processor: StreamProcessor
|
|
59
95
|
private connection: SubscribeConnectionAdapter
|
|
60
96
|
private readonly uniqueId: string
|
|
@@ -65,6 +101,7 @@ export class ChatClient {
|
|
|
65
101
|
// merged on every send, with `forwardedProps` winning on key collision.
|
|
66
102
|
private bodyOption: Record<string, any> = {}
|
|
67
103
|
private forwardedPropsOption: Record<string, any> = {}
|
|
104
|
+
private context: TContext | undefined = undefined
|
|
68
105
|
private pendingMessageBody: Record<string, any> | undefined = undefined
|
|
69
106
|
private isLoading = false
|
|
70
107
|
private isSubscribed = false
|
|
@@ -86,6 +123,8 @@ export class ChatClient {
|
|
|
86
123
|
private readonly postStreamActions: Array<() => Promise<void>> = []
|
|
87
124
|
// Track pending client tool executions to await them before stream finalization
|
|
88
125
|
private readonly pendingToolExecutions: Map<string, Promise<void>> = new Map()
|
|
126
|
+
private activeClientTools: Map<string, AnyClientTool> | null = null
|
|
127
|
+
private activeContext: TContext | undefined = undefined
|
|
89
128
|
// Flag to deduplicate continuation checks during action draining
|
|
90
129
|
private continuationPending = false
|
|
91
130
|
private subscriptionAbortController: AbortController | null = null
|
|
@@ -121,7 +160,7 @@ export class ChatClient {
|
|
|
121
160
|
}
|
|
122
161
|
}
|
|
123
162
|
|
|
124
|
-
constructor(options: ChatClientOptions) {
|
|
163
|
+
constructor(options: ChatClientOptions<TTools, TContext>) {
|
|
125
164
|
this.uniqueId = options.id || this.generateUniqueId('chat')
|
|
126
165
|
this.threadId = options.threadId || this.generateUniqueId('thread')
|
|
127
166
|
// Both `body` (deprecated) and `forwardedProps` populate the AG-UI
|
|
@@ -131,6 +170,7 @@ export class ChatClient {
|
|
|
131
170
|
// winning on key collision.
|
|
132
171
|
this.bodyOption = options.body || {}
|
|
133
172
|
this.forwardedPropsOption = options.forwardedProps || {}
|
|
173
|
+
this.context = options.context
|
|
134
174
|
this.connection = normalizeConnectionAdapter(resolveTransport(options))
|
|
135
175
|
|
|
136
176
|
// Build client tools map
|
|
@@ -288,7 +328,9 @@ export class ChatClient {
|
|
|
288
328
|
input: any
|
|
289
329
|
}) => {
|
|
290
330
|
// Handle client-side tool execution automatically
|
|
291
|
-
const
|
|
331
|
+
const clientTools =
|
|
332
|
+
this.activeClientTools ?? this.clientToolsRef.current
|
|
333
|
+
const clientTool = clientTools.get(args.toolName)
|
|
292
334
|
const executeFunc = clientTool?.execute
|
|
293
335
|
if (executeFunc) {
|
|
294
336
|
// Capture the run context at execution-start so a tool whose
|
|
@@ -300,18 +342,27 @@ export class ChatClient {
|
|
|
300
342
|
// Create and track the execution promise
|
|
301
343
|
const executionPromise = (async () => {
|
|
302
344
|
try {
|
|
303
|
-
const
|
|
304
|
-
|
|
345
|
+
const context =
|
|
346
|
+
this.activeClientTools === null
|
|
347
|
+
? this.context
|
|
348
|
+
: this.activeContext
|
|
349
|
+
const output = await executeFunc(args.input, {
|
|
350
|
+
toolCallId: args.toolCallId,
|
|
351
|
+
context: context as TContext,
|
|
352
|
+
emitCustomEvent: () => {},
|
|
353
|
+
})
|
|
354
|
+
await this.addToolResultForClientTool(
|
|
305
355
|
{
|
|
306
356
|
toolCallId: args.toolCallId,
|
|
307
357
|
tool: args.toolName,
|
|
308
358
|
output,
|
|
309
359
|
state: 'output-available',
|
|
310
360
|
},
|
|
361
|
+
clientTool,
|
|
311
362
|
runEventContext,
|
|
312
363
|
)
|
|
313
364
|
} catch (error: any) {
|
|
314
|
-
await this.
|
|
365
|
+
await this.addToolResultForClientTool(
|
|
315
366
|
{
|
|
316
367
|
toolCallId: args.toolCallId,
|
|
317
368
|
tool: args.toolName,
|
|
@@ -319,6 +370,7 @@ export class ChatClient {
|
|
|
319
370
|
state: 'output-error',
|
|
320
371
|
errorText: error.message,
|
|
321
372
|
},
|
|
373
|
+
clientTool,
|
|
322
374
|
runEventContext,
|
|
323
375
|
)
|
|
324
376
|
} finally {
|
|
@@ -761,6 +813,8 @@ export class ChatClient {
|
|
|
761
813
|
try {
|
|
762
814
|
// Get UIMessages with parts (preserves approval state and client tool results)
|
|
763
815
|
const messages = this.processor.getMessages()
|
|
816
|
+
const clientTools = new Map(this.clientToolsRef.current)
|
|
817
|
+
const runtimeContext = this.context
|
|
764
818
|
|
|
765
819
|
// Call onResponse callback
|
|
766
820
|
await this.callbacksRef.current.onResponse()
|
|
@@ -796,6 +850,8 @@ export class ChatClient {
|
|
|
796
850
|
this.currentStreamId = this.generateUniqueId('stream')
|
|
797
851
|
this.devtoolsBridge.setCurrentStreamId(this.currentStreamId)
|
|
798
852
|
this.currentMessageId = null
|
|
853
|
+
this.activeClientTools = clientTools
|
|
854
|
+
this.activeContext = runtimeContext
|
|
799
855
|
|
|
800
856
|
// Reset processor stream state for new response — prevents stale
|
|
801
857
|
// messageStates entries (from a previous stream) from blocking
|
|
@@ -819,15 +875,13 @@ export class ChatClient {
|
|
|
819
875
|
const runContext = {
|
|
820
876
|
threadId: this.threadId,
|
|
821
877
|
runId: `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
822
|
-
clientTools: Array.from(
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
}),
|
|
830
|
-
),
|
|
878
|
+
clientTools: Array.from(clientTools.values()).map((t) => ({
|
|
879
|
+
name: t.name,
|
|
880
|
+
description: t.description,
|
|
881
|
+
parameters: t.inputSchema
|
|
882
|
+
? convertSchemaToJsonSchema(t.inputSchema)
|
|
883
|
+
: { type: 'object' },
|
|
884
|
+
})),
|
|
831
885
|
forwardedProps: { ...mergedBody },
|
|
832
886
|
}
|
|
833
887
|
this.devtoolsBridge.beginRun(runContext.runId, this.threadId)
|
|
@@ -913,6 +967,8 @@ export class ChatClient {
|
|
|
913
967
|
this.currentStreamId = null
|
|
914
968
|
this.devtoolsBridge.setCurrentStreamId(null)
|
|
915
969
|
this.currentMessageId = null
|
|
970
|
+
this.activeClientTools = null
|
|
971
|
+
this.activeContext = undefined
|
|
916
972
|
this.abortController = null
|
|
917
973
|
this.setIsLoading(false)
|
|
918
974
|
this.pendingMessageBody = undefined // Ensure it's cleared even on error
|
|
@@ -1044,26 +1100,32 @@ export class ChatClient {
|
|
|
1044
1100
|
/**
|
|
1045
1101
|
* Add the result of a client-side tool execution
|
|
1046
1102
|
*/
|
|
1047
|
-
async addToolResult(result: {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
output: any
|
|
1051
|
-
state?: 'output-available' | 'output-error'
|
|
1052
|
-
errorText?: string
|
|
1053
|
-
}): Promise<void> {
|
|
1054
|
-
await this.addToolResultInternal(result)
|
|
1103
|
+
async addToolResult(result: ClientToolResult): Promise<void> {
|
|
1104
|
+
const clientTool = this.clientToolsRef.current.get(result.tool)
|
|
1105
|
+
await this.addToolResultForClientTool(result, clientTool)
|
|
1055
1106
|
}
|
|
1056
1107
|
|
|
1057
|
-
private async
|
|
1058
|
-
result:
|
|
1059
|
-
|
|
1060
|
-
tool: string
|
|
1061
|
-
output: any
|
|
1062
|
-
state?: 'output-available' | 'output-error'
|
|
1063
|
-
errorText?: string
|
|
1064
|
-
},
|
|
1108
|
+
private async addToolResultForClientTool(
|
|
1109
|
+
result: ClientToolResult,
|
|
1110
|
+
clientTool: AnyClientTool | undefined,
|
|
1065
1111
|
context?: ChatClientRunEventContext,
|
|
1066
1112
|
): Promise<void> {
|
|
1113
|
+
if (clientTool && result.state !== 'output-error') {
|
|
1114
|
+
try {
|
|
1115
|
+
result = {
|
|
1116
|
+
...result,
|
|
1117
|
+
output: this.validateClientToolOutput(clientTool, result.output),
|
|
1118
|
+
}
|
|
1119
|
+
} catch (error: any) {
|
|
1120
|
+
result = {
|
|
1121
|
+
...result,
|
|
1122
|
+
output: null,
|
|
1123
|
+
state: 'output-error',
|
|
1124
|
+
errorText: error.message,
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1067
1129
|
this.events.toolResultAdded(
|
|
1068
1130
|
result.toolCallId,
|
|
1069
1131
|
result.tool,
|
|
@@ -1088,6 +1150,17 @@ export class ChatClient {
|
|
|
1088
1150
|
await this.checkForContinuation()
|
|
1089
1151
|
}
|
|
1090
1152
|
|
|
1153
|
+
private validateClientToolOutput(
|
|
1154
|
+
clientTool: AnyClientTool,
|
|
1155
|
+
output: any,
|
|
1156
|
+
): any {
|
|
1157
|
+
if (clientTool.outputSchema && isStandardSchema(clientTool.outputSchema)) {
|
|
1158
|
+
return parseWithStandardSchema(clientTool.outputSchema, output)
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
return output
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1091
1164
|
/**
|
|
1092
1165
|
* Respond to a tool approval request
|
|
1093
1166
|
*/
|
|
@@ -1206,8 +1279,8 @@ export class ChatClient {
|
|
|
1206
1279
|
/**
|
|
1207
1280
|
* Get current messages
|
|
1208
1281
|
*/
|
|
1209
|
-
getMessages(): Array<UIMessage
|
|
1210
|
-
return this.processor.getMessages()
|
|
1282
|
+
getMessages(): Array<UIMessage<TTools>> {
|
|
1283
|
+
return this.processor.getMessages() as Array<UIMessage<TTools>>
|
|
1211
1284
|
}
|
|
1212
1285
|
|
|
1213
1286
|
/**
|
|
@@ -1258,7 +1331,7 @@ export class ChatClient {
|
|
|
1258
1331
|
/**
|
|
1259
1332
|
* Manually set messages
|
|
1260
1333
|
*/
|
|
1261
|
-
setMessagesManually(messages: Array<UIMessage
|
|
1334
|
+
setMessagesManually(messages: Array<UIMessage<TTools>>): void {
|
|
1262
1335
|
this.processor.setMessages(messages)
|
|
1263
1336
|
this.devtoolsBridge.emitSnapshot()
|
|
1264
1337
|
}
|
|
@@ -1266,26 +1339,16 @@ export class ChatClient {
|
|
|
1266
1339
|
/**
|
|
1267
1340
|
* Update options refs (for use in React hooks to avoid recreating client)
|
|
1268
1341
|
*/
|
|
1269
|
-
updateOptions(options:
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
onError?: (error: Error) => void
|
|
1280
|
-
onSubscriptionChange?: (isSubscribed: boolean) => void
|
|
1281
|
-
onConnectionStatusChange?: (status: ConnectionStatus) => void
|
|
1282
|
-
onSessionGeneratingChange?: (isGenerating: boolean) => void
|
|
1283
|
-
onCustomEvent?: (
|
|
1284
|
-
eventType: string,
|
|
1285
|
-
data: unknown,
|
|
1286
|
-
context: { toolCallId?: string },
|
|
1287
|
-
) => void
|
|
1288
|
-
}): void {
|
|
1342
|
+
updateOptions(options: ChatClientUpdateOptionsWithoutContext<TTools>): void
|
|
1343
|
+
updateOptions(
|
|
1344
|
+
options: ChatClientUpdateOptionsWithoutContext<TTools> &
|
|
1345
|
+
Pick<ChatClientOptions<TTools, TContext>, 'context'>,
|
|
1346
|
+
): void
|
|
1347
|
+
updateOptions(
|
|
1348
|
+
options: ChatClientUpdateOptionsWithoutContext<TTools> & {
|
|
1349
|
+
context?: TContext | undefined
|
|
1350
|
+
},
|
|
1351
|
+
): void {
|
|
1289
1352
|
if (options.connection !== undefined || options.fetcher !== undefined) {
|
|
1290
1353
|
const wasSubscribed = this.isSubscribed
|
|
1291
1354
|
|
|
@@ -1312,15 +1375,19 @@ export class ChatClient {
|
|
|
1312
1375
|
this.subscribe()
|
|
1313
1376
|
}
|
|
1314
1377
|
}
|
|
1315
|
-
// Replace each slot independently so callers can update one
|
|
1316
|
-
// wiping the other.
|
|
1317
|
-
//
|
|
1378
|
+
// Replace each wire-payload slot independently so callers can update one
|
|
1379
|
+
// without wiping the other. Passing `undefined` for `body` or
|
|
1380
|
+
// `forwardedProps` leaves that slot unchanged; context is cleared when the
|
|
1381
|
+
// key is present with an `undefined` value.
|
|
1318
1382
|
if (options.body !== undefined) {
|
|
1319
1383
|
this.bodyOption = options.body
|
|
1320
1384
|
}
|
|
1321
1385
|
if (options.forwardedProps !== undefined) {
|
|
1322
1386
|
this.forwardedPropsOption = options.forwardedProps
|
|
1323
1387
|
}
|
|
1388
|
+
if ('context' in options) {
|
|
1389
|
+
this.context = options.context
|
|
1390
|
+
}
|
|
1324
1391
|
if (options.tools !== undefined) {
|
|
1325
1392
|
this.clientToolsRef.current = new Map()
|
|
1326
1393
|
for (const tool of options.tools) {
|
package/src/devtools.ts
CHANGED
|
@@ -1219,7 +1219,9 @@ function hydrateToolCallOutputs(
|
|
|
1219
1219
|
candidate.output === undefined,
|
|
1220
1220
|
)
|
|
1221
1221
|
if (toolCall) {
|
|
1222
|
-
toolCall.output =
|
|
1222
|
+
toolCall.output = Array.isArray(part.content)
|
|
1223
|
+
? part.content
|
|
1224
|
+
: parseFixtureResultContent(part.content)
|
|
1223
1225
|
}
|
|
1224
1226
|
}
|
|
1225
1227
|
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -220,7 +220,7 @@ export type ToolCallPart<TTools extends ReadonlyArray<AnyClientTool> = any> =
|
|
|
220
220
|
export interface ToolResultPart {
|
|
221
221
|
type: 'tool-result'
|
|
222
222
|
toolCallId: string
|
|
223
|
-
content: string
|
|
223
|
+
content: string | Array<ContentPart>
|
|
224
224
|
state: ToolResultState
|
|
225
225
|
error?: string // Error message if state is "error"
|
|
226
226
|
}
|
|
@@ -266,14 +266,91 @@ export interface UIMessage<
|
|
|
266
266
|
createdAt?: Date
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
type IsUnknown<T> = unknown extends T
|
|
270
|
+
? [T] extends [unknown]
|
|
271
|
+
? true
|
|
272
|
+
: false
|
|
273
|
+
: false
|
|
274
|
+
|
|
275
|
+
type KnownContext<T> = IsUnknown<T> extends true ? never : T
|
|
276
|
+
|
|
277
|
+
type MergeContext<TLeft, TRight> = [TLeft] extends [never]
|
|
278
|
+
? TRight
|
|
279
|
+
: [TRight] extends [never]
|
|
280
|
+
? TLeft
|
|
281
|
+
: TLeft & TRight
|
|
282
|
+
|
|
283
|
+
type UnionToIntersection<T> = [T] extends [never]
|
|
284
|
+
? never
|
|
285
|
+
: (T extends unknown ? (value: T) => void : never) extends (
|
|
286
|
+
value: infer TIntersection,
|
|
287
|
+
) => void
|
|
288
|
+
? TIntersection
|
|
289
|
+
: never
|
|
290
|
+
|
|
291
|
+
type DefinedContext<T> = Exclude<T, undefined>
|
|
292
|
+
|
|
293
|
+
type ContextFromExecute<T> = T extends (...args: any) => any
|
|
294
|
+
? NonNullable<Parameters<T>[1]> extends { context: infer TContext }
|
|
295
|
+
? KnownContext<TContext>
|
|
296
|
+
: never
|
|
297
|
+
: never
|
|
298
|
+
|
|
299
|
+
type ContextFromClientTool<T> = T extends AnyClientTool
|
|
300
|
+
? T extends { execute?: infer TExecute }
|
|
301
|
+
? ContextFromExecute<TExecute>
|
|
302
|
+
: never
|
|
303
|
+
: never
|
|
304
|
+
|
|
305
|
+
type RequiredContextFromClientToolUnion<T> = T extends unknown
|
|
306
|
+
? undefined extends ContextFromClientTool<T>
|
|
307
|
+
? never
|
|
308
|
+
: ContextFromClientTool<T>
|
|
309
|
+
: never
|
|
310
|
+
|
|
311
|
+
type ContextFromClientToolUnion<T> = [
|
|
312
|
+
UnionToIntersection<DefinedContext<ContextFromClientTool<T>>>,
|
|
313
|
+
] extends [never]
|
|
314
|
+
? never
|
|
315
|
+
: [RequiredContextFromClientToolUnion<T>] extends [never]
|
|
316
|
+
? UnionToIntersection<DefinedContext<ContextFromClientTool<T>>> | undefined
|
|
317
|
+
: UnionToIntersection<DefinedContext<ContextFromClientTool<T>>>
|
|
318
|
+
|
|
319
|
+
type ContextFromClientTools<TTools> =
|
|
320
|
+
IsUnknown<TTools> extends true
|
|
321
|
+
? never
|
|
322
|
+
: TTools extends readonly [infer THead, ...infer TTail]
|
|
323
|
+
? MergeContext<
|
|
324
|
+
ContextFromClientTool<THead>,
|
|
325
|
+
ContextFromClientTools<TTail>
|
|
326
|
+
>
|
|
327
|
+
: TTools extends ReadonlyArray<infer TItem>
|
|
328
|
+
? ContextFromClientToolUnion<TItem>
|
|
329
|
+
: never
|
|
330
|
+
|
|
331
|
+
export type InferredClientContext<TTools> = [
|
|
332
|
+
ContextFromClientTools<TTools>,
|
|
333
|
+
] extends [never]
|
|
334
|
+
? unknown
|
|
335
|
+
: ContextFromClientTools<TTools>
|
|
336
|
+
|
|
337
|
+
export type ClientContextOptionFromTools<TTools, TContext> = [
|
|
338
|
+
ContextFromClientTools<TTools>,
|
|
339
|
+
] extends [never]
|
|
340
|
+
? { context?: TContext }
|
|
341
|
+
: undefined extends ContextFromClientTools<TTools>
|
|
342
|
+
? { context?: TContext & ContextFromClientTools<TTools> }
|
|
343
|
+
: { context: TContext & ContextFromClientTools<TTools> }
|
|
344
|
+
|
|
269
345
|
/**
|
|
270
|
-
*
|
|
271
|
-
*
|
|
346
|
+
* Base options for `ChatClient`, excluding the transport (`connection` or
|
|
347
|
+
* `fetcher`) which is supplied separately via `ChatTransport` so the XOR
|
|
348
|
+
* is preserved when composing the final `ChatClientOptions` type.
|
|
272
349
|
*/
|
|
273
|
-
export
|
|
350
|
+
export interface ChatClientBaseOptions<
|
|
274
351
|
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
275
352
|
TContext = unknown,
|
|
276
|
-
>
|
|
353
|
+
> {
|
|
277
354
|
/**
|
|
278
355
|
* Initial messages to populate the chat
|
|
279
356
|
*/
|
|
@@ -311,6 +388,14 @@ export type ChatClientOptions<
|
|
|
311
388
|
*/
|
|
312
389
|
body?: Record<string, any>
|
|
313
390
|
|
|
391
|
+
/**
|
|
392
|
+
* Client-local runtime context passed to client tool implementations.
|
|
393
|
+
*
|
|
394
|
+
* This value is not serialized to the server. Use `forwardedProps` for
|
|
395
|
+
* explicit client-to-server handoff of serializable values.
|
|
396
|
+
*/
|
|
397
|
+
context?: TContext
|
|
398
|
+
|
|
314
399
|
/**
|
|
315
400
|
* Callback when a response is received
|
|
316
401
|
*/
|
|
@@ -390,11 +475,6 @@ export type ChatClientOptions<
|
|
|
390
475
|
*/
|
|
391
476
|
tools?: TTools
|
|
392
477
|
|
|
393
|
-
/**
|
|
394
|
-
* Client-local context passed to client-side tool execution.
|
|
395
|
-
*/
|
|
396
|
-
context?: TContext
|
|
397
|
-
|
|
398
478
|
/**
|
|
399
479
|
* Devtools hook metadata for this client instance.
|
|
400
480
|
*/
|
|
@@ -420,7 +500,18 @@ export type ChatClientOptions<
|
|
|
420
500
|
*/
|
|
421
501
|
chunkStrategy?: ChunkStrategy
|
|
422
502
|
}
|
|
423
|
-
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Options for `ChatClient`. Exactly one of `connection` or `fetcher` must be
|
|
507
|
+
* provided — the type-level XOR is enforced via `ChatTransport`.
|
|
508
|
+
*/
|
|
509
|
+
export type ChatClientOptions<
|
|
510
|
+
TTools extends ReadonlyArray<AnyClientTool> = any,
|
|
511
|
+
TContext = InferredClientContext<TTools>,
|
|
512
|
+
> = DistributedOmit<ChatClientBaseOptions<TTools, TContext>, 'context'> &
|
|
513
|
+
ClientContextOptionFromTools<TTools, TContext> &
|
|
514
|
+
ChatTransport
|
|
424
515
|
|
|
425
516
|
export interface ChatRequestBody {
|
|
426
517
|
messages: Array<ModelMessage>
|
|
@@ -466,7 +557,7 @@ export function clientTools<const T extends Array<AnyClientTool>>(
|
|
|
466
557
|
*/
|
|
467
558
|
export function createChatClientOptions<
|
|
468
559
|
const TTools extends ReadonlyArray<AnyClientTool>,
|
|
469
|
-
TContext =
|
|
560
|
+
TContext = InferredClientContext<TTools>,
|
|
470
561
|
>(
|
|
471
562
|
options: ChatClientOptions<TTools, TContext>,
|
|
472
563
|
): ChatClientOptions<TTools, TContext> {
|