@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
@@ -0,0 +1,81 @@
1
+ import { Effect, Layer, ManagedRuntime, Stream } from "effect";
2
+ import { Image, ImageModel, ImageRequest } from "./image.js";
3
+ import { ImageClient } from "./image-client.js";
4
+ import { LLM } from "./index.js";
5
+ import { LLMClient } from "./route/client.js";
6
+ import { RequestExecutor } from "./route/executor.js";
7
+ import { LanguageModel, LLMRequest } from "./schema/index.js";
8
+ import { Speech, SpeechModel, SpeechRequest } from "./speech.js";
9
+ import { SpeechClient } from "./speech-client.js";
10
+ import { Transcription, TranscriptionModel, TranscriptionRequest, } from "./transcription.js";
11
+ import { TranscriptionClient } from "./transcription-client.js";
12
+ import { Video, VideoModel, VideoRequest } from "./video.js";
13
+ import { VideoClient } from "./video-client.js";
14
+ const abortEffect = (signal) => signal === undefined
15
+ ? Effect.never
16
+ : Effect.callback((resume) => {
17
+ if (signal.aborted) {
18
+ resume(Effect.void);
19
+ return;
20
+ }
21
+ const onAbort = () => resume(Effect.void);
22
+ signal.addEventListener("abort", onAbort, { once: true });
23
+ return Effect.sync(() => signal.removeEventListener("abort", onAbort));
24
+ });
25
+ export const make = (options = {}) => {
26
+ const runtime = ManagedRuntime.make(Layer.mergeAll(LLMClient.layer, ImageClient.layer, VideoClient.layer, SpeechClient.layer, TranscriptionClient.layer).pipe(Layer.provideMerge(options.layer ?? RequestExecutor.fetchLayer)));
27
+ /** Run any package Effect (for example `asset.bytes()`) inside this runtime. */
28
+ const run = (effect, options) => runtime.runPromise(effect, { signal: options?.signal });
29
+ const iterate = (stream, options) => Stream.toAsyncIterable(Stream.unwrap(runtime.contextEffect.pipe(Effect.map((context) => stream.pipe(Stream.interruptWhen(abortEffect(options?.signal)), Stream.provideContext(context))))));
30
+ const handle = (generation) => ({
31
+ ...generation.snapshot,
32
+ token: generation.token,
33
+ await: (options) => run(generation.await({ poll: options?.poll }), options),
34
+ refresh: (options) => run(generation.refresh(), options).then(handle),
35
+ cancel: (options) => run(generation.cancel(), options),
36
+ });
37
+ // The typed `generate`/`stream` overloads take a concrete input or a request, not the union; normalize once here.
38
+ const llmRequest = (input) => (input instanceof LLMRequest ? input : LLM.request(input));
39
+ const imageRequest = (input) => input instanceof ImageRequest ? input : Image.request(input);
40
+ const videoRequest = (input) => input instanceof VideoRequest ? input : Video.request(input);
41
+ const speechRequest = (input) => input instanceof SpeechRequest ? input : Speech.request(input);
42
+ const transcriptionRequest = (input) => input instanceof TranscriptionRequest ? input : Transcription.request(input);
43
+ return {
44
+ run,
45
+ llm: {
46
+ request: LLM.request,
47
+ generate: (input, options) => run(LLM.generate(llmRequest(input)), options),
48
+ stream: (input, options) => iterate(LLM.stream(llmRequest(input)), options),
49
+ },
50
+ image: {
51
+ request: Image.request,
52
+ generate: (input, options) => run(Image.generate(imageRequest(input), { poll: options?.poll }), options),
53
+ stream: (input, options) => iterate(Image.stream(imageRequest(input), { poll: options?.poll }), options),
54
+ start: (input, options) => run(Image.start(imageRequest(input)), options).then(handle),
55
+ resume: (model, token, options) => run(Image.resume(model, token), options).then(handle),
56
+ },
57
+ video: {
58
+ request: Video.request,
59
+ start: (input, options) => run(Video.start(videoRequest(input)), options).then(handle),
60
+ generate: (input, options) => run(Video.generate(videoRequest(input), { poll: options?.poll }), options),
61
+ resume: (model, token, options) => run(Video.resume(model, token), options).then(handle),
62
+ stream: (input, options) => iterate(Video.stream(videoRequest(input), { poll: options?.poll }), options),
63
+ },
64
+ speech: {
65
+ request: Speech.request,
66
+ generate: (input, options) => run(Speech.generate(speechRequest(input)), options),
67
+ stream: (input, options) => iterate(Speech.stream(speechRequest(input)), options),
68
+ },
69
+ transcription: {
70
+ request: Transcription.request,
71
+ generate: (input, options) => run(Transcription.generate(transcriptionRequest(input), { poll: options?.poll }), options),
72
+ stream: (input, options) => iterate(Transcription.stream(transcriptionRequest(input), { poll: options?.poll }), options),
73
+ start: (input, options) => run(Transcription.start(transcriptionRequest(input)), options).then(handle),
74
+ resume: (model, token, options) => run(Transcription.resume(model, token), options).then(handle),
75
+ },
76
+ dispose: () => runtime.dispose(),
77
+ };
78
+ };
79
+ /** Default client over `RequestExecutor.fetchLayer` for scripts; the runtime builds its layer on first use. */
80
+ export const ai = make();
81
+ export * as AI from "./promise.js";
@@ -50,6 +50,12 @@ export declare const protocol: Protocol<{
50
50
  readonly image_url: {
51
51
  readonly url: string;
52
52
  };
53
+ } | {
54
+ readonly type: "file";
55
+ readonly file: {
56
+ readonly filename: string;
57
+ readonly file_data: string;
58
+ };
53
59
  })[];
