@opencode/ai 2.0.13 → 2.0.15

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 (86) hide show
  1. package/README.md +115 -56
  2. package/dist/experimental/evaluation-client.d.ts +1 -1
  3. package/dist/experimental/evaluation-client.js +39 -3
  4. package/dist/experimental/evaluation.d.ts +4 -4
  5. package/dist/experimental/evaluation.js +2 -2
  6. package/dist/experimental/system-one.d.ts +3 -3
  7. package/dist/experimental/system-one.js +40 -51
  8. package/dist/generation.d.ts +69 -0
  9. package/dist/generation.js +84 -0
  10. package/dist/image-client.d.ts +6 -4
  11. package/dist/image-client.js +11 -11
  12. package/dist/image.d.ts +1040 -62
  13. package/dist/image.js +80 -52
  14. package/dist/index.d.ts +5 -2
  15. package/dist/index.js +3 -1
  16. package/dist/llm.d.ts +9 -1
  17. package/dist/media.d.ts +212 -0
  18. package/dist/media.js +228 -0
  19. package/dist/promise.d.ts +554 -0
  20. package/dist/promise.js +44 -0
  21. package/dist/protocols/anthropic-messages.js +7 -17
  22. package/dist/protocols/bedrock-converse.d.ts +4 -4
  23. package/dist/protocols/bedrock-converse.js +1 -6
  24. package/dist/protocols/gemini.d.ts +21 -0
  25. package/dist/protocols/gemini.js +42 -6
  26. package/dist/protocols/google-images.d.ts +9 -21
  27. package/dist/protocols/google-images.js +169 -132
  28. package/dist/protocols/meta-images.d.ts +7 -12
  29. package/dist/protocols/meta-images.js +92 -66
  30. package/dist/protocols/mistral-chat.js +7 -6
  31. package/dist/protocols/open-responses.d.ts +11 -3
  32. package/dist/protocols/open-responses.js +23 -12
  33. package/dist/protocols/openai-chat.d.ts +34 -1
  34. package/dist/protocols/openai-chat.js +100 -31
  35. package/dist/protocols/openai-images.d.ts +9 -20
  36. package/dist/protocols/openai-images.js +112 -142
  37. package/dist/protocols/shared.d.ts +17 -17
  38. package/dist/protocols/shared.js +32 -35
  39. package/dist/protocols/utils/bedrock-media.d.ts +2 -3
  40. package/dist/protocols/utils/bedrock-media.js +4 -4
  41. package/dist/protocols/utils/media-input.d.ts +10 -0
  42. package/dist/protocols/utils/media-input.js +17 -0
  43. package/dist/protocols/utils/responses-compaction.js +6 -5
  44. package/dist/protocols/utils/tool-stream.d.ts +27 -3
  45. package/dist/protocols/xai-images.d.ts +9 -15
  46. package/dist/protocols/xai-images.js +85 -83
  47. package/dist/protocols/zai-images.d.ts +9 -13
  48. package/dist/protocols/zai-images.js +59 -57
  49. package/dist/providers/amazon-bedrock.d.ts +2 -2
  50. package/dist/providers/cerebras.js +6 -1
  51. package/dist/providers/deepinfra.js +6 -1
  52. package/dist/providers/google-vertex.d.ts +7 -0
  53. package/dist/providers/google.d.ts +7 -0
  54. package/dist/providers/index.d.ts +1 -0
  55. package/dist/providers/index.js +1 -0
  56. package/dist/providers/openrouter.d.ts +19 -0
  57. package/dist/providers/openrouter.js +13 -1
  58. package/dist/providers/vercel-ai-gateway.d.ts +41 -0
  59. package/dist/providers/vercel-ai-gateway.js +85 -0
  60. package/dist/route/client.d.ts +9 -1
  61. package/dist/route/endpoint.d.ts +10 -10
  62. package/dist/route/executor-service.d.ts +12 -0
  63. package/dist/route/executor-service.js +3 -0
  64. package/dist/route/executor.d.ts +4 -9
  65. package/dist/route/executor.js +3 -3
  66. package/dist/route/index.d.ts +2 -0
  67. package/dist/route/index.js +2 -0
  68. package/dist/route/media-protocol.d.ts +45 -0
  69. package/dist/route/media-protocol.js +40 -0
  70. package/dist/route/media.d.ts +46 -0
  71. package/dist/route/media.js +64 -0
  72. package/dist/schema/errors.d.ts +13 -3
  73. package/dist/schema/errors.js +7 -0
  74. package/dist/schema/events.d.ts +563 -40
  75. package/dist/schema/events.js +35 -2
  76. package/dist/schema/messages.d.ts +98 -8
  77. package/dist/schema/messages.js +8 -6
  78. package/dist/schema/options.d.ts +2 -0
  79. package/dist/schema/options.js +3 -0
  80. package/dist/testing.d.ts +72 -8
  81. package/dist/utils/media-type.d.ts +6 -0
  82. package/dist/utils/media-type.js +49 -0
  83. package/dist/utils/sanitize.js +3 -1
  84. package/package.json +7 -3
  85. package/dist/protocols/utils/image-input.d.ts +0 -21
  86. package/dist/protocols/utils/image-input.js +0 -20
