ai 7.0.128 → 7.0.129
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 +35 -0
- package/dist/index.d.ts +18 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +148 -32
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +10 -2
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +68 -16
- package/dist/internal/index.js.map +1 -1
- package/docs/02-foundations/06-provider-options.mdx +11 -10
- package/docs/03-agents/02-building-agents.mdx +16 -11
- package/docs/03-agents/07-workflow-agent.mdx +3 -3
- package/docs/03-ai-sdk-core/17-runtime-and-tool-context.mdx +18 -14
- package/docs/03-ai-sdk-core/25-settings.mdx +3 -2
- package/docs/03-ai-sdk-core/26-reasoning.mdx +12 -5
- package/docs/03-ai-sdk-core/32-decisions.mdx +17 -20
- package/docs/03-ai-sdk-core/35-image-generation.mdx +2 -0
- package/docs/04-ai-sdk-ui/21-error-handling.mdx +28 -3
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +2 -2
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +2 -2
- package/package.json +15 -15
- package/src/generate-object/generate-object.ts +10 -0
- package/src/generate-object/stream-object.ts +10 -0
- package/src/generate-speech/index.ts +13 -1
- package/src/generate-text/generate-text.ts +3 -1
- package/src/generate-text/tool-approval-signature.ts +7 -4
- package/src/global.ts +2 -1
- package/src/logger/deprecations.ts +72 -0
- package/src/logger/log-warnings.ts +25 -3
- package/src/model/as-language-model-v3.ts +4 -0
- package/src/prompt/convert-to-language-model-prompt.ts +7 -1
- package/src/realtime/encode-realtime-frame.ts +7 -2
- package/src/transcribe/index.ts +13 -1
- package/src/types/index.ts +1 -0
- package/src/types/language-model.ts +9 -1
- package/src/types/usage.ts +21 -0
- package/src/ui/chat.ts +16 -8
- package/src/ui/convert-to-model-messages.ts +40 -8
- package/src/ui/http-chat-transport.ts +5 -3
- package/src/ui/process-text-stream.ts +5 -1
- package/src/ui/text-stream-chat-transport.ts +3 -1
- package/src/ui/ui-messages.ts +8 -0
- package/src/ui/unavailable-tool.ts +17 -0
- package/src/ui/validate-ui-messages.ts +10 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// These codes are stable identifiers. Do not rename or reuse an existing code.
|
|
2
|
+
const deprecationCodes = new Map<string, string>([
|
|
3
|
+
['generateObject', 'AISDK_DEP_GENERATE_OBJECT'],
|
|
4
|
+
['streamObject', 'AISDK_DEP_STREAM_OBJECT'],
|
|
5
|
+
['experimental_generateSpeech', 'AISDK_DEP_EXPERIMENTAL_GENERATE_SPEECH'],
|
|
6
|
+
['experimental_transcribe', 'AISDK_DEP_EXPERIMENTAL_TRANSCRIBE'],
|
|
7
|
+
['"image" content part', 'AISDK_DEP_IMAGE_CONTENT_PART'],
|
|
8
|
+
[
|
|
9
|
+
'"tool-result" content of type "file-data"',
|
|
10
|
+
'AISDK_DEP_TOOL_RESULT_FILE_DATA',
|
|
11
|
+
],
|
|
12
|
+
[
|
|
13
|
+
'"tool-result" content of type "file-url"',
|
|
14
|
+
'AISDK_DEP_TOOL_RESULT_FILE_URL',
|
|
15
|
+
],
|
|
16
|
+
['"tool-result" content of type "file-id"', 'AISDK_DEP_TOOL_RESULT_FILE_ID'],
|
|
17
|
+
[
|
|
18
|
+
'"tool-result" content of type "file-reference"',
|
|
19
|
+
'AISDK_DEP_TOOL_RESULT_FILE_REFERENCE',
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
'"tool-result" content of type "image-data"',
|
|
23
|
+
'AISDK_DEP_TOOL_RESULT_IMAGE_DATA',
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
'"tool-result" content of type "image-url"',
|
|
27
|
+
'AISDK_DEP_TOOL_RESULT_IMAGE_URL',
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
'"tool-result" content of type "image-file-id"',
|
|
31
|
+
'AISDK_DEP_TOOL_RESULT_IMAGE_FILE_ID',
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
'"tool-result" content of type "image-file-reference"',
|
|
35
|
+
'AISDK_DEP_TOOL_RESULT_IMAGE_FILE_REFERENCE',
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
'rawInput in output-error UI message parts',
|
|
39
|
+
'AISDK_DEP_UI_MESSAGE_RAW_INPUT',
|
|
40
|
+
],
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Escape punctuation and UTF-16 code units without losing case or collapsing
|
|
45
|
+
* distinct settings. Underscores are escaped too, so `__` separates code parts.
|
|
46
|
+
*/
|
|
47
|
+
function encodeCodePart(value: string): string {
|
|
48
|
+
return value.replace(
|
|
49
|
+
/[^a-zA-Z0-9]/g,
|
|
50
|
+
character =>
|
|
51
|
+
`_${character.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getDeprecationCode({
|
|
56
|
+
setting,
|
|
57
|
+
provider,
|
|
58
|
+
}: {
|
|
59
|
+
setting: string;
|
|
60
|
+
provider?: string;
|
|
61
|
+
}): string {
|
|
62
|
+
if (provider == null) {
|
|
63
|
+
return (
|
|
64
|
+
deprecationCodes.get(setting) ??
|
|
65
|
+
`AISDK_DEP_SETTING_${encodeCodePart(setting)}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Provider codes are scoped to the provider, but not to a particular model or
|
|
70
|
+
// message. This works for third-party providers without changing their spec.
|
|
71
|
+
return `AISDK_DEP_PROVIDER_${encodeCodePart(provider)}__${encodeCodePart(setting)}`;
|
|
72
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Warning } from '../types';
|
|
2
|
+
import { getDeprecationCode } from './deprecations';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* A function for logging warnings.
|
|
@@ -87,21 +88,24 @@ export const FIRST_WARNING_INFO_MESSAGE =
|
|
|
87
88
|
'AI SDK Warning System: To turn off warning logging, set the AI_SDK_LOG_WARNINGS global to false.';
|
|
88
89
|
|
|
89
90
|
let hasLoggedBefore = false;
|
|
91
|
+
const emittedDeprecationCodes = new Set<string>();
|
|
90
92
|
|
|
91
93
|
function emitWarning({
|
|
92
94
|
message,
|
|
93
95
|
type,
|
|
96
|
+
code,
|
|
94
97
|
}: {
|
|
95
98
|
message: string;
|
|
96
99
|
type: 'DeprecationWarning' | 'Warning';
|
|
100
|
+
code?: string;
|
|
97
101
|
}) {
|
|
98
102
|
if (
|
|
99
103
|
typeof process !== 'undefined' &&
|
|
100
104
|
typeof process.emitWarning === 'function'
|
|
101
105
|
) {
|
|
102
|
-
process.emitWarning(message, { type });
|
|
106
|
+
process.emitWarning(message, code == null ? { type } : { type, code });
|
|
103
107
|
} else {
|
|
104
|
-
console.warn(message);
|
|
108
|
+
console.warn(code == null ? message : `[${code}] ${message}`);
|
|
105
109
|
}
|
|
106
110
|
}
|
|
107
111
|
|
|
@@ -111,7 +115,9 @@ function emitWarning({
|
|
|
111
115
|
* The behavior can be customized via the `AI_SDK_LOG_WARNINGS` global variable:
|
|
112
116
|
* - If set to `false`, warnings are suppressed.
|
|
113
117
|
* - If set to a function, that function is called with the warnings.
|
|
114
|
-
* - Otherwise, warnings
|
|
118
|
+
* - Otherwise, warnings use `process.emitWarning` when available, falling back
|
|
119
|
+
* to `console.warn`. Deprecations have stable codes and are emitted once per
|
|
120
|
+
* code by the default logger. Custom loggers receive every warning.
|
|
115
121
|
*
|
|
116
122
|
* @param options - The options containing warnings and context.
|
|
117
123
|
* @param options.warnings - The warnings to log.
|
|
@@ -148,6 +154,20 @@ export const logWarnings: LogWarningsFunction = options => {
|
|
|
148
154
|
|
|
149
155
|
// default behavior: log warnings via process.emitWarning if available, otherwise console.warn
|
|
150
156
|
for (const warning of options.warnings) {
|
|
157
|
+
const code =
|
|
158
|
+
warning.type === 'deprecated'
|
|
159
|
+
? getDeprecationCode({
|
|
160
|
+
setting: warning.setting,
|
|
161
|
+
provider: options.provider,
|
|
162
|
+
})
|
|
163
|
+
: undefined;
|
|
164
|
+
if (code != null) {
|
|
165
|
+
if (emittedDeprecationCodes.has(code)) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
emittedDeprecationCodes.add(code);
|
|
169
|
+
}
|
|
170
|
+
|
|
151
171
|
const message = formatWarning({
|
|
152
172
|
warning,
|
|
153
173
|
provider: options.provider,
|
|
@@ -156,6 +176,7 @@ export const logWarnings: LogWarningsFunction = options => {
|
|
|
156
176
|
emitWarning({
|
|
157
177
|
message,
|
|
158
178
|
type: warning.type === 'deprecated' ? 'DeprecationWarning' : 'Warning',
|
|
179
|
+
code,
|
|
159
180
|
});
|
|
160
181
|
}
|
|
161
182
|
};
|
|
@@ -165,4 +186,5 @@ export const logWarnings: LogWarningsFunction = options => {
|
|
|
165
186
|
*/
|
|
166
187
|
export const resetLogWarningsState = () => {
|
|
167
188
|
hasLoggedBefore = false;
|
|
189
|
+
emittedDeprecationCodes.clear();
|
|
168
190
|
};
|
|
@@ -99,5 +99,9 @@ function convertV2UsageToV3(usage: LanguageModelV2Usage): LanguageModelV3Usage {
|
|
|
99
99
|
text: undefined,
|
|
100
100
|
reasoning: usage.reasoningTokens,
|
|
101
101
|
},
|
|
102
|
+
raw:
|
|
103
|
+
usage.totalTokens == null
|
|
104
|
+
? undefined
|
|
105
|
+
: { totalTokens: usage.totalTokens },
|
|
102
106
|
};
|
|
103
107
|
}
|
|
@@ -123,7 +123,13 @@ export async function convertToLanguageModelPrompt({
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
lastCombinedMessage.content.push(...message.content);
|
|
126
|
-
lastCombinedMessage.providerOptions =
|
|
126
|
+
lastCombinedMessage.providerOptions =
|
|
127
|
+
lastContentPart == null
|
|
128
|
+
? mergeObjects(
|
|
129
|
+
lastCombinedMessage.providerOptions,
|
|
130
|
+
message.providerOptions,
|
|
131
|
+
)
|
|
132
|
+
: message.providerOptions;
|
|
127
133
|
} else {
|
|
128
134
|
combinedMessages.push(message);
|
|
129
135
|
}
|
|
@@ -3,14 +3,19 @@ export const REALTIME_MAX_FRAME_BYTES = 128 * 1024;
|
|
|
3
3
|
export const REALTIME_MAX_BUFFERED_BYTES = 128 * 1024;
|
|
4
4
|
|
|
5
5
|
export type EncodedRealtimeFrame = {
|
|
6
|
-
data: string | ArrayBuffer | ArrayBufferView | Blob;
|
|
6
|
+
data: string | ArrayBuffer | ArrayBufferView<ArrayBuffer> | Blob;
|
|
7
7
|
byteLength: number;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
/** Serialize once, measuring the exact payload passed to the browser. */
|
|
11
11
|
export function encodeRealtimeFrame(value: unknown): EncodedRealtimeFrame {
|
|
12
|
-
if (value instanceof ArrayBuffer
|
|
12
|
+
if (value instanceof ArrayBuffer)
|
|
13
13
|
return { data: value, byteLength: value.byteLength };
|
|
14
|
+
if (ArrayBuffer.isView(value))
|
|
15
|
+
return {
|
|
16
|
+
data: value as ArrayBufferView<ArrayBuffer>,
|
|
17
|
+
byteLength: value.byteLength,
|
|
18
|
+
};
|
|
14
19
|
if (value instanceof Blob) return { data: value, byteLength: value.size };
|
|
15
20
|
const data = typeof value === 'string' ? value : JSON.stringify(value);
|
|
16
21
|
if (data === undefined)
|
package/src/transcribe/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TranscriptionResult } from './transcribe-result';
|
|
2
2
|
import { transcribe } from './transcribe';
|
|
3
|
+
import { logWarnings } from '../logger/log-warnings';
|
|
3
4
|
|
|
4
5
|
export { transcribe } from './transcribe';
|
|
5
6
|
export { streamTranscribe as experimental_streamTranscribe } from './stream-transcribe';
|
|
@@ -20,7 +21,18 @@ export type {
|
|
|
20
21
|
/**
|
|
21
22
|
* @deprecated Use `transcribe` instead.
|
|
22
23
|
*/
|
|
23
|
-
const experimental_transcribe =
|
|
24
|
+
const experimental_transcribe: typeof transcribe = options => {
|
|
25
|
+
logWarnings({
|
|
26
|
+
warnings: [
|
|
27
|
+
{
|
|
28
|
+
type: 'deprecated',
|
|
29
|
+
setting: 'experimental_transcribe',
|
|
30
|
+
message: 'Use transcribe instead.',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
return transcribe(options);
|
|
35
|
+
};
|
|
24
36
|
export { experimental_transcribe };
|
|
25
37
|
|
|
26
38
|
/**
|
package/src/types/index.ts
CHANGED
|
@@ -80,11 +80,19 @@ export type FinishReason =
|
|
|
80
80
|
| 'error'
|
|
81
81
|
| 'other';
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Registry for call warning variants. Packages that expose additional warning
|
|
85
|
+
* protocols can augment this interface.
|
|
86
|
+
*/
|
|
87
|
+
export interface CallWarningRegistry {
|
|
88
|
+
sharedV4: SharedV4Warning;
|
|
89
|
+
}
|
|
90
|
+
|
|
83
91
|
/**
|
|
84
92
|
* Warning from the model provider for this call. The call will proceed, but e.g.
|
|
85
93
|
* some settings might not be supported, which can lead to suboptimal results.
|
|
86
94
|
*/
|
|
87
|
-
export type CallWarning =
|
|
95
|
+
export type CallWarning = CallWarningRegistry[keyof CallWarningRegistry];
|
|
88
96
|
|
|
89
97
|
/**
|
|
90
98
|
* A source that has been used as input to generate the response.
|
package/src/types/usage.ts
CHANGED
|
@@ -123,6 +123,12 @@ export function addLanguageModelUsage(
|
|
|
123
123
|
usage1: LanguageModelUsage,
|
|
124
124
|
usage2: LanguageModelUsage,
|
|
125
125
|
): LanguageModelUsage {
|
|
126
|
+
const raw = isNullLanguageModelUsage(usage1)
|
|
127
|
+
? usage2.raw
|
|
128
|
+
: isNullLanguageModelUsage(usage2)
|
|
129
|
+
? usage1.raw
|
|
130
|
+
: undefined;
|
|
131
|
+
|
|
126
132
|
return {
|
|
127
133
|
inputTokens: addTokenCounts(usage1.inputTokens, usage2.inputTokens),
|
|
128
134
|
inputTokenDetails: {
|
|
@@ -151,9 +157,24 @@ export function addLanguageModelUsage(
|
|
|
151
157
|
),
|
|
152
158
|
},
|
|
153
159
|
totalTokens: addTokenCounts(usage1.totalTokens, usage2.totalTokens),
|
|
160
|
+
...(raw == null ? {} : { raw }),
|
|
154
161
|
};
|
|
155
162
|
}
|
|
156
163
|
|
|
164
|
+
function isNullLanguageModelUsage(usage: LanguageModelUsage): boolean {
|
|
165
|
+
return (
|
|
166
|
+
usage.inputTokens == null &&
|
|
167
|
+
usage.inputTokenDetails?.noCacheTokens == null &&
|
|
168
|
+
usage.inputTokenDetails?.cacheReadTokens == null &&
|
|
169
|
+
usage.inputTokenDetails?.cacheWriteTokens == null &&
|
|
170
|
+
usage.outputTokens == null &&
|
|
171
|
+
usage.outputTokenDetails?.textTokens == null &&
|
|
172
|
+
usage.outputTokenDetails?.reasoningTokens == null &&
|
|
173
|
+
usage.totalTokens == null &&
|
|
174
|
+
usage.raw == null
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
157
178
|
function addTokenCounts(
|
|
158
179
|
tokenCount1: number | undefined,
|
|
159
180
|
tokenCount2: number | undefined,
|
package/src/ui/chat.ts
CHANGED
|
@@ -315,7 +315,12 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
315
315
|
status: ChatStatus;
|
|
316
316
|
error?: Error;
|
|
317
317
|
}) {
|
|
318
|
-
if (this.status === status)
|
|
318
|
+
if (this.status === status) {
|
|
319
|
+
if (this.error !== error) {
|
|
320
|
+
this.state.error = error;
|
|
321
|
+
}
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
319
324
|
|
|
320
325
|
this.state.status = status;
|
|
321
326
|
this.state.error = error;
|
|
@@ -1010,13 +1015,16 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
1010
1015
|
|
|
1011
1016
|
isError = true;
|
|
1012
1017
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1018
|
+
if (err instanceof TypeError) {
|
|
1019
|
+
const message = err.message.toLowerCase();
|
|
1020
|
+
|
|
1021
|
+
isDisconnect =
|
|
1022
|
+
// Chromium request failures; Node.js fetch failures.
|
|
1023
|
+
message.includes('fetch') ||
|
|
1024
|
+
// Firefox request failures; Chromium response-body failures.
|
|
1025
|
+
message.includes('network') ||
|
|
1026
|
+
// Safari/WebKit request and response-body failures.
|
|
1027
|
+
message === 'load failed';
|
|
1020
1028
|
}
|
|
1021
1029
|
|
|
1022
1030
|
if (isDisconnect) {
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
type ToolUIPart,
|
|
34
34
|
type UIMessage,
|
|
35
35
|
} from './ui-messages';
|
|
36
|
+
import { isToolPartFromUnavailableTool } from './unavailable-tool';
|
|
36
37
|
import { warnIfUIMessageHasDeprecatedRawInput } from './warn-if-ui-message-has-deprecated-raw-input';
|
|
37
38
|
/**
|
|
38
39
|
* Converts an array of UI messages from useChat into an array of ModelMessages that can be used
|
|
@@ -85,6 +86,39 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
|
|
|
85
86
|
}));
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
async function createModelOutput({
|
|
90
|
+
toolPart,
|
|
91
|
+
toolName,
|
|
92
|
+
output,
|
|
93
|
+
errorMode,
|
|
94
|
+
}: {
|
|
95
|
+
toolPart: ToolUIPart<InferUIMessageTools<UI_MESSAGE>> | DynamicToolUIPart;
|
|
96
|
+
toolName: string;
|
|
97
|
+
output: unknown;
|
|
98
|
+
errorMode: 'none' | 'text' | 'json';
|
|
99
|
+
}) {
|
|
100
|
+
const tool = getOwn(options?.tools, toolName);
|
|
101
|
+
|
|
102
|
+
if (
|
|
103
|
+
errorMode === 'none' &&
|
|
104
|
+
tool == null &&
|
|
105
|
+
isToolPartFromUnavailableTool(toolPart)
|
|
106
|
+
) {
|
|
107
|
+
return {
|
|
108
|
+
type: 'text' as const,
|
|
109
|
+
value: 'Tool output omitted because the tool is no longer available.',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return createToolModelOutput({
|
|
114
|
+
toolCallId: toolPart.toolCallId,
|
|
115
|
+
input: toolPart.input,
|
|
116
|
+
output,
|
|
117
|
+
tool,
|
|
118
|
+
errorMode,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
88
122
|
for (const message of messages) {
|
|
89
123
|
switch (message.role) {
|
|
90
124
|
case 'system': {
|
|
@@ -284,14 +318,13 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
|
|
|
284
318
|
type: 'tool-result',
|
|
285
319
|
toolCallId: part.toolCallId,
|
|
286
320
|
toolName,
|
|
287
|
-
output: await
|
|
288
|
-
|
|
289
|
-
|
|
321
|
+
output: await createModelOutput({
|
|
322
|
+
toolPart: part,
|
|
323
|
+
toolName,
|
|
290
324
|
output:
|
|
291
325
|
part.state === 'output-error'
|
|
292
326
|
? part.errorText
|
|
293
327
|
: part.output,
|
|
294
|
-
tool: getOwn(options?.tools, toolName),
|
|
295
328
|
errorMode:
|
|
296
329
|
part.state === 'output-error' ? 'json' : 'none',
|
|
297
330
|
}),
|
|
@@ -403,14 +436,13 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
|
|
|
403
436
|
type: 'tool-result',
|
|
404
437
|
toolCallId: toolPart.toolCallId,
|
|
405
438
|
toolName,
|
|
406
|
-
output: await
|
|
407
|
-
|
|
408
|
-
|
|
439
|
+
output: await createModelOutput({
|
|
440
|
+
toolPart,
|
|
441
|
+
toolName,
|
|
409
442
|
output:
|
|
410
443
|
toolPart.state === 'output-error'
|
|
411
444
|
? toolPart.errorText
|
|
412
445
|
: toolPart.output,
|
|
413
|
-
tool: getOwn(options?.tools, toolName),
|
|
414
446
|
errorMode:
|
|
415
447
|
toolPart.state === 'output-error' ? 'text' : 'none',
|
|
416
448
|
}),
|
|
@@ -13,10 +13,12 @@ import type { UIMessage } from './ui-messages';
|
|
|
13
13
|
|
|
14
14
|
function appendPathToUrl(url: string, path: string): string {
|
|
15
15
|
const queryOrFragmentStart = url.search(/[?#]/);
|
|
16
|
+
const urlPath =
|
|
17
|
+
queryOrFragmentStart === -1 ? url : url.slice(0, queryOrFragmentStart);
|
|
18
|
+
const suffix =
|
|
19
|
+
queryOrFragmentStart === -1 ? '' : url.slice(queryOrFragmentStart);
|
|
16
20
|
|
|
17
|
-
return
|
|
18
|
-
? `${url}${path}`
|
|
19
|
-
: `${url.slice(0, queryOrFragmentStart)}${path}${url.slice(queryOrFragmentStart)}`;
|
|
21
|
+
return `${urlPath.endsWith('/') && path.startsWith('/') ? urlPath.slice(0, -1) : urlPath}${path}${suffix}`;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
export type PrepareSendMessagesRequest<UI_MESSAGE extends UIMessage> = (
|
|
@@ -5,7 +5,11 @@ export async function processTextStream({
|
|
|
5
5
|
stream: ReadableStream<Uint8Array>;
|
|
6
6
|
onTextPart: (chunk: string) => Promise<void> | void;
|
|
7
7
|
}): Promise<void> {
|
|
8
|
-
const reader = stream
|
|
8
|
+
const reader = stream
|
|
9
|
+
.pipeThrough(
|
|
10
|
+
new TextDecoderStream() as ReadableWritablePair<string, Uint8Array>,
|
|
11
|
+
)
|
|
12
|
+
.getReader();
|
|
9
13
|
while (true) {
|
|
10
14
|
const { done, value } = await reader.read();
|
|
11
15
|
if (done) {
|
|
@@ -17,7 +17,9 @@ export class TextStreamChatTransport<
|
|
|
17
17
|
stream: ReadableStream<Uint8Array<ArrayBufferLike>>,
|
|
18
18
|
): ReadableStream<UIMessageChunk> {
|
|
19
19
|
return transformTextToUiMessageStream({
|
|
20
|
-
stream: stream.pipeThrough(
|
|
20
|
+
stream: stream.pipeThrough(
|
|
21
|
+
new TextDecoderStream() as ReadableWritablePair<string, Uint8Array>,
|
|
22
|
+
),
|
|
21
23
|
});
|
|
22
24
|
}
|
|
23
25
|
}
|
package/src/ui/ui-messages.ts
CHANGED
|
@@ -421,6 +421,14 @@ export type ToolUIPart<TOOLS extends UITools = UITools> = ValueOf<{
|
|
|
421
421
|
export type DynamicToolUIPart = {
|
|
422
422
|
type: 'dynamic-tool';
|
|
423
423
|
|
|
424
|
+
/**
|
|
425
|
+
* `false` when a static tool part was normalized to a dynamic tool part
|
|
426
|
+
* because its current tool schema was unavailable or incompatible.
|
|
427
|
+
*
|
|
428
|
+
* This preserves its static origin across persistence boundaries.
|
|
429
|
+
*/
|
|
430
|
+
dynamic?: false;
|
|
431
|
+
|
|
424
432
|
/**
|
|
425
433
|
* Name of the tool that is being called.
|
|
426
434
|
*/
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DynamicToolUIPart, ToolUIPart } from './ui-messages';
|
|
2
|
+
|
|
3
|
+
export function markToolPartAsUnavailable(
|
|
4
|
+
part: DynamicToolUIPart,
|
|
5
|
+
): DynamicToolUIPart {
|
|
6
|
+
// Preserve the static tool's provenance across persistence boundaries so
|
|
7
|
+
// model-message conversion never falls back to its unfiltered raw output.
|
|
8
|
+
part.dynamic = false;
|
|
9
|
+
|
|
10
|
+
return part;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isToolPartFromUnavailableTool(
|
|
14
|
+
part: ToolUIPart | DynamicToolUIPart,
|
|
15
|
+
): boolean {
|
|
16
|
+
return part.type === 'dynamic-tool' && part.dynamic === false;
|
|
17
|
+
}
|
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
ToolUIPart,
|
|
23
23
|
UIMessage,
|
|
24
24
|
} from './ui-messages';
|
|
25
|
+
import { markToolPartAsUnavailable } from './unavailable-tool';
|
|
25
26
|
import { warnIfUIMessageHasDeprecatedRawInput } from './warn-if-ui-message-has-deprecated-raw-input';
|
|
26
27
|
|
|
27
28
|
const toolMetadataSchema: ZodType<JSONObject> = z.record(
|
|
@@ -43,11 +44,11 @@ function isEmptyObject(value: unknown): value is Record<string, never> {
|
|
|
43
44
|
function asDynamicToolPart(toolPart: ToolUIPart): DynamicToolUIPart {
|
|
44
45
|
const { type, ...part } = toolPart;
|
|
45
46
|
|
|
46
|
-
return {
|
|
47
|
+
return markToolPartAsUnavailable({
|
|
47
48
|
...part,
|
|
48
49
|
type: 'dynamic-tool',
|
|
49
50
|
toolName: type.slice(5),
|
|
50
|
-
} as DynamicToolUIPart;
|
|
51
|
+
} as DynamicToolUIPart);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
function getToolPartInputSchemaInput(
|
|
@@ -148,6 +149,7 @@ const uiMessagesSchema = lazySchema(() => {
|
|
|
148
149
|
}),
|
|
149
150
|
z.object({
|
|
150
151
|
type: z.literal('dynamic-tool'),
|
|
152
|
+
dynamic: z.literal(false).optional(),
|
|
151
153
|
toolName: z.string(),
|
|
152
154
|
toolCallId: z.string(),
|
|
153
155
|
title: z.string().optional(),
|
|
@@ -163,6 +165,7 @@ const uiMessagesSchema = lazySchema(() => {
|
|
|
163
165
|
}),
|
|
164
166
|
z.object({
|
|
165
167
|
type: z.literal('dynamic-tool'),
|
|
168
|
+
dynamic: z.literal(false).optional(),
|
|
166
169
|
toolName: z.string(),
|
|
167
170
|
toolCallId: z.string(),
|
|
168
171
|
title: z.string().optional(),
|
|
@@ -177,6 +180,7 @@ const uiMessagesSchema = lazySchema(() => {
|
|
|
177
180
|
}),
|
|
178
181
|
z.object({
|
|
179
182
|
type: z.literal('dynamic-tool'),
|
|
183
|
+
dynamic: z.literal(false).optional(),
|
|
180
184
|
toolName: z.string(),
|
|
181
185
|
toolCallId: z.string(),
|
|
182
186
|
title: z.string().optional(),
|
|
@@ -191,6 +195,7 @@ const uiMessagesSchema = lazySchema(() => {
|
|
|
191
195
|
}),
|
|
192
196
|
z.object({
|
|
193
197
|
type: z.literal('dynamic-tool'),
|
|
198
|
+
dynamic: z.literal(false).optional(),
|
|
194
199
|
toolName: z.string(),
|
|
195
200
|
toolCallId: z.string(),
|
|
196
201
|
title: z.string().optional(),
|
|
@@ -205,6 +210,7 @@ const uiMessagesSchema = lazySchema(() => {
|
|
|
205
210
|
}),
|
|
206
211
|
z.object({
|
|
207
212
|
type: z.literal('dynamic-tool'),
|
|
213
|
+
dynamic: z.literal(false).optional(),
|
|
208
214
|
toolName: z.string(),
|
|
209
215
|
toolCallId: z.string(),
|
|
210
216
|
title: z.string().optional(),
|
|
@@ -221,6 +227,7 @@ const uiMessagesSchema = lazySchema(() => {
|
|
|
221
227
|
}),
|
|
222
228
|
z.object({
|
|
223
229
|
type: z.literal('dynamic-tool'),
|
|
230
|
+
dynamic: z.literal(false).optional(),
|
|
224
231
|
toolName: z.string(),
|
|
225
232
|
toolCallId: z.string(),
|
|
226
233
|
title: z.string().optional(),
|
|
@@ -237,6 +244,7 @@ const uiMessagesSchema = lazySchema(() => {
|
|
|
237
244
|
}),
|
|
238
245
|
z.object({
|
|
239
246
|
type: z.literal('dynamic-tool'),
|
|
247
|
+
dynamic: z.literal(false).optional(),
|
|
240
248
|
toolName: z.string(),
|
|
241
249
|
toolCallId: z.string(),
|
|
242
250
|
title: z.string().optional(),
|