ai 6.0.289 → 6.0.291
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/CHANGELOG.md +28 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +326 -250
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +263 -182
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +4 -2
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +4 -2
- package/dist/internal/index.mjs.map +1 -1
- package/docs/03-ai-sdk-core/16-mcp-tools.mdx +57 -0
- package/docs/04-ai-sdk-ui/03-chatbot-tool-usage.mdx +7 -0
- package/package.json +4 -3
- package/src/embed/embed-many.ts +19 -3
- package/src/generate-speech/generated-audio-file.ts +0 -8
- package/src/generate-text/generated-file.ts +0 -4
- package/src/generate-text/parse-tool-call.ts +30 -23
- package/src/generate-text/stream-text.ts +9 -1
- package/src/generate-text/to-response-messages.ts +7 -0
- package/src/generate-text/tool-call.ts +26 -0
- package/src/generate-text/validate-tool-approvals.ts +20 -2
- package/src/middleware/extract-reasoning-middleware.ts +1 -1
- package/src/middleware/wrap-embedding-model.ts +12 -2
- package/src/model/get-embedding-model-provider-options-transformer.ts +17 -0
- package/src/prompt/content-part.ts +2 -0
- package/src/ui/chat.ts +108 -17
- package/src/ui/convert-to-model-messages.ts +8 -0
- package/src/ui/process-ui-message-stream.ts +6 -0
- package/src/ui/ui-messages.ts +10 -0
- package/src/ui/validate-ui-messages.ts +79 -114
- package/src/ui-message-stream/ui-message-chunks.ts +2 -0
- package/src/util/write-to-server-response.ts +0 -2
package/src/embed/embed-many.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
} from '@ai-sdk/provider-utils';
|
|
5
5
|
import { logWarnings } from '../logger/log-warnings';
|
|
6
6
|
import { getEmbeddingModelMaxInputBytesPerCall } from '../model/get-embedding-model-max-input-bytes-per-call';
|
|
7
|
+
import { getEmbeddingModelProviderOptionsTransformer } from '../model/get-embedding-model-provider-options-transformer';
|
|
7
8
|
import { resolveEmbeddingModel } from '../model/resolve-model';
|
|
8
9
|
import { assembleOperationName } from '../telemetry/assemble-operation-name';
|
|
9
10
|
import { getBaseTelemetryAttributes } from '../telemetry/get-base-telemetry-attributes';
|
|
@@ -250,6 +251,8 @@ export async function embedMany({
|
|
|
250
251
|
? maxInputBytesPerCall
|
|
251
252
|
: Infinity,
|
|
252
253
|
});
|
|
254
|
+
const providerOptionsTransformer =
|
|
255
|
+
getEmbeddingModelProviderOptionsTransformer(model);
|
|
253
256
|
|
|
254
257
|
// serially embed the chunks:
|
|
255
258
|
const embeddings: Array<Embedding> = [];
|
|
@@ -269,9 +272,22 @@ export async function embedMany({
|
|
|
269
272
|
supportsParallelCalls ? maxParallelCalls : 1,
|
|
270
273
|
);
|
|
271
274
|
|
|
275
|
+
let nextChunkStartIndex = 0;
|
|
272
276
|
for (const parallelChunk of parallelChunks) {
|
|
273
277
|
const results = await Promise.all(
|
|
274
|
-
parallelChunk.map(chunk => {
|
|
278
|
+
parallelChunk.map(async chunk => {
|
|
279
|
+
// Capture the range before awaiting transformations or retrying.
|
|
280
|
+
const startIndex = nextChunkStartIndex;
|
|
281
|
+
nextChunkStartIndex += chunk.length;
|
|
282
|
+
const chunkProviderOptions = providerOptionsTransformer
|
|
283
|
+
? await providerOptionsTransformer({
|
|
284
|
+
providerOptions,
|
|
285
|
+
values,
|
|
286
|
+
startIndex,
|
|
287
|
+
endIndex: startIndex + chunk.length,
|
|
288
|
+
})
|
|
289
|
+
: providerOptions;
|
|
290
|
+
|
|
275
291
|
return retry(() => {
|
|
276
292
|
// nested spans to align with the embedMany telemetry data:
|
|
277
293
|
return recordSpan({
|
|
@@ -296,7 +312,7 @@ export async function embedMany({
|
|
|
296
312
|
values: chunk,
|
|
297
313
|
abortSignal,
|
|
298
314
|
headers: headersWithUserAgent,
|
|
299
|
-
providerOptions,
|
|
315
|
+
providerOptions: chunkProviderOptions,
|
|
300
316
|
});
|
|
301
317
|
|
|
302
318
|
const embeddings = modelResponse.embeddings;
|
|
@@ -343,7 +359,7 @@ export async function embedMany({
|
|
|
343
359
|
result.providerMetadata,
|
|
344
360
|
)) {
|
|
345
361
|
providerMetadata[providerName] = {
|
|
346
|
-
...
|
|
362
|
+
...providerMetadata[providerName],
|
|
347
363
|
...metadata,
|
|
348
364
|
};
|
|
349
365
|
}
|
|
@@ -54,12 +54,4 @@ export class DefaultGeneratedAudioFile
|
|
|
54
54
|
|
|
55
55
|
export class DefaultGeneratedAudioFileWithType extends DefaultGeneratedAudioFile {
|
|
56
56
|
readonly type = 'audio';
|
|
57
|
-
|
|
58
|
-
constructor(options: {
|
|
59
|
-
data: string | Uint8Array;
|
|
60
|
-
mediaType: string;
|
|
61
|
-
format: string;
|
|
62
|
-
}) {
|
|
63
|
-
super(options);
|
|
64
|
-
}
|
|
65
57
|
}
|
|
@@ -63,8 +63,4 @@ export class DefaultGeneratedFile implements GeneratedFile {
|
|
|
63
63
|
|
|
64
64
|
export class DefaultGeneratedFileWithType extends DefaultGeneratedFile {
|
|
65
65
|
readonly type = 'file';
|
|
66
|
-
|
|
67
|
-
constructor(options: { data: string | Uint8Array; mediaType: string }) {
|
|
68
|
-
super(options);
|
|
69
|
-
}
|
|
70
66
|
}
|
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
import { InvalidToolInputError } from '../error/invalid-tool-input-error';
|
|
10
10
|
import { NoSuchToolError } from '../error/no-such-tool-error';
|
|
11
11
|
import { ToolCallRepairError } from '../error/tool-call-repair-error';
|
|
12
|
-
import
|
|
12
|
+
import {
|
|
13
|
+
setToolCallInputSchemaInput,
|
|
14
|
+
type DynamicToolCall,
|
|
15
|
+
type TypedToolCall,
|
|
16
|
+
} from './tool-call';
|
|
13
17
|
import type { ToolCallRepairFunction } from './tool-call-repair-function';
|
|
14
18
|
import type { ToolSet } from './tool-set';
|
|
15
19
|
|
|
@@ -167,26 +171,29 @@ async function doParseToolCall<TOOLS extends ToolSet>({
|
|
|
167
171
|
});
|
|
168
172
|
}
|
|
169
173
|
|
|
170
|
-
return
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
174
|
+
return setToolCallInputSchemaInput(
|
|
175
|
+
tool.type === 'dynamic'
|
|
176
|
+
? {
|
|
177
|
+
type: 'tool-call',
|
|
178
|
+
toolCallId: toolCall.toolCallId,
|
|
179
|
+
toolName: toolCall.toolName,
|
|
180
|
+
input: parseResult.value,
|
|
181
|
+
providerExecuted: toolCall.providerExecuted,
|
|
182
|
+
providerMetadata: toolCall.providerMetadata,
|
|
183
|
+
...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
|
|
184
|
+
dynamic: true,
|
|
185
|
+
title: tool.title,
|
|
186
|
+
}
|
|
187
|
+
: {
|
|
188
|
+
type: 'tool-call',
|
|
189
|
+
toolCallId: toolCall.toolCallId,
|
|
190
|
+
toolName,
|
|
191
|
+
input: parseResult.value,
|
|
192
|
+
providerExecuted: toolCall.providerExecuted,
|
|
193
|
+
providerMetadata: toolCall.providerMetadata,
|
|
194
|
+
...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
|
|
195
|
+
title: tool.title,
|
|
196
|
+
},
|
|
197
|
+
parseResult.rawValue,
|
|
198
|
+
);
|
|
192
199
|
}
|
|
@@ -79,6 +79,7 @@ import { consumeStream } from '../util/consume-stream';
|
|
|
79
79
|
import { createIdMap } from '../util/create-id-map';
|
|
80
80
|
import { createStitchableStream } from '../util/create-stitchable-stream';
|
|
81
81
|
import type { DownloadFunction } from '../util/download/download-function';
|
|
82
|
+
import { isDeepEqualData } from '../util/is-deep-equal-data';
|
|
82
83
|
import { mergeAbortSignals } from '../util/merge-abort-signals';
|
|
83
84
|
import { mergeObjects } from '../util/merge-objects';
|
|
84
85
|
import { now as originalNow } from '../util/now';
|
|
@@ -123,7 +124,7 @@ import type {
|
|
|
123
124
|
UIMessageStreamOptions,
|
|
124
125
|
} from './stream-text-result';
|
|
125
126
|
import { toResponseMessages } from './to-response-messages';
|
|
126
|
-
import type
|
|
127
|
+
import { getToolCallInputSchemaInput, type TypedToolCall } from './tool-call';
|
|
127
128
|
import type { ToolCallRepairFunction } from './tool-call-repair-function';
|
|
128
129
|
import type { ToolOutput } from './tool-output';
|
|
129
130
|
import type { StaticToolOutputDenied } from './tool-output-denied';
|
|
@@ -2984,10 +2985,17 @@ class DefaultStreamTextResult<
|
|
|
2984
2985
|
}
|
|
2985
2986
|
|
|
2986
2987
|
case 'tool-approval-request': {
|
|
2988
|
+
const inputSchemaInput = getToolCallInputSchemaInput(
|
|
2989
|
+
part.toolCall,
|
|
2990
|
+
);
|
|
2987
2991
|
controller.enqueue({
|
|
2988
2992
|
type: 'tool-approval-request',
|
|
2989
2993
|
approvalId: part.approvalId,
|
|
2990
2994
|
toolCallId: part.toolCall.toolCallId,
|
|
2995
|
+
...(inputSchemaInput != null &&
|
|
2996
|
+
!isDeepEqualData(inputSchemaInput.value, part.toolCall.input)
|
|
2997
|
+
? { inputSchemaInput: inputSchemaInput.value }
|
|
2998
|
+
: {}),
|
|
2991
2999
|
...(part.signature != null
|
|
2992
3000
|
? { signature: part.signature }
|
|
2993
3001
|
: {}),
|
|
@@ -5,8 +5,10 @@ import type {
|
|
|
5
5
|
ToolModelMessage,
|
|
6
6
|
} from '../prompt';
|
|
7
7
|
import { createToolModelOutput } from '../prompt/create-tool-model-output';
|
|
8
|
+
import { isDeepEqualData } from '../util/is-deep-equal-data';
|
|
8
9
|
import type { ContentPart } from './content-part';
|
|
9
10
|
import type { ToolSet } from './tool-set';
|
|
11
|
+
import { getToolCallInputSchemaInput } from './tool-call';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Converts the result of a `generateText` or `streamText` call to a list of response messages.
|
|
@@ -113,11 +115,16 @@ export async function toResponseMessages<TOOLS extends ToolSet>({
|
|
|
113
115
|
break;
|
|
114
116
|
}
|
|
115
117
|
case 'tool-approval-request':
|
|
118
|
+
const inputSchemaInput = getToolCallInputSchemaInput(part.toolCall);
|
|
116
119
|
content.push({
|
|
117
120
|
type: 'tool-approval-request',
|
|
118
121
|
approvalId: part.approvalId,
|
|
119
122
|
toolCallId: part.toolCall.toolCallId,
|
|
120
123
|
...(part.signature != null ? { signature: part.signature } : {}),
|
|
124
|
+
...(inputSchemaInput != null &&
|
|
125
|
+
!isDeepEqualData(inputSchemaInput.value, part.toolCall.input)
|
|
126
|
+
? { inputSchemaInput: inputSchemaInput.value }
|
|
127
|
+
: {}),
|
|
121
128
|
});
|
|
122
129
|
break;
|
|
123
130
|
}
|
|
@@ -4,6 +4,8 @@ import type { ProviderMetadata } from '../types';
|
|
|
4
4
|
import type { ValueOf } from '../util/value-of';
|
|
5
5
|
import type { ToolSet } from './tool-set';
|
|
6
6
|
|
|
7
|
+
const inputSchemaInputSymbol = Symbol('ai-sdk-tool-call-input-schema-input');
|
|
8
|
+
|
|
7
9
|
type BaseToolCall = {
|
|
8
10
|
type: 'tool-call';
|
|
9
11
|
toolCallId: string;
|
|
@@ -47,3 +49,27 @@ export type DynamicToolCall = BaseToolCall & {
|
|
|
47
49
|
export type TypedToolCall<TOOLS extends ToolSet> =
|
|
48
50
|
| StaticToolCall<TOOLS>
|
|
49
51
|
| DynamicToolCall;
|
|
52
|
+
|
|
53
|
+
export function setToolCallInputSchemaInput<TOOLS extends ToolSet>(
|
|
54
|
+
toolCall: TypedToolCall<TOOLS>,
|
|
55
|
+
inputSchemaInput: unknown,
|
|
56
|
+
): TypedToolCall<TOOLS> {
|
|
57
|
+
Object.defineProperty(toolCall, inputSchemaInputSymbol, {
|
|
58
|
+
value: inputSchemaInput,
|
|
59
|
+
});
|
|
60
|
+
return toolCall;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function getToolCallInputSchemaInput<TOOLS extends ToolSet>(
|
|
64
|
+
toolCall: TypedToolCall<TOOLS>,
|
|
65
|
+
): { value: unknown } | undefined {
|
|
66
|
+
return inputSchemaInputSymbol in toolCall
|
|
67
|
+
? {
|
|
68
|
+
value: (
|
|
69
|
+
toolCall as TypedToolCall<TOOLS> & {
|
|
70
|
+
[inputSchemaInputSymbol]: unknown;
|
|
71
|
+
}
|
|
72
|
+
)[inputSchemaInputSymbol],
|
|
73
|
+
}
|
|
74
|
+
: undefined;
|
|
75
|
+
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from '@ai-sdk/provider-utils';
|
|
6
6
|
import { InvalidToolApprovalSignatureError } from '../error/invalid-tool-approval-signature-error';
|
|
7
7
|
import { InvalidToolInputError } from '../error/invalid-tool-input-error';
|
|
8
|
+
import { isDeepEqualData } from '../util/is-deep-equal-data';
|
|
8
9
|
import type { CollectedToolApprovals } from './collect-tool-approvals';
|
|
9
10
|
import { isApprovalNeeded } from './is-approval-needed';
|
|
10
11
|
import { verifyToolApprovalSignature } from './tool-approval-signature';
|
|
@@ -86,18 +87,35 @@ export async function validateApprovedToolApprovals<TOOLS extends ToolSet>({
|
|
|
86
87
|
typeof tool.execute === 'function' &&
|
|
87
88
|
tool.inputSchema != null
|
|
88
89
|
) {
|
|
90
|
+
const hasInputSchemaInput = Object.prototype.hasOwnProperty.call(
|
|
91
|
+
approvalRequest,
|
|
92
|
+
'inputSchemaInput',
|
|
93
|
+
);
|
|
89
94
|
const validation = await safeValidateTypes({
|
|
90
|
-
value:
|
|
95
|
+
value: hasInputSchemaInput
|
|
96
|
+
? approvalRequest.inputSchemaInput
|
|
97
|
+
: toolCall.input,
|
|
91
98
|
schema: asSchema(tool.inputSchema),
|
|
92
99
|
});
|
|
93
100
|
|
|
101
|
+
let validationError: unknown;
|
|
94
102
|
if (!validation.success) {
|
|
103
|
+
validationError = validation.error;
|
|
104
|
+
} else if (!isDeepEqualData(validation.value, toolCall.input)) {
|
|
105
|
+
// Revalidation must never change the operation that was approved,
|
|
106
|
+
// including when older or projected history omits the schema input.
|
|
107
|
+
validationError = new Error(
|
|
108
|
+
'Approved tool input does not match the validated schema output.',
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (validationError != null) {
|
|
95
113
|
invalid.push({
|
|
96
114
|
...approval,
|
|
97
115
|
error: new InvalidToolInputError({
|
|
98
116
|
toolName: toolCall.toolName,
|
|
99
117
|
toolInput: JSON.stringify(toolCall.input),
|
|
100
|
-
cause:
|
|
118
|
+
cause: validationError,
|
|
101
119
|
}),
|
|
102
120
|
});
|
|
103
121
|
continue;
|
|
@@ -24,7 +24,7 @@ export function extractReasoningMiddleware({
|
|
|
24
24
|
startWithReasoning?: boolean;
|
|
25
25
|
}): LanguageModelMiddleware {
|
|
26
26
|
const openingTag = `<${tagName}>`;
|
|
27
|
-
const closingTag =
|
|
27
|
+
const closingTag = `</${tagName}>`;
|
|
28
28
|
|
|
29
29
|
return {
|
|
30
30
|
specificationVersion: 'v3',
|
|
@@ -2,11 +2,18 @@ import type {
|
|
|
2
2
|
EmbeddingModelV3,
|
|
3
3
|
EmbeddingModelV3CallOptions,
|
|
4
4
|
} from '@ai-sdk/provider';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL,
|
|
7
|
+
EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER,
|
|
8
|
+
} from '@ai-sdk/provider-utils';
|
|
6
9
|
import {
|
|
7
10
|
getEmbeddingModelMaxInputBytesPerCall,
|
|
8
11
|
type EmbeddingModelWithMaxInputBytesPerCall,
|
|
9
12
|
} from '../model/get-embedding-model-max-input-bytes-per-call';
|
|
13
|
+
import {
|
|
14
|
+
getEmbeddingModelProviderOptionsTransformer,
|
|
15
|
+
type EmbeddingModelWithProviderOptionsTransformer,
|
|
16
|
+
} from '../model/get-embedding-model-provider-options-transformer';
|
|
10
17
|
import type { EmbeddingModelMiddleware } from '../types';
|
|
11
18
|
import { asArray } from '../util/as-array';
|
|
12
19
|
|
|
@@ -57,7 +64,8 @@ const doWrap = ({
|
|
|
57
64
|
middleware: EmbeddingModelMiddleware;
|
|
58
65
|
modelId?: string;
|
|
59
66
|
providerId?: string;
|
|
60
|
-
}): EmbeddingModelWithMaxInputBytesPerCall
|
|
67
|
+
}): EmbeddingModelWithMaxInputBytesPerCall &
|
|
68
|
+
EmbeddingModelWithProviderOptionsTransformer => {
|
|
61
69
|
async function doTransform({
|
|
62
70
|
params,
|
|
63
71
|
}: {
|
|
@@ -74,6 +82,8 @@ const doWrap = ({
|
|
|
74
82
|
overrideMaxEmbeddingsPerCall?.({ model }) ?? model.maxEmbeddingsPerCall,
|
|
75
83
|
[EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL]:
|
|
76
84
|
getEmbeddingModelMaxInputBytesPerCall(model),
|
|
85
|
+
[EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER]:
|
|
86
|
+
getEmbeddingModelProviderOptionsTransformer(model),
|
|
77
87
|
supportsParallelCalls:
|
|
78
88
|
overrideSupportsParallelCalls?.({ model }) ?? model.supportsParallelCalls,
|
|
79
89
|
async doEmbed(
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EmbeddingModelV3 } from '@ai-sdk/provider';
|
|
2
|
+
import {
|
|
3
|
+
EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER,
|
|
4
|
+
type EmbeddingModelProviderOptionsTransformer,
|
|
5
|
+
} from '@ai-sdk/provider-utils';
|
|
6
|
+
|
|
7
|
+
export type EmbeddingModelWithProviderOptionsTransformer = EmbeddingModelV3 & {
|
|
8
|
+
readonly [EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER]?: EmbeddingModelProviderOptionsTransformer;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function getEmbeddingModelProviderOptionsTransformer(
|
|
12
|
+
model: EmbeddingModelV3,
|
|
13
|
+
) {
|
|
14
|
+
return (model as EmbeddingModelWithProviderOptionsTransformer)[
|
|
15
|
+
EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER
|
|
16
|
+
];
|
|
17
|
+
}
|
|
@@ -201,6 +201,8 @@ export const toolApprovalRequestSchema: z.ZodType<ToolApprovalRequest> =
|
|
|
201
201
|
type: z.literal('tool-approval-request'),
|
|
202
202
|
approvalId: z.string(),
|
|
203
203
|
toolCallId: z.string(),
|
|
204
|
+
signature: z.string().optional(),
|
|
205
|
+
inputSchemaInput: z.unknown().optional(),
|
|
204
206
|
});
|
|
205
207
|
|
|
206
208
|
/**
|
package/src/ui/chat.ts
CHANGED
|
@@ -259,6 +259,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
259
259
|
private onData?: ChatInit<UI_MESSAGE>['onData'];
|
|
260
260
|
private sendAutomaticallyWhen?: ChatInit<UI_MESSAGE>['sendAutomaticallyWhen'];
|
|
261
261
|
|
|
262
|
+
private pendingApprovalMessageId: string | undefined;
|
|
262
263
|
private activeResponse: ActiveResponse<UI_MESSAGE> | undefined = undefined;
|
|
263
264
|
private activeResumeRequest: ActiveResumeRequest | undefined = undefined;
|
|
264
265
|
private jobExecutor = new SerialJobExecutor();
|
|
@@ -361,9 +362,37 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
361
362
|
options?: ChatRequestOptions,
|
|
362
363
|
): Promise<void> => {
|
|
363
364
|
if (message == null) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
365
|
+
let messageId = this.pendingApprovalMessageId;
|
|
366
|
+
|
|
367
|
+
if (messageId == null) {
|
|
368
|
+
messageId = this.lastMessage?.id;
|
|
369
|
+
|
|
370
|
+
// When hydrating a chat with an already-responded approval, continue
|
|
371
|
+
// the most recent matching assistant message so result chunks can
|
|
372
|
+
// resolve its tool invocation.
|
|
373
|
+
for (let i = this.state.messages.length - 1; i >= 0; i--) {
|
|
374
|
+
const candidate = this.state.messages[i];
|
|
375
|
+
if (
|
|
376
|
+
candidate.role === 'assistant' &&
|
|
377
|
+
candidate.parts.some(
|
|
378
|
+
part => isToolUIPart(part) && part.state === 'approval-responded',
|
|
379
|
+
)
|
|
380
|
+
) {
|
|
381
|
+
messageId = candidate.id;
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const consumesPendingApproval =
|
|
388
|
+
messageId != null && messageId === this.pendingApprovalMessageId;
|
|
389
|
+
const pendingApprovalMessageIndex = consumesPendingApproval
|
|
390
|
+
? this.state.messages.findIndex(message => message.id === messageId)
|
|
391
|
+
: -1;
|
|
392
|
+
|
|
393
|
+
await this.makeRequestForToolApproval({
|
|
394
|
+
messageId,
|
|
395
|
+
messageIndex: pendingApprovalMessageIndex,
|
|
367
396
|
...options,
|
|
368
397
|
});
|
|
369
398
|
return;
|
|
@@ -489,7 +518,6 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
489
518
|
}) =>
|
|
490
519
|
this.jobExecutor.run(async () => {
|
|
491
520
|
const messages = this.state.messages;
|
|
492
|
-
const lastMessage = messages[messages.length - 1];
|
|
493
521
|
|
|
494
522
|
const updatePart = (
|
|
495
523
|
part: UIMessagePart<UIDataTypes, UITools>,
|
|
@@ -504,11 +532,25 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
504
532
|
}
|
|
505
533
|
: part;
|
|
506
534
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
535
|
+
const messageIndex = messages.findIndex(message =>
|
|
536
|
+
message.parts.some(
|
|
537
|
+
part =>
|
|
538
|
+
isToolUIPart(part) &&
|
|
539
|
+
part.state === 'approval-requested' &&
|
|
540
|
+
part.approval.id === id,
|
|
541
|
+
),
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
if (messageIndex !== -1) {
|
|
545
|
+
const message = messages[messageIndex];
|
|
546
|
+
|
|
547
|
+
// update the message to trigger an immediate UI update
|
|
548
|
+
this.state.replaceMessage(messageIndex, {
|
|
549
|
+
...message,
|
|
550
|
+
parts: message.parts.map(updatePart),
|
|
551
|
+
});
|
|
552
|
+
this.pendingApprovalMessageId = message.id;
|
|
553
|
+
}
|
|
512
554
|
|
|
513
555
|
// update the active response if it exists
|
|
514
556
|
if (this.activeResponse) {
|
|
@@ -525,9 +567,14 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
525
567
|
this.shouldSendAutomatically().then(shouldSend => {
|
|
526
568
|
if (shouldSend) {
|
|
527
569
|
// no await to avoid deadlocking
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
570
|
+
const messageId =
|
|
571
|
+
messageIndex === -1
|
|
572
|
+
? this.lastMessage?.id
|
|
573
|
+
: messages[messageIndex].id;
|
|
574
|
+
|
|
575
|
+
this.makeRequestForToolApproval({
|
|
576
|
+
messageId,
|
|
577
|
+
messageIndex,
|
|
531
578
|
...options,
|
|
532
579
|
});
|
|
533
580
|
}
|
|
@@ -610,6 +657,36 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
610
657
|
return result as boolean;
|
|
611
658
|
}
|
|
612
659
|
|
|
660
|
+
private async makeRequestForToolApproval({
|
|
661
|
+
messageId,
|
|
662
|
+
messageIndex,
|
|
663
|
+
...options
|
|
664
|
+
}: {
|
|
665
|
+
messageId?: string;
|
|
666
|
+
messageIndex: number;
|
|
667
|
+
} & ChatRequestOptions) {
|
|
668
|
+
const consumesPendingApproval =
|
|
669
|
+
messageId != null && messageId === this.pendingApprovalMessageId;
|
|
670
|
+
if (consumesPendingApproval) {
|
|
671
|
+
this.pendingApprovalMessageId = undefined;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
await this.makeRequest({
|
|
675
|
+
trigger: 'submit-message',
|
|
676
|
+
messageId,
|
|
677
|
+
...options,
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
if (
|
|
681
|
+
consumesPendingApproval &&
|
|
682
|
+
this.status === 'error' &&
|
|
683
|
+
this.pendingApprovalMessageId == null
|
|
684
|
+
) {
|
|
685
|
+
this.pendingApprovalMessageId =
|
|
686
|
+
this.state.messages[messageIndex]?.id ?? messageId;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
613
690
|
private async makeRequest({
|
|
614
691
|
trigger,
|
|
615
692
|
metadata,
|
|
@@ -697,6 +774,18 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
697
774
|
this.setStatus({ status: 'submitted', error: undefined });
|
|
698
775
|
|
|
699
776
|
const lastMessage = this.lastMessage;
|
|
777
|
+
const responseMessageIndex =
|
|
778
|
+
trigger === 'submit-message' && messageId != null
|
|
779
|
+
? this.state.messages.findIndex(message => message.id === messageId)
|
|
780
|
+
: this.state.messages.length - 1;
|
|
781
|
+
const responseMessage =
|
|
782
|
+
responseMessageIndex === -1
|
|
783
|
+
? lastMessage
|
|
784
|
+
: this.state.messages[responseMessageIndex];
|
|
785
|
+
const usesEarlierAssistantMessage =
|
|
786
|
+
responseMessageIndex !== -1 &&
|
|
787
|
+
responseMessageIndex < this.state.messages.length - 1 &&
|
|
788
|
+
responseMessage?.role === 'assistant';
|
|
700
789
|
|
|
701
790
|
let isAbort = false;
|
|
702
791
|
let isDisconnect = false;
|
|
@@ -709,7 +798,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
709
798
|
lastMessage:
|
|
710
799
|
trigger === 'resume-stream' || trigger === 'regenerate-message'
|
|
711
800
|
? undefined
|
|
712
|
-
: this.state.snapshot(
|
|
801
|
+
: this.state.snapshot(responseMessage),
|
|
713
802
|
messageId: this.generateId(),
|
|
714
803
|
}),
|
|
715
804
|
abortController,
|
|
@@ -763,10 +852,12 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
763
852
|
this.setStatus({ status: 'streaming' });
|
|
764
853
|
}
|
|
765
854
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
855
|
+
if (usesEarlierAssistantMessage) {
|
|
856
|
+
this.state.replaceMessage(
|
|
857
|
+
responseMessageIndex,
|
|
858
|
+
response.state.message,
|
|
859
|
+
);
|
|
860
|
+
} else if (response.state.message.id === this.lastMessage?.id) {
|
|
770
861
|
this.state.replaceMessage(
|
|
771
862
|
this.state.messages.length - 1,
|
|
772
863
|
response.state.message,
|
|
@@ -204,6 +204,14 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
|
|
|
204
204
|
type: 'tool-approval-request' as const,
|
|
205
205
|
approvalId: part.approval.id,
|
|
206
206
|
toolCallId: part.toolCallId,
|
|
207
|
+
...(Object.prototype.hasOwnProperty.call(
|
|
208
|
+
part.approval,
|
|
209
|
+
'inputSchemaInput',
|
|
210
|
+
)
|
|
211
|
+
? {
|
|
212
|
+
inputSchemaInput: part.approval.inputSchemaInput,
|
|
213
|
+
}
|
|
214
|
+
: {}),
|
|
207
215
|
...(part.approval.signature != null
|
|
208
216
|
? { signature: part.approval.signature }
|
|
209
217
|
: {}),
|
|
@@ -721,6 +721,12 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
|
|
|
721
721
|
...(chunk.approvalDescriptor != null
|
|
722
722
|
? { descriptor: chunk.approvalDescriptor }
|
|
723
723
|
: {}),
|
|
724
|
+
...(Object.prototype.hasOwnProperty.call(
|
|
725
|
+
chunk,
|
|
726
|
+
'inputSchemaInput',
|
|
727
|
+
)
|
|
728
|
+
? { inputSchemaInput: chunk.inputSchemaInput }
|
|
729
|
+
: {}),
|
|
724
730
|
...(chunk.signature != null
|
|
725
731
|
? { signature: chunk.signature }
|
|
726
732
|
: {}),
|
package/src/ui/ui-messages.ts
CHANGED
|
@@ -263,6 +263,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
|
|
|
263
263
|
descriptor?: unknown;
|
|
264
264
|
reason?: never;
|
|
265
265
|
signature?: string;
|
|
266
|
+
inputSchemaInput?: unknown;
|
|
266
267
|
};
|
|
267
268
|
}
|
|
268
269
|
| {
|
|
@@ -277,6 +278,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
|
|
|
277
278
|
descriptor?: unknown;
|
|
278
279
|
reason?: string;
|
|
279
280
|
signature?: string;
|
|
281
|
+
inputSchemaInput?: unknown;
|
|
280
282
|
};
|
|
281
283
|
}
|
|
282
284
|
| {
|
|
@@ -293,6 +295,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
|
|
|
293
295
|
descriptor?: unknown;
|
|
294
296
|
reason?: string;
|
|
295
297
|
signature?: string;
|
|
298
|
+
inputSchemaInput?: unknown;
|
|
296
299
|
};
|
|
297
300
|
}
|
|
298
301
|
| {
|
|
@@ -309,6 +312,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
|
|
|
309
312
|
descriptor?: unknown;
|
|
310
313
|
reason?: string;
|
|
311
314
|
signature?: string;
|
|
315
|
+
inputSchemaInput?: unknown;
|
|
312
316
|
};
|
|
313
317
|
}
|
|
314
318
|
| {
|
|
@@ -323,6 +327,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
|
|
|
323
327
|
descriptor?: unknown;
|
|
324
328
|
reason?: string;
|
|
325
329
|
signature?: string;
|
|
330
|
+
inputSchemaInput?: unknown;
|
|
326
331
|
};
|
|
327
332
|
}
|
|
328
333
|
);
|
|
@@ -381,6 +386,7 @@ export type DynamicToolUIPart = {
|
|
|
381
386
|
descriptor?: unknown;
|
|
382
387
|
reason?: never;
|
|
383
388
|
signature?: string;
|
|
389
|
+
inputSchemaInput?: unknown;
|
|
384
390
|
};
|
|
385
391
|
}
|
|
386
392
|
| {
|
|
@@ -395,6 +401,7 @@ export type DynamicToolUIPart = {
|
|
|
395
401
|
descriptor?: unknown;
|
|
396
402
|
reason?: string;
|
|
397
403
|
signature?: string;
|
|
404
|
+
inputSchemaInput?: unknown;
|
|
398
405
|
};
|
|
399
406
|
}
|
|
400
407
|
| {
|
|
@@ -411,6 +418,7 @@ export type DynamicToolUIPart = {
|
|
|
411
418
|
descriptor?: unknown;
|
|
412
419
|
reason?: string;
|
|
413
420
|
signature?: string;
|
|
421
|
+
inputSchemaInput?: unknown;
|
|
414
422
|
};
|
|
415
423
|
}
|
|
416
424
|
| {
|
|
@@ -426,6 +434,7 @@ export type DynamicToolUIPart = {
|
|
|
426
434
|
descriptor?: unknown;
|
|
427
435
|
reason?: string;
|
|
428
436
|
signature?: string;
|
|
437
|
+
inputSchemaInput?: unknown;
|
|
429
438
|
};
|
|
430
439
|
}
|
|
431
440
|
| {
|
|
@@ -440,6 +449,7 @@ export type DynamicToolUIPart = {
|
|
|
440
449
|
descriptor?: unknown;
|
|
441
450
|
reason?: string;
|
|
442
451
|
signature?: string;
|
|
452
|
+
inputSchemaInput?: unknown;
|
|
443
453
|
};
|
|
444
454
|
}
|
|
445
455
|
);
|