@@ -244,6 +244,14 @@ export declare const finish: <K extends StreamKey>(route: string, tools: State<K
244
244
  };
245
245
  } | undefined;
246
246
  readonly namespace?: string | undefined;
247
+ } | {
248
+ readonly type: "media";
249
+ readonly media: import("../../media.js").Asset;
250
+ readonly providerMetadata?: {
251
+ readonly [x: string]: {
252
+ readonly [x: string]: unknown;
253
+ };
254
+ } | undefined;
247
255
  } | {
248
256
  readonly type: "step-finish";
249
257
  readonly reason: {
@@ -272,12 +280,12 @@ export declare const finish: <K extends StreamKey>(route: string, tools: State<K
272
280
  } | {
273
281
  readonly type: "provider-error";
274
282
  readonly message: string;
283
+ readonly classification?: "context-overflow" | "payload-too-large" | undefined;
275
284
  readonly providerMetadata?: {
276
285
  readonly [x: string]: {
277
286
  readonly [x: string]: unknown;
278
287
  };
279
288
  } | undefined;
280
- readonly classification?: "context-overflow" | "payload-too-large" | undefined;
281
289
  })[];
282
290
  }, AIError, never>;
283
291
  /**
@@ -459,6 +467,14 @@ export declare const finishWithInput: <K extends StreamKey>(route: string, tools
459
467
  };
460
468
  } | undefined;
461
469
  readonly namespace?: string | undefined;
470
+ } | {
471
+ readonly type: "media";
472
+ readonly media: import("../../media.js").Asset;
473
+ readonly providerMetadata?: {
474
+ readonly [x: string]: {
475
+ readonly [x: string]: unknown;
476
+ };
477
+ } | undefined;
462
478
  } | {
463
479
  readonly type: "step-finish";
464
480
  readonly reason: {
@@ -487,12 +503,12 @@ export declare const finishWithInput: <K extends StreamKey>(route: string, tools
487
503
  } | {
488
504
  readonly type: "provider-error";
489
505
  readonly message: string;
506
+ readonly classification?: "context-overflow" | "payload-too-large" | undefined;
490
507
  readonly providerMetadata?: {
491
508
  readonly [x: string]: {
492
509
  readonly [x: string]: unknown;
493
510
  };
494
511
  } | undefined;
495
- readonly classification?: "context-overflow" | "payload-too-large" | undefined;
496
512
  })[];
497
513
  }, AIError, never>;
498
514
  /**
@@ -671,6 +687,14 @@ export declare const finishAll: <K extends StreamKey>(route: string, tools: Stat
671
687
  };
672
688
  } | undefined;
673
689
  readonly namespace?: string | undefined;
690
+ } | {
691
+ readonly type: "media";
692
+ readonly media: import("../../media.js").Asset;
693
+ readonly providerMetadata?: {
694
+ readonly [x: string]: {
695
+ readonly [x: string]: unknown;
696
+ };
697
+ } | undefined;
674
698
  } | {
675
699
  readonly type: "step-finish";
676
700
  readonly reason: {
@@ -699,12 +723,12 @@ export declare const finishAll: <K extends StreamKey>(route: string, tools: Stat
699
723
  } | {
700
724
  readonly type: "provider-error";
701
725
  readonly message: string;
726
+ readonly classification?: "context-overflow" | "payload-too-large" | undefined;
702
727
  readonly providerMetadata?: {
703
728
  readonly [x: string]: {
704
729
  readonly [x: string]: unknown;
705
730
  };
706
731
  } | undefined;
707
- readonly classification?: "context-overflow" | "payload-too-large" | undefined;
708
732
  })[];
709
733
  }, AIError, never>;
710
734
  export * as ToolStream from "./tool-stream.js";
@@ -1,26 +1,20 @@
1
- import { ImageModel } from "../image.js";
2
- import { type Definition as AuthDefinition } from "../route/auth.js";
3
- import { type HttpOptions } from "../schema/index.js";
1
+ import { ImageModel, ImageResponse, type ImageRequestFor } from "../image.js";
2
+ import { MediaProtocol } from "../route/media-protocol.js";
3
+ import { MediaRoute } from "../route/media.js";
4
4
  export declare const DEFAULT_BASE_URL = "https://api.x.ai/v1";
5
5
  export declare const PATH = "/images/generations";
6
6
  export declare const EDIT_PATH = "/images/edits";
7
7
  export type XAIImageString<Known extends string> = Known | (string & {});
8
+ /** Provider-native options. Common fields (`n`, `aspectRatio`, `images`) live on the request. */
8
9
  export type XAIImageOptions = {
9
- readonly n?: number;
10
- readonly aspectRatio?: XAIImageString<"1:1" | "3:4" | "4:3" | "9:16" | "16:9" | "2:3" | "3:2" | "9:19.5" | "19.5:9" | "9:20" | "20:9" | "1:2" | "2:1" | "auto">;
11
- readonly aspect_ratio?: XAIImageString<"1:1" | "3:4" | "4:3" | "9:16" | "16:9" | "2:3" | "3:2" | "9:19.5" | "19.5:9" | "9:20" | "20:9" | "1:2" | "2:1" | "auto">;
12
10
  readonly resolution?: XAIImageString<"1k" | "2k">;
13
11
  readonly responseFormat?: XAIImageString<"url" | "b64_json">;
14
12
  readonly response_format?: XAIImageString<"url" | "b64_json">;
15
13
  } & Record<string, unknown>;
