@opencode/ai 2.0.14 → 2.0.16

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 (208) hide show
  1. package/README.md +399 -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 +83 -0
  9. package/dist/generation.js +113 -0
  10. package/dist/image-client.d.ts +16 -7
  11. package/dist/image-client.js +29 -13
  12. package/dist/image.d.ts +1410 -81
  13. package/dist/image.js +97 -67
  14. package/dist/index.d.ts +17 -2
  15. package/dist/index.js +12 -1
  16. package/dist/llm.d.ts +9 -1
  17. package/dist/media-model.d.ts +44 -0
  18. package/dist/media-model.js +49 -0
  19. package/dist/media.d.ts +213 -0
  20. package/dist/media.js +227 -0
  21. package/dist/promise.d.ts +974 -0
  22. package/dist/promise.js +81 -0
  23. package/dist/protocols/alibaba-chat.d.ts +12 -0
  24. package/dist/protocols/alibaba-responses.d.ts +2 -2
  25. package/dist/protocols/anthropic-messages.js +8 -19
  26. package/dist/protocols/assemblyai-transcription.d.ts +40 -0
  27. package/dist/protocols/assemblyai-transcription.js +138 -0
  28. package/dist/protocols/bedrock-converse.d.ts +4 -4
  29. package/dist/protocols/bedrock-converse.js +6 -17
  30. package/dist/protocols/bfl-images.d.ts +32 -0
  31. package/dist/protocols/bfl-images.js +153 -0
  32. package/dist/protocols/cartesia-speech.d.ts +127 -0
  33. package/dist/protocols/cartesia-speech.js +126 -0
  34. package/dist/protocols/deepgram-speech.d.ts +119 -0
  35. package/dist/protocols/deepgram-speech.js +92 -0
  36. package/dist/protocols/deepgram-transcription.d.ts +25 -0
  37. package/dist/protocols/deepgram-transcription.js +129 -0
  38. package/dist/protocols/elevenlabs-speech.d.ts +122 -0
  39. package/dist/protocols/elevenlabs-speech.js +115 -0
  40. package/dist/protocols/fal-images.d.ts +24 -0
  41. package/dist/protocols/fal-images.js +114 -0
  42. package/dist/protocols/fal-video.d.ts +29 -0
  43. package/dist/protocols/fal-video.js +88 -0
  44. package/dist/protocols/gemini.d.ts +30 -9
  45. package/dist/protocols/gemini.js +45 -35
  46. package/dist/protocols/google-images.d.ts +9 -21
  47. package/dist/protocols/google-images.js +158 -133
  48. package/dist/protocols/google-speech.d.ts +130 -0
  49. package/dist/protocols/google-speech.js +84 -0
  50. package/dist/protocols/google-transcription.d.ts +173 -0
  51. package/dist/protocols/google-transcription.js +138 -0
  52. package/dist/protocols/google-video.d.ts +26 -0
  53. package/dist/protocols/google-video.js +158 -0
  54. package/dist/protocols/meta-images.d.ts +7 -12
  55. package/dist/protocols/meta-images.js +85 -66
  56. package/dist/protocols/meta-responses.d.ts +4 -4
  57. package/dist/protocols/meta-responses.js +1 -1
  58. package/dist/protocols/mistral-chat.js +7 -6
  59. package/dist/protocols/open-responses.d.ts +17 -9
  60. package/dist/protocols/open-responses.js +24 -14
  61. package/dist/protocols/openai-chat.d.ts +118 -1
  62. package/dist/protocols/openai-chat.js +125 -44
  63. package/dist/protocols/openai-compatible-chat.d.ts +12 -0
  64. package/dist/protocols/openai-compatible-responses.d.ts +2 -2
  65. package/dist/protocols/openai-images.d.ts +128 -18
  66. package/dist/protocols/openai-images.js +177 -154
  67. package/dist/protocols/openai-responses.d.ts +15 -15
  68. package/dist/protocols/openai-responses.js +5 -6
  69. package/dist/protocols/openai-speech.d.ts +116 -0
  70. package/dist/protocols/openai-speech.js +98 -0
  71. package/dist/protocols/openai-transcription.d.ts +207 -0
  72. package/dist/protocols/openai-transcription.js +190 -0
  73. package/dist/protocols/replicate-images.d.ts +28 -0
  74. package/dist/protocols/replicate-images.js +133 -0
  75. package/dist/protocols/runway-video.d.ts +38 -0
  76. package/dist/protocols/runway-video.js +146 -0
  77. package/dist/protocols/shared.d.ts +27 -17
  78. package/dist/protocols/shared.js +52 -35
  79. package/dist/protocols/stability-images.d.ts +38 -0
  80. package/dist/protocols/stability-images.js +148 -0
  81. package/dist/protocols/utils/bedrock-media.d.ts +2 -3
  82. package/dist/protocols/utils/bedrock-media.js +4 -4
  83. package/dist/protocols/utils/fal-queue.d.ts +28 -0
  84. package/dist/protocols/utils/fal-queue.js +69 -0
  85. package/dist/protocols/utils/gemini-generate-content.d.ts +65 -0
  86. package/dist/protocols/utils/gemini-generate-content.js +65 -0
  87. package/dist/protocols/utils/gemini-json-schema.d.ts +3 -0
  88. package/dist/protocols/utils/gemini-json-schema.js +76 -0
  89. package/dist/protocols/utils/media-input.d.ts +18 -0
  90. package/dist/protocols/utils/media-input.js +35 -0
  91. package/dist/protocols/utils/responses-compaction.js +6 -5
  92. package/dist/protocols/utils/speech-stream.d.ts +49 -0
  93. package/dist/protocols/utils/speech-stream.js +67 -0
  94. package/dist/protocols/utils/tool-schema.d.ts +2 -2
  95. package/dist/protocols/utils/tool-schema.js +40 -17
  96. package/dist/protocols/utils/tool-stream.d.ts +27 -3
  97. package/dist/protocols/xai-images.d.ts +9 -15
  98. package/dist/protocols/xai-images.js +75 -84
  99. package/dist/protocols/xai-responses.d.ts +2 -2
  100. package/dist/protocols/xai-video.d.ts +34 -0
  101. package/dist/protocols/xai-video.js +147 -0
  102. package/dist/protocols/zai-chat.d.ts +13 -1
  103. package/dist/protocols/zai-images.d.ts +9 -13
  104. package/dist/protocols/zai-images.js +59 -57
  105. package/dist/provider-error.js +3 -0
  106. package/dist/providers/alibaba.d.ts +14 -2
  107. package/dist/providers/amazon-bedrock-mantle.d.ts +14 -2
  108. package/dist/providers/amazon-bedrock.d.ts +2 -2
  109. package/dist/providers/assemblyai.d.ts +25 -0
  110. package/dist/providers/assemblyai.js +29 -0
  111. package/dist/providers/azure.d.ts +18 -6
  112. package/dist/providers/baseten.d.ts +24 -0
  113. package/dist/providers/black-forest-labs.d.ts +25 -0
  114. package/dist/providers/black-forest-labs.js +28 -0
  115. package/dist/providers/cartesia.d.ts +24 -0
  116. package/dist/providers/cartesia.js +22 -0
  117. package/dist/providers/cerebras.d.ts +24 -0
  118. package/dist/providers/cerebras.js +6 -1
  119. package/dist/providers/cloudflare-ai-gateway.d.ts +30 -6
  120. package/dist/providers/cloudflare-workers-ai.d.ts +24 -0
  121. package/dist/providers/deepgram.d.ts +29 -0
  122. package/dist/providers/deepgram.js +31 -0
  123. package/dist/providers/deepinfra.d.ts +24 -0
  124. package/dist/providers/deepinfra.js +6 -1
  125. package/dist/providers/deepseek.d.ts +24 -0
  126. package/dist/providers/elevenlabs.d.ts +24 -0
  127. package/dist/providers/elevenlabs.js +28 -0
  128. package/dist/providers/fal.d.ts +29 -0
  129. package/dist/providers/fal.js +33 -0
  130. package/dist/providers/fireworks.d.ts +24 -0
  131. package/dist/providers/google-vertex-chat.d.ts +12 -0
  132. package/dist/providers/google-vertex-responses.d.ts +2 -2
  133. package/dist/providers/google-vertex.d.ts +10 -3
  134. package/dist/providers/google.d.ts +25 -3
  135. package/dist/providers/google.js +11 -2
  136. package/dist/providers/groq.d.ts +24 -0
  137. package/dist/providers/index.d.ts +10 -0
  138. package/dist/providers/index.js +10 -0
  139. package/dist/providers/meta.d.ts +14 -2
  140. package/dist/providers/minimax.d.ts +14 -2
  141. package/dist/providers/moonshot.d.ts +14 -2
  142. package/dist/providers/moonshot.js +3 -3
  143. package/dist/providers/openai-compatible-responses.d.ts +2 -2
  144. package/dist/providers/openai-compatible.d.ts +12 -0
  145. package/dist/providers/openai.d.ts +25 -3
  146. package/dist/providers/openai.js +10 -1
  147. package/dist/providers/openrouter.d.ts +67 -0
  148. package/dist/providers/openrouter.js +13 -1
  149. package/dist/providers/replicate.d.ts +25 -0
  150. package/dist/providers/replicate.js +22 -0
  151. package/dist/providers/runway.d.ts +24 -0
  152. package/dist/providers/runway.js +22 -0
  153. package/dist/providers/stability.d.ts +28 -0
  154. package/dist/providers/stability.js +23 -0
  155. package/dist/providers/togetherai.d.ts +24 -0
  156. package/dist/providers/vercel-ai-gateway.d.ts +41 -0
  157. package/dist/providers/vercel-ai-gateway.js +85 -0
  158. package/dist/providers/xai.d.ts +17 -0
  159. package/dist/providers/xai.js +5 -2
  160. package/dist/providers/zai-coding-plan.d.ts +15 -3
  161. package/dist/providers/zai.d.ts +13 -1
  162. package/dist/route/auth.d.ts +4 -1
  163. package/dist/route/auth.js +6 -0
  164. package/dist/route/client.d.ts +9 -1
  165. package/dist/route/endpoint.d.ts +10 -10
  166. package/dist/route/executor-service.d.ts +12 -0
  167. package/dist/route/executor-service.js +3 -0
  168. package/dist/route/executor.d.ts +4 -9
  169. package/dist/route/executor.js +3 -3
  170. package/dist/route/framing.d.ts +5 -1
  171. package/dist/route/framing.js +9 -0
  172. package/dist/route/index.d.ts +2 -0
  173. package/dist/route/index.js +2 -0
  174. package/dist/route/media-protocol.d.ts +158 -0
  175. package/dist/route/media-protocol.js +96 -0
  176. package/dist/route/media.d.ts +97 -0
  177. package/dist/route/media.js +236 -0
  178. package/dist/schema/errors.d.ts +13 -3
  179. package/dist/schema/errors.js +7 -0
  180. package/dist/schema/events.d.ts +557 -40
  181. package/dist/schema/events.js +35 -2
  182. package/dist/schema/messages.d.ts +95 -8
  183. package/dist/schema/messages.js +8 -6
  184. package/dist/schema/options.d.ts +6 -3
  185. package/dist/schema/options.js +6 -2
  186. package/dist/speech-client.d.ts +21 -0
  187. package/dist/speech-client.js +25 -0
  188. package/dist/speech.d.ts +1150 -0
  189. package/dist/speech.js +119 -0
  190. package/dist/testing.d.ts +72 -8
  191. package/dist/transcription-client.d.ts +28 -0
  192. package/dist/transcription-client.js +44 -0
  193. package/dist/transcription.d.ts +1504 -0
  194. package/dist/transcription.js +133 -0
  195. package/dist/utils/bytes.d.ts +1 -0
  196. package/dist/utils/bytes.js +10 -0
  197. package/dist/utils/media-type.d.ts +7 -0
  198. package/dist/utils/media-type.js +70 -0
  199. package/dist/utils/sanitize.js +3 -1
  200. package/dist/video-client.d.ts +28 -0
  201. package/dist/video-client.js +40 -0
  202. package/dist/video.d.ts +1359 -0
  203. package/dist/video.js +119 -0
  204. package/package.json +7 -3
  205. package/dist/protocols/utils/gemini-tool-schema.d.ts +0 -2
  206. package/dist/protocols/utils/gemini-tool-schema.js +0 -103
  207. package/dist/protocols/utils/image-input.d.ts +0 -21
  208. package/dist/protocols/utils/image-input.js +0 -20
