@opencode/ai 2.0.15 → 2.0.17

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 (224) hide show
  1. package/README.md +441 -99
  2. package/dist/ai-client.d.ts +8 -0
  3. package/dist/ai-client.js +12 -0
  4. package/dist/experimental/evaluation-client.d.ts +3 -3
  5. package/dist/experimental/evaluation-client.js +1 -1
  6. package/dist/experimental/evaluation.js +1 -1
  7. package/dist/generation.d.ts +39 -29
  8. package/dist/generation.js +62 -36
  9. package/dist/image-client.d.ts +59 -17
  10. package/dist/image-client.js +16 -24
  11. package/dist/image.d.ts +394 -55
  12. package/dist/image.js +48 -57
  13. package/dist/index.d.ts +15 -2
  14. package/dist/index.js +10 -0
  15. package/dist/llm.d.ts +7 -5
  16. package/dist/llm.js +10 -4
  17. package/dist/media-client.d.ts +30 -0
  18. package/dist/media-client.js +51 -0
  19. package/dist/media-model.d.ts +43 -0
  20. package/dist/media-model.js +47 -0
  21. package/dist/media.d.ts +10 -9
  22. package/dist/media.js +11 -12
  23. package/dist/promise.d.ts +473 -15
  24. package/dist/promise.js +71 -13
  25. package/dist/protocols/alibaba-chat.d.ts +12 -0
  26. package/dist/protocols/alibaba-chat.js +4 -1
  27. package/dist/protocols/alibaba-messages.d.ts +1 -1
  28. package/dist/protocols/alibaba-messages.js +6 -4
  29. package/dist/protocols/alibaba-responses.d.ts +2 -2
  30. package/dist/protocols/anthropic-messages.d.ts +34 -34
  31. package/dist/protocols/anthropic-messages.js +14 -9
  32. package/dist/protocols/assemblyai-transcription.d.ts +41 -0
  33. package/dist/protocols/assemblyai-transcription.js +136 -0
  34. package/dist/protocols/bedrock-converse.d.ts +7 -0
  35. package/dist/protocols/bedrock-converse.js +39 -19
  36. package/dist/protocols/bfl-images.d.ts +38 -0
  37. package/dist/protocols/bfl-images.js +153 -0
  38. package/dist/protocols/cartesia-speech.d.ts +143 -0
  39. package/dist/protocols/cartesia-speech.js +120 -0
  40. package/dist/protocols/deepgram-speech.d.ts +135 -0
  41. package/dist/protocols/deepgram-speech.js +91 -0
  42. package/dist/protocols/deepgram-transcription.d.ts +26 -0
  43. package/dist/protocols/deepgram-transcription.js +125 -0
  44. package/dist/protocols/elevenlabs-speech.d.ts +138 -0
  45. package/dist/protocols/elevenlabs-speech.js +111 -0
  46. package/dist/protocols/fal-images.d.ts +25 -0
  47. package/dist/protocols/fal-images.js +104 -0
  48. package/dist/protocols/fal-video.d.ts +29 -0
  49. package/dist/protocols/fal-video.js +73 -0
  50. package/dist/protocols/gemini.d.ts +22 -22
  51. package/dist/protocols/gemini.js +19 -36
  52. package/dist/protocols/google-images.d.ts +3 -3
  53. package/dist/protocols/google-images.js +12 -34
  54. package/dist/protocols/google-speech.d.ts +146 -0
  55. package/dist/protocols/google-speech.js +88 -0
  56. package/dist/protocols/google-transcription.d.ts +174 -0
  57. package/dist/protocols/google-transcription.js +126 -0
  58. package/dist/protocols/google-video.d.ts +26 -0
  59. package/dist/protocols/google-video.js +142 -0
  60. package/dist/protocols/meta-images.d.ts +3 -4
  61. package/dist/protocols/meta-images.js +13 -29
  62. package/dist/protocols/meta-messages.d.ts +5 -5
  63. package/dist/protocols/meta-responses.d.ts +4 -4
  64. package/dist/protocols/meta-responses.js +6 -4
  65. package/dist/protocols/open-responses.d.ts +10 -9
  66. package/dist/protocols/open-responses.js +4 -9
  67. package/dist/protocols/openai-chat.d.ts +84 -0
  68. package/dist/protocols/openai-chat.js +28 -17
  69. package/dist/protocols/openai-compatible-chat.d.ts +12 -0
  70. package/dist/protocols/openai-compatible-responses.d.ts +2 -2
  71. package/dist/protocols/openai-images.d.ts +130 -7
  72. package/dist/protocols/openai-images.js +143 -91
  73. package/dist/protocols/openai-responses.d.ts +20 -20
  74. package/dist/protocols/openai-responses.js +31 -31
  75. package/dist/protocols/openai-speech.d.ts +136 -0
  76. package/dist/protocols/openai-speech.js +97 -0
  77. package/dist/protocols/openai-transcription.d.ts +211 -0
  78. package/dist/protocols/openai-transcription.js +202 -0
  79. package/dist/protocols/replicate-images.d.ts +28 -0
  80. package/dist/protocols/replicate-images.js +127 -0
  81. package/dist/protocols/runway-video.d.ts +38 -0
  82. package/dist/protocols/runway-video.js +140 -0
  83. package/dist/protocols/shared.d.ts +21 -17
  84. package/dist/protocols/shared.js +27 -36
  85. package/dist/protocols/stability-images.d.ts +39 -0
  86. package/dist/protocols/stability-images.js +136 -0
  87. package/dist/protocols/utils/fal-queue.d.ts +26 -0
  88. package/dist/protocols/utils/fal-queue.js +67 -0
  89. package/dist/protocols/utils/gemini-generate-content.d.ts +65 -0
  90. package/dist/protocols/utils/gemini-generate-content.js +65 -0
  91. package/dist/protocols/utils/gemini-json-schema.d.ts +3 -0
  92. package/dist/protocols/utils/gemini-json-schema.js +76 -0
  93. package/dist/protocols/utils/media-input.d.ts +23 -1
  94. package/dist/protocols/utils/media-input.js +40 -0
  95. package/dist/protocols/utils/responses-checkpoint.js +3 -7
  96. package/dist/protocols/utils/responses-compaction.d.ts +3 -1
  97. package/dist/protocols/utils/responses-compaction.js +16 -3
  98. package/dist/protocols/utils/speech-stream.d.ts +49 -0
  99. package/dist/protocols/utils/speech-stream.js +64 -0
  100. package/dist/protocols/utils/tool-schema.d.ts +2 -2
  101. package/dist/protocols/utils/tool-schema.js +62 -19
  102. package/dist/protocols/xai-images.d.ts +4 -4
  103. package/dist/protocols/xai-images.js +14 -40
  104. package/dist/protocols/xai-responses.d.ts +2 -2
  105. package/dist/protocols/xai-responses.js +1 -1
  106. package/dist/protocols/xai-video.d.ts +34 -0
  107. package/dist/protocols/xai-video.js +141 -0
  108. package/dist/protocols/zai-chat.d.ts +13 -1
  109. package/dist/protocols/zai-images.d.ts +2 -2
  110. package/dist/protocols/zai-images.js +11 -14
  111. package/dist/protocols/zai-messages.d.ts +1 -1
  112. package/dist/provider-error.js +10 -1
  113. package/dist/providers/alibaba.d.ts +15 -3
  114. package/dist/providers/amazon-bedrock-mantle.d.ts +14 -2
  115. package/dist/providers/amazon-bedrock.d.ts +2 -0
  116. package/dist/providers/amazon-bedrock.js +1 -0
  117. package/dist/providers/anthropic-compatible.d.ts +5 -5
  118. package/dist/providers/anthropic.d.ts +5 -5
  119. package/dist/providers/assemblyai.d.ts +25 -0
  120. package/dist/providers/assemblyai.js +24 -0
  121. package/dist/providers/azure.d.ts +20 -8
  122. package/dist/providers/azure.js +2 -2
  123. package/dist/providers/baseten.d.ts +24 -0
  124. package/dist/providers/black-forest-labs.d.ts +25 -0
  125. package/dist/providers/black-forest-labs.js +23 -0
  126. package/dist/providers/cartesia.d.ts +24 -0
  127. package/dist/providers/cartesia.js +17 -0
  128. package/dist/providers/cerebras.d.ts +24 -0
  129. package/dist/providers/cloudflare-ai-gateway.d.ts +42 -18
  130. package/dist/providers/cloudflare-workers-ai.d.ts +24 -0
  131. package/dist/providers/deepgram.d.ts +29 -0
  132. package/dist/providers/deepgram.js +25 -0
  133. package/dist/providers/deepinfra.d.ts +24 -0
  134. package/dist/providers/deepseek.d.ts +24 -0
  135. package/dist/providers/elevenlabs.d.ts +24 -0
  136. package/dist/providers/elevenlabs.js +23 -0
  137. package/dist/providers/fal.d.ts +29 -0
  138. package/dist/providers/fal.js +26 -0
  139. package/dist/providers/fireworks.d.ts +24 -0
  140. package/dist/providers/google-vertex-chat.d.ts +12 -0
  141. package/dist/providers/google-vertex-messages.d.ts +5 -5
  142. package/dist/providers/google-vertex-responses.d.ts +2 -2
  143. package/dist/providers/google-vertex.d.ts +6 -6
  144. package/dist/providers/google.d.ts +21 -6
  145. package/dist/providers/google.js +13 -9
  146. package/dist/providers/groq.d.ts +24 -0
  147. package/dist/providers/index.d.ts +9 -0
  148. package/dist/providers/index.js +9 -0
  149. package/dist/providers/meta.d.ts +22 -10
  150. package/dist/providers/meta.js +4 -8
  151. package/dist/providers/minimax.d.ts +19 -7
  152. package/dist/providers/moonshot.d.ts +19 -7
  153. package/dist/providers/moonshot.js +3 -3
  154. package/dist/providers/openai-compatible-responses.d.ts +2 -2
  155. package/dist/providers/openai-compatible.d.ts +12 -0
  156. package/dist/providers/openai-options.d.ts +3 -9
  157. package/dist/providers/openai-options.js +4 -7
  158. package/dist/providers/openai.d.ts +33 -12
  159. package/dist/providers/openai.js +17 -9
  160. package/dist/providers/opencode-zen.js +1 -1
  161. package/dist/providers/openrouter.d.ts +54 -7
  162. package/dist/providers/openrouter.js +10 -5
  163. package/dist/providers/replicate.d.ts +25 -0
  164. package/dist/providers/replicate.js +17 -0
  165. package/dist/providers/runway.d.ts +24 -0
  166. package/dist/providers/runway.js +17 -0
  167. package/dist/providers/stability.d.ts +28 -0
  168. package/dist/providers/stability.js +18 -0
  169. package/dist/providers/togetherai.d.ts +24 -0
  170. package/dist/providers/typesafe-ai.js +1 -1
  171. package/dist/providers/vercel-ai-gateway.js +1 -1
  172. package/dist/providers/xai.d.ts +17 -0
  173. package/dist/providers/xai.js +7 -9
  174. package/dist/providers/zai-coding-plan.d.ts +16 -4
  175. package/dist/providers/zai.d.ts +13 -1
  176. package/dist/providers/zai.js +4 -8
  177. package/dist/route/auth.d.ts +5 -2
  178. package/dist/route/auth.js +20 -13
  179. package/dist/route/client.d.ts +9 -7
  180. package/dist/route/client.js +6 -8
  181. package/dist/route/endpoint.d.ts +1 -0
  182. package/dist/route/endpoint.js +2 -2
  183. package/dist/route/executor-service.d.ts +4 -2
  184. package/dist/route/executor-service.js +2 -1
  185. package/dist/route/executor.d.ts +3 -1
  186. package/dist/route/executor.js +7 -0
  187. package/dist/route/framing.d.ts +15 -2
  188. package/dist/route/framing.js +53 -4
  189. package/dist/route/index.d.ts +1 -1
  190. package/dist/route/media-protocol.d.ts +145 -18
  191. package/dist/route/media-protocol.js +96 -28
  192. package/dist/route/media.d.ts +54 -9
  193. package/dist/route/media.js +194 -38
  194. package/dist/route/protocol.d.ts +3 -1
  195. package/dist/schema/events.d.ts +0 -6
  196. package/dist/schema/messages.d.ts +0 -3
  197. package/dist/schema/options.d.ts +10 -6
  198. package/dist/schema/options.js +10 -5
  199. package/dist/speech-client.d.ts +66 -0
  200. package/dist/speech-client.js +21 -0
  201. package/dist/speech.d.ts +1307 -0
  202. package/dist/speech.js +124 -0
  203. package/dist/testing.d.ts +2 -2
  204. package/dist/transcription-client.d.ts +82 -0
  205. package/dist/transcription-client.js +13 -0
  206. package/dist/transcription.d.ts +1492 -0
  207. package/dist/transcription.js +127 -0
  208. package/dist/utils/bytes.d.ts +1 -0
  209. package/dist/utils/bytes.js +10 -0
  210. package/dist/utils/json.d.ts +4 -0
  211. package/dist/utils/json.js +4 -0
  212. package/dist/utils/media-type.d.ts +3 -1
  213. package/dist/utils/media-type.js +25 -2
  214. package/dist/video-client.d.ts +59 -0
  215. package/dist/video-client.js +20 -0
  216. package/dist/video.d.ts +1351 -0
  217. package/dist/video.js +118 -0
  218. package/package.json +3 -3
  219. package/dist/protocols/utils/gemini-tool-schema.d.ts +0 -2
  220. package/dist/protocols/utils/gemini-tool-schema.js +0 -103
  221. package/dist/protocols/utils/meta-image.d.ts +0 -2
  222. package/dist/protocols/utils/meta-image.js +0 -13
  223. package/dist/protocols/utils/openai-image.d.ts +0 -5
  224. package/dist/protocols/utils/openai-image.js +0 -18