16
- export interface ModelInput {
17
- readonly id: string;
18
- readonly auth: AuthDefinition;
19
- readonly baseURL?: string;
20
- readonly headers?: Record<string, string>;
21
- readonly http?: HttpOptions;
22
- }
23
- export declare const model: (input: ModelInput) => ImageModel<XAIImageOptions>;
14
+ export type Request = ImageRequestFor<XAIImageOptions>;
15
+ export declare const protocol: MediaProtocol.Inline<Request, ImageResponse>;
16
+ export declare const model: (input: MediaRoute.ModelInput) => ImageModel<XAIImageOptions>;
24
17
  export declare const XAIImages: {
25
- readonly model: (input: ModelInput) => ImageModel<XAIImageOptions>;
18
+ readonly protocol: MediaProtocol.Inline<Request, ImageResponse>;
19
+ readonly model: (input: MediaRoute.ModelInput) => ImageModel<XAIImageOptions>;
26
20
  };
@@ -1,14 +1,20 @@
1
- import { Effect, Encoding, Schema } from "effect";
2
- import { Headers, HttpClientRequest } from "effect/unstable/http";
3
- import { GeneratedImage, ImageModel, ImageResponse } from "../image.js";
4
- import { Auth } from "../route/auth.js";
5
- import { Usage, mergeHttpOptions, mergeJsonRecords } from "../schema/index.js";
1
+ import { Effect, Schema } from "effect";
2
+ import { ImageModel, ImageResponse } from "../image.js";
3
+ import { Media } from "../media.js";
4
+ import { MediaProtocol } from "../route/media-protocol.js";
5
+ import { MediaRoute } from "../route/media.js";
6
+ import { ProviderID, mergeJsonRecords } from "../schema/index.js";
6
7
  import { ProviderShared, optionalNull } from "./shared.js";
7
- import { ImageInputs } from "./utils/image-input.js";
8
+ import { MediaInput } from "./utils/media-input.js";
8
9
  const ADAPTER = "xai-images";
10
+ const NAME = "xAI Images";
11
+ const PROVIDER = ProviderID.make("xai");
9
12
  export const DEFAULT_BASE_URL = "https://api.x.ai/v1";
10
13
  export const PATH = "/images/generations";
11
14
  export const EDIT_PATH = "/images/edits";
