@ai-sdk/gateway 4.0.0-beta.10 → 4.0.0-beta.109

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 (46) hide show
  1. package/CHANGELOG.md +758 -4
  2. package/dist/index.d.ts +314 -42
  3. package/dist/index.js +1413 -397
  4. package/dist/index.js.map +1 -1
  5. package/docs/00-ai-gateway.mdx +447 -61
  6. package/package.json +14 -14
  7. package/src/errors/as-gateway-error.ts +2 -1
  8. package/src/errors/create-gateway-error.ts +17 -3
  9. package/src/errors/gateway-authentication-error.ts +8 -5
  10. package/src/errors/gateway-error.ts +8 -0
  11. package/src/errors/gateway-failed-dependency-error.ts +35 -0
  12. package/src/errors/gateway-forbidden-error.ts +34 -0
  13. package/src/errors/gateway-response-error.ts +1 -1
  14. package/src/errors/index.ts +2 -0
  15. package/src/errors/parse-auth-method.ts +1 -2
  16. package/src/gateway-config.ts +1 -1
  17. package/src/gateway-embedding-model-settings.ts +1 -0
  18. package/src/gateway-embedding-model.ts +62 -15
  19. package/src/gateway-fetch-metadata.ts +51 -38
  20. package/src/gateway-generation-info.ts +149 -0
  21. package/src/gateway-headers.ts +3 -0
  22. package/src/gateway-image-model-settings.ts +8 -1
  23. package/src/gateway-image-model.ts +46 -21
  24. package/src/gateway-language-model-settings.ts +44 -26
  25. package/src/gateway-language-model.ts +72 -42
  26. package/src/gateway-model-entry.ts +15 -3
  27. package/src/gateway-provider-options.ts +50 -78
  28. package/src/gateway-provider.ts +296 -35
  29. package/src/gateway-realtime-auth.ts +126 -0
  30. package/src/gateway-realtime-model-settings.ts +1 -0
  31. package/src/gateway-realtime-model.ts +118 -0
  32. package/src/gateway-reranking-model-settings.ts +7 -0
  33. package/src/gateway-reranking-model.ts +142 -0
  34. package/src/gateway-speech-model-settings.ts +1 -0
  35. package/src/gateway-speech-model.ts +145 -0
  36. package/src/gateway-spend-report.ts +193 -0
  37. package/src/gateway-transcription-model-settings.ts +1 -0
  38. package/src/gateway-transcription-model.ts +155 -0
  39. package/src/gateway-video-model-settings.ts +4 -0
  40. package/src/gateway-video-model.ts +29 -19
  41. package/src/index.ts +30 -5
  42. package/src/tool/parallel-search.ts +10 -11
  43. package/src/tool/perplexity-search.ts +10 -11
  44. package/dist/index.d.mts +0 -602
  45. package/dist/index.mjs +0 -1539
  46. package/dist/index.mjs.map +0 -1
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@ai-sdk/gateway",
3
3
  "private": false,
4
- "version": "4.0.0-beta.10",
4
+ "version": "4.0.0-beta.109",
5
+ "type": "module",
5
6
  "license": "Apache-2.0",
6
7
  "sideEffects": false,
7
8
  "main": "./dist/index.js",
8
- "module": "./dist/index.mjs",
9
9
  "types": "./dist/index.d.ts",
10
10
  "files": [
11
11
  "dist/**/*",
@@ -25,37 +25,39 @@
25
25
  "./package.json": "./package.json",
26
26
  ".": {
27
27
  "types": "./dist/index.d.ts",
28
- "import": "./dist/index.mjs",
29
- "require": "./dist/index.js"
28
+ "import": "./dist/index.js",
29
+ "default": "./dist/index.js"
30
30
  }
31
31
  },
32
32
  "dependencies": {
33
33
  "@vercel/oidc": "3.2.0",
34
- "@ai-sdk/provider": "4.0.0-beta.0",
35
- "@ai-sdk/provider-utils": "5.0.0-beta.1"
34
+ "@ai-sdk/provider": "4.0.0-beta.19",
35
+ "@ai-sdk/provider-utils": "5.0.0-beta.49"
36
36
  },
