@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,173 @@
1
+ import { MediaProtocol } from "../route/media-protocol.js";
2
+ import { MediaRoute } from "../route/media.js";
3
+ import { TranscriptionModel, type TranscriptionRequestFor, type TranscriptionSegment, type TranscriptionWord } from "../transcription.js";
4
+ import { GeminiGenerateContent } from "./utils/gemini-generate-content.js";
5
+ export declare const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
6
+ /**
7
+ * Merged into `generationConfig`. The API rejects `customVocabulary` and `mode: "SMART"` alongside diarization or word
8
+ * timestamps.
9
+ */
10
+ export type GoogleTranscriptionOptions = {
11
+ readonly audioTranscriptionConfig?: {
12
+ readonly mode?: "VERBATIM" | "SMART" | (string & {});
13
+ readonly customVocabulary?: ReadonlyArray<string>;
14
+ readonly languageCodes?: ReadonlyArray<string>;
15
+ };
16
+ } & Record<string, unknown>;
17
+ export type Request = TranscriptionRequestFor<GoogleTranscriptionOptions>;
18
+ interface State extends GeminiGenerateContent.Metadata {
19
+ readonly text: string;
20
+ readonly segments: Array<TranscriptionSegment>;
21
+ readonly words: Array<TranscriptionWord>;
22
+ }
23
+ export declare const protocol: MediaProtocol.Streamed<Request, {
24
+ readonly id: string;
25
+ readonly type: "generation-queued";
26
+ readonly position?: number | undefined;
27
+ } | {
28
+ readonly id: string;
29
+ readonly type: "generation-progress";
30
+ readonly progress?: number | undefined;
31
+ } | {
32
+ readonly type: "text-delta";
33
+ readonly delta: string;
34
+ } | {
35
+ readonly type: "segment";
36
+ readonly segment: {
37
+ readonly text: string;
38
+ readonly startSeconds: number;
39
+ readonly endSeconds: number;
40
+ readonly speaker?: string | undefined;
41
+ };
42
+ } | {
43
+ readonly type: "finish";
44
+ readonly text: string;
45
+ readonly durationSeconds?: number | undefined;
46
+ readonly providerMetadata?: {
47
+ readonly [x: string]: {
48
+ readonly [x: string]: unknown;
49
+ };
50
+ } | undefined;
51
+ readonly usage?: {
52
+ readonly type: "tokens";
53
+ readonly input?: number | undefined;
54
+ readonly output?: number | undefined;
55
+ readonly total?: number | undefined;
56
+ readonly details?: {
57
+ readonly [x: string]: unknown;
58
+ } | undefined;
59
+ } | {
60
+ readonly type: "seconds";
61
+ readonly seconds: number;
62
+ } | {
63
+ readonly type: "characters";
64
+ readonly characters: number;
65
+ } | {
66
+ readonly type: "credits";
67
+ readonly credits: number;
68
+ } | {
69
+ readonly type: "compute";
70
+ readonly seconds: number;
71
+ } | undefined;
72
+ readonly notices?: readonly {
73
+ readonly type: "other" | "moderated" | "filtered";
74
+ readonly message: string;
75
+ readonly providerMetadata?: {
76
+ readonly [x: string]: {
77
+ readonly [x: string]: unknown;
78
+ };
79
+ } | undefined;
80
+ }[] | undefined;
81
+ readonly language?: string | undefined;
82
+ readonly segments?: readonly {
83
+ readonly text: string;
84
+ readonly startSeconds: number;
85
+ readonly endSeconds: number;
86
+ readonly speaker?: string | undefined;
87
+ }[] | undefined;
88
+ readonly words?: readonly {
89
+ readonly text: string;
90
+ readonly startSeconds: number;
91
+ readonly endSeconds: number;
92
+ readonly speaker?: string | undefined;
93
+ readonly confidence?: number | undefined;
94
+ }[] | undefined;
95
+ }, string, State>;
96
+ export declare const model: (input: MediaRoute.ModelInput) => TranscriptionModel<GoogleTranscriptionOptions>;
97
+ export declare const GoogleTranscription: {
98
+ readonly protocol: MediaProtocol.Streamed<Request, {
99
+ readonly id: string;
100
+ readonly type: "generation-queued";
101
+ readonly position?: number | undefined;
102
+ } | {
103
+ readonly id: string;
104
+ readonly type: "generation-progress";
105
+ readonly progress?: number | undefined;
106
+ } | {
107
+ readonly type: "text-delta";
108
+ readonly delta: string;
109
+ } | {
110
+ readonly type: "segment";
111
+ readonly segment: {
112
+ readonly text: string;
113
+ readonly startSeconds: number;
114
+ readonly endSeconds: number;
115
+ readonly speaker?: string | undefined;
116
+ };
117
+ } | {
118
+ readonly type: "finish";
119
+ readonly text: string;
120
+ readonly durationSeconds?: number | undefined;
121
+ readonly providerMetadata?: {
122
+ readonly [x: string]: {
123
+ readonly [x: string]: unknown;
124
+ };
125
+ } | undefined;
126
+ readonly usage?: {
127
+ readonly type: "tokens";
128
+ readonly input?: number | undefined;
129
+ readonly output?: number | undefined;
130
+ readonly total?: number | undefined;
131
+ readonly details?: {
132
+ readonly [x: string]: unknown;
133
+ } | undefined;
134
+ } | {
135
+ readonly type: "seconds";
136
+ readonly seconds: number;
137
+ } | {
138
+ readonly type: "characters";
139
+ readonly characters: number;
140
+ } | {
141
+ readonly type: "credits";
142
+ readonly credits: number;
143
+ } | {
144
+ readonly type: "compute";
145
+ readonly seconds: number;
146
+ } | undefined;
147
+ readonly notices?: readonly {
148
+ readonly type: "other" | "moderated" | "filtered";
149
+ readonly message: string;
150
+ readonly providerMetadata?: {
151
+ readonly [x: string]: {
152
+ readonly [x: string]: unknown;
153
+ };
154
+ } | undefined;
155
+ }[] | undefined;
156
+ readonly language?: string | undefined;
157
+ readonly segments?: readonly {
158
+ readonly text: string;
159
+ readonly startSeconds: number;
160
+ readonly endSeconds: number;
161
+ readonly speaker?: string | undefined;
162
+ }[] | undefined;
163
+ readonly words?: readonly {
164
+ readonly text: string;
165
+ readonly startSeconds: number;
166
+ readonly endSeconds: number;
167
+ readonly speaker?: string | undefined;
168
+ readonly confidence?: number | undefined;
169
+ }[] | undefined;
170
+ }, string, State>;
171
+ readonly model: (input: MediaRoute.ModelInput) => TranscriptionModel<GoogleTranscriptionOptions>;
172
+ };
173
+ export {};
@@ -0,0 +1,138 @@
1
+ import { Effect, Schema, SchemaGetter } from "effect";
2
+ import { MediaProtocol } from "../route/media-protocol.js";
3
+ import { MediaRoute } from "../route/media.js";
4
+ import { ProviderID, mergeJsonRecords } from "../schema/index.js";
5
+ import { TranscriptionFinishEvent, TranscriptionModel, TranscriptionSegmentEvent, TranscriptionTextDeltaEvent, } from "../transcription.js";
6
+ import { ProviderShared } from "./shared.js";
7
+ import { GeminiGenerateContent } from "./utils/gemini-generate-content.js";
8
+ const ADAPTER = "google-transcription";
9
+ const NAME = "Google Transcription";
10
+ const PROVIDER = ProviderID.make("google");
11
+ export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
12
+ // ---------------------------------------------------------------------------
13
+ // 3. Streaming event schema
14
+ // ---------------------------------------------------------------------------
15
+ const Seconds = Schema.String.check(Schema.isPattern(/^\d+(\.\d+)?s$/)).pipe(Schema.decodeTo(Schema.Number, {
16
+ decode: SchemaGetter.transform((value) => Number.parseFloat(value)),
17
+ encode: SchemaGetter.transform((value) => `${value}s`),
18
+ }));
19
+ const AudioTranscription = Schema.Struct({
20
+ text: Schema.String,
21
+ speakerLabel: Schema.optional(Schema.String),
22
+ words: Schema.optional(Schema.Array(Schema.Struct({
23
+ word: Schema.String,
24
+ startOffset: Seconds,
25
+ endOffset: Seconds,
26
+ speakerLabel: Schema.optional(Schema.String),
27
+ }))),
28
+ });
29
+ const decodeChunk = MediaProtocol.decodeFrame(ADAPTER, NAME, GeminiGenerateContent.chunk(Schema.Struct({ audioTranscription: Schema.optional(AudioTranscription) })));
30
+ // ---------------------------------------------------------------------------
31
+ // 5. Request body construction
32
+ // ---------------------------------------------------------------------------
33
+ const fromRequest = Effect.fn("GoogleTranscription.fromRequest")(function* (request) {
34
+ // General Gemini models ignore `audioTranscriptionConfig` and answer the audio conversationally.
35
+ if (!request.model.id.includes("transcribe"))
36
+ return yield* ProviderShared.unsupportedOperation({
37
+ operation: "transcription.model",
38
+ provider: PROVIDER,
39
+ route: ADAPTER,
40
+ message: `${request.model.id} is not a transcription model; use a transcribe model such as gemini-3.5-transcribe`,
41
+ });
42
+ return MediaProtocol.json(mergeJsonRecords({
43
+ contents: [{ role: "user", parts: [yield* GeminiGenerateContent.mediaPart(ADAPTER, request.audio)] }],
44
+ generationConfig: mergeJsonRecords({
45
+ audioTranscriptionConfig: {
46
+ languageCodes: request.language === undefined ? undefined : [request.language],
47
+ // Parts carry no offsets of their own, so segment times come from word offsets.
48
+ wordTimestamp: request.diarize === true || request.timestamps === "word" || request.timestamps === "segment"
49
+ ? true
50
+ : undefined,
51
+ diarization: request.diarize === true ? true : undefined,
52
+ },
53
+ }, request.providerOptions),
54
+ }, request.http?.body) ?? {});
55
+ });
56
+ // ---------------------------------------------------------------------------
57
+ // 6. Stream parsing
58
+ // ---------------------------------------------------------------------------
59
+ const turn = (part) => {
60
+ const words = (part.words ?? []).map((word) => ({
61
+ text: word.word,
62
+ startSeconds: word.startOffset,
63
+ endSeconds: word.endOffset,
64
+ speaker: word.speakerLabel ?? part.speakerLabel,
65
+ }));
66
+ const first = words[0];
67
+ const last = words.at(-1);
68
+ return {
69
+ text: part.text,
70
+ words,
71
+ segment: first === undefined || last === undefined
72
+ ? undefined
73
+ : {
74
+ text: part.text,
75
+ startSeconds: first.startSeconds,
76
+ endSeconds: last.endSeconds,
77
+ speaker: part.speakerLabel,
78
+ },
79
+ };
80
+ };
81
+ const step = Effect.fn("GoogleTranscription.step")(function* (state, frame) {
82
+ const chunk = yield* decodeChunk(frame);
83
+ const blocked = GeminiGenerateContent.blocked(NAME, chunk, frame);
84
+ if (blocked !== undefined)
85
+ return yield* blocked;
86
+ const turns = (chunk.candidates?.[0]?.content?.parts ?? []).flatMap((part) => part.audioTranscription === undefined ? [] : [turn(part.audioTranscription)]);
87
+ const segments = turns.flatMap((item) => (item.segment === undefined ? [] : [item.segment]));
88
+ state.words.push(...turns.flatMap((item) => item.words));
89
+ state.segments.push(...segments);
90
+ // Each part is one whole speaker turn without surrounding whitespace, so turns join with a space.
91
+ const text = turns
92
+ .map((item) => item.text)
93
+ .filter((item) => item.length > 0)
94
+ .join(" ");
95
+ const delta = text.length === 0 || state.text.length === 0 ? text : ` ${text}`;
96
+ const events = [
97
+ ...(delta.length === 0 ? [] : [TranscriptionTextDeltaEvent.make({ delta })]),
98
+ ...segments.map((segment) => TranscriptionSegmentEvent.make({ segment })),
99
+ ];
100
+ return [{ ...GeminiGenerateContent.track(state, chunk), text: state.text + delta }, events];
101
+ });
102
+ const finish = (state) => {
103
+ if (state.finishReason === undefined)
104
+ return Effect.fail(MediaProtocol.incomplete(ADAPTER));
105
+ return Effect.succeed([
106
+ TranscriptionFinishEvent.make({
107
+ text: state.text,
108
+ segments: state.segments.length === 0 ? undefined : state.segments,
109
+ words: state.words.length === 0 ? undefined : state.words,
110
+ usage: GeminiGenerateContent.usage(state.usage),
111
+ providerMetadata: GeminiGenerateContent.providerMetadata(state),
112
+ }),
113
+ ]);
114
+ };
115
+ // ---------------------------------------------------------------------------
116
+ // 7. Protocol and route
117
+ // ---------------------------------------------------------------------------
118
+ export const protocol = MediaProtocol.stream({
119
+ id: ADAPTER,
120
+ name: NAME,
121
+ unsupported: ["prompt", "speakers"],
122
+ body: { from: fromRequest },
123
+ frames: (bytes, context) => GeminiGenerateContent.frames(bytes, context.request.mode),
124
+ initial: () => ({ text: "", segments: [], words: [] }),
125
+ step,
126
+ finish,
127
+ });
128
+ export const model = (input) => TranscriptionModel.fromRoute({
129
+ id: ADAPTER,
130
+ provider: PROVIDER,
131
+ protocol,
132
+ baseURL: DEFAULT_BASE_URL,
133
+ path: ({ request }) => GeminiGenerateContent.path(request.model.id, request.mode),
134
+ }, input);
135
+ export const GoogleTranscription = {
136
+ protocol,
137
+ model,
138
+ };
@@ -0,0 +1,26 @@
1
+ import { Schema } from "effect";
2
+ import { MediaProtocol } from "../route/media-protocol.js";
3
+ import { MediaRoute } from "../route/media.js";
4
+ import { VideoModel, VideoResponse, type VideoRequestFor } from "../video.js";
5
+ export declare const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
6
+ export type GoogleVideoString<Known extends string> = Known | (string & {});
7
+ /** Provider-native `parameters`. Common fields (`aspectRatio`, `resolution`, `durationSeconds`, `seed`) live on the request. */
8
+ export type GoogleVideoOptions = {
9
+ readonly personGeneration?: GoogleVideoString<"allow_all" | "allow_adult" | "dont_allow">;
10
+ } & Record<string, unknown>;
11
+ export type Request = VideoRequestFor<GoogleVideoOptions>;
12
+ /** The long-running operation name, e.g. `models/veo-3.1-generate-preview/operations/abc123`. */
13
+ export declare const Token: Schema.Struct<{
14
+ readonly operation: Schema.String;
15
+ }>;
16
+ export type Token = Schema.Schema.Type<typeof Token>;
17
+ export declare const protocol: MediaProtocol.Queued<Request, VideoResponse, {
18
+ readonly operation: string;
19
+ }>;
20
+ export declare const model: (input: MediaRoute.ModelInput) => VideoModel<GoogleVideoOptions>;
21
+ export declare const GoogleVideo: {
22
+ readonly protocol: MediaProtocol.Queued<Request, VideoResponse, {
23
+ readonly operation: string;
24
+ }>;
25
+ readonly model: (input: MediaRoute.ModelInput) => VideoModel<GoogleVideoOptions>;
26
+ };
@@ -0,0 +1,158 @@
1
+ import { Duration, Effect, Schema } from "effect";
2
+ import { Media } from "../media.js";
3
+ import { MediaProtocol } from "../route/media-protocol.js";
4
+ import { MediaRoute } from "../route/media.js";
5
+ import { ProviderID, mergeJsonRecords } from "../schema/index.js";
6
+ import { VideoModel, VideoResponse } from "../video.js";
7
+ import { ProviderShared, optionalArray } from "./shared.js";
8
+ const ADAPTER = "google-video";
9
+ const NAME = "Google Veo";
10
+ const PROVIDER = ProviderID.make("google");
11
+ export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
12
+ /** Veo keeps generated files for two days; the asset carries that deadline so callers materialize in time. */
13
+ const FILE_RETENTION = Duration.days(2);
14
+ // ---------------------------------------------------------------------------
15
+ // 2. Token and response schemas
16
+ // ---------------------------------------------------------------------------
17
+ /** The long-running operation name, e.g. `models/veo-3.1-generate-preview/operations/abc123`. */
18
+ export const Token = Schema.Struct({ operation: Schema.String });
19
+ const StartResponse = Schema.Struct({ name: Schema.String });
20
+ const Operation = Schema.Struct({
21
+ done: Schema.optional(Schema.Boolean),
22
+ error: Schema.optional(Schema.Struct({ message: Schema.optional(Schema.String) })),
23
+ response: Schema.optional(Schema.Struct({
24
+ generateVideoResponse: Schema.optional(Schema.Struct({
25
+ generatedSamples: optionalArray(Schema.Struct({
26
+ video: Schema.optional(Schema.Struct({
27
+ uri: Schema.optional(Schema.String),
28
+ mimeType: Schema.optional(Schema.String),
29
+ })),
30
+ })),
31
+ raiMediaFilteredCount: Schema.optional(Schema.Number),
32
+ raiMediaFilteredReasons: optionalArray(Schema.String),
33
+ })),
34
+ })),
35
+ metadata: Schema.optional(Schema.Unknown),
36
+ });
37
+ // ---------------------------------------------------------------------------
38
+ // 5. Request body construction
39
+ // ---------------------------------------------------------------------------
40
+ // Veo takes inline media only; a prior Veo output is `Media.url` with transient auth, so materialize it first.
41
+ const inlineMedia = (asset) => ProviderShared.requireInlineMedia(NAME, asset).pipe(Effect.map((inline) => ({ inlineData: { mimeType: inline.mime, data: inline.base64 } })));
42
+ const fromRequest = Effect.fn("GoogleVideo.fromRequest")(function* (request) {
43
+ if (request.n !== undefined && request.n > 1)
44
+ return yield* ProviderShared.unsupportedOperation({
45
+ operation: "video.n",
46
+ provider: PROVIDER,
47
+ route: ADAPTER,
48
+ message: `${NAME} generates one video per request; call it once per video instead of n=${request.n}`,
49
+ });
50
+ if (request.audio === false)
51
+ return yield* ProviderShared.unsupportedOperation({
52
+ operation: "video.audio",
53
+ provider: PROVIDER,
54
+ route: ADAPTER,
55
+ message: `${NAME} always generates audio; audio: false cannot be honored`,
56
+ });
57
+ if (request.frames?.last !== undefined && request.frames.first === undefined)
58
+ return yield* ProviderShared.invalidRequest(`${NAME} requires frames.first when frames.last is set`);
59
+ const image = request.frames?.first === undefined ? undefined : yield* inlineMedia(request.frames.first);
60
+ const lastFrame = request.frames?.last === undefined ? undefined : yield* inlineMedia(request.frames.last);
61
+ const video = request.video === undefined ? undefined : yield* inlineMedia(request.video);
62
+ const referenceImages = yield* Effect.forEach(request.references ?? [], (asset) => inlineMedia(asset).pipe(Effect.map((image) => ({ image, referenceType: "asset" }))));
63
+ return MediaProtocol.json(mergeJsonRecords({
64
+ instances: [
65
+ {
66
+ prompt: request.prompt,
67
+ image,
68
+ lastFrame,
69
+ referenceImages: referenceImages.length === 0 ? undefined : referenceImages,
70
+ video,
71
+ },
72
+ ],
73
+ parameters: mergeJsonRecords({
74
+ aspectRatio: request.aspectRatio,
75
+ resolution: request.resolution,
76
+ durationSeconds: request.durationSeconds,
77
+ negativePrompt: request.negativePrompt,
78
+ seed: request.seed,
79
+ }, request.providerOptions),
80
+ }, request.http?.body) ?? {});
81
+ });
82
+ // ---------------------------------------------------------------------------
83
+ // 6. Response decoding
84
+ // ---------------------------------------------------------------------------
85
+ const decodeStart = MediaProtocol.decodeStarted(ADAPTER, NAME, StartResponse, (value) => ({
86
+ token: { operation: value.name },
87
+ snapshot: { id: value.name, status: "running" },
88
+ }));
89
+ // Operations carry no status string: not done is running, done with `error` failed, otherwise completed.
90
+ const statusOf = (operation) => {
91
+ if (operation.done !== true)
92
+ return "running";
93
+ return operation.error === undefined ? "completed" : "failed";
94
+ };
95
+ const decodeOperation = MediaProtocol.decodeJson(ADAPTER, NAME, Operation);
96
+ const decodeStatus = Effect.fn("GoogleVideo.decodeStatus")(function* (response, context) {
97
+ const output = yield* decodeOperation(response);
98
+ return { id: context.token.operation, status: statusOf(output.value) };
99
+ });
100
+ const decodeResult = Effect.fn("GoogleVideo.decodeResult")(function* (response, context) {
101
+ const output = yield* decodeOperation(response);
102
+ const operation = output.value;
103
+ const status = statusOf(operation);
104
+ if (status === "running")
105
+ return yield* output.invalid(`${NAME} operation ${context.token.operation} has not finished`);
106
+ if (status === "failed")
107
+ return yield* output.ended("failed", `${NAME} operation failed${operation.error?.message === undefined ? "" : `: ${operation.error.message}`}`);
108
+ const generated = operation.response?.generateVideoResponse;
109
+ // Downloads require the same API key as the poll; the asset carries it transiently and follows the redirect.
110
+ const videos = yield* Effect.forEach((generated?.generatedSamples ?? []).flatMap((sample) => sample.video?.uri === undefined ? [] : [{ uri: sample.video.uri, mimeType: sample.video.mimeType }]), (video) => MediaProtocol.expiringUrl(video.uri, FILE_RETENTION, {
111
+ mediaType: video.mimeType ?? "video/mp4",
112
+ headers: context.auth,
113
+ }));
114
+ const reasons = generated?.raiMediaFilteredReasons ?? [];
115
+ const notices = reasons.map((reason) => ({
116
+ type: "filtered",
117
+ message: `${NAME} filtered media: ${reason}`,
118
+ providerMetadata: { google: { raiMediaFilteredReason: reason } },
119
+ }));
120
+ if (videos.length === 0 && (reasons.length > 0 || (generated?.raiMediaFilteredCount ?? 0) > 0))
121
+ return yield* output.contentPolicy(`${NAME} filtered every video${reasons.length === 0 ? "" : `: ${reasons.join("; ")}`}`);
122
+ if (videos.length === 0)
123
+ return yield* output.invalid(`${NAME} operation completed without any video`);
124
+ return new VideoResponse({
125
+ videos,
126
+ notices: notices.length === 0 ? undefined : notices,
127
+ providerMetadata: {
128
+ google: {
129
+ operation: context.token.operation,
130
+ raiMediaFilteredCount: generated?.raiMediaFilteredCount,
131
+ metadata: operation.metadata,
132
+ },
133
+ },
134
+ });
135
+ });
136
+ // ---------------------------------------------------------------------------
137
+ // 7. Protocol and route
138
+ // ---------------------------------------------------------------------------
139
+ const operationPath = (token) => `/${token.operation}`;
140
+ export const protocol = MediaProtocol.queued({
141
+ id: ADAPTER,
142
+ name: NAME,
143
+ token: Token,
144
+ start: { body: { from: fromRequest }, decode: decodeStart },
145
+ status: { path: operationPath, decode: decodeStatus },
146
+ result: { path: operationPath, decode: decodeResult },
147
+ });
148
+ export const model = (input) => VideoModel.fromRoute({
149
+ id: ADAPTER,
150
+ provider: PROVIDER,
151
+ protocol,
152
+ baseURL: DEFAULT_BASE_URL,
153
+ path: ({ request }) => `/models/${request.model.id}:predictLongRunning`,
154
+ }, input);
155
+ export const GoogleVideo = {
156
+ protocol,
157
+ model,
158
+ };
@@ -1,12 +1,9 @@
1
- import { ImageModel } from "../image.js";
2
- import { Auth } from "../route/auth.js";
3
- import { type HttpOptions } from "../schema/index.js";
1
+ import { ImageModel, ImageResponse, type ImageRequestFor } from "../image.js";
2
+ import { MediaProtocol } from "../route/media-protocol.js";
3
+ import { MediaRoute } from "../route/media.js";
4
4
  type OpenString<Known extends string> = Known | (string & {});