54
60
  } | {
55
61
  readonly role: "user";
@@ -65,6 +71,12 @@ export declare const protocol: Protocol<{
65
71
  readonly image_url: {
66
72
  readonly url: string;
67
73
  };
74
+ } | {
75
+ readonly type: "file";
76
+ readonly file: {
77
+ readonly filename: string;
78
+ readonly file_data: string;
79
+ };
68
80
  })[];
69
81
  } | {
70
82
  readonly [x: string]: unknown;
@@ -77,8 +77,8 @@ export declare const protocol: Protocol<{
77
77
  } | {
78
78
  readonly type: "input_file";
79
79
  readonly filename: string;
80
- readonly detail?: string | undefined;
81
80
  readonly file_data?: string | undefined;
81
+ readonly detail?: string | undefined;
82
82
  readonly file_url?: string | undefined;
83
83
  } | {
84
84
  readonly type: "input_text";
@@ -114,8 +114,8 @@ export declare const protocol: Protocol<{
114
114
  } | {
115
115
  readonly type: "input_file";
116
116
  readonly filename: string;
117
- readonly detail?: string | undefined;
118
117
  readonly file_data?: string | undefined;
118
+ readonly detail?: string | undefined;
119
119
  readonly file_url?: string | undefined;
120
120
  } | {
121
121
  readonly type: "input_text";
@@ -537,7 +537,7 @@ const citationsFromMetadata = (metadata) => {
537
537
  };
538
538
  const isHttpUrl = (value) => /^https?:\/\//i.test(value.trim());
539
539
  const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part, breakpoints) {
540
- const mime = part.mediaType.toLowerCase();
540
+ const mime = part.media.mediaType.toLowerCase();
541
541
  const cacheControlValue = breakpoints ? cacheControl(breakpoints, part.cache) : undefined;
542
542
  const fileId = fileIdFromMetadata(part.metadata);
543
543
  // SDK file sources: FileImageSource:2350 / FileDocumentSource:2344 {type:"file", file_id}
@@ -564,9 +564,9 @@ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part, br
564
564
  : { citations: citationsFromMetadata(part.metadata) }),
565
565
  };
566
566
  }
567
- const rawString = typeof part.data === "string" ? part.data.trim() : undefined;
567
+ const rawString = ProviderShared.mediaUrl(part.media)?.trim();
568
568
  // SDK URL sources: URLImageSource:3817 / URLPDFSource:3823 {type:"url", url}