37
37
  "devDependencies": {
38
- "@types/node": "18.15.11",
39
- "tsup": "^8",
38
+ "@types/node": "22.19.19",
39
+ "tsup": "^8.5.1",
40
40
  "tsx": "4.19.2",
41
41
  "typescript": "5.8.3",
42
42
  "zod": "3.25.76",
43
- "@ai-sdk/test-server": "2.0.0-beta.0",
43
+ "@ai-sdk/test-server": "2.0.0-beta.7",
44
44
  "@vercel/ai-tsconfig": "0.0.0"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "zod": "^3.25.76 || ^4.1.8"
48
48
  },
49
49
  "engines": {
50
- "node": ">=18"
50
+ "node": ">=22"
51
51
  },
52
52
  "publishConfig": {
53
- "access": "public"
53
+ "access": "public",
54
+ "provenance": true
54
55
  },
55
56
  "homepage": "https://ai-sdk.dev/docs",
56
57
  "repository": {
57
58
  "type": "git",
58
- "url": "git+https://github.com/vercel/ai.git"
59
+ "url": "https://github.com/vercel/ai",
60
+ "directory": "packages/gateway"
59
61
  },
60
62
  "bugs": {
61
63
  "url": "https://github.com/vercel/ai/issues"
@@ -68,9 +70,7 @@
68
70
  "build:watch": "pnpm clean && tsup --watch",
69
71
  "clean": "del-cli dist docs *.tsbuildinfo",
70
72
  "generate-model-settings": "tsx scripts/generate-model-settings.ts",
71
- "lint": "eslint \"./**/*.ts*\"",
72
73
  "type-check": "tsc --build",
73
- "prettier-check": "prettier --check \"./**/*.ts*\"",
74
74
  "test": "pnpm test:node && pnpm test:edge",
75
75
  "test:update": "pnpm test:node -u",
76
76
  "test:watch": "vitest --config vitest.node.config.js",
@@ -1,6 +1,7 @@
1
1
  import { APICallError } from '@ai-sdk/provider';
2
- import { extractApiCallResponse, GatewayError } from '.';
3
2
  import { createGatewayErrorFromResponse } from './create-gateway-error';
3
+ import { extractApiCallResponse } from './extract-api-call-response';
4
+ import { GatewayError } from './gateway-error';
4
5
  import { GatewayTimeoutError } from './gateway-timeout-error';
5
6
 
6
7
  /**
@@ -8,15 +8,15 @@ import {
8
8
  modelNotFoundParamSchema,
9
9
  } from './gateway-model-not-found-error';
10
10
  import { GatewayInternalServerError } from './gateway-internal-server-error';
11
+ import { GatewayFailedDependencyError } from './gateway-failed-dependency-error';
12
+ import { GatewayForbiddenError } from './gateway-forbidden-error';
11
13
  import { GatewayResponseError } from './gateway-response-error';
12
14
  import {
13
- InferSchema,
14
15
  lazySchema,
15
16
  safeValidateTypes,
16
- validateTypes,
17
17
  zodSchema,
18
+ type InferSchema,
18
19
  } from '@ai-sdk/provider-utils';
19
-
20
20
  export async function createGatewayErrorFromResponse({
21
21
  response,
22
22
  statusCode,
@@ -103,6 +103,20 @@ export async function createGatewayErrorFromResponse({
103
103
  cause,
104
104
  generationId,
105
105
  });
106
+ case 'failed_dependency':
107
+ return new GatewayFailedDependencyError({
108
+ message,
109
+ statusCode,
110
+ cause,
111
+ generationId,
112
+ });
113
+ case 'forbidden':
114
+ return new GatewayForbiddenError({
115
+ message,
116
+ statusCode,
117
+ cause,
118
+ generationId,
119
+ });
106
120
  default:
107
121
  return new GatewayInternalServerError({
108
122
  message,
@@ -37,7 +37,6 @@ export class GatewayAuthenticationError extends GatewayError {
37
37
  static createContextualError({
38
38
  apiKeyProvided,
39
39
  oidcTokenProvided,
40
- message = 'Authentication failed',
41
40
  statusCode = 401,
42
41
  cause,
43
42
  generationId,
@@ -52,17 +51,18 @@ export class GatewayAuthenticationError extends GatewayError {
52
51
  let contextualMessage: string;
53
52
 
54
53
  if (apiKeyProvided) {
55
- contextualMessage = `AI Gateway authentication failed: Invalid API key.
54
+ contextualMessage = `AI Gateway authentication failed: Invalid API key or token.
56
55
 
57
56
  Create a new API key: https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys
58
57
 
59
- Provide via 'apiKey' option or 'AI_GATEWAY_API_KEY' environment variable.`;
58
+ Provide an API key or Vercel access token via 'apiKey' option or 'AI_GATEWAY_API_KEY' environment variable.`;
60
59
  } else if (oidcTokenProvided) {
61
60
  contextualMessage = `AI Gateway authentication failed: Invalid OIDC token.
62
61
 
63
62
  Run 'npx vercel link' to link your project, then 'vc env pull' to fetch the token.
64
63
 
65
- Alternatively, use an API key: https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys`;
64
+ Alternatively, use an API key: https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys
65
+ or pass a Vercel access token via the 'apiKey' option.`;
66
66
  } else {
67
67
  contextualMessage = `AI Gateway authentication failed: No authentication provided.
68
68
 
@@ -70,7 +70,10 @@ Option 1 - API key:
70
70
  Create an API key: https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys
71
71
  Provide via 'apiKey' option or 'AI_GATEWAY_API_KEY' environment variable.
72
72
 
73
- Option 2 - OIDC token:
73
+ Option 2 - Vercel access token:
74
+ Pass a Vercel personal access token or Vercel app access token via the 'apiKey' option.
75
+
76
+ Option 3 - OIDC token:
74
77
  Run 'npx vercel link' to link your project, then 'vc env pull' to fetch the token.`;
75
78
  }
76
79
 
@@ -9,22 +9,30 @@ export abstract class GatewayError extends Error {
9
9
  readonly statusCode: number;
10
10
  readonly cause?: unknown;
11
11
  readonly generationId?: string;
12
+ readonly isRetryable: boolean;
12
13
 
13
14
  constructor({
14
15
  message,
15
16
  statusCode = 500,
16
17
  cause,
17
18
  generationId,
19
+ isRetryable = statusCode != null &&
20
+ (statusCode === 408 || // request timeout
21
+ statusCode === 409 || // conflict
22
+ statusCode === 429 || // too many requests
23
+ statusCode >= 500), // server error
18
24
  }: {
19
25
  message: string;
20
26
  statusCode?: number;
21
27
  cause?: unknown;
22
28
  generationId?: string;
29
+ isRetryable?: boolean;
23
30
  }) {
24
31
  super(generationId ? `${message} [${generationId}]` : message);
25
32
  this.statusCode = statusCode;
26
33
  this.cause = cause;
27
34
  this.generationId = generationId;
35
+ this.isRetryable = isRetryable;
28
36
  }
29
37
 
30
38
  /**
@@ -0,0 +1,35 @@
1
+ import { GatewayError } from './gateway-error';
2
+
3
+ const name = 'GatewayFailedDependencyError';
4
+ const marker = `vercel.ai.gateway.error.${name}`;
5
+ const symbol = Symbol.for(marker);
6
+
7
+ /**
8
+ * The request could not be fulfilled because a dependency it relied on was not
9
+ * available on the credentials or provider used to serve it (HTTP 424). Not
10
+ * retryable — the caller must change the request.
11
+ */
12
+ export class GatewayFailedDependencyError extends GatewayError {
13
+ private readonly [symbol] = true; // used in isInstance
14
+
15
+ readonly name = name;
16
+ readonly type = 'failed_dependency';
17
+
18
+ constructor({
19
+ message = 'Failed dependency',
20
+ statusCode = 424,
21
+ cause,
22
+ generationId,
23
+ }: {
24
+ message?: string;
25
+ statusCode?: number;
26
+ cause?: unknown;
27
+ generationId?: string;
28
+ } = {}) {
29
+ super({ message, statusCode, cause, generationId });
30
+ }
31
+
32
+ static isInstance(error: unknown): error is GatewayFailedDependencyError {
33
+ return GatewayError.hasMarker(error) && symbol in error;
34
+ }
35
+ }
@@ -0,0 +1,34 @@
1
+ import { GatewayError } from './gateway-error';
2
+
3
+ const name = 'GatewayForbiddenError';
4
+ const marker = `vercel.ai.gateway.error.${name}`;
5
+ const symbol = Symbol.for(marker);
6
+
7
+ /**
8
+ * Forbidden - the request was rejected by policy (e.g. a routing rule),
9
+ * not an authentication failure.
10
+ */
11
+ export class GatewayForbiddenError extends GatewayError {
12
+ private readonly [symbol] = true; // used in isInstance
13
+
14
+ readonly name = name;
15
+ readonly type = 'forbidden';
16
+
17
+ constructor({
18
+ message = 'Forbidden',
19
+ statusCode = 403,
20
+ cause,
21
+ generationId,
22
+ }: {
23
+ message?: string;
24
+ statusCode?: number;
25
+ cause?: unknown;
26
+ generationId?: string;
27
+ } = {}) {
28
+ super({ message, statusCode, cause, generationId });
29
+ }
30
+
31
+ static isInstance(error: unknown): error is GatewayForbiddenError {
32
+ return GatewayError.hasMarker(error) && symbol in error;
33
+ }
34
+ }
@@ -1,4 +1,4 @@
1
- import { TypeValidationError } from '@ai-sdk/provider';
1
+ import type { TypeValidationError } from '@ai-sdk/provider';
2
2
  import { GatewayError } from './gateway-error';
3
3
 
4
4
  const name = 'GatewayResponseError';
@@ -6,6 +6,8 @@ export {
6
6
  export { extractApiCallResponse } from './extract-api-call-response';
7
7
  export { GatewayError } from './gateway-error';
8
8
  export { GatewayAuthenticationError } from './gateway-authentication-error';
9
+ export { GatewayFailedDependencyError } from './gateway-failed-dependency-error';
10
+ export { GatewayForbiddenError } from './gateway-forbidden-error';
9
11
  export { GatewayInternalServerError } from './gateway-internal-server-error';
10
12
  export { GatewayInvalidRequestError } from './gateway-invalid-request-error';
11
13
  export {
@@ -4,8 +4,7 @@ import {
4
4
  safeValidateTypes,
5
5
  zodSchema,
6
6
  } from '@ai-sdk/provider-utils';
7
-
8
- export const GATEWAY_AUTH_METHOD_HEADER = 'ai-gateway-auth-method' as const;
7
+ import { GATEWAY_AUTH_METHOD_HEADER } from '../gateway-headers';
9
8
 
10
9
  export async function parseAuthMethod(
11
10
  headers: Record<string, string | undefined>,
@@ -2,6 +2,6 @@ import type { FetchFunction, Resolvable } from '@ai-sdk/provider-utils';
2
2
 
3
3
  export type GatewayConfig = {
4
4
  baseURL: string;
5
- headers: () => Resolvable<Record<string, string | undefined>>;
5
+ headers?: Resolvable<Record<string, string | undefined>>;
6
6
  fetch?: FetchFunction;
7
7
  };
@@ -5,6 +5,7 @@ export type GatewayEmbeddingModelId =
5
5
  | 'amazon/titan-embed-text-v2'
6
6
  | 'cohere/embed-v4.0'
7
7
  | 'google/gemini-embedding-001'
8
+ | 'google/gemini-embedding-2'
8
9
  | 'google/text-embedding-005'
9
10
  | 'google/text-multilingual-embedding-002'
10
11
  | 'mistral/codestral-embed'
@@ -1,6 +1,6 @@
1
1
  import type {
2
- EmbeddingModelV3,
3
- SharedV3ProviderMetadata,
2
+ EmbeddingModelV4,
3
+ SharedV4ProviderMetadata,
4
4
  } from '@ai-sdk/provider';
5
5
  import {
6
6
  combineHeaders,
@@ -9,6 +9,9 @@ import {
9
9
  lazySchema,
10
10
  postJsonToApi,
11
11
  resolve,
12
+ serializeModelOptions,
13
+ WORKFLOW_SERIALIZE,
14
+ WORKFLOW_DESERIALIZE,
12
15
  zodSchema,
13
16
  type Resolvable,
14
17
  } from '@ai-sdk/provider-utils';
@@ -17,17 +20,33 @@ import { asGatewayError } from './errors';
17
20
  import { parseAuthMethod } from './errors/parse-auth-method';
18
21
  import type { GatewayConfig } from './gateway-config';
19
22
 
20
- export class GatewayEmbeddingModel implements EmbeddingModelV3 {
21
- readonly specificationVersion = 'v3';
23
+ type GatewayEmbeddingConfig = GatewayConfig & {
24
+ provider: string;
25
+ o11yHeaders: Resolvable<Record<string, string>>;
26
+ };
27
+
28
+ export class GatewayEmbeddingModel implements EmbeddingModelV4 {
29
+ readonly specificationVersion = 'v4';
22
30
  readonly maxEmbeddingsPerCall = 2048;
23
31
  readonly supportsParallelCalls = true;
24
32
 
33
+ static [WORKFLOW_SERIALIZE](model: GatewayEmbeddingModel) {
34
+ return serializeModelOptions({
35
+ modelId: model.modelId,
36
+ config: model.config,
37
+ });
38
+ }
39
+
40
+ static [WORKFLOW_DESERIALIZE](options: {
41
+ modelId: string;
42
+ config: GatewayEmbeddingConfig;
43
+ }) {
44
+ return new GatewayEmbeddingModel(options.modelId, options.config);
45
+ }
46
+
25
47
  constructor(
26
48
  readonly modelId: string,
27
- private readonly config: GatewayConfig & {
28
- provider: string;
29
- o11yHeaders: Resolvable<Record<string, string>>;
30
- },
49
+ private readonly config: GatewayEmbeddingConfig,
31
50
  ) {}
32
51
 
33
52
  get provider(): string {
@@ -39,10 +58,12 @@ export class GatewayEmbeddingModel implements EmbeddingModelV3 {
39
58
  headers,
40
59
  abortSignal,
41
60
  providerOptions,
42
- }: Parameters<EmbeddingModelV3['doEmbed']>[0]): Promise<
43
- Awaited<ReturnType<EmbeddingModelV3['doEmbed']>>
61
+ }: Parameters<EmbeddingModelV4['doEmbed']>[0]): Promise<
62
+ Awaited<ReturnType<EmbeddingModelV4['doEmbed']>>
44
63
  > {
45
- const resolvedHeaders = await resolve(this.config.headers());
64
+ const resolvedHeaders = this.config.headers
65
+ ? await resolve(this.config.headers)
66
+ : undefined;
46
67
  try {
47
68
  const {
48
69
  responseHeaders,
@@ -75,12 +96,15 @@ export class GatewayEmbeddingModel implements EmbeddingModelV3 {
75
96
  embeddings: responseBody.embeddings,
76
97
  usage: responseBody.usage ?? undefined,
77
98
  providerMetadata:
78
- responseBody.providerMetadata as unknown as SharedV3ProviderMetadata,
99
+ responseBody.providerMetadata as unknown as SharedV4ProviderMetadata,
79
100
  response: { headers: responseHeaders, body: rawValue },
80
- warnings: [],
101
+ warnings: responseBody.warnings ?? [],
81
102
  };
82
103
  } catch (error) {
83
- throw await asGatewayError(error, await parseAuthMethod(resolvedHeaders));
104
+ throw await asGatewayError(
105
+ error,
106
+ await parseAuthMethod(resolvedHeaders ?? {}),
107
+ );
84
108
  }
85
109
  }
86
110
 
@@ -90,17 +114,40 @@ export class GatewayEmbeddingModel implements EmbeddingModelV3 {
90
114
 
91
115
  private getModelConfigHeaders() {
92
116
  return {
93
- 'ai-embedding-model-specification-version': '3',
117
+ 'ai-embedding-model-specification-version': '4',
94
118
  'ai-model-id': this.modelId,
95
119
  };
96
120
  }
97
121
  }
98
122
 
123
+ const gatewayEmbeddingWarningSchema = z.discriminatedUnion('type', [
124
+ z.object({
125
+ type: z.literal('unsupported'),
126
+ feature: z.string(),
127
+ details: z.string().optional(),
128
+ }),
129
+ z.object({
130
+ type: z.literal('compatibility'),
131
+ feature: z.string(),
132
+ details: z.string().optional(),
133
+ }),
134
+ z.object({
135
+ type: z.literal('deprecated'),
136
+ setting: z.string(),
137
+ message: z.string(),
138
+ }),
139
+ z.object({
140
+ type: z.literal('other'),
141
+ message: z.string(),
142
+ }),
143
+ ]);
144
+
99
145
  const gatewayEmbeddingResponseSchema = lazySchema(() =>
100
146
  zodSchema(
101
147
  z.object({
102
148
  embeddings: z.array(z.array(z.number())),
103
149
  usage: z.object({ tokens: z.number() }).nullish(),
150
+ warnings: z.array(gatewayEmbeddingWarningSchema).optional(),
104
151
  providerMetadata: z
105
152
  .record(z.string(), z.record(z.string(), z.unknown()))
106
153
  .optional(),
@@ -9,8 +9,11 @@ import {
9
9
  import { z } from 'zod/v4';
10
10
  import { asGatewayError } from './errors';
11
11
  import type { GatewayConfig } from './gateway-config';
12
- import type { GatewayLanguageModelEntry } from './gateway-model-entry';
13
-
12
+ import {
13
+ KNOWN_MODEL_TYPES,
14
+ type GatewayLanguageModelEntry,
15
+ type KnownModelType,
16
+ } from './gateway-model-entry';
14
17
  type GatewayFetchMetadataConfig = GatewayConfig;
15
18
 
16
19
  export interface GatewayFetchMetadataResponse {
@@ -31,7 +34,9 @@ export class GatewayFetchMetadata {
31
34
  try {
32
35
  const { value } = await getFromApi({
33
36
  url: `${this.config.baseURL}/config`,
34
- headers: await resolve(this.config.headers()),
37
+ headers: this.config.headers
38
+ ? await resolve(this.config.headers)
39
+ : undefined,
35
40
  successfulResponseHandler: createJsonResponseHandler(
36
41
  gatewayAvailableModelsResponseSchema,
37
42
  ),
@@ -54,7 +59,9 @@ export class GatewayFetchMetadata {
54
59
 
55
60
  const { value } = await getFromApi({
56
61
  url: `${baseUrl.origin}/v1/credits`,
57
- headers: await resolve(this.config.headers()),
62
+ headers: this.config.headers
63
+ ? await resolve(this.config.headers)
64
+ : undefined,
58
65
  successfulResponseHandler: createJsonResponseHandler(
59
66
  gatewayCreditsResponseSchema,
60
67
  ),
@@ -75,41 +82,47 @@ export class GatewayFetchMetadata {
75
82
  const gatewayAvailableModelsResponseSchema = lazySchema(() =>
76
83
  zodSchema(
77
84
  z.object({
78
- models: z.array(
79
- z.object({
80
- id: z.string(),
81
- name: z.string(),
82
- description: z.string().nullish(),
83
- pricing: z
84
- .object({
85
- input: z.string(),
86
- output: z.string(),
87
- input_cache_read: z.string().nullish(),
88
- input_cache_write: z.string().nullish(),
89
- })
90
- .transform(
91
- ({ input, output, input_cache_read, input_cache_write }) => ({
92
- input,
93
- output,
94
- ...(input_cache_read
95
- ? { cachedInputTokens: input_cache_read }
96
- : {}),
97
- ...(input_cache_write
98
- ? { cacheCreationInputTokens: input_cache_write }
99
- : {}),
100
- }),
101
- )
102
- .nullish(),
103
- specification: z.object({
104
- specificationVersion: z.literal('v3'),
105
- provider: z.string(),
106
- modelId: z.string(),
85
+ models: z
86
+ .array(
87
+ z.object({
88
+ id: z.string(),
89
+ name: z.string(),
90
+ description: z.string().nullish(),
91
+ pricing: z
92
+ .object({
93
+ input: z.string(),
94
+ output: z.string(),
95
+ input_cache_read: z.string().nullish(),
96
+ input_cache_write: z.string().nullish(),
97
+ })
98
+ .transform(
99
+ ({ input, output, input_cache_read, input_cache_write }) => ({
100
+ input,
101
+ output,
102
+ ...(input_cache_read
103
+ ? { cachedInputTokens: input_cache_read }
104
+ : {}),
105
+ ...(input_cache_write
106
+ ? { cacheCreationInputTokens: input_cache_write }
107
+ : {}),
108
+ }),
109
+ )
110
+ .nullish(),
111
+ specification: z.object({
112
+ specificationVersion: z.literal('v4'),
113
+ provider: z.string(),
114
+ modelId: z.string(),
115
+ }),
116
+ modelType: z.string().nullish(),
107
117
  }),
108
- modelType: z
109
- .enum(['embedding', 'image', 'language', 'video'])
110
- .nullish(),
111
- }),
112
- ),
118
+ )
119
+ .transform(models =>
120
+ models.filter(
121
+ (m): m is typeof m & { modelType?: KnownModelType | null } =>
122
+ m.modelType == null ||
123
+ KNOWN_MODEL_TYPES.includes(m.modelType as KnownModelType),
124
+ ),
125
+ ),
113
126
  }),
114
127
  ),
115
128
  );