5
+ /** Provider-native options. Common fields (`n`, `size`, `format`, `images`) live on the request. */
5
6
  export type ImageOptions = {
6
- readonly n?: number;
7
- /** Aspect ratio hint, not an exact output resolution. */
8
- readonly size?: string;
9
- readonly outputFormat?: OpenString<"webp" | "png" | "jpeg">;
10
7
  readonly responseFormat?: OpenString<"b64_json" | "url">;
11
8
  readonly reasoningStrength?: OpenString<"low" | "high">;
12
9
  readonly toolEnablement?: {
@@ -16,11 +13,9 @@ export type ImageOptions = {
16
13
  };
17
14
  readonly [key: string]: unknown;
18
15
  };
19
- export declare const model: (input: {
20
- readonly id: string;
21
- readonly auth: Auth.Definition;
16
+ export type Request = ImageRequestFor<ImageOptions>;
17
+ export declare const protocol: MediaProtocol.Inline<Request, ImageResponse>;
18
+ export declare const model: (input: MediaRoute.ModelInput & {
22
19
  readonly baseURL: string;
23
- readonly headers?: Record<string, string>;
24
- readonly http?: HttpOptions;
25
20
  }) => ImageModel<ImageOptions>;
26
21
  export * as MetaImages from "./meta-images.js";