15
+ // ---------------------------------------------------------------------------
16
+ // 2. Response schema
17
+ // ---------------------------------------------------------------------------
12
18
  const XAIImageResponse = Schema.Struct({
13
19
  data: Schema.Array(Schema.Struct({
14
20
  b64_json: optionalNull(Schema.String),
@@ -18,89 +24,85 @@ const XAIImageResponse = Schema.Struct({
18
24
  })),
19
25
  usage: Schema.optional(Schema.Unknown),
20
26
  });
27
+ // ---------------------------------------------------------------------------
28
+ // 5. Request body construction
29
+ // ---------------------------------------------------------------------------
21
30
  const nativeOptions = (options) => {
22
31
  if (!options)
23
32
  return undefined;
24
- const { aspectRatio, responseFormat, ...native } = options;
25
- return {
26
- aspect_ratio: aspectRatio,
27
- response_format: responseFormat,
28
- ...native,
29
- };
33
+ const { responseFormat, ...native } = options;
34
+ return { response_format: responseFormat, ...native };
30
35
  };
31
- const applyQuery = (url, query) => {
32
- if (!query)
33
- return url;
34
- const next = new URL(url);
35
- Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
36
- return next.toString();
36
+ const isEdit = (request) => (request.images?.length ?? 0) > 0;
37
+ const reference = (asset) => {
38
+ const inline = asset.inline();
39
+ if (inline)
40
+ return Effect.succeed({ url: inline.dataUrl, type: "image_url" });
41
+ const url = ProviderShared.mediaUrl(asset);
42
+ if (url)
43
+ return Effect.succeed({ url, type: "image_url" });
44
+ const id = MediaInput.refID(asset, PROVIDER);
45
+ if (id)
46
+ return Effect.succeed({ file_id: id });
47
+ return Effect.fail(ProviderShared.invalidRequest(`${NAME} accepts image URLs, data URLs, bytes, and xAI file IDs`));
37
48
  };
38
- export const model = (input) => {
39
- const route = {
40
- id: ADAPTER,
41
- generate: Effect.fn("XAIImages.generate")(function* (request, execute) {
42
- const http = mergeHttpOptions(request.model.http, request.http);
43
- const imageReferences = (request.images ?? []).map((image) => {
44
- if (image.type === "bytes")
45
- return { url: ImageInputs.dataUrl(image), type: "image_url" };
46
- if (image.type === "url")
47
- return { url: image.url, type: "image_url" };
48
- if (image.type === "file-id")
49
- return { file_id: image.id };
50
- return undefined;
51
- });
52
- if (imageReferences.some((image) => image === undefined))
53
- return yield* ImageInputs.invalid("xAI Images accepts image URLs, data URLs, bytes, and file IDs");
54
- const requestBody = mergeJsonRecords({
55
- model: request.model.id,
56
- prompt: request.prompt,
57
- image: imageReferences.length === 1 ? imageReferences[0] : undefined,
58
- images: imageReferences.length > 1 ? imageReferences : undefined,
59
- }, nativeOptions(request.options), http?.body);
60
- const text = ProviderShared.encodeJson(requestBody);
61
- const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${imageReferences.length === 0 ? PATH : EDIT_PATH}`, http?.query);
62
- const headers = yield* Auth.toEffect(input.auth)({
63
- request,
64
- method: "POST",
65
- url,
66
- body: text,
67
- headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
68
- });
69
- const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
70
- const output = yield* ProviderShared.imageResponse(ADAPTER, "xAI Images", response);
71
- const decoded = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(XAIImageResponse))(output.body).pipe(Effect.mapError((cause) => output.invalid("xAI Images returned an invalid response", cause)));
72
- const images = yield* Effect.forEach(decoded.data, (item, index) => {
73
- const mediaType = item.mime_type ?? "application/octet-stream";
74
- if (item.b64_json)
75
- return Effect.fromResult(Encoding.decodeBase64(item.b64_json)).pipe(Effect.mapError((cause) => output.invalid(`xAI Images result ${index} contains invalid base64 data`, cause)), Effect.map((data) => new GeneratedImage({
76
- mediaType,
77
- data,
78
- providerMetadata: item.revised_prompt === undefined || item.revised_prompt === null
79
- ? undefined
80
- : { xai: { revisedPrompt: item.revised_prompt } },
81
- })));
82
- if (item.url)
83
- return Effect.succeed(new GeneratedImage({
84
- mediaType,
85
- data: item.url,
86
- providerMetadata: item.revised_prompt === undefined || item.revised_prompt === null
87
- ? undefined
88
- : { xai: { revisedPrompt: item.revised_prompt } },
89
- }));
90
- return Effect.fail(output.invalid(`xAI Images result ${index} has neither image data nor a URL`));
91
- });
92
- if (images.length === 0)
93
- return yield* output.invalid("xAI Images returned no images");
94
- const usage = ProviderShared.isRecord(decoded.usage) ? decoded.usage : undefined;
95
- return new ImageResponse({
96
- images,
97
- usage: usage === undefined ? undefined : new Usage({ providerMetadata: { xai: usage } }),
98
- providerMetadata: usage === undefined ? undefined : { xai: { usage } },
49
+ const fromRequest = Effect.fn("XAIImages.fromRequest")(function* (request) {
50
+ const references = yield* Effect.forEach(request.images ?? [], reference);
51
+ return MediaProtocol.json(mergeJsonRecords({
52
+ model: request.model.id,
53
+ prompt: request.prompt,
54
+ // xAI takes one edit source as `image` and several as `images`.
55
+ image: references.length === 1 ? references[0] : undefined,
56
+ images: references.length > 1 ? references : undefined,
57
+ n: request.n,
58
+ aspect_ratio: request.aspectRatio,
59
+ }, nativeOptions(request.providerOptions), request.http?.body) ?? {});
60
+ });
61
+ // ---------------------------------------------------------------------------
62
+ // 6. Response decoding
63
+ // ---------------------------------------------------------------------------
64
+ const decodeResponse = Effect.fn("XAIImages.decodeResponse")(function* (response) {
65
+ const output = yield* MediaProtocol.decodeJson(ADAPTER, NAME, XAIImageResponse)(response);
66
+ const decoded = output.value;
67
+ const images = yield* Effect.forEach(decoded.data, (item, index) => {
68
+ const providerMetadata = item.revised_prompt === undefined || item.revised_prompt === null
69
+ ? undefined
70
+ : { xai: { revisedPrompt: item.revised_prompt } };
71
+ if (item.b64_json)
72
+ return MediaInput.decodedAsset(output.invalid, `${NAME} result ${index}`, item.b64_json, item.mime_type ?? undefined, {
73
+ providerMetadata,
99
74
  });
100
- }),
101
- };
102
- return ImageModel.make({ id: input.id, provider: "xai", route, http: input.http });
103
- };
75
+ if (item.url)
76
+ return Effect.succeed(Media.url(item.url, { mediaType: item.mime_type ?? undefined, providerMetadata }));
77
+ return Effect.fail(output.invalid(`${NAME} result ${index} has neither image data nor a URL`));
78
+ });
79
+ if (images.length === 0)
80
+ return yield* output.invalid(`${NAME} returned no images`);
81
+ const usage = ProviderShared.isRecord(decoded.usage) ? decoded.usage : undefined;
82
+ // xAI reports image counts rather than tokens, seconds, or credits; the raw record stays in provider metadata.
83
+ return new ImageResponse({
84
+ images,
85
+ providerMetadata: usage === undefined ? undefined : { xai: { usage } },
86
+ });
87
+ });
88
+ // ---------------------------------------------------------------------------
89
+ // 7. Protocol and route
90
+ // ---------------------------------------------------------------------------
91
+ export const protocol = MediaProtocol.inline({
92
+ id: ADAPTER,
93
+ name: NAME,
94
+ unsupported: ["mask", "size", "seed", "format"],
95
+ body: { from: fromRequest },
96
+ response: { decode: decodeResponse },
97
+ });
98
+ export const model = (input) => ImageModel.fromRoute({
99
+ id: ADAPTER,
100
+ provider: PROVIDER,
101
+ protocol,
102
+ baseURL: DEFAULT_BASE_URL,
103
+ path: ({ request }) => (isEdit(request) ? EDIT_PATH : PATH),
104
+ }, input);
104
105
  export const XAIImages = {
106
+ protocol,
105
107
  model,
106
108
  };
@@ -1,22 +1,18 @@
1
- import { ImageModel } from "../image.js";
2
- import { type Definition as AuthDefinition } from "../route/auth.js";
3
- import { type HttpOptions } from "../schema/index.js";
1
+ import { ImageModel, ImageResponse, type ImageRequestFor } from "../image.js";
2
+ import { MediaProtocol } from "../route/media-protocol.js";
3
+ import { MediaRoute } from "../route/media.js";
4
4
  export declare const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4";
5
5
  export declare const PATH = "/images/generations";
6
6
  export type ZAIImageString<Known extends string> = Known | (string & {});
7
+ /** Provider-native options. The common `size` field lives on the request. */
7
8
  export type ZAIImageOptions = {
8
- readonly size?: ZAIImageString<"1024x1024" | "768x1344" | "864x1152" | "1344x768" | "1152x864" | "1440x720" | "720x1440">;
9
9
  readonly quality?: ZAIImageString<"hd" | "standard">;
10
10
  readonly userID?: string;
11
11
  } & Record<string, unknown>;
12
- export interface ModelInput {
13
- readonly id: string;
14
- readonly auth: AuthDefinition;
15
- readonly baseURL?: string;
16
- readonly headers?: Record<string, string>;
17
- readonly http?: HttpOptions;
18
- }
19
- export declare const model: (input: ModelInput) => ImageModel<ZAIImageOptions>;
12
+ export type Request = ImageRequestFor<ZAIImageOptions>;
13
+ export declare const protocol: MediaProtocol.Inline<Request, ImageResponse>;
14
+ export declare const model: (input: MediaRoute.ModelInput) => ImageModel<ZAIImageOptions>;
20
15
  export declare const ZAIImages: {
21
- readonly model: (input: ModelInput) => ImageModel<ZAIImageOptions>;
16
+ readonly protocol: MediaProtocol.Inline<Request, ImageResponse>;
17
+ readonly model: (input: MediaRoute.ModelInput) => ImageModel<ZAIImageOptions>;
22
18
  };
@@ -1,13 +1,17 @@
1
1
  import { Effect, Schema } from "effect";
2
- import { Headers, HttpClientRequest } from "effect/unstable/http";
3
- import { GeneratedImage, ImageModel, ImageResponse } from "../image.js";
4
- import { Auth } from "../route/auth.js";
5
- import { mergeHttpOptions, mergeJsonRecords } from "../schema/index.js";
6
- import { ProviderShared } from "./shared.js";
7
- import { ImageInputs } from "./utils/image-input.js";
2
+ import { ImageModel, ImageResponse } from "../image.js";
3
+ import { Media } from "../media.js";
4
+ import { MediaProtocol } from "../route/media-protocol.js";
5
+ import { MediaRoute } from "../route/media.js";
6
+ import { ProviderID, mergeJsonRecords } from "../schema/index.js";
8
7
  const ADAPTER = "zai-images";
8
+ const NAME = "Z.ai Images";
9
+ const PROVIDER = ProviderID.make("zai");
9
10
  export const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4";
10
11
  export const PATH = "/images/generations";
12
+ // ---------------------------------------------------------------------------
13
+ // 2. Response schema
14
+ // ---------------------------------------------------------------------------
11
15
  const ZAIImageResponse = Schema.Struct({
12
16
  created: Schema.optional(Schema.Int),
13
17
  id: Schema.optional(Schema.String),
@@ -18,62 +22,60 @@ const ZAIImageResponse = Schema.Struct({
18
22
  level: Schema.optional(Schema.Number),
19
23
  }))),
20
24
  });
25
+ // ---------------------------------------------------------------------------
26
+ // 5. Request body construction
27
+ // ---------------------------------------------------------------------------
21
28
  const nativeOptions = (options) => {
22
29
  if (!options)
23
30
  return undefined;
24
31
  const { userID, ...native } = options;
25
- return {
26
- user_id: userID,
27
- ...native,
28
- };
29
- };
30
- const applyQuery = (url, query) => {
31
- if (!query)
32
- return url;
33
- const next = new URL(url);
34
- Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
35
- return next.toString();
36
- };
37
- export const model = (input) => {
38
- const route = {
39
- id: ADAPTER,
40
- generate: Effect.fn("ZAIImages.generate")(function* (request, execute) {
41
- if ((request.images?.length ?? 0) > 0)
42
- return yield* ImageInputs.invalid("Z.ai hosted image generation does not support image inputs");
43
- const http = mergeHttpOptions(request.model.http, request.http);
44
- const requestBody = mergeJsonRecords({ model: request.model.id, prompt: request.prompt }, nativeOptions(request.options), http?.body);
45
- const text = ProviderShared.encodeJson(requestBody);
46
- const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${PATH}`, http?.query);
47
- const headers = yield* Auth.toEffect(input.auth)({
48
- request,
49
- method: "POST",
50
- url,
51
- body: text,
52
- headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
53
- });
54
- const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
55
- const output = yield* ProviderShared.imageResponse(ADAPTER, "Z.ai Images", response);
56
- const decoded = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(ZAIImageResponse))(output.body).pipe(Effect.mapError((cause) => output.invalid("Z.ai Images returned an invalid response", cause)));
57
- if (decoded.data.length === 0)
58
- return yield* output.invalid("Z.ai Images returned no images");
59
- return new ImageResponse({
60
- images: decoded.data.map((item) => new GeneratedImage({
61
- mediaType: "application/octet-stream",
62
- data: item.url,
63
- })),
64
- providerMetadata: {
65
- zai: {
66
- created: decoded.created,
67
- id: decoded.id,
68
- requestID: decoded.request_id,
69
- contentFilter: decoded.content_filter,
70
- },
71
- },
72
- });
73
- }),
74
- };
75
- return ImageModel.make({ id: input.id, provider: "zai", route, http: input.http });
32
+ return { user_id: userID, ...native };
76
33
  };
34
+ const fromRequest = Effect.fn("ZAIImages.fromRequest")(function* (request) {
35
+ return MediaProtocol.json(mergeJsonRecords({ model: request.model.id, prompt: request.prompt, size: request.size }, nativeOptions(request.providerOptions), request.http?.body) ?? {});
36
+ });
37
+ // ---------------------------------------------------------------------------
38
+ // 6. Response decoding
39
+ // ---------------------------------------------------------------------------
40
+ const decodeResponse = Effect.fn("ZAIImages.decodeResponse")(function* (response) {
41
+ const output = yield* MediaProtocol.decodeJson(ADAPTER, NAME, ZAIImageResponse)(response);
42
+ const decoded = output.value;
43
+ if (decoded.data.length === 0)
44
+ return yield* output.invalid(`${NAME} returned no images`);
45
+ const filters = decoded.content_filter ?? [];
46
+ return new ImageResponse({
47
+ // Z.ai returns only URLs and no content type; the media type resolves when the asset is materialized.
48
+ images: decoded.data.map((item) => Media.url(item.url)),
49
+ // Z.ai reports applied content filters alongside a successful result; surface them instead of dropping them.
50
+ notices: filters.length === 0
51
+ ? undefined
52
+ : filters.map((filter) => ({
53
+ type: "moderated",
54
+ message: `${NAME} applied a content filter${filter.role === undefined ? "" : ` for ${filter.role}`}${filter.level === undefined ? "" : ` at level ${filter.level}`}`,
55
+ providerMetadata: { zai: filter },
56
+ })),
57
+ providerMetadata: {
58
+ zai: {
59
+ created: decoded.created,
60
+ id: decoded.id,
61
+ requestID: decoded.request_id,
62
+ contentFilter: decoded.content_filter,
63
+ },
64
+ },
65
+ });
66
+ });
67
+ // ---------------------------------------------------------------------------
68
+ // 7. Protocol and route
69
+ // ---------------------------------------------------------------------------
70
+ export const protocol = MediaProtocol.inline({
71
+ id: ADAPTER,
72
+ name: NAME,
73
+ unsupported: ["images", "mask", "n", "aspectRatio", "seed", "format"],
74
+ body: { from: fromRequest },
75
+ response: { decode: decodeResponse },
76
+ });
77
+ export const model = (input) => ImageModel.fromRoute({ id: ADAPTER, provider: PROVIDER, protocol, baseURL: DEFAULT_BASE_URL, path: PATH }, input);
77
78
  export const ZAIImages = {
79
+ protocol,
78
80
  model,
79
81
  };
@@ -44,7 +44,7 @@ export declare const routes: import("../route/client.js").Route<{
44
44
  };
45
45
  } | {
46
46
  readonly document: {
47
- readonly format: "pdf" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html" | "txt" | "md";
47
+ readonly format: "pdf" | "txt" | "md" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html";
48
48
  readonly name: string;
49
49
  readonly source: {
50
50
  readonly bytes: string;
@@ -63,7 +63,7 @@ export declare const routes: import("../route/client.js").Route<{
63
63
  };
64
64
  } | {
65
65
  readonly document: {
66
- readonly format: "pdf" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html" | "txt" | "md";
66
+ readonly format: "pdf" | "txt" | "md" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html";
67
67
  readonly name: string;
68
68
  readonly source: {
69
69
  readonly bytes: string;
@@ -25,7 +25,12 @@ export const configure = (input = {}) => {
25
25
  id,
26
26
  model: (modelID) => configured.model({
27
27
  id: modelID,
28
- compatibility: { maxTokensField: "max_tokens", reasoningField: "reasoning", supportsStore: false, supportsPromptCacheKey: true },
28
+ compatibility: {
29
+ maxTokensField: "max_tokens",
30
+ reasoningField: "reasoning",
31
+ supportsStore: false,
32
+ supportsPromptCacheKey: true,
33
+ },
29
34
  }),
30
35
  configure,
31
36
  };
@@ -28,7 +28,12 @@ export const configure = (input = {}) => {
28
28
  id,
29
29
  model: (modelID) => configured.model({
30
30
  id: modelID,
31
- compatibility: { maxTokensField: "max_tokens", reasoningField: "reasoning_content", supportsStore: false, supportsPromptCacheKey: true },
31
+ compatibility: {
32
+ maxTokensField: "max_tokens",
33
+ reasoningField: "reasoning_content",
34
+ supportsStore: false,
35
+ supportsPromptCacheKey: true,
36
+ },
32
37
  }),
33
38
  configure,
34
39
  };
@@ -32,10 +32,16 @@ export declare const routes: Route<{
32
32
  readonly mimeType: string;
33
33
  readonly data: string;
34
34
  };
35
+ readonly thoughtSignature?: string | null | undefined;
35
36
  } | {
36
37
  readonly text: string;
37
38
  readonly thought?: boolean | null | undefined;
38
39
  readonly thoughtSignature?: string | null | undefined;
40
+ } | {
41
+ readonly fileData: {
42
+ readonly mimeType: string;
43
+ readonly fileUri: string;
44
+ };
39
45
  } | {
40
46
  readonly functionCall: {
41
47
  readonly name: string;
@@ -53,6 +59,7 @@ export declare const routes: Route<{
53
59
  readonly mimeType: string;
54
60
  readonly data: string;
55
61
  };
62
+ readonly thoughtSignature?: string | null | undefined;
56
63
  }[] | undefined;
57
64
  };
58
65
  })[] | null | undefined;
@@ -14,10 +14,16 @@ export declare const routes: import("../route/client.js").Route<{
14
14
  readonly mimeType: string;
15
15
  readonly data: string;
16
16
  };
17
+ readonly thoughtSignature?: string | null | undefined;
17
18
  } | {
18
19
  readonly text: string;
19
20
  readonly thought?: boolean | null | undefined;
20
21
  readonly thoughtSignature?: string | null | undefined;
22
+ } | {
23
+ readonly fileData: {
24
+ readonly mimeType: string;
25
+ readonly fileUri: string;
26
+ };
21
27
  } | {
22
28
  readonly functionCall: {
23
29
  readonly name: string;
@@ -35,6 +41,7 @@ export declare const routes: import("../route/client.js").Route<{
35
41
  readonly mimeType: string;
36
42
  readonly data: string;
37
43
  };
44
+ readonly thoughtSignature?: string | null | undefined;
38
45
  }[] | undefined;
39
46
  };
40
47
  })[] | null | undefined;
