@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
@@ -291,8 +291,8 @@ export declare const protocol: Protocol<{
291
291
  } | {
292
292
  readonly type: "input_file";
293
293
  readonly filename: string;
294
- readonly detail?: string | undefined;
295
294
  readonly file_data?: string | undefined;
295
+ readonly detail?: string | undefined;
296
296
  readonly file_url?: string | undefined;
297
297
  } | {
298
298
  readonly type: "input_text";
@@ -328,8 +328,8 @@ export declare const protocol: Protocol<{
328
328
  } | {
329
329
  readonly type: "input_file";
330
330
  readonly filename: string;
331
- readonly detail?: string | undefined;
332
331
  readonly file_data?: string | undefined;
332
+ readonly detail?: string | undefined;
333
333
  readonly file_url?: string | undefined;
334
334
  } | {
335
335
  readonly type: "input_text";
@@ -364,8 +364,8 @@ export declare const protocol: Protocol<{
364
364
  readonly type: "image_generation_call";
365
365
  readonly status?: string | undefined;
366
366
  readonly result?: string | null | undefined;
367
- readonly revised_prompt?: string | null | undefined;
368
367
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
368
+ readonly revised_prompt?: string | null | undefined;
369
369
  })[];
370
370
  readonly model: string;
371
371
  readonly stream: true;
@@ -392,9 +392,9 @@ export declare const protocol: Protocol<{
392
392
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
393
393
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
394
394
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
395
+ readonly partial_images?: number | undefined;
395
396
  readonly output_compression?: number | undefined;
396
397
  readonly input_fidelity?: "low" | "high" | undefined;
397
- readonly partial_images?: number | undefined;
398
398
  } | {
399
399
  readonly type: "namespace";
400
400
  readonly name: string;
@@ -574,8 +574,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
574
574
  } | {
575
575
  readonly type: "input_file";
576
576
  readonly filename: string;
577
- readonly detail?: string | undefined;
578
577
  readonly file_data?: string | undefined;
578
+ readonly detail?: string | undefined;
579
579
  readonly file_url?: string | undefined;
580
580
  } | {
581
581
  readonly type: "input_text";
@@ -611,8 +611,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
611
611
  } | {
612
612
  readonly type: "input_file";
613
613
  readonly filename: string;
614
- readonly detail?: string | undefined;
615
614
  readonly file_data?: string | undefined;
615
+ readonly detail?: string | undefined;
616
616
  readonly file_url?: string | undefined;
617
617
  } | {
618
618
  readonly type: "input_text";
@@ -647,8 +647,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
647
647
  readonly type: "image_generation_call";
648
648
  readonly status?: string | undefined;
649
649
  readonly result?: string | null | undefined;
650
- readonly revised_prompt?: string | null | undefined;
651
650
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
651
+ readonly revised_prompt?: string | null | undefined;
652
652
  })[];
653
653
  readonly model: string;
654
654
  readonly stream: true;
@@ -675,9 +675,9 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
675
675
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
676
676
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
677
677
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
678
+ readonly partial_images?: number | undefined;
678
679
  readonly output_compression?: number | undefined;
679
680
  readonly input_fidelity?: "low" | "high" | undefined;