@@ -1,15 +1,23 @@
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 { JsonObject, ProviderShared, optionalNull } from "./shared.js";
7
- import { ImageInputs } from "./utils/image-input.js";
8
+ import { MediaInput } from "./utils/media-input.js";
9
+ const ADAPTER = "meta-images";
10
+ const NAME = "Meta Images";
11
+ const PROVIDER = ProviderID.make("meta");
12
+ // ---------------------------------------------------------------------------
13
+ // 2. Request body and response schemas
14
+ // ---------------------------------------------------------------------------
8
15
  const Body = Schema.StructWithRest(Schema.Struct({
9
16
  model: Schema.String,
10
17
  prompt: Schema.String,
11
18
  images: Schema.optional(Schema.Array(JsonObject)),
12
19
  n: Schema.optional(Schema.Number),
20
+ /** Aspect ratio hint, not an exact output resolution. */
13
21
  size: Schema.optional(Schema.String),
14
22
  output_format: Schema.optional(Schema.String),
15
23
  response_format: Schema.optional(Schema.String),
@@ -25,65 +33,76 @@ const Response = Schema.Struct({
25
33
  total_tokens: Schema.optional(Schema.Number),
26
34
  })),
27
35
  });
28
- export const model = (input) => {
29
- const route = {
30
- id: "meta-images",
31
- generate: Effect.fn("MetaImages.generate")(function* (request, execute) {
32
- const http = mergeHttpOptions(request.model.http, request.http);
33
- const images = yield* Effect.forEach(request.images ?? [], (image) => {
34
- if (image.type === "bytes")
35
- return Effect.succeed({ image_url: ImageInputs.dataUrl(image) });
36
- if (image.type === "url")
37
- return Effect.succeed({ image_url: image.url });
38
- return ImageInputs.invalid("Meta Images accepts image bytes and URLs");
39
- });
40
- const { outputFormat, responseFormat, reasoningStrength, toolEnablement, ...native } = request.options ?? {};
41
- const payload = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Body))(mergeJsonRecords({
42
- model: request.model.id,
43
- prompt: request.prompt,
44
- images: images.length === 0 ? undefined : images,
45
- output_format: outputFormat,
46
- response_format: responseFormat,
47
- reasoning_strength: reasoningStrength,
48
- tool_enablement: toolEnablement,
49
- }, native, http?.body));
50
- const body = ProviderShared.encodeJson(payload);
51
- const url = new URL(`${input.baseURL.replace(/\/$/, "")}/images/${images.length === 0 ? "generations" : "edits"}`);
52
- Object.entries(http?.query ?? {}).forEach(([key, value]) => url.searchParams.set(key, value));
53
- const headers = yield* Auth.toEffect(input.auth)({
54
- request,
55
- method: "POST",
56
- url: url.toString(),
57
- body,
58
- headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
59
- });
60
- const response = yield* execute(HttpClientRequest.post(url.toString()).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(body, "application/json")));
61
- const output = yield* ProviderShared.imageResponse("meta-images", "Meta Images", response);
62
- const decoded = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(Response))(output.body).pipe(Effect.mapError((cause) => output.invalid("Meta Images returned an invalid response", cause)));
63
- const format = decoded.output_format ?? payload.output_format ?? "webp";
64
- const generated = yield* Effect.forEach(decoded.data, (item, index) => {
65
- if (item.b64_json)
66
- return Effect.fromResult(Encoding.decodeBase64(item.b64_json)).pipe(Effect.mapError((cause) => output.invalid(`Meta Images result ${index} contains invalid base64`, cause)), Effect.map((data) => new GeneratedImage({ mediaType: `image/${format}`, data })));
67
- if (item.url)
68
- return Effect.succeed(new GeneratedImage({ mediaType: `image/${format}`, data: item.url }));
69
- return output.invalid(`Meta Images result ${index} has neither image data nor a URL`);
70
- });
71
- if (generated.length === 0)
72
- return yield* output.invalid("Meta Images returned no images");
73
- return new ImageResponse({
74
- images: generated,
75
- usage: decoded.usage === undefined
76
- ? undefined
77
- : new Usage({
78
- inputTokens: decoded.usage.input_tokens,
79
- outputTokens: decoded.usage.output_tokens,
80
- totalTokens: decoded.usage.total_tokens,
81
- providerMetadata: { meta: decoded.usage },
82
- }),
83
- providerMetadata: { meta: { outputFormat: format } },
36
+ // ---------------------------------------------------------------------------
37
+ // 5. Request body construction
38
+ // ---------------------------------------------------------------------------
39
+ const isEdit = (request) => (request.images?.length ?? 0) > 0;
40
+ // Meta has no file handles: refs are rejected even when they name this provider.
41
+ const reference = (asset) => ProviderShared.mediaReference(asset, undefined, NAME).pipe(Effect.map((item) => ({ image_url: item.value })));
42
+ const fromRequest = Effect.fn("MetaImages.fromRequest")(function* (request) {
43
+ const images = yield* Effect.forEach(request.images ?? [], reference);
44
+ const { responseFormat, reasoningStrength, toolEnablement, ...native } = request.providerOptions ?? {};
45
+ const payload = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Body))(mergeJsonRecords({
46
+ model: request.model.id,
47
+ prompt: request.prompt,
48
+ images: images.length === 0 ? undefined : images,
49
+ n: request.n,
50
+ size: request.size,
51
+ output_format: request.format,
52
+ response_format: responseFormat,
53
+ reasoning_strength: reasoningStrength,
54
+ tool_enablement: toolEnablement,
55
+ }, native, request.http?.body));
56
+ return MediaProtocol.json(payload);
57
+ });
58
+ // ---------------------------------------------------------------------------
59
+ // 6. Response decoding
60
+ // ---------------------------------------------------------------------------
61
+ const decodeResponse = Effect.fn("MetaImages.decodeResponse")(function* (response, context) {
62
+ const output = yield* MediaProtocol.decodeJson(ADAPTER, NAME, Response)(response);
63
+ const decoded = output.value;
64
+ const requested = context.body.type === "json" ? context.body.value.output_format : undefined;
65
+ const format = decoded.output_format ?? (typeof requested === "string" ? requested : "webp");
66
+ const mediaType = `image/${format}`;
67
+ const images = yield* Effect.forEach(decoded.data, (item, index) => {
68
+ if (item.b64_json)
69
+ return MediaInput.decodedAsset(output.invalid, `${NAME} result ${index}`, item.b64_json, mediaType, {
70
+ info: { format },
84
71
  });
85
- }),
86
- };
87
- return ImageModel.make({ id: input.id, provider: "meta", route, http: input.http });
88
- };
72
+ if (item.url)
73
+ return Effect.succeed(Media.url(item.url, { mediaType, info: { format } }));
74
+ return Effect.fail(output.invalid(`${NAME} result ${index} has neither image data nor a URL`));
75
+ });
76
+ if (images.length === 0)
77
+ return yield* output.invalid(`${NAME} returned no images`);
78
+ return new ImageResponse({
79
+ images,
80
+ usage: decoded.usage === undefined
81
+ ? undefined
82
+ : {
83
+ type: "tokens",
84
+ input: decoded.usage.input_tokens,
85
+ output: decoded.usage.output_tokens,
86
+ total: decoded.usage.total_tokens,
87
+ details: { meta: decoded.usage },
88
+ },
89
+ providerMetadata: { meta: { outputFormat: format } },
90
+ });
91
+ });
92
+ // ---------------------------------------------------------------------------
93
+ // 7. Protocol and route
94
+ // ---------------------------------------------------------------------------
95
+ export const protocol = MediaProtocol.inline({
96
+ id: ADAPTER,
97
+ name: NAME,
98
+ unsupported: ["mask", "aspectRatio", "seed"],
99
+ body: { from: fromRequest },
100
+ response: { decode: decodeResponse },
101
+ });
102
+ export const model = (input) => ImageModel.fromRoute({
103
+ id: ADAPTER,
104
+ provider: PROVIDER,
105
+ protocol,
106
+ path: ({ request }) => `/images/${isEdit(request) ? "edits" : "generations"}`,
107
+ }, input);
89
108
  export * as MetaImages from "./meta-images.js";
