@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.
Files changed (117) hide show
  1. package/CHANGELOG.md +373 -11
  2. package/dist/index.d.ts +1460 -553
  3. package/dist/index.js +1044 -361
  4. package/dist/index.js.map +1 -1
  5. package/dist/test/index.d.ts +2 -1
  6. package/dist/test/index.js +18 -37
  7. package/dist/test/index.js.map +1 -1
  8. package/package.json +16 -16
  9. package/src/add-additional-properties-to-json-schema.ts +1 -1
  10. package/src/as-array.ts +12 -0
  11. package/src/cancel-response-body.ts +19 -0
  12. package/src/convert-image-model-file-to-data-uri.ts +1 -1
  13. package/src/convert-inline-file-data-to-uint8-array.ts +30 -0
  14. package/src/create-tool-name-mapping.ts +1 -1
  15. package/src/detect-media-type.ts +312 -0
  16. package/src/download-blob.ts +8 -9
  17. package/src/extract-lines.ts +31 -0
  18. package/src/fetch-with-validated-redirects.ts +87 -0
  19. package/src/filter-nullable.ts +11 -0
  20. package/src/get-error-message.ts +1 -15
  21. package/src/get-from-api.ts +2 -2
  22. package/src/has-required-key.ts +6 -0
  23. package/src/index.ts +47 -12
  24. package/src/inject-json-instruction.ts +1 -1
  25. package/src/is-browser-runtime.ts +13 -0
  26. package/src/is-buffer.ts +9 -0
  27. package/src/is-json-serializable.ts +29 -0
  28. package/src/is-provider-reference.ts +21 -0
  29. package/src/is-same-origin.ts +19 -0
  30. package/src/is-url-supported.ts +17 -2
  31. package/src/load-api-key.ts +1 -1
  32. package/src/load-setting.ts +1 -1
  33. package/src/map-reasoning-to-provider.ts +108 -0
  34. package/src/maybe-promise-like.ts +3 -0
  35. package/src/parse-json-event-stream.ts +3 -3
  36. package/src/parse-json.ts +3 -3
  37. package/src/parse-provider-options.ts +1 -1
  38. package/src/post-to-api.ts +4 -4
  39. package/src/provider-defined-tool-factory.ts +129 -0
  40. package/src/provider-executed-tool-factory.ts +69 -0
  41. package/src/read-response-with-size-limit.ts +4 -0
  42. package/src/resolve-full-media-type.ts +49 -0
  43. package/src/resolve-provider-reference.ts +26 -0
  44. package/src/resolve.ts +16 -1
  45. package/src/response-handler.ts +3 -3
  46. package/src/schema.ts +11 -4
  47. package/src/secure-json-parse.ts +1 -1
  48. package/src/serialize-model-options.ts +63 -0
  49. package/src/streaming-tool-call-tracker.ts +241 -0
  50. package/src/test/convert-response-stream-to-array.ts +1 -1
  51. package/src/test/is-node-version.ts +22 -1
  52. package/src/to-json-schema/zod3-to-json-schema/options.ts +3 -3
  53. package/src/to-json-schema/zod3-to-json-schema/parse-def.ts +3 -3
  54. package/src/to-json-schema/zod3-to-json-schema/parse-types.ts +22 -22
  55. package/src/to-json-schema/zod3-to-json-schema/parsers/array.ts +3 -3
  56. package/src/to-json-schema/zod3-to-json-schema/parsers/bigint.ts +1 -1
  57. package/src/to-json-schema/zod3-to-json-schema/parsers/branded.ts +2 -2
  58. package/src/to-json-schema/zod3-to-json-schema/parsers/catch.ts +2 -2
  59. package/src/to-json-schema/zod3-to-json-schema/parsers/date.ts +4 -4
  60. package/src/to-json-schema/zod3-to-json-schema/parsers/default.ts +3 -3
  61. package/src/to-json-schema/zod3-to-json-schema/parsers/effects.ts +3 -3
  62. package/src/to-json-schema/zod3-to-json-schema/parsers/enum.ts +1 -1
  63. package/src/to-json-schema/zod3-to-json-schema/parsers/intersection.ts +5 -5
  64. package/src/to-json-schema/zod3-to-json-schema/parsers/literal.ts +1 -1
  65. package/src/to-json-schema/zod3-to-json-schema/parsers/map.ts +4 -5
  66. package/src/to-json-schema/zod3-to-json-schema/parsers/native-enum.ts +1 -1
  67. package/src/to-json-schema/zod3-to-json-schema/parsers/never.ts +1 -2
  68. package/src/to-json-schema/zod3-to-json-schema/parsers/nullable.ts +4 -4
  69. package/src/to-json-schema/zod3-to-json-schema/parsers/number.ts +1 -1
  70. package/src/to-json-schema/zod3-to-json-schema/parsers/object.ts +3 -3
  71. package/src/to-json-schema/zod3-to-json-schema/parsers/optional.ts +3 -3
  72. package/src/to-json-schema/zod3-to-json-schema/parsers/pipeline.ts +10 -8
  73. package/src/to-json-schema/zod3-to-json-schema/parsers/promise.ts +3 -3
  74. package/src/to-json-schema/zod3-to-json-schema/parsers/readonly.ts +2 -2
  75. package/src/to-json-schema/zod3-to-json-schema/parsers/record.ts +9 -10
  76. package/src/to-json-schema/zod3-to-json-schema/parsers/set.ts +3 -3
  77. package/src/to-json-schema/zod3-to-json-schema/parsers/string.ts +2 -2
  78. package/src/to-json-schema/zod3-to-json-schema/parsers/tuple.ts +3 -3
  79. package/src/to-json-schema/zod3-to-json-schema/parsers/undefined.ts +1 -2
  80. package/src/to-json-schema/zod3-to-json-schema/parsers/union.ts +3 -3
  81. package/src/to-json-schema/zod3-to-json-schema/parsers/unknown.ts +1 -2
  82. package/src/to-json-schema/zod3-to-json-schema/refs.ts +3 -3
  83. package/src/to-json-schema/zod3-to-json-schema/select-parser.ts +2 -2
  84. package/src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.ts +3 -3
  85. package/src/types/assistant-model-message.ts +5 -3
  86. package/src/types/content-part.ts +158 -19
  87. package/src/types/context.ts +4 -0
  88. package/src/types/executable-tool.ts +17 -0
  89. package/src/types/execute-tool.ts +29 -9
  90. package/src/types/file-data.ts +48 -0
  91. package/src/types/index.ts +29 -11
  92. package/src/types/infer-tool-context.ts +41 -0
  93. package/src/types/infer-tool-input.ts +7 -0
  94. package/src/types/infer-tool-output.ts +7 -0
  95. package/src/types/infer-tool-set-context.ts +44 -0
  96. package/src/types/model-message.ts +4 -4
  97. package/src/types/never-optional.ts +7 -0
  98. package/src/types/provider-options.ts +1 -1
  99. package/src/types/provider-reference.ts +10 -0
  100. package/src/types/sandbox.ts +217 -0
  101. package/src/types/system-model-message.ts +1 -1
  102. package/src/types/tool-approval-request.ts +13 -0
  103. package/src/types/tool-execute-function.ts +56 -0
  104. package/src/types/tool-model-message.ts +3 -3
  105. package/src/types/tool-needs-approval-function.ts +39 -0
  106. package/src/types/tool-set.ts +22 -0
  107. package/src/types/tool.ts +278 -225
  108. package/src/types/user-model-message.ts +2 -2
  109. package/src/validate-download-url.ts +120 -33
  110. package/src/validate-types.ts +5 -3
  111. package/dist/index.d.mts +0 -1455
  112. package/dist/index.mjs +0 -2754
  113. package/dist/index.mjs.map +0 -1
  114. package/dist/test/index.d.mts +0 -17
  115. package/dist/test/index.mjs +0 -77
  116. package/dist/test/index.mjs.map +0 -1
  117. 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
