@opencode/ai 2.0.16 → 2.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +187 -129
- package/dist/ai-client.d.ts +8 -0
- package/dist/ai-client.js +12 -0
- package/dist/experimental/evaluation-client.d.ts +3 -3
- package/dist/experimental/evaluation-client.js +1 -1
- package/dist/experimental/evaluation.js +1 -1
- package/dist/generation.d.ts +6 -10
- package/dist/generation.js +17 -20
- package/dist/image-client.d.ts +59 -24
- package/dist/image-client.js +16 -40
- package/dist/image.d.ts +15 -27
- package/dist/image.js +4 -15
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/llm.d.ts +7 -5
- package/dist/llm.js +10 -4
- package/dist/media-client.d.ts +30 -0
- package/dist/media-client.js +51 -0
- package/dist/media-model.d.ts +9 -10
- package/dist/media-model.js +10 -12
- package/dist/media.js +2 -2
- package/dist/promise.d.ts +68 -30
- package/dist/promise.js +52 -31
- package/dist/protocols/alibaba-chat.js +4 -1
- package/dist/protocols/alibaba-messages.d.ts +1 -1
- package/dist/protocols/alibaba-messages.js +6 -4
- package/dist/protocols/anthropic-messages.d.ts +34 -34
- package/dist/protocols/anthropic-messages.js +14 -8
- package/dist/protocols/assemblyai-transcription.d.ts +2 -1
- package/dist/protocols/assemblyai-transcription.js +14 -16
- package/dist/protocols/bedrock-converse.d.ts +7 -0
- package/dist/protocols/bedrock-converse.js +37 -11
- package/dist/protocols/bfl-images.d.ts +7 -1
- package/dist/protocols/bfl-images.js +36 -36
- package/dist/protocols/cartesia-speech.d.ts +18 -2
- package/dist/protocols/cartesia-speech.js +10 -16
- package/dist/protocols/deepgram-speech.d.ts +19 -3
- package/dist/protocols/deepgram-speech.js +11 -12
- package/dist/protocols/deepgram-transcription.d.ts +2 -1
- package/dist/protocols/deepgram-transcription.js +9 -13
- package/dist/protocols/elevenlabs-speech.d.ts +19 -3
- package/dist/protocols/elevenlabs-speech.js +9 -13
- package/dist/protocols/fal-images.d.ts +2 -1
- package/dist/protocols/fal-images.js +30 -40
- package/dist/protocols/fal-video.d.ts +2 -2
- package/dist/protocols/fal-video.js +9 -24
- package/dist/protocols/gemini.d.ts +13 -13
- package/dist/protocols/gemini.js +16 -7
- package/dist/protocols/google-images.d.ts +3 -3
- package/dist/protocols/google-images.js +11 -21
- package/dist/protocols/google-speech.d.ts +16 -0
- package/dist/protocols/google-speech.js +20 -16
- package/dist/protocols/google-transcription.d.ts +2 -1
- package/dist/protocols/google-transcription.js +8 -20
- package/dist/protocols/google-video.d.ts +2 -2
- package/dist/protocols/google-video.js +14 -30
- package/dist/protocols/meta-images.d.ts +3 -4
- package/dist/protocols/meta-images.js +12 -21
- package/dist/protocols/meta-messages.d.ts +5 -5
- package/dist/protocols/meta-responses.js +6 -4
- package/dist/protocols/open-responses.d.ts +4 -3
- package/dist/protocols/open-responses.js +4 -8
- package/dist/protocols/openai-chat.js +3 -4
- package/dist/protocols/openai-images.d.ts +7 -5
- package/dist/protocols/openai-images.js +65 -66
- package/dist/protocols/openai-responses.d.ts +10 -10
- package/dist/protocols/openai-responses.js +31 -30
- package/dist/protocols/openai-speech.d.ts +21 -1
- package/dist/protocols/openai-speech.js +11 -12
- package/dist/protocols/openai-transcription.d.ts +4 -0
- package/dist/protocols/openai-transcription.js +44 -32
- package/dist/protocols/replicate-images.js +11 -17
- package/dist/protocols/runway-video.d.ts +3 -3
- package/dist/protocols/runway-video.js +11 -17
- package/dist/protocols/shared.d.ts +11 -17
- package/dist/protocols/shared.js +11 -40
- package/dist/protocols/stability-images.d.ts +2 -1
- package/dist/protocols/stability-images.js +24 -36
- package/dist/protocols/utils/fal-queue.d.ts +1 -3
- package/dist/protocols/utils/fal-queue.js +4 -6
- package/dist/protocols/utils/media-input.d.ts +15 -1
- package/dist/protocols/utils/media-input.js +22 -0
- package/dist/protocols/utils/responses-checkpoint.js +3 -7
- package/dist/protocols/utils/responses-compaction.d.ts +3 -1
- package/dist/protocols/utils/responses-compaction.js +16 -3
- package/dist/protocols/utils/speech-stream.d.ts +3 -3
- package/dist/protocols/utils/speech-stream.js +1 -4
- package/dist/protocols/utils/tool-schema.d.ts +2 -2
- package/dist/protocols/utils/tool-schema.js +29 -9
- package/dist/protocols/xai-images.d.ts +4 -4
- package/dist/protocols/xai-images.js +14 -29
- package/dist/protocols/xai-responses.js +1 -1
- package/dist/protocols/xai-video.js +12 -18
- package/dist/protocols/zai-images.d.ts +2 -2
- package/dist/protocols/zai-images.js +11 -14
- package/dist/protocols/zai-messages.d.ts +1 -1
- package/dist/provider-error.js +7 -1
- package/dist/providers/alibaba.d.ts +1 -1
- package/dist/providers/amazon-bedrock.d.ts +2 -0
- package/dist/providers/amazon-bedrock.js +1 -0
- package/dist/providers/anthropic-compatible.d.ts +5 -5
- package/dist/providers/anthropic.d.ts +5 -5
- package/dist/providers/assemblyai.d.ts +1 -1
- package/dist/providers/assemblyai.js +5 -10
- package/dist/providers/azure.d.ts +4 -4
- package/dist/providers/azure.js +2 -2
- package/dist/providers/black-forest-labs.d.ts +1 -1
- package/dist/providers/black-forest-labs.js +5 -10
- package/dist/providers/cartesia.d.ts +1 -1
- package/dist/providers/cartesia.js +5 -10
- package/dist/providers/cloudflare-ai-gateway.d.ts +14 -14
- package/dist/providers/deepgram.d.ts +1 -1
- package/dist/providers/deepgram.js +6 -12
- package/dist/providers/elevenlabs.d.ts +1 -1
- package/dist/providers/elevenlabs.js +5 -10
- package/dist/providers/fal.d.ts +1 -1
- package/dist/providers/fal.js +5 -12
- package/dist/providers/google-vertex-messages.d.ts +5 -5
- package/dist/providers/google-vertex.d.ts +3 -3
- package/dist/providers/google.d.ts +3 -3
- package/dist/providers/google.js +7 -12
- package/dist/providers/meta.d.ts +8 -8
- package/dist/providers/meta.js +4 -8
- package/dist/providers/minimax.d.ts +5 -5
- package/dist/providers/moonshot.d.ts +5 -5
- package/dist/providers/openai-options.d.ts +3 -9
- package/dist/providers/openai-options.js +4 -7
- package/dist/providers/openai.d.ts +9 -10
- package/dist/providers/openai.js +11 -12
- package/dist/providers/opencode-zen.js +1 -1
- package/dist/providers/openrouter.d.ts +6 -7
- package/dist/providers/openrouter.js +10 -5
- package/dist/providers/replicate.d.ts +1 -1
- package/dist/providers/replicate.js +5 -10
- package/dist/providers/runway.d.ts +1 -1
- package/dist/providers/runway.js +5 -10
- package/dist/providers/stability.d.ts +1 -1
- package/dist/providers/stability.js +6 -11
- package/dist/providers/typesafe-ai.js +1 -1
- package/dist/providers/vercel-ai-gateway.js +1 -1
- package/dist/providers/xai.js +5 -10
- package/dist/providers/zai-coding-plan.d.ts +1 -1
- package/dist/providers/zai.js +4 -8
- package/dist/route/auth.d.ts +1 -1
- package/dist/route/auth.js +14 -13
- package/dist/route/client.d.ts +9 -7
- package/dist/route/client.js +6 -8
- package/dist/route/endpoint.d.ts +1 -0
- package/dist/route/endpoint.js +2 -2
- package/dist/route/executor-service.d.ts +4 -2
- package/dist/route/executor-service.js +2 -1
- package/dist/route/executor.d.ts +3 -1
- package/dist/route/executor.js +7 -0
- package/dist/route/framing.d.ts +10 -1
- package/dist/route/framing.js +45 -5
- package/dist/route/index.d.ts +1 -1
- package/dist/route/media-protocol.d.ts +49 -35
- package/dist/route/media-protocol.js +77 -65
- package/dist/route/media.d.ts +10 -16
- package/dist/route/media.js +46 -62
- package/dist/route/protocol.d.ts +3 -1
- package/dist/schema/options.d.ts +6 -3
- package/dist/schema/options.js +7 -3
- package/dist/speech-client.d.ts +62 -17
- package/dist/speech-client.js +17 -21
- package/dist/speech.d.ts +178 -21
- package/dist/speech.js +13 -8
- package/dist/testing.d.ts +2 -2
- package/dist/transcription-client.d.ts +78 -24
- package/dist/transcription-client.js +9 -40
- package/dist/transcription.d.ts +15 -27
- package/dist/transcription.js +4 -10
- package/dist/utils/json.d.ts +4 -0
- package/dist/utils/json.js +4 -0
- package/dist/utils/media-type.d.ts +2 -1
- package/dist/utils/media-type.js +3 -1
- package/dist/video-client.d.ts +55 -24
- package/dist/video-client.js +16 -36
- package/dist/video.d.ts +18 -26
- package/dist/video.js +15 -16
- package/package.json +3 -3
- package/dist/protocols/utils/meta-image.d.ts +0 -2
- package/dist/protocols/utils/meta-image.js +0 -13
- package/dist/protocols/utils/openai-image.d.ts +0 -5
- package/dist/protocols/utils/openai-image.js +0 -18
|
@@ -4,12 +4,10 @@ import { Media } from "../media.js";
|
|
|
4
4
|
import { Framing } from "../route/framing.js";
|
|
5
5
|
import { MediaProtocol } from "../route/media-protocol.js";
|
|
6
6
|
import { MediaRoute } from "../route/media.js";
|
|
7
|
-
import {
|
|
7
|
+
import { mergeJsonRecords } from "../schema/index.js";
|
|
8
8
|
import { ProviderShared } from "./shared.js";
|
|
9
9
|
import { MediaInput } from "./utils/media-input.js";
|
|
10
|
-
const
|
|
11
|
-
const NAME = "OpenAI Images";
|
|
12
|
-
const PROVIDER = ProviderID.make("openai");
|
|
10
|
+
const route = MediaProtocol.identity({ id: "openai-images", name: "OpenAI Images", provider: "openai" });
|
|
13
11
|
export const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
14
12
|
export const PATH = "/images/generations";
|
|
15
13
|
export const EDIT_PATH = "/images/edits";
|
|
@@ -23,13 +21,16 @@ const Usage = Schema.Struct({
|
|
|
23
21
|
input_tokens_details: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
|
|
24
22
|
output_tokens_details: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
|
|
25
23
|
});
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
b64_json: Schema.optional(Schema.String),
|
|
29
|
-
url: Schema.optional(Schema.String),
|
|
30
|
-
revised_prompt: Schema.optional(Schema.String),
|
|
31
|
-
})),
|
|
24
|
+
/** What the provider actually rendered; it can differ from the request when `auto` or a default applied. */
|
|
25
|
+
const Settings = {
|
|
32
26
|
output_format: Schema.optional(Schema.String),
|
|
27
|
+
size: Schema.optional(Schema.String),
|
|
28
|
+
quality: Schema.optional(Schema.String),
|
|
29
|
+
background: Schema.optional(Schema.String),
|
|
30
|
+
};
|
|
31
|
+
const OpenAIImageResponse = Schema.Struct({
|
|
32
|
+
data: Schema.Array(Schema.Struct({ b64_json: Schema.String })),
|
|
33
|
+
...Settings,
|
|
33
34
|
usage: Schema.optional(Usage),
|
|
34
35
|
});
|
|
35
36
|
// ---------------------------------------------------------------------------
|
|
@@ -40,16 +41,18 @@ const StreamEvent = Schema.Union([
|
|
|
40
41
|
type: Schema.Literals(["image_generation.partial_image", "image_edit.partial_image"]),
|
|
41
42
|
b64_json: Schema.String,
|
|
42
43
|
partial_image_index: Schema.Number,
|
|
44
|
+
...Settings,
|
|
43
45
|
output_format: Schema.String,
|
|
44
46
|
}),
|
|
45
47
|
Schema.Struct({
|
|
46
48
|
type: Schema.Literals(["image_generation.completed", "image_edit.completed"]),
|
|
47
49
|
b64_json: Schema.String,
|
|
50
|
+
...Settings,
|
|
48
51
|
output_format: Schema.String,
|
|
49
52
|
usage: Schema.optional(Usage),
|
|
50
53
|
}),
|
|
51
54
|
]);
|
|
52
|
-
const decodeEvent =
|
|
55
|
+
const decodeEvent = route.decodeFrame(StreamEvent);
|
|
53
56
|
const decodeDocument = Schema.decodeUnknownEffect(Schema.fromJsonString(OpenAIImageResponse));
|
|
54
57
|
// ---------------------------------------------------------------------------
|
|
55
58
|
// 5. Request body construction
|
|
@@ -65,25 +68,13 @@ const nativeOptions = (options) => {
|
|
|
65
68
|
const streamOptions = (request) => {
|
|
66
69
|
if (request.mode !== "stream")
|
|
67
70
|
return Effect.succeed(undefined);
|
|
68
|
-
if (request.model.id.startsWith("dall-e"))
|
|
69
|
-
return Effect.fail(ProviderShared.unsupportedOperation({
|
|
70
|
-
operation: "media.stream",
|
|
71
|
-
provider: PROVIDER,
|
|
72
|
-
route: ADAPTER,
|
|
73
|
-
message: `${request.model.id} does not stream; use Image.generate or a GPT image model`,
|
|
74
|
-
}));
|
|
75
71
|
if (request.n !== undefined && request.n > 1)
|
|
76
|
-
return Effect.fail(
|
|
77
|
-
operation: "media.n",
|
|
78
|
-
provider: PROVIDER,
|
|
79
|
-
route: ADAPTER,
|
|
80
|
-
message: `${NAME} streams one image; use Image.generate for n=${request.n}`,
|
|
81
|
-
}));
|
|
72
|
+
return Effect.fail(route.unsupported("media.n", `${route.name} streams one image; use Image.generate for n=${request.n}`));
|
|
82
73
|
return Effect.succeed({ stream: true, partial_images: request.providerOptions?.partialImages ?? 2 });
|
|
83
74
|
};
|
|
84
75
|
const isEdit = (request) => (request.images?.length ?? 0) > 0;
|
|
85
76
|
const isInline = (asset) => asset.source.type === "bytes" || asset.source.type === "base64";
|
|
86
|
-
const reference = (asset) => ProviderShared.mediaReference(asset,
|
|
77
|
+
const reference = (asset) => ProviderShared.mediaReference(asset, route.provider, route.name).pipe(Effect.map((item) => (item.type === "ref" ? { file_id: item.value } : { image_url: item.value })));
|
|
87
78
|
const fromRequest = Effect.fn("OpenAIImages.fromRequest")(function* (request) {
|
|
88
79
|
const images = request.images ?? [];
|
|
89
80
|
const mask = request.mask;
|
|
@@ -93,17 +84,11 @@ const fromRequest = Effect.fn("OpenAIImages.fromRequest")(function* (request) {
|
|
|
93
84
|
// Owned bytes go through multipart edits; remote URLs and file IDs use the JSON edits body instead.
|
|
94
85
|
if (images.length > 0 && images.every(isInline) && (mask === undefined || isInline(mask))) {
|
|
95
86
|
const form = new FormData();
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
Object.entries(fields ?? {}).forEach(([key, value]) => {
|
|
99
|
-
if (RESERVED_FORM_FIELDS.has(key))
|
|
100
|
-
return;
|
|
101
|
-
form.append(key, typeof value === "string" ? value : ProviderShared.encodeJson(value));
|
|
102
|
-
});
|
|
103
|
-
const uploads = yield* Effect.forEach(images, (image) => MediaInput.inlineBytes(ADAPTER, image));
|
|
87
|
+
MediaInput.appendFields(form, { model: request.model.id, prompt: request.prompt }, { overlay: fields, reserved: RESERVED_FORM_FIELDS });
|
|
88
|
+
const uploads = yield* Effect.forEach(images, (image) => MediaInput.inlineBytes(route.id, image));
|
|
104
89
|
uploads.forEach((data, index) => form.append("image[]", MediaInput.blob(data, images[index].mediaType), `image-${index}`));
|
|
105
90
|
if (mask !== undefined)
|
|
106
|
-
form.append("mask", MediaInput.blob(yield* MediaInput.inlineBytes(
|
|
91
|
+
form.append("mask", MediaInput.blob(yield* MediaInput.inlineBytes(route.id, mask), mask.mediaType), "mask");
|
|
107
92
|
return MediaProtocol.multipart(form);
|
|
108
93
|
}
|
|
109
94
|
const references = yield* Effect.forEach(images, reference);
|
|
@@ -133,55 +118,75 @@ const usage = (value) => value === undefined
|
|
|
133
118
|
total: value.total_tokens,
|
|
134
119
|
details: { openai: value },
|
|
135
120
|
};
|
|
136
|
-
|
|
121
|
+
/** `size` echoes the rendered `WIDTHxHEIGHT`; `auto` or any other value leaves the dimensions unknown. */
|
|
122
|
+
const info = (format, size) => {
|
|
123
|
+
const match = size?.match(/^(\d+)x(\d+)$/);
|
|
124
|
+
return match ? { format, width: Number(match[1]), height: Number(match[2]) } : { format };
|
|
125
|
+
};
|
|
126
|
+
const eventImage = (frame, label, data, format, size) => MediaInput.decodedAsset((message, cause) => route.frameError(message, frame, cause), label, data, `image/${format}`, {
|
|
127
|
+
info: info(format, size),
|
|
128
|
+
});
|
|
137
129
|
const onEvent = Effect.fn("OpenAIImages.onEvent")(function* (state, frame) {
|
|
138
130
|
const event = yield* decodeEvent(frame);
|
|
139
131
|
const format = event.output_format;
|
|
140
132
|
if ("partial_image_index" in event) {
|
|
141
|
-
const image = yield* eventImage(frame, `${
|
|
133
|
+
const image = yield* eventImage(frame, `${route.name} partial image`, event.b64_json, format, event.size);
|
|
142
134
|
return [state, [ImagePartialEvent.make({ index: event.partial_image_index, image })]];
|
|
143
135
|
}
|
|
144
|
-
const image = yield* eventImage(frame, `${
|
|
136
|
+
const image = yield* eventImage(frame, `${route.name} result ${state.completed}`, event.b64_json, format, event.size);
|
|
145
137
|
return [
|
|
146
|
-
{
|
|
138
|
+
{
|
|
139
|
+
completed: state.completed + 1,
|
|
140
|
+
format,
|
|
141
|
+
size: event.size,
|
|
142
|
+
quality: event.quality,
|
|
143
|
+
background: event.background,
|
|
144
|
+
usage: usage(event.usage),
|
|
145
|
+
},
|
|
147
146
|
[ImageOutputEvent.make({ index: state.completed, image })],
|
|
148
147
|
];
|
|
149
148
|
});
|
|
150
149
|
const onDocument = Effect.fn("OpenAIImages.onDocument")(function* (frame) {
|
|
151
|
-
const invalid = (message, cause) =>
|
|
152
|
-
const decoded = yield* decodeDocument(frame.document).pipe(Effect.mapError((cause) => invalid(`${
|
|
150
|
+
const invalid = (message, cause) => route.frameError(message, frame.document, cause);
|
|
151
|
+
const decoded = yield* decodeDocument(frame.document).pipe(Effect.mapError((cause) => invalid(`${route.name} returned an invalid response`, cause)));
|
|
153
152
|
const format = decoded.output_format ?? frame.requested ?? "png";
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (item.b64_json)
|
|
158
|
-
return MediaInput.decodedAsset(invalid, `${NAME} result ${index}`, item.b64_json, mediaType, {
|
|
159
|
-
info: { format },
|
|
160
|
-
providerMetadata,
|
|
161
|
-
});
|
|
162
|
-
if (item.url)
|
|
163
|
-
return Effect.succeed(Media.url(item.url, { mediaType, info: { format }, providerMetadata }));
|
|
164
|
-
return Effect.fail(invalid(`${NAME} result ${index} has neither image data nor a URL`));
|
|
165
|
-
});
|
|
153
|
+
const images = yield* Effect.forEach(decoded.data, (item, index) => MediaInput.decodedAsset(invalid, `${route.name} result ${index}`, item.b64_json, `image/${format}`, {
|
|
154
|
+
info: info(format, decoded.size),
|
|
155
|
+
}));
|
|
166
156
|
if (images.length === 0)
|
|
167
|
-
return yield* invalid(`${
|
|
168
|
-
const state = {
|
|
157
|
+
return yield* invalid(`${route.name} returned no images`);
|
|
158
|
+
const state = {
|
|
159
|
+
completed: images.length,
|
|
160
|
+
format,
|
|
161
|
+
size: decoded.size,
|
|
162
|
+
quality: decoded.quality,
|
|
163
|
+
background: decoded.background,
|
|
164
|
+
usage: usage(decoded.usage),
|
|
165
|
+
};
|
|
169
166
|
return [state, images.map((image, index) => ImageOutputEvent.make({ index, image }))];
|
|
170
167
|
});
|
|
171
168
|
const step = (state, frame) => (typeof frame === "string" ? onEvent(state, frame) : onDocument(frame));
|
|
172
169
|
const finish = (state) => {
|
|
173
170
|
if (state.completed === 0)
|
|
174
|
-
return Effect.fail(
|
|
171
|
+
return Effect.fail(route.incomplete());
|
|
175
172
|
return Effect.succeed([
|
|
176
|
-
ImageFinishEvent.make({
|
|
173
|
+
ImageFinishEvent.make({
|
|
174
|
+
usage: state.usage,
|
|
175
|
+
providerMetadata: {
|
|
176
|
+
openai: {
|
|
177
|
+
outputFormat: state.format,
|
|
178
|
+
size: state.size,
|
|
179
|
+
quality: state.quality,
|
|
180
|
+
background: state.background,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
}),
|
|
177
184
|
]);
|
|
178
185
|
};
|
|
179
186
|
// ---------------------------------------------------------------------------
|
|
180
187
|
// 7. Protocol and route
|
|
181
188
|
// ---------------------------------------------------------------------------
|
|
182
|
-
export const protocol = MediaProtocol.stream({
|
|
183
|
-
id: ADAPTER,
|
|
184
|
-
name: NAME,
|
|
189
|
+
export const protocol = MediaProtocol.stream(route, {
|
|
185
190
|
unsupported: ["aspectRatio", "seed"],
|
|
186
191
|
body: { from: fromRequest },
|
|
187
192
|
frames: (bytes, context) => context.request.mode === "stream"
|
|
@@ -193,13 +198,7 @@ export const protocol = MediaProtocol.stream({
|
|
|
193
198
|
step,
|
|
194
199
|
finish,
|
|
195
200
|
});
|
|
196
|
-
export const model = (input) => ImageModel.fromRoute({
|
|
197
|
-
id: ADAPTER,
|
|
198
|
-
provider: PROVIDER,
|
|
199
|
-
protocol,
|
|
200
|
-
baseURL: DEFAULT_BASE_URL,
|
|
201
|
-
path: ({ request }) => (isEdit(request) ? EDIT_PATH : PATH),
|
|
202
|
-
}, input);
|
|
201
|
+
export const model = (input) => ImageModel.fromRoute({ protocol, baseURL: DEFAULT_BASE_URL, path: ({ request }) => (isEdit(request) ? EDIT_PATH : PATH) }, input);
|
|
203
202
|
export const OpenAIImages = {
|
|
204
203
|
protocol,
|
|
205
204
|
model,
|
|
@@ -364,8 +364,8 @@ export declare const protocol: Protocol<{
|
|
|
364
364
|
readonly type: "image_generation_call";
|
|
365
365
|
readonly status?: string | undefined;
|
|
366
366
|
readonly result?: string | null | undefined;
|
|
367
|
-
readonly revised_prompt?: string | null | undefined;
|
|
368
367
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
368
|
+
readonly revised_prompt?: string | null | undefined;
|
|
369
369
|
})[];
|
|
370
370
|
readonly model: string;
|
|
371
371
|
readonly stream: true;
|
|
@@ -390,9 +390,9 @@ export declare const protocol: Protocol<{
|
|
|
390
390
|
readonly size?: string | undefined;
|
|
391
391
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
392
392
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
393
|
-
readonly partial_images?: number | undefined;
|
|
394
393
|
readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
|
|
395
394
|
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
395
|
+
readonly partial_images?: number | undefined;
|
|
396
396
|
readonly output_compression?: number | undefined;
|
|
397
397
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
398
398
|
} | {
|
|
@@ -647,8 +647,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
647
647
|
readonly type: "image_generation_call";
|
|
648
648
|
readonly status?: string | undefined;
|
|
649
649
|
readonly result?: string | null | undefined;
|
|
650
|
-
readonly revised_prompt?: string | null | undefined;
|
|
651
650
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
651
|
+
readonly revised_prompt?: string | null | undefined;
|
|
652
652
|
})[];
|
|
653
653
|
readonly model: string;
|
|
654
654
|
readonly stream: true;
|
|
@@ -673,9 +673,9 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
673
673
|
readonly size?: string | undefined;
|
|
674
674
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
675
675
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
676
|
-
readonly partial_images?: number | undefined;
|
|
677
676
|
readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
|
|
678
677
|
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
678
|
+
readonly partial_images?: number | undefined;
|
|
679
679
|
readonly output_compression?: number | undefined;
|
|
680
680
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
681
681
|
} | {
|
|
@@ -875,8 +875,8 @@ export declare const channelTransport: (options: import("./open-responses-channe
|
|
|
875
875
|
readonly type: "image_generation_call";
|
|
876
876
|
readonly status?: string | undefined;
|
|
877
877
|
readonly result?: string | null | undefined;
|
|
878
|
-
readonly revised_prompt?: string | null | undefined;
|
|
879
878
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
879
|
+
readonly revised_prompt?: string | null | undefined;
|
|
880
880
|
})[];
|
|
881
881
|
readonly model: string;
|
|
882
882
|
readonly stream: true;
|
|
@@ -901,9 +901,9 @@ export declare const channelTransport: (options: import("./open-responses-channe
|
|
|
901
901
|
readonly size?: string | undefined;
|
|
902
902
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
903
903
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
904
|
-
readonly partial_images?: number | undefined;
|
|
905
904
|
readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
|
|
906
905
|
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
906
|
+
readonly partial_images?: number | undefined;
|
|
907
907
|
readonly output_compression?: number | undefined;
|
|
908
908
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
909
909
|
} | {
|
|
@@ -1103,8 +1103,8 @@ export declare const transport: import("../route/transport/index.js").Transport<
|
|
|
1103
1103
|
readonly type: "image_generation_call";
|
|
1104
1104
|
readonly status?: string | undefined;
|
|
1105
1105
|
readonly result?: string | null | undefined;
|
|
1106
|
-
readonly revised_prompt?: string | null | undefined;
|
|
1107
1106
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
1107
|
+
readonly revised_prompt?: string | null | undefined;
|
|
1108
1108
|
})[];
|
|
1109
1109
|
readonly model: string;
|
|
1110
1110
|
readonly stream: true;
|
|
@@ -1129,9 +1129,9 @@ export declare const transport: import("../route/transport/index.js").Transport<
|
|
|
1129
1129
|
readonly size?: string | undefined;
|
|
1130
1130
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
1131
1131
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
1132
|
-
readonly partial_images?: number | undefined;
|
|
1133
1132
|
readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
|
|
1134
1133
|
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
1134
|
+
readonly partial_images?: number | undefined;
|
|
1135
1135
|
readonly output_compression?: number | undefined;
|
|
1136
1136
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
1137
1137
|
} | {
|
|
@@ -1331,8 +1331,8 @@ export declare const route: Route<{
|
|
|
1331
1331
|
readonly type: "image_generation_call";
|
|
1332
1332
|
readonly status?: string | undefined;
|
|
1333
1333
|
readonly result?: string | null | undefined;
|
|
1334
|
-
readonly revised_prompt?: string | null | undefined;
|
|
1335
1334
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
1335
|
+
readonly revised_prompt?: string | null | undefined;
|
|
1336
1336
|
})[];
|
|
1337
1337
|
readonly model: string;
|
|
1338
1338
|
readonly stream: true;
|
|
@@ -1357,9 +1357,9 @@ export declare const route: Route<{
|
|
|
1357
1357
|
readonly size?: string | undefined;
|
|
1358
1358
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
1359
1359
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
1360
|
-
readonly partial_images?: number | undefined;
|
|
1361
1360
|
readonly quality?: "auto" | "low" | "medium" | "high" | undefined;
|
|
1362
1361
|
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
1362
|
+
readonly partial_images?: number | undefined;
|
|
1363
1363
|
readonly output_compression?: number | undefined;
|
|
1364
1364
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
1365
1365
|
} | {
|
|
@@ -5,14 +5,12 @@ import { Auth } from "../route/auth.js";
|
|
|
5
5
|
import { Endpoint } from "../route/endpoint.js";
|
|
6
6
|
import { Protocol } from "../route/protocol.js";
|
|
7
7
|
import { HttpTransport } from "../route/transport/index.js";
|
|
8
|
-
import { LLMRequest
|
|
8
|
+
import { LLMRequest } from "../schema/index.js";
|
|
9
9
|
import { resolveEffortUpdates } from "../effort-updates.js";
|
|
10
10
|
import { OpenResponses } from "./open-responses.js";
|
|
11
11
|
import { OpenResponsesOptions } from "./utils/open-responses-options.js";
|
|
12
12
|
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
|
|
13
|
-
import { OpenAIImage } from "./utils/openai-image.js";
|
|
14
13
|
import { ResponsesHostedTools } from "./utils/responses-hosted-tools.js";
|
|
15
|
-
import { ToolSchemaProjection } from "./utils/tool-schema.js";
|
|
16
14
|
import { OpenResponsesChannel } from "./open-responses-channel.js";
|
|
17
15
|
import { ResponsesCompaction } from "./utils/responses-compaction.js";
|
|
18
16
|
import { ResponsesCheckpoint } from "./utils/responses-checkpoint.js";
|
|
@@ -35,7 +33,14 @@ const OpenAIResponsesImageGenerationTool = Schema.Struct({
|
|
|
35
33
|
output_format: Schema.optional(Schema.Literals(["png", "jpeg", "webp"])),
|
|
36
34
|
partial_images: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),
|
|
37
35
|
quality: Schema.optional(Schema.Literals(["auto", "low", "medium", "high"])),
|
|
38
|
-
size: Schema.optional(
|
|
36
|
+
size: Schema.optional(Schema.String.check(Schema.makeFilter((value) => {
|
|
37
|
+
if (value === "auto")
|
|
38
|
+
return undefined;
|
|
39
|
+
const match = /^(\d+)x(\d+)$/.exec(value);
|
|
40
|
+
if (!match)
|
|
41
|
+
return "image size must be `auto` or `{width}x{height}`";
|
|
42
|
+
return Number(match[1]) > 0 && Number(match[2]) > 0 ? undefined : "image dimensions must be positive integers";
|
|
43
|
+
}))),
|
|
39
44
|
});
|
|
40
45
|
const OpenAIResponsesHostedToolItem = Schema.Union([
|
|
41
46
|
Schema.StructWithRest(Schema.Struct({
|
|
@@ -100,23 +105,23 @@ export const CompactionTrigger = Schema.Struct({ type: Schema.Literal("compactio
|
|
|
100
105
|
const CheckpointBody = Schema.Struct({
|
|
101
106
|
...OpenAIResponsesBody.fields,
|
|
102
107
|
input: Schema.Array(Schema.Union([OpenAIResponsesInputItem, CompactionTrigger])),
|
|
103
|
-
store: Schema.Literal(false),
|
|
104
|
-
prompt_cache_retention: optionalNull(Schema.String),
|
|
105
|
-
prompt_cache_options: optionalNull(Schema.Struct({ mode: Schema.optional(Schema.String), ttl: Schema.optional(Schema.String) })),
|
|
106
108
|
});
|
|
107
109
|
const adapter = {
|
|
108
110
|
id: ADAPTER,
|
|
109
111
|
name: NAME,
|
|
110
112
|
restoreHostedToolItem: (item) => (Schema.is(OpenAIResponsesHostedToolItem)(item) ? item : undefined),
|
|
111
113
|
};
|
|
112
|
-
//
|
|
114
|
+
// GPT-6 Astra, Sol, and Luna accept `configuration_update` only in standard mode (not `reasoning.mode: "pro"` or
|
|
115
|
+
// `-pro` slugs), and never alongside automatic `context_management` compaction.
|
|
113
116
|
const supportsEffortUpdates = (request) => {
|
|
114
117
|
if (request.providerOptions?.contextManagement !== undefined)
|
|
115
118
|
return false;
|
|
119
|
+
if (Schema.is(Schema.Struct({ mode: Schema.Literal("pro") }))(request.http?.body?.reasoning))
|
|
120
|
+
return false;
|
|
116
121
|
const override = request.model.compatibility?.supportsEffortUpdates;
|
|
117
122
|
if (override !== undefined)
|
|
118
123
|
return override;
|
|
119
|
-
return /(?:^|\/)gpt-6-astra$/i.test(request.model.id);
|
|
124
|
+
return /(?:^|\/)gpt-6-(?:astra|sol|luna)$/i.test(request.model.id);
|
|
120
125
|
};
|
|
121
126
|
const nativeImageToolInput = (tool) => {
|
|
122
127
|
const native = tool.native?.openai;
|
|
@@ -126,29 +131,30 @@ const nativeImageTool = (tool) => {
|
|
|
126
131
|
const native = nativeImageToolInput(tool);
|
|
127
132
|
return Schema.is(OpenAIResponsesImageGenerationTool)(native) ? native : undefined;
|
|
128
133
|
};
|
|
129
|
-
const lowerTool = Effect.fn("OpenAIResponses.lowerTool")(function* (tool
|
|
134
|
+
const lowerTool = Effect.fn("OpenAIResponses.lowerTool")(function* (tool) {
|
|
130
135
|
const native = nativeImageToolInput(tool);
|
|
131
136
|
if (native !== undefined) {
|
|
132
137
|
if (Schema.is(OpenAIResponsesImageGenerationTool)(native))
|
|
133
138
|
return native;
|
|
134
139
|
return yield* ProviderShared.invalidRequest("OpenAI Responses image generation tool options are invalid");
|
|
135
140
|
}
|
|
136
|
-
return yield* OpenResponses.lowerTool(NAME, tool
|
|
141
|
+
return yield* OpenResponses.lowerTool(NAME, tool);
|
|
137
142
|
});
|
|
138
143
|
// Native namespaces hold only function tools, so deeper levels flatten into
|
|
139
144
|
// the leaf names the same way non-native protocols flatten the whole tree.
|
|
140
|
-
const lowerToolEntry = Effect.fn("OpenAIResponses.lowerToolEntry")(function* (tool
|
|
145
|
+
const lowerToolEntry = Effect.fn("OpenAIResponses.lowerToolEntry")(function* (tool) {
|
|
141
146
|
if (tool.type === "tool")
|
|
142
|
-
return yield* lowerTool(tool
|
|
147
|
+
return yield* lowerTool(tool);
|
|
143
148
|
// OpenAI requires a namespace description; fall back to a generic one so a
|
|
144
149
|
// missing description never blocks the request.
|
|
145
150
|
return {
|
|
146
151
|
type: "namespace",
|
|
147
152
|
name: tool.name,
|
|
148
153
|
description: tool.description ?? `Tools in the ${tool.name} namespace.`,
|
|
149
|
-
tools: yield* Effect.forEach(ProviderShared.flattenTools(tool.tools), (leaf) => OpenResponses.lowerTool(NAME, leaf
|
|
154
|
+
tools: yield* Effect.forEach(ProviderShared.flattenTools(tool.tools), (leaf) => OpenResponses.lowerTool(NAME, leaf)),
|
|
150
155
|
};
|
|
151
156
|
});
|
|
157
|
+
const lowerTools = (request) => Effect.forEach(request.tools, lowerToolEntry);
|
|
152
158
|
const lowerToolChoice = (toolChoice, tools) => ProviderShared.matchToolChoice(NAME, toolChoice, {
|
|
153
159
|
auto: () => "auto",
|
|
154
160
|
none: () => "none",
|
|
@@ -166,9 +172,7 @@ const fromRequest = Effect.fn("OpenAIResponses.fromRequest")(function* (request)
|
|
|
166
172
|
...(yield* OpenResponses.lowerConversation(updates.request, adapter)),
|
|
167
173
|
...OpenResponses.lowerGeneration(request, { ...options, reasoningEffort: updates.effort }),
|
|
168
174
|
context_management: management?.map((edit) => ({ type: edit.type, compact_threshold: edit.compactThreshold })),
|
|
169
|
-
tools: request.tools.length === 0
|
|
170
|
-
? undefined
|
|
171
|
-
: yield* Effect.forEach(request.tools, (tool) => lowerToolEntry(tool, request.model)),
|
|
175
|
+
tools: request.tools.length === 0 ? undefined : yield* lowerTools(request),
|
|
172
176
|
tool_choice: request.tools.length === 0
|
|
173
177
|
? undefined
|
|
174
178
|
: (OpenResponses.allowedToolChoice(request) ??
|
|
@@ -178,25 +182,19 @@ const fromRequest = Effect.fn("OpenAIResponses.fromRequest")(function* (request)
|
|
|
178
182
|
const checkpointBody = {
|
|
179
183
|
schema: CheckpointBody,
|
|
180
184
|
from: Effect.fn("OpenAIResponses.checkpointBody")(function* (request) {
|
|
181
|
-
const native = yield* fromRequest(LLMRequest.update(request, { toolChoice: undefined }));
|
|
182
185
|
const overlay = request.http?.body;
|
|
183
186
|
// Complete history is required for stateless replay and SSE recovery. Raw input overrides bypass that contract.
|
|
184
187
|
if (overlay?.input !== undefined ||
|
|
185
188
|
overlay?.previous_response_id !== undefined ||
|
|
186
189
|
overlay?.conversation !== undefined)
|
|
187
190
|
return yield* ProviderShared.invalidRequest("Trigger compaction requires complete canonical history, not an input or continuation override");
|
|
188
|
-
|
|
189
|
-
|
|
191
|
+
if (overlay?.stream !== undefined && overlay.stream !== true)
|
|
192
|
+
return yield* ProviderShared.invalidRequest("Trigger compaction requires a streamed response");
|
|
193
|
+
const native = yield* fromRequest(request);
|
|
194
|
+
return {
|
|
195
|
+
...native,
|
|
190
196
|
input: [...native.input, { type: "compaction_trigger" }],
|
|
191
|
-
|
|
192
|
-
store: false,
|
|
193
|
-
parallel_tool_calls: true,
|
|
194
|
-
tool_choice: undefined,
|
|
195
|
-
context_management: undefined,
|
|
196
|
-
text: undefined,
|
|
197
|
-
max_output_tokens: undefined,
|
|
198
|
-
max_tool_calls: undefined,
|
|
199
|
-
});
|
|
197
|
+
};
|
|
200
198
|
}),
|
|
201
199
|
};
|
|
202
200
|
const hostedToolResult = Effect.fn("OpenAIResponses.hostedToolResult")(function* (item) {
|
|
@@ -267,7 +265,10 @@ export const transport = channelTransport({
|
|
|
267
265
|
headers: (headers) => Headers.set(headers, "openai-beta", headers["openai-beta"] ?? WEBSOCKET_PROTOCOL_HEADER),
|
|
268
266
|
});
|
|
269
267
|
export const route = Route.make({
|
|
270
|
-
compact: {
|
|
268
|
+
compact: {
|
|
269
|
+
endpoint: ResponsesCompaction.make(adapter, lowerTools),
|
|
270
|
+
trigger: ResponsesCheckpoint.make(checkpointBody),
|
|
271
|
+
},
|
|
271
272
|
id: ADAPTER,
|
|
272
273
|
provider: "openai",
|
|
273
274
|
providerMetadataKey: "openai",
|
|
@@ -5,13 +5,25 @@ import { SpeechModel, type SpeechRequestFor } from "../speech.js";
|
|
|
5
5
|
import { SpeechStream } from "./utils/speech-stream.js";
|
|
6
6
|
export declare const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
7
7
|
export declare const PATH = "/audio/speech";
|
|
8
|
-
|
|
8
|
+
/** `voice`, `instructions`, `speed`, and `format` are common request fields; other native body fields pass through. */
|
|
9
|
+
export type OpenAISpeechOptions = {
|
|
10
|
+
/** Defaults to `"sse"` in `stream` mode on models that support it; the merged value selects the response framing. */
|
|
11
|
+
readonly stream_format?: "sse" | "audio";
|
|
12
|
+
} & Record<string, unknown>;
|
|
9
13
|
export type Request = SpeechRequestFor<OpenAISpeechOptions>;
|
|
10
14
|
interface State extends SpeechStream.Audio {
|
|
11
15
|
readonly done: boolean;
|
|
12
16
|
readonly usage?: MediaUsage;
|
|
13
17
|
}
|
|
14
18
|
export declare const protocol: MediaProtocol.Streamed<Request, {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly type: "generation-queued";
|
|
21
|
+
readonly position?: number | undefined;
|
|
22
|
+
} | {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly type: "generation-progress";
|
|
25
|
+
readonly progress?: number | undefined;
|
|
26
|
+
} | {
|
|
15
27
|
readonly type: "audio-delta";
|
|
16
28
|
readonly chunk: Uint8Array<ArrayBufferLike>;
|
|
17
29
|
} | {
|
|
@@ -63,6 +75,14 @@ export declare const protocol: MediaProtocol.Streamed<Request, {
|
|
|
63
75
|
export declare const model: (input: MediaRoute.ModelInput) => SpeechModel<OpenAISpeechOptions>;
|
|
64
76
|
export declare const OpenAISpeech: {
|
|
65
77
|
readonly protocol: MediaProtocol.Streamed<Request, {
|
|
78
|
+
readonly id: string;
|
|
79
|
+
readonly type: "generation-queued";
|
|
80
|
+
readonly position?: number | undefined;
|
|
81
|
+
} | {
|
|
82
|
+
readonly id: string;
|
|
83
|
+
readonly type: "generation-progress";
|
|
84
|
+
readonly progress?: number | undefined;
|
|
85
|
+
} | {
|
|
66
86
|
readonly type: "audio-delta";
|
|
67
87
|
readonly chunk: Uint8Array<ArrayBufferLike>;
|
|
68
88
|
} | {
|
|
@@ -2,12 +2,10 @@ import { Effect, Schema } from "effect";
|
|
|
2
2
|
import { Framing } from "../route/framing.js";
|
|
3
3
|
import { MediaProtocol } from "../route/media-protocol.js";
|
|
4
4
|
import { MediaRoute } from "../route/media.js";
|
|
5
|
-
import {
|
|
5
|
+
import { mergeJsonRecords } from "../schema/index.js";
|
|
6
6
|
import { SpeechModel } from "../speech.js";
|
|
7
7
|
import { SpeechStream } from "./utils/speech-stream.js";
|
|
8
|
-
const
|
|
9
|
-
const NAME = "OpenAI Speech";
|
|
10
|
-
const PROVIDER = ProviderID.make("openai");
|
|
8
|
+
const route = MediaProtocol.identity({ id: "openai-speech", name: "OpenAI Speech", provider: "openai" });
|
|
11
9
|
export const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
12
10
|
export const PATH = "/audio/speech";
|
|
13
11
|
/** `pcm` is raw 24 kHz, 16-bit signed little-endian mono samples without a header. */
|
|
@@ -26,13 +24,16 @@ const SpeechStreamEvent = Schema.Union([
|
|
|
26
24
|
})),
|
|
27
25
|
}),
|
|
28
26
|
]);
|
|
29
|
-
const decodeEvent =
|
|
27
|
+
const decodeEvent = route.decodeFrame(SpeechStreamEvent);
|
|
30
28
|
// ---------------------------------------------------------------------------
|
|
31
29
|
// 5. Request body construction
|
|
32
30
|
// ---------------------------------------------------------------------------
|
|
33
31
|
// `sse` is not supported for `tts-1` or `tts-1-hd`; those models stream the raw audio body instead.
|
|
34
32
|
const supportsSse = (model) => !/^tts-1(-hd)?(-|$)/.test(model);
|
|
35
33
|
const fromRequest = Effect.fn("OpenAISpeech.fromRequest")(function* (request) {
|
|
34
|
+
// Not in `unsupported`: that list would also reject `timestamps: false`, which asks for nothing.
|
|
35
|
+
if (request.timestamps === true)
|
|
36
|
+
return yield* route.unsupported("media.timestamps", `${route.name} does not return timestamps`);
|
|
36
37
|
return MediaProtocol.json(mergeJsonRecords({
|
|
37
38
|
model: request.model.id,
|
|
38
39
|
input: request.text,
|
|
@@ -71,9 +72,9 @@ const onEvent = Effect.fn("OpenAISpeech.onEvent")(function* (state, frame) {
|
|
|
71
72
|
});
|
|
72
73
|
const finish = (state, context) => {
|
|
73
74
|
if (isSse(context.body) && !state.done)
|
|
74
|
-
return Effect.fail(
|
|
75
|
+
return Effect.fail(route.incomplete());
|
|
75
76
|
const format = context.request.format ?? "mp3";
|
|
76
|
-
return SpeechStream.finish(
|
|
77
|
+
return SpeechStream.finish(route, state, {
|
|
77
78
|
...(format === "pcm" ? SpeechStream.pcm("pcm_s16le", PCM_SAMPLE_RATE) : SpeechStream.container(format)),
|
|
78
79
|
usage: state.usage,
|
|
79
80
|
});
|
|
@@ -81,17 +82,15 @@ const finish = (state, context) => {
|
|
|
81
82
|
// ---------------------------------------------------------------------------
|
|
82
83
|
// 7. Protocol and route
|
|
83
84
|
// ---------------------------------------------------------------------------
|
|
84
|
-
export const protocol = MediaProtocol.stream({
|
|
85
|
-
|
|
86
|
-
name: NAME,
|
|
87
|
-
unsupported: ["language", "timestamps"],
|
|
85
|
+
export const protocol = MediaProtocol.stream(route, {
|
|
86
|
+
unsupported: ["language"],
|
|
88
87
|
body: { from: fromRequest },
|
|
89
88
|
frames: (bytes, context) => (isSse(context.body) ? Framing.sse.frame(bytes) : bytes),
|
|
90
89
|
initial: () => ({ chunks: [], done: false }),
|
|
91
90
|
step: SpeechStream.step(onEvent),
|
|
92
91
|
finish,
|
|
93
92
|
});
|
|
94
|
-
export const model = (input) => SpeechModel.fromRoute({
|
|
93
|
+
export const model = (input) => SpeechModel.fromRoute({ protocol, baseURL: DEFAULT_BASE_URL, path: PATH }, input);
|
|
95
94
|
export const OpenAISpeech = {
|
|
96
95
|
protocol,
|
|
97
96
|
model,
|
|
@@ -41,6 +41,10 @@ declare const Transcript: Schema.Struct<{
|
|
|
41
41
|
readonly input_tokens: Schema.optional<Schema.Number>;
|
|
42
42
|
readonly output_tokens: Schema.optional<Schema.Number>;
|
|
43
43
|
readonly total_tokens: Schema.optional<Schema.Number>;
|
|
44
|
+
readonly input_token_details: Schema.optional<Schema.Struct<{
|
|
45
|
+
readonly audio_tokens: Schema.optional<Schema.Number>;
|
|
46
|
+
readonly text_tokens: Schema.optional<Schema.Number>;
|
|
47
|
+
}>>;
|
|
44
48
|
}>, Schema.Struct<{
|
|
45
49
|
readonly type: Schema.Literal<"duration">;
|
|
46
50
|
readonly seconds: Schema.Number;
|