@@ -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";
@@ -82,7 +82,7 @@ const fromRequest = Effect.fn("MetaResponses.fromRequest")(function* (request) {
82
82
  ? undefined
83
83
  : yield* Effect.forEach(projected.tools, (tool) => Effect.gen(function* () {
84
84
  if (tool.native === undefined)
85
- return yield* OpenResponses.lowerTool(NAME, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, request.model.compatibility?.toolSchema));
85
+ return yield* OpenResponses.lowerTool(NAME, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, request.model));
86
86
  return yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(NativeTool))(tool.native.meta);
87
87
  })),
88
88
  tool_choice: OpenResponses.allowedToolChoice(request) ??
@@ -141,13 +141,14 @@ const MistralEvent = Schema.StructWithRest(Schema.Struct({
141
141
  }), [Schema.Record(Schema.String, Schema.Unknown)]);
142
142
  const MistralStreamEvent = Schema.Union([Schema.Literal(DONE), Protocol.jsonEvent(MistralEvent)]);
143
143
  const lowerMedia = Effect.fn("MistralChat.lowerMedia")(function* (part) {
144
- const media = ProviderShared.normalizeMedia(part);
145
- const url = typeof part.data === "string" && /^(?:https?:|data:)/.test(part.data) ? part.data : media.dataUrl;
146
- if (media.mime.startsWith("image/"))
144
+ const mime = part.media.mediaType.toLowerCase();
145
+ const url = ProviderShared.mediaUrl(part.media) ??
146
+ (yield* ProviderShared.requireInlineMedia("Mistral Chat", part.media)).dataUrl;
147
+ if (mime.startsWith("image/"))
147
148
  return { type: "image_url", image_url: url };
148
- if (media.mime === "application/pdf")
149
+ if (mime === "application/pdf")
149
150
  return { type: "document_url", document_url: url };
150
- return yield* ProviderShared.invalidRequest(`Mistral Chat does not support media type ${part.mediaType}`);
151
+ return yield* ProviderShared.invalidRequest(`Mistral Chat does not support media type ${part.media.mediaType}`);
151
152
  });