569
- if (rawString && isHttpUrl(rawString) && !rawString.startsWith("data:")) {
569
+ if (rawString && isHttpUrl(rawString)) {
570
570
  if (mime.startsWith("image/"))
571
571
  return {
572
572
  type: "image",
@@ -590,19 +590,10 @@ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part, br
590
590
  : { citations: citationsFromMetadata(part.metadata) }),
591
591
  };
592
592
  }
593
+ const media = yield* ProviderShared.requireInlineMedia("Anthropic Messages", part.media);
593
594
  // SDK PlainTextSource:2716 {type:"text", media_type:"text/plain", data}
594
595
  if (mime === "text/plain") {
595
- const textData = typeof part.data !== "string"
596
- ? Buffer.from(part.data).toString("utf8")
597
- : part.data.startsWith("data:")
598
- ? (() => {
599
- const comma = part.data.indexOf(",");
600
- const payload = comma >= 0 ? part.data.slice(comma + 1) : part.data;
601
- return part.data.includes(";base64")
602
- ? Buffer.from(payload, "base64").toString("utf8")
603
- : decodeURIComponent(payload);
604
- })()
605
- : part.data;
596
+ const textData = Buffer.from(media.base64, "base64").toString("utf8");
606
597
  return {
607
598
  type: "document",
608
599
  source: { type: "text", media_type: "text/plain", data: textData },
@@ -616,7 +607,6 @@ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part, br
616
607
  : { citations: citationsFromMetadata(part.metadata) }),
617
608
  };
618
609
  }
619
- const media = ProviderShared.normalizeMedia(part);
620
610
  if (media.mime === "application/pdf")
621
611
  return {
622
612
  type: "document",
@@ -635,7 +625,7 @@ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part, br
635
625
  : { citations: citationsFromMetadata(part.metadata) }),
636
626
  };
637
627
  if (!media.mime.startsWith("image/"))
