@ai-sdk/provider-utils 5.0.0-beta.4 → 5.0.0-beta.49
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 +373 -11
- package/dist/index.d.ts +1460 -553
- package/dist/index.js +1044 -361
- package/dist/index.js.map +1 -1
- package/dist/test/index.d.ts +2 -1
- package/dist/test/index.js +18 -37
- package/dist/test/index.js.map +1 -1
- package/package.json +16 -16
- package/src/add-additional-properties-to-json-schema.ts +1 -1
- package/src/as-array.ts +12 -0
- package/src/cancel-response-body.ts +19 -0
- package/src/convert-image-model-file-to-data-uri.ts +1 -1
- package/src/convert-inline-file-data-to-uint8-array.ts +30 -0
- package/src/create-tool-name-mapping.ts +1 -1
- package/src/detect-media-type.ts +312 -0
- package/src/download-blob.ts +8 -9
- package/src/extract-lines.ts +31 -0
- package/src/fetch-with-validated-redirects.ts +87 -0
- package/src/filter-nullable.ts +11 -0
- package/src/get-error-message.ts +1 -15
- package/src/get-from-api.ts +2 -2
- package/src/has-required-key.ts +6 -0
- package/src/index.ts +47 -12
- package/src/inject-json-instruction.ts +1 -1
- package/src/is-browser-runtime.ts +13 -0
- package/src/is-buffer.ts +9 -0
- package/src/is-json-serializable.ts +29 -0
- package/src/is-provider-reference.ts +21 -0
- package/src/is-same-origin.ts +19 -0
- package/src/is-url-supported.ts +17 -2
- package/src/load-api-key.ts +1 -1
- package/src/load-setting.ts +1 -1
- package/src/map-reasoning-to-provider.ts +108 -0
- package/src/maybe-promise-like.ts +3 -0
- package/src/parse-json-event-stream.ts +3 -3
- package/src/parse-json.ts +3 -3
- package/src/parse-provider-options.ts +1 -1
- package/src/post-to-api.ts +4 -4
- package/src/provider-defined-tool-factory.ts +129 -0
- package/src/provider-executed-tool-factory.ts +69 -0
- package/src/read-response-with-size-limit.ts +4 -0
- package/src/resolve-full-media-type.ts +49 -0
- package/src/resolve-provider-reference.ts +26 -0
- package/src/resolve.ts +16 -1
- package/src/response-handler.ts +3 -3
- package/src/schema.ts +11 -4
- package/src/secure-json-parse.ts +1 -1
- package/src/serialize-model-options.ts +63 -0
- package/src/streaming-tool-call-tracker.ts +241 -0
- package/src/test/convert-response-stream-to-array.ts +1 -1
- package/src/test/is-node-version.ts +22 -1
- package/src/to-json-schema/zod3-to-json-schema/options.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parse-def.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parse-types.ts +22 -22
- package/src/to-json-schema/zod3-to-json-schema/parsers/array.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/bigint.ts +1 -1
- package/src/to-json-schema/zod3-to-json-schema/parsers/branded.ts +2 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/catch.ts +2 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/date.ts +4 -4
- package/src/to-json-schema/zod3-to-json-schema/parsers/default.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/effects.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/enum.ts +1 -1
- package/src/to-json-schema/zod3-to-json-schema/parsers/intersection.ts +5 -5
- package/src/to-json-schema/zod3-to-json-schema/parsers/literal.ts +1 -1
- package/src/to-json-schema/zod3-to-json-schema/parsers/map.ts +4 -5
- package/src/to-json-schema/zod3-to-json-schema/parsers/native-enum.ts +1 -1
- package/src/to-json-schema/zod3-to-json-schema/parsers/never.ts +1 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/nullable.ts +4 -4
- package/src/to-json-schema/zod3-to-json-schema/parsers/number.ts +1 -1
- package/src/to-json-schema/zod3-to-json-schema/parsers/object.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/optional.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/pipeline.ts +10 -8
- package/src/to-json-schema/zod3-to-json-schema/parsers/promise.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/readonly.ts +2 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/record.ts +9 -10
- package/src/to-json-schema/zod3-to-json-schema/parsers/set.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/string.ts +2 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/tuple.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/undefined.ts +1 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/union.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/unknown.ts +1 -2
- package/src/to-json-schema/zod3-to-json-schema/refs.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/select-parser.ts +2 -2
- package/src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.ts +3 -3
- package/src/types/assistant-model-message.ts +5 -3
- package/src/types/content-part.ts +158 -19
- package/src/types/context.ts +4 -0
- package/src/types/executable-tool.ts +17 -0
- package/src/types/execute-tool.ts +29 -9
- package/src/types/file-data.ts +48 -0
- package/src/types/index.ts +29 -11
- package/src/types/infer-tool-context.ts +41 -0
- package/src/types/infer-tool-input.ts +7 -0
- package/src/types/infer-tool-output.ts +7 -0
- package/src/types/infer-tool-set-context.ts +44 -0
- package/src/types/model-message.ts +4 -4
- package/src/types/never-optional.ts +7 -0
- package/src/types/provider-options.ts +1 -1
- package/src/types/provider-reference.ts +10 -0
- package/src/types/sandbox.ts +217 -0
- package/src/types/system-model-message.ts +1 -1
- package/src/types/tool-approval-request.ts +13 -0
- package/src/types/tool-execute-function.ts +56 -0
- package/src/types/tool-model-message.ts +3 -3
- package/src/types/tool-needs-approval-function.ts +39 -0
- package/src/types/tool-set.ts +22 -0
- package/src/types/tool.ts +278 -225
- package/src/types/user-model-message.ts +2 -2
- package/src/validate-download-url.ts +120 -33
- package/src/validate-types.ts +5 -3
- package/dist/index.d.mts +0 -1455
- package/dist/index.mjs +0 -2754
- package/dist/index.mjs.map +0 -1
- package/dist/test/index.d.mts +0 -17
- package/dist/test/index.mjs +0 -77
- package/dist/test/index.mjs.map +0 -1
- package/src/provider-tool-factory.ts +0 -125
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { cancelResponseBody } from './cancel-response-body';
|
|
2
|
+
import { DownloadError } from './download-error';
|
|
3
|
+
import { isBrowserRuntime } from './is-browser-runtime';
|
|
4
|
+
import { validateDownloadUrl } from './validate-download-url';
|
|
5
|
+
|
|
6
|
+
const MAX_DOWNLOAD_REDIRECTS = 10;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Fetches a URL while enforcing the SSRF download guard on every hop.
|
|
10
|
+
*
|
|
11
|
+
* Redirects are followed manually (`redirect: 'manual'`) so each hop is
|
|
12
|
+
* validated with {@link validateDownloadUrl} *before* it is requested. Relying
|
|
13
|
+
* on the default `redirect: 'follow'` would issue the request to a redirect
|
|
14
|
+
* target (e.g. an internal address) before we ever see its URL, defeating the
|
|
15
|
+
* SSRF guard.
|
|
16
|
+
*
|
|
17
|
+
* A `redirect: 'manual'` request yields an unreadable opaque response in the
|
|
18
|
+
* browser (and in other spec-compliant fetch implementations), so the redirect
|
|
19
|
+
* target cannot be validated here. In a real browser this is safe to follow
|
|
20
|
+
* natively because SSRF is not reachable (fetch is constrained by CORS and
|
|
21
|
+
* cannot reach a server's internal network or cloud-metadata). On any other
|
|
22
|
+
* runtime we cannot validate the hop, so we fail closed rather than follow it
|
|
23
|
+
* blindly and bypass the SSRF guard.
|
|
24
|
+
*
|
|
25
|
+
* The returned response is the final (non-redirect) response. The caller is
|
|
26
|
+
* responsible for checking `response.ok` and reading the body.
|
|
27
|
+
*
|
|
28
|
+
* @throws DownloadError if a hop is unsafe, the redirect limit is exceeded, or
|
|
29
|
+
* a redirect cannot be validated on a non-browser runtime.
|
|
30
|
+
*/
|
|
31
|
+
export async function fetchWithValidatedRedirects({
|
|
32
|
+
url,
|
|
33
|
+
headers,
|
|
34
|
+
abortSignal,
|
|
35
|
+
maxRedirects = MAX_DOWNLOAD_REDIRECTS,
|
|
36
|
+
}: {
|
|
37
|
+
url: string;
|
|
38
|
+
headers?: HeadersInit;
|
|
39
|
+
abortSignal?: AbortSignal;
|
|
40
|
+
maxRedirects?: number;
|
|
41
|
+
}): Promise<Response> {
|
|
42
|
+
// Per-hop request options. Only the `redirect` mode varies between hops, so
|
|
43
|
+
// the rest is assembled once. `headers` is omitted entirely when not provided
|
|
44
|
+
// so callers that send none issue a bare request.
|
|
45
|
+
const baseInit: RequestInit = { signal: abortSignal };
|
|
46
|
+
if (headers !== undefined) {
|
|
47
|
+
baseInit.headers = headers;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let currentUrl = url;
|
|
51
|
+
// The bound also acts as a backstop against an unterminated redirect chain.
|
|
52
|
+
for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount++) {
|
|
53
|
+
validateDownloadUrl(currentUrl);
|
|
54
|
+
|
|
55
|
+
const response = await fetch(currentUrl, {
|
|
56
|
+
...baseInit,
|
|
57
|
+
redirect: 'manual',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (response.type === 'opaqueredirect') {
|
|
61
|
+
if (!isBrowserRuntime()) {
|
|
62
|
+
throw new DownloadError({
|
|
63
|
+
url,
|
|
64
|
+
message: `Redirect from ${currentUrl} could not be validated and was blocked`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return await fetch(currentUrl, { ...baseInit, redirect: 'follow' });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const location = response.headers.get('location');
|
|
71
|
+
if (response.status >= 300 && response.status < 400 && location) {
|
|
72
|
+
// Release the redirect response's connection before moving to the next
|
|
73
|
+
// hop. Whether that hop is followed or rejected by the SSRF guard, an
|
|
74
|
+
// unconsumed 3xx body would leak the underlying socket.
|
|
75
|
+
await cancelResponseBody(response);
|
|
76
|
+
currentUrl = new URL(location, currentUrl).toString();
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return response;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
throw new DownloadError({
|
|
84
|
+
url,
|
|
85
|
+
message: `Too many redirects (max ${maxRedirects})`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filters `null` and `undefined` values out of a list of values.
|
|
3
|
+
*
|
|
4
|
+
* @param values - The values to filter.
|
|
5
|
+
* @returns A new array containing only non-nullish values.
|
|
6
|
+
*/
|
|
7
|
+
export function filterNullable<T>(
|
|
8
|
+
...values: Array<T | undefined | null>
|
|
9
|
+
): Array<T> {
|
|
10
|
+
return values.filter((value): value is NonNullable<T> => value != null);
|
|
11
|
+
}
|
package/src/get-error-message.ts
CHANGED
|
@@ -1,15 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
if (error == null) {
|
|
3
|
-
return 'unknown error';
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
if (typeof error === 'string') {
|
|
7
|
-
return error;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
if (error instanceof Error) {
|
|
11
|
-
return error.message;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return JSON.stringify(error);
|
|
15
|
-
}
|
|
1
|
+
export { getErrorMessage } from '@ai-sdk/provider';
|
package/src/get-from-api.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { APICallError } from '@ai-sdk/provider';
|
|
2
2
|
import { extractResponseHeaders } from './extract-response-headers';
|
|
3
|
-
import { FetchFunction } from './fetch-function';
|
|
3
|
+
import type { FetchFunction } from './fetch-function';
|
|
4
4
|
import { handleFetchError } from './handle-fetch-error';
|
|
5
5
|
import { isAbortError } from './is-abort-error';
|
|
6
|
-
import { ResponseHandler } from './response-handler';
|
|
6
|
+
import type { ResponseHandler } from './response-handler';
|
|
7
7
|
import { getRuntimeEnvironmentUserAgent } from './get-runtime-environment-user-agent';
|
|
8
8
|
import { withUserAgentSuffix } from './with-user-agent-suffix';
|
|
9
9
|
import { VERSION } from './version';
|
package/src/index.ts
CHANGED
|
@@ -1,32 +1,49 @@
|
|
|
1
|
+
export { asArray } from './as-array';
|
|
2
|
+
export type { Arrayable } from './as-array';
|
|
1
3
|
export * from './combine-headers';
|
|
2
4
|
export { convertAsyncIteratorToReadableStream } from './convert-async-iterator-to-readable-stream';
|
|
5
|
+
export { convertInlineFileDataToUint8Array } from './convert-inline-file-data-to-uint8-array';
|
|
6
|
+
export { convertImageModelFileToDataUri } from './convert-image-model-file-to-data-uri';
|
|
7
|
+
export { convertToFormData } from './convert-to-form-data';
|
|
3
8
|
export {
|
|
4
9
|
createToolNameMapping,
|
|
5
10
|
type ToolNameMapping,
|
|
6
11
|
} from './create-tool-name-mapping';
|
|
7
12
|
export * from './delay';
|
|
8
13
|
export { DelayedPromise } from './delayed-promise';
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
export {
|
|
15
|
+
detectMediaType,
|
|
16
|
+
getTopLevelMediaType,
|
|
17
|
+
isFullMediaType,
|
|
18
|
+
} from './detect-media-type';
|
|
12
19
|
export { downloadBlob } from './download-blob';
|
|
13
20
|
export { DownloadError } from './download-error';
|
|
14
|
-
export {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from './read-response-with-size-limit';
|
|
21
|
+
export { fetchWithValidatedRedirects } from './fetch-with-validated-redirects';
|
|
22
|
+
export { extractLines } from './extract-lines';
|
|
23
|
+
export * from './extract-response-headers';
|
|
18
24
|
export * from './fetch-function';
|
|
25
|
+
export { filterNullable } from './filter-nullable';
|
|
19
26
|
export { createIdGenerator, generateId, type IdGenerator } from './generate-id';
|
|
20
27
|
export * from './get-error-message';
|
|
21
28
|
export * from './get-from-api';
|
|
22
29
|
export { getRuntimeEnvironmentUserAgent } from './get-runtime-environment-user-agent';
|
|
30
|
+
export type { HasRequiredKey } from './has-required-key';
|
|
23
31
|
export { injectJsonInstructionIntoMessages } from './inject-json-instruction';
|
|
24
32
|
export * from './is-abort-error';
|
|
33
|
+
export { isBrowserRuntime } from './is-browser-runtime';
|
|
34
|
+
export { isBuffer } from './is-buffer';
|
|
35
|
+
export { isSameOrigin } from './is-same-origin';
|
|
25
36
|
export { isNonNullable } from './is-non-nullable';
|
|
37
|
+
export { isProviderReference } from './is-provider-reference';
|
|
26
38
|
export { isUrlSupported } from './is-url-supported';
|
|
27
39
|
export * from './load-api-key';
|
|
28
40
|
export { loadOptionalSetting } from './load-optional-setting';
|
|
29
41
|
export { loadSetting } from './load-setting';
|
|
42
|
+
export {
|
|
43
|
+
isCustomReasoning,
|
|
44
|
+
mapReasoningToProviderBudget,
|
|
45
|
+
mapReasoningToProviderEffort,
|
|
46
|
+
} from './map-reasoning-to-provider';
|
|
30
47
|
export { type MaybePromiseLike } from './maybe-promise-like';
|
|
31
48
|
export { mediaTypeToExtension } from './media-type-to-extension';
|
|
32
49
|
export { normalizeHeaders } from './normalize-headers';
|
|
@@ -35,13 +52,24 @@ export { parseJsonEventStream } from './parse-json-event-stream';
|
|
|
35
52
|
export { parseProviderOptions } from './parse-provider-options';
|
|
36
53
|
export * from './post-to-api';
|
|
37
54
|
export {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
type
|
|
41
|
-
type
|
|
42
|
-
} from './provider-tool-factory';
|
|
55
|
+
createProviderDefinedToolFactory,
|
|
56
|
+
createProviderDefinedToolFactoryWithOutputSchema,
|
|
57
|
+
type ProviderDefinedToolFactory,
|
|
58
|
+
type ProviderDefinedToolFactoryWithOutputSchema,
|
|
59
|
+
} from './provider-defined-tool-factory';
|
|
60
|
+
export {
|
|
61
|
+
createProviderExecutedToolFactory,
|
|
62
|
+
type ProviderExecutedToolFactory,
|
|
63
|
+
} from './provider-executed-tool-factory';
|
|
64
|
+
export { cancelResponseBody } from './cancel-response-body';
|
|
65
|
+
export {
|
|
66
|
+
DEFAULT_MAX_DOWNLOAD_SIZE,
|
|
67
|
+
readResponseWithSizeLimit,
|
|
68
|
+
} from './read-response-with-size-limit';
|
|
43
69
|
export * from './remove-undefined-entries';
|
|
44
70
|
export * from './resolve';
|
|
71
|
+
export { resolveFullMediaType } from './resolve-full-media-type';
|
|
72
|
+
export { resolveProviderReference } from './resolve-provider-reference';
|
|
45
73
|
export * from './response-handler';
|
|
46
74
|
export {
|
|
47
75
|
asSchema,
|
|
@@ -54,6 +82,12 @@ export {
|
|
|
54
82
|
type Schema,
|
|
55
83
|
type ValidationResult,
|
|
56
84
|
} from './schema';
|
|
85
|
+
export { serializeModelOptions } from './serialize-model-options';
|
|
86
|
+
export {
|
|
87
|
+
StreamingToolCallTracker,
|
|
88
|
+
type StreamingToolCallDelta,
|
|
89
|
+
type StreamingToolCallTrackerOptions,
|
|
90
|
+
} from './streaming-tool-call-tracker';
|
|
57
91
|
export { stripFileExtension } from './strip-file-extension';
|
|
58
92
|
export * from './uint8-utils';
|
|
59
93
|
export { validateDownloadUrl } from './validate-download-url';
|
|
@@ -67,6 +101,7 @@ export * from './types';
|
|
|
67
101
|
|
|
68
102
|
// external re-exports
|
|
69
103
|
export type * from '@standard-schema/spec';
|
|
104
|
+
export { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from '@workflow/serde';
|
|
70
105
|
export {
|
|
71
106
|
EventSourceParserStream,
|
|
72
107
|
type EventSourceMessage,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns `true` when running in a browser.
|
|
3
|
+
*
|
|
4
|
+
* Detection keys on the presence of a global `window`, matching the browser
|
|
5
|
+
* check used elsewhere in this package (see `getRuntimeEnvironmentUserAgent`)
|
|
6
|
+
* so the SDK has a single, consistent definition of "browser". Server runtimes
|
|
7
|
+
* (Node.js, Deno, Bun, edge/workers) do not define `window`.
|
|
8
|
+
*/
|
|
9
|
+
export function isBrowserRuntime(
|
|
10
|
+
globalThisAny: any = globalThis as any,
|
|
11
|
+
): boolean {
|
|
12
|
+
return globalThisAny.window != null;
|
|
13
|
+
}
|
package/src/is-buffer.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-guard for Node.js `Buffer` instances.
|
|
3
|
+
*
|
|
4
|
+
* Uses optional chaining on `globalThis.Buffer` so it returns `false` in
|
|
5
|
+
* runtimes where `Buffer` is not available (e.g. CloudFlare Workers).
|
|
6
|
+
*/
|
|
7
|
+
export function isBuffer(value: unknown): value is Buffer {
|
|
8
|
+
return globalThis.Buffer?.isBuffer(value) ?? false;
|
|
9
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { JSONValue } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks whether a value can cross a workflow serialization boundary.
|
|
5
|
+
*
|
|
6
|
+
* The check accepts JSON-like primitives, arrays, and plain objects whose
|
|
7
|
+
* nested values are also serializable. It rejects functions, symbols,
|
|
8
|
+
* bigints, and non-plain objects such as class instances, dates, and regexes.
|
|
9
|
+
*/
|
|
10
|
+
export function isJSONSerializable(value: unknown): value is JSONValue {
|
|
11
|
+
if (value === null || value === undefined) return true;
|
|
12
|
+
|
|
13
|
+
const type = typeof value;
|
|
14
|
+
if (type === 'string' || type === 'number' || type === 'boolean') return true;
|
|
15
|
+
if (type === 'function' || type === 'symbol' || type === 'bigint')
|
|
16
|
+
return false;
|
|
17
|
+
|
|
18
|
+
if (Array.isArray(value)) {
|
|
19
|
+
return value.every(isJSONSerializable);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (Object.getPrototypeOf(value) === Object.prototype) {
|
|
23
|
+
return Object.values(value as Record<string, unknown>).every(
|
|
24
|
+
isJSONSerializable,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SharedV4ProviderReference } from '@ai-sdk/provider';
|
|
2
|
+
import { isBuffer } from './is-buffer';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Checks whether a value is a provider reference (a mapping of provider names
|
|
6
|
+
* to provider-specific identifiers) as opposed to raw bytes, a URL, or a
|
|
7
|
+
* tagged `{ type: ... }` object.
|
|
8
|
+
*/
|
|
9
|
+
export function isProviderReference(
|
|
10
|
+
data: unknown,
|
|
11
|
+
): data is SharedV4ProviderReference {
|
|
12
|
+
return (
|
|
13
|
+
typeof data === 'object' &&
|
|
14
|
+
data !== null &&
|
|
15
|
+
!(data instanceof Uint8Array) &&
|
|
16
|
+
!(data instanceof URL) &&
|
|
17
|
+
!(data instanceof ArrayBuffer) &&
|
|
18
|
+
!isBuffer(data) &&
|
|
19
|
+
!('type' in data)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true when `url` has the same origin (scheme + host + port) as
|
|
3
|
+
* `baseUrl`.
|
|
4
|
+
*
|
|
5
|
+
* Used to decide whether provider credentials may be attached to a request to a
|
|
6
|
+
* URL taken from a provider response (e.g. a polling or media-download URL).
|
|
7
|
+
* Credentials must only be sent to the provider's own origin; a response that
|
|
8
|
+
* names a foreign host (a CDN, or an attacker-controlled host if the response
|
|
9
|
+
* is tampered with) must not receive the API key.
|
|
10
|
+
*
|
|
11
|
+
* Returns false if either value is not a valid absolute URL (fail-closed).
|
|
12
|
+
*/
|
|
13
|
+
export function isSameOrigin(url: string, baseUrl: string): boolean {
|
|
14
|
+
try {
|
|
15
|
+
return new URL(url).origin === new URL(baseUrl).origin;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/is-url-supported.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Checks if the given URL is supported natively by the model.
|
|
3
3
|
*
|
|
4
|
-
* @param mediaType - The media type of the URL. Case-sensitive.
|
|
4
|
+
* @param mediaType - The media type of the URL. Case-sensitive. May be a full
|
|
5
|
+
* `type/subtype`, a wildcard `type/*`, or just the
|
|
6
|
+
* top-level segment (e.g. `image`).
|
|
5
7
|
* @param url - The URL to check.
|
|
6
8
|
* @param supportedUrls - A record where keys are case-sensitive media types (or '*')
|
|
7
9
|
* and values are arrays of RegExp patterns for URLs.
|
|
@@ -22,6 +24,8 @@ export function isUrlSupported({
|
|
|
22
24
|
url = url.toLowerCase();
|
|
23
25
|
mediaType = mediaType.toLowerCase();
|
|
24
26
|
|
|
27
|
+
const isTopLevelOnly = !mediaType.includes('/');
|
|
28
|
+
|
|
25
29
|
return (
|
|
26
30
|
Object.entries(supportedUrls)
|
|
27
31
|
// standardize supported url map into lowercase prefixes:
|
|
@@ -32,7 +36,18 @@ export function isUrlSupported({
|
|
|
32
36
|
: { mediaTypePrefix: mediaType.replace(/\*/, ''), regexes: value };
|
|
33
37
|
})
|
|
34
38
|
// gather all regexp pattern from matched media type prefixes:
|
|
35
|
-
.filter(({ mediaTypePrefix }) =>
|
|
39
|
+
.filter(({ mediaTypePrefix }) => {
|
|
40
|
+
if (mediaTypePrefix === '') {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
// For a top-level-only media type (e.g. `image`), we cannot determine
|
|
44
|
+
// whether a specific subtype (e.g. `image/png`) would apply, so we
|
|
45
|
+
// only match the corresponding `type/*` prefix exactly.
|
|
46
|
+
if (isTopLevelOnly) {
|
|
47
|
+
return `${mediaType}/` === mediaTypePrefix;
|
|
48
|
+
}
|
|
49
|
+
return mediaType.startsWith(mediaTypePrefix);
|
|
50
|
+
})
|
|
36
51
|
.flatMap(({ regexes }) => regexes)
|
|
37
52
|
// check if any pattern matches the url:
|
|
38
53
|
.some(pattern => pattern.test(url))
|
package/src/load-api-key.ts
CHANGED
|
@@ -23,7 +23,7 @@ export function loadApiKey({
|
|
|
23
23
|
|
|
24
24
|
if (typeof process === 'undefined') {
|
|
25
25
|
throw new LoadAPIKeyError({
|
|
26
|
-
message: `${description} API key is missing. Pass it using the '${apiKeyParameterName}' parameter. Environment variables
|
|
26
|
+
message: `${description} API key is missing. Pass it using the '${apiKeyParameterName}' parameter. Environment variables are not supported in this environment.`,
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
|
package/src/load-setting.ts
CHANGED
|
@@ -35,7 +35,7 @@ export function loadSetting({
|
|
|
35
35
|
message:
|
|
36
36
|
`${description} setting is missing. ` +
|
|
37
37
|
`Pass it using the '${settingName}' parameter. ` +
|
|
38
|
-
`Environment variables
|
|
38
|
+
`Environment variables are not supported in this environment.`,
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV4CallOptions,
|
|
3
|
+
SharedV4Warning,
|
|
4
|
+
} from '@ai-sdk/provider';
|
|
5
|
+
|
|
6
|
+
export type ReasoningLevel = Exclude<
|
|
7
|
+
LanguageModelV4CallOptions['reasoning'],
|
|
8
|
+
'none' | 'provider-default' | undefined
|
|
9
|
+
>;
|
|
10
|
+
|
|
11
|
+
export function isCustomReasoning(
|
|
12
|
+
reasoning: LanguageModelV4CallOptions['reasoning'],
|
|
13
|
+
): reasoning is Exclude<
|
|
14
|
+
LanguageModelV4CallOptions['reasoning'],
|
|
15
|
+
'provider-default' | undefined
|
|
16
|
+
> {
|
|
17
|
+
return reasoning !== undefined && reasoning !== 'provider-default';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Maps a top-level reasoning level to a provider-specific effort string using
|
|
22
|
+
* the given effort map. Pushes a compatibility warning if the reasoning level
|
|
23
|
+
* maps to a different string, or an unsupported warning if the level is not
|
|
24
|
+
* present in the map.
|
|
25
|
+
*
|
|
26
|
+
* @returns The mapped effort string, or `undefined` if the level is not
|
|
27
|
+
* supported.
|
|
28
|
+
*/
|
|
29
|
+
export function mapReasoningToProviderEffort<T extends string>({
|
|
30
|
+
reasoning,
|
|
31
|
+
effortMap,
|
|
32
|
+
warnings,
|
|
33
|
+
}: {
|
|
34
|
+
reasoning: ReasoningLevel;
|
|
35
|
+
effortMap: Partial<Record<ReasoningLevel, T>>;
|
|
36
|
+
warnings: SharedV4Warning[];
|
|
37
|
+
}): T | undefined {
|
|
38
|
+
const mapped = effortMap[reasoning];
|
|
39
|
+
|
|
40
|
+
if (mapped == null) {
|
|
41
|
+
warnings.push({
|
|
42
|
+
type: 'unsupported',
|
|
43
|
+
feature: 'reasoning',
|
|
44
|
+
details: `reasoning "${reasoning}" is not supported by this model.`,
|
|
45
|
+
});
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (mapped !== reasoning) {
|
|
50
|
+
warnings.push({
|
|
51
|
+
type: 'compatibility',
|
|
52
|
+
feature: 'reasoning',
|
|
53
|
+
details: `reasoning "${reasoning}" is not directly supported by this model. mapped to effort "${mapped}".`,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return mapped;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const DEFAULT_REASONING_BUDGET_PERCENTAGES: Record<ReasoningLevel, number> = {
|
|
61
|
+
minimal: 0.02,
|
|
62
|
+
low: 0.1,
|
|
63
|
+
medium: 0.3,
|
|
64
|
+
high: 0.6,
|
|
65
|
+
xhigh: 0.9,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Maps a top-level reasoning level to an absolute token budget by multiplying
|
|
70
|
+
* the model's max output tokens by a percentage from the budget percentages
|
|
71
|
+
* map. The result is clamped between `minReasoningBudget` (default 1024) and
|
|
72
|
+
* `maxReasoningBudget`. Pushes an unsupported warning if the level is not
|
|
73
|
+
* present in the budget percentages map.
|
|
74
|
+
*
|
|
75
|
+
* @returns The computed token budget, or `undefined` if the level is not
|
|
76
|
+
* supported.
|
|
77
|
+
*/
|
|
78
|
+
export function mapReasoningToProviderBudget({
|
|
79
|
+
reasoning,
|
|
80
|
+
maxOutputTokens,
|
|
81
|
+
maxReasoningBudget,
|
|
82
|
+
minReasoningBudget = 1024,
|
|
83
|
+
budgetPercentages = DEFAULT_REASONING_BUDGET_PERCENTAGES,
|
|
84
|
+
warnings,
|
|
85
|
+
}: {
|
|
86
|
+
reasoning: ReasoningLevel;
|
|
87
|
+
maxOutputTokens: number;
|
|
88
|
+
maxReasoningBudget: number;
|
|
89
|
+
minReasoningBudget?: number;
|
|
90
|
+
budgetPercentages?: Partial<Record<ReasoningLevel, number>>;
|
|
91
|
+
warnings: SharedV4Warning[];
|
|
92
|
+
}): number | undefined {
|
|
93
|
+
const pct = budgetPercentages[reasoning];
|
|
94
|
+
|
|
95
|
+
if (pct == null) {
|
|
96
|
+
warnings.push({
|
|
97
|
+
type: 'unsupported',
|
|
98
|
+
feature: 'reasoning',
|
|
99
|
+
details: `reasoning "${reasoning}" is not supported by this model.`,
|
|
100
|
+
});
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Math.min(
|
|
105
|
+
maxReasoningBudget,
|
|
106
|
+
Math.max(minReasoningBudget, Math.round(maxOutputTokens * pct)),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
EventSourceMessage,
|
|
3
2
|
EventSourceParserStream,
|
|
3
|
+
type EventSourceMessage,
|
|
4
4
|
} from 'eventsource-parser/stream';
|
|
5
|
-
import {
|
|
6
|
-
import { FlexibleSchema } from './schema';
|
|
5
|
+
import { safeParseJSON, type ParseResult } from './parse-json';
|
|
6
|
+
import type { FlexibleSchema } from './schema';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Parses a JSON event stream into a stream of parsed JSON objects.
|
package/src/parse-json.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
JSONParseError,
|
|
3
|
-
JSONValue,
|
|
4
3
|
TypeValidationError,
|
|
4
|
+
type JSONValue,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
6
|
import { secureJsonParse } from './secure-json-parse';
|
|
7
7
|
import { safeValidateTypes, validateTypes } from './validate-types';
|
|
8
|
-
import { FlexibleSchema } from './schema';
|
|
8
|
+
import type { FlexibleSchema } from './schema';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Parses a JSON string into an unknown object.
|
|
@@ -43,7 +43,7 @@ export async function parseJSON<T>({
|
|
|
43
43
|
return value;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
return validateTypes<T>({ value, schema });
|
|
46
|
+
return await validateTypes<T>({ value, schema });
|
|
47
47
|
} catch (error) {
|
|
48
48
|
if (
|
|
49
49
|
JSONParseError.isInstance(error) ||
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InvalidArgumentError } from '@ai-sdk/provider';
|
|
2
2
|
import { safeValidateTypes } from './validate-types';
|
|
3
|
-
import { FlexibleSchema } from './schema';
|
|
3
|
+
import type { FlexibleSchema } from './schema';
|
|
4
4
|
|
|
5
5
|
export async function parseProviderOptions<OPTIONS>({
|
|
6
6
|
provider,
|
package/src/post-to-api.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { APICallError } from '@ai-sdk/provider';
|
|
2
2
|
import { extractResponseHeaders } from './extract-response-headers';
|
|
3
|
-
import { FetchFunction } from './fetch-function';
|
|
3
|
+
import type { FetchFunction } from './fetch-function';
|
|
4
4
|
import { handleFetchError } from './handle-fetch-error';
|
|
5
5
|
import { isAbortError } from './is-abort-error';
|
|
6
|
-
import { ResponseHandler } from './response-handler';
|
|
6
|
+
import type { ResponseHandler } from './response-handler';
|
|
7
7
|
import { getRuntimeEnvironmentUserAgent } from './get-runtime-environment-user-agent';
|
|
8
8
|
import { withUserAgentSuffix } from './with-user-agent-suffix';
|
|
9
9
|
import { VERSION } from './version';
|
|
@@ -28,7 +28,7 @@ export const postJsonToApi = async <T>({
|
|
|
28
28
|
abortSignal?: AbortSignal;
|
|
29
29
|
fetch?: FetchFunction;
|
|
30
30
|
}) =>
|
|
31
|
-
postToApi({
|
|
31
|
+
await postToApi({
|
|
32
32
|
url,
|
|
33
33
|
headers: {
|
|
34
34
|
'Content-Type': 'application/json',
|
|
@@ -61,7 +61,7 @@ export const postFormDataToApi = async <T>({
|
|
|
61
61
|
abortSignal?: AbortSignal;
|
|
62
62
|
fetch?: FetchFunction;
|
|
63
63
|
}) =>
|
|
64
|
-
postToApi({
|
|
64
|
+
await postToApi({
|
|
65
65
|
url,
|
|
66
66
|
headers,
|
|
67
67
|
body: {
|