152
153
  const lowerUser = Effect.fn("MistralChat.lowerUser")(function* (message) {
153
154
  const content = [];
@@ -227,7 +228,7 @@ const lowerToolResults = Effect.fn("MistralChat.lowerToolResults")(function* (me
227
228
  content.push({ type: "text", text: item.text });
228
229
  continue;
229
230
  }
230
- content.push(yield* lowerMedia({ type: "media", mediaType: item.mime, data: item.uri, filename: item.name }));
231
+ content.push(yield* lowerMedia(ProviderShared.toolFileMedia(item)));
231
232
  }
232
233
  output.push({
233
234
  role: "tool",
@@ -2,7 +2,7 @@ import { Effect, Schema } from "effect";
2
2
  import { HttpTransport } from "../route/transport/index.js";
3
3
  import { Protocol } from "../route/protocol.js";
4
4
  import { AIError, LLMEvent, Usage, type LLMRequest, type MediaPart, type ProviderMetadata, type ToolDefinition } from "../schema/index.js";
5
- import { ProviderShared } from "./shared.js";
5
+ import type { Media } from "../media.js";
6
6
  import { OpenResponsesOptions } from "./utils/open-responses-options.js";
7
7
  import { Lifecycle } from "./utils/lifecycle.js";
8
8
  import { ToolStream } from "./utils/tool-stream.js";
@@ -689,7 +689,7 @@ export interface ProviderAdapter {
689
689
  }, AIError>;
690
690
  readonly lowerMedia?: (input: {
691
691
  readonly part: MediaPart;
692
- readonly media: ProviderShared.NormalizedMedia;
692
+ readonly media: Media.Inline | undefined;
693
693
  readonly request: LLMRequest;
694
694
  }) => MediaInput | undefined;
695
695
  readonly restoreHostedToolItem?: (item: unknown) => HostedToolReplayItem | undefined;
@@ -895,8 +895,8 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
895
895
  } | {
896
896
  readonly type: "input_file";
897
897
  readonly filename: string;
898
- readonly detail?: string | undefined;
899
898
  readonly file_data?: string | undefined;
899
+ readonly detail?: string | undefined;
900
900
  readonly file_url?: string | undefined;
901
901
  } | {
902
902
  readonly type: "input_text";
@@ -932,8 +932,8 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
932
932
  } | {
933
933
  readonly type: "input_file";
934
934
  readonly filename: string;
935
- readonly detail?: string | undefined;
936
935
  readonly file_data?: string | undefined;
936
+ readonly detail?: string | undefined;
937
937
  readonly file_url?: string | undefined;
938
938
  } | {
939
939
  readonly type: "input_text";
@@ -1177,6 +1177,14 @@ export declare const step: (state: ParserState, event: NormalizedEvent) => AIErr
1177
1177
  };
1178
1178
  } | undefined;
1179
1179
  readonly namespace?: string | undefined;
1180
+ } | {
1181
+ readonly type: "media";
1182
+ readonly media: Media.Asset;
1183
+ readonly providerMetadata?: {
1184
+ readonly [x: string]: {
1185
+ readonly [x: string]: unknown;
1186
+ };
1187
+ } | undefined;
1180
1188
  } | {
1181
1189
  readonly type: "step-finish";
1182
1190
  readonly reason: {
@@ -1205,12 +1213,12 @@ export declare const step: (state: ParserState, event: NormalizedEvent) => AIErr
1205
1213
  } | {
1206
1214
  readonly type: "provider-error";
1207
1215
  readonly message: string;
1216
+ readonly classification?: "context-overflow" | "payload-too-large" | undefined;
1208
1217
  readonly providerMetadata?: {
1209
1218
  readonly [x: string]: {
1210
1219
  readonly [x: string]: unknown;
1211
1220
  };
1212
1221
  } | undefined;
1213
- readonly classification?: "context-overflow" | "payload-too-large" | undefined;
1214
1222
  })[]], AIError, never> | Effect.Effect<StepResult, never, never>;
