@ai-sdk/provider-utils 4.0.48 → 4.0.50
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 +12 -0
- package/dist/index.d.mts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +107 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +106 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/embedding-model-capabilities.ts +9 -0
- package/src/handle-fetch-error.ts +44 -12
- package/src/index.ts +1 -0
- package/src/response-handler.ts +78 -2
package/package.json
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol for exposing the UTF-8 input byte budget of an embedding model.
|
|
3
|
+
*
|
|
4
|
+
* This capability is experimental and intentionally lives outside the versioned
|
|
5
|
+
* embedding model specification.
|
|
6
|
+
*/
|
|
7
|
+
export const EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL = Symbol.for(
|
|
8
|
+
'vercel.ai.embeddingModel.maxInputBytesPerCall',
|
|
9
|
+
);
|
|
@@ -3,7 +3,7 @@ import { isAbortError } from './is-abort-error';
|
|
|
3
3
|
|
|
4
4
|
const FETCH_FAILED_ERROR_MESSAGES = ['fetch failed', 'failed to fetch'];
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const RETRYABLE_NETWORK_ERROR_CODES = new Set([
|
|
7
7
|
'ConnectionRefused',
|
|
8
8
|
'ConnectionClosed',
|
|
9
9
|
'FailedToOpenSocket',
|
|
@@ -11,19 +11,33 @@ const BUN_ERROR_CODES = [
|
|
|
11
11
|
'ECONNREFUSED',
|
|
12
12
|
'ETIMEDOUT',
|
|
13
13
|
'EPIPE',
|
|
14
|
-
|
|
14
|
+
'UND_ERR_SOCKET',
|
|
15
|
+
'UND_ERR_HEADERS_TIMEOUT',
|
|
16
|
+
'UND_ERR_BODY_TIMEOUT',
|
|
17
|
+
'UND_ERR_CONNECT_TIMEOUT',
|
|
18
|
+
]);
|
|
15
19
|
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
function findNetworkError(
|
|
21
|
+
error: unknown,
|
|
22
|
+
): (Error & { code?: unknown }) | undefined {
|
|
23
|
+
const visited = new Set<Error>();
|
|
24
|
+
let current = error;
|
|
25
|
+
|
|
26
|
+
while (current instanceof Error && !visited.has(current)) {
|
|
27
|
+
visited.add(current);
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
const errorWithCode = current as Error & { code?: unknown };
|
|
30
|
+
if (
|
|
31
|
+
typeof errorWithCode.code === 'string' &&
|
|
32
|
+
RETRYABLE_NETWORK_ERROR_CODES.has(errorWithCode.code)
|
|
33
|
+
) {
|
|
34
|
+
return errorWithCode;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
current = (current as Error & { cause?: unknown }).cause;
|
|
24
38
|
}
|
|
25
39
|
|
|
26
|
-
return
|
|
40
|
+
return undefined;
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
export function handleFetchError({
|
|
@@ -58,9 +72,27 @@ export function handleFetchError({
|
|
|
58
72
|
}
|
|
59
73
|
}
|
|
60
74
|
|
|
61
|
-
|
|
75
|
+
const networkError = findNetworkError(error);
|
|
76
|
+
|
|
77
|
+
if (networkError != null) {
|
|
78
|
+
if (APICallError.isInstance(error)) {
|
|
79
|
+
return new APICallError({
|
|
80
|
+
message: error.message,
|
|
81
|
+
cause: error.cause,
|
|
82
|
+
url: error.url,
|
|
83
|
+
requestBodyValues: error.requestBodyValues,
|
|
84
|
+
statusCode: error.statusCode,
|
|
85
|
+
responseHeaders: error.responseHeaders,
|
|
86
|
+
responseBody: error.responseBody,
|
|
87
|
+
data: error.data,
|
|
88
|
+
isRetryable: true,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
62
92
|
return new APICallError({
|
|
63
|
-
message: `Cannot connect to API: ${
|
|
93
|
+
message: `Cannot connect to API: ${
|
|
94
|
+
error instanceof Error ? error.message : networkError.message
|
|
95
|
+
}`,
|
|
64
96
|
cause: error,
|
|
65
97
|
url,
|
|
66
98
|
requestBodyValues,
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export {
|
|
|
15
15
|
readResponseWithSizeLimit,
|
|
16
16
|
DEFAULT_MAX_DOWNLOAD_SIZE,
|
|
17
17
|
} from './read-response-with-size-limit';
|
|
18
|
+
export { EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL as EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL } from './embedding-model-capabilities';
|
|
18
19
|
export { fetchWithValidatedRedirects } from './fetch-with-validated-redirects';
|
|
19
20
|
export * from './fetch-function';
|
|
20
21
|
export { createIdGenerator, generateId, type IdGenerator } from './generate-id';
|
package/src/response-handler.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { APICallError, EmptyResponseBodyError } from '@ai-sdk/provider';
|
|
2
2
|
import { extractResponseHeaders } from './extract-response-headers';
|
|
3
|
+
import { handleFetchError } from './handle-fetch-error';
|
|
4
|
+
import { isAbortError } from './is-abort-error';
|
|
3
5
|
import { parseJSON, safeParseJSON, type ParseResult } from './parse-json';
|
|
4
6
|
import { parseJsonEventStream } from './parse-json-event-stream';
|
|
5
7
|
import { readResponseWithSizeLimit } from './read-response-with-size-limit';
|
|
@@ -17,6 +19,74 @@ export type ResponseHandler<RETURN_TYPE> = (options: {
|
|
|
17
19
|
|
|
18
20
|
const textDecoder = new TextDecoder();
|
|
19
21
|
|
|
22
|
+
function wrapResponseBodyStream({
|
|
23
|
+
stream,
|
|
24
|
+
url,
|
|
25
|
+
requestBodyValues,
|
|
26
|
+
statusCode,
|
|
27
|
+
responseHeaders,
|
|
28
|
+
}: {
|
|
29
|
+
stream: ReadableStream<Uint8Array>;
|
|
30
|
+
url: string;
|
|
31
|
+
requestBodyValues: unknown;
|
|
32
|
+
statusCode: number;
|
|
33
|
+
responseHeaders: Record<string, string>;
|
|
34
|
+
}): ReadableStream<Uint8Array> {
|
|
35
|
+
const reader = stream.getReader();
|
|
36
|
+
let readerReleased = false;
|
|
37
|
+
|
|
38
|
+
const releaseReader = () => {
|
|
39
|
+
if (!readerReleased) {
|
|
40
|
+
reader.releaseLock();
|
|
41
|
+
readerReleased = true;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return new ReadableStream<Uint8Array>({
|
|
46
|
+
async pull(controller) {
|
|
47
|
+
try {
|
|
48
|
+
const { done, value } = await reader.read();
|
|
49
|
+
|
|
50
|
+
if (done) {
|
|
51
|
+
releaseReader();
|
|
52
|
+
controller.close();
|
|
53
|
+
} else {
|
|
54
|
+
controller.enqueue(value);
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
releaseReader();
|
|
58
|
+
|
|
59
|
+
if (isAbortError(error)) {
|
|
60
|
+
controller.error(error);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
controller.error(
|
|
65
|
+
handleFetchError({
|
|
66
|
+
error: new APICallError({
|
|
67
|
+
message: 'Failed to process successful response',
|
|
68
|
+
cause: error,
|
|
69
|
+
statusCode,
|
|
70
|
+
url,
|
|
71
|
+
responseHeaders,
|
|
72
|
+
requestBodyValues,
|
|
73
|
+
}),
|
|
74
|
+
url,
|
|
75
|
+
requestBodyValues,
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
async cancel(reason) {
|
|
81
|
+
try {
|
|
82
|
+
await reader.cancel(reason);
|
|
83
|
+
} finally {
|
|
84
|
+
releaseReader();
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
20
90
|
async function readResponseBodyAsText({
|
|
21
91
|
response,
|
|
22
92
|
url,
|
|
@@ -102,7 +172,7 @@ export const createEventSourceResponseHandler =
|
|
|
102
172
|
<T>(
|
|
103
173
|
chunkSchema: FlexibleSchema<T>,
|
|
104
174
|
): ResponseHandler<ReadableStream<ParseResult<T>>> =>
|
|
105
|
-
async ({ response
|
|
175
|
+
async ({ response, url, requestBodyValues }) => {
|
|
106
176
|
const responseHeaders = extractResponseHeaders(response);
|
|
107
177
|
|
|
108
178
|
if (response.body == null) {
|
|
@@ -112,7 +182,13 @@ export const createEventSourceResponseHandler =
|
|
|
112
182
|
return {
|
|
113
183
|
responseHeaders,
|
|
114
184
|
value: parseJsonEventStream({
|
|
115
|
-
stream:
|
|
185
|
+
stream: wrapResponseBodyStream({
|
|
186
|
+
stream: response.body,
|
|
187
|
+
url,
|
|
188
|
+
requestBodyValues,
|
|
189
|
+
statusCode: response.status,
|
|
190
|
+
responseHeaders,
|
|
191
|
+
}),
|
|
116
192
|
schema: chunkSchema,
|
|
117
193
|
}),
|
|
118
194
|
};
|