@@ -28,6 +28,7 @@ export * as OpenCodeZen from "./opencode-zen.js";
28
28
  export * as OpenRouter from "./openrouter.js";
29
29
  export * as TogetherAI from "./togetherai.js";
30
30
  export * as TypeSafeAI from "./typesafe-ai.js";
31
+ export * as VercelAIGateway from "./vercel-ai-gateway.js";
31
32
  export * as XAI from "./xai.js";
32
33
  export * as ZAI from "./zai.js";
33
34
  export * as ZAICodingPlan from "./zai-coding-plan.js";
@@ -28,6 +28,7 @@ export * as OpenCodeZen from "./opencode-zen.js";
28
28
  export * as OpenRouter from "./openrouter.js";
29
29
  export * as TogetherAI from "./togetherai.js";
30
30
  export * as TypeSafeAI from "./typesafe-ai.js";
31
+ export * as VercelAIGateway from "./vercel-ai-gateway.js";
31
32
  export * as XAI from "./xai.js";
32
33
  export * as ZAI from "./zai.js";
33
34
  export * as ZAICodingPlan from "./zai-coding-plan.js";
@@ -73,6 +73,13 @@ export interface OpenRouterOptions {
73
73
  }>;
74
74
  }
75
75
  export type OpenRouterProviderOptionsInput = OpenRouterOptions;