1215
1223
  /**
1216
1224
  * The provider-neutral Open Responses protocol. Provider-specific Responses
@@ -1284,8 +1292,8 @@ export declare const protocol: Protocol<{
1284
1292
  } | {
1285
1293
  readonly type: "input_file";
1286
1294
  readonly filename: string;
1287
- readonly detail?: string | undefined;
1288
1295
  readonly file_data?: string | undefined;
1296
+ readonly detail?: string | undefined;
1289
1297
  readonly file_url?: string | undefined;
1290
1298
  } | {
1291
1299
  readonly type: "input_text";
@@ -1321,8 +1329,8 @@ export declare const protocol: Protocol<{
1321
1329
  } | {
1322
1330
  readonly type: "input_file";
1323
1331
  readonly filename: string;
1324
- readonly detail?: string | undefined;
1325
1332
  readonly file_data?: string | undefined;
1333
+ readonly detail?: string | undefined;
1326
1334
  readonly file_url?: string | undefined;
1327
1335
  } | {
1328
1336
  readonly type: "input_text";
@@ -1505,8 +1513,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
1505
1513
  } | {
1506
1514
  readonly type: "input_file";
1507
1515
  readonly filename: string;
1508
- readonly detail?: string | undefined;
1509
1516
  readonly file_data?: string | undefined;
1517
+ readonly detail?: string | undefined;
1510
1518
  readonly file_url?: string | undefined;
1511
1519
  } | {
1512
1520
  readonly type: "input_text";
@@ -1542,8 +1550,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
1542
1550
  } | {
1543
1551
  readonly type: "input_file";
1544
1552
  readonly filename: string;
1545
- readonly detail?: string | undefined;
1546
1553
  readonly file_data?: string | undefined;
1554
+ readonly detail?: string | undefined;
1547
1555
  readonly file_url?: string | undefined;
1548
1556
  } | {
1549
1557
  readonly type: "input_text";
@@ -335,27 +335,27 @@ const lowerReasoning = (part, providerMetadataKey) => {
335
335
  };
336
336
  };
337
337
  const lowerMedia = Effect.fn("OpenResponses.lowerMedia")(function* (part, request, adapter, target) {
338
- const media = ProviderShared.normalizeMedia(part);
338
+ const media = part.media.inline();
339
339
  const providerMedia = adapter.lowerMedia?.({ part, media, request });
340
340
  if (providerMedia)
341
341
  return providerMedia;
342
342
  const detail = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenResponsesInputImage.fields.detail))(part.providerMetadata?.[metadataKey(request.model)]?.detail);
343
- const url = typeof part.data === "string" && (part.data.startsWith("https://") || part.data.startsWith("http://"))
344
- ? part.data
345
- : undefined;
346
- if (!media.mime.startsWith("image/")) {
347
- if (target === "tool-result" && media.mime.startsWith("video/"))
348
- return { type: "input_video", video_url: url ?? media.dataUrl };
343
+ const mime = part.media.mediaType.toLowerCase();
344
+ const url = ProviderShared.mediaUrl(part.media);
345
+ const location = url ?? (yield* ProviderShared.requireInlineMedia(adapter.name, part.media)).dataUrl;
346
+ if (part.media.kind !== "image") {
347
+ if (target === "tool-result" && part.media.kind === "video")
348
+ return { type: "input_video", video_url: location };
349
349
  return {
350
350
  type: "input_file",
351
- filename: part.filename ?? (media.mime === "application/pdf" ? "document.pdf" : "file"),
351
+ filename: part.filename ?? (mime === "application/pdf" ? "document.pdf" : "file"),
352
352
  detail,
353
- ...(url ? { file_url: url } : { file_data: media.dataUrl }),
353
+ ...(url ? { file_url: url } : { file_data: location }),
354
354
  };
355
355
  }
356
356
  return {
357
357
  type: "input_image",
358
- image_url: url ?? media.dataUrl,
358
+ image_url: location,
359
359
  detail,
360
360
  };
361
361
  });
@@ -377,12 +377,12 @@ const lowerMessageMedia = Effect.fnUntraced(function* (part, request, adapter) {
377
377
  const lowerToolResultContentItem = Effect.fnUntraced(function* (item, request, adapter) {
378
378
  if (item.type === "text")
379
379
  return { type: "input_text", text: item.text };
380
- return yield* lowerMedia({ type: "media", mediaType: item.mime, data: item.uri, filename: item.name }, request, adapter, "tool-result");
380
+ return yield* lowerMedia(ProviderShared.toolFileMedia(item), request, adapter, "tool-result");
381
381
  });
382
382
  const lowerHostedToolResultContentItem = Effect.fnUntraced(function* (item, request, adapter) {
383
383
  if (item.type === "text")
384
384
  return { type: "input_text", text: item.text };
385
- return yield* lowerMessageMedia({ type: "media", mediaType: item.mime, data: item.uri, filename: item.name }, request, adapter);
385
+ return yield* lowerMessageMedia(ProviderShared.toolFileMedia(item), request, adapter);
386
386
  });
387
387
  const lowerToolResultOutput = Effect.fnUntraced(function* (part, request, adapter) {
388
388
  // Text/json/error results are encoded as a plain string for backward
@@ -513,11 +513,22 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
513
513
  });
514
514
  continue;
515
515
  }
516
+ if (part.type === "media") {
517
+ flushText();
518
+ // Responses has no assistant-authored image item; replay generated media (e.g. from Gemini) as user input.
519
+ input.push({
520
+ type: "message",
521
+ role: "user",
522
+ content: [yield* lowerMessageMedia(part, request, adapter)],
523
+ });
524
+ continue;
525
+ }
516
526
  return yield* ProviderShared.unsupportedContent(adapter.name, "assistant", [
517
527
  "text",
518
528
  "reasoning",
519
529
  "tool-call",
520
530
  "tool-result",
531
+ "media",
521
532
  ]);
522
533
  }
523
534
  flushText();
@@ -592,7 +603,6 @@ export const allowedToolChoice = (request) => {
592
603
  };
593
604
  export const fromRequestWithAdapter = Effect.fn("OpenResponses.fromRequestWithAdapter")(function* (request, adapter) {
594
605
  const projected = ProviderShared.flattenToolRequest(request);
595
- const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
596
606
  return {
597
607
  ...(yield* lowerConversation(projected.request, adapter)),
598
608
  ...lowerGeneration(request),
@@ -600,7 +610,7 @@ export const fromRequestWithAdapter = Effect.fn("OpenResponses.fromRequestWithAd
600
610
  ? undefined
601
611
  : yield* Effect.forEach(projected.tools, (tool) => tool.native !== undefined && adapter.nativeTool
602
612
  ? adapter.nativeTool(tool.native)
603
- : lowerTool(adapter.name, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, toolSchemaCompatibility))),
613
+ : lowerTool(adapter.name, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, request.model))),
604
614
  tool_choice: allowedToolChoice(request) ??
605
615
  (request.toolChoice ? yield* lowerToolChoice(adapter.name, request.toolChoice) : undefined),
606
616
  };