680
- readonly partial_images?: number | undefined;
681
681
  } | {
682
682
  readonly type: "namespace";
683
683
  readonly name: string;
@@ -802,8 +802,8 @@ export declare const channelTransport: (options: import("./open-responses-channe
802
802
  } | {
803
803
  readonly type: "input_file";
804
804
  readonly filename: string;
805
- readonly detail?: string | undefined;
806
805
  readonly file_data?: string | undefined;
806
+ readonly detail?: string | undefined;
807
807
  readonly file_url?: string | undefined;
808
808
  } | {
809
809
  readonly type: "input_text";
@@ -839,8 +839,8 @@ export declare const channelTransport: (options: import("./open-responses-channe
839
839
  } | {
840
840
  readonly type: "input_file";
841
841
  readonly filename: string;
842
- readonly detail?: string | undefined;
843
842
  readonly file_data?: string | undefined;
843
+ readonly detail?: string | undefined;
844
844
  readonly file_url?: string | undefined;
845
845
  } | {
846
846
  readonly type: "input_text";
@@ -875,8 +875,8 @@ export declare const channelTransport: (options: import("./open-responses-channe
875
875
  readonly type: "image_generation_call";
876
876
  readonly status?: string | undefined;
877
877
  readonly result?: string | null | undefined;
878
- readonly revised_prompt?: string | null | undefined;
879
878
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
879
+ readonly revised_prompt?: string | null | undefined;
880
880
  })[];
881
881
  readonly model: string;
882
882
  readonly stream: true;
@@ -903,9 +903,9 @@ export declare const channelTransport: (options: import("./open-responses-channe
903
903
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
904
904
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
905
905
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
906
+ readonly partial_images?: number | undefined;
906
907
  readonly output_compression?: number | undefined;
907
908
  readonly input_fidelity?: "low" | "high" | undefined;
908
- readonly partial_images?: number | undefined;
909
909
  } | {
910
910
  readonly type: "namespace";
911
911
  readonly name: string;
@@ -1030,8 +1030,8 @@ export declare const transport: import("../route/transport/index.js").Transport<
1030
1030
  } | {
1031
1031
  readonly type: "input_file";
1032
1032
  readonly filename: string;
1033
- readonly detail?: string | undefined;
1034
1033
  readonly file_data?: string | undefined;
1034
+ readonly detail?: string | undefined;
1035
1035
  readonly file_url?: string | undefined;
1036
1036
  } | {
1037
1037
  readonly type: "input_text";
@@ -1067,8 +1067,8 @@ export declare const transport: import("../route/transport/index.js").Transport<
1067
1067
  } | {
1068
1068
  readonly type: "input_file";
1069
1069
  readonly filename: string;
1070
- readonly detail?: string | undefined;
1071
1070
  readonly file_data?: string | undefined;
1071
+ readonly detail?: string | undefined;
1072
1072
  readonly file_url?: string | undefined;
1073
1073
  } | {
1074
1074
  readonly type: "input_text";
@@ -1103,8 +1103,8 @@ export declare const transport: import("../route/transport/index.js").Transport<
1103
1103
  readonly type: "image_generation_call";
1104
1104
  readonly status?: string | undefined;
1105
1105
  readonly result?: string | null | undefined;
1106
- readonly revised_prompt?: string | null | undefined;
1107
1106
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
1107
+ readonly revised_prompt?: string | null | undefined;
1108
1108
  })[];
1109
1109
  readonly model: string;
1110
1110
  readonly stream: true;
@@ -1131,9 +1131,9 @@ export declare const transport: import("../route/transport/index.js").Transport<
1131
1131
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
1132
1132
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
1133
1133
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
1134
+ readonly partial_images?: number | undefined;
1134
1135
  readonly output_compression?: number | undefined;
1135
1136
  readonly input_fidelity?: "low" | "high" | undefined;
1136
- readonly partial_images?: number | undefined;
1137
1137
  } | {
1138
1138
  readonly type: "namespace";
1139
1139
  readonly name: string;
@@ -1258,8 +1258,8 @@ export declare const route: Route<{
1258
1258
  } | {
1259
1259
  readonly type: "input_file";
1260
1260
  readonly filename: string;
1261
- readonly detail?: string | undefined;
1262
1261
  readonly file_data?: string | undefined;
1262
+ readonly detail?: string | undefined;
1263
1263
  readonly file_url?: string | undefined;
1264
1264
  } | {
1265
1265
  readonly type: "input_text";
@@ -1295,8 +1295,8 @@ export declare const route: Route<{
1295
1295
  } | {
1296
1296
  readonly type: "input_file";
1297
1297
  readonly filename: string;
1298
- readonly detail?: string | undefined;
1299
1298
  readonly file_data?: string | undefined;
1299
+ readonly detail?: string | undefined;
1300
1300
  readonly file_url?: string | undefined;
1301
1301
  } | {
1302
1302
  readonly type: "input_text";
@@ -1331,8 +1331,8 @@ export declare const route: Route<{
1331
1331
  readonly type: "image_generation_call";
1332
1332
  readonly status?: string | undefined;
1333
1333
  readonly result?: string | null | undefined;
1334
- readonly revised_prompt?: string | null | undefined;
1335
1334
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
1335
+ readonly revised_prompt?: string | null | undefined;
1336
1336
  })[];
1337
1337
  readonly model: string;
1338
1338
  readonly stream: true;
@@ -1359,9 +1359,9 @@ export declare const route: Route<{
1359
1359
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
1360
1360
  readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
1361
1361
  readonly background?: "auto" | "opaque" | "transparent" | undefined;
1362
+ readonly partial_images?: number | undefined;
1362
1363
  readonly output_compression?: number | undefined;
1363
1364
  readonly input_fidelity?: "low" | "high" | undefined;
1364
- readonly partial_images?: number | undefined;
1365
1365
  } | {
1366
1366
  readonly type: "namespace";
1367
1367
  readonly name: string;
@@ -5,14 +5,12 @@ import { Auth } from "../route/auth.js";
5
5
  import { Endpoint } from "../route/endpoint.js";
6
6
  import { Protocol } from "../route/protocol.js";
7
7
  import { HttpTransport } from "../route/transport/index.js";
8
- import { LLMRequest, mergeJsonRecords } from "../schema/index.js";
8
+ import { LLMRequest } from "../schema/index.js";
9
9
  import { resolveEffortUpdates } from "../effort-updates.js";
10
10
  import { OpenResponses } from "./open-responses.js";
11
11
  import { OpenResponsesOptions } from "./utils/open-responses-options.js";
12
12
  import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
13
- import { OpenAIImage } from "./utils/openai-image.js";
14
13
  import { ResponsesHostedTools } from "./utils/responses-hosted-tools.js";
15
- import { ToolSchemaProjection } from "./utils/tool-schema.js";
16
14
  import { OpenResponsesChannel } from "./open-responses-channel.js";
17
15
  import { ResponsesCompaction } from "./utils/responses-compaction.js";
18
16
  import { ResponsesCheckpoint } from "./utils/responses-checkpoint.js";
@@ -35,7 +33,14 @@ const OpenAIResponsesImageGenerationTool = Schema.Struct({
35
33
  output_format: Schema.optional(Schema.Literals(["png", "jpeg", "webp"])),
36
34
  partial_images: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),
37
35
  quality: Schema.optional(Schema.Literals(["auto", "low", "medium", "high"])),
38
- size: Schema.optional(OpenAIImage.Size),
36
+ size: Schema.optional(Schema.String.check(Schema.makeFilter((value) => {
37
+ if (value === "auto")
38
+ return undefined;
39
+ const match = /^(\d+)x(\d+)$/.exec(value);
40
+ if (!match)
41
+ return "image size must be `auto` or `{width}x{height}`";
42
+ return Number(match[1]) > 0 && Number(match[2]) > 0 ? undefined : "image dimensions must be positive integers";
43
+ }))),
39
44
  });
40
45
  const OpenAIResponsesHostedToolItem = Schema.Union([
41
46
  Schema.StructWithRest(Schema.Struct({
@@ -100,23 +105,23 @@ export const CompactionTrigger = Schema.Struct({ type: Schema.Literal("compactio
100
105
  const CheckpointBody = Schema.Struct({
101
106
  ...OpenAIResponsesBody.fields,
102
107
  input: Schema.Array(Schema.Union([OpenAIResponsesInputItem, CompactionTrigger])),
103
- store: Schema.Literal(false),
104
- prompt_cache_retention: optionalNull(Schema.String),
105
- prompt_cache_options: optionalNull(Schema.Struct({ mode: Schema.optional(Schema.String), ttl: Schema.optional(Schema.String) })),
106
108
  });
107
109
  const adapter = {
108
110
  id: ADAPTER,
109
111
  name: NAME,
110
112
  restoreHostedToolItem: (item) => (Schema.is(OpenAIResponsesHostedToolItem)(item) ? item : undefined),
111
113
  };
112
- // Only GPT-6 Astra accepts `configuration_update`, and never alongside automatic `context_management` compaction.
114
+ // GPT-6 Astra, Sol, and Luna accept `configuration_update` only in standard mode (not `reasoning.mode: "pro"` or
115
+ // `-pro` slugs), and never alongside automatic `context_management` compaction.
113
116
  const supportsEffortUpdates = (request) => {
114
117
  if (request.providerOptions?.contextManagement !== undefined)
115
118
  return false;
119
+ if (Schema.is(Schema.Struct({ mode: Schema.Literal("pro") }))(request.http?.body?.reasoning))
120
+ return false;
116
121
  const override = request.model.compatibility?.supportsEffortUpdates;
117
122
  if (override !== undefined)
118
123
  return override;
119
- return /(?:^|\/)gpt-6-astra$/i.test(request.model.id);
124
+ return /(?:^|\/)gpt-6-(?:astra|sol|luna)$/i.test(request.model.id);
120
125
  };
121
126
  const nativeImageToolInput = (tool) => {
122
127
  const native = tool.native?.openai;
@@ -126,29 +131,30 @@ const nativeImageTool = (tool) => {
126
131
  const native = nativeImageToolInput(tool);
127
132
  return Schema.is(OpenAIResponsesImageGenerationTool)(native) ? native : undefined;
128
133
  };
129
- const lowerTool = Effect.fn("OpenAIResponses.lowerTool")(function* (tool, inputSchema) {
134
+ const lowerTool = Effect.fn("OpenAIResponses.lowerTool")(function* (tool) {
130
135
  const native = nativeImageToolInput(tool);
131
136
  if (native !== undefined) {
132
137
  if (Schema.is(OpenAIResponsesImageGenerationTool)(native))
133
138
  return native;
134
139
  return yield* ProviderShared.invalidRequest("OpenAI Responses image generation tool options are invalid");
135
140
  }
136
- return yield* OpenResponses.lowerTool(NAME, tool, inputSchema);
141
+ return yield* OpenResponses.lowerTool(NAME, tool);
137
142
  });
138
143
  // Native namespaces hold only function tools, so deeper levels flatten into
139
144
  // the leaf names the same way non-native protocols flatten the whole tree.
140
- const lowerToolEntry = Effect.fn("OpenAIResponses.lowerToolEntry")(function* (tool, compatibility) {
145
+ const lowerToolEntry = Effect.fn("OpenAIResponses.lowerToolEntry")(function* (tool) {
141
146
  if (tool.type === "tool")
142
- return yield* lowerTool(tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, compatibility));
147
+ return yield* lowerTool(tool);
143
148
  // OpenAI requires a namespace description; fall back to a generic one so a
144
149
  // missing description never blocks the request.
145
150
  return {
146
151
  type: "namespace",
147
152
  name: tool.name,
148
153
  description: tool.description ?? `Tools in the ${tool.name} namespace.`,
149
- tools: yield* Effect.forEach(ProviderShared.flattenTools(tool.tools), (leaf) => OpenResponses.lowerTool(NAME, leaf, ToolSchemaProjection.modelCompatibility(leaf.inputSchema, compatibility))),
154
+ tools: yield* Effect.forEach(ProviderShared.flattenTools(tool.tools), (leaf) => OpenResponses.lowerTool(NAME, leaf)),
150
155
  };
151
156
  });
157
+ const lowerTools = (request) => Effect.forEach(request.tools, lowerToolEntry);
152
158
  const lowerToolChoice = (toolChoice, tools) => ProviderShared.matchToolChoice(NAME, toolChoice, {
153
159
  auto: () => "auto",
154
160
  none: () => "none",
@@ -162,14 +168,11 @@ const fromRequest = Effect.fn("OpenAIResponses.fromRequest")(function* (request)
162
168
  const management = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Schema.UndefinedOr(ContextManagement)))(request.providerOptions?.contextManagement);
163
169
  const options = OpenResponsesOptions.resolve(request);
164
170
  const updates = resolveEffortUpdates(request, options.reasoningEffort);
165
- const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
166
171
  return yield* decodeBody({
167
172
  ...(yield* OpenResponses.lowerConversation(updates.request, adapter)),
168
173
  ...OpenResponses.lowerGeneration(request, { ...options, reasoningEffort: updates.effort }),
169
174
  context_management: management?.map((edit) => ({ type: edit.type, compact_threshold: edit.compactThreshold })),
170
- tools: request.tools.length === 0
171
- ? undefined
172
- : yield* Effect.forEach(request.tools, (tool) => lowerToolEntry(tool, toolSchemaCompatibility)),
175
+ tools: request.tools.length === 0 ? undefined : yield* lowerTools(request),
173
176
  tool_choice: request.tools.length === 0
174
177
  ? undefined
175
178
  : (OpenResponses.allowedToolChoice(request) ??
@@ -179,25 +182,19 @@ const fromRequest = Effect.fn("OpenAIResponses.fromRequest")(function* (request)
179
182
  const checkpointBody = {
180
183
  schema: CheckpointBody,
181
184
  from: Effect.fn("OpenAIResponses.checkpointBody")(function* (request) {
182
- const native = yield* fromRequest(LLMRequest.update(request, { toolChoice: undefined }));
183
185
  const overlay = request.http?.body;
184
186
  // Complete history is required for stateless replay and SSE recovery. Raw input overrides bypass that contract.
185
187
  if (overlay?.input !== undefined ||
186
188
  overlay?.previous_response_id !== undefined ||
187
189
  overlay?.conversation !== undefined)
188
190
  return yield* ProviderShared.invalidRequest("Trigger compaction requires complete canonical history, not an input or continuation override");
189
- return yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(CheckpointBody))({
190
- ...mergeJsonRecords(native, overlay),
191
+ if (overlay?.stream !== undefined && overlay.stream !== true)
192
+ return yield* ProviderShared.invalidRequest("Trigger compaction requires a streamed response");
193
+ const native = yield* fromRequest(request);
194
+ return {
195
+ ...native,
191
196
  input: [...native.input, { type: "compaction_trigger" }],
192
- stream: true,
193
- store: false,
194
- parallel_tool_calls: true,
195
- tool_choice: undefined,
196
- context_management: undefined,
197
- text: undefined,
198
- max_output_tokens: undefined,
199
- max_tool_calls: undefined,
200
- });
197
+ };
201
198
  }),
202
199
  };
203
200
  const hostedToolResult = Effect.fn("OpenAIResponses.hostedToolResult")(function* (item) {
@@ -268,7 +265,10 @@ export const transport = channelTransport({
268
265
  headers: (headers) => Headers.set(headers, "openai-beta", headers["openai-beta"] ?? WEBSOCKET_PROTOCOL_HEADER),
269
266
  });
270
267
  export const route = Route.make({
271
- compact: { endpoint: ResponsesCompaction.make(adapter), trigger: ResponsesCheckpoint.make(checkpointBody) },
268
+ compact: {
269
+ endpoint: ResponsesCompaction.make(adapter, lowerTools),
270
+ trigger: ResponsesCheckpoint.make(checkpointBody),
271
+ },
272
272
  id: ADAPTER,
273
273
  provider: "openai",
274
274
  providerMetadataKey: "openai",
@@ -0,0 +1,136 @@
1
+ import { MediaProtocol } from "../route/media-protocol.js";
2
+ import { MediaRoute } from "../route/media.js";
3
+ import { type MediaUsage } from "../schema/index.js";
4
+ import { SpeechModel, type SpeechRequestFor } from "../speech.js";
5
+ import { SpeechStream } from "./utils/speech-stream.js";
6
+ export declare const DEFAULT_BASE_URL = "https://api.openai.com/v1";
7
+ export declare const PATH = "/audio/speech";
8
+ /** `voice`, `instructions`, `speed`, and `format` are common request fields; other native body fields pass through. */
9
+ export type OpenAISpeechOptions = {
10
+ /** Defaults to `"sse"` in `stream` mode on models that support it; the merged value selects the response framing. */
11
+ readonly stream_format?: "sse" | "audio";
12
+ } & Record<string, unknown>;
13
+ export type Request = SpeechRequestFor<OpenAISpeechOptions>;
14
+ interface State extends SpeechStream.Audio {
15
+ readonly done: boolean;
16
+ readonly usage?: MediaUsage;
17
+ }
18
+ export declare const protocol: MediaProtocol.Streamed<Request, {
19
+ readonly id: string;
20
+ readonly type: "generation-queued";
21
+ readonly position?: number | undefined;
22
+ } | {
23
+ readonly id: string;
24
+ readonly type: "generation-progress";
25
+ readonly progress?: number | undefined;
26
+ } | {
27
+ readonly type: "audio-delta";
28
+ readonly chunk: Uint8Array<ArrayBufferLike>;
29
+ } | {
30
+ readonly type: "timestamps";
31
+ readonly items: readonly {
32
+ readonly text: string;
33
+ readonly startSeconds: number;
34
+ readonly endSeconds: number;
35
+ }[];
36
+ } | {
37
+ readonly type: "finish";
38
+ readonly audio: import("../media.js").Asset;
39
+ readonly providerMetadata?: {
40
+ readonly [x: string]: {
41
+ readonly [x: string]: unknown;
42
+ };
43
+ } | undefined;
44
+ readonly usage?: {
45
+ readonly type: "tokens";
46
+ readonly input?: number | undefined;
47
+ readonly output?: number | undefined;
48
+ readonly total?: number | undefined;
49
+ readonly details?: {
50
+ readonly [x: string]: unknown;
51
+ } | undefined;
52
+ } | {
53
+ readonly type: "seconds";
54
+ readonly seconds: number;
55
+ } | {
56
+ readonly type: "characters";
57
+ readonly characters: number;
58
+ } | {
59
+ readonly type: "credits";
60
+ readonly credits: number;
61
+ } | {
62
+ readonly type: "compute";
63
+ readonly seconds: number;
64
+ } | undefined;
65
+ readonly notices?: readonly {
66
+ readonly type: "other" | "moderated" | "filtered";
67
+ readonly message: string;
68
+ readonly providerMetadata?: {
69
+ readonly [x: string]: {
70
+ readonly [x: string]: unknown;
71
+ };
72
+ } | undefined;
73
+ }[] | undefined;
74
+ }, string | Uint8Array<ArrayBufferLike>, State>;
75
+ export declare const model: (input: MediaRoute.ModelInput) => SpeechModel<OpenAISpeechOptions>;
76
+ export declare const OpenAISpeech: {
77
+ readonly protocol: MediaProtocol.Streamed<Request, {
78
+ readonly id: string;
79
+ readonly type: "generation-queued";
80
+ readonly position?: number | undefined;
81
+ } | {
82
+ readonly id: string;
83
+ readonly type: "generation-progress";
84
+ readonly progress?: number | undefined;
85
+ } | {
86
+ readonly type: "audio-delta";
87
+ readonly chunk: Uint8Array<ArrayBufferLike>;
88
+ } | {
89
+ readonly type: "timestamps";
90
+ readonly items: readonly {
91
+ readonly text: string;
92
+ readonly startSeconds: number;
93
+ readonly endSeconds: number;
94
+ }[];
95
+ } | {
96
+ readonly type: "finish";
97
+ readonly audio: import("../media.js").Asset;
98
+ readonly providerMetadata?: {
99
+ readonly [x: string]: {
100
+ readonly [x: string]: unknown;
101
+ };
102
+ } | undefined;
103
+ readonly usage?: {
104
+ readonly type: "tokens";
105
+ readonly input?: number | undefined;
106
+ readonly output?: number | undefined;
107
+ readonly total?: number | undefined;
108
+ readonly details?: {
109
+ readonly [x: string]: unknown;
110
+ } | undefined;
111
+ } | {
112
+ readonly type: "seconds";
113
+ readonly seconds: number;
114
+ } | {
115
+ readonly type: "characters";
116
+ readonly characters: number;
117
+ } | {
118
+ readonly type: "credits";
119
+ readonly credits: number;
120
+ } | {
121
+ readonly type: "compute";
122
+ readonly seconds: number;
123
+ } | undefined;
124
+ readonly notices?: readonly {
125
+ readonly type: "other" | "moderated" | "filtered";
126
+ readonly message: string;
127
+ readonly providerMetadata?: {
128
+ readonly [x: string]: {
129
+ readonly [x: string]: unknown;
130
+ };
131
+ } | undefined;
132
+ }[] | undefined;
133
+ }, string | Uint8Array<ArrayBufferLike>, State>;
134
+ readonly model: (input: MediaRoute.ModelInput) => SpeechModel<OpenAISpeechOptions>;
135
+ };
136
+ export {};
@@ -0,0 +1,97 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { Framing } from "../route/framing.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 { SpeechModel } from "../speech.js";
7
+ import { SpeechStream } from "./utils/speech-stream.js";
8
+ const route = MediaProtocol.identity({ id: "openai-speech", name: "OpenAI Speech", provider: "openai" });
9
+ export const DEFAULT_BASE_URL = "https://api.openai.com/v1";
10
+ export const PATH = "/audio/speech";
11
+ /** `pcm` is raw 24 kHz, 16-bit signed little-endian mono samples without a header. */
12
+ const PCM_SAMPLE_RATE = 24000;
13
+ // ---------------------------------------------------------------------------
14
+ // 3. Streaming event schema
15
+ // ---------------------------------------------------------------------------
16
+ const SpeechStreamEvent = Schema.Union([
17
+ Schema.Struct({ type: Schema.Literal("speech.audio.delta"), audio: Schema.Uint8ArrayFromBase64 }),
18
+ Schema.Struct({
19
+ type: Schema.Literal("speech.audio.done"),
20
+ usage: Schema.optional(Schema.Struct({
21
+ input_tokens: Schema.optional(Schema.Number),
22
+ output_tokens: Schema.optional(Schema.Number),
23
+ total_tokens: Schema.optional(Schema.Number),
24
+ })),
25
+ }),
26
+ ]);
27
+ const decodeEvent = route.decodeFrame(SpeechStreamEvent);
28
+ // ---------------------------------------------------------------------------
29
+ // 5. Request body construction
30
+ // ---------------------------------------------------------------------------
31
+ // `sse` is not supported for `tts-1` or `tts-1-hd`; those models stream the raw audio body instead.
32
+ const supportsSse = (model) => !/^tts-1(-hd)?(-|$)/.test(model);
33
+ const fromRequest = Effect.fn("OpenAISpeech.fromRequest")(function* (request) {
34
+ // Not in `unsupported`: that list would also reject `timestamps: false`, which asks for nothing.
35
+ if (request.timestamps === true)
36
+ return yield* route.unsupported("media.timestamps", `${route.name} does not return timestamps`);
37
+ return MediaProtocol.json(mergeJsonRecords({
38
+ model: request.model.id,
39
+ input: request.text,
40
+ voice: request.voice,
41
+ instructions: request.instructions,
42
+ response_format: request.format,
43
+ speed: request.speed,
44
+ stream_format: request.mode === "stream" && supportsSse(request.model.id) ? "sse" : undefined,
45
+ }, request.providerOptions, request.http?.body) ?? {});
46
+ });
47
+ // ---------------------------------------------------------------------------
48
+ // 6. Stream parsing
49
+ // ---------------------------------------------------------------------------
50
+ const isSse = (body) => body.type === "json" && body.value.stream_format === "sse";
51
+ const onEvent = Effect.fn("OpenAISpeech.onEvent")(function* (state, frame) {
52
+ const event = yield* decodeEvent(frame);
53
+ if (event.type === "speech.audio.delta")
54
+ return SpeechStream.delta(state, event.audio);
55
+ const usage = event.usage;
56
+ return [
57
+ {
58
+ ...state,
59
+ done: true,
60
+ usage: usage === undefined
61
+ ? undefined
62
+ : {
63
+ type: "tokens",
64
+ input: usage.input_tokens,
65
+ output: usage.output_tokens,
66
+ total: usage.total_tokens,
67
+ details: { openai: usage },
68
+ },
69
+ },
70
+ [],
71
+ ];
72
+ });
73
+ const finish = (state, context) => {
74
+ if (isSse(context.body) && !state.done)
75
+ return Effect.fail(route.incomplete());
76
+ const format = context.request.format ?? "mp3";
77
+ return SpeechStream.finish(route, state, {
78
+ ...(format === "pcm" ? SpeechStream.pcm("pcm_s16le", PCM_SAMPLE_RATE) : SpeechStream.container(format)),
79
+ usage: state.usage,
80
+ });
81
+ };
82
+ // ---------------------------------------------------------------------------
83
+ // 7. Protocol and route
84
+ // ---------------------------------------------------------------------------
85
+ export const protocol = MediaProtocol.stream(route, {
86
+ unsupported: ["language"],
87
+ body: { from: fromRequest },
88
+ frames: (bytes, context) => (isSse(context.body) ? Framing.sse.frame(bytes) : bytes),
89
+ initial: () => ({ chunks: [], done: false }),
90
+ step: SpeechStream.step(onEvent),
91
+ finish,
92
+ });
93
+ export const model = (input) => SpeechModel.fromRoute({ protocol, baseURL: DEFAULT_BASE_URL, path: PATH }, input);
94
+ export const OpenAISpeech = {
95
+ protocol,
96
+ model,
97
+ };