@@ -3,12 +3,10 @@ import { ImageModel, ImageResponse } from "../image.js";
3
3
  import { Media } from "../media.js";
4
4
  import { MediaProtocol } from "../route/media-protocol.js";
5
5
  import { MediaRoute } from "../route/media.js";
6
- import { ProviderID, mergeJsonRecords } from "../schema/index.js";
6
+ import { mergeJsonRecords } from "../schema/index.js";
7
7
  import { ProviderShared, optionalNull } from "./shared.js";
8
8
  import { MediaInput } from "./utils/media-input.js";
9
- const ADAPTER = "xai-images";
10
- const NAME = "xAI Images";
11
- const PROVIDER = ProviderID.make("xai");
9
+ const route = MediaProtocol.identity({ id: "xai-images", name: "xAI Images", provider: "xai" });
12
10
  export const DEFAULT_BASE_URL = "https://api.x.ai/v1";
13
11
  export const PATH = "/images/generations";
14
12
  export const EDIT_PATH = "/images/edits";
@@ -34,18 +32,7 @@ const nativeOptions = (options) => {
34
32
  return { response_format: responseFormat, ...native };
35
33
  };
36
34
  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`));
48
- };
35
+ const reference = (asset) => ProviderShared.mediaReference(asset, route.provider, route.name).pipe(Effect.map((item) => item.type === "ref" ? { file_id: item.value } : { url: item.value, type: "image_url" }));
49
36
  const fromRequest = Effect.fn("XAIImages.fromRequest")(function* (request) {
50
37
  const references = yield* Effect.forEach(request.images ?? [], reference);
51
38
  return MediaProtocol.json(mergeJsonRecords({
@@ -61,25 +48,20 @@ const fromRequest = Effect.fn("XAIImages.fromRequest")(function* (request) {
61
48
  // ---------------------------------------------------------------------------
62
49
  // 6. Response decoding
63
50
  // ---------------------------------------------------------------------------
51
+ const decodeDocument = route.decodeJson(XAIImageResponse);
64
52
  const decodeResponse = Effect.fn("XAIImages.decodeResponse")(function* (response) {
65
- const output = yield* MediaProtocol.decodeJson(ADAPTER, NAME, XAIImageResponse)(response);
53
+ const output = yield* decodeDocument(response);
66
54
  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
55
+ const images = yield* Effect.forEach(decoded.data, (item, index) => MediaInput.imageOutput(output.invalid, `${route.name} result ${index}`, item, item.mime_type ?? undefined, {
56
+ providerMetadata: item.revised_prompt === undefined || item.revised_prompt === null
69
57
  ? 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,
74
- });
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
- });
58
+ : { xai: { revisedPrompt: item.revised_prompt } },
59
+ }));
79
60
  if (images.length === 0)
80
- return yield* output.invalid(`${NAME} returned no images`);
61
+ return yield* output.invalid(`${route.name} returned no images`);
81
62
  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.
63
+ // xAI reports a USD cost (`cost_in_usd_ticks`) rather than tokens, seconds, or credits; the raw record stays in
64
+ // provider metadata.
83
65
  return new ImageResponse({
84
66
  images,
85
67
  providerMetadata: usage === undefined ? undefined : { xai: { usage } },
@@ -88,20 +70,12 @@ const decodeResponse = Effect.fn("XAIImages.decodeResponse")(function* (response
88
70
  // ---------------------------------------------------------------------------
89
71
  // 7. Protocol and route
90
72
  // ---------------------------------------------------------------------------
91
- export const protocol = MediaProtocol.inline({
92
- id: ADAPTER,
93
- name: NAME,
73
+ export const protocol = MediaProtocol.inline(route, {
94
74
  unsupported: ["mask", "size", "seed", "format"],
95
75
  body: { from: fromRequest },
96
76
  response: { decode: decodeResponse },
97
77
  });
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);
78
+ export const model = (input) => ImageModel.fromRoute({ protocol, baseURL: DEFAULT_BASE_URL, path: ({ request }) => (isEdit(request) ? EDIT_PATH : PATH) }, input);
105
79
  export const XAIImages = {
106
80
  protocol,
107
81
  model,
@@ -67,8 +67,8 @@ export declare const protocol: Protocol<{
67
67
  } | {
68
68
  readonly type: "input_file";
69
69
  readonly filename: string;
70
- readonly detail?: string | undefined;
71
70
  readonly file_data?: string | undefined;
71
+ readonly detail?: string | undefined;
72
72
  readonly file_url?: string | undefined;
73
73
  } | {
74
74
  readonly type: "input_text";
@@ -104,8 +104,8 @@ export declare const protocol: Protocol<{
104
104
  } | {
105
105
  readonly type: "input_file";
106
106
  readonly filename: string;
107
- readonly detail?: string | undefined;
108
107
  readonly file_data?: string | undefined;
108
+ readonly detail?: string | undefined;
109
109
  readonly file_url?: string | undefined;
110
110
  } | {
111
111
  readonly type: "input_text";
@@ -77,5 +77,5 @@ export const protocol = Protocol.make({
77
77
  terminal: OpenResponses.terminal,
78
78
  },
79
79
  });
80
- export const compact = ResponsesCompaction.make(adapter);
80
+ export const compact = ResponsesCompaction.make(adapter, (request) => OpenResponses.lowerTools(ProviderShared.flattenTools(request.tools), adapter));
81
81
  export * as XAIResponses from "./xai-responses.js";
@@ -0,0 +1,34 @@
1
+ import { Schema } from "effect";
2
+ import { MediaProtocol } from "../route/media-protocol.js";
3
+ import { MediaRoute } from "../route/media.js";
4
+ import { VideoModel, VideoResponse, type VideoRequestFor } from "../video.js";
5
+ export declare const DEFAULT_BASE_URL = "https://api.x.ai/v1";
6
+ export declare const PATH = "/videos/generations";
7
+ export declare const EDIT_PATH = "/videos/edits";
8
+ export declare const EXTEND_PATH = "/videos/extensions";
9
+ export declare const STATUS_PATH = "/videos";
10
+ /**
11
+ * Provider-native options. Common fields (`frames`, `references`, `video`, `durationSeconds`, `aspectRatio`,
12
+ * `resolution`, `audio`) live on the request. `mode` selects the endpoint a `video` input is sent to.
13
+ */
14
+ export type XAIVideoOptions = {
15
+ readonly mode?: "edit" | "extend";
16
+ readonly reference_audios?: ReadonlyArray<{
17
+ readonly voice_id: string;
18
+ }>;
19
+ } & Record<string, unknown>;
20
+ export type Request = VideoRequestFor<XAIVideoOptions>;
21
+ export declare const Token: Schema.Struct<{
22
+ readonly requestID: Schema.String;
23
+ }>;
24
+ export type Token = Schema.Schema.Type<typeof Token>;
25
+ export declare const protocol: MediaProtocol.Queued<Request, VideoResponse, {
26
+ readonly requestID: string;
27
+ }>;
28
+ export declare const model: (input: MediaRoute.ModelInput) => VideoModel<XAIVideoOptions>;
29
+ export declare const XAIVideo: {
30
+ readonly protocol: MediaProtocol.Queued<Request, VideoResponse, {
31
+ readonly requestID: string;
32
+ }>;
33
+ readonly model: (input: MediaRoute.ModelInput) => VideoModel<XAIVideoOptions>;
34
+ };
@@ -0,0 +1,141 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { Media } from "../media.js";
3
+ import { MediaProtocol } from "../route/media-protocol.js";
4
+ import { MediaRoute } from "../route/media.js";
5
+ import { mergeJsonRecords } from "../schema/index.js";
6
+ import { VideoModel, VideoResponse } from "../video.js";
7
+ import { ProviderShared, optionalNull } from "./shared.js";
8
+ const route = MediaProtocol.identity({ id: "xai-video", name: "xAI Video", provider: "xai" });
9
+ export const DEFAULT_BASE_URL = "https://api.x.ai/v1";
10
+ export const PATH = "/videos/generations";
11
+ export const EDIT_PATH = "/videos/edits";
12
+ export const EXTEND_PATH = "/videos/extensions";
13
+ export const STATUS_PATH = "/videos";
14
+ // ---------------------------------------------------------------------------
15
+ // 2. Token and response schemas
16
+ // ---------------------------------------------------------------------------
17
+ export const Token = Schema.Struct({ requestID: Schema.String });
18
+ const StartResponse = Schema.Struct({ request_id: Schema.String });
19
+ const VideoStatus = Schema.Struct({
20
+ status: Schema.String,
21
+ progress: optionalNull(Schema.Number),
22
+ video: optionalNull(Schema.Struct({
23
+ url: optionalNull(Schema.String),
24
+ duration: optionalNull(Schema.Number),
25
+ respect_moderation: optionalNull(Schema.Boolean),
26
+ })),
27
+ error: optionalNull(Schema.Struct({
28
+ code: optionalNull(Schema.String),
29
+ message: optionalNull(Schema.String),
30
+ })),
31
+ model: optionalNull(Schema.String),
32
+ });
33
+ const STATUS = {
34
+ pending: "running",
35
+ done: "completed",
36
+ failed: "failed",
37
+ expired: "expired",
38
+ };
39
+ // ---------------------------------------------------------------------------
40
+ // 5. Request body construction
41
+ // ---------------------------------------------------------------------------
42
+ const mediaInput = (asset) => ProviderShared.mediaReference(asset, route.provider, route.name).pipe(Effect.map((reference) => (reference.type === "ref" ? { file_id: reference.value } : { url: reference.value })));
43
+ const nativeOptions = (options) => {
44
+ if (!options)
45
+ return undefined;
46
+ const { mode: _mode, ...native } = options;
47
+ return native;
48
+ };
49
+ const fromRequest = Effect.fn("XAIVideo.fromRequest")(function* (request) {
50
+ const image = request.frames?.first === undefined ? undefined : yield* mediaInput(request.frames.first);
51
+ const lastFrame = request.frames?.last === undefined ? undefined : yield* mediaInput(request.frames.last);
52
+ const video = request.video === undefined ? undefined : yield* mediaInput(request.video);
53
+ const references = yield* Effect.forEach(request.references ?? [], mediaInput);
54
+ return MediaProtocol.json(mergeJsonRecords({
55
+ model: request.model.id,
56
+ prompt: request.prompt,
57
+ image,
58
+ last_frame: lastFrame,
59
+ reference_images: references.length === 0 ? undefined : references,
60
+ video,
61
+ duration: request.durationSeconds,
62
+ aspect_ratio: request.aspectRatio,
63
+ resolution: request.resolution,
64
+ generate_audio: request.audio,
65
+ }, nativeOptions(request.providerOptions), request.http?.body) ?? {});
66
+ });
67
+ // ---------------------------------------------------------------------------
68
+ // 6. Response decoding
69
+ // ---------------------------------------------------------------------------
70
+ const decodeStart = route.decodeStarted(StartResponse, (value) => ({
71
+ token: { requestID: value.request_id },
72
+ snapshot: { id: value.request_id, status: "running" },
73
+ }));
74
+ // `progress` is undocumented but observed live as a 0..100 percentage (recorded cassette: 1 → 10 → 37 → 100).
75
+ const fraction = (progress) => progress !== undefined && progress !== null && progress >= 0 && progress <= 100 ? progress / 100 : undefined;
76
+ const decodeVideoStatus = route.decodeJson(VideoStatus);
77
+ const decodeStatus = Effect.fn("XAIVideo.decodeStatus")(function* (response, context) {
78
+ const output = yield* decodeVideoStatus(response);
79
+ const status = yield* MediaProtocol.status(STATUS, output.value.status, output);
80
+ return { id: context.token.requestID, status, progress: fraction(output.value.progress) };
81
+ });
82
+ const decodeResult = Effect.fn("XAIVideo.decodeResult")(function* (response, context) {
83
+ const output = yield* decodeVideoStatus(response);
84
+ const decoded = output.value;
85
+ const status = yield* MediaProtocol.status(STATUS, decoded.status, output);
86
+ if (status === "running")
87
+ return yield* output.pending(context.token.requestID);
88
+ if (status === "failed") {
89
+ const code = decoded.error?.code ?? undefined;
90
+ const message = decoded.error?.message ?? undefined;
91
+ return yield* output.ended("failed", `${route.name} generation failed${code === undefined ? "" : ` (${code})`}${message === undefined ? "" : `: ${message}`}`);
92
+ }
93
+ if (status !== "completed")
94
+ return yield* output.ended("expired", `${route.name} request ${context.token.requestID} expired`);
95
+ // `respect_moderation: false` marks a filtered result; a URL may still be present, so report it as a notice.
96
+ const notices = decoded.video?.respect_moderation === false
97
+ ? [{ type: "moderated", message: `${route.name} flagged the generated video for moderation` }]
98
+ : undefined;
99
+ const url = decoded.video?.url ?? undefined;
100
+ if (url === undefined && notices !== undefined)
101
+ return yield* output.contentPolicy(`${route.name} withheld the video for moderation`);
102
+ if (url === undefined)
103
+ return yield* output.invalid(`${route.name} completed without a video URL`);
104
+ const duration = decoded.video?.duration ?? undefined;
105
+ return new VideoResponse({
106
+ videos: [
107
+ Media.url(url, {
108
+ mediaType: "video/mp4",
109
+ info: duration === undefined ? undefined : { durationSeconds: duration },
110
+ }),
111
+ ],
112
+ notices,
113
+ providerMetadata: { xai: { requestId: context.token.requestID, model: decoded.model ?? undefined } },
114
+ });
115
+ });
116
+ // ---------------------------------------------------------------------------
117
+ // 7. Protocol and route
118
+ // ---------------------------------------------------------------------------
119
+ const statusPath = (token) => `${STATUS_PATH}/${token.requestID}`;
120
+ export const protocol = MediaProtocol.queued(route, {
121
+ token: Token,
122
+ unsupported: ["n", "seed", "negativePrompt"],
123
+ start: { body: { from: fromRequest }, decode: decodeStart },
124
+ status: { path: statusPath, decode: decodeStatus },
125
+ result: { path: statusPath, decode: decodeResult },
126
+ });
127
+ // A source video goes to `/videos/edits` unless `providerOptions.mode` asks for an extension.
128
+ const startPath = (request) => {
129
+ if (request.video === undefined)
130
+ return PATH;
131
+ return request.providerOptions?.mode === "extend" ? EXTEND_PATH : EDIT_PATH;
132
+ };
133
+ export const model = (input) => VideoModel.fromRoute({
134
+ protocol,
135
+ baseURL: DEFAULT_BASE_URL,
136
+ path: ({ request }) => startPath(request),
137
+ }, input);
138
+ export const XAIVideo = {
139
+ protocol,
140
+ model,
141
+ };
@@ -39,6 +39,12 @@ export declare const protocol: Protocol<{
39
39
  readonly image_url: {
40
40
  readonly url: string;
41
41
  };
42
+ } | {
43
+ readonly type: "file";
44
+ readonly file: {
45
+ readonly filename: string;
46
+ readonly file_data: string;
47
+ };
42
48
  })[];
43
49
  } | {
44
50
  readonly role: "user";
@@ -54,6 +60,12 @@ export declare const protocol: Protocol<{
54
60
  readonly image_url: {
55
61
  readonly url: string;
56
62
  };
63
+ } | {
64
+ readonly type: "file";
65
+ readonly file: {
66
+ readonly filename: string;
67
+ readonly file_data: string;
68
+ };
57
69
  })[];
58
70
  } | {
59
71
  readonly [x: string]: unknown;
@@ -129,8 +141,8 @@ export declare const protocol: Protocol<{
129
141
  readonly response_format?: {
130
142
  readonly type: string;
131
143
  } | undefined;
132
- readonly do_sample?: boolean | undefined;
133
144
  readonly request_id?: string | undefined;
145
+ readonly do_sample?: boolean | undefined;
134
146
  }, string, "[DONE]" | {
135
147
  readonly [x: string]: unknown;
136
148
  readonly error?: {
@@ -1,12 +1,12 @@
1
1
  import { ImageModel, ImageResponse, type ImageRequestFor } from "../image.js";
2
2
  import { MediaProtocol } from "../route/media-protocol.js";
3
3
  import { MediaRoute } from "../route/media.js";
4
+ import { type OpenString } from "../schema/index.js";
4
5
  export declare const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4";
5
6
  export declare const PATH = "/images/generations";
6
- export type ZAIImageString<Known extends string> = Known | (string & {});
7
7
  /** Provider-native options. The common `size` field lives on the request. */
8
8
  export type ZAIImageOptions = {
9
- readonly quality?: ZAIImageString<"hd" | "standard">;
9
+ readonly quality?: OpenString<"hd" | "standard">;
10
10
  readonly userID?: string;
11
11
  } & Record<string, unknown>;
12
12
  export type Request = ImageRequestFor<ZAIImageOptions>;
@@ -1,14 +1,12 @@
1
- import { Effect, Schema } from "effect";
1
+ import { Duration, Effect, Schema } from "effect";
2
2
  import { ImageModel, ImageResponse } from "../image.js";
3
- import { Media } from "../media.js";
4
3
  import { MediaProtocol } from "../route/media-protocol.js";
5
4
  import { MediaRoute } from "../route/media.js";
6
- import { ProviderID, mergeJsonRecords } from "../schema/index.js";
7
- const ADAPTER = "zai-images";
8
- const NAME = "Z.ai Images";
9
- const PROVIDER = ProviderID.make("zai");
5
+ import { mergeJsonRecords } from "../schema/index.js";
6
+ const route = MediaProtocol.identity({ id: "zai-images", name: "Z.ai Images", provider: "zai" });
10
7
  export const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4";
11
8
  export const PATH = "/images/generations";
9
+ const OUTPUT_RETENTION = Duration.days(30);
12
10
  // ---------------------------------------------------------------------------
13
11
  // 2. Response schema
14
12
  // ---------------------------------------------------------------------------
@@ -37,21 +35,22 @@ const fromRequest = Effect.fn("ZAIImages.fromRequest")(function* (request) {
37
35
  // ---------------------------------------------------------------------------
38
36
  // 6. Response decoding
39
37
  // ---------------------------------------------------------------------------
38
+ const decodeDocument = route.decodeJson(ZAIImageResponse);
40
39
  const decodeResponse = Effect.fn("ZAIImages.decodeResponse")(function* (response) {
41
- const output = yield* MediaProtocol.decodeJson(ADAPTER, NAME, ZAIImageResponse)(response);
40
+ const output = yield* decodeDocument(response);
42
41
  const decoded = output.value;
43
42
  if (decoded.data.length === 0)
44
- return yield* output.invalid(`${NAME} returned no images`);
43
+ return yield* output.invalid(`${route.name} returned no images`);
45
44
  const filters = decoded.content_filter ?? [];
46
45
  return new ImageResponse({
47
46
  // 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)),
47
+ images: yield* Effect.forEach(decoded.data, (item) => MediaProtocol.expiringUrl(item.url, OUTPUT_RETENTION)),
49
48
  // Z.ai reports applied content filters alongside a successful result; surface them instead of dropping them.
50
49
  notices: filters.length === 0
51
50
  ? undefined
52
51
  : filters.map((filter) => ({
53
52
  type: "moderated",
54
- message: `${NAME} applied a content filter${filter.role === undefined ? "" : ` for ${filter.role}`}${filter.level === undefined ? "" : ` at level ${filter.level}`}`,
53
+ message: `${route.name} applied a content filter${filter.role === undefined ? "" : ` for ${filter.role}`}${filter.level === undefined ? "" : ` at level ${filter.level}`}`,
55
54
  providerMetadata: { zai: filter },
56
55
  })),
57
56
  providerMetadata: {
@@ -67,14 +66,12 @@ const decodeResponse = Effect.fn("ZAIImages.decodeResponse")(function* (response
67
66
  // ---------------------------------------------------------------------------
68
67
  // 7. Protocol and route
69
68
  // ---------------------------------------------------------------------------
70
- export const protocol = MediaProtocol.inline({
71
- id: ADAPTER,
72
- name: NAME,
69
+ export const protocol = MediaProtocol.inline(route, {
73
70
  unsupported: ["images", "mask", "n", "aspectRatio", "seed", "format"],
74
71
  body: { from: fromRequest },
75
72
  response: { decode: decodeResponse },
76
73
  });
77
- export const model = (input) => ImageModel.fromRoute({ id: ADAPTER, provider: PROVIDER, protocol, baseURL: DEFAULT_BASE_URL, path: PATH }, input);
74
+ export const model = (input) => ImageModel.fromRoute({ protocol, baseURL: DEFAULT_BASE_URL, path: PATH }, input);
78
75
  export const ZAIImages = {
79
76
  protocol,
80
77
  model,
@@ -222,7 +222,7 @@ export declare const protocol: Protocol<{
222
222
  readonly thinking?: {
223
223
  readonly type: string;
224
224
  } | undefined;
225
- readonly service_tier?: "auto" | (string & {}) | "standard_only" | undefined;
225
+ readonly service_tier?: import("../schema/options.js").OpenString<"auto" | "standard_only"> | undefined;
226
226
  readonly container?: string | {
227
227
  readonly id?: string | null | undefined;
228
228
  readonly skills?: readonly {
@@ -59,12 +59,19 @@ const SERVER_CODES = new Set([
59
59
  "slow_down",
60
60
  "serviceunavailableexception",
61
61
  ]);
62
- const INVALID_REQUEST_CODES = new Set(["invalid_prompt", "invalid_request_error", "validationexception"]);
62
+ // `invalid_request` is the Vercel AI Gateway's code for an upstream request rejection.
63
+ const INVALID_REQUEST_CODES = new Set([
64
+ "invalid_prompt",
65
+ "invalid_request",
66
+ "invalid_request_error",
67
+ "validationexception",
68
+ ]);
63
69
  // Azure OpenAI reports `content_filter` with `innererror.code` ResponsibleAIPolicyViolation.
64
70
  // OpenRouter tags provider failures with a typed `error_type`; its Responses skin also
65
71
  // emits `image_content_policy_violation` as the native code.
66
72
  const CONTENT_POLICY_CODES = new Set([
67
73
  "content_filter",
74
+ "content_moderation",
68
75
  "responsibleaipolicyviolation",
69
76
  "content_policy_violation",
70
77
  "image_content_policy_violation",
@@ -157,6 +164,8 @@ function providerCodes(value) {
157
164
  const exception = isRecord(decoded.exception) ? decoded.exception : undefined;
158
165
  return [
159
166
  decoded.code,
167
+ // Stability's `{ id, name, errors }` bodies carry the code in `name`.
168
+ Array.isArray(decoded.errors) ? decoded.name : undefined,
160
169
  decoded.error_type,
161
170
  error?.code,
162
171
  error?.type,
@@ -39,6 +39,12 @@ export declare const routes: (Route<{
39
39
  readonly image_url: {
40
40
  readonly url: string;
41
41
  };
42
+ } | {
43
+ readonly type: "file";
44
+ readonly file: {
45
+ readonly filename: string;
46
+ readonly file_data: string;
47
+ };
42
48
  })[];
43
49
  } | {
44
50
  readonly role: "user";
@@ -54,6 +60,12 @@ export declare const routes: (Route<{
54
60
  readonly image_url: {
55
61
  readonly url: string;
56
62
  };
63
+ } | {
64
+ readonly type: "file";
65
+ readonly file: {
66
+ readonly filename: string;
67
+ readonly file_data: string;
68
+ };
57
69
  })[];
58
70
  } | {
59
71
  readonly [x: string]: unknown;
@@ -360,7 +372,7 @@ export declare const routes: (Route<{
360
372
  readonly type: string;
361
373
  readonly budget_tokens?: number | undefined;
362
374
  } | undefined;
363
- readonly service_tier?: "auto" | (string & {}) | "standard_only" | undefined;
375
+ readonly service_tier?: import("../schema/options.js").OpenString<"auto" | "standard_only"> | undefined;
364
376
  readonly container?: string | {
365
377
  readonly id?: string | null | undefined;
366
378
  readonly skills?: readonly {
@@ -470,8 +482,8 @@ export declare const routes: (Route<{
470
482
  } | {
471
483
  readonly type: "input_file";
472
484
  readonly filename: string;
473
- readonly detail?: string | undefined;
474
485
  readonly file_data?: string | undefined;
486
+ readonly detail?: string | undefined;
475
487
  readonly file_url?: string | undefined;
476
488
  } | {
477
489
  readonly type: "input_text";
@@ -507,8 +519,8 @@ export declare const routes: (Route<{
507
519
  } | {
508
520
  readonly type: "input_file";
509
521
  readonly filename: string;
510
- readonly detail?: string | undefined;
511
522
  readonly file_data?: string | undefined;
523
+ readonly detail?: string | undefined;
512
524
  readonly file_url?: string | undefined;
513
525
  } | {
514
526
  readonly type: "input_text";
@@ -42,6 +42,12 @@ export declare const routes: (Route<{
42
42
  readonly image_url: {
43
43
  readonly url: string;
44
44
  };
45
+ } | {
46
+ readonly type: "file";
47
+ readonly file: {
48
+ readonly filename: string;
49
+ readonly file_data: string;
50
+ };
45
51
  })[];
46
52
  } | {
47
53
  readonly role: "user";
@@ -57,6 +63,12 @@ export declare const routes: (Route<{
57
63
  readonly image_url: {
58
64
  readonly url: string;
59
65
  };
66
+ } | {
67
+ readonly type: "file";
68
+ readonly file: {
69
+ readonly filename: string;
70
+ readonly file_data: string;
71
+ };
60
72
  })[];
61
73
  } | {
62
74
  readonly [x: string]: unknown;
@@ -191,8 +203,8 @@ export declare const routes: (Route<{
191
203
  } | {
192
204
  readonly type: "input_file";
193
205
  readonly filename: string;
194
- readonly detail?: string | undefined;
195
206
  readonly file_data?: string | undefined;
207
+ readonly detail?: string | undefined;
196
208
  readonly file_url?: string | undefined;
197
209
  } | {
198
210
  readonly type: "input_text";
@@ -228,8 +240,8 @@ export declare const routes: (Route<{
228
240
  } | {
229
241
  readonly type: "input_file";
230
242
  readonly filename: string;
231
- readonly detail?: string | undefined;
232
243
  readonly file_data?: string | undefined;
244
+ readonly detail?: string | undefined;
233
245
  readonly file_url?: string | undefined;
234
246
  } | {
235
247
  readonly type: "input_text";
@@ -1,6 +1,7 @@
1
1
  import type { RouteDefaultsInput } from "../route/client.js";
2
2
  import type { ProviderPackage } from "../provider-package.js";
3
3
  import { type ModelID } from "../schema/index.js";
4
+ import * as BedrockConverse from "../protocols/bedrock-converse.js";
4
5
  import type { BedrockCredentials } from "../protocols/bedrock-converse.js";
5
6
  export declare const id: string & import("effect/Brand").Brand<"AI.ProviderID">;
6
7
  export type Config = RouteDefaultsInput & {
@@ -26,6 +27,7 @@ export interface Settings extends ProviderPackage.Settings {
26
27
  readonly profile?: string;
27
28
  readonly region?: string;
28
29
  readonly topP?: number;
30
+ readonly thinking?: BedrockConverse.OptionsInput["thinking"];
29
31
  }
30
32
  export declare const routes: import("../route/client.js").Route<{
31
33
  readonly messages: readonly ({
@@ -36,6 +36,7 @@ export const model = (modelID, settings) => configure({
36
36
  generation: settings.topP === undefined ? undefined : { topP: settings.topP },
37
37
  headers: settings.headers === undefined ? undefined : { ...settings.headers },
38
38
  http: settings.body === undefined ? undefined : { body: { ...settings.body } },
39
+ providerOptions: settings.thinking === undefined ? undefined : { thinking: settings.thinking },
39
40
  profile: settings.profile,
40
41
  region: settings.region,
41
42
  }).model(modelID);
@@ -238,20 +238,20 @@ export declare const routes: import("../route/client.js").Route<{
238
238
  readonly thinking?: {
239
239
  readonly type: "enabled";
240
240
  readonly budget_tokens: number;
241
- readonly display?: (string & {}) | "summarized" | "omitted" | undefined;
241
+ readonly display?: import("../schema/options.js").OpenString<"summarized" | "omitted"> | undefined;
242
242
  readonly block_binding?: {
243
- readonly prefix_mismatch_behavior?: (string & {}) | "error" | "drop_block" | undefined;
243
+ readonly prefix_mismatch_behavior?: import("../schema/options.js").OpenString<"error" | "drop_block"> | undefined;
244
244
  } | undefined;
245
245
  } | {
246
246
  readonly type: "adaptive";
247
- readonly display?: (string & {}) | "summarized" | "omitted" | undefined;
247
+ readonly display?: import("../schema/options.js").OpenString<"summarized" | "omitted"> | undefined;
248
248
  readonly block_binding?: {
249
- readonly prefix_mismatch_behavior?: (string & {}) | "error" | "drop_block" | undefined;
249
+ readonly prefix_mismatch_behavior?: import("../schema/options.js").OpenString<"error" | "drop_block"> | undefined;
250
250
  } | undefined;
251
251
  } | {
252
252
  readonly type: "disabled";
253
253
  } | undefined;
254
- readonly service_tier?: "auto" | (string & {}) | "standard_only" | undefined;
254
+ readonly service_tier?: import("../schema/options.js").OpenString<"auto" | "standard_only"> | undefined;
255
255
  readonly container?: string | {
256
256
  readonly id?: string | null | undefined;
257
257
  readonly skills?: readonly {
@@ -223,20 +223,20 @@ export declare const routes: import("../route/client.js").Route<{
223
223
  readonly thinking?: {
224
224
  readonly type: "enabled";
225
225
  readonly budget_tokens: number;
226
- readonly display?: (string & {}) | "summarized" | "omitted" | undefined;
226
+ readonly display?: import("../schema/options.js").OpenString<"summarized" | "omitted"> | undefined;
227
227
  readonly block_binding?: {
228
- readonly prefix_mismatch_behavior?: (string & {}) | "error" | "drop_block" | undefined;
228
+ readonly prefix_mismatch_behavior?: import("../schema/options.js").OpenString<"error" | "drop_block"> | undefined;
229
229
  } | undefined;
230
230
  } | {
231
231
  readonly type: "adaptive";
232
- readonly display?: (string & {}) | "summarized" | "omitted" | undefined;
232
+ readonly display?: import("../schema/options.js").OpenString<"summarized" | "omitted"> | undefined;
233
233
  readonly block_binding?: {
234
- readonly prefix_mismatch_behavior?: (string & {}) | "error" | "drop_block" | undefined;
234
+ readonly prefix_mismatch_behavior?: import("../schema/options.js").OpenString<"error" | "drop_block"> | undefined;
235
235
  } | undefined;
236
236
  } | {
237
237
  readonly type: "disabled";
238
238
  } | undefined;
239
- readonly service_tier?: "auto" | (string & {}) | "standard_only" | undefined;
239
+ readonly service_tier?: import("../schema/options.js").OpenString<"auto" | "standard_only"> | undefined;
240
240
  readonly container?: string | {
241
241
  readonly id?: string | null | undefined;
242
242
  readonly skills?: readonly {