@happyvertical/ai 0.85.3 → 0.85.5
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 +87 -1
- package/dist/chunks/{anthropic-DA8sdt12.js → anthropic-DuYUnw8j.js} +20 -4
- package/dist/chunks/anthropic-DuYUnw8j.js.map +1 -0
- package/dist/chunks/{bedrock-DDiYFUw0.js → bedrock-ikQJAwWB.js} +19 -3
- package/dist/chunks/bedrock-ikQJAwWB.js.map +1 -0
- package/dist/chunks/{bifrost-fM9FpW_w.js → bifrost-CaYhelVL.js} +5 -4
- package/dist/chunks/bifrost-CaYhelVL.js.map +1 -0
- package/dist/chunks/byteplus-modelark-B8l8sPXu.js +473 -0
- package/dist/chunks/byteplus-modelark-B8l8sPXu.js.map +1 -0
- package/dist/chunks/{claude-cli-CnJh5KQT.js → claude-cli-DeTKk9nu.js} +19 -3
- package/dist/chunks/claude-cli-DeTKk9nu.js.map +1 -0
- package/dist/chunks/{gateway-admin-CiKvHst7.js → gateway-admin-DJXEDpUP.js} +2 -2
- package/dist/chunks/{gateway-admin-CiKvHst7.js.map → gateway-admin-DJXEDpUP.js.map} +1 -1
- package/dist/chunks/{gemini-Bu7Fke7c.js → gemini-BT8fjXWz.js} +294 -11
- package/dist/chunks/gemini-BT8fjXWz.js.map +1 -0
- package/dist/chunks/{huggingface-B07_6wHW.js → huggingface-CqzyxeLB.js} +19 -3
- package/dist/chunks/huggingface-CqzyxeLB.js.map +1 -0
- package/dist/chunks/{litellm-D_Oo9OQ_.js → litellm-viqTuSY8.js} +5 -4
- package/dist/chunks/litellm-viqTuSY8.js.map +1 -0
- package/dist/chunks/{ollama-D4ksOTO8.js → ollama-CdWqwZ05.js} +19 -3
- package/dist/chunks/ollama-CdWqwZ05.js.map +1 -0
- package/dist/chunks/{openai--F38QHiJ.js → openai-BgrK3OfW.js} +20 -4
- package/dist/chunks/openai-BgrK3OfW.js.map +1 -0
- package/dist/chunks/openai-compat-video-D_U7jo0d.js +369 -0
- package/dist/chunks/openai-compat-video-D_U7jo0d.js.map +1 -0
- package/dist/chunks/{qwen-tts-DRRbOrhA.js → qwen-tts-BXjzY7DX.js} +21 -4
- package/dist/chunks/qwen-tts-BXjzY7DX.js.map +1 -0
- package/dist/chunks/{rate-limit-BwyPGXFW.js → rate-limit-DgEhanDL.js} +4 -3
- package/dist/chunks/rate-limit-DgEhanDL.js.map +1 -0
- package/dist/chunks/{safety-CEnoJS6X.js → safety-C415clBm.js} +25 -7
- package/dist/chunks/safety-C415clBm.js.map +1 -0
- package/dist/chunks/src-CU4qWJ2c.js +740 -0
- package/dist/chunks/src-CU4qWJ2c.js.map +1 -0
- package/dist/chunks/{types-FHs-KbGL.js → types-CINwUPDo.js} +4 -2
- package/dist/chunks/types-CINwUPDo.js.map +1 -0
- package/dist/index.js +3 -717
- package/dist/node/factory.d.ts +9 -0
- package/dist/node/factory.d.ts.map +1 -1
- package/dist/node.d.ts +29 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +198 -0
- package/dist/node.js.map +1 -0
- package/dist/shared/factory.d.ts.map +1 -1
- package/dist/shared/providers/anthropic.d.ts +6 -1
- package/dist/shared/providers/anthropic.d.ts.map +1 -1
- package/dist/shared/providers/bedrock.d.ts +6 -1
- package/dist/shared/providers/bedrock.d.ts.map +1 -1
- package/dist/shared/providers/bifrost.d.ts.map +1 -1
- package/dist/shared/providers/byteplus-modelark.d.ts +91 -0
- package/dist/shared/providers/byteplus-modelark.d.ts.map +1 -0
- package/dist/shared/providers/claude-cli.d.ts +6 -1
- package/dist/shared/providers/claude-cli.d.ts.map +1 -1
- package/dist/shared/providers/gemini.d.ts +115 -2
- package/dist/shared/providers/gemini.d.ts.map +1 -1
- package/dist/shared/providers/huggingface.d.ts +6 -1
- package/dist/shared/providers/huggingface.d.ts.map +1 -1
- package/dist/shared/providers/litellm.d.ts.map +1 -1
- package/dist/shared/providers/ollama.d.ts +6 -1
- package/dist/shared/providers/ollama.d.ts.map +1 -1
- package/dist/shared/providers/openai-compat-video.d.ts +98 -0
- package/dist/shared/providers/openai-compat-video.d.ts.map +1 -0
- package/dist/shared/providers/openai.d.ts +6 -1
- package/dist/shared/providers/openai.d.ts.map +1 -1
- package/dist/shared/providers/qwen-tts.d.ts +6 -1
- package/dist/shared/providers/qwen-tts.d.ts.map +1 -1
- package/dist/shared/rate-limit.d.ts.map +1 -1
- package/dist/shared/safety.d.ts.map +1 -1
- package/dist/shared/types.d.ts +343 -5
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +7 -3
- package/dist/chunks/anthropic-DA8sdt12.js.map +0 -1
- package/dist/chunks/bedrock-DDiYFUw0.js.map +0 -1
- package/dist/chunks/bifrost-fM9FpW_w.js.map +0 -1
- package/dist/chunks/claude-cli-CnJh5KQT.js.map +0 -1
- package/dist/chunks/gemini-Bu7Fke7c.js.map +0 -1
- package/dist/chunks/huggingface-B07_6wHW.js.map +0 -1
- package/dist/chunks/litellm-D_Oo9OQ_.js.map +0 -1
- package/dist/chunks/ollama-D4ksOTO8.js.map +0 -1
- package/dist/chunks/openai--F38QHiJ.js.map +0 -1
- package/dist/chunks/qwen-tts-DRRbOrhA.js.map +0 -1
- package/dist/chunks/rate-limit-BwyPGXFW.js.map +0 -1
- package/dist/chunks/safety-CEnoJS6X.js.map +0 -1
- package/dist/chunks/types-FHs-KbGL.js.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-CINwUPDo.js";
|
|
2
|
+
import { n as extractRetryAfterSeconds } from "./rate-limit-DgEhanDL.js";
|
|
3
|
+
import { a as normalizeBaseAIOptions } from "./safety-C415clBm.js";
|
|
4
|
+
import { t as emitUsage } from "./usage-C1Y1Nlg4.js";
|
|
5
|
+
//#region src/shared/providers/byteplus-modelark.ts
|
|
6
|
+
/**
|
|
7
|
+
* BytePlus ModelArk (Seedance) video-generation provider implementation
|
|
8
|
+
*
|
|
9
|
+
* Raw-HTTP provider following the `qwen3-tts` precedent: no vendor SDK, just
|
|
10
|
+
* the ModelArk video-generation task API (create task / poll / fetch /
|
|
11
|
+
* cancel). This provider is video-generation only; it throws
|
|
12
|
+
* `NOT_IMPLEMENTED` for chat, embeddings, and other text/TTS operations.
|
|
13
|
+
*
|
|
14
|
+
* The base URL, endpoint paths (`/contents/generations/tasks`), request/
|
|
15
|
+
* response field names, and the `queued|running|cancelled|succeeded|failed|
|
|
16
|
+
* expired` status enum were confirmed directly against BytePlus's own
|
|
17
|
+
* published docs and blog examples at the time this was written (see the
|
|
18
|
+
* link below). The `expired` status, the `page_num`/`page_size`/`filter.*`
|
|
19
|
+
* list query params, and the DELETE-as-cancel-or-delete semantics
|
|
20
|
+
* (`queued` → cancelled, `running` → rejected, terminal → deleted) are
|
|
21
|
+
* corroborated from that same documentation but have not been exercised
|
|
22
|
+
* against a live ModelArk account by this package's test suite — see
|
|
23
|
+
* `packages/ai/src/byteplus-modelark.integration.test.ts` for an opt-in
|
|
24
|
+
* check (skipped unless `MODELARK_API_KEY` is set) that exercises those
|
|
25
|
+
* specifics against the real API.
|
|
26
|
+
*
|
|
27
|
+
* @see https://docs.byteplus.com/en/docs/ModelArk/Video_Generation_API
|
|
28
|
+
*/
|
|
29
|
+
var DEFAULT_MODELARK_BASE_URL = "https://ark.ap-southeast.bytepluses.com/api/v3";
|
|
30
|
+
var DEFAULT_MODELARK_MODEL = "seedance-1-0-lite-t2v-250428";
|
|
31
|
+
var DEFAULT_MODELARK_I2V_MODEL = "seedance-1-0-lite-i2v-250428";
|
|
32
|
+
/** Burst capacity for the submit token bucket — see {@link SubmitRateLimiter}. */
|
|
33
|
+
var SUBMIT_BURST_CAPACITY = 2;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve after `ms` milliseconds, or as soon as `signal` aborts —
|
|
36
|
+
* whichever comes first. Never rejects; callers re-check `signal.aborted`
|
|
37
|
+
* themselves immediately after, so a single check covers both "timed out
|
|
38
|
+
* normally" and "woke up early because of an abort."
|
|
39
|
+
*/
|
|
40
|
+
function delayOrAbort(ms, signal) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
if (signal?.aborted) {
|
|
43
|
+
resolve();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const timer = setTimeout(resolve, ms);
|
|
47
|
+
signal?.addEventListener("abort", () => {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
resolve();
|
|
50
|
+
}, { once: true });
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Local token-bucket + concurrency limiter for submit-time throttling.
|
|
55
|
+
*
|
|
56
|
+
* ModelArk documents Seedance account limits of QPS 2 / 3 *concurrent
|
|
57
|
+
* submissions*; this only gates `submitVideoGenerationJob` (task creation
|
|
58
|
+
* HTTP calls), matching the "submit-time throttling" requirement — polling
|
|
59
|
+
* and cancellation are not subject to the same account-level task-creation
|
|
60
|
+
* limits. `maxConcurrent` bounds concurrent in-flight *submit HTTP
|
|
61
|
+
* requests*, not the lifetime of the provider-side render tasks those
|
|
62
|
+
* submissions create — this limiter has no way to observe when a task
|
|
63
|
+
* actually finishes rendering on ModelArk's infrastructure, so
|
|
64
|
+
* task-lifetime concurrency is not, and cannot be, tracked here.
|
|
65
|
+
*
|
|
66
|
+
* The bucket's burst capacity is capped at {@link SUBMIT_BURST_CAPACITY}
|
|
67
|
+
* (independent of `requestsPerMinute`) so the bucket doesn't start full —
|
|
68
|
+
* without this cap, a bucket sized to `requestsPerMinute` (e.g. 120) would
|
|
69
|
+
* start with 120 tokens and let the first 120 submits through instantly,
|
|
70
|
+
* defeating the QPS limit entirely.
|
|
71
|
+
*/
|
|
72
|
+
var SubmitRateLimiter = class {
|
|
73
|
+
tokens;
|
|
74
|
+
lastRefill;
|
|
75
|
+
maxTokens;
|
|
76
|
+
refillRate;
|
|
77
|
+
activeRequests = 0;
|
|
78
|
+
maxConcurrent;
|
|
79
|
+
constructor(options) {
|
|
80
|
+
const requestsPerMinute = options.requestsPerMinute ?? 120;
|
|
81
|
+
this.refillRate = requestsPerMinute / 60;
|
|
82
|
+
this.maxTokens = SUBMIT_BURST_CAPACITY;
|
|
83
|
+
this.tokens = this.maxTokens;
|
|
84
|
+
this.lastRefill = Date.now();
|
|
85
|
+
this.maxConcurrent = options.maxConcurrent ?? 3;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Acquire a submit slot + token, honoring `signal` throughout the wait.
|
|
89
|
+
*
|
|
90
|
+
* The concurrency slot and the token are checked and committed together
|
|
91
|
+
* in one synchronous section (no `await` between the check and the
|
|
92
|
+
* `tokens -=`/`activeRequests +=` mutations below), so a waiter can never
|
|
93
|
+
* observe a stale "slot available" snapshot from before another waiter's
|
|
94
|
+
* own commit — closing the TOCTOU window where two callers could both
|
|
95
|
+
* pass the checks and push `activeRequests` past `maxConcurrent`.
|
|
96
|
+
*
|
|
97
|
+
* An aborted `signal` — whether already aborted at entry or aborted
|
|
98
|
+
* while waiting for a slot/token — throws before any token is consumed
|
|
99
|
+
* or `activeRequests` is incremented, so an aborted submission never
|
|
100
|
+
* silently proceeds to create a billable job.
|
|
101
|
+
*/
|
|
102
|
+
async acquire(signal) {
|
|
103
|
+
const throwIfAborted = () => {
|
|
104
|
+
if (signal?.aborted) throw new AIError("ModelArk submission aborted by caller while waiting for the rate limiter", "AI_ABORTED", "byteplus-modelark");
|
|
105
|
+
};
|
|
106
|
+
throwIfAborted();
|
|
107
|
+
const now = Date.now();
|
|
108
|
+
const elapsed = (now - this.lastRefill) / 1e3;
|
|
109
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + elapsed * this.refillRate);
|
|
110
|
+
this.lastRefill = now;
|
|
111
|
+
for (;;) {
|
|
112
|
+
throwIfAborted();
|
|
113
|
+
if (this.activeRequests < this.maxConcurrent && this.tokens >= 1) {
|
|
114
|
+
this.tokens -= 1;
|
|
115
|
+
this.activeRequests += 1;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (this.activeRequests >= this.maxConcurrent) await delayOrAbort(100, signal);
|
|
119
|
+
else {
|
|
120
|
+
await delayOrAbort(Math.max(1, (1 - this.tokens) / this.refillRate * 1e3), signal);
|
|
121
|
+
const elapsedNow = (Date.now() - this.lastRefill) / 1e3;
|
|
122
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + elapsedNow * this.refillRate);
|
|
123
|
+
this.lastRefill = Date.now();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
release() {
|
|
128
|
+
this.activeRequests = Math.max(0, this.activeRequests - 1);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* FNV-1a hash, used only to avoid retaining raw API keys as in-memory Map
|
|
133
|
+
* keys in {@link sharedSubmitLimiters}. Not cryptographic — collisions just
|
|
134
|
+
* mean two distinct keys share a rate-limit bucket, which is a pacing
|
|
135
|
+
* inefficiency, not a security issue.
|
|
136
|
+
*/
|
|
137
|
+
function hashKey(value) {
|
|
138
|
+
let hash = 2166136261;
|
|
139
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
140
|
+
hash ^= value.charCodeAt(index);
|
|
141
|
+
hash = Math.imul(hash, 16777619);
|
|
142
|
+
}
|
|
143
|
+
return (hash >>> 0).toString(36);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Submit-time limiters keyed by credential/config, shared module-wide.
|
|
147
|
+
*
|
|
148
|
+
* Provider instances are frequently constructed per pipeline step (a fresh
|
|
149
|
+
* `getAI({ type: 'byteplus-modelark', ... })` per job), so a limiter owned
|
|
150
|
+
* by a single provider instance would reset on every construction and never
|
|
151
|
+
* actually enforce the account-wide QPS/concurrency limits. Keying by the
|
|
152
|
+
* resolved credentials (or an explicit `rateLimit.key`) lets independently
|
|
153
|
+
* constructed instances that target the same ModelArk account share one
|
|
154
|
+
* bucket, mirroring how {@link createRateLimitedAI}'s budget coordinators
|
|
155
|
+
* are shared by `rateLimit.key`.
|
|
156
|
+
*/
|
|
157
|
+
var sharedSubmitLimiters = /* @__PURE__ */ new Map();
|
|
158
|
+
function getSharedSubmitLimiter(options) {
|
|
159
|
+
const key = options.rateLimit?.key?.trim() || `byteplus-modelark:${hashKey(options.apiKey || "default")}`;
|
|
160
|
+
let limiter = sharedSubmitLimiters.get(key);
|
|
161
|
+
if (!limiter) {
|
|
162
|
+
limiter = new SubmitRateLimiter({
|
|
163
|
+
requestsPerMinute: options.rateLimit?.requestsPerMinute,
|
|
164
|
+
maxConcurrent: options.rateLimit?.maxConcurrent
|
|
165
|
+
});
|
|
166
|
+
sharedSubmitLimiters.set(key, limiter);
|
|
167
|
+
}
|
|
168
|
+
return limiter;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* BytePlus ModelArk provider implementation for asynchronous video
|
|
172
|
+
* generation (Seedance). Focuses on video-generation capabilities and
|
|
173
|
+
* throws `NOT_IMPLEMENTED` for text generation methods.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* import { getAI } from '@happyvertical/ai';
|
|
178
|
+
*
|
|
179
|
+
* const modelark = await getAI({
|
|
180
|
+
* type: 'byteplus-modelark',
|
|
181
|
+
* apiKey: process.env.MODELARK_API_KEY!,
|
|
182
|
+
* });
|
|
183
|
+
*
|
|
184
|
+
* const job = await modelark.submitVideoGenerationJob({
|
|
185
|
+
* prompt: 'A drone shot flying over a coastal cliff at sunrise',
|
|
186
|
+
* durationSeconds: 5,
|
|
187
|
+
* resolution: '720p',
|
|
188
|
+
* aspectRatio: '16:9',
|
|
189
|
+
* });
|
|
190
|
+
*
|
|
191
|
+
* // Persist `job`, poll later (even after a restart):
|
|
192
|
+
* const status = await modelark.getVideoGenerationJob(job);
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
var ByteplusModelArkProvider = class {
|
|
196
|
+
options;
|
|
197
|
+
submitLimiter;
|
|
198
|
+
constructor(options) {
|
|
199
|
+
this.options = normalizeBaseAIOptions({ ...options });
|
|
200
|
+
this.submitLimiter = getSharedSubmitLimiter(this.options);
|
|
201
|
+
}
|
|
202
|
+
get baseUrl() {
|
|
203
|
+
return (this.options.baseUrl || DEFAULT_MODELARK_BASE_URL).replace(/\/+$/, "");
|
|
204
|
+
}
|
|
205
|
+
async request(path, options = {}) {
|
|
206
|
+
if (options.signal?.aborted) throw new AIError("ModelArk request aborted by caller", "AI_ABORTED", "byteplus-modelark");
|
|
207
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
208
|
+
for (const [key, value] of Object.entries(options.query ?? {})) url.searchParams.set(key, String(value));
|
|
209
|
+
const controller = new AbortController();
|
|
210
|
+
const timeoutMs = options.timeout ?? this.options.timeout;
|
|
211
|
+
let timedOut = false;
|
|
212
|
+
const timer = timeoutMs ? setTimeout(() => {
|
|
213
|
+
timedOut = true;
|
|
214
|
+
controller.abort();
|
|
215
|
+
}, timeoutMs) : void 0;
|
|
216
|
+
const onExternalAbort = () => controller.abort(options.signal?.reason);
|
|
217
|
+
if (options.signal?.aborted) onExternalAbort();
|
|
218
|
+
else options.signal?.addEventListener("abort", onExternalAbort, { once: true });
|
|
219
|
+
try {
|
|
220
|
+
let response;
|
|
221
|
+
try {
|
|
222
|
+
response = await fetch(url, {
|
|
223
|
+
method: options.method || "GET",
|
|
224
|
+
headers: {
|
|
225
|
+
"Content-Type": "application/json",
|
|
226
|
+
Authorization: `Bearer ${this.options.apiKey ?? ""}`,
|
|
227
|
+
...this.options.headers || {}
|
|
228
|
+
},
|
|
229
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
230
|
+
signal: controller.signal
|
|
231
|
+
});
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (timedOut) throw new AIError(`ModelArk request to ${path} timed out after ${timeoutMs}ms`, "AI_TIMEOUT", "byteplus-modelark", void 0, true);
|
|
234
|
+
if (options.signal?.aborted) throw new AIError("ModelArk request aborted by caller", "AI_ABORTED", "byteplus-modelark");
|
|
235
|
+
throw new AIError(`Network error calling ModelArk ${path}: ${error instanceof Error ? error.message : String(error)}`, "NETWORK_ERROR", "byteplus-modelark", void 0, true);
|
|
236
|
+
}
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
const errorText = await response.text();
|
|
239
|
+
if (response.status === 401 || response.status === 403) throw new AuthenticationError("byteplus-modelark");
|
|
240
|
+
if (response.status === 429) throw new RateLimitError("byteplus-modelark", extractRetryAfterSeconds({ headers: response.headers }));
|
|
241
|
+
throw new AIError(`ModelArk request to ${path} failed: ${response.status} ${errorText}`, "MODELARK_REQUEST_FAILED", "byteplus-modelark");
|
|
242
|
+
}
|
|
243
|
+
if (response.status === 204) return {};
|
|
244
|
+
const text = await response.text();
|
|
245
|
+
return text ? JSON.parse(text) : {};
|
|
246
|
+
} finally {
|
|
247
|
+
if (timer) clearTimeout(timer);
|
|
248
|
+
options.signal?.removeEventListener("abort", onExternalAbort);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
toImageUrl(reference) {
|
|
252
|
+
if (typeof reference.image === "string") return reference.image;
|
|
253
|
+
return `data:${reference.mimeType || "image/png"};base64,${reference.image.toString("base64")}`;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Submit an asynchronous Seedance video-generation task.
|
|
257
|
+
*
|
|
258
|
+
* Submission is locally throttled to approximate ModelArk's documented
|
|
259
|
+
* Seedance account limits (QPS 2, capped at {@link SUBMIT_BURST_CAPACITY}
|
|
260
|
+
* burst / 3 concurrent *submissions* — task-lifetime concurrency on
|
|
261
|
+
* ModelArk's infrastructure is not tracked; see {@link SubmitRateLimiter}).
|
|
262
|
+
*
|
|
263
|
+
* When `referenceImages` are supplied and no explicit `model` is chosen,
|
|
264
|
+
* this selects the image-to-video default model rather than silently
|
|
265
|
+
* generating text-to-video with the image ignored.
|
|
266
|
+
*/
|
|
267
|
+
async submitVideoGenerationJob(options) {
|
|
268
|
+
const startTime = Date.now();
|
|
269
|
+
await this.submitLimiter.acquire(options.signal);
|
|
270
|
+
try {
|
|
271
|
+
const hasReferenceImages = (options.referenceImages?.length ?? 0) > 0;
|
|
272
|
+
const model = options.model || this.options.defaultModel || (hasReferenceImages ? DEFAULT_MODELARK_I2V_MODEL : DEFAULT_MODELARK_MODEL);
|
|
273
|
+
const content = [];
|
|
274
|
+
if (options.prompt) content.push({
|
|
275
|
+
type: "text",
|
|
276
|
+
text: options.prompt
|
|
277
|
+
});
|
|
278
|
+
for (const reference of options.referenceImages ?? []) content.push({
|
|
279
|
+
type: "image_url",
|
|
280
|
+
image_url: { url: this.toImageUrl(reference) },
|
|
281
|
+
role: reference.role || "reference_image"
|
|
282
|
+
});
|
|
283
|
+
if (content.length === 0) throw new AIError("submitVideoGenerationJob requires a prompt or at least one reference image", "INVALID_INPUT", "byteplus-modelark");
|
|
284
|
+
const body = {
|
|
285
|
+
model,
|
|
286
|
+
content,
|
|
287
|
+
resolution: options.resolution,
|
|
288
|
+
ratio: options.aspectRatio,
|
|
289
|
+
duration: options.durationSeconds,
|
|
290
|
+
seed: options.seed
|
|
291
|
+
};
|
|
292
|
+
const response = await this.request("/contents/generations/tasks", {
|
|
293
|
+
method: "POST",
|
|
294
|
+
body,
|
|
295
|
+
signal: options.signal,
|
|
296
|
+
timeout: options.timeout
|
|
297
|
+
});
|
|
298
|
+
emitUsage(this.options, "byteplus-modelark", "submitVideoGenerationJob", model, void 0, startTime, {
|
|
299
|
+
...options.durationSeconds !== void 0 ? { durationSeconds: String(options.durationSeconds) } : {},
|
|
300
|
+
...options.resolution ? { resolution: options.resolution } : {},
|
|
301
|
+
...options.aspectRatio ? { aspectRatio: options.aspectRatio } : {},
|
|
302
|
+
...options.usageTags
|
|
303
|
+
});
|
|
304
|
+
return {
|
|
305
|
+
jobId: response.id,
|
|
306
|
+
provider: "byteplus-modelark",
|
|
307
|
+
model,
|
|
308
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
309
|
+
};
|
|
310
|
+
} finally {
|
|
311
|
+
this.submitLimiter.release();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Poll the status of a ModelArk video-generation task.
|
|
316
|
+
*/
|
|
317
|
+
async getVideoGenerationJob(handle) {
|
|
318
|
+
const task = await this.request(`/contents/generations/tasks/${handle.jobId}`);
|
|
319
|
+
return this.mapTask(task);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Fetch the result of a completed ModelArk video-generation task.
|
|
323
|
+
*
|
|
324
|
+
* @throws {AIError} When the job has not succeeded yet
|
|
325
|
+
*/
|
|
326
|
+
async fetchVideoGenerationResult(handle) {
|
|
327
|
+
const status = await this.getVideoGenerationJob(handle);
|
|
328
|
+
if (status.status !== "succeeded" || !status.result) throw new AIError(`ModelArk video-generation job ${handle.jobId} has not succeeded (status: ${status.status})`, "VIDEO_JOB_NOT_READY", "byteplus-modelark", handle.model);
|
|
329
|
+
return status.result;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Cancel a ModelArk video-generation task.
|
|
333
|
+
*
|
|
334
|
+
* ModelArk's DELETE endpoint only transitions a `queued` task to
|
|
335
|
+
* `cancelled`; a task that is already `running` cannot be cancelled and
|
|
336
|
+
* the request fails with an {@link AIError} describing why. Terminal
|
|
337
|
+
* tasks (`succeeded`/`failed`/`expired`) are deleted instead.
|
|
338
|
+
*/
|
|
339
|
+
async cancelVideoGenerationJob(handle) {
|
|
340
|
+
await this.request(`/contents/generations/tasks/${handle.jobId}`, { method: "DELETE" });
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Cheap auth-shaped check for ModelArk access: lists tasks with a page
|
|
344
|
+
* size of 1. Callers on a hot path must cache the result themselves.
|
|
345
|
+
*/
|
|
346
|
+
async validateVideoGenerationAccess() {
|
|
347
|
+
await this.request("/contents/generations/tasks", { query: { page_size: 1 } });
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
mapTask(task) {
|
|
351
|
+
switch (task.status) {
|
|
352
|
+
case "queued": return { status: "queued" };
|
|
353
|
+
case "running": return { status: "running" };
|
|
354
|
+
case "cancelled": return { status: "cancelled" };
|
|
355
|
+
case "expired": return {
|
|
356
|
+
status: "failed",
|
|
357
|
+
error: "ModelArk task expired before the result was retrieved"
|
|
358
|
+
};
|
|
359
|
+
case "failed": return {
|
|
360
|
+
status: "failed",
|
|
361
|
+
error: task.error?.message || "ModelArk video generation failed"
|
|
362
|
+
};
|
|
363
|
+
case "succeeded":
|
|
364
|
+
if (!task.content?.video_url) return {
|
|
365
|
+
status: "failed",
|
|
366
|
+
error: "ModelArk reported success with no video_url"
|
|
367
|
+
};
|
|
368
|
+
return {
|
|
369
|
+
status: "succeeded",
|
|
370
|
+
result: {
|
|
371
|
+
url: task.content.video_url,
|
|
372
|
+
mimeType: "video/mp4",
|
|
373
|
+
durationSeconds: task.duration
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
default: return {
|
|
377
|
+
status: "running",
|
|
378
|
+
rawStatus: task.status
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async getModels() {
|
|
383
|
+
return [{
|
|
384
|
+
id: "seedance-1-0-lite-t2v-250428",
|
|
385
|
+
name: "Seedance 1.0 Lite (text-to-video)",
|
|
386
|
+
description: "ModelArk Seedance 1.0 Lite text-to-video model",
|
|
387
|
+
contextLength: 0,
|
|
388
|
+
capabilities: ["video_generation"],
|
|
389
|
+
supportsFunctions: false,
|
|
390
|
+
supportsVision: false
|
|
391
|
+
}, {
|
|
392
|
+
id: "seedance-1-0-lite-i2v-250428",
|
|
393
|
+
name: "Seedance 1.0 Lite (image-to-video)",
|
|
394
|
+
description: "ModelArk Seedance 1.0 Lite image-to-video model",
|
|
395
|
+
contextLength: 0,
|
|
396
|
+
capabilities: ["video_generation"],
|
|
397
|
+
supportsFunctions: false,
|
|
398
|
+
supportsVision: true
|
|
399
|
+
}];
|
|
400
|
+
}
|
|
401
|
+
async getCapabilities() {
|
|
402
|
+
return {
|
|
403
|
+
chat: false,
|
|
404
|
+
completion: false,
|
|
405
|
+
embeddings: false,
|
|
406
|
+
streaming: false,
|
|
407
|
+
functions: false,
|
|
408
|
+
vision: false,
|
|
409
|
+
fineTuning: false,
|
|
410
|
+
imageEmbeddings: false,
|
|
411
|
+
imageGeneration: false,
|
|
412
|
+
videoGeneration: true,
|
|
413
|
+
tts: false,
|
|
414
|
+
voiceCloning: false,
|
|
415
|
+
voiceDesign: false,
|
|
416
|
+
maxContextLength: 0,
|
|
417
|
+
supportedOperations: [
|
|
418
|
+
"submitVideoGenerationJob",
|
|
419
|
+
"getVideoGenerationJob",
|
|
420
|
+
"fetchVideoGenerationResult",
|
|
421
|
+
"cancelVideoGenerationJob"
|
|
422
|
+
]
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
async chat(_messages, _options) {
|
|
426
|
+
throw new AIError("Chat is not supported by the BytePlus ModelArk provider. Use OpenAI, Anthropic, or another provider for text generation.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
427
|
+
}
|
|
428
|
+
async complete(_prompt, _options) {
|
|
429
|
+
throw new AIError("Complete is not supported by the BytePlus ModelArk provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
430
|
+
}
|
|
431
|
+
async message(_text, _options) {
|
|
432
|
+
throw new AIError("Message is not supported by the BytePlus ModelArk provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
433
|
+
}
|
|
434
|
+
async embed(_text, _options) {
|
|
435
|
+
throw new AIError("Embeddings are not supported by the BytePlus ModelArk provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
436
|
+
}
|
|
437
|
+
async embedImage(_image, _options) {
|
|
438
|
+
throw new AIError("Image embeddings are not supported by the BytePlus ModelArk provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
439
|
+
}
|
|
440
|
+
async describeImage(_image, _prompt, _options) {
|
|
441
|
+
throw new AIError("Image description is not supported by the BytePlus ModelArk provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
442
|
+
}
|
|
443
|
+
async generateImage(_prompt, _options) {
|
|
444
|
+
throw new AIError("Synchronous image generation is not yet implemented by the BytePlus ModelArk provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
445
|
+
}
|
|
446
|
+
stream(_messages, _options) {
|
|
447
|
+
const error = new AIError("Chat streaming is not supported by the BytePlus ModelArk provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
448
|
+
return { [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }) };
|
|
449
|
+
}
|
|
450
|
+
async countTokens(_text) {
|
|
451
|
+
throw new AIError("Token counting is not supported by the BytePlus ModelArk provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
452
|
+
}
|
|
453
|
+
async synthesizeSpeech(_text, _options) {
|
|
454
|
+
throw new AIError("TTS is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
455
|
+
}
|
|
456
|
+
streamSpeech(_text, _options) {
|
|
457
|
+
const error = new AIError("TTS streaming is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
458
|
+
return { [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }) };
|
|
459
|
+
}
|
|
460
|
+
async cloneVoice(_options) {
|
|
461
|
+
throw new AIError("Voice cloning is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
462
|
+
}
|
|
463
|
+
async designVoice(_options) {
|
|
464
|
+
throw new AIError("Voice design is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
465
|
+
}
|
|
466
|
+
async getVoices(_options) {
|
|
467
|
+
throw new AIError("Voice listing is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.", "NOT_IMPLEMENTED", "byteplus-modelark");
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
//#endregion
|
|
471
|
+
export { ByteplusModelArkProvider };
|
|
472
|
+
|
|
473
|
+
//# sourceMappingURL=byteplus-modelark-B8l8sPXu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"byteplus-modelark-B8l8sPXu.js","names":[],"sources":["../../src/shared/providers/byteplus-modelark.ts"],"sourcesContent":["/**\n * BytePlus ModelArk (Seedance) video-generation provider implementation\n *\n * Raw-HTTP provider following the `qwen3-tts` precedent: no vendor SDK, just\n * the ModelArk video-generation task API (create task / poll / fetch /\n * cancel). This provider is video-generation only; it throws\n * `NOT_IMPLEMENTED` for chat, embeddings, and other text/TTS operations.\n *\n * The base URL, endpoint paths (`/contents/generations/tasks`), request/\n * response field names, and the `queued|running|cancelled|succeeded|failed|\n * expired` status enum were confirmed directly against BytePlus's own\n * published docs and blog examples at the time this was written (see the\n * link below). The `expired` status, the `page_num`/`page_size`/`filter.*`\n * list query params, and the DELETE-as-cancel-or-delete semantics\n * (`queued` → cancelled, `running` → rejected, terminal → deleted) are\n * corroborated from that same documentation but have not been exercised\n * against a live ModelArk account by this package's test suite — see\n * `packages/ai/src/byteplus-modelark.integration.test.ts` for an opt-in\n * check (skipped unless `MODELARK_API_KEY` is set) that exercises those\n * specifics against the real API.\n *\n * @see https://docs.byteplus.com/en/docs/ModelArk/Video_Generation_API\n */\n\nimport { extractRetryAfterSeconds } from '../rate-limit';\nimport { normalizeBaseAIOptions } from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ByteplusModelArkOptions,\n ChatOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n TTSOptions,\n TTSResponse,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationReferenceImage,\n VideoGenerationResult,\n VideoGenerationStatusResult,\n Voice,\n VoiceCloneOptions,\n VoiceDesignOptions,\n VoiceListOptions,\n} from '../types';\nimport { AIError, AuthenticationError, RateLimitError } from '../types';\nimport { emitUsage } from './usage';\n\nconst DEFAULT_MODELARK_BASE_URL =\n 'https://ark.ap-southeast.bytepluses.com/api/v3';\nconst DEFAULT_MODELARK_MODEL = 'seedance-1-0-lite-t2v-250428';\nconst DEFAULT_MODELARK_I2V_MODEL = 'seedance-1-0-lite-i2v-250428';\n/** Burst capacity for the submit token bucket — see {@link SubmitRateLimiter}. */\nconst SUBMIT_BURST_CAPACITY = 2;\n\ntype ModelArkTaskStatus =\n | 'queued'\n | 'running'\n | 'cancelled'\n | 'succeeded'\n | 'failed'\n | 'expired';\n\ninterface ModelArkContentItem {\n type: 'text' | 'image_url';\n text?: string;\n image_url?: { url: string };\n role?: string;\n}\n\ninterface ModelArkCreateTaskRequest {\n model: string;\n content: ModelArkContentItem[];\n resolution?: string;\n ratio?: string;\n duration?: number;\n seed?: number;\n}\n\ninterface ModelArkCreateTaskResponse {\n id: string;\n}\n\ninterface ModelArkTaskResponse {\n id: string;\n model: string;\n status: ModelArkTaskStatus;\n content?: { video_url?: string; last_frame_url?: string };\n error?: { code: string; message: string } | null;\n created_at: number;\n updated_at: number;\n seed?: number;\n resolution?: string;\n ratio?: string;\n duration?: number;\n}\n\n/**\n * Resolve after `ms` milliseconds, or as soon as `signal` aborts —\n * whichever comes first. Never rejects; callers re-check `signal.aborted`\n * themselves immediately after, so a single check covers both \"timed out\n * normally\" and \"woke up early because of an abort.\"\n */\nfunction delayOrAbort(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n resolve();\n },\n { once: true },\n );\n });\n}\n\n/**\n * Local token-bucket + concurrency limiter for submit-time throttling.\n *\n * ModelArk documents Seedance account limits of QPS 2 / 3 *concurrent\n * submissions*; this only gates `submitVideoGenerationJob` (task creation\n * HTTP calls), matching the \"submit-time throttling\" requirement — polling\n * and cancellation are not subject to the same account-level task-creation\n * limits. `maxConcurrent` bounds concurrent in-flight *submit HTTP\n * requests*, not the lifetime of the provider-side render tasks those\n * submissions create — this limiter has no way to observe when a task\n * actually finishes rendering on ModelArk's infrastructure, so\n * task-lifetime concurrency is not, and cannot be, tracked here.\n *\n * The bucket's burst capacity is capped at {@link SUBMIT_BURST_CAPACITY}\n * (independent of `requestsPerMinute`) so the bucket doesn't start full —\n * without this cap, a bucket sized to `requestsPerMinute` (e.g. 120) would\n * start with 120 tokens and let the first 120 submits through instantly,\n * defeating the QPS limit entirely.\n */\nclass SubmitRateLimiter {\n private tokens: number;\n private lastRefill: number;\n private readonly maxTokens: number;\n private readonly refillRate: number; // tokens per second\n private activeRequests = 0;\n private readonly maxConcurrent: number;\n\n constructor(options: { requestsPerMinute?: number; maxConcurrent?: number }) {\n const requestsPerMinute = options.requestsPerMinute ?? 120; // ~QPS 2\n this.refillRate = requestsPerMinute / 60;\n this.maxTokens = SUBMIT_BURST_CAPACITY;\n this.tokens = this.maxTokens;\n this.lastRefill = Date.now();\n this.maxConcurrent = options.maxConcurrent ?? 3;\n }\n\n /**\n * Acquire a submit slot + token, honoring `signal` throughout the wait.\n *\n * The concurrency slot and the token are checked and committed together\n * in one synchronous section (no `await` between the check and the\n * `tokens -=`/`activeRequests +=` mutations below), so a waiter can never\n * observe a stale \"slot available\" snapshot from before another waiter's\n * own commit — closing the TOCTOU window where two callers could both\n * pass the checks and push `activeRequests` past `maxConcurrent`.\n *\n * An aborted `signal` — whether already aborted at entry or aborted\n * while waiting for a slot/token — throws before any token is consumed\n * or `activeRequests` is incremented, so an aborted submission never\n * silently proceeds to create a billable job.\n */\n async acquire(signal?: AbortSignal): Promise<void> {\n const throwIfAborted = () => {\n if (signal?.aborted) {\n throw new AIError(\n 'ModelArk submission aborted by caller while waiting for the rate limiter',\n 'AI_ABORTED',\n 'byteplus-modelark',\n );\n }\n };\n\n throwIfAborted();\n\n const now = Date.now();\n const elapsed = (now - this.lastRefill) / 1000;\n this.tokens = Math.min(\n this.maxTokens,\n this.tokens + elapsed * this.refillRate,\n );\n this.lastRefill = now;\n\n for (;;) {\n throwIfAborted();\n\n if (this.activeRequests < this.maxConcurrent && this.tokens >= 1) {\n this.tokens -= 1;\n this.activeRequests += 1;\n return;\n }\n\n if (this.activeRequests >= this.maxConcurrent) {\n await delayOrAbort(100, signal);\n } else {\n const waitTime = Math.max(\n 1,\n ((1 - this.tokens) / this.refillRate) * 1000,\n );\n await delayOrAbort(waitTime, signal);\n const elapsedNow = (Date.now() - this.lastRefill) / 1000;\n this.tokens = Math.min(\n this.maxTokens,\n this.tokens + elapsedNow * this.refillRate,\n );\n this.lastRefill = Date.now();\n }\n }\n }\n\n release(): void {\n this.activeRequests = Math.max(0, this.activeRequests - 1);\n }\n}\n\n/**\n * FNV-1a hash, used only to avoid retaining raw API keys as in-memory Map\n * keys in {@link sharedSubmitLimiters}. Not cryptographic — collisions just\n * mean two distinct keys share a rate-limit bucket, which is a pacing\n * inefficiency, not a security issue.\n */\nfunction hashKey(value: string): string {\n let hash = 2166136261;\n for (let index = 0; index < value.length; index += 1) {\n hash ^= value.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n return (hash >>> 0).toString(36);\n}\n\n/**\n * Submit-time limiters keyed by credential/config, shared module-wide.\n *\n * Provider instances are frequently constructed per pipeline step (a fresh\n * `getAI({ type: 'byteplus-modelark', ... })` per job), so a limiter owned\n * by a single provider instance would reset on every construction and never\n * actually enforce the account-wide QPS/concurrency limits. Keying by the\n * resolved credentials (or an explicit `rateLimit.key`) lets independently\n * constructed instances that target the same ModelArk account share one\n * bucket, mirroring how {@link createRateLimitedAI}'s budget coordinators\n * are shared by `rateLimit.key`.\n */\nconst sharedSubmitLimiters = new Map<string, SubmitRateLimiter>();\n\nfunction getSharedSubmitLimiter(\n options: ByteplusModelArkOptions,\n): SubmitRateLimiter {\n const key =\n options.rateLimit?.key?.trim() ||\n `byteplus-modelark:${hashKey(options.apiKey || 'default')}`;\n\n let limiter = sharedSubmitLimiters.get(key);\n if (!limiter) {\n limiter = new SubmitRateLimiter({\n requestsPerMinute: options.rateLimit?.requestsPerMinute,\n maxConcurrent: options.rateLimit?.maxConcurrent,\n });\n sharedSubmitLimiters.set(key, limiter);\n }\n return limiter;\n}\n\n/** @internal Test-only: clear shared submit limiters between test cases. */\nexport function __resetByteplusModelArkRateLimitersForTests(): void {\n sharedSubmitLimiters.clear();\n}\n\n/**\n * BytePlus ModelArk provider implementation for asynchronous video\n * generation (Seedance). Focuses on video-generation capabilities and\n * throws `NOT_IMPLEMENTED` for text generation methods.\n *\n * @example\n * ```typescript\n * import { getAI } from '@happyvertical/ai';\n *\n * const modelark = await getAI({\n * type: 'byteplus-modelark',\n * apiKey: process.env.MODELARK_API_KEY!,\n * });\n *\n * const job = await modelark.submitVideoGenerationJob({\n * prompt: 'A drone shot flying over a coastal cliff at sunrise',\n * durationSeconds: 5,\n * resolution: '720p',\n * aspectRatio: '16:9',\n * });\n *\n * // Persist `job`, poll later (even after a restart):\n * const status = await modelark.getVideoGenerationJob(job);\n * ```\n */\nexport class ByteplusModelArkProvider implements AIInterface {\n private options: ByteplusModelArkOptions;\n private readonly submitLimiter: SubmitRateLimiter;\n\n constructor(options: ByteplusModelArkOptions) {\n // Note: deliberately no `defaultModel` fallback baked in here — the\n // model-selection logic in submitVideoGenerationJob picks between the\n // t2v and i2v defaults based on whether referenceImages are present,\n // and an unconditional default here would always shadow that.\n this.options = normalizeBaseAIOptions({ ...options });\n\n this.submitLimiter = getSharedSubmitLimiter(this.options);\n }\n\n private get baseUrl(): string {\n return (this.options.baseUrl || DEFAULT_MODELARK_BASE_URL).replace(\n /\\/+$/,\n '',\n );\n }\n\n private async request<T>(\n path: string,\n options: {\n method?: 'GET' | 'POST' | 'DELETE';\n body?: unknown;\n query?: Record<string, string | number>;\n signal?: AbortSignal;\n timeout?: number;\n } = {},\n ): Promise<T> {\n if (options.signal?.aborted) {\n throw new AIError(\n 'ModelArk request aborted by caller',\n 'AI_ABORTED',\n 'byteplus-modelark',\n );\n }\n\n const url = new URL(`${this.baseUrl}${path}`);\n for (const [key, value] of Object.entries(options.query ?? {})) {\n url.searchParams.set(key, String(value));\n }\n\n const controller = new AbortController();\n const timeoutMs = options.timeout ?? this.options.timeout;\n let timedOut = false;\n const timer = timeoutMs\n ? setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs)\n : undefined;\n // Mirrors `prepareRequestControls`'s pattern (packages/ai/src/shared/safety.ts):\n // addEventListener on an already-aborted signal never fires, so an\n // already-aborted `signal` must invoke the abort immediately instead of\n // only listening for a future 'abort' event.\n const onExternalAbort = () => controller.abort(options.signal?.reason);\n if (options.signal?.aborted) {\n onExternalAbort();\n } else {\n options.signal?.addEventListener('abort', onExternalAbort, {\n once: true,\n });\n }\n\n try {\n let response: Response;\n try {\n response = await fetch(url, {\n method: options.method || 'GET',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.options.apiKey ?? ''}`,\n ...(this.options.headers || {}),\n },\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n } catch (error) {\n // The fetch() call itself failed (network error, DNS failure,\n // timeout/abort) — wrap it into the AIError taxonomy so callers get\n // a consistent, retryable-vs-terminal-distinguishable error rather\n // than a raw TypeError/DOMException.\n if (timedOut) {\n throw new AIError(\n `ModelArk request to ${path} timed out after ${timeoutMs}ms`,\n 'AI_TIMEOUT',\n 'byteplus-modelark',\n undefined,\n true,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'ModelArk request aborted by caller',\n 'AI_ABORTED',\n 'byteplus-modelark',\n );\n }\n throw new AIError(\n `Network error calling ModelArk ${path}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'NETWORK_ERROR',\n 'byteplus-modelark',\n undefined,\n true,\n );\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n\n if (response.status === 401 || response.status === 403) {\n throw new AuthenticationError('byteplus-modelark');\n }\n if (response.status === 429) {\n throw new RateLimitError(\n 'byteplus-modelark',\n extractRetryAfterSeconds({ headers: response.headers }),\n );\n }\n\n throw new AIError(\n `ModelArk request to ${path} failed: ${response.status} ${errorText}`,\n 'MODELARK_REQUEST_FAILED',\n 'byteplus-modelark',\n );\n }\n\n if (response.status === 204) {\n return {} as T;\n }\n\n const text = await response.text();\n return text ? (JSON.parse(text) as T) : ({} as T);\n } finally {\n if (timer) clearTimeout(timer);\n options.signal?.removeEventListener('abort', onExternalAbort);\n }\n }\n\n private toImageUrl(reference: VideoGenerationReferenceImage): string {\n if (typeof reference.image === 'string') {\n return reference.image;\n }\n const mimeType = reference.mimeType || 'image/png';\n return `data:${mimeType};base64,${reference.image.toString('base64')}`;\n }\n\n // ============================================================================\n // Video Generation Methods (Implemented)\n // ============================================================================\n\n /**\n * Submit an asynchronous Seedance video-generation task.\n *\n * Submission is locally throttled to approximate ModelArk's documented\n * Seedance account limits (QPS 2, capped at {@link SUBMIT_BURST_CAPACITY}\n * burst / 3 concurrent *submissions* — task-lifetime concurrency on\n * ModelArk's infrastructure is not tracked; see {@link SubmitRateLimiter}).\n *\n * When `referenceImages` are supplied and no explicit `model` is chosen,\n * this selects the image-to-video default model rather than silently\n * generating text-to-video with the image ignored.\n */\n async submitVideoGenerationJob(\n options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n const startTime = Date.now();\n await this.submitLimiter.acquire(options.signal);\n\n try {\n const hasReferenceImages = (options.referenceImages?.length ?? 0) > 0;\n const model =\n options.model ||\n this.options.defaultModel ||\n (hasReferenceImages\n ? DEFAULT_MODELARK_I2V_MODEL\n : DEFAULT_MODELARK_MODEL);\n\n const content: ModelArkContentItem[] = [];\n if (options.prompt) {\n content.push({ type: 'text', text: options.prompt });\n }\n for (const reference of options.referenceImages ?? []) {\n content.push({\n type: 'image_url',\n image_url: { url: this.toImageUrl(reference) },\n role: reference.role || 'reference_image',\n });\n }\n\n if (content.length === 0) {\n throw new AIError(\n 'submitVideoGenerationJob requires a prompt or at least one reference image',\n 'INVALID_INPUT',\n 'byteplus-modelark',\n );\n }\n\n const body: ModelArkCreateTaskRequest = {\n model,\n content,\n resolution: options.resolution,\n ratio: options.aspectRatio,\n duration: options.durationSeconds,\n seed: options.seed,\n };\n // Note: ModelArk's documented create-task schema has no negative_prompt\n // or fps fields, so those VideoGenerationOptions are intentionally not\n // forwarded for this provider.\n\n const response = await this.request<ModelArkCreateTaskResponse>(\n '/contents/generations/tasks',\n {\n method: 'POST',\n body,\n signal: options.signal,\n timeout: options.timeout,\n },\n );\n\n emitUsage(\n this.options,\n 'byteplus-modelark',\n 'submitVideoGenerationJob',\n model,\n undefined,\n startTime,\n {\n ...(options.durationSeconds !== undefined\n ? { durationSeconds: String(options.durationSeconds) }\n : {}),\n ...(options.resolution ? { resolution: options.resolution } : {}),\n ...(options.aspectRatio ? { aspectRatio: options.aspectRatio } : {}),\n ...options.usageTags,\n },\n );\n\n return {\n jobId: response.id,\n provider: 'byteplus-modelark',\n model,\n createdAt: new Date().toISOString(),\n };\n } finally {\n this.submitLimiter.release();\n }\n }\n\n /**\n * Poll the status of a ModelArk video-generation task.\n */\n async getVideoGenerationJob(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n const task = await this.request<ModelArkTaskResponse>(\n `/contents/generations/tasks/${handle.jobId}`,\n );\n return this.mapTask(task);\n }\n\n /**\n * Fetch the result of a completed ModelArk video-generation task.\n *\n * @throws {AIError} When the job has not succeeded yet\n */\n async fetchVideoGenerationResult(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n const status = await this.getVideoGenerationJob(handle);\n if (status.status !== 'succeeded' || !status.result) {\n throw new AIError(\n `ModelArk video-generation job ${handle.jobId} has not succeeded (status: ${status.status})`,\n 'VIDEO_JOB_NOT_READY',\n 'byteplus-modelark',\n handle.model,\n );\n }\n return status.result;\n }\n\n /**\n * Cancel a ModelArk video-generation task.\n *\n * ModelArk's DELETE endpoint only transitions a `queued` task to\n * `cancelled`; a task that is already `running` cannot be cancelled and\n * the request fails with an {@link AIError} describing why. Terminal\n * tasks (`succeeded`/`failed`/`expired`) are deleted instead.\n */\n async cancelVideoGenerationJob(handle: VideoGenerationJob): Promise<void> {\n await this.request(`/contents/generations/tasks/${handle.jobId}`, {\n method: 'DELETE',\n });\n }\n\n /**\n * Cheap auth-shaped check for ModelArk access: lists tasks with a page\n * size of 1. Callers on a hot path must cache the result themselves.\n */\n async validateVideoGenerationAccess(): Promise<boolean> {\n await this.request('/contents/generations/tasks', {\n query: { page_size: 1 },\n });\n return true;\n }\n\n private mapTask(task: ModelArkTaskResponse): VideoGenerationStatusResult {\n switch (task.status) {\n case 'queued':\n return { status: 'queued' };\n case 'running':\n return { status: 'running' };\n case 'cancelled':\n return { status: 'cancelled' };\n case 'expired':\n return {\n status: 'failed',\n error: 'ModelArk task expired before the result was retrieved',\n };\n case 'failed':\n return {\n status: 'failed',\n error: task.error?.message || 'ModelArk video generation failed',\n };\n case 'succeeded': {\n if (!task.content?.video_url) {\n return {\n status: 'failed',\n error: 'ModelArk reported success with no video_url',\n };\n }\n return {\n status: 'succeeded',\n result: {\n url: task.content.video_url,\n mimeType: 'video/mp4',\n durationSeconds: task.duration,\n },\n };\n }\n default:\n // Never invent a terminal state for a status this package doesn't\n // recognize (e.g. a future ModelArk status addition) — treat it as\n // still in flight so a caller doesn't misread a mid-render job as\n // failed, resubmit, and double-bill. The raw value is preserved for\n // diagnostics.\n return { status: 'running', rawStatus: task.status };\n }\n }\n\n async getModels(): Promise<AIModel[]> {\n return [\n {\n id: 'seedance-1-0-lite-t2v-250428',\n name: 'Seedance 1.0 Lite (text-to-video)',\n description: 'ModelArk Seedance 1.0 Lite text-to-video model',\n contextLength: 0,\n capabilities: ['video_generation'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'seedance-1-0-lite-i2v-250428',\n name: 'Seedance 1.0 Lite (image-to-video)',\n description: 'ModelArk Seedance 1.0 Lite image-to-video model',\n contextLength: 0,\n capabilities: ['video_generation'],\n supportsFunctions: false,\n supportsVision: true,\n },\n ];\n }\n\n async getCapabilities(): Promise<AICapabilities> {\n return {\n chat: false,\n completion: false,\n embeddings: false,\n streaming: false,\n functions: false,\n vision: false,\n fineTuning: false,\n imageEmbeddings: false,\n imageGeneration: false,\n videoGeneration: true,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 0,\n supportedOperations: [\n 'submitVideoGenerationJob',\n 'getVideoGenerationJob',\n 'fetchVideoGenerationResult',\n 'cancelVideoGenerationJob',\n ],\n };\n }\n\n // ============================================================================\n // Text Generation / TTS Methods (Not Implemented - Use another provider)\n // ============================================================================\n\n async chat(\n _messages: AIMessage[],\n _options?: ChatOptions,\n ): Promise<AIResponse> {\n throw new AIError(\n 'Chat is not supported by the BytePlus ModelArk provider. Use OpenAI, Anthropic, or another provider for text generation.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async complete(\n _prompt: string,\n _options?: CompletionOptions,\n ): Promise<AIResponse> {\n throw new AIError(\n 'Complete is not supported by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async message(_text: string, _options?: MessageOptions): Promise<string> {\n throw new AIError(\n 'Message is not supported by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async embed(\n _text: string | string[],\n _options?: EmbeddingOptions,\n ): Promise<EmbeddingResponse> {\n throw new AIError(\n 'Embeddings are not supported by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async embedImage(\n _image: string | Buffer,\n _options?: ImageEmbeddingOptions,\n ): Promise<EmbeddingResponse> {\n throw new AIError(\n 'Image embeddings are not supported by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async describeImage(\n _image: string | Buffer,\n _prompt?: string,\n _options?: ImageDescriptionOptions,\n ): Promise<string> {\n throw new AIError(\n 'Image description is not supported by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async generateImage(\n _prompt: string,\n _options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n throw new AIError(\n 'Synchronous image generation is not yet implemented by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n stream(\n _messages: AIMessage[],\n _options?: ChatOptions,\n ): AsyncIterable<string> {\n const error = new AIError(\n 'Chat streaming is not supported by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n return {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.reject(error),\n }),\n };\n }\n\n async countTokens(_text: string): Promise<number> {\n throw new AIError(\n 'Token counting is not supported by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n throw new AIError(\n 'TTS is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n return {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.reject(error),\n }),\n };\n }\n\n async cloneVoice(_options: VoiceCloneOptions): Promise<Voice> {\n throw new AIError(\n 'Voice cloning is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n throw new AIError(\n 'Voice design is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n throw new AIError(\n 'Voice listing is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,IAAM,4BACJ;AACF,IAAM,yBAAyB;AAC/B,IAAM,6BAA6B;;AAEnC,IAAM,wBAAwB;;;;;;;AAkD9B,SAAS,aAAa,IAAY,QAAqC;CACrE,OAAO,IAAI,SAAS,YAAY;EAC9B,IAAI,QAAQ,SAAS;GACnB,QAAQ;GACR;EACF;EACA,MAAM,QAAQ,WAAW,SAAS,EAAE;EACpC,QAAQ,iBACN,eACM;GACJ,aAAa,KAAK;GAClB,QAAQ;EACV,GACA,EAAE,MAAM,KAAK,CACf;CACF,CAAC;AACH;;;;;;;;;;;;;;;;;;;;AAqBA,IAAM,oBAAN,MAAwB;CACtB;CACA;CACA;CACA;CACA,iBAAyB;CACzB;CAEA,YAAY,SAAiE;EAC3E,MAAM,oBAAoB,QAAQ,qBAAqB;EACvD,KAAK,aAAa,oBAAoB;EACtC,KAAK,YAAY;EACjB,KAAK,SAAS,KAAK;EACnB,KAAK,aAAa,KAAK,IAAI;EAC3B,KAAK,gBAAgB,QAAQ,iBAAiB;CAChD;;;;;;;;;;;;;;;;CAiBA,MAAM,QAAQ,QAAqC;EACjD,MAAM,uBAAuB;GAC3B,IAAI,QAAQ,SACV,MAAM,IAAI,QACR,4EACA,cACA,mBACF;EAEJ;EAEA,eAAe;EAEf,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,WAAW,MAAM,KAAK,cAAc;EAC1C,KAAK,SAAS,KAAK,IACjB,KAAK,WACL,KAAK,SAAS,UAAU,KAAK,UAC/B;EACA,KAAK,aAAa;EAElB,SAAS;GACP,eAAe;GAEf,IAAI,KAAK,iBAAiB,KAAK,iBAAiB,KAAK,UAAU,GAAG;IAChE,KAAK,UAAU;IACf,KAAK,kBAAkB;IACvB;GACF;GAEA,IAAI,KAAK,kBAAkB,KAAK,eAC9B,MAAM,aAAa,KAAK,MAAM;QACzB;IAKL,MAAM,aAJW,KAAK,IACpB,IACE,IAAI,KAAK,UAAU,KAAK,aAAc,GAEvB,GAAU,MAAM;IACnC,MAAM,cAAc,KAAK,IAAI,IAAI,KAAK,cAAc;IACpD,KAAK,SAAS,KAAK,IACjB,KAAK,WACL,KAAK,SAAS,aAAa,KAAK,UAClC;IACA,KAAK,aAAa,KAAK,IAAI;GAC7B;EACF;CACF;CAEA,UAAgB;EACd,KAAK,iBAAiB,KAAK,IAAI,GAAG,KAAK,iBAAiB,CAAC;CAC3D;AACF;;;;;;;AAQA,SAAS,QAAQ,OAAuB;CACtC,IAAI,OAAO;CACX,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACpD,QAAQ,MAAM,WAAW,KAAK;EAC9B,OAAO,KAAK,KAAK,MAAM,QAAQ;CACjC;CACA,QAAQ,SAAS,EAAA,CAAG,SAAS,EAAE;AACjC;;;;;;;;;;;;;AAcA,IAAM,uCAAuB,IAAI,IAA+B;AAEhE,SAAS,uBACP,SACmB;CACnB,MAAM,MACJ,QAAQ,WAAW,KAAK,KAAK,KAC7B,qBAAqB,QAAQ,QAAQ,UAAU,SAAS;CAE1D,IAAI,UAAU,qBAAqB,IAAI,GAAG;CAC1C,IAAI,CAAC,SAAS;EACZ,UAAU,IAAI,kBAAkB;GAC9B,mBAAmB,QAAQ,WAAW;GACtC,eAAe,QAAQ,WAAW;EACpC,CAAC;EACD,qBAAqB,IAAI,KAAK,OAAO;CACvC;CACA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAa,2BAAb,MAA6D;CAC3D;CACA;CAEA,YAAY,SAAkC;EAK5C,KAAK,UAAU,uBAAuB,EAAE,GAAG,QAAQ,CAAC;EAEpD,KAAK,gBAAgB,uBAAuB,KAAK,OAAO;CAC1D;CAEA,IAAY,UAAkB;EAC5B,QAAQ,KAAK,QAAQ,WAAW,0BAAA,CAA2B,QACzD,QACA,EACF;CACF;CAEA,MAAc,QACZ,MACA,UAMI,CAAC,GACO;EACZ,IAAI,QAAQ,QAAQ,SAClB,MAAM,IAAI,QACR,sCACA,cACA,mBACF;EAGF,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,UAAU,MAAM;EAC5C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,SAAS,CAAC,CAAC,GAC3D,IAAI,aAAa,IAAI,KAAK,OAAO,KAAK,CAAC;EAGzC,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,YAAY,QAAQ,WAAW,KAAK,QAAQ;EAClD,IAAI,WAAW;EACf,MAAM,QAAQ,YACV,iBAAiB;GACf,WAAW;GACX,WAAW,MAAM;EACnB,GAAG,SAAS,IACZ,KAAA;EAKJ,MAAM,wBAAwB,WAAW,MAAM,QAAQ,QAAQ,MAAM;EACrE,IAAI,QAAQ,QAAQ,SAClB,gBAAgB;OAEhB,QAAQ,QAAQ,iBAAiB,SAAS,iBAAiB,EACzD,MAAM,KACR,CAAC;EAGH,IAAI;GACF,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,MAAM,KAAK;KAC1B,QAAQ,QAAQ,UAAU;KAC1B,SAAS;MACP,gBAAgB;MAChB,eAAe,UAAU,KAAK,QAAQ,UAAU;MAChD,GAAI,KAAK,QAAQ,WAAW,CAAC;KAC/B;KACA,MAAM,QAAQ,OAAO,KAAK,UAAU,QAAQ,IAAI,IAAI,KAAA;KACpD,QAAQ,WAAW;IACrB,CAAC;GACH,SAAS,OAAO;IAKd,IAAI,UACF,MAAM,IAAI,QACR,uBAAuB,KAAK,mBAAmB,UAAU,KACzD,cACA,qBACA,KAAA,GACA,IACF;IAEF,IAAI,QAAQ,QAAQ,SAClB,MAAM,IAAI,QACR,sCACA,cACA,mBACF;IAEF,MAAM,IAAI,QACR,kCAAkC,KAAK,IACrC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAEvD,iBACA,qBACA,KAAA,GACA,IACF;GACF;GAEA,IAAI,CAAC,SAAS,IAAI;IAChB,MAAM,YAAY,MAAM,SAAS,KAAK;IAEtC,IAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KACjD,MAAM,IAAI,oBAAoB,mBAAmB;IAEnD,IAAI,SAAS,WAAW,KACtB,MAAM,IAAI,eACR,qBACA,yBAAyB,EAAE,SAAS,SAAS,QAAQ,CAAC,CACxD;IAGF,MAAM,IAAI,QACR,uBAAuB,KAAK,WAAW,SAAS,OAAO,GAAG,aAC1D,2BACA,mBACF;GACF;GAEA,IAAI,SAAS,WAAW,KACtB,OAAO,CAAC;GAGV,MAAM,OAAO,MAAM,SAAS,KAAK;GACjC,OAAO,OAAQ,KAAK,MAAM,IAAI,IAAW,CAAC;EAC5C,UAAU;GACR,IAAI,OAAO,aAAa,KAAK;GAC7B,QAAQ,QAAQ,oBAAoB,SAAS,eAAe;EAC9D;CACF;CAEA,WAAmB,WAAkD;EACnE,IAAI,OAAO,UAAU,UAAU,UAC7B,OAAO,UAAU;EAGnB,OAAO,QADU,UAAU,YAAY,YACf,UAAU,UAAU,MAAM,SAAS,QAAQ;CACrE;;;;;;;;;;;;;CAkBA,MAAM,yBACJ,SAC6B;EAC7B,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,KAAK,cAAc,QAAQ,QAAQ,MAAM;EAE/C,IAAI;GACF,MAAM,sBAAsB,QAAQ,iBAAiB,UAAU,KAAK;GACpE,MAAM,QACJ,QAAQ,SACR,KAAK,QAAQ,iBACZ,qBACG,6BACA;GAEN,MAAM,UAAiC,CAAC;GACxC,IAAI,QAAQ,QACV,QAAQ,KAAK;IAAE,MAAM;IAAQ,MAAM,QAAQ;GAAO,CAAC;GAErD,KAAK,MAAM,aAAa,QAAQ,mBAAmB,CAAC,GAClD,QAAQ,KAAK;IACX,MAAM;IACN,WAAW,EAAE,KAAK,KAAK,WAAW,SAAS,EAAE;IAC7C,MAAM,UAAU,QAAQ;GAC1B,CAAC;GAGH,IAAI,QAAQ,WAAW,GACrB,MAAM,IAAI,QACR,8EACA,iBACA,mBACF;GAGF,MAAM,OAAkC;IACtC;IACA;IACA,YAAY,QAAQ;IACpB,OAAO,QAAQ;IACf,UAAU,QAAQ;IAClB,MAAM,QAAQ;GAChB;GAKA,MAAM,WAAW,MAAM,KAAK,QAC1B,+BACA;IACE,QAAQ;IACR;IACA,QAAQ,QAAQ;IAChB,SAAS,QAAQ;GACnB,CACF;GAEA,UACE,KAAK,SACL,qBACA,4BACA,OACA,KAAA,GACA,WACA;IACE,GAAI,QAAQ,oBAAoB,KAAA,IAC5B,EAAE,iBAAiB,OAAO,QAAQ,eAAe,EAAE,IACnD,CAAC;IACL,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;IAC/D,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;IAClE,GAAG,QAAQ;GACb,CACF;GAEA,OAAO;IACL,OAAO,SAAS;IAChB,UAAU;IACV;IACA,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;GACpC;EACF,UAAU;GACR,KAAK,cAAc,QAAQ;EAC7B;CACF;;;;CAKA,MAAM,sBACJ,QACsC;EACtC,MAAM,OAAO,MAAM,KAAK,QACtB,+BAA+B,OAAO,OACxC;EACA,OAAO,KAAK,QAAQ,IAAI;CAC1B;;;;;;CAOA,MAAM,2BACJ,QACgC;EAChC,MAAM,SAAS,MAAM,KAAK,sBAAsB,MAAM;EACtD,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,QAC3C,MAAM,IAAI,QACR,iCAAiC,OAAO,MAAM,8BAA8B,OAAO,OAAO,IAC1F,uBACA,qBACA,OAAO,KACT;EAEF,OAAO,OAAO;CAChB;;;;;;;;;CAUA,MAAM,yBAAyB,QAA2C;EACxE,MAAM,KAAK,QAAQ,+BAA+B,OAAO,SAAS,EAChE,QAAQ,SACV,CAAC;CACH;;;;;CAMA,MAAM,gCAAkD;EACtD,MAAM,KAAK,QAAQ,+BAA+B,EAChD,OAAO,EAAE,WAAW,EAAE,EACxB,CAAC;EACD,OAAO;CACT;CAEA,QAAgB,MAAyD;EACvE,QAAQ,KAAK,QAAb;GACE,KAAK,UACH,OAAO,EAAE,QAAQ,SAAS;GAC5B,KAAK,WACH,OAAO,EAAE,QAAQ,UAAU;GAC7B,KAAK,aACH,OAAO,EAAE,QAAQ,YAAY;GAC/B,KAAK,WACH,OAAO;IACL,QAAQ;IACR,OAAO;GACT;GACF,KAAK,UACH,OAAO;IACL,QAAQ;IACR,OAAO,KAAK,OAAO,WAAW;GAChC;GACF,KAAK;IACH,IAAI,CAAC,KAAK,SAAS,WACjB,OAAO;KACL,QAAQ;KACR,OAAO;IACT;IAEF,OAAO;KACL,QAAQ;KACR,QAAQ;MACN,KAAK,KAAK,QAAQ;MAClB,UAAU;MACV,iBAAiB,KAAK;KACxB;IACF;GAEF,SAME,OAAO;IAAE,QAAQ;IAAW,WAAW,KAAK;GAAO;EACvD;CACF;CAEA,MAAM,YAAgC;EACpC,OAAO,CACL;GACE,IAAI;GACJ,MAAM;GACN,aAAa;GACb,eAAe;GACf,cAAc,CAAC,kBAAkB;GACjC,mBAAmB;GACnB,gBAAgB;EAClB,GACA;GACE,IAAI;GACJ,MAAM;GACN,aAAa;GACb,eAAe;GACf,cAAc,CAAC,kBAAkB;GACjC,mBAAmB;GACnB,gBAAgB;EAClB,CACF;CACF;CAEA,MAAM,kBAA2C;EAC/C,OAAO;GACL,MAAM;GACN,YAAY;GACZ,YAAY;GACZ,WAAW;GACX,WAAW;GACX,QAAQ;GACR,YAAY;GACZ,iBAAiB;GACjB,iBAAiB;GACjB,iBAAiB;GACjB,KAAK;GACL,cAAc;GACd,aAAa;GACb,kBAAkB;GAClB,qBAAqB;IACnB;IACA;IACA;IACA;GACF;EACF;CACF;CAMA,MAAM,KACJ,WACA,UACqB;EACrB,MAAM,IAAI,QACR,4HACA,mBACA,mBACF;CACF;CAEA,MAAM,SACJ,SACA,UACqB;EACrB,MAAM,IAAI,QACR,gEACA,mBACA,mBACF;CACF;CAEA,MAAM,QAAQ,OAAe,UAA4C;EACvE,MAAM,IAAI,QACR,+DACA,mBACA,mBACF;CACF;CAEA,MAAM,MACJ,OACA,UAC4B;EAC5B,MAAM,IAAI,QACR,mEACA,mBACA,mBACF;CACF;CAEA,MAAM,WACJ,QACA,UAC4B;EAC5B,MAAM,IAAI,QACR,yEACA,mBACA,mBACF;CACF;CAEA,MAAM,cACJ,QACA,SACA,UACiB;EACjB,MAAM,IAAI,QACR,yEACA,mBACA,mBACF;CACF;CAEA,MAAM,cACJ,SACA,UACkC;EAClC,MAAM,IAAI,QACR,0FACA,mBACA,mBACF;CACF;CAEA,OACE,WACA,UACuB;EACvB,MAAM,QAAQ,IAAI,QAChB,sEACA,mBACA,mBACF;EACA,OAAO,GACJ,OAAO,uBAAuB,EAC7B,YAAY,QAAQ,OAAO,KAAK,EAClC,GACF;CACF;CAEA,MAAM,YAAY,OAAgC;EAChD,MAAM,IAAI,QACR,sEACA,mBACA,mBACF;CACF;CAEA,MAAM,iBACJ,OACA,UACsB;EACtB,MAAM,IAAI,QACR,mFACA,mBACA,mBACF;CACF;CAEA,aAAa,OAAe,UAA8C;EACxE,MAAM,QAAQ,IAAI,QAChB,6FACA,mBACA,mBACF;EACA,OAAO,GACJ,OAAO,uBAAuB,EAC7B,YAAY,QAAQ,OAAO,KAAK,EAClC,GACF;CACF;CAEA,MAAM,WAAW,UAA6C;EAC5D,MAAM,IAAI,QACR,6FACA,mBACA,mBACF;CACF;CAEA,MAAM,YAAY,UAA8C;EAC9D,MAAM,IAAI,QACR,4FACA,mBACA,mBACF;CACF;CAEA,MAAM,UAAU,UAA+C;EAC7D,MAAM,IAAI,QACR,6FACA,mBACA,mBACF;CACF;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ContextLengthError, c as extractTextContent, r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-
|
|
1
|
+
import { a as ContextLengthError, c as extractTextContent, r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-CINwUPDo.js";
|
|
2
2
|
import { t as emitUsage } from "./usage-C1Y1Nlg4.js";
|
|
3
3
|
import { exec, spawn } from "node:child_process";
|
|
4
4
|
import { promisify } from "node:util";
|
|
@@ -47,7 +47,7 @@ var ClaudeCliProvider = class {
|
|
|
47
47
|
if (this.anthropicFallback !== null) return;
|
|
48
48
|
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
49
49
|
if (!apiKey) return;
|
|
50
|
-
const { AnthropicProvider } = await import("./anthropic-
|
|
50
|
+
const { AnthropicProvider } = await import("./anthropic-DuYUnw8j.js");
|
|
51
51
|
const defaultModel = {
|
|
52
52
|
sonnet: "claude-3-5-sonnet-20241022",
|
|
53
53
|
opus: "claude-3-opus-20240229",
|
|
@@ -464,6 +464,7 @@ var ClaudeCliProvider = class {
|
|
|
464
464
|
fineTuning: false,
|
|
465
465
|
imageEmbeddings: false,
|
|
466
466
|
imageGeneration: false,
|
|
467
|
+
videoGeneration: false,
|
|
467
468
|
tts: false,
|
|
468
469
|
voiceCloning: false,
|
|
469
470
|
voiceDesign: false,
|
|
@@ -475,6 +476,21 @@ var ClaudeCliProvider = class {
|
|
|
475
476
|
]
|
|
476
477
|
};
|
|
477
478
|
}
|
|
479
|
+
async submitVideoGenerationJob(_options) {
|
|
480
|
+
throw new AIError("Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.", "NOT_IMPLEMENTED", "claude-cli");
|
|
481
|
+
}
|
|
482
|
+
async getVideoGenerationJob(_handle) {
|
|
483
|
+
throw new AIError("Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.", "NOT_IMPLEMENTED", "claude-cli");
|
|
484
|
+
}
|
|
485
|
+
async fetchVideoGenerationResult(_handle) {
|
|
486
|
+
throw new AIError("Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.", "NOT_IMPLEMENTED", "claude-cli");
|
|
487
|
+
}
|
|
488
|
+
async cancelVideoGenerationJob(_handle) {
|
|
489
|
+
throw new AIError("Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.", "NOT_IMPLEMENTED", "claude-cli");
|
|
490
|
+
}
|
|
491
|
+
async validateVideoGenerationAccess() {
|
|
492
|
+
throw new AIError("Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.", "NOT_IMPLEMENTED", "claude-cli");
|
|
493
|
+
}
|
|
478
494
|
async synthesizeSpeech(_text, _options) {
|
|
479
495
|
throw new AIError("TTS is not supported by Claude CLI provider. Use Qwen3-TTS provider.", "NOT_IMPLEMENTED", "claude-cli");
|
|
480
496
|
}
|
|
@@ -495,4 +511,4 @@ var ClaudeCliProvider = class {
|
|
|
495
511
|
//#endregion
|
|
496
512
|
export { ClaudeCliProvider };
|
|
497
513
|
|
|
498
|
-
//# sourceMappingURL=claude-cli-
|
|
514
|
+
//# sourceMappingURL=claude-cli-DeTKk9nu.js.map
|