@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
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { Effect, Schema, Stream } from "effect";
|
|
2
|
+
import { classifyProviderFailure } from "../provider-error.js";
|
|
2
3
|
import { Framing } from "../route/framing.js";
|
|
3
4
|
import { MediaProtocol } from "../route/media-protocol.js";
|
|
4
5
|
import { MediaRoute } from "../route/media.js";
|
|
5
|
-
import {
|
|
6
|
+
import { AIError, mergeJsonRecords } from "../schema/index.js";
|
|
6
7
|
import { TranscriptionFinishEvent, TranscriptionModel, TranscriptionSegmentEvent, TranscriptionTextDeltaEvent, } from "../transcription.js";
|
|
7
8
|
import { mediaTypeExtension } from "../utils/media-type.js";
|
|
8
9
|
import { ProviderShared } from "./shared.js";
|
|
9
10
|
import { MediaInput } from "./utils/media-input.js";
|
|
10
|
-
const
|
|
11
|
-
const NAME = "OpenAI Transcription";
|
|
12
|
-
const PROVIDER = ProviderID.make("openai");
|
|
11
|
+
const route = MediaProtocol.identity({ id: "openai-transcription", name: "OpenAI Transcription", provider: "openai" });
|
|
13
12
|
export const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
14
13
|
export const PATH = "/audio/transcriptions";
|
|
15
14
|
// ---------------------------------------------------------------------------
|
|
@@ -27,6 +26,7 @@ const Usage = Schema.Union([
|
|
|
27
26
|
input_tokens: Schema.optional(Schema.Number),
|
|
28
27
|
output_tokens: Schema.optional(Schema.Number),
|
|
29
28
|
total_tokens: Schema.optional(Schema.Number),
|
|
29
|
+
input_token_details: Schema.optional(Schema.Struct({ audio_tokens: Schema.optional(Schema.Number), text_tokens: Schema.optional(Schema.Number) })),
|
|
30
30
|
}),
|
|
31
31
|
Schema.Struct({ type: Schema.Literal("duration"), seconds: Schema.Number }),
|
|
32
32
|
]);
|
|
@@ -39,14 +39,23 @@ const transcriptFields = {
|
|
|
39
39
|
words: Schema.optional(Schema.Array(Schema.Struct({ word: Schema.String, start: Schema.Number, end: Schema.Number }))),
|
|
40
40
|
usage: Schema.optional(Usage),
|
|
41
41
|
};
|
|
42
|
+
/** OpenAI may add stream event types; frames outside `EVENT_TYPES` are ignored. */
|
|
43
|
+
const EventType = Schema.Struct({ type: Schema.String });
|
|
42
44
|
const Event = Schema.Union([
|
|
43
45
|
Schema.Struct({ type: Schema.Literal("transcript.text.delta"), delta: Schema.String }),
|
|
44
46
|
Schema.Struct({ type: Schema.Literal("transcript.text.segment"), ...Segment.fields }),
|
|
45
47
|
Schema.Struct({ type: Schema.Literal("transcript.text.done"), ...transcriptFields }),
|
|
48
|
+
Schema.Struct({
|
|
49
|
+
type: Schema.Literal("error"),
|
|
50
|
+
message: Schema.optional(Schema.String),
|
|
51
|
+
error: Schema.optional(Schema.Struct({ message: Schema.optional(Schema.String) })),
|
|
52
|
+
}),
|
|
46
53
|
]);
|
|
54
|
+
const EVENT_TYPES = new Set(["transcript.text.delta", "transcript.text.segment", "transcript.text.done", "error"]);
|
|
47
55
|
const Transcript = Schema.Struct(transcriptFields);
|
|
48
|
-
const
|
|
49
|
-
const
|
|
56
|
+
const decodeEventType = route.decodeFrame(EventType);
|
|
57
|
+
const decodeEvent = route.decodeFrame(Event);
|
|
58
|
+
const decodeTranscript = route.decodeFrame(Transcript);
|
|
50
59
|
const TRANSCRIBE = { stream: true, timestamps: [], diarize: false, languageField: "language" };
|
|
51
60
|
const capabilities = (model) => {
|
|
52
61
|
if (model.startsWith("whisper"))
|
|
@@ -58,20 +67,19 @@ const capabilities = (model) => {
|
|
|
58
67
|
return { ...TRANSCRIBE, languageField: "languages" };
|
|
59
68
|
return TRANSCRIBE;
|
|
60
69
|
};
|
|
61
|
-
|
|
70
|
+
/** whisper-1 ignores `stream`, so its `stream` mode sends a plain request and emits only `finish`. */
|
|
71
|
+
const streamsEvents = (request) => request.mode === "stream" && capabilities(request.model.id).stream;
|
|
62
72
|
const validate = (request, model) => {
|
|
63
73
|
const id = request.model.id;
|
|
64
|
-
if (request.mode === "stream" && !model.stream)
|
|
65
|
-
return unsupported("media.stream", `${id} does not stream; use Transcription.generate`);
|
|
66
74
|
if (request.diarize === true && !model.diarize)
|
|
67
|
-
return unsupported("media.diarize", `${id} does not diarize; use gpt-4o-transcribe-diarize`);
|
|
75
|
+
return Effect.fail(route.unsupported("media.diarize", `${id} does not diarize; use gpt-4o-transcribe-diarize`));
|
|
68
76
|
if (request.prompt !== undefined && model.diarize)
|
|
69
|
-
return unsupported("media.prompt", `${id} does not accept a prompt`);
|
|
77
|
+
return Effect.fail(route.unsupported("media.prompt", `${id} does not accept a prompt`));
|
|
70
78
|
if (request.timestamps === undefined ||
|
|
71
79
|
request.timestamps === "none" ||
|
|
72
80
|
model.timestamps.includes(request.timestamps))
|
|
73
81
|
return Effect.void;
|
|
74
|
-
return unsupported("media.timestamps", `${id} does not return ${request.timestamps} timestamps`);
|
|
82
|
+
return Effect.fail(route.unsupported("media.timestamps", `${id} does not return ${request.timestamps} timestamps`));
|
|
75
83
|
};
|
|
76
84
|
const RESERVED_FORM_FIELDS = new Set([
|
|
77
85
|
"file",
|
|
@@ -82,26 +90,22 @@ const RESERVED_FORM_FIELDS = new Set([
|
|
|
82
90
|
"timestamp_granularities",
|
|
83
91
|
"stream",
|
|
84
92
|
]);
|
|
85
|
-
const appendField = (form, key, value) => {
|
|
86
|
-
if (Array.isArray(value))
|
|
87
|
-
return value.forEach((item) => form.append(`${key}[]`, String(item)));
|
|
88
|
-
form.append(key, typeof value === "object" && value !== null ? ProviderShared.encodeJson(value) : String(value));
|
|
89
|
-
};
|
|
90
93
|
const fromRequest = Effect.fn("OpenAITranscription.fromRequest")(function* (request) {
|
|
91
94
|
const model = capabilities(request.model.id);
|
|
92
95
|
yield* validate(request, model);
|
|
93
96
|
// The API detects the audio format from the upload's filename extension.
|
|
94
97
|
const extension = mediaTypeExtension(request.audio.mediaType);
|
|
95
98
|
if (extension === undefined)
|
|
96
|
-
return yield* ProviderShared.invalidRequest(`${
|
|
97
|
-
const audio = yield* MediaInput.inlineBytes(
|
|
99
|
+
return yield* ProviderShared.invalidRequest(`${route.name} cannot name a ${request.audio.mediaType} upload; send mp3, mp4, m4a, wav, webm, ogg, or flac audio`);
|
|
100
|
+
const audio = yield* MediaInput.inlineBytes(route.id, request.audio);
|
|
98
101
|
const responseFormat = model.diarize
|
|
99
102
|
? "diarized_json"
|
|
100
103
|
: request.timestamps === undefined || request.timestamps === "none"
|
|
101
104
|
? undefined
|
|
102
105
|
: "verbose_json";
|
|
103
|
-
const
|
|
104
|
-
|
|
106
|
+
const form = new FormData();
|
|
107
|
+
form.append("file", MediaInput.blob(audio, request.audio.mediaType), `audio.${extension}`);
|
|
108
|
+
MediaInput.appendFields(form, {
|
|
105
109
|
model: request.model.id,
|
|
106
110
|
language: model.languageField === "language" ? request.language : undefined,
|
|
107
111
|
languages: model.languageField === "languages" && request.language !== undefined ? [request.language] : undefined,
|
|
@@ -110,11 +114,12 @@ const fromRequest = Effect.fn("OpenAITranscription.fromRequest")(function* (requ
|
|
|
110
114
|
timestamp_granularities: responseFormat === "verbose_json" ? [request.timestamps] : undefined,
|
|
111
115
|
// Diarizing audio longer than 30 seconds requires a chunking strategy.
|
|
112
116
|
chunking_strategy: model.diarize ? "auto" : undefined,
|
|
113
|
-
stream: request
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
stream: streamsEvents(request) ? true : undefined,
|
|
118
|
+
}, {
|
|
119
|
+
overlay: mergeJsonRecords(request.providerOptions, request.http?.body),
|
|
120
|
+
reserved: RESERVED_FORM_FIELDS,
|
|
121
|
+
repeatArrays: true,
|
|
122
|
+
});
|
|
118
123
|
return MediaProtocol.multipart(form);
|
|
119
124
|
});
|
|
120
125
|
// ---------------------------------------------------------------------------
|
|
@@ -127,7 +132,16 @@ const segment = (value) => ({
|
|
|
127
132
|
speaker: value.speaker,
|
|
128
133
|
});
|
|
129
134
|
const onEvent = Effect.fn("OpenAITranscription.onEvent")(function* (state, frame) {
|
|
135
|
+
if (!EVENT_TYPES.has((yield* decodeEventType(frame)).type))
|
|
136
|
+
return [state, []];
|
|
130
137
|
const event = yield* decodeEvent(frame);
|
|
138
|
+
if (event.type === "error")
|
|
139
|
+
return yield* new AIError({
|
|
140
|
+
reason: classifyProviderFailure({
|
|
141
|
+
message: `${route.name} stream failed: ${event.message ?? event.error?.message ?? "unknown error"}`,
|
|
142
|
+
rawBody: frame,
|
|
143
|
+
}),
|
|
144
|
+
});
|
|
131
145
|
if (event.type === "transcript.text.done")
|
|
132
146
|
return [{ ...state, transcript: event }, []];
|
|
133
147
|
if (event.type === "transcript.text.delta")
|
|
@@ -155,7 +169,7 @@ const usage = (value) => {
|
|
|
155
169
|
const finish = (state) => {
|
|
156
170
|
const transcript = state.transcript;
|
|
157
171
|
if (transcript === undefined)
|
|
158
|
-
return Effect.fail(
|
|
172
|
+
return Effect.fail(route.incomplete());
|
|
159
173
|
const segments = transcript.segments?.map(segment) ?? state.segments;
|
|
160
174
|
return Effect.succeed([
|
|
161
175
|
TranscriptionFinishEvent.make({
|
|
@@ -171,19 +185,17 @@ const finish = (state) => {
|
|
|
171
185
|
// ---------------------------------------------------------------------------
|
|
172
186
|
// 7. Protocol and route
|
|
173
187
|
// ---------------------------------------------------------------------------
|
|
174
|
-
export const protocol = MediaProtocol.stream({
|
|
175
|
-
id: ADAPTER,
|
|
176
|
-
name: NAME,
|
|
188
|
+
export const protocol = MediaProtocol.stream(route, {
|
|
177
189
|
unsupported: ["speakers"],
|
|
178
190
|
body: { from: fromRequest },
|
|
179
|
-
frames: (bytes, context) => context.request
|
|
191
|
+
frames: (bytes, context) => streamsEvents(context.request)
|
|
180
192
|
? Framing.sse.frame(bytes)
|
|
181
193
|
: Framing.document.frame(bytes).pipe(Stream.map((document) => ({ document }))),
|
|
182
194
|
initial: () => ({ segments: [] }),
|
|
183
195
|
step,
|
|
184
196
|
finish,
|
|
185
197
|
});
|
|
186
|
-
export const model = (input) => TranscriptionModel.fromRoute({
|
|
198
|
+
export const model = (input) => TranscriptionModel.fromRoute({ protocol, baseURL: DEFAULT_BASE_URL, path: PATH }, input);
|
|
187
199
|
export const OpenAITranscription = {
|
|
188
200
|
protocol,
|
|
189
201
|
model,
|
|
@@ -3,11 +3,9 @@ import { ImageModel, ImageResponse } from "../image.js";
|
|
|
3
3
|
import { Media } from "../media.js";
|
|
4
4
|
import { MediaProtocol } from "../route/media-protocol.js";
|
|
5
5
|
import { MediaRoute } from "../route/media.js";
|
|
6
|
-
import {
|
|
6
|
+
import { mergeJsonRecords } from "../schema/index.js";
|
|
7
7
|
import { ProviderShared, optionalNull } from "./shared.js";
|
|
8
|
-
const
|
|
9
|
-
const NAME = "Replicate";
|
|
10
|
-
const PROVIDER = ProviderID.make("replicate");
|
|
8
|
+
const route = MediaProtocol.identity({ id: "replicate-images", name: "Replicate", provider: "replicate" });
|
|
11
9
|
export const DEFAULT_BASE_URL = "https://api.replicate.com";
|
|
12
10
|
const OUTPUT_RETENTION = Duration.hours(1);
|
|
13
11
|
const MAX_DATA_URL_BYTES = 256 * 1024;
|
|
@@ -50,8 +48,8 @@ const inlineSize = (source) => {
|
|
|
50
48
|
};
|
|
51
49
|
const fileInput = (asset) => {
|
|
52
50
|
if (inlineSize(asset.source) > MAX_DATA_URL_BYTES)
|
|
53
|
-
return Effect.fail(ProviderShared.invalidRequest(`${
|
|
54
|
-
return ProviderShared.mediaReference(asset, undefined,
|
|
51
|
+
return Effect.fail(ProviderShared.invalidRequest(`${route.name} data URL inputs are limited to 256 KB; pass a larger file by https URL`));
|
|
52
|
+
return ProviderShared.mediaReference(asset, undefined, route.name).pipe(Effect.map((reference) => reference.value));
|
|
55
53
|
};
|
|
56
54
|
const inputValue = (value) => {
|
|
57
55
|
if (value instanceof Media.Asset)
|
|
@@ -69,7 +67,7 @@ const fromRequest = Effect.fn("ReplicateImages.fromRequest")(function* (request)
|
|
|
69
67
|
// ---------------------------------------------------------------------------
|
|
70
68
|
// 6. Response decoding
|
|
71
69
|
// ---------------------------------------------------------------------------
|
|
72
|
-
const decodePrediction =
|
|
70
|
+
const decodePrediction = route.decodeJson(Prediction);
|
|
73
71
|
const decodeStart = Effect.fn("ReplicateImages.decodeStart")(function* (response) {
|
|
74
72
|
const output = yield* decodePrediction(response);
|
|
75
73
|
const prediction = output.value;
|
|
@@ -88,16 +86,16 @@ const decodeResult = Effect.fn("ReplicateImages.decodeResult")(function* (respon
|
|
|
88
86
|
const prediction = output.value;
|
|
89
87
|
const status = yield* MediaProtocol.status(STATUS, prediction.status, output);
|
|
90
88
|
if (status === "failed" || status === "cancelled" || status === "expired")
|
|
91
|
-
return yield* output.ended(status, `${
|
|
89
|
+
return yield* output.ended(status, `${route.name} prediction ${context.token.id} ${prediction.status}${typeof prediction.error === "string" ? `: ${prediction.error}` : ""}`);
|
|
92
90
|
if (status !== "completed")
|
|
93
|
-
return yield* output.
|
|
91
|
+
return yield* output.pending(context.token.id);
|
|
94
92
|
if (prediction.data_removed === true)
|
|
95
|
-
return yield* output.ended("expired", `${
|
|
93
|
+
return yield* output.ended("expired", `${route.name} removed the output of prediction ${context.token.id}`);
|
|
96
94
|
if (!isOutput(prediction.output))
|
|
97
|
-
return yield* output.invalid(`${
|
|
95
|
+
return yield* output.invalid(`${route.name} prediction ${context.token.id} returned output that is not image URLs`);
|
|
98
96
|
const urls = typeof prediction.output === "string" ? [prediction.output] : prediction.output;
|
|
99
97
|
if (urls.length === 0)
|
|
100
|
-
return yield* output.invalid(`${
|
|
98
|
+
return yield* output.invalid(`${route.name} prediction ${context.token.id} returned no images`);
|
|
101
99
|
const predictTime = prediction.metrics?.predict_time ?? undefined;
|
|
102
100
|
const completedAt = prediction.completed_at ?? undefined;
|
|
103
101
|
const expiresAt = completedAt === undefined ? undefined : Date.parse(completedAt) + Duration.toMillis(OUTPUT_RETENTION);
|
|
@@ -110,9 +108,7 @@ const decodeResult = Effect.fn("ReplicateImages.decodeResult")(function* (respon
|
|
|
110
108
|
// ---------------------------------------------------------------------------
|
|
111
109
|
// 7. Protocol and route
|
|
112
110
|
// ---------------------------------------------------------------------------
|
|
113
|
-
export const protocol = MediaProtocol.queued({
|
|
114
|
-
id: ADAPTER,
|
|
115
|
-
name: NAME,
|
|
111
|
+
export const protocol = MediaProtocol.queued(route, {
|
|
116
112
|
token: Token,
|
|
117
113
|
unsupported: ["images", "mask", "n", "size", "aspectRatio", "seed", "format"],
|
|
118
114
|
start: { body: { from: fromRequest }, decode: decodeStart },
|
|
@@ -121,8 +117,6 @@ export const protocol = MediaProtocol.queued({
|
|
|
121
117
|
cancel: { method: "POST", path: (token) => token.cancelURL },
|
|
122
118
|
});
|
|
123
119
|
export const model = (input) => ImageModel.fromRoute({
|
|
124
|
-
id: ADAPTER,
|
|
125
|
-
provider: PROVIDER,
|
|
126
120
|
protocol,
|
|
127
121
|
baseURL: DEFAULT_BASE_URL,
|
|
128
122
|
path: ({ request }) => isOfficial(request.model.id) ? `/v1/models/${request.model.id}/predictions` : "/v1/predictions",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { MediaProtocol } from "../route/media-protocol.js";
|
|
3
3
|
import { MediaRoute } from "../route/media.js";
|
|
4
|
+
import { type OpenString } from "../schema/index.js";
|
|
4
5
|
import { VideoModel, VideoResponse, type VideoRequestFor } from "../video.js";
|
|
5
6
|
export declare const DEFAULT_BASE_URL = "https://api.dev.runwayml.com/v1";
|
|
6
7
|
/** Every Runway request must pin the API version. */
|
|
@@ -9,7 +10,6 @@ export declare const TEXT_TO_VIDEO_PATH = "/text_to_video";
|
|
|
9
10
|
export declare const IMAGE_TO_VIDEO_PATH = "/image_to_video";
|
|
10
11
|
export declare const VIDEO_TO_VIDEO_PATH = "/video_to_video";
|
|
11
12
|
export declare const TASKS_PATH = "/tasks";
|
|
12
|
-
export type RunwayVideoString<Known extends string> = Known | (string & {});
|
|
13
13
|
/**
|
|
14
14
|
* Provider-native options. Common fields lower to Runway's names: `aspectRatio` → `ratio` (Runway expects pixel
|
|
15
15
|
* ratios such as `1280:720` for most models), `durationSeconds` → `duration`, `audio`, `negativePrompt`,
|
|
@@ -17,9 +17,9 @@ export type RunwayVideoString<Known extends string> = Known | (string & {});
|
|
|
17
17
|
*/
|
|
18
18
|
export type RunwayVideoOptions = {
|
|
19
19
|
readonly contentModeration?: {
|
|
20
|
-
readonly publicFigureThreshold?:
|
|
20
|
+
readonly publicFigureThreshold?: OpenString<"auto" | "low">;
|
|
21
21
|
};
|
|
22
|
-
readonly outputFormat?:
|
|
22
|
+
readonly outputFormat?: OpenString<"mp4" | "prores" | "png_sequence">;
|
|
23
23
|
} & Record<string, unknown>;
|
|
24
24
|
export type Request = VideoRequestFor<RunwayVideoOptions>;
|
|
25
25
|
export declare const Token: Schema.Struct<{
|
|
@@ -2,12 +2,10 @@ import { Duration, Effect, Schema } from "effect";
|
|
|
2
2
|
import { Media } from "../media.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 { VideoModel, VideoResponse } from "../video.js";
|
|
7
7
|
import { ProviderShared, optionalArray, optionalNull } from "./shared.js";
|
|
8
|
-
const
|
|
9
|
-
const NAME = "Runway";
|
|
10
|
-
const PROVIDER = ProviderID.make("runway");
|
|
8
|
+
const route = MediaProtocol.identity({ id: "runway-video", name: "Runway", provider: "runway" });
|
|
11
9
|
export const DEFAULT_BASE_URL = "https://api.dev.runwayml.com/v1";
|
|
12
10
|
/** Every Runway request must pin the API version. */
|
|
13
11
|
export const API_VERSION = "2024-11-06";
|
|
@@ -44,7 +42,7 @@ const STATUS = {
|
|
|
44
42
|
// 5. Request body construction
|
|
45
43
|
// ---------------------------------------------------------------------------
|
|
46
44
|
// Runway accepts HTTPS URLs, `runway://` upload URIs, and data URIs, all as one string.
|
|
47
|
-
const mediaUri = (asset) => ProviderShared.mediaReference(asset,
|
|
45
|
+
const mediaUri = (asset) => ProviderShared.mediaReference(asset, route.provider, route.name).pipe(Effect.map((reference) => reference.value));
|
|
48
46
|
const fromRequest = Effect.fn("RunwayVideo.fromRequest")(function* (request) {
|
|
49
47
|
const first = request.frames?.first === undefined ? undefined : yield* mediaUri(request.frames.first);
|
|
50
48
|
const last = request.frames?.last === undefined ? undefined : yield* mediaUri(request.frames.last);
|
|
@@ -71,11 +69,11 @@ const fromRequest = Effect.fn("RunwayVideo.fromRequest")(function* (request) {
|
|
|
71
69
|
// ---------------------------------------------------------------------------
|
|
72
70
|
// 6. Response decoding
|
|
73
71
|
// ---------------------------------------------------------------------------
|
|
74
|
-
const decodeStart =
|
|
72
|
+
const decodeStart = route.decodeStarted(StartResponse, (value) => ({
|
|
75
73
|
token: { taskID: value.id },
|
|
76
74
|
snapshot: { id: value.id, status: "queued" },
|
|
77
75
|
}));
|
|
78
|
-
const decodeTask =
|
|
76
|
+
const decodeTask = route.decodeJson(Task);
|
|
79
77
|
const decodeStatus = Effect.fn("RunwayVideo.decodeStatus")(function* (response, context) {
|
|
80
78
|
const output = yield* decodeTask(response);
|
|
81
79
|
const status = yield* MediaProtocol.status(STATUS, output.value.status, output);
|
|
@@ -87,19 +85,19 @@ const decodeResult = Effect.fn("RunwayVideo.decodeResult")(function* (response,
|
|
|
87
85
|
const status = yield* MediaProtocol.status(STATUS, task.status, output);
|
|
88
86
|
if (status === "failed") {
|
|
89
87
|
const code = task.failureCode ?? undefined;
|
|
90
|
-
const message = `${
|
|
88
|
+
const message = `${route.name} task failed${code === undefined ? "" : ` (${code})`}${task.failure ? `: ${task.failure}` : ""}`;
|
|
91
89
|
// Runway failure codes are dotted paths; every moderation outcome carries a SAFETY segment.
|
|
92
90
|
if (code !== undefined && /(^|\.)SAFETY(\.|$)/.test(code))
|
|
93
91
|
return yield* output.contentPolicy(message);
|
|
94
92
|
return yield* output.ended("failed", message);
|
|
95
93
|
}
|
|
96
94
|
if (status === "cancelled")
|
|
97
|
-
return yield* output.ended("cancelled", `${
|
|
95
|
+
return yield* output.ended("cancelled", `${route.name} task ${context.token.taskID} was cancelled`);
|
|
98
96
|
if (status !== "completed")
|
|
99
|
-
return yield* output.
|
|
97
|
+
return yield* output.pending(context.token.taskID);
|
|
100
98
|
const urls = task.output ?? [];
|
|
101
99
|
if (urls.length === 0)
|
|
102
|
-
return yield* output.invalid(`${
|
|
100
|
+
return yield* output.invalid(`${route.name} task succeeded without any output`);
|
|
103
101
|
return new VideoResponse({
|
|
104
102
|
videos: yield* Effect.forEach(urls, (url) => MediaProtocol.expiringUrl(url, OUTPUT_RETENTION, { mediaType: "video/mp4" })),
|
|
105
103
|
usage: task.cost === undefined ? undefined : { type: "credits", credits: task.cost.credits },
|
|
@@ -115,15 +113,13 @@ const decodeResult = Effect.fn("RunwayVideo.decodeResult")(function* (response,
|
|
|
115
113
|
// 7. Protocol and route
|
|
116
114
|
// ---------------------------------------------------------------------------
|
|
117
115
|
const taskPath = (token) => `${TASKS_PATH}/${token.taskID}`;
|
|
118
|
-
export const protocol = MediaProtocol.queued({
|
|
119
|
-
id: ADAPTER,
|
|
120
|
-
name: NAME,
|
|
116
|
+
export const protocol = MediaProtocol.queued(route, {
|
|
121
117
|
token: Token,
|
|
122
118
|
unsupported: ["n"],
|
|
123
119
|
start: { body: { from: fromRequest }, decode: decodeStart },
|
|
124
120
|
status: { path: taskPath, decode: decodeStatus },
|
|
125
121
|
result: { path: taskPath, decode: decodeResult },
|
|
126
|
-
cancel: { method: "DELETE", path: taskPath },
|
|
122
|
+
cancel: { method: "DELETE", path: taskPath, activeOnly: true },
|
|
127
123
|
});
|
|
128
124
|
const startPath = (request) => {
|
|
129
125
|
if (request.video !== undefined)
|
|
@@ -133,8 +129,6 @@ const startPath = (request) => {
|
|
|
133
129
|
return TEXT_TO_VIDEO_PATH;
|
|
134
130
|
};
|
|
135
131
|
export const model = (input) => VideoModel.fromRoute({
|
|
136
|
-
id: ADAPTER,
|
|
137
|
-
provider: PROVIDER,
|
|
138
132
|
protocol,
|
|
139
133
|
baseURL: DEFAULT_BASE_URL,
|
|
140
134
|
headers: { "X-Runway-Version": API_VERSION },
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { Tool } from "@opencode/schema/tool";
|
|
2
|
-
import { Effect, Schema
|
|
2
|
+
import { Effect, Schema } from "effect";
|
|
3
3
|
import { Headers, HttpClientRequest } from "effect/unstable/http";
|
|
4
4
|
import { Media } from "../media.js";
|
|
5
|
-
import { AIError, LLMRequest, Message, ToolDefinition, type ContentPart, type MediaPart, type ProviderID, type ToolEntry, type ToolResultPart } from "../schema/index.js";
|
|
5
|
+
import { AIError, LLMRequest, Message, ToolDefinition, type ContentPart, type MediaPart, type OpenString, type ProviderID, type ToolEntry, type ToolResultPart } from "../schema/index.js";
|
|
6
|
+
import { Json, decodeJson, encodeJson } from "../utils/json.js";
|
|
6
7
|
import { isRecord } from "../utils/record.js";
|
|
7
|
-
export { isRecord };
|
|
8
|
-
export declare const Json: Schema.fromJsonString<Schema.Unknown>;
|
|
9
|
-
export declare const decodeJson: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => unknown;
|
|
10
|
-
export declare const encodeJson: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => string;
|
|
8
|
+
export { Json, decodeJson, encodeJson, isRecord };
|
|
11
9
|
export declare const JsonObject: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
12
10
|
export declare const optionalArray: <const S extends Schema.Top>(schema: S) => Schema.optional<Schema.$Array<S>>;
|
|
13
11
|
export declare const optionalNull: <const S extends Schema.Top>(schema: S) => Schema.optional<Schema.NullOr<S>>;
|
|
14
12
|
/** Optional field whose malformed value decodes to `undefined` instead of failing the enclosing struct. */
|
|
15
13
|
export declare const lenient: <const S extends Schema.Top>(schema: S) => Schema.optionalKey<Schema.middlewareDecoding<Schema.UndefinedOr<S>, S["DecodingServices"]>>;
|
|
16
14
|
/** Provider-defined string enum: known values for autocomplete, any string accepted at runtime. */
|
|
17
|
-
export declare const knownString: <Known extends string>() => Schema.declare<
|
|
15
|
+
export declare const knownString: <Known extends string>() => Schema.declare<OpenString<Known>, OpenString<Known>>;
|
|
18
16
|
export declare const OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH = 64;
|
|
19
17
|
export declare const promptCacheKey: (request: LLMRequest) => string | undefined;
|
|
20
18
|
/**
|
|
@@ -60,6 +58,12 @@ export declare const subtractTokens: (total: number | undefined, subtrahend: num
|
|
|
60
58
|
* (e.g. Anthropic, whose `input_tokens` is already non-cached only).
|
|
61
59
|
*/
|
|
62
60
|
export declare const sumTokens: (...values: ReadonlyArray<number | undefined>) => number | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Caps an explicit thinking budget at half the output limit. Thinking counts against the output limit, so a budget
|
|
63
|
+
* near it leaves the answer, a tool call, or a summary without room. Smaller budgets, special values such as `-1` and
|
|
64
|
+
* `0`, and requests without an output limit pass through unchanged.
|
|
65
|
+
*/
|
|
66
|
+
export declare const fitThinkingBudget: (budget: number, maxTokens: number | undefined, minimum?: number) => number;
|
|
63
67
|
export declare const eventError: (route: string, message: string, body?: string, cause?: unknown) => AIError;
|
|
64
68
|
export declare const parseJson: (route: string, input: string, message: string) => Effect.Effect<unknown, AIError, never>;
|
|
65
69
|
/**
|
|
@@ -131,18 +135,8 @@ export declare const mediaReference: (asset: Media.Asset, provider: ProviderID |
|
|
|
131
135
|
* `uri`; the declared `mime` wins over any data-URL prefix so tool authors control the type the model sees.
|
|
132
136
|
*/
|
|
133
137
|
export declare const toolFileMedia: (item: Tool.FileContent) => MediaPart;
|
|
134
|
-
export declare const trimBaseUrl: (value: string) => string;
|
|
135
138
|
export declare const toolResultText: (part: ToolResultPart) => string;
|
|
136
139
|
export declare const errorText: (error: unknown) => string;
|
|
137
|
-
/**
|
|
138
|
-
* `framing` step for Server-Sent Events. Decodes UTF-8, runs the SSE channel
|
|
139
|
-
* decoder, optionally filters named events, and drops empty events and known
|
|
140
|
-
* keepalives that proxies send as data. `[DONE]` is dropped by default or
|
|
141
|
-
* retained for protocols that use it as their stream boundary. Retry control events are ignored without
|
|
142
|
-
* interrupting the stream. Decoder failures become provider output errors so
|
|
143
|
-
* the public error channel stays `AIError`.
|
|
144
|
-
*/
|
|
145
|
-
export declare const sseFraming: (bytes: Stream.Stream<Uint8Array, AIError>, events?: ReadonlySet<string>, includeDone?: boolean) => Stream.Stream<string, AIError>;
|
|
146
140
|
/**
|
|
147
141
|
* Canonical invalid-request constructor shared by protocol lowering.
|
|
148
142
|
*/
|
package/dist/protocols/shared.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { Tool } from "@opencode/schema/tool";
|
|
2
|
-
import { Effect, Option, Schema
|
|
3
|
-
import * as Sse from "effect/unstable/encoding/Sse";
|
|
2
|
+
import { Effect, Option, Schema } from "effect";
|
|
4
3
|
import { Headers, HttpClientRequest } from "effect/unstable/http";
|
|
5
4
|
import { Media } from "../media.js";
|
|
6
5
|
import { InvalidProviderOutputError, InvalidRequestError, UnsupportedOperationError, AIError, LLMRequest, Message, ToolDefinition, } from "../schema/index.js";
|
|
6
|
+
import { Json, decodeJson, encodeJson } from "../utils/json.js";
|
|
7
7
|
import { isRecord } from "../utils/record.js";
|
|
8
|
-
export { isRecord };
|
|
9
|
-
export const Json = Schema.fromJsonString(Schema.Unknown);
|
|
10
|
-
export const decodeJson = Schema.decodeUnknownSync(Json);
|
|
11
|
-
export const encodeJson = Schema.encodeSync(Json);
|
|
8
|
+
export { Json, decodeJson, encodeJson, isRecord };
|
|
12
9
|
const isJson = Schema.is(Schema.Json);
|
|
13
10
|
export const JsonObject = Schema.Record(Schema.String, Schema.Unknown);
|
|
14
11
|
export const optionalArray = (schema) => Schema.optional(Schema.Array(schema));
|
|
@@ -78,6 +75,12 @@ export const sumTokens = (...values) => {
|
|
|
78
75
|
return undefined;
|
|
79
76
|
return values.reduce((acc, value) => acc + (value ?? 0), 0);
|
|
80
77
|
};
|
|
78
|
+
/**
|
|
79
|
+
* Caps an explicit thinking budget at half the output limit. Thinking counts against the output limit, so a budget
|
|
80
|
+
* near it leaves the answer, a tool call, or a summary without room. Smaller budgets, special values such as `-1` and
|
|
81
|
+
* `0`, and requests without an output limit pass through unchanged.
|
|
82
|
+
*/
|
|
83
|
+
export const fitThinkingBudget = (budget, maxTokens, minimum = 1) => maxTokens === undefined || budget <= maxTokens / 2 ? budget : Math.max(minimum, Math.floor(maxTokens / 2));
|
|
81
84
|
export const eventError = (route, message, body, cause) => new AIError({
|
|
82
85
|
reason: new InvalidProviderOutputError({ route, message, body, cause }),
|
|
83
86
|
});
|
|
@@ -149,7 +152,8 @@ export const mediaReference = (asset, provider, label) => {
|
|
|
149
152
|
if (provider !== undefined && asset.source.type === "ref" && asset.source.provider === provider)
|
|
150
153
|
return Effect.succeed({ type: "ref", value: asset.source.id });
|
|
151
154
|
const accepted = provider === undefined ? "" : `, and ${provider} references`;
|
|
152
|
-
|
|
155
|
+
const got = asset.source.type === "ref" ? `; got ${asset.source.provider}:${asset.source.id}` : "";
|
|
156
|
+
return Effect.fail(invalidRequest(`${label} accepts inline bytes, data URLs, http(s) URLs${accepted}${got}`));
|
|
153
157
|
};
|
|
154
158
|
/**
|
|
155
159
|
* Lift a tool-result file into a `MediaPart`. Tool files carry either a data URL, an `http(s)` URL, or raw base64 in
|
|
@@ -164,7 +168,6 @@ export const toolFileMedia = (item) => {
|
|
|
164
168
|
: Media.base64(item.uri, item.mime);
|
|
165
169
|
return Message.media(asset, { filename: item.name });
|
|
166
170
|
};
|
|
167
|
-
export const trimBaseUrl = (value) => value.replace(/\/+$/, "");
|
|
168
171
|
export const toolResultText = (part) => {
|
|
169
172
|
if (part.result.type === "text")
|
|
170
173
|
return String(part.result.value);
|
|
@@ -189,38 +192,6 @@ export const errorText = (error) => {
|
|
|
189
192
|
return "undefined";
|
|
190
193
|
return "Unknown stream error";
|
|
191
194
|
};
|
|
192
|
-
/**
|
|
193
|
-
* `framing` step for Server-Sent Events. Decodes UTF-8, runs the SSE channel
|
|
194
|
-
* decoder, optionally filters named events, and drops empty events and known
|
|
195
|
-
* keepalives that proxies send as data. `[DONE]` is dropped by default or
|
|
196
|
-
* retained for protocols that use it as their stream boundary. Retry control events are ignored without
|
|
197
|
-
* interrupting the stream. Decoder failures become provider output errors so
|
|
198
|
-
* the public error channel stays `AIError`.
|
|
199
|
-
*/
|
|
200
|
-
export const sseFraming = (bytes, events, includeDone = false) => bytes.pipe(Stream.decodeText(), Stream.mapAccumEffect(() => {
|
|
201
|
-
const output = [];
|
|
202
|
-
return {
|
|
203
|
-
output,
|
|
204
|
-
parser: Sse.makeParser((event) => {
|
|
205
|
-
if (event._tag === "Event")
|
|
206
|
-
output.push(event);
|
|
207
|
-
}),
|
|
208
|
-
};
|
|
209
|
-
}, (state, chunk) => Effect.gen(function* () {
|
|
210
|
-
const error = state.parser.feed(chunk);
|
|
211
|
-
if (error)
|
|
212
|
-
return yield* eventError("sse", error.message, chunk, error);
|
|
213
|
-
return [state, state.output.splice(0)];
|
|
214
|
-
})), Stream.filter((event) => (events === undefined || events.has(event.event)) &&
|
|
215
|
-
event.data.length > 0 &&
|
|
216
|
-
// Some OpenAI-compatible proxies serialize an empty flush as a bare
|
|
217
|
-
// `data: null`, between events or after `[DONE]`. No protocol has a
|
|
218
|
-
// null event, so it carries nothing and must not abort the stream.
|
|
219
|
-
event.data !== "null" &&
|
|
220
|
-
// Vertex AI partner models (e.g. `xai/grok-4.6`) send their SSE
|
|
221
|
-
// keepalive comment as `data: : keepalive` while reasoning.
|
|
222
|
-
event.data !== ": keepalive" &&
|
|
223
|
-
(event.data !== "[DONE]" || includeDone || (events !== undefined && event.event !== "message"))), Stream.map((event) => event.data));
|
|
224
195
|
/**
|
|
225
196
|
* Canonical invalid-request constructor shared by protocol lowering.
|
|
226
197
|
*/
|
|
@@ -2,8 +2,9 @@ import { Schema } from "effect";
|
|
|
2
2
|
import { ImageModel, ImageResponse, type ImageRequestFor } from "../image.js";
|
|
3
3
|
import { MediaProtocol } from "../route/media-protocol.js";
|
|
4
4
|
import { MediaRoute } from "../route/media.js";
|
|
5
|
+
import { type OpenString } from "../schema/index.js";
|
|
5
6
|
export declare const DEFAULT_BASE_URL = "https://api.stability.ai";
|
|
6
|
-
export type StabilityStylePreset = "enhance" | "anime" | "photographic" | "digital-art" | "comic-book" | "fantasy-art" | "line-art" | "analog-film" | "neon-punk" | "isometric" | "low-poly" | "origami" | "modeling-compound" | "cinematic" | "3d-model" | "pixel-art" | "tile-texture"
|
|
7
|
+
export type StabilityStylePreset = OpenString<"enhance" | "anime" | "photographic" | "digital-art" | "comic-book" | "fantasy-art" | "line-art" | "analog-film" | "neon-punk" | "isometric" | "low-poly" | "origami" | "modeling-compound" | "cinematic" | "3d-model" | "pixel-art" | "tile-texture">;
|
|
7
8
|
export type StabilityImageOptions = {
|
|
8
9
|
readonly negative_prompt?: string;
|
|
9
10
|
readonly style_preset?: StabilityStylePreset;
|