+ }
@@ -1,15 +1 @@
1
- export function getErrorMessage(error: unknown | undefined) {
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';
@@ -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';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Checks if an object has required keys.
3
+ * @param OBJECT - The object to check.
4
+ * @returns True if the object has required keys, false otherwise.
5
+ */
6
+ export type HasRequiredKey<OBJECT> = {} extends OBJECT ? false : true;
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 * from './extract-response-headers';
10
- export { convertImageModelFileToDataUri } from './convert-image-model-file-to-data-uri';
11
- export { convertToFormData } from './convert-to-form-data';
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
- readResponseWithSizeLimit,
16
- DEFAULT_MAX_DOWNLOAD_SIZE,
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
- createProviderToolFactory,
39
- createProviderToolFactoryWithOutputSchema,
40
- type ProviderToolFactory,
41
- type ProviderToolFactoryWithOutputSchema,
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,
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  JSONSchema7,
3
3
  LanguageModelV4Message,
4
4
  LanguageModelV4Prompt,
@@ -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
+ }
@@ -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
+ }
@@ -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 }) => mediaType.startsWith(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))
@@ -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 is not supported in this environment.`,
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
 
@@ -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 is not supported in this environment.`,
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,3 +1,6 @@
1
+ /**
2
+ * A value that can be provided either synchronously or as a promise-like.
3
+ */
1
4
  export type MaybePromiseLike<T> =
2
5
  | T // Raw value
3
6
  | PromiseLike<T>; // Promise of value
@@ -1,9 +1,9 @@
1
1
  import {
2
- EventSourceMessage,
3
2
  EventSourceParserStream,
3
+ type EventSourceMessage,
4
4
  } from 'eventsource-parser/stream';
5
- import { ParseResult, safeParseJSON } from './parse-json';
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,
@@ -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: {