@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
@@ -0,0 +1,142 @@
1
+ import { Duration, 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, optionalArray } from "./shared.js";
8
+ const route = MediaProtocol.identity({ id: "google-video", name: "Google Veo", provider: "google" });
9
+ export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
10
+ /** Veo keeps generated files for two days; the asset carries that deadline so callers materialize in time. */
11
+ const FILE_RETENTION = Duration.days(2);
12
+ // ---------------------------------------------------------------------------
13
+ // 2. Token and response schemas
14
+ // ---------------------------------------------------------------------------
15
+ /** The long-running operation name, e.g. `models/veo-3.1-generate-preview/operations/abc123`. */
16
+ export const Token = Schema.Struct({ operation: Schema.String });
17
+ const StartResponse = Schema.Struct({ name: Schema.String });
18
+ const Operation = Schema.Struct({
19
+ done: Schema.optional(Schema.Boolean),
20
+ error: Schema.optional(Schema.Struct({ message: Schema.optional(Schema.String) })),
21
+ response: Schema.optional(Schema.Struct({
22
+ generateVideoResponse: Schema.optional(Schema.Struct({
23
+ generatedSamples: optionalArray(Schema.Struct({
24
+ video: Schema.optional(Schema.Struct({
25
+ uri: Schema.optional(Schema.String),
26
+ mimeType: Schema.optional(Schema.String),
27
+ })),
28
+ })),
29
+ raiMediaFilteredCount: Schema.optional(Schema.Number),
30
+ raiMediaFilteredReasons: optionalArray(Schema.String),
31
+ })),
32
+ })),
33
+ metadata: Schema.optional(Schema.Unknown),
34
+ });
35
+ // ---------------------------------------------------------------------------
36
+ // 5. Request body construction
37
+ // ---------------------------------------------------------------------------
38
+ // Veo takes inline media only; a prior Veo output is `Media.url` with transient auth, so materialize it first.
39
+ const inlineMedia = (asset) => ProviderShared.requireInlineMedia(route.name, asset).pipe(Effect.map((inline) => ({ inlineData: { mimeType: inline.mime, data: inline.base64 } })));
40
+ const fromRequest = Effect.fn("GoogleVideo.fromRequest")(function* (request) {
41
+ if (request.n !== undefined && request.n > 1)
42
+ return yield* route.unsupported("video.n", `${route.name} generates one video per request; call it once per video instead of n=${request.n}`);
43
+ if (request.audio === false)
44
+ return yield* route.unsupported("video.audio", `${route.name} always generates audio; audio: false cannot be honored`);
45
+ if (request.frames?.last !== undefined && request.frames.first === undefined)
46
+ return yield* ProviderShared.invalidRequest(`${route.name} requires frames.first when frames.last is set`);
47
+ const image = request.frames?.first === undefined ? undefined : yield* inlineMedia(request.frames.first);
48
+ const lastFrame = request.frames?.last === undefined ? undefined : yield* inlineMedia(request.frames.last);
49
+ const video = request.video === undefined ? undefined : yield* inlineMedia(request.video);
50
+ const referenceImages = yield* Effect.forEach(request.references ?? [], (asset) => inlineMedia(asset).pipe(Effect.map((image) => ({ image, referenceType: "asset" }))));
51
+ return MediaProtocol.json(mergeJsonRecords({
52
+ instances: [
53
+ {
54
+ prompt: request.prompt,
55
+ image,
56
+ lastFrame,
57
+ referenceImages: referenceImages.length === 0 ? undefined : referenceImages,
58
+ video,
59
+ },
60
+ ],
61
+ parameters: mergeJsonRecords({
62
+ aspectRatio: request.aspectRatio,
63
+ resolution: request.resolution,
64
+ durationSeconds: request.durationSeconds,
65
+ negativePrompt: request.negativePrompt,
66
+ seed: request.seed,
67
+ }, request.providerOptions),
68
+ }, request.http?.body) ?? {});
69
+ });
70
+ // ---------------------------------------------------------------------------
71
+ // 6. Response decoding
72
+ // ---------------------------------------------------------------------------
73
+ const decodeStart = route.decodeStarted(StartResponse, (value) => ({
74
+ token: { operation: value.name },
75
+ snapshot: { id: value.name, status: "running" },
76
+ }));
77
+ // Operations carry no status string: not done is running, done with `error` failed, otherwise completed.
78
+ const statusOf = (operation) => {
79
+ if (operation.done !== true)
80
+ return "running";
81
+ return operation.error === undefined ? "completed" : "failed";
82
+ };
83
+ const decodeOperation = route.decodeJson(Operation);
84
+ const decodeStatus = Effect.fn("GoogleVideo.decodeStatus")(function* (response, context) {
85
+ const output = yield* decodeOperation(response);
86
+ return { id: context.token.operation, status: statusOf(output.value) };
87
+ });
88
+ const decodeResult = Effect.fn("GoogleVideo.decodeResult")(function* (response, context) {
89
+ const output = yield* decodeOperation(response);
90
+ const operation = output.value;
91
+ const status = statusOf(operation);
92
+ if (status === "running")
93
+ return yield* output.pending(context.token.operation);
94
+ if (status === "failed")
95
+ return yield* output.ended("failed", `${route.name} operation failed${operation.error?.message === undefined ? "" : `: ${operation.error.message}`}`);
96
+ const generated = operation.response?.generateVideoResponse;
97
+ // Downloads require the same API key as the poll; the asset carries it transiently and follows the redirect.
98
+ const videos = yield* Effect.forEach((generated?.generatedSamples ?? []).flatMap((sample) => sample.video?.uri === undefined ? [] : [{ uri: sample.video.uri, mimeType: sample.video.mimeType }]), (video) => MediaProtocol.expiringUrl(video.uri, FILE_RETENTION, {
99
+ mediaType: video.mimeType ?? "video/mp4",
100
+ headers: context.auth,
101
+ }));
102
+ const reasons = generated?.raiMediaFilteredReasons ?? [];
103
+ const notices = reasons.map((reason) => ({
104
+ type: "filtered",
105
+ message: `${route.name} filtered media: ${reason}`,
106
+ providerMetadata: { google: { raiMediaFilteredReason: reason } },
107
+ }));
108
+ if (videos.length === 0 && (reasons.length > 0 || (generated?.raiMediaFilteredCount ?? 0) > 0))
109
+ return yield* output.contentPolicy(`${route.name} filtered every video${reasons.length === 0 ? "" : `: ${reasons.join("; ")}`}`);
110
+ if (videos.length === 0)
111
+ return yield* output.invalid(`${route.name} operation completed without any video`);
112
+ return new VideoResponse({
113
+ videos,
114
+ notices: notices.length === 0 ? undefined : notices,
115
+ providerMetadata: {
116
+ google: {
117
+ operation: context.token.operation,
118
+ raiMediaFilteredCount: generated?.raiMediaFilteredCount,
119
+ metadata: operation.metadata,
120
+ },
121
+ },
122
+ });
123
+ });
124
+ // ---------------------------------------------------------------------------
125
+ // 7. Protocol and route
126
+ // ---------------------------------------------------------------------------
127
+ const operationPath = (token) => `/${token.operation}`;
128
+ export const protocol = MediaProtocol.queued(route, {
129
+ token: Token,
130
+ start: { body: { from: fromRequest }, decode: decodeStart },
131
+ status: { path: operationPath, decode: decodeStatus },
132
+ result: { path: operationPath, decode: decodeResult },
133
+ });
134
+ export const model = (input) => VideoModel.fromRoute({
135
+ protocol,
136
+ baseURL: DEFAULT_BASE_URL,
137
+ path: ({ request }) => `/models/${request.model.id}:predictLongRunning`,
138
+ }, input);
139
+ export const GoogleVideo = {
140
+ protocol,
141
+ model,
142
+ };
@@ -1,7 +1,8 @@
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
- type OpenString<Known extends string> = Known | (string & {});
4
+ import { type OpenString } from "../schema/index.js";
5
+ export declare const DEFAULT_BASE_URL = "https://api.meta.ai/v1";
5
6
  /** Provider-native options. Common fields (`n`, `size`, `format`, `images`) live on the request. */
