@opencode/ai 2.0.15 → 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.
- package/README.md +286 -2
- package/dist/generation.d.ts +36 -22
- package/dist/generation.js +53 -24
- package/dist/image-client.d.ts +14 -7
- package/dist/image-client.js +24 -8
- package/dist/image.d.ts +398 -47
- package/dist/image.js +47 -45
- package/dist/index.d.ts +13 -1
- package/dist/index.js +9 -0
- package/dist/media-model.d.ts +44 -0
- package/dist/media-model.js +49 -0
- package/dist/media.d.ts +10 -9
- package/dist/media.js +9 -10
- package/dist/promise.d.ts +428 -8
- package/dist/promise.js +40 -3
- package/dist/protocols/alibaba-chat.d.ts +12 -0
- package/dist/protocols/alibaba-responses.d.ts +2 -2
- package/dist/protocols/anthropic-messages.js +1 -2
- package/dist/protocols/assemblyai-transcription.d.ts +40 -0
- package/dist/protocols/assemblyai-transcription.js +138 -0
- package/dist/protocols/bedrock-converse.js +5 -11
- package/dist/protocols/bfl-images.d.ts +32 -0
- package/dist/protocols/bfl-images.js +153 -0
- package/dist/protocols/cartesia-speech.d.ts +127 -0
- package/dist/protocols/cartesia-speech.js +126 -0
- package/dist/protocols/deepgram-speech.d.ts +119 -0
- package/dist/protocols/deepgram-speech.js +92 -0
- package/dist/protocols/deepgram-transcription.d.ts +25 -0
- package/dist/protocols/deepgram-transcription.js +129 -0
- package/dist/protocols/elevenlabs-speech.d.ts +122 -0
- package/dist/protocols/elevenlabs-speech.js +115 -0
- package/dist/protocols/fal-images.d.ts +24 -0
- package/dist/protocols/fal-images.js +114 -0
- package/dist/protocols/fal-video.d.ts +29 -0
- package/dist/protocols/fal-video.js +88 -0
- package/dist/protocols/gemini.d.ts +9 -9
- package/dist/protocols/gemini.js +8 -34
- package/dist/protocols/google-images.js +2 -14
- package/dist/protocols/google-speech.d.ts +130 -0
- package/dist/protocols/google-speech.js +84 -0
- package/dist/protocols/google-transcription.d.ts +173 -0
- package/dist/protocols/google-transcription.js +138 -0
- package/dist/protocols/google-video.d.ts +26 -0
- package/dist/protocols/google-video.js +158 -0
- package/dist/protocols/meta-images.js +2 -9
- package/dist/protocols/meta-responses.d.ts +4 -4
- package/dist/protocols/meta-responses.js +1 -1
- package/dist/protocols/open-responses.d.ts +6 -6
- package/dist/protocols/open-responses.js +1 -2
- package/dist/protocols/openai-chat.d.ts +84 -0
- package/dist/protocols/openai-chat.js +26 -14
- package/dist/protocols/openai-compatible-chat.d.ts +12 -0
- package/dist/protocols/openai-compatible-responses.d.ts +2 -2
- package/dist/protocols/openai-images.d.ts +124 -3
- package/dist/protocols/openai-images.js +107 -54
- package/dist/protocols/openai-responses.d.ts +15 -15
- package/dist/protocols/openai-responses.js +5 -6
- package/dist/protocols/openai-speech.d.ts +116 -0
- package/dist/protocols/openai-speech.js +98 -0
- package/dist/protocols/openai-transcription.d.ts +207 -0
- package/dist/protocols/openai-transcription.js +190 -0
- package/dist/protocols/replicate-images.d.ts +28 -0
- package/dist/protocols/replicate-images.js +133 -0
- package/dist/protocols/runway-video.d.ts +38 -0
- package/dist/protocols/runway-video.js +146 -0
- package/dist/protocols/shared.d.ts +13 -3
- package/dist/protocols/shared.js +23 -3
- package/dist/protocols/stability-images.d.ts +38 -0
- package/dist/protocols/stability-images.js +148 -0
- package/dist/protocols/utils/fal-queue.d.ts +28 -0
- package/dist/protocols/utils/fal-queue.js +69 -0
- package/dist/protocols/utils/gemini-generate-content.d.ts +65 -0
- package/dist/protocols/utils/gemini-generate-content.js +65 -0
- package/dist/protocols/utils/gemini-json-schema.d.ts +3 -0
- package/dist/protocols/utils/gemini-json-schema.js +76 -0
- package/dist/protocols/utils/media-input.d.ts +8 -0
- package/dist/protocols/utils/media-input.js +18 -0
- package/dist/protocols/utils/speech-stream.d.ts +49 -0
- package/dist/protocols/utils/speech-stream.js +67 -0
- package/dist/protocols/utils/tool-schema.d.ts +2 -2
- package/dist/protocols/utils/tool-schema.js +40 -17
- package/dist/protocols/xai-images.js +1 -12
- package/dist/protocols/xai-responses.d.ts +2 -2
- package/dist/protocols/xai-video.d.ts +34 -0
- package/dist/protocols/xai-video.js +147 -0
- package/dist/protocols/zai-chat.d.ts +13 -1
- package/dist/provider-error.js +3 -0
- package/dist/providers/alibaba.d.ts +14 -2
- package/dist/providers/amazon-bedrock-mantle.d.ts +14 -2
- package/dist/providers/assemblyai.d.ts +25 -0
- package/dist/providers/assemblyai.js +29 -0
- package/dist/providers/azure.d.ts +18 -6
- package/dist/providers/baseten.d.ts +24 -0
- package/dist/providers/black-forest-labs.d.ts +25 -0
- package/dist/providers/black-forest-labs.js +28 -0
- package/dist/providers/cartesia.d.ts +24 -0
- package/dist/providers/cartesia.js +22 -0
- package/dist/providers/cerebras.d.ts +24 -0
- package/dist/providers/cloudflare-ai-gateway.d.ts +30 -6
- package/dist/providers/cloudflare-workers-ai.d.ts +24 -0
- package/dist/providers/deepgram.d.ts +29 -0
- package/dist/providers/deepgram.js +31 -0
- package/dist/providers/deepinfra.d.ts +24 -0
- package/dist/providers/deepseek.d.ts +24 -0
- package/dist/providers/elevenlabs.d.ts +24 -0
- package/dist/providers/elevenlabs.js +28 -0
- package/dist/providers/fal.d.ts +29 -0
- package/dist/providers/fal.js +33 -0
- package/dist/providers/fireworks.d.ts +24 -0
- package/dist/providers/google-vertex-chat.d.ts +12 -0
- package/dist/providers/google-vertex-responses.d.ts +2 -2
- package/dist/providers/google-vertex.d.ts +3 -3
- package/dist/providers/google.d.ts +18 -3
- package/dist/providers/google.js +11 -2
- package/dist/providers/groq.d.ts +24 -0
- package/dist/providers/index.d.ts +9 -0
- package/dist/providers/index.js +9 -0
- package/dist/providers/meta.d.ts +14 -2
- package/dist/providers/minimax.d.ts +14 -2
- package/dist/providers/moonshot.d.ts +14 -2
- package/dist/providers/moonshot.js +3 -3
- package/dist/providers/openai-compatible-responses.d.ts +2 -2
- package/dist/providers/openai-compatible.d.ts +12 -0
- package/dist/providers/openai.d.ts +25 -3
- package/dist/providers/openai.js +10 -1
- package/dist/providers/openrouter.d.ts +48 -0
- package/dist/providers/replicate.d.ts +25 -0
- package/dist/providers/replicate.js +22 -0
- package/dist/providers/runway.d.ts +24 -0
- package/dist/providers/runway.js +22 -0
- package/dist/providers/stability.d.ts +28 -0
- package/dist/providers/stability.js +23 -0
- package/dist/providers/togetherai.d.ts +24 -0
- package/dist/providers/xai.d.ts +17 -0
- package/dist/providers/xai.js +5 -2
- package/dist/providers/zai-coding-plan.d.ts +15 -3
- package/dist/providers/zai.d.ts +13 -1
- package/dist/route/auth.d.ts +4 -1
- package/dist/route/auth.js +6 -0
- package/dist/route/framing.d.ts +5 -1
- package/dist/route/framing.js +9 -0
- package/dist/route/media-protocol.d.ts +116 -3
- package/dist/route/media-protocol.js +60 -4
- package/dist/route/media.d.ts +58 -7
- package/dist/route/media.js +201 -29
- package/dist/schema/events.d.ts +0 -6
- package/dist/schema/messages.d.ts +0 -3
- package/dist/schema/options.d.ts +4 -3
- package/dist/schema/options.js +3 -2
- package/dist/speech-client.d.ts +21 -0
- package/dist/speech-client.js +25 -0
- package/dist/speech.d.ts +1150 -0
- package/dist/speech.js +119 -0
- package/dist/transcription-client.d.ts +28 -0
- package/dist/transcription-client.js +44 -0
- package/dist/transcription.d.ts +1504 -0
- package/dist/transcription.js +133 -0
- package/dist/utils/bytes.d.ts +1 -0
- package/dist/utils/bytes.js +10 -0
- package/dist/utils/media-type.d.ts +1 -0
- package/dist/utils/media-type.js +22 -1
- package/dist/video-client.d.ts +28 -0
- package/dist/video-client.js +40 -0
- package/dist/video.d.ts +1359 -0
- package/dist/video.js +119 -0
- package/package.json +3 -3
- package/dist/protocols/utils/gemini-tool-schema.d.ts +0 -2
- package/dist/protocols/utils/gemini-tool-schema.js +0 -103
|
@@ -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
|
+
};
|
|
@@ -37,15 +37,8 @@ const Response = Schema.Struct({
|
|
|
37
37
|
// 5. Request body construction
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
39
|
const isEdit = (request) => (request.images?.length ?? 0) > 0;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (inline)
|
|
43
|
-
return Effect.succeed({ image_url: inline.dataUrl });
|
|
44
|
-
const url = ProviderShared.mediaUrl(asset);
|
|
45
|
-
if (url)
|
|
46
|
-
return Effect.succeed({ image_url: url });
|
|
47
|
-
return Effect.fail(ProviderShared.invalidRequest(`${NAME} accepts image bytes and URLs`));
|
|
48
|
-
};
|
|
40
|
+
// Meta has no file handles: refs are rejected even when they name this provider.
|
|
41
|
+
const reference = (asset) => ProviderShared.mediaReference(asset, undefined, NAME).pipe(Effect.map((item) => ({ image_url: item.value })));
|
|
49
42
|
const fromRequest = Effect.fn("MetaImages.fromRequest")(function* (request) {
|
|
50
43
|
const images = yield* Effect.forEach(request.images ?? [], reference);
|
|
51
44
|
const { responseFormat, reasoningStrength, toolEnablement, ...native } = request.providerOptions ?? {};
|
|
@@ -92,8 +92,8 @@ export declare const protocol: Protocol<{
|
|
|
92
92
|
} | {
|
|
93
93
|
readonly type: "input_file";
|
|
94
94
|
readonly filename: string;
|
|
95
|
-
readonly detail?: string | undefined;
|
|
96
95
|
readonly file_data?: string | undefined;
|
|
96
|
+
readonly detail?: string | undefined;
|
|
97
97
|
readonly file_url?: string | undefined;
|
|
98
98
|
} | {
|
|
99
99
|
readonly type: "input_text";
|
|
@@ -129,8 +129,8 @@ export declare const protocol: Protocol<{
|
|
|
129
129
|
} | {
|
|
130
130
|
readonly type: "input_file";
|
|
131
131
|
readonly filename: string;
|
|
132
|
-
readonly detail?: string | undefined;
|
|
133
132
|
readonly file_data?: string | undefined;
|
|
133
|
+
readonly detail?: string | undefined;
|
|
134
134
|
readonly file_url?: string | undefined;
|
|
135
135
|
} | {
|
|
136
136
|
readonly type: "input_text";
|
|
@@ -338,8 +338,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
338
338
|
} | {
|
|
339
339
|
readonly type: "input_file";
|
|
340
340
|
readonly filename: string;
|
|
341
|
-
readonly detail?: string | undefined;
|
|
342
341
|
readonly file_data?: string | undefined;
|
|
342
|
+
readonly detail?: string | undefined;
|
|
343
343
|
readonly file_url?: string | undefined;
|
|
344
344
|
} | {
|
|
345
345
|
readonly type: "input_text";
|
|
@@ -375,8 +375,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
375
375
|
} | {
|
|
376
376
|
readonly type: "input_file";
|
|
377
377
|
readonly filename: string;
|
|
378
|
-
readonly detail?: string | undefined;
|
|
379
378
|
readonly file_data?: string | undefined;
|
|
379
|
+
readonly detail?: string | undefined;
|
|
380
380
|
readonly file_url?: string | undefined;
|
|
381
381
|
} | {
|
|
382
382
|
readonly type: "input_text";
|
|
@@ -82,7 +82,7 @@ const fromRequest = Effect.fn("MetaResponses.fromRequest")(function* (request) {
|
|
|
82
82
|
? undefined
|
|
83
83
|
: yield* Effect.forEach(projected.tools, (tool) => Effect.gen(function* () {
|
|
84
84
|
if (tool.native === undefined)
|
|
85
|
-
return yield* OpenResponses.lowerTool(NAME, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, request.model
|
|
85
|
+
return yield* OpenResponses.lowerTool(NAME, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, request.model));
|
|
86
86
|
return yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(NativeTool))(tool.native.meta);
|
|
87
87
|
})),
|
|
88
88
|
tool_choice: OpenResponses.allowedToolChoice(request) ??
|
|
@@ -895,8 +895,8 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
|
|
|
895
895
|
} | {
|
|
896
896
|
readonly type: "input_file";
|
|
897
897
|
readonly filename: string;
|
|
898
|
-
readonly detail?: string | undefined;
|
|
899
898
|
readonly file_data?: string | undefined;
|
|
899
|
+
readonly detail?: string | undefined;
|
|
900
900
|
readonly file_url?: string | undefined;
|
|
901
901
|
} | {
|
|
902
902
|
readonly type: "input_text";
|
|
@@ -932,8 +932,8 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
|
|
|
932
932
|
} | {
|
|
933
933
|
readonly type: "input_file";
|
|
934
934
|
readonly filename: string;
|
|
935
|
-
readonly detail?: string | undefined;
|
|
936
935
|
readonly file_data?: string | undefined;
|
|
936
|
+
readonly detail?: string | undefined;
|
|
937
937
|
readonly file_url?: string | undefined;
|
|
938
938
|
} | {
|
|
939
939
|
readonly type: "input_text";
|
|
@@ -1292,8 +1292,8 @@ export declare const protocol: Protocol<{
|
|
|
1292
1292
|
} | {
|
|
1293
1293
|
readonly type: "input_file";
|
|
1294
1294
|
readonly filename: string;
|
|
1295
|
-
readonly detail?: string | undefined;
|
|
1296
1295
|
readonly file_data?: string | undefined;
|
|
1296
|
+
readonly detail?: string | undefined;
|
|
1297
1297
|
readonly file_url?: string | undefined;
|
|
1298
1298
|
} | {
|
|
1299
1299
|
readonly type: "input_text";
|
|
@@ -1329,8 +1329,8 @@ export declare const protocol: Protocol<{
|
|
|
1329
1329
|
} | {
|
|
1330
1330
|
readonly type: "input_file";
|
|
1331
1331
|
readonly filename: string;
|
|
1332
|
-
readonly detail?: string | undefined;
|
|
1333
1332
|
readonly file_data?: string | undefined;
|
|
1333
|
+
readonly detail?: string | undefined;
|
|
1334
1334
|
readonly file_url?: string | undefined;
|
|
1335
1335
|
} | {
|
|
1336
1336
|
readonly type: "input_text";
|
|
@@ -1513,8 +1513,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
1513
1513
|
} | {
|
|
1514
1514
|
readonly type: "input_file";
|
|
1515
1515
|
readonly filename: string;
|
|
1516
|
-
readonly detail?: string | undefined;
|
|
1517
1516
|
readonly file_data?: string | undefined;
|
|
1517
|
+
readonly detail?: string | undefined;
|
|
1518
1518
|
readonly file_url?: string | undefined;
|
|
1519
1519
|
} | {
|
|
1520
1520
|
readonly type: "input_text";
|
|
@@ -1550,8 +1550,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
1550
1550
|
} | {
|
|
1551
1551
|
readonly type: "input_file";
|
|
1552
1552
|
readonly filename: string;
|
|
1553
|
-
readonly detail?: string | undefined;
|
|
1554
1553
|
readonly file_data?: string | undefined;
|
|
1554
|
+
readonly detail?: string | undefined;
|
|
1555
1555
|
readonly file_url?: string | undefined;
|
|
1556
1556
|
} | {
|
|
1557
1557
|
readonly type: "input_text";
|
|
@@ -603,7 +603,6 @@ export const allowedToolChoice = (request) => {
|
|
|
603
603
|
};
|
|
604
604
|
export const fromRequestWithAdapter = Effect.fn("OpenResponses.fromRequestWithAdapter")(function* (request, adapter) {
|
|
605
605
|
const projected = ProviderShared.flattenToolRequest(request);
|
|
606
|
-
const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
|
|
607
606
|
return {
|
|
608
607
|
...(yield* lowerConversation(projected.request, adapter)),
|
|
609
608
|
...lowerGeneration(request),
|
|
@@ -611,7 +610,7 @@ export const fromRequestWithAdapter = Effect.fn("OpenResponses.fromRequestWithAd
|
|
|
611
610
|
? undefined
|
|
612
611
|
: yield* Effect.forEach(projected.tools, (tool) => tool.native !== undefined && adapter.nativeTool
|
|
613
612
|
? adapter.nativeTool(tool.native)
|
|
614
|
-
: lowerTool(adapter.name, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema,
|
|
613
|
+
: lowerTool(adapter.name, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, request.model))),
|
|
615
614
|
tool_choice: allowedToolChoice(request) ??
|
|
616
615
|
(request.toolChoice ? yield* lowerToolChoice(adapter.name, request.toolChoice) : undefined),
|
|
617
616
|
};
|