638
- return yield* invalid(`Anthropic Messages does not support media type ${part.mediaType}`);
628
+ return yield* invalid(`Anthropic Messages does not support media type ${part.media.mediaType}`);
639
629
  return {
640
630
  type: "image",
641
631
  source: {
@@ -654,7 +644,7 @@ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part, br
654
644
  const lowerToolResultContentItem = Effect.fnUntraced(function* (item) {
655
645
  if (item.type === "text")
656
646
  return { type: "text", text: item.text };
657
- return yield* lowerMedia({ type: "media", mediaType: item.mime, data: item.uri, filename: item.name });
647
+ return yield* lowerMedia(ProviderShared.toolFileMedia(item));
658
648
  });
659
649
  const lowerToolResultContent = Effect.fnUntraced(function* (part) {
660
650
  // Text / json / error results stay as a string for backward compatibility
@@ -907,7 +897,6 @@ const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (reques
907
897
  const format = outputConfig?.format ?? undefined;
908
898
  const updates = resolveEffortUpdates(request, options.effort ?? outputConfig?.effort ?? undefined);
909
899
  const generation = request.generation;
910
- const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
911
900
  // Allocate the 4-breakpoint budget in invalidation order: tools → system →
912
901
  // messages. Tools live highest in the cache hierarchy, so when callers
913
902
  // over-mark we keep their tool hints and shed the message-tail ones first.
@@ -915,7 +904,7 @@ const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (reques
915
904
  const flattened = ProviderShared.flattenToolRequest(updates.request);
916
905
  const tools = flattened.tools.length === 0
917
906
  ? undefined
918
- : flattened.tools.map((tool) => lowerTool(breakpoints, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, toolSchemaCompatibility)));
907
+ : flattened.tools.map((tool) => lowerTool(breakpoints, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, request.model)));
919
908
  // Anthropic rejects tool_choice when tools are absent; "none" is only meaningful with tools present.
920
909
  const toolChoice = tools === undefined || !request.toolChoice ? undefined : yield* lowerToolChoice(request.toolChoice);
921
910
  const systemParts = request.system.filter((part) => part.text.length > 0);
@@ -0,0 +1,40 @@
1
+ import { Schema } from "effect";
2
+ import { MediaProtocol } from "../route/media-protocol.js";
3
+ import { MediaRoute } from "../route/media.js";
4
+ import { TranscriptionModel, TranscriptionResponse, type TranscriptionRequestFor } from "../transcription.js";
5
+ export declare const DEFAULT_BASE_URL = "https://api.assemblyai.com";
6
+ export declare const PATH = "/v2/transcript";
7
+ export declare const UPLOAD_PATH = "/v2/upload";
8
+ export type AssemblyAITranscriptionOptions = {
9
+ readonly keyterms_prompt?: ReadonlyArray<string>;
10
+ readonly punctuate?: boolean;
11
+ readonly format_text?: boolean;
12
+ readonly disfluencies?: boolean;
13
+ readonly filter_profanity?: boolean;
14
+ readonly temperature?: number;
15
+ readonly speaker_options?: {
16
+ readonly min_speakers_expected?: number;
17
+ readonly max_speakers_expected?: number;
18
+ };
19
+ readonly language_detection_options?: {
20
+ readonly expected_languages?: ReadonlyArray<string>;
21
+ readonly fallback_language?: string;
22
+ readonly code_switching?: boolean;
23
+ };
24
+ readonly speech_models?: ReadonlyArray<"universal-3-5-pro" | "universal-2" | (string & {})>;
25
+ } & Record<string, unknown>;
26
+ export type Request = TranscriptionRequestFor<AssemblyAITranscriptionOptions>;
27
+ export declare const Token: Schema.Struct<{
28
+ readonly transcriptID: Schema.String;
29
+ }>;
30
+ export type Token = Schema.Schema.Type<typeof Token>;
31
+ export declare const protocol: MediaProtocol.Queued<Request, TranscriptionResponse, {
32
+ readonly transcriptID: string;
33
+ }>;
34
+ export declare const model: (input: MediaRoute.ModelInput) => TranscriptionModel<AssemblyAITranscriptionOptions>;
35
+ export declare const AssemblyAITranscription: {
36
+ readonly protocol: MediaProtocol.Queued<Request, TranscriptionResponse, {
37
+ readonly transcriptID: string;
38
+ }>;
39
+ readonly model: (input: MediaRoute.ModelInput) => TranscriptionModel<AssemblyAITranscriptionOptions>;
40
+ };
@@ -0,0 +1,138 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { Media } from "../media.js";
3
+ import { MediaProtocol } from "../route/media-protocol.js";
4
+ import { MediaRoute } from "../route/media.js";
5
+ import { ProviderID, mergeJsonRecords } from "../schema/index.js";
6
+ import { TranscriptionModel, TranscriptionResponse } from "../transcription.js";
7
+ import { ProviderShared, optionalNull } from "./shared.js";
8
+ import { MediaInput } from "./utils/media-input.js";
9
+ const ADAPTER = "assemblyai-transcription";
10
+ const NAME = "AssemblyAI";
11
+ const PROVIDER = ProviderID.make("assemblyai");
12
+ export const DEFAULT_BASE_URL = "https://api.assemblyai.com";
13
+ export const PATH = "/v2/transcript";
14
+ export const UPLOAD_PATH = "/v2/upload";
15
+ // ---------------------------------------------------------------------------
16
+ // 2. Token and response schemas
17
+ // ---------------------------------------------------------------------------
18
+ export const Token = Schema.Struct({ transcriptID: Schema.String });
19
+ const Upload = Schema.Struct({ upload_url: Schema.String });
20
+ /** Word and utterance times are milliseconds. */
21
+ const Transcript = Schema.Struct({
22
+ id: Schema.String,
23
+ status: Schema.String,
24
+ text: optionalNull(Schema.String),
25
+ words: optionalNull(Schema.Array(Schema.Struct({
26
+ text: Schema.String,
27
+ start: Schema.Number,
28
+ end: Schema.Number,
29
+ confidence: optionalNull(Schema.Number),
30
+ speaker: optionalNull(Schema.String),
31
+ }))),
32
+ utterances: optionalNull(Schema.Array(Schema.Struct({
33
+ text: Schema.String,
34
+ start: Schema.Number,
35
+ end: Schema.Number,
36
+ speaker: optionalNull(Schema.String),
37
+ }))),
38
+ language_code: optionalNull(Schema.String),
39
+ audio_duration: optionalNull(Schema.Number),
40
+ speech_model_used: optionalNull(Schema.String),
41
+ error: optionalNull(Schema.String),
42
+ });
43
+ const STATUS = {
44
+ queued: "queued",
45
+ processing: "running",
46
+ completed: "completed",
47
+ error: "failed",
48
+ };
49
+ // ---------------------------------------------------------------------------
50
+ // 5. Request body construction
51
+ // ---------------------------------------------------------------------------
52
+ const decodeUpload = MediaProtocol.decodeJson(ADAPTER, NAME, Upload);
53
+ /** `/v2/transcript` only takes a URL, so inline audio is uploaded to `/v2/upload` first. */
54
+ const prepare = Effect.fn("AssemblyAITranscription.prepare")(function* (request, send) {
55
+ if (request.audio.source.type !== "bytes" && request.audio.source.type !== "base64")
56
+ return request;
57
+ const audio = yield* MediaInput.inlineBytes(ADAPTER, request.audio);
58
+ const uploaded = yield* send(UPLOAD_PATH, MediaProtocol.binary(audio, "application/octet-stream")).pipe(Effect.flatMap(decodeUpload));
59
+ return { ...request, audio: Media.url(uploaded.value.upload_url, { mediaType: request.audio.mediaType }) };
60
+ });
61
+ const fromRequest = Effect.fn("AssemblyAITranscription.fromRequest")(function* (request) {
62
+ const audio = yield* ProviderShared.mediaReference(request.audio, PROVIDER, NAME);
63
+ return MediaProtocol.json(mergeJsonRecords({
64
+ audio_url: audio.value,
65
+ speech_models: [request.model.id],
66
+ language_code: request.language,
67
+ language_detection: request.language === undefined ? true : undefined,
68
+ prompt: request.prompt,
69
+ // Turn-level `utterances`, the only segments AssemblyAI returns, require speaker labels.
70
+ speaker_labels: request.diarize === true || request.timestamps === "segment" ? true : undefined,
71
+ speakers_expected: request.speakers,
72
+ }, request.providerOptions, request.http?.body) ?? {});
73
+ });
74
+ // ---------------------------------------------------------------------------
75
+ // 6. Response decoding
76
+ // ---------------------------------------------------------------------------
77
+ const decodeTranscript = MediaProtocol.decodeJson(ADAPTER, NAME, Transcript);
78
+ const decodeStart = Effect.fn("AssemblyAITranscription.decodeStart")(function* (response) {
79
+ const output = yield* decodeTranscript(response);
80
+ const status = yield* MediaProtocol.status(STATUS, output.value.status, output);
81
+ return { token: { transcriptID: output.value.id }, snapshot: { id: output.value.id, status } };
82
+ });
83
+ const decodeStatus = Effect.fn("AssemblyAITranscription.decodeStatus")(function* (response, context) {
84
+ const output = yield* decodeTranscript(response);
85
+ const status = yield* MediaProtocol.status(STATUS, output.value.status, output);
86
+ return { id: context.token.transcriptID, status };
87
+ });
88
+ const seconds = (milliseconds) => milliseconds / 1000;
89
+ const decodeResult = Effect.fn("AssemblyAITranscription.decodeResult")(function* (response, context) {
90
+ const output = yield* decodeTranscript(response);
91
+ const transcript = output.value;
92
+ const status = yield* MediaProtocol.status(STATUS, transcript.status, output);
93
+ const error = transcript.error ?? undefined;
94
+ if (status === "failed")
95
+ return yield* output.ended("failed", `${NAME} transcription failed${error === undefined ? "" : `: ${error}`}`);
96
+ if (status !== "completed")
97
+ return yield* output.invalid(`${NAME} transcript ${context.token.transcriptID} has not finished`);
98
+ const duration = transcript.audio_duration ?? undefined;
99
+ return new TranscriptionResponse({
100
+ text: transcript.text ?? "",
101
+ segments: transcript.utterances?.map((utterance) => ({
102
+ text: utterance.text,
103
+ startSeconds: seconds(utterance.start),
104
+ endSeconds: seconds(utterance.end),
105
+ speaker: utterance.speaker ?? undefined,
106
+ })),
107
+ words: transcript.words?.map((word) => ({
108
+ text: word.text,
109
+ startSeconds: seconds(word.start),
110
+ endSeconds: seconds(word.end),
111
+ speaker: word.speaker ?? undefined,
112
+ confidence: word.confidence ?? undefined,
113
+ })),
114
+ language: transcript.language_code?.toLowerCase(),
115
+ durationSeconds: duration,
116
+ usage: duration === undefined ? undefined : { type: "seconds", seconds: duration },
117
+ providerMetadata: {
118
+ assemblyai: { transcriptId: transcript.id, speechModel: transcript.speech_model_used ?? undefined },
119
+ },
120
+ });
121
+ });
122
+ // ---------------------------------------------------------------------------
123
+ // 7. Protocol and route
124
+ // ---------------------------------------------------------------------------
125
+ const transcriptPath = (token) => `${PATH}/${token.transcriptID}`;
126
+ export const protocol = MediaProtocol.queued({
127
+ id: ADAPTER,
128
+ name: NAME,
129
+ token: Token,
130
+ start: { prepare, body: { from: fromRequest }, decode: decodeStart },
131
+ status: { path: transcriptPath, decode: decodeStatus },
132
+ result: { path: transcriptPath, decode: decodeResult },
133
+ });
134
+ export const model = (input) => TranscriptionModel.fromRoute({ id: ADAPTER, provider: PROVIDER, protocol, baseURL: DEFAULT_BASE_URL, path: PATH }, input);
135
+ export const AssemblyAITranscription = {
136
+ protocol,
137
+ model,
138
+ };
@@ -157,7 +157,7 @@ export declare const protocol: Protocol<{
157
157
  };
158
158
  } | {
159
159
  readonly document: {
160
- readonly format: "pdf" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html" | "txt" | "md";
160
+ readonly format: "pdf" | "txt" | "md" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html";
161
161
  readonly name: string;
162
162
  readonly source: {
163
163
  readonly bytes: string;
@@ -176,7 +176,7 @@ export declare const protocol: Protocol<{
176
176
  };
177
177
  } | {
178
178
  readonly document: {
179
- readonly format: "pdf" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html" | "txt" | "md";
179
+ readonly format: "pdf" | "txt" | "md" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html";
180
180
  readonly name: string;
181
181
  readonly source: {
182
182
  readonly bytes: string;
@@ -386,7 +386,7 @@ export declare const route: Route<{
386
386
  };
387
387
  } | {
388
388
  readonly document: {
389
- readonly format: "pdf" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html" | "txt" | "md";
389
+ readonly format: "pdf" | "txt" | "md" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html";
390
390
  readonly name: string;
391
391
  readonly source: {
392
392
  readonly bytes: string;
@@ -405,7 +405,7 @@ export declare const route: Route<{
405
405
  };
406
406
  } | {
407
407
  readonly document: {
408
- readonly format: "pdf" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html" | "txt" | "md";
408
+ readonly format: "pdf" | "txt" | "md" | "csv" | "doc" | "docx" | "xls" | "xlsx" | "html";
409
409
  readonly name: string;
410
410
  readonly source: {
411
411
  readonly bytes: string;
@@ -13,6 +13,7 @@ import { Lifecycle } from "./utils/lifecycle.js";
13
13
  import { MistralToolID } from "./utils/mistral-tool-id.js";
14
14
  import { ToolSchemaProjection } from "./utils/tool-schema.js";
15
15
  import { ToolStream } from "./utils/tool-stream.js";
16
+ import { concatBytes } from "../utils/bytes.js";
16
17
  const ADAPTER = "bedrock-converse";
17
18
  // =============================================================================
18
19
  // Request Body Schema
@@ -162,10 +163,10 @@ const lowerToolSpec = (tool, inputSchema) => ({
162
163
  inputSchema: { json: inputSchema },
163
164
  },
164
165
  });
165
- const lowerTools = (compatibility, breakpoints, tools) => {
166
+ const lowerTools = (model, breakpoints, tools) => {
166
167
  const result = [];
167
168
  for (const tool of tools) {
168
- result.push(lowerToolSpec(tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, compatibility)));
169
+ result.push(lowerToolSpec(tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, model)));
169
170
  const cachePoint = BedrockCache.block(breakpoints, tool.cache);
170
171
  if (cachePoint)
171
172
  result.push(cachePoint);
@@ -221,12 +222,7 @@ const lowerToolResultContent = Effect.fn("BedrockConverse.lowerToolResultContent
221
222
  content.push({ text: item.text });
222
223
  continue;
223
224
  }
224
- const media = yield* BedrockMedia.lower({
225
- type: "media",
226
- mediaType: item.mime,
227
- data: item.uri,
228
- filename: item.name,
229
- }, documentNames);
225
+ const media = yield* BedrockMedia.lower(ProviderShared.toolFileMedia(item), documentNames);
230
226
  content.push(...media);
231
227
  }
232
228
  return content;
@@ -353,7 +349,7 @@ const fromRequest = Effect.fn("BedrockConverse.fromRequest")(function* (request)
353
349
  if (flattened.tools.length === 0)
354
350
  return undefined;
355
351
  return {
356
- tools: lowerTools(request.model.compatibility?.toolSchema, breakpoints, flattened.tools),
352
+ tools: lowerTools(request.model, breakpoints, flattened.tools),
357
353
  // Converse has no native "none". Keep definitions stable for prompt
358
354
  // caching and omit only the unsupported choice.
359
355
  toolChoice,
@@ -418,14 +414,7 @@ const mapUsage = (usage, providerMetadataKey) => {
418
414
  providerMetadata: { [providerMetadataKey]: usage },
419
415
  });
420
416
  };
421
- const encodeRedactedContent = (chunks) => {
422
- const bytes = new Uint8Array(chunks.reduce((total, chunk) => total + chunk.length, 0));
423
- chunks.reduce((offset, chunk) => {
424
- bytes.set(chunk, offset);
425
- return offset + chunk.length;
426
- }, 0);
427
- return Encoding.encodeBase64(bytes);
428
- };
417
+ const encodeRedactedContent = (chunks) => Encoding.encodeBase64(concatBytes(chunks));
429
418
  const step = (state, event) => Effect.gen(function* () {
430
419
  if (event.contentBlockStart?.start?.toolUse) {
431
420
  const index = event.contentBlockStart.contentBlockIndex;
@@ -0,0 +1,32 @@
1
+ import { Schema } from "effect";
2
+ import { ImageModel, ImageResponse, type ImageRequestFor } from "../image.js";
3
+ import { MediaProtocol } from "../route/media-protocol.js";
4
+ import { MediaRoute } from "../route/media.js";
5
+ export declare const DEFAULT_BASE_URL = "https://api.bfl.ai";
6
+ export type BlackForestLabsImageOptions = {
7
+ readonly safety_tolerance?: number;
8
+ readonly prompt_upsampling?: boolean;
9
+ readonly disable_pup?: boolean;
10
+ readonly raw?: boolean;
11
+ readonly guidance?: number;
12
+ readonly steps?: number;
13
+ } & Record<string, unknown>;
14
+ export type Request = ImageRequestFor<BlackForestLabsImageOptions>;
15
+ /** Regional clusters answer on different hosts, so the returned `polling_url` is followed verbatim. */
16
+ export declare const Token: Schema.Struct<{
17
+ readonly id: Schema.String;
18
+ readonly pollingURL: Schema.String;
19
+ }>;
20
+ export type Token = Schema.Schema.Type<typeof Token>;
21
+ export declare const protocol: MediaProtocol.Queued<Request, ImageResponse, {
22
+ readonly id: string;
23
+ readonly pollingURL: string;
24
+ }>;
25
+ export declare const model: (input: MediaRoute.ModelInput) => ImageModel<BlackForestLabsImageOptions>;
26
+ export declare const BlackForestLabsImages: {
27
+ readonly protocol: MediaProtocol.Queued<Request, ImageResponse, {
28
+ readonly id: string;
29
+ readonly pollingURL: string;
30
+ }>;
31
+ readonly model: (input: MediaRoute.ModelInput) => ImageModel<BlackForestLabsImageOptions>;
32
+ };