6
7
  export type ImageOptions = {
7
8
  readonly responseFormat?: OpenString<"b64_json" | "url">;
@@ -15,7 +16,5 @@ export type ImageOptions = {
15
16
  };
16
17
  export type Request = ImageRequestFor<ImageOptions>;
17
18
  export declare const protocol: MediaProtocol.Inline<Request, ImageResponse>;
18
- export declare const model: (input: MediaRoute.ModelInput & {
19
- readonly baseURL: string;
20
- }) => ImageModel<ImageOptions>;
19
+ export declare const model: (input: MediaRoute.ModelInput) => ImageModel<ImageOptions>;
21
20
  export * as MetaImages from "./meta-images.js";
@@ -3,12 +3,11 @@ 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 { JsonObject, ProviderShared, optionalNull } from "./shared.js";
8
8
  import { MediaInput } from "./utils/media-input.js";
9
- const ADAPTER = "meta-images";
10
- const NAME = "Meta Images";
11
- const PROVIDER = ProviderID.make("meta");
9
+ const route = MediaProtocol.identity({ id: "meta-images", name: "Meta Images", provider: "meta" });
10
+ export const DEFAULT_BASE_URL = "https://api.meta.ai/v1";
12
11
  // ---------------------------------------------------------------------------
13
12
  // 2. Request body and response schemas
14
13
  // ---------------------------------------------------------------------------
@@ -37,15 +36,8 @@ const Response = Schema.Struct({
37
36
  // 5. Request body construction
38
37
  // ---------------------------------------------------------------------------
39
38
  const isEdit = (request) => (request.images?.length ?? 0) > 0;
40
- const reference = (asset) => {
41
- const inline = asset.inline();
42
- if (inline)
43
- return Effect.succeed({ image_url: inline.dataUrl });
44
- const url = ProviderShared.mediaUrl(asset);
45
- if (url)
46
- return Effect.succeed({ image_url: url });
47
- return Effect.fail(ProviderShared.invalidRequest(`${NAME} accepts image bytes and URLs`));
48
- };
39
+ // Meta has no file handles: refs are rejected even when they name this provider.
40
+ const reference = (asset) => ProviderShared.mediaReference(asset, undefined, route.name).pipe(Effect.map((item) => ({ image_url: item.value })));
49
41
  const fromRequest = Effect.fn("MetaImages.fromRequest")(function* (request) {
50
42
  const images = yield* Effect.forEach(request.images ?? [], reference);
51
43
  const { responseFormat, reasoningStrength, toolEnablement, ...native } = request.providerOptions ?? {};
@@ -65,23 +57,18 @@ const fromRequest = Effect.fn("MetaImages.fromRequest")(function* (request) {
65
57
  // ---------------------------------------------------------------------------
66
58
  // 6. Response decoding
67
59
  // ---------------------------------------------------------------------------
60
+ const decodeDocument = route.decodeJson(Response);
68
61
  const decodeResponse = Effect.fn("MetaImages.decodeResponse")(function* (response, context) {
69
- const output = yield* MediaProtocol.decodeJson(ADAPTER, NAME, Response)(response);
62
+ const output = yield* decodeDocument(response);
70
63
  const decoded = output.value;
71
64
  const requested = context.body.type === "json" ? context.body.value.output_format : undefined;
72
65
  const format = decoded.output_format ?? (typeof requested === "string" ? requested : "webp");
73
66
  const mediaType = `image/${format}`;
74
- const images = yield* Effect.forEach(decoded.data, (item, index) => {
75
- if (item.b64_json)
76
- return MediaInput.decodedAsset(output.invalid, `${NAME} result ${index}`, item.b64_json, mediaType, {
77
- info: { format },
78
- });
79
- if (item.url)
80
- return Effect.succeed(Media.url(item.url, { mediaType, info: { format } }));
81
- return Effect.fail(output.invalid(`${NAME} result ${index} has neither image data nor a URL`));
82
- });
67
+ const images = yield* Effect.forEach(decoded.data, (item, index) => MediaInput.imageOutput(output.invalid, `${route.name} result ${index}`, item, mediaType, {
68
+ info: { format },
69
+ }));
83
70
  if (images.length === 0)
84
- return yield* output.invalid(`${NAME} returned no images`);
71
+ return yield* output.invalid(`${route.name} returned no images`);
85
72
  return new ImageResponse({
86
73
  images,
87
74
  usage: decoded.usage === undefined
@@ -99,17 +86,14 @@ const decodeResponse = Effect.fn("MetaImages.decodeResponse")(function* (respons
99
86
  // ---------------------------------------------------------------------------
100
87
  // 7. Protocol and route
101
88
  // ---------------------------------------------------------------------------
102
- export const protocol = MediaProtocol.inline({
103
- id: ADAPTER,
104
- name: NAME,
89
+ export const protocol = MediaProtocol.inline(route, {
105
90
  unsupported: ["mask", "aspectRatio", "seed"],
106
91
  body: { from: fromRequest },
107
92
  response: { decode: decodeResponse },
108
93
  });
109
94
  export const model = (input) => ImageModel.fromRoute({
110
- id: ADAPTER,
111
- provider: PROVIDER,
112
95
  protocol,
96
+ baseURL: DEFAULT_BASE_URL,
113
97
  path: ({ request }) => `/images/${isEdit(request) ? "edits" : "generations"}`,
114
98
  }, input);
115
99
  export * as MetaImages from "./meta-images.js";
@@ -221,20 +221,20 @@ export declare const protocol: Protocol<{
221
221
  readonly thinking?: {
222
222
  readonly type: "enabled";
223
223
  readonly budget_tokens: number;
224
- readonly display?: (string & {}) | "summarized" | "omitted" | undefined;
224
+ readonly display?: import("../schema/options.js").OpenString<"summarized" | "omitted"> | undefined;
225
225
  readonly block_binding?: {
226
- readonly prefix_mismatch_behavior?: (string & {}) | "error" | "drop_block" | undefined;
226
+ readonly prefix_mismatch_behavior?: import("../schema/options.js").OpenString<"error" | "drop_block"> | undefined;
227
227
  } | undefined;
228
228
  } | {
229
229
  readonly type: "adaptive";
230
- readonly display?: (string & {}) | "summarized" | "omitted" | undefined;
230
+ readonly display?: import("../schema/options.js").OpenString<"summarized" | "omitted"> | undefined;
231
231
  readonly block_binding?: {
232
- readonly prefix_mismatch_behavior?: (string & {}) | "error" | "drop_block" | undefined;
232
+ readonly prefix_mismatch_behavior?: import("../schema/options.js").OpenString<"error" | "drop_block"> | undefined;
233
233
  } | undefined;
234
234
  } | {
235
235
  readonly type: "disabled";
236
236
  } | undefined;
237
- readonly service_tier?: "auto" | (string & {}) | "standard_only" | undefined;
237
+ readonly service_tier?: import("../schema/options.js").OpenString<"auto" | "standard_only"> | undefined;
238
238
  readonly container?: string | {
239
239
  readonly id?: string | null | undefined;
240
240
  readonly skills?: readonly {
@@ -92,8 +92,8 @@ export declare const protocol: Protocol<{
92
92
  } | {
93
93
  readonly type: "input_file";
94
94
  readonly filename: string;
95
- readonly detail?: string | undefined;
96
95
  readonly file_data?: string | undefined;
96
+ readonly detail?: string | undefined;
97
97
  readonly file_url?: string | undefined;
98
98
  } | {
99
99
  readonly type: "input_text";
@@ -129,8 +129,8 @@ export declare const protocol: Protocol<{
129
129
  } | {
130
130
  readonly type: "input_file";
131
131
  readonly filename: string;
132
- readonly detail?: string | undefined;
133
132
  readonly file_data?: string | undefined;
133
+ readonly detail?: string | undefined;
134
134
  readonly file_url?: string | undefined;
135
135
  } | {
136
136
  readonly type: "input_text";
@@ -338,8 +338,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
338
338
  } | {
339
339
  readonly type: "input_file";
340
340
  readonly filename: string;
341
- readonly detail?: string | undefined;
342
341
  readonly file_data?: string | undefined;
342
+ readonly detail?: string | undefined;
343
343
  readonly file_url?: string | undefined;
344
344
  } | {
345
345
  readonly type: "input_text";
@@ -375,8 +375,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
375
375
  } | {
376
376
  readonly type: "input_file";
377
377
  readonly filename: string;
378
- readonly detail?: string | undefined;
379
378
  readonly file_data?: string | undefined;
379
+ readonly detail?: string | undefined;
380
380
  readonly file_url?: string | undefined;
381
381
  } | {
382
382
  readonly type: "input_text";
@@ -5,8 +5,7 @@ import { LLMEvent, LLMRequest, Message, ToolResultPart } from "../schema/index.j
5
5
  import { OpenResponses } from "./open-responses.js";
6
6
  import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
7
7
  import { ResponsesHostedTools } from "./utils/responses-hosted-tools.js";
8
- import { ToolSchemaProjection } from "./utils/tool-schema.js";
9
- import { MetaImage } from "./utils/meta-image.js";
8
+ import { detectMediaType } from "../utils/media-type.js";
10
9
  const ADAPTER = "meta-responses";
11
10
  const NAME = "Meta Responses";
12
11
  export const WebSearch = Schema.Struct({
@@ -82,7 +81,7 @@ const fromRequest = Effect.fn("MetaResponses.fromRequest")(function* (request) {
82
81
  ? undefined
83
82
  : yield* Effect.forEach(projected.tools, (tool) => Effect.gen(function* () {
84
83
  if (tool.native === undefined)
85
- return yield* OpenResponses.lowerTool(NAME, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, request.model.compatibility?.toolSchema));
84
+ return yield* OpenResponses.lowerTool(NAME, tool);
86
85
  return yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(NativeTool))(tool.native.meta);
87
86
  })),
88
87
  tool_choice: OpenResponses.allowedToolChoice(request) ??
@@ -101,7 +100,10 @@ const HOSTED_TOOLS = {
101
100
  if (!item.result)
102
101
  return yield* ProviderShared.eventError(ADAPTER, "Meta returned an image without data", ProviderShared.encodeJson(raw));
103
102
  const data = yield* Effect.fromResult(Encoding.decodeBase64(item.result)).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "Meta returned invalid image base64", ProviderShared.encodeJson(raw), cause)));
104
- const mime = MetaImage.mediaType(data, item.output_format);
103
+ // Responses image items can omit output_format, including when PNG/JPEG was requested.
104
+ const mime = item.output_format === undefined
105
+ ? (detectMediaType(data) ?? "application/octet-stream")
106
+ : `image/${item.output_format}`;
105
107
  return {
106
108
  type: "content",
107
109
  value: [{ type: "file", uri: `data:${mime};base64,${item.result}`, mime }],
@@ -716,9 +716,7 @@ interface ReasoningStreamItem {
716
716
  readonly summaryParts: Readonly<Record<number, ReasoningSummaryStatus>>;
717
717
  readonly deltaIndexes: ReadonlySet<number>;
718
718
  }
719
- export declare const lowerTool: (protocolName: string, tool: ToolDefinition, inputSchema: {
720
- readonly [x: string]: unknown;
721
- }) => Effect.Effect<{
719
+ export declare const lowerTool: (protocolName: string, tool: ToolDefinition) => Effect.Effect<{
722
720
  type: "function";
723
721
  name: string;
724
722
  description: string;
@@ -727,6 +725,9 @@ export declare const lowerTool: (protocolName: string, tool: ToolDefinition, inp
727
725
  };
728
726
  strict: boolean;
729
727
  }, AIError, never>;
728
+ export declare const lowerTools: (tools: ReadonlyArray<ToolDefinition>, adapter: ProviderAdapter) => Effect.Effect<{
729
+ readonly type: string;
730
+ }[], AIError, never>;
730
731
  export declare const lowerToolChoice: (protocolName: string, toolChoice: NonNullable<LLMRequest["toolChoice"]>) => Effect.Effect<"required" | "auto" | "none" | {
731
732
  type: "function";
732
733
  name: string;
@@ -895,8 +896,8 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
895
896
  } | {
896
897
  readonly type: "input_file";
897
898
  readonly filename: string;
898
- readonly detail?: string | undefined;
899
899
  readonly file_data?: string | undefined;
900
+ readonly detail?: string | undefined;
900
901
  readonly file_url?: string | undefined;
901
902
  } | {
902
903
  readonly type: "input_text";
@@ -932,8 +933,8 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
932
933
  } | {
933
934
  readonly type: "input_file";
934
935
  readonly filename: string;
935
- readonly detail?: string | undefined;
936
936
  readonly file_data?: string | undefined;
937
+ readonly detail?: string | undefined;
937
938
  readonly file_url?: string | undefined;
938
939
  } | {
939
940
  readonly type: "input_text";
@@ -1292,8 +1293,8 @@ export declare const protocol: Protocol<{
1292
1293
  } | {
1293
1294
  readonly type: "input_file";
1294
1295
  readonly filename: string;
1295
- readonly detail?: string | undefined;
1296
1296
  readonly file_data?: string | undefined;
1297
+ readonly detail?: string | undefined;
1297
1298
  readonly file_url?: string | undefined;
1298
1299
  } | {
1299
1300
  readonly type: "input_text";
@@ -1329,8 +1330,8 @@ export declare const protocol: Protocol<{
1329
1330
  } | {
1330
1331
  readonly type: "input_file";
1331
1332
  readonly filename: string;
1332
- readonly detail?: string | undefined;
1333
1333
  readonly file_data?: string | undefined;
1334
+ readonly detail?: string | undefined;
1334
1335
  readonly file_url?: string | undefined;
1335
1336
  } | {
1336
1337
  readonly type: "input_text";
@@ -1513,8 +1514,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
1513
1514
  } | {
1514
1515
  readonly type: "input_file";
1515
1516
  readonly filename: string;
1516
- readonly detail?: string | undefined;
1517
1517
  readonly file_data?: string | undefined;
1518
+ readonly detail?: string | undefined;
1518
1519
  readonly file_url?: string | undefined;
1519
1520
  } | {
1520
1521
  readonly type: "input_text";
@@ -1550,8 +1551,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
1550
1551
  } | {
1551
1552
  readonly type: "input_file";
1552
1553
  readonly filename: string;
1553
- readonly detail?: string | undefined;
1554
1554
  readonly file_data?: string | undefined;
1555
+ readonly detail?: string | undefined;
1555
1556
  readonly file_url?: string | undefined;
1556
1557
  } | {
1557
1558
  readonly type: "input_text";
@@ -7,7 +7,6 @@ import { classifyProviderFailure } from "../provider-error.js";
7
7
  import { effortUpdate } from "../effort-updates.js";
8
8
  import { OpenResponsesOptions } from "./utils/open-responses-options.js";
9
9
  import { Lifecycle } from "./utils/lifecycle.js";
10
- import { ToolSchemaProjection } from "./utils/tool-schema.js";
11
10
  import { ToolStream } from "./utils/tool-stream.js";
12
11
  const ADAPTER = "open-responses";
13
12
  const NAME = "Open Responses";
@@ -281,18 +280,19 @@ const BASE_ADAPTER = { id: ADAPTER, name: NAME };
281
280
  // =============================================================================
282
281
  // Request Lowering
283
282
  // =============================================================================
284
- export const lowerTool = Effect.fn("OpenResponses.lowerTool")(function* (protocolName, tool, inputSchema) {
283
+ export const lowerTool = Effect.fn("OpenResponses.lowerTool")(function* (protocolName, tool) {
285
284
  if (tool.native !== undefined)
286
285
  return yield* ProviderShared.invalidRequest(`${protocolName} does not support provider-native tool ${tool.name}`);
287
286
  return {
288
287
  type: "function",
289
288
  name: tool.name,
290
289
  description: tool.description,
291
- parameters: inputSchema,
290
+ parameters: tool.inputSchema,
292
291
  // The common tool definition does not currently express Responses strict-schema policy.
293
292
  strict: false,
294
293
  };
295
294
  });
295
+ export const lowerTools = (tools, adapter) => Effect.forEach(tools, (tool) => tool.native !== undefined && adapter.nativeTool ? adapter.nativeTool(tool.native) : lowerTool(adapter.name, tool));
296
296
  export const lowerToolChoice = (protocolName, toolChoice) => ProviderShared.matchToolChoice(protocolName, toolChoice, {
297
297
  auto: () => "auto",
298
298
  none: () => "none",
@@ -603,15 +603,10 @@ export const allowedToolChoice = (request) => {
603
603
  };
604
604
  export const fromRequestWithAdapter = Effect.fn("OpenResponses.fromRequestWithAdapter")(function* (request, adapter) {
605
605
  const projected = ProviderShared.flattenToolRequest(request);
606
- const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
607
606
  return {
608
607
  ...(yield* lowerConversation(projected.request, adapter)),
609
608
  ...lowerGeneration(request),
610
- tools: projected.tools.length === 0
611
- ? undefined
612
- : yield* Effect.forEach(projected.tools, (tool) => tool.native !== undefined && adapter.nativeTool
613
- ? adapter.nativeTool(tool.native)
614
- : lowerTool(adapter.name, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, toolSchemaCompatibility))),
609
+ tools: projected.tools.length === 0 ? undefined : yield* lowerTools(projected.tools, adapter),
615
610
  tool_choice: allowedToolChoice(request) ??
616
611
  (request.toolChoice ? yield* lowerToolChoice(adapter.name, request.toolChoice) : undefined),
617
612
  };
@@ -59,6 +59,12 @@ export declare const bodyFields: {
59
59
  readonly image_url: Schema.Struct<{
60
60
  readonly url: Schema.String;
61
61
  }>;
62
+ }>, Schema.Struct<{
63
+ readonly type: Schema.Literal<"file">;
64
+ readonly file: Schema.Struct<{
65
+ readonly filename: Schema.String;
66
+ readonly file_data: Schema.String;
67
+ }>;
62
68
  }>]>>]>;
63
69
  }>, Schema.Struct<{
64
70
  readonly role: Schema.Literal<"user">;
@@ -74,6 +80,12 @@ export declare const bodyFields: {
74
80
  readonly image_url: Schema.Struct<{
75
81
  readonly url: Schema.String;
76
82
  }>;
83
+ }>, Schema.Struct<{
84
+ readonly type: Schema.Literal<"file">;
85
+ readonly file: Schema.Struct<{
86
+ readonly filename: Schema.String;
87
+ readonly file_data: Schema.String;
88
+ }>;
77
89
  }>]>>]>;
78
90
  }>, Schema.StructWithRest<Schema.Struct<{
79
91
  readonly role: Schema.Literal<"assistant">;
@@ -155,6 +167,12 @@ declare const OpenAIChatBody: Schema.Struct<{
155
167
  readonly image_url: Schema.Struct<{
156
168
  readonly url: Schema.String;
157
169
  }>;
170
+ }>, Schema.Struct<{
171
+ readonly type: Schema.Literal<"file">;
172
+ readonly file: Schema.Struct<{
173
+ readonly filename: Schema.String;
174
+ readonly file_data: Schema.String;
175
+ }>;
158
176
  }>]>>]>;
159
177
  }>, Schema.Struct<{
160
178
  readonly role: Schema.Literal<"user">;
@@ -170,6 +188,12 @@ declare const OpenAIChatBody: Schema.Struct<{
170
188
  readonly image_url: Schema.Struct<{
171
189
  readonly url: Schema.String;
172
190
  }>;
191
+ }>, Schema.Struct<{
192
+ readonly type: Schema.Literal<"file">;
193
+ readonly file: Schema.Struct<{
194
+ readonly filename: Schema.String;
195
+ readonly file_data: Schema.String;
196
+ }>;
173
197
  }>]>>]>;
174
198
  }>, Schema.StructWithRest<Schema.Struct<{
175
199
  readonly role: Schema.Literal<"assistant">;
@@ -354,6 +378,12 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
354
378
  readonly image_url: {
355
379
  readonly url: string;
356
380
  };
381
+ } | {
382
+ readonly type: "file";
383
+ readonly file: {
384
+ readonly filename: string;
385
+ readonly file_data: string;
386
+ };
357
387
  })[];
358
388
  } | {
359
389
  readonly role: "user";
@@ -369,6 +399,12 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
369
399
  readonly image_url: {
370
400
  readonly url: string;
371
401
  };
402
+ } | {
403
+ readonly type: "file";
404
+ readonly file: {
405
+ readonly filename: string;
406
+ readonly file_data: string;
407
+ };
372
408
  })[];
373
409
  } | {
374
410
  readonly [x: string]: unknown;
@@ -451,6 +487,12 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
451
487
  readonly image_url: {
452
488
  readonly url: string;
453
489
  };
490
+ } | {
491
+ readonly type: "file";
492
+ readonly file: {
493
+ readonly filename: string;
494
+ readonly file_data: string;
495
+ };
454
496
  })[];
455
497
  } | {
456
498
  readonly role: "user";
@@ -466,6 +508,12 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
466
508
  readonly image_url: {
467
509
  readonly url: string;
468
510
  };
511
+ } | {
512
+ readonly type: "file";
513
+ readonly file: {
514
+ readonly filename: string;
515
+ readonly file_data: string;
516
+ };
469
517
  })[];
470
518
  } | {
471
519
  readonly [x: string]: unknown;
@@ -541,6 +589,12 @@ export declare const protocol: Protocol<{
541
589
  readonly image_url: {
542
590
  readonly url: string;
543
591
  };
592
+ } | {
593
+ readonly type: "file";
594
+ readonly file: {
595
+ readonly filename: string;
596
+ readonly file_data: string;
597
+ };
544
598
  })[];
545
599
  } | {
546
600
  readonly role: "user";
@@ -556,6 +610,12 @@ export declare const protocol: Protocol<{
556
610
  readonly image_url: {
557
611
  readonly url: string;
558
612
  };
613
+ } | {
614
+ readonly type: "file";
615
+ readonly file: {
616
+ readonly filename: string;
617
+ readonly file_data: string;
618
+ };
559
619
  })[];
560
620
  } | {
561
621
  readonly [x: string]: unknown;
@@ -708,6 +768,12 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
708
768
  readonly image_url: {
709
769
  readonly url: string;
710
770
  };
771
+ } | {
772
+ readonly type: "file";
773
+ readonly file: {
774
+ readonly filename: string;
775
+ readonly file_data: string;
776
+ };
711
777
  })[];
712
778
  } | {
713
779
  readonly role: "user";
@@ -723,6 +789,12 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
723
789
  readonly image_url: {
724
790
  readonly url: string;
725
791
  };
792
+ } | {
793
+ readonly type: "file";
794
+ readonly file: {
795
+ readonly filename: string;
796
+ readonly file_data: string;
797
+ };
726
798
  })[];
727
799
  } | {
728
800
  readonly [x: string]: unknown;
@@ -807,6 +879,12 @@ export declare const route: Route<{
807
879
  readonly image_url: {
808
880
  readonly url: string;
809
881
  };
882
+ } | {
883
+ readonly type: "file";
884
+ readonly file: {
885
+ readonly filename: string;
886
+ readonly file_data: string;
887
+ };
810
888
  })[];
811
889
  } | {
812
890
  readonly role: "user";
@@ -822,6 +900,12 @@ export declare const route: Route<{
822
900
  readonly image_url: {
823
901
  readonly url: string;
824
902
  };
903
+ } | {
904
+ readonly type: "file";
905
+ readonly file: {
906
+ readonly filename: string;
907
+ readonly file_data: string;
908
+ };
825
909
  })[];
826
910
  } | {
827
911
  readonly [x: string]: unknown;