76
+ export interface OpenRouterEvaluationOptions {
77
+ readonly [key: string]: unknown;
78
+ readonly provider?: OpenRouterProviderRouting;
79
+ readonly session_id?: string;
80
+ readonly trace?: Readonly<Record<string, unknown>>;
81
+ readonly user?: string;
82
+ }
76
83
  export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> & ProviderAuthOption<"optional"> & {
77
84
  readonly baseURL?: string;
78
85
  readonly providerOptions?: OpenRouterProviderOptionsInput;
@@ -548,16 +555,28 @@ export declare const routes: Route<{
548
555
  export declare const configure: (input?: LanguageModelOptions) => {
549
556
  id: string & import("effect/Brand").Brand<"AI.ProviderID">;
550
557
  model: (modelID: string | ModelID) => import("../schema/options.js").LanguageModel<OpenRouterOptions, import("../route/client.js").CompactionOperations | undefined>;
558
+ experimental: {
559
+ evaluation: (modelID: string | ModelID) => import("../experimental.js").EvaluationModel<OpenRouterEvaluationOptions>;
560
+ };
551
561
  configure: (input?: LanguageModelOptions) => /*elided*/ any;
552
562
  };
553
563
  export declare const provider: {
554
564
  id: string & import("effect/Brand").Brand<"AI.ProviderID">;
555
565
  model: (modelID: string | ModelID) => import("../schema/options.js").LanguageModel<OpenRouterOptions, import("../route/client.js").CompactionOperations | undefined>;
566
+ experimental: {
567
+ evaluation: (modelID: string | ModelID) => import("../experimental.js").EvaluationModel<OpenRouterEvaluationOptions>;
568
+ };
556
569
  configure: (input?: LanguageModelOptions) => {
557
570
  id: string & import("effect/Brand").Brand<"AI.ProviderID">;
558
571
  model: (modelID: string | ModelID) => import("../schema/options.js").LanguageModel<OpenRouterOptions, import("../route/client.js").CompactionOperations | undefined>;
572
+ experimental: {
573
+ evaluation: (modelID: string | ModelID) => import("../experimental.js").EvaluationModel<OpenRouterEvaluationOptions>;
574
+ };
559
575
  configure: /*elided*/ any;
560
576
  };
561
577
  };
578
+ export declare const experimental: {
579
+ evaluation: (modelID: string | ModelID) => import("../experimental.js").EvaluationModel<OpenRouterEvaluationOptions>;
580
+ };
562
581
  export declare const model: ProviderPackage.Definition<Settings, OpenRouterProviderOptionsInput>["model"];
563
582
  export {};