@opencode/ai 2.0.14 → 2.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +399 -56
- package/dist/experimental/evaluation-client.d.ts +1 -1
- package/dist/experimental/evaluation-client.js +39 -3
- package/dist/experimental/evaluation.d.ts +4 -4
- package/dist/experimental/evaluation.js +2 -2
- package/dist/experimental/system-one.d.ts +3 -3
- package/dist/experimental/system-one.js +40 -51
- package/dist/generation.d.ts +83 -0
- package/dist/generation.js +113 -0
- package/dist/image-client.d.ts +16 -7
- package/dist/image-client.js +29 -13
- package/dist/image.d.ts +1410 -81
- package/dist/image.js +97 -67
- package/dist/index.d.ts +17 -2
- package/dist/index.js +12 -1
- package/dist/llm.d.ts +9 -1
- package/dist/media-model.d.ts +44 -0
- package/dist/media-model.js +49 -0
- package/dist/media.d.ts +213 -0
- package/dist/media.js +227 -0
- package/dist/promise.d.ts +974 -0
- package/dist/promise.js +81 -0
- package/dist/protocols/alibaba-chat.d.ts +12 -0
- package/dist/protocols/alibaba-responses.d.ts +2 -2
- package/dist/protocols/anthropic-messages.js +8 -19
- package/dist/protocols/assemblyai-transcription.d.ts +40 -0
- package/dist/protocols/assemblyai-transcription.js +138 -0
- package/dist/protocols/bedrock-converse.d.ts +4 -4
- package/dist/protocols/bedrock-converse.js +6 -17
- 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 +30 -9
- package/dist/protocols/gemini.js +45 -35
- package/dist/protocols/google-images.d.ts +9 -21
- package/dist/protocols/google-images.js +158 -133
- 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.d.ts +7 -12
- package/dist/protocols/meta-images.js +85 -66
- package/dist/protocols/meta-responses.d.ts +4 -4
- package/dist/protocols/meta-responses.js +1 -1
- package/dist/protocols/mistral-chat.js +7 -6
- package/dist/protocols/open-responses.d.ts +17 -9
- package/dist/protocols/open-responses.js +24 -14
- package/dist/protocols/openai-chat.d.ts +118 -1
- package/dist/protocols/openai-chat.js +125 -44
- 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 +128 -18
- package/dist/protocols/openai-images.js +177 -154
- 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 +27 -17
- package/dist/protocols/shared.js +52 -35
- package/dist/protocols/stability-images.d.ts +38 -0
- package/dist/protocols/stability-images.js +148 -0
- package/dist/protocols/utils/bedrock-media.d.ts +2 -3
- package/dist/protocols/utils/bedrock-media.js +4 -4
- 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 +18 -0
- package/dist/protocols/utils/media-input.js +35 -0
- package/dist/protocols/utils/responses-compaction.js +6 -5
- 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/utils/tool-stream.d.ts +27 -3
- package/dist/protocols/xai-images.d.ts +9 -15
- package/dist/protocols/xai-images.js +75 -84
- 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/protocols/zai-images.d.ts +9 -13
- package/dist/protocols/zai-images.js +59 -57
- 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/amazon-bedrock.d.ts +2 -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/cerebras.js +6 -1
- 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/deepinfra.js +6 -1
- 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 +10 -3
- package/dist/providers/google.d.ts +25 -3
- package/dist/providers/google.js +11 -2
- package/dist/providers/groq.d.ts +24 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.js +10 -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 +67 -0
- package/dist/providers/openrouter.js +13 -1
- 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/vercel-ai-gateway.d.ts +41 -0
- package/dist/providers/vercel-ai-gateway.js +85 -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/client.d.ts +9 -1
- package/dist/route/endpoint.d.ts +10 -10
- package/dist/route/executor-service.d.ts +12 -0
- package/dist/route/executor-service.js +3 -0
- package/dist/route/executor.d.ts +4 -9
- package/dist/route/executor.js +3 -3
- package/dist/route/framing.d.ts +5 -1
- package/dist/route/framing.js +9 -0
- package/dist/route/index.d.ts +2 -0
- package/dist/route/index.js +2 -0
- package/dist/route/media-protocol.d.ts +158 -0
- package/dist/route/media-protocol.js +96 -0
- package/dist/route/media.d.ts +97 -0
- package/dist/route/media.js +236 -0
- package/dist/schema/errors.d.ts +13 -3
- package/dist/schema/errors.js +7 -0
- package/dist/schema/events.d.ts +557 -40
- package/dist/schema/events.js +35 -2
- package/dist/schema/messages.d.ts +95 -8
- package/dist/schema/messages.js +8 -6
- package/dist/schema/options.d.ts +6 -3
- package/dist/schema/options.js +6 -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/testing.d.ts +72 -8
- 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 +7 -0
- package/dist/utils/media-type.js +70 -0
- package/dist/utils/sanitize.js +3 -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 +7 -3
- package/dist/protocols/utils/gemini-tool-schema.d.ts +0 -2
- package/dist/protocols/utils/gemini-tool-schema.js +0 -103
- package/dist/protocols/utils/image-input.d.ts +0 -21
- package/dist/protocols/utils/image-input.js +0 -20
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { Effect, Schema, Stream } from "effect";
|
|
2
|
+
import { Framing } from "../route/framing.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 { TranscriptionFinishEvent, TranscriptionModel, TranscriptionSegmentEvent, TranscriptionTextDeltaEvent, } from "../transcription.js";
|
|
7
|
+
import { mediaTypeExtension } from "../utils/media-type.js";
|
|
8
|
+
import { ProviderShared } from "./shared.js";
|
|
9
|
+
import { MediaInput } from "./utils/media-input.js";
|
|
10
|
+
const ADAPTER = "openai-transcription";
|
|
11
|
+
const NAME = "OpenAI Transcription";
|
|
12
|
+
const PROVIDER = ProviderID.make("openai");
|
|
13
|
+
export const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
14
|
+
export const PATH = "/audio/transcriptions";
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// 3. Streaming event schema
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
const Segment = Schema.Struct({
|
|
19
|
+
text: Schema.String,
|
|
20
|
+
start: Schema.Number,
|
|
21
|
+
end: Schema.Number,
|
|
22
|
+
speaker: Schema.optional(Schema.String),
|
|
23
|
+
});
|
|
24
|
+
const Usage = Schema.Union([
|
|
25
|
+
Schema.Struct({
|
|
26
|
+
type: Schema.Literal("tokens"),
|
|
27
|
+
input_tokens: Schema.optional(Schema.Number),
|
|
28
|
+
output_tokens: Schema.optional(Schema.Number),
|
|
29
|
+
total_tokens: Schema.optional(Schema.Number),
|
|
30
|
+
}),
|
|
31
|
+
Schema.Struct({ type: Schema.Literal("duration"), seconds: Schema.Number }),
|
|
32
|
+
]);
|
|
33
|
+
const transcriptFields = {
|
|
34
|
+
text: Schema.String,
|
|
35
|
+
language: Schema.optional(Schema.String),
|
|
36
|
+
languages: Schema.optional(Schema.Array(Schema.Struct({ code: Schema.String }))),
|
|
37
|
+
duration: Schema.optional(Schema.Number),
|
|
38
|
+
segments: Schema.optional(Schema.Array(Segment)),
|
|
39
|
+
words: Schema.optional(Schema.Array(Schema.Struct({ word: Schema.String, start: Schema.Number, end: Schema.Number }))),
|
|
40
|
+
usage: Schema.optional(Usage),
|
|
41
|
+
};
|
|
42
|
+
const Event = Schema.Union([
|
|
43
|
+
Schema.Struct({ type: Schema.Literal("transcript.text.delta"), delta: Schema.String }),
|
|
44
|
+
Schema.Struct({ type: Schema.Literal("transcript.text.segment"), ...Segment.fields }),
|
|
45
|
+
Schema.Struct({ type: Schema.Literal("transcript.text.done"), ...transcriptFields }),
|
|
46
|
+
]);
|
|
47
|
+
const Transcript = Schema.Struct(transcriptFields);
|
|
48
|
+
const decodeEvent = MediaProtocol.decodeFrame(ADAPTER, NAME, Event);
|
|
49
|
+
const decodeTranscript = MediaProtocol.decodeFrame(ADAPTER, NAME, Transcript);
|
|
50
|
+
const TRANSCRIBE = { stream: true, timestamps: [], diarize: false, languageField: "language" };
|
|
51
|
+
const capabilities = (model) => {
|
|
52
|
+
if (model.startsWith("whisper"))
|
|
53
|
+
return { ...TRANSCRIBE, stream: false, timestamps: ["segment", "word"] };
|
|
54
|
+
if (model.includes("diarize"))
|
|
55
|
+
return { ...TRANSCRIBE, timestamps: ["segment"], diarize: true };
|
|
56
|
+
// `gpt-transcribe` replaces `language` with `languages[]` and rejects both together.
|
|
57
|
+
if (model.startsWith("gpt-transcribe"))
|
|
58
|
+
return { ...TRANSCRIBE, languageField: "languages" };
|
|
59
|
+
return TRANSCRIBE;
|
|
60
|
+
};
|
|
61
|
+
const unsupported = (operation, message) => Effect.fail(ProviderShared.unsupportedOperation({ operation, provider: PROVIDER, route: ADAPTER, message }));
|
|
62
|
+
const validate = (request, model) => {
|
|
63
|
+
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
|
+
if (request.diarize === true && !model.diarize)
|
|
67
|
+
return unsupported("media.diarize", `${id} does not diarize; use gpt-4o-transcribe-diarize`);
|
|
68
|
+
if (request.prompt !== undefined && model.diarize)
|
|
69
|
+
return unsupported("media.prompt", `${id} does not accept a prompt`);
|
|
70
|
+
if (request.timestamps === undefined ||
|
|
71
|
+
request.timestamps === "none" ||
|
|
72
|
+
model.timestamps.includes(request.timestamps))
|
|
73
|
+
return Effect.void;
|
|
74
|
+
return unsupported("media.timestamps", `${id} does not return ${request.timestamps} timestamps`);
|
|
75
|
+
};
|
|
76
|
+
const RESERVED_FORM_FIELDS = new Set([
|
|
77
|
+
"file",
|
|
78
|
+
"model",
|
|
79
|
+
"prompt",
|
|
80
|
+
"language",
|
|
81
|
+
"response_format",
|
|
82
|
+
"timestamp_granularities",
|
|
83
|
+
"stream",
|
|
84
|
+
]);
|
|
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
|
+
const fromRequest = Effect.fn("OpenAITranscription.fromRequest")(function* (request) {
|
|
91
|
+
const model = capabilities(request.model.id);
|
|
92
|
+
yield* validate(request, model);
|
|
93
|
+
// The API detects the audio format from the upload's filename extension.
|
|
94
|
+
const extension = mediaTypeExtension(request.audio.mediaType);
|
|
95
|
+
if (extension === undefined)
|
|
96
|
+
return yield* ProviderShared.invalidRequest(`${NAME} cannot name a ${request.audio.mediaType} upload; send mp3, mp4, m4a, wav, webm, ogg, or flac audio`);
|
|
97
|
+
const audio = yield* MediaInput.inlineBytes(ADAPTER, request.audio);
|
|
98
|
+
const responseFormat = model.diarize
|
|
99
|
+
? "diarized_json"
|
|
100
|
+
: request.timestamps === undefined || request.timestamps === "none"
|
|
101
|
+
? undefined
|
|
102
|
+
: "verbose_json";
|
|
103
|
+
const native = Object.entries(mergeJsonRecords(request.providerOptions, request.http?.body) ?? {}).filter(([key]) => !RESERVED_FORM_FIELDS.has(key));
|
|
104
|
+
const fields = mergeJsonRecords({
|
|
105
|
+
model: request.model.id,
|
|
106
|
+
language: model.languageField === "language" ? request.language : undefined,
|
|
107
|
+
languages: model.languageField === "languages" && request.language !== undefined ? [request.language] : undefined,
|
|
108
|
+
prompt: request.prompt,
|
|
109
|
+
response_format: responseFormat,
|
|
110
|
+
timestamp_granularities: responseFormat === "verbose_json" ? [request.timestamps] : undefined,
|
|
111
|
+
// Diarizing audio longer than 30 seconds requires a chunking strategy.
|
|
112
|
+
chunking_strategy: model.diarize ? "auto" : undefined,
|
|
113
|
+
stream: request.mode === "stream" ? true : undefined,
|
|
114
|
+
}, Object.fromEntries(native));
|
|
115
|
+
const form = new FormData();
|
|
116
|
+
form.append("file", MediaInput.blob(audio, request.audio.mediaType), `audio.${extension}`);
|
|
117
|
+
Object.entries(fields ?? {}).forEach(([key, value]) => appendField(form, key, value));
|
|
118
|
+
return MediaProtocol.multipart(form);
|
|
119
|
+
});
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// 6. Stream parsing
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
const segment = (value) => ({
|
|
124
|
+
text: value.text.trim(),
|
|
125
|
+
startSeconds: value.start,
|
|
126
|
+
endSeconds: value.end,
|
|
127
|
+
speaker: value.speaker,
|
|
128
|
+
});
|
|
129
|
+
const onEvent = Effect.fn("OpenAITranscription.onEvent")(function* (state, frame) {
|
|
130
|
+
const event = yield* decodeEvent(frame);
|
|
131
|
+
if (event.type === "transcript.text.done")
|
|
132
|
+
return [{ ...state, transcript: event }, []];
|
|
133
|
+
if (event.type === "transcript.text.delta")
|
|
134
|
+
return [state, event.delta.length === 0 ? [] : [TranscriptionTextDeltaEvent.make({ delta: event.delta })]];
|
|
135
|
+
const next = segment(event);
|
|
136
|
+
state.segments.push(next);
|
|
137
|
+
return [state, [TranscriptionSegmentEvent.make({ segment: next })]];
|
|
138
|
+
});
|
|
139
|
+
const step = (state, frame) => typeof frame === "string"
|
|
140
|
+
? onEvent(state, frame)
|
|
141
|
+
: decodeTranscript(frame.document).pipe(Effect.map((transcript) => [{ ...state, transcript }, []]));
|
|
142
|
+
const usage = (value) => {
|
|
143
|
+
if (value === undefined)
|
|
144
|
+
return undefined;
|
|
145
|
+
if (value.type === "duration")
|
|
146
|
+
return { type: "seconds", seconds: value.seconds };
|
|
147
|
+
return {
|
|
148
|
+
type: "tokens",
|
|
149
|
+
input: value.input_tokens,
|
|
150
|
+
output: value.output_tokens,
|
|
151
|
+
total: value.total_tokens,
|
|
152
|
+
details: { openai: value },
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
const finish = (state) => {
|
|
156
|
+
const transcript = state.transcript;
|
|
157
|
+
if (transcript === undefined)
|
|
158
|
+
return Effect.fail(MediaProtocol.incomplete(ADAPTER));
|
|
159
|
+
const segments = transcript.segments?.map(segment) ?? state.segments;
|
|
160
|
+
return Effect.succeed([
|
|
161
|
+
TranscriptionFinishEvent.make({
|
|
162
|
+
text: transcript.text,
|
|
163
|
+
segments: segments.length === 0 ? undefined : segments,
|
|
164
|
+
words: transcript.words?.map((word) => ({ text: word.word, startSeconds: word.start, endSeconds: word.end })),
|
|
165
|
+
language: (transcript.language ?? transcript.languages?.[0]?.code)?.toLowerCase(),
|
|
166
|
+
durationSeconds: transcript.duration,
|
|
167
|
+
usage: usage(transcript.usage),
|
|
168
|
+
}),
|
|
169
|
+
]);
|
|
170
|
+
};
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
// 7. Protocol and route
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
export const protocol = MediaProtocol.stream({
|
|
175
|
+
id: ADAPTER,
|
|
176
|
+
name: NAME,
|
|
177
|
+
unsupported: ["speakers"],
|
|
178
|
+
body: { from: fromRequest },
|
|
179
|
+
frames: (bytes, context) => context.request.mode === "stream"
|
|
180
|
+
? Framing.sse.frame(bytes)
|
|
181
|
+
: Framing.document.frame(bytes).pipe(Stream.map((document) => ({ document }))),
|
|
182
|
+
initial: () => ({ segments: [] }),
|
|
183
|
+
step,
|
|
184
|
+
finish,
|
|
185
|
+
});
|
|
186
|
+
export const model = (input) => TranscriptionModel.fromRoute({ id: ADAPTER, provider: PROVIDER, protocol, baseURL: DEFAULT_BASE_URL, path: PATH }, input);
|
|
187
|
+
export const OpenAITranscription = {
|
|
188
|
+
protocol,
|
|
189
|
+
model,
|
|
190
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { ImageModel, ImageResponse, type ImageRequestFor } from "../image.js";
|
|
3
|
+
import { MediaProtocol } from "../route/media-protocol.js";
|
|
4
|
+
import { MediaRoute } from "../route/media.js";
|
|
5
|
+
export declare const DEFAULT_BASE_URL = "https://api.replicate.com";
|
|
6
|
+
/** Model-defined `input` fields under the model's own names; `Media.Asset` values lower to URLs or data URLs. */
|
|
7
|
+
export type ReplicateImageOptions = Record<string, unknown>;
|
|
8
|
+
export type Request = ImageRequestFor<ReplicateImageOptions>;
|
|
9
|
+
export declare const Token: Schema.Struct<{
|
|
10
|
+
readonly id: Schema.String;
|
|
11
|
+
readonly getURL: Schema.String;
|
|
12
|
+
readonly cancelURL: Schema.String;
|
|
13
|
+
}>;
|
|
14
|
+
export type Token = Schema.Schema.Type<typeof Token>;
|
|
15
|
+
export declare const protocol: MediaProtocol.Queued<Request, ImageResponse, {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly getURL: string;
|
|
18
|
+
readonly cancelURL: string;
|
|
19
|
+
}>;
|
|
20
|
+
export declare const model: (input: MediaRoute.ModelInput) => ImageModel<ReplicateImageOptions>;
|
|
21
|
+
export declare const ReplicateImages: {
|
|
22
|
+
readonly protocol: MediaProtocol.Queued<Request, ImageResponse, {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly getURL: string;
|
|
25
|
+
readonly cancelURL: string;
|
|
26
|
+
}>;
|
|
27
|
+
readonly model: (input: MediaRoute.ModelInput) => ImageModel<ReplicateImageOptions>;
|
|
28
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Duration, Effect, Schema } from "effect";
|
|
2
|
+
import { ImageModel, ImageResponse } from "../image.js";
|
|
3
|
+
import { Media } from "../media.js";
|
|
4
|
+
import { MediaProtocol } from "../route/media-protocol.js";
|
|
5
|
+
import { MediaRoute } from "../route/media.js";
|
|
6
|
+
import { ProviderID, mergeJsonRecords } from "../schema/index.js";
|
|
7
|
+
import { ProviderShared, optionalNull } from "./shared.js";
|
|
8
|
+
const ADAPTER = "replicate-images";
|
|
9
|
+
const NAME = "Replicate";
|
|
10
|
+
const PROVIDER = ProviderID.make("replicate");
|
|
11
|
+
export const DEFAULT_BASE_URL = "https://api.replicate.com";
|
|
12
|
+
const OUTPUT_RETENTION = Duration.hours(1);
|
|
13
|
+
const MAX_DATA_URL_BYTES = 256 * 1024;
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// 2. Token and response schemas
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
export const Token = Schema.Struct({ id: Schema.String, getURL: Schema.String, cancelURL: Schema.String });
|
|
18
|
+
const Prediction = Schema.Struct({
|
|
19
|
+
id: Schema.String,
|
|
20
|
+
status: Schema.String,
|
|
21
|
+
output: optionalNull(Schema.Unknown),
|
|
22
|
+
error: optionalNull(Schema.Unknown),
|
|
23
|
+
data_removed: optionalNull(Schema.Boolean),
|
|
24
|
+
completed_at: optionalNull(Schema.String),
|
|
25
|
+
metrics: optionalNull(Schema.Struct({ predict_time: optionalNull(Schema.Number) })),
|
|
26
|
+
urls: Schema.Struct({ get: Schema.String, cancel: Schema.String }),
|
|
27
|
+
});
|
|
28
|
+
const Output = Schema.Union([Schema.String, Schema.Array(Schema.String)]);
|
|
29
|
+
const isOutput = Schema.is(Output);
|
|
30
|
+
const STATUS = {
|
|
31
|
+
starting: "queued",
|
|
32
|
+
processing: "running",
|
|
33
|
+
succeeded: "completed",
|
|
34
|
+
failed: "failed",
|
|
35
|
+
canceled: "cancelled",
|
|
36
|
+
// The prediction hit its `Cancel-After` deadline before it started running.
|
|
37
|
+
aborted: "expired",
|
|
38
|
+
};
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// 5. Request body construction
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Official models run by `owner/name`; anything else (`owner/name:version` or a bare version id) is a pinned version.
|
|
43
|
+
const isOfficial = (model) => /^[^/:]+\/[^/:]+$/.test(model);
|
|
44
|
+
const inlineSize = (source) => {
|
|
45
|
+
if (source.type === "bytes")
|
|
46
|
+
return source.data.byteLength;
|
|
47
|
+
if (source.type === "base64")
|
|
48
|
+
return source.data.length * 0.75;
|
|
49
|
+
return 0;
|
|
50
|
+
};
|
|
51
|
+
const fileInput = (asset) => {
|
|
52
|
+
if (inlineSize(asset.source) > MAX_DATA_URL_BYTES)
|
|
53
|
+
return Effect.fail(ProviderShared.invalidRequest(`${NAME} data URL inputs are limited to 256 KB; pass a larger file by https URL`));
|
|
54
|
+
return ProviderShared.mediaReference(asset, undefined, NAME).pipe(Effect.map((reference) => reference.value));
|
|
55
|
+
};
|
|
56
|
+
const inputValue = (value) => {
|
|
57
|
+
if (value instanceof Media.Asset)
|
|
58
|
+
return fileInput(value);
|
|
59
|
+
if (Array.isArray(value) && value.some((item) => item instanceof Media.Asset))
|
|
60
|
+
return Effect.forEach(value, (item) => (item instanceof Media.Asset ? fileInput(item) : Effect.succeed(item)));
|
|
61
|
+
return Effect.succeed(value);
|
|
62
|
+
};
|
|
63
|
+
const fromRequest = Effect.fn("ReplicateImages.fromRequest")(function* (request) {
|
|
64
|
+
const native = yield* Effect.forEach(Object.entries(request.providerOptions ?? {}), ([key, value]) => inputValue(value).pipe(Effect.map((lowered) => [key, lowered])));
|
|
65
|
+
const input = mergeJsonRecords({ prompt: request.prompt }, Object.fromEntries(native));
|
|
66
|
+
const model = request.model.id;
|
|
67
|
+
return MediaProtocol.json(mergeJsonRecords(isOfficial(model) ? { input } : { version: model, input }, request.http?.body) ?? {});
|
|
68
|
+
});
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// 6. Response decoding
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
const decodePrediction = MediaProtocol.decodeJson(ADAPTER, NAME, Prediction);
|
|
73
|
+
const decodeStart = Effect.fn("ReplicateImages.decodeStart")(function* (response) {
|
|
74
|
+
const output = yield* decodePrediction(response);
|
|
75
|
+
const prediction = output.value;
|
|
76
|
+
return {
|
|
77
|
+
token: { id: prediction.id, getURL: prediction.urls.get, cancelURL: prediction.urls.cancel },
|
|
78
|
+
// `Prefer: wait` can return an already-finished prediction, so `await` skips straight to the result.
|
|
79
|
+
snapshot: { id: prediction.id, status: yield* MediaProtocol.status(STATUS, prediction.status, output) },
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
const decodeStatus = Effect.fn("ReplicateImages.decodeStatus")(function* (response, context) {
|
|
83
|
+
const output = yield* decodePrediction(response);
|
|
84
|
+
return { id: context.token.id, status: yield* MediaProtocol.status(STATUS, output.value.status, output) };
|
|
85
|
+
});
|
|
86
|
+
const decodeResult = Effect.fn("ReplicateImages.decodeResult")(function* (response, context) {
|
|
87
|
+
const output = yield* decodePrediction(response);
|
|
88
|
+
const prediction = output.value;
|
|
89
|
+
const status = yield* MediaProtocol.status(STATUS, prediction.status, output);
|
|
90
|
+
if (status === "failed" || status === "cancelled" || status === "expired")
|
|
91
|
+
return yield* output.ended(status, `${NAME} prediction ${context.token.id} ${prediction.status}${typeof prediction.error === "string" ? `: ${prediction.error}` : ""}`);
|
|
92
|
+
if (status !== "completed")
|
|
93
|
+
return yield* output.invalid(`${NAME} prediction ${context.token.id} has not finished`);
|
|
94
|
+
if (prediction.data_removed === true)
|
|
95
|
+
return yield* output.ended("expired", `${NAME} removed the output of prediction ${context.token.id}`);
|
|
96
|
+
if (!isOutput(prediction.output))
|
|
97
|
+
return yield* output.invalid(`${NAME} prediction ${context.token.id} returned output that is not image URLs`);
|
|
98
|
+
const urls = typeof prediction.output === "string" ? [prediction.output] : prediction.output;
|
|
99
|
+
if (urls.length === 0)
|
|
100
|
+
return yield* output.invalid(`${NAME} prediction ${context.token.id} returned no images`);
|
|
101
|
+
const predictTime = prediction.metrics?.predict_time ?? undefined;
|
|
102
|
+
const completedAt = prediction.completed_at ?? undefined;
|
|
103
|
+
const expiresAt = completedAt === undefined ? undefined : Date.parse(completedAt) + Duration.toMillis(OUTPUT_RETENTION);
|
|
104
|
+
return new ImageResponse({
|
|
105
|
+
images: urls.map((url) => Media.url(url, { expiresAt })),
|
|
106
|
+
usage: predictTime === undefined ? undefined : { type: "compute", seconds: predictTime },
|
|
107
|
+
providerMetadata: { replicate: { predictionId: prediction.id } },
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// 7. Protocol and route
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
export const protocol = MediaProtocol.queued({
|
|
114
|
+
id: ADAPTER,
|
|
115
|
+
name: NAME,
|
|
116
|
+
token: Token,
|
|
117
|
+
unsupported: ["images", "mask", "n", "size", "aspectRatio", "seed", "format"],
|
|
118
|
+
start: { body: { from: fromRequest }, decode: decodeStart },
|
|
119
|
+
status: { path: (token) => token.getURL, decode: decodeStatus },
|
|
120
|
+
result: { path: (token) => token.getURL, decode: decodeResult },
|
|
121
|
+
cancel: { method: "POST", path: (token) => token.cancelURL },
|
|
122
|
+
});
|
|
123
|
+
export const model = (input) => ImageModel.fromRoute({
|
|
124
|
+
id: ADAPTER,
|
|
125
|
+
provider: PROVIDER,
|
|
126
|
+
protocol,
|
|
127
|
+
baseURL: DEFAULT_BASE_URL,
|
|
128
|
+
path: ({ request }) => isOfficial(request.model.id) ? `/v1/models/${request.model.id}/predictions` : "/v1/predictions",
|
|
129
|
+
}, input);
|
|
130
|
+
export const ReplicateImages = {
|
|
131
|
+
protocol,
|
|
132
|
+
model,
|
|
133
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
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://api.dev.runwayml.com/v1";
|
|
6
|
+
/** Every Runway request must pin the API version. */
|
|
7
|
+
export declare const API_VERSION = "2024-11-06";
|
|
8
|
+
export declare const TEXT_TO_VIDEO_PATH = "/text_to_video";
|
|
9
|
+
export declare const IMAGE_TO_VIDEO_PATH = "/image_to_video";
|
|
10
|
+
export declare const VIDEO_TO_VIDEO_PATH = "/video_to_video";
|
|
11
|
+
export declare const TASKS_PATH = "/tasks";
|
|
12
|
+
export type RunwayVideoString<Known extends string> = Known | (string & {});
|
|
13
|
+
/**
|
|
14
|
+
* Provider-native options. Common fields lower to Runway's names: `aspectRatio` → `ratio` (Runway expects pixel
|
|
15
|
+
* ratios such as `1280:720` for most models), `durationSeconds` → `duration`, `audio`, `negativePrompt`,
|
|
16
|
+
* `resolution`, `references`, and `frames` → `promptImage`.
|
|
17
|
+
*/
|
|
18
|
+
export type RunwayVideoOptions = {
|
|
19
|
+
readonly contentModeration?: {
|
|
20
|
+
readonly publicFigureThreshold?: RunwayVideoString<"auto" | "low">;
|
|
21
|
+
};
|
|
22
|
+
readonly outputFormat?: RunwayVideoString<"mp4" | "prores" | "png_sequence">;
|
|
23
|
+
} & Record<string, unknown>;
|
|
24
|
+
export type Request = VideoRequestFor<RunwayVideoOptions>;
|
|
25
|
+
export declare const Token: Schema.Struct<{
|
|
26
|
+
readonly taskID: Schema.String;
|
|
27
|
+
}>;
|
|
28
|
+
export type Token = Schema.Schema.Type<typeof Token>;
|
|
29
|
+
export declare const protocol: MediaProtocol.Queued<Request, VideoResponse, {
|
|
30
|
+
readonly taskID: string;
|
|
31
|
+
}>;
|
|
32
|
+
export declare const model: (input: MediaRoute.ModelInput) => VideoModel<RunwayVideoOptions>;
|
|
33
|
+
export declare const RunwayVideo: {
|
|
34
|
+
readonly protocol: MediaProtocol.Queued<Request, VideoResponse, {
|
|
35
|
+
readonly taskID: string;
|
|
36
|
+
}>;
|
|
37
|
+
readonly model: (input: MediaRoute.ModelInput) => VideoModel<RunwayVideoOptions>;
|
|
38
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
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, optionalNull } from "./shared.js";
|
|
8
|
+
const ADAPTER = "runway-video";
|
|
9
|
+
const NAME = "Runway";
|
|
10
|
+
const PROVIDER = ProviderID.make("runway");
|
|
11
|
+
export const DEFAULT_BASE_URL = "https://api.dev.runwayml.com/v1";
|
|
12
|
+
/** Every Runway request must pin the API version. */
|
|
13
|
+
export const API_VERSION = "2024-11-06";
|
|
14
|
+
export const TEXT_TO_VIDEO_PATH = "/text_to_video";
|
|
15
|
+
export const IMAGE_TO_VIDEO_PATH = "/image_to_video";
|
|
16
|
+
export const VIDEO_TO_VIDEO_PATH = "/video_to_video";
|
|
17
|
+
export const TASKS_PATH = "/tasks";
|
|
18
|
+
/** Output URLs are valid for 24–48 hours; the asset carries the conservative bound. */
|
|
19
|
+
const OUTPUT_RETENTION = Duration.hours(24);
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// 2. Token and response schemas
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
export const Token = Schema.Struct({ taskID: Schema.String });
|
|
24
|
+
const Cost = Schema.Struct({ credits: Schema.Number });
|
|
25
|
+
const StartResponse = Schema.Struct({ id: Schema.String });
|
|
26
|
+
const Task = Schema.Struct({
|
|
27
|
+
status: Schema.String,
|
|
28
|
+
progress: optionalNull(Schema.Number),
|
|
29
|
+
output: optionalArray(Schema.String),
|
|
30
|
+
failure: optionalNull(Schema.String),
|
|
31
|
+
failureCode: optionalNull(Schema.String),
|
|
32
|
+
cost: Schema.optional(Cost),
|
|
33
|
+
estimatedCost: Schema.optional(Cost),
|
|
34
|
+
});
|
|
35
|
+
const STATUS = {
|
|
36
|
+
PENDING: "queued",
|
|
37
|
+
THROTTLED: "queued",
|
|
38
|
+
RUNNING: "running",
|
|
39
|
+
SUCCEEDED: "completed",
|
|
40
|
+
FAILED: "failed",
|
|
41
|
+
CANCELLED: "cancelled",
|
|
42
|
+
};
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// 5. Request body construction
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Runway accepts HTTPS URLs, `runway://` upload URIs, and data URIs, all as one string.
|
|
47
|
+
const mediaUri = (asset) => ProviderShared.mediaReference(asset, PROVIDER, NAME).pipe(Effect.map((reference) => reference.value));
|
|
48
|
+
const fromRequest = Effect.fn("RunwayVideo.fromRequest")(function* (request) {
|
|
49
|
+
const first = request.frames?.first === undefined ? undefined : yield* mediaUri(request.frames.first);
|
|
50
|
+
const last = request.frames?.last === undefined ? undefined : yield* mediaUri(request.frames.last);
|
|
51
|
+
const promptImage = [
|
|
52
|
+
...(first === undefined ? [] : [{ uri: first, position: "first" }]),
|
|
53
|
+
...(last === undefined ? [] : [{ uri: last, position: "last" }]),
|
|
54
|
+
];
|
|
55
|
+
const videoUri = request.video === undefined ? undefined : yield* mediaUri(request.video);
|
|
56
|
+
const references = yield* Effect.forEach(request.references ?? [], (asset) => mediaUri(asset).pipe(Effect.map((uri) => ({ uri }))));
|
|
57
|
+
return MediaProtocol.json(mergeJsonRecords({
|
|
58
|
+
model: request.model.id,
|
|
59
|
+
promptText: request.prompt,
|
|
60
|
+
promptImage: promptImage.length === 0 ? undefined : promptImage,
|
|
61
|
+
videoUri,
|
|
62
|
+
references: references.length === 0 ? undefined : references,
|
|
63
|
+
ratio: request.aspectRatio,
|
|
64
|
+
duration: request.durationSeconds,
|
|
65
|
+
resolution: request.resolution,
|
|
66
|
+
audio: request.audio,
|
|
67
|
+
negativePrompt: request.negativePrompt,
|
|
68
|
+
seed: request.seed,
|
|
69
|
+
}, request.providerOptions, request.http?.body) ?? {});
|
|
70
|
+
});
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// 6. Response decoding
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
const decodeStart = MediaProtocol.decodeStarted(ADAPTER, NAME, StartResponse, (value) => ({
|
|
75
|
+
token: { taskID: value.id },
|
|
76
|
+
snapshot: { id: value.id, status: "queued" },
|
|
77
|
+
}));
|
|
78
|
+
const decodeTask = MediaProtocol.decodeJson(ADAPTER, NAME, Task);
|
|
79
|
+
const decodeStatus = Effect.fn("RunwayVideo.decodeStatus")(function* (response, context) {
|
|
80
|
+
const output = yield* decodeTask(response);
|
|
81
|
+
const status = yield* MediaProtocol.status(STATUS, output.value.status, output);
|
|
82
|
+
return { id: context.token.taskID, status, progress: output.value.progress ?? undefined };
|
|
83
|
+
});
|
|
84
|
+
const decodeResult = Effect.fn("RunwayVideo.decodeResult")(function* (response, context) {
|
|
85
|
+
const output = yield* decodeTask(response);
|
|
86
|
+
const task = output.value;
|
|
87
|
+
const status = yield* MediaProtocol.status(STATUS, task.status, output);
|
|
88
|
+
if (status === "failed") {
|
|
89
|
+
const code = task.failureCode ?? undefined;
|
|
90
|
+
const message = `${NAME} task failed${code === undefined ? "" : ` (${code})`}${task.failure ? `: ${task.failure}` : ""}`;
|
|
91
|
+
// Runway failure codes are dotted paths; every moderation outcome carries a SAFETY segment.
|
|
92
|
+
if (code !== undefined && /(^|\.)SAFETY(\.|$)/.test(code))
|
|
93
|
+
return yield* output.contentPolicy(message);
|
|
94
|
+
return yield* output.ended("failed", message);
|
|
95
|
+
}
|
|
96
|
+
if (status === "cancelled")
|
|
97
|
+
return yield* output.ended("cancelled", `${NAME} task ${context.token.taskID} was cancelled`);
|
|
98
|
+
if (status !== "completed")
|
|
99
|
+
return yield* output.invalid(`${NAME} task ${context.token.taskID} has not finished`);
|
|
100
|
+
const urls = task.output ?? [];
|
|
101
|
+
if (urls.length === 0)
|
|
102
|
+
return yield* output.invalid(`${NAME} task succeeded without any output`);
|
|
103
|
+
return new VideoResponse({
|
|
104
|
+
videos: yield* Effect.forEach(urls, (url) => MediaProtocol.expiringUrl(url, OUTPUT_RETENTION, { mediaType: "video/mp4" })),
|
|
105
|
+
usage: task.cost === undefined ? undefined : { type: "credits", credits: task.cost.credits },
|
|
106
|
+
providerMetadata: {
|
|
107
|
+
runway: {
|
|
108
|
+
taskId: context.token.taskID,
|
|
109
|
+
estimatedCredits: task.estimatedCost?.credits,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// 7. Protocol and route
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
const taskPath = (token) => `${TASKS_PATH}/${token.taskID}`;
|
|
118
|
+
export const protocol = MediaProtocol.queued({
|
|
119
|
+
id: ADAPTER,
|
|
120
|
+
name: NAME,
|
|
121
|
+
token: Token,
|
|
122
|
+
unsupported: ["n"],
|
|
123
|
+
start: { body: { from: fromRequest }, decode: decodeStart },
|
|
124
|
+
status: { path: taskPath, decode: decodeStatus },
|
|
125
|
+
result: { path: taskPath, decode: decodeResult },
|
|
126
|
+
cancel: { method: "DELETE", path: taskPath },
|
|
127
|
+
});
|
|
128
|
+
const startPath = (request) => {
|
|
129
|
+
if (request.video !== undefined)
|
|
130
|
+
return VIDEO_TO_VIDEO_PATH;
|
|
131
|
+
if (request.frames?.first !== undefined || request.frames?.last !== undefined)
|
|
132
|
+
return IMAGE_TO_VIDEO_PATH;
|
|
133
|
+
return TEXT_TO_VIDEO_PATH;
|
|
134
|
+
};
|
|
135
|
+
export const model = (input) => VideoModel.fromRoute({
|
|
136
|
+
id: ADAPTER,
|
|
137
|
+
provider: PROVIDER,
|
|
138
|
+
protocol,
|
|
139
|
+
baseURL: DEFAULT_BASE_URL,
|
|
140
|
+
headers: { "X-Runway-Version": API_VERSION },
|
|
141
|
+
path: ({ request }) => startPath(request),
|
|
142
|
+
}, input);
|
|
143
|
+
export const RunwayVideo = {
|
|
144
|
+
protocol,
|
|
145
|
+
model,
|
|
146
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Tool } from "@opencode/schema/tool";
|
|
2
2
|
import { Effect, Schema, Stream } from "effect";
|
|
3
|
-
import { Headers, HttpClientRequest
|
|
3
|
+
import { Headers, HttpClientRequest } from "effect/unstable/http";
|
|
4
|
+
import { Media } from "../media.js";
|
|
4
5
|
import { AIError, LLMRequest, Message, ToolDefinition, type ContentPart, type MediaPart, type ProviderID, type ToolEntry, type ToolResultPart } from "../schema/index.js";
|
|
5
6
|
import { isRecord } from "../utils/record.js";
|
|
6
7
|
export { isRecord };
|
|
@@ -110,23 +111,36 @@ export declare const wrappedSystemUpdate: (route: string, message: Message) => E
|
|
|
110
111
|
* routes: `Invalid JSON input for <route> tool call <name>`.
|
|
111
112
|
*/
|
|
112
113
|
export declare const parseToolInput: (route: string, name: string, raw: string) => Effect.Effect<unknown, AIError, never>;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
export
|
|
119
|
-
|
|
114
|
+
/** Inline view or a typed `InvalidRequest` for routes that cannot fetch URLs or dereference provider refs. */
|
|
115
|
+
export declare const requireInlineMedia: (route: string, asset: Media.Asset) => Effect.Effect<Media.Inline, AIError>;
|
|
116
|
+
export declare const inlineRequired: (route: string, asset: Media.Asset) => AIError;
|
|
117
|
+
/** The remote URL of a `url` asset, for protocols that accept `http(s)` references natively. */
|
|
118
|
+
export declare const mediaUrl: (asset: Media.Asset) => string | undefined;
|
|
119
|
+
export type MediaReference = {
|
|
120
|
+
readonly type: "dataUrl" | "url" | "ref";
|
|
121
|
+
readonly value: string;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* The one string a provider can address an asset by: inline payloads as a data URL, `url` sources as their URL, and
|
|
125
|
+
* this provider's own `ref` as its id. Other providers' refs are never forwarded and fail typed; omit `provider` for
|
|
126
|
+
* APIs with no file handles at all.
|
|
127
|
+
*/
|
|
128
|
+
export declare const mediaReference: (asset: Media.Asset, provider: ProviderID | undefined, label: string) => Effect.Effect<MediaReference, AIError>;
|
|
129
|
+
/**
|
|
130
|
+
* Lift a tool-result file into a `MediaPart`. Tool files carry either a data URL, an `http(s)` URL, or raw base64 in
|
|
131
|
+
* `uri`; the declared `mime` wins over any data-URL prefix so tool authors control the type the model sees.
|
|
132
|
+
*/
|
|
133
|
+
export declare const toolFileMedia: (item: Tool.FileContent) => MediaPart;
|
|
120
134
|
export declare const trimBaseUrl: (value: string) => string;
|
|
121
135
|
export declare const toolResultText: (part: ToolResultPart) => string;
|
|
122
136
|
export declare const errorText: (error: unknown) => string;
|
|
123
137
|
/**
|
|
124
138
|
* `framing` step for Server-Sent Events. Decodes UTF-8, runs the SSE channel
|
|
125
|
-
* decoder, optionally filters named events, and drops empty events
|
|
126
|
-
*
|
|
127
|
-
* boundary. Retry control events are ignored without
|
|
128
|
-
* Decoder failures become provider output errors so
|
|
129
|
-
* stays `AIError`.
|
|
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`.
|
|
130
144
|
*/
|
|
131
145
|
export declare const sseFraming: (bytes: Stream.Stream<Uint8Array, AIError>, events?: ReadonlySet<string>, includeDone?: boolean) => Stream.Stream<string, AIError>;
|
|
132
146
|
/**
|
|
@@ -158,10 +172,6 @@ export declare const flattenToolRequest: (request: LLMRequest) => {
|
|
|
158
172
|
readonly [x: string]: unknown;
|
|
159
173
|
}, import("../route.js").CompactionOperations | undefined>>;
|
|
160
174
|
};
|
|
161
|
-
export declare const imageResponse: (route: string, name: string, response: HttpClientResponse.HttpClientResponse) => Effect.Effect<{
|
|
162
|
-
body: string;
|
|
163
|
-
invalid: (message: string, cause?: unknown) => AIError;
|
|
164
|
-
}, AIError, never>;
|
|
165
175
|
export declare const matchToolChoice: <Auto, None, Required, Tool>(route: string, toolChoice: NonNullable<LLMRequest["toolChoice"]>, cases: {
|
|
166
176
|
readonly auto: () => Auto;
|
|
167
177
|
readonly none: () => None;
|