@neta-art/generation 0.1.1 → 0.1.3

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.
@@ -0,0 +1,1263 @@
1
+ import { a as compactArray, c as slugifyFileName, i as cloneJson, l as MODEL_SCHEMA, n as getBuiltinGenerationModel, o as compactObject, r as listBuiltinGenerationModels, s as getBlockMeta, t as builtinGenerationModels } from "./builtins-FumzmWvj.js";
2
+ import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
3
+ import { extname, join } from "node:path";
4
+ import { parse, stringify } from "yaml";
5
+
6
+ //#region src/errors.ts
7
+ var GenerationError = class extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = "GenerationError";
11
+ }
12
+ };
13
+ var GenerationConfigError = class extends GenerationError {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "GenerationConfigError";
17
+ }
18
+ };
19
+ var GenerationValidationError = class extends GenerationError {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "GenerationValidationError";
23
+ }
24
+ };
25
+ var GenerationUnsupportedAdapterError = class extends GenerationError {
26
+ constructor(adapterType) {
27
+ super(`Unsupported generation adapter: ${adapterType}`);
28
+ this.name = "GenerationUnsupportedAdapterError";
29
+ }
30
+ };
31
+ var GenerationProviderError = class extends GenerationError {
32
+ status;
33
+ body;
34
+ details;
35
+ constructor(message = "Generation provider request failed", options) {
36
+ super(message);
37
+ this.name = "GenerationProviderError";
38
+ this.status = options?.status;
39
+ this.body = options?.body;
40
+ this.details = options?.details;
41
+ }
42
+ };
43
+ var GenerationTimeoutError = class extends GenerationProviderError {
44
+ constructor(message = "Generation request timed out", details) {
45
+ super(message, details ? { details } : void 0);
46
+ this.name = "GenerationTimeoutError";
47
+ }
48
+ };
49
+
50
+ //#endregion
51
+ //#region src/http.ts
52
+ function headersToRecord(headers) {
53
+ if (!headers) return {};
54
+ if (headers instanceof Headers) {
55
+ const output = {};
56
+ headers.forEach((value, key) => {
57
+ output[key] = value;
58
+ });
59
+ return output;
60
+ }
61
+ if (Array.isArray(headers)) return Object.fromEntries(headers.map(([key, value]) => [key, value]));
62
+ return { ...headers };
63
+ }
64
+ function parseJsonBody(body) {
65
+ try {
66
+ return JSON.parse(body);
67
+ } catch {
68
+ return body;
69
+ }
70
+ }
71
+ function parseDebugBody(body) {
72
+ if (typeof body !== "string") return body ? "[non-string body]" : void 0;
73
+ return parseJsonBody(body);
74
+ }
75
+ const TRACE_HEADER_PATTERN = /(^|[-_])(request|trace|span|correlation|cf-ray|x-tt-logid)([-_]|$)|^(x-request-id|x-trace-id|x-correlation-id|traceparent|tracestate|cf-ray|server-timing)$/i;
76
+ function pickTraceHeaders(headers) {
77
+ const output = {};
78
+ for (const [key, value] of Object.entries(headers)) if (TRACE_HEADER_PATTERN.test(key)) output[key] = value;
79
+ return output;
80
+ }
81
+ function emitDebugRequest(debug, url, init) {
82
+ debug.logger({
83
+ type: "request",
84
+ url,
85
+ method: init.method ?? "GET",
86
+ headers: headersToRecord(init.headers),
87
+ body: parseDebugBody(init.body)
88
+ });
89
+ return Date.now();
90
+ }
91
+ async function readDebugResponseBody(response) {
92
+ const contentType = response.headers.get("content-type") ?? "";
93
+ if (contentType.includes("application/json") || contentType.includes("+json")) return parseJsonBody(await response.clone().text());
94
+ if (contentType.startsWith("text/")) return response.clone().text();
95
+ return "[non-text body]";
96
+ }
97
+ async function emitDebugResponse(debug, url, response, startedAt) {
98
+ const headers = headersToRecord(response.headers);
99
+ debug.logger({
100
+ type: "response",
101
+ url,
102
+ status: response.status,
103
+ statusText: response.statusText,
104
+ headers,
105
+ trace: pickTraceHeaders(headers),
106
+ elapsedMs: Date.now() - startedAt,
107
+ ...debug.includeResponseBody ? { body: await readDebugResponseBody(response) } : {}
108
+ });
109
+ }
110
+ function createDebugFetch(fetchFn, debug) {
111
+ return (async (input, init) => {
112
+ const url = String(input instanceof Request ? input.url : input);
113
+ const startedAt = emitDebugRequest(debug, url, init ?? (input instanceof Request ? input : {}));
114
+ const response = await fetchFn(input, init);
115
+ await emitDebugResponse(debug, url, response, startedAt);
116
+ return response;
117
+ });
118
+ }
119
+ async function fetchWithTimeout(fetchFn, url, init, timeoutMs) {
120
+ const controller = new AbortController();
121
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
122
+ try {
123
+ return await fetchFn(url, {
124
+ ...init,
125
+ signal: controller.signal
126
+ });
127
+ } catch (error) {
128
+ if (error instanceof Error && error.name === "AbortError") throw new GenerationTimeoutError();
129
+ throw error;
130
+ } finally {
131
+ clearTimeout(timeout);
132
+ }
133
+ }
134
+ function joinUrl(baseUrl, path) {
135
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
136
+ }
137
+
138
+ //#endregion
139
+ //#region src/validation.ts
140
+ function specsByType(specs) {
141
+ const map = /* @__PURE__ */ new Map();
142
+ for (const spec of specs) map.set(spec.type, spec);
143
+ return map;
144
+ }
145
+ function validateGenerationContent(declaration, content) {
146
+ const inputSpecs = specsByType(declaration.content.input);
147
+ const counts = /* @__PURE__ */ new Map();
148
+ for (const block of content) {
149
+ const spec = inputSpecs.get(block.type);
150
+ if (!spec) throw new GenerationValidationError(`Content block type is not supported by ${declaration.model}: ${block.type}`);
151
+ counts.set(block.type, (counts.get(block.type) ?? 0) + 1);
152
+ if ("source" in block && spec.sources && !spec.sources.includes(block.source.type)) throw new GenerationValidationError(`${block.type} source is not supported by ${declaration.model}: ${block.source.type}`);
153
+ }
154
+ for (const spec of declaration.content.input) {
155
+ const count = counts.get(spec.type) ?? 0;
156
+ if (spec.required && count === 0) throw new GenerationValidationError(`Missing required ${spec.type} content block`);
157
+ if (spec.min !== void 0 && count < spec.min) throw new GenerationValidationError(`Expected at least ${spec.min} ${spec.type} content block(s)`);
158
+ if (spec.max !== void 0 && count > spec.max) throw new GenerationValidationError(`Expected at most ${spec.max} ${spec.type} content block(s)`);
159
+ }
160
+ }
161
+ function resolveGenerationParameters(declaration, parameters) {
162
+ const specs = declaration.parameters ?? {};
163
+ const resolved = {};
164
+ const allowUnknownParameters = declaration.allowUnknownParameters ?? false;
165
+ for (const key of Object.keys(parameters ?? {})) if (!specs[key]) {
166
+ if (!allowUnknownParameters) throw new GenerationValidationError(`Unknown parameter: ${key}`);
167
+ const value = parameters?.[key];
168
+ if (value !== void 0) resolved[key] = value;
169
+ }
170
+ for (const [key, spec] of Object.entries(specs)) {
171
+ const value = parameters?.[key];
172
+ if (value === void 0) {
173
+ if (spec.default !== void 0) resolved[key] = spec.default;
174
+ else if (!spec.optional) throw new GenerationValidationError(`Missing required parameter: ${key}`);
175
+ continue;
176
+ }
177
+ validateSpecValue(`Parameter ${key}`, spec, value);
178
+ resolved[key] = value;
179
+ }
180
+ return resolved;
181
+ }
182
+ function mergeGenerationMeta(requestMeta, content) {
183
+ const merged = {};
184
+ mergeMetaFields(merged, requestMeta);
185
+ for (const block of content) mergeMetaFields(merged, block.meta);
186
+ return merged;
187
+ }
188
+ function normalizeMetaKey(key) {
189
+ return key === "metadataParams" ? "metadata_params" : key;
190
+ }
191
+ function mergeMetaFields(target, meta) {
192
+ for (const [key, value] of Object.entries(meta ?? {})) {
193
+ const normalizedKey = normalizeMetaKey(key);
194
+ if (value !== void 0 && target[normalizedKey] === void 0) target[normalizedKey] = value;
195
+ }
196
+ }
197
+ function validateSpecValue(label, spec, value) {
198
+ switch (spec.type) {
199
+ case "string":
200
+ if (typeof value !== "string") throw new GenerationValidationError(`${label} must be a string`);
201
+ if (spec.enum && !spec.enum.includes(value)) throw new GenerationValidationError(`${label} must be one of: ${spec.enum.join(", ")}`);
202
+ break;
203
+ case "number":
204
+ if (typeof value !== "number" || !Number.isFinite(value)) throw new GenerationValidationError(`${label} must be a number`);
205
+ if (spec.min !== void 0 && value < spec.min) throw new GenerationValidationError(`${label} must be >= ${spec.min}`);
206
+ if (spec.max !== void 0 && value > spec.max) throw new GenerationValidationError(`${label} must be <= ${spec.max}`);
207
+ break;
208
+ case "integer":
209
+ if (typeof value !== "number" || !Number.isInteger(value)) throw new GenerationValidationError(`${label} must be an integer`);
210
+ if (spec.min !== void 0 && value < spec.min) throw new GenerationValidationError(`${label} must be >= ${spec.min}`);
211
+ if (spec.max !== void 0 && value > spec.max) throw new GenerationValidationError(`${label} must be <= ${spec.max}`);
212
+ break;
213
+ case "boolean":
214
+ if (typeof value !== "boolean") throw new GenerationValidationError(`${label} must be a boolean`);
215
+ break;
216
+ case "object":
217
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new GenerationValidationError(`${label} must be an object`);
218
+ break;
219
+ }
220
+ }
221
+ function resolveGenerationMeta(declaration, meta, content) {
222
+ const specs = declaration.meta?.fields ?? {};
223
+ const resolved = {};
224
+ for (const key of Object.keys(meta ?? {})) if (!specs[key]) {
225
+ const value = meta?.[key];
226
+ if (value !== void 0) resolved[key] = value;
227
+ }
228
+ for (const [key, spec] of Object.entries(specs)) {
229
+ const value = meta?.[key];
230
+ if (value === void 0) {
231
+ if (!spec.optional) throw new GenerationValidationError(`Missing required meta: ${key}`);
232
+ continue;
233
+ }
234
+ validateSpecValue(`meta.${key}`, spec, value);
235
+ resolved[key] = value;
236
+ }
237
+ const taskField = declaration.meta?.taskField;
238
+ const task = taskField && typeof resolved[taskField] === "string" ? resolved[taskField] : void 0;
239
+ if (!task) return resolved;
240
+ const variant = declaration.meta?.taskVariants?.[task];
241
+ if (!variant) throw new GenerationValidationError(`Unsupported meta.${taskField}: ${task}`);
242
+ for (const key of variant.required ?? []) if (resolved[key] === void 0 || resolved[key] === null || resolved[key] === "") throw new GenerationValidationError(`meta.${taskField} ${task} requires meta.${key}`);
243
+ for (const type of variant.requiredContent ?? []) if (!content.some((block) => block.type === type)) throw new GenerationValidationError(`meta.${taskField} ${task} requires ${type} content`);
244
+ return resolved;
245
+ }
246
+ function mergeTextBlocks(declaration, content) {
247
+ const textSpec = declaration.content.input.find((spec) => spec.type === "text");
248
+ const separator = textSpec?.merge === "space" ? " " : textSpec?.merge === "concat" ? "" : "\n";
249
+ return content.filter((block) => block.type === "text").map((block) => block.text).join(separator).trim();
250
+ }
251
+
252
+ //#endregion
253
+ //#region src/adapters/ark-video-generations.ts
254
+ const REQUEST_TIMEOUT_MS$3 = 6e4;
255
+ const DEFAULT_POLL_INTERVAL_SEC$1 = 2;
256
+ const DEFAULT_MAX_WAIT_SEC$1 = 600;
257
+ const RESOLUTION_SHORT_EDGE = {
258
+ "480p": 480,
259
+ "720p": 720,
260
+ "1080p": 1080,
261
+ "2K": 1440
262
+ };
263
+ const ASPECT_RATIOS = {
264
+ "16:9": [16, 9],
265
+ "9:16": [9, 16],
266
+ "1:1": [1, 1],
267
+ "4:3": [4, 3],
268
+ "3:2": [3, 2],
269
+ "2:3": [2, 3],
270
+ "3:4": [3, 4],
271
+ "21:9": [21, 9],
272
+ adaptive: null
273
+ };
274
+ function sleep$1(ms) {
275
+ return new Promise((resolve) => setTimeout(resolve, ms));
276
+ }
277
+ function asString$1(value) {
278
+ return typeof value === "string" && value ? value : void 0;
279
+ }
280
+ function asNumber(value) {
281
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
282
+ }
283
+ function asBoolean(value) {
284
+ return typeof value === "boolean" ? value : void 0;
285
+ }
286
+ function normalizeStatus$1(value) {
287
+ const status = value.toLowerCase();
288
+ return status === "success" ? "succeeded" : status;
289
+ }
290
+ function getIntegerParameter(parameters, key, fallback) {
291
+ const value = parameters[key];
292
+ return typeof value === "number" && Number.isInteger(value) ? value : fallback;
293
+ }
294
+ function resolveSize(resolution, aspectRatio) {
295
+ const ratio = ASPECT_RATIOS[aspectRatio];
296
+ if (!ratio) return null;
297
+ const shortEdge = RESOLUTION_SHORT_EDGE[resolution];
298
+ if (!shortEdge) throw new GenerationValidationError(`Unsupported resolution: ${resolution}`);
299
+ const [wRatio, hRatio] = ratio;
300
+ const width = wRatio >= hRatio ? Math.round(shortEdge * wRatio / hRatio) : shortEdge;
301
+ const height = wRatio >= hRatio ? shortEdge : Math.round(shortEdge * hRatio / wRatio);
302
+ return {
303
+ width: width % 2 === 0 ? width : width + 1,
304
+ height: height % 2 === 0 ? height : height + 1
305
+ };
306
+ }
307
+ function getImageRole(block) {
308
+ const role = getBlockMeta(block)?.role;
309
+ return typeof role === "string" && role ? role : void 0;
310
+ }
311
+ function classifyImages(images) {
312
+ if (images.length === 0) return null;
313
+ const hasFirstOrLast = images.some((image) => image.role === "first_frame" || image.role === "last_frame");
314
+ const hasReference = images.some((image) => image.role === "reference_image");
315
+ if ([
316
+ images.some((image) => !image.role),
317
+ hasFirstOrLast,
318
+ hasReference
319
+ ].filter(Boolean).length > 1) throw new GenerationValidationError("Cannot mix video image modes: use only plain image, first_frame/last_frame, or reference_image");
320
+ if (hasReference) return "reference";
321
+ if (hasFirstOrLast) return "frame";
322
+ return "image";
323
+ }
324
+ function buildMetadataContent(prompt, images, mode) {
325
+ const content = [{
326
+ type: "text",
327
+ text: prompt
328
+ }];
329
+ for (const image of images) {
330
+ if (mode === "frame" && image.role !== "first_frame" && image.role !== "last_frame") throw new GenerationValidationError("Frame mode images must use meta.role first_frame or last_frame");
331
+ if (mode === "reference" && image.role !== "reference_image") throw new GenerationValidationError("Reference mode images must use meta.role reference_image");
332
+ content.push({
333
+ type: "image_url",
334
+ image_url: { url: image.url },
335
+ role: image.role
336
+ });
337
+ }
338
+ return content;
339
+ }
340
+ function extractTaskId$1(response) {
341
+ const taskId = asString$1(response.task_id) ?? asString$1(response.id);
342
+ if (!taskId) throw new GenerationProviderError("Video generation provider did not return a task id", { details: { response } });
343
+ return taskId;
344
+ }
345
+ function normalizeTaskStatus(response) {
346
+ if (response.data) {
347
+ const wrapper = response.data;
348
+ const native = wrapper.data;
349
+ const status = normalizeStatus$1(asString$1(native?.status) ?? asString$1(wrapper.status) ?? "unknown");
350
+ const videoUrl = asString$1(wrapper.result_url) ?? asString$1(native?.content?.video_url);
351
+ const lastFrameUrl = asString$1(wrapper.first_frame) ?? asString$1(native?.content?.first_frame);
352
+ const metadata = {
353
+ progress: wrapper.progress,
354
+ resolution: native?.resolution,
355
+ ratio: native?.ratio,
356
+ duration: native?.duration,
357
+ framespersecond: native?.framespersecond,
358
+ seed: native?.seed,
359
+ generate_audio: native?.generate_audio,
360
+ model: native?.model,
361
+ usage: native?.usage
362
+ };
363
+ for (const key of Object.keys(metadata)) if (metadata[key] === void 0) delete metadata[key];
364
+ return {
365
+ status,
366
+ videoUrl,
367
+ lastFrameUrl,
368
+ metadata
369
+ };
370
+ }
371
+ return {
372
+ status: "unknown",
373
+ videoUrl: void 0,
374
+ lastFrameUrl: void 0,
375
+ metadata: {}
376
+ };
377
+ }
378
+ async function requestJson$1(input, path, init) {
379
+ const response = await fetchWithTimeout(input.context.fetch, joinUrl(input.context.baseUrl, path), {
380
+ ...init,
381
+ headers: {
382
+ Authorization: `Bearer ${input.context.apiKey}`,
383
+ "Content-Type": "application/json",
384
+ ...init.headers
385
+ }
386
+ }, REQUEST_TIMEOUT_MS$3);
387
+ if (!response.ok) {
388
+ const body = await response.text().catch(() => response.statusText);
389
+ throw new GenerationProviderError("Video generation provider request failed", {
390
+ status: response.status,
391
+ body
392
+ });
393
+ }
394
+ return response.json();
395
+ }
396
+ async function arkVideoGenerationsAdapter(input) {
397
+ const prompt = mergeTextBlocks(input.declaration, input.request.content);
398
+ if (!prompt) throw new GenerationValidationError("Prompt text is required");
399
+ const imageBlocks = input.request.content.filter((block) => block.type === "image");
400
+ const images = await Promise.all(imageBlocks.map(async (block) => ({
401
+ url: await input.context.resolveSource(block.source),
402
+ role: getImageRole(block)
403
+ })));
404
+ const mode = classifyImages(images);
405
+ const resolution = asString$1(input.parameters.resolution) ?? "720p";
406
+ const aspectRatio = asString$1(input.parameters.aspect_ratio) ?? "16:9";
407
+ const duration = getIntegerParameter(input.parameters, "duration", 5);
408
+ const fps = getIntegerParameter(input.parameters, "fps", 30);
409
+ const pollIntervalSec = getIntegerParameter(input.parameters, "poll_interval", DEFAULT_POLL_INTERVAL_SEC$1);
410
+ const maxWaitSec = getIntegerParameter(input.parameters, "max_wait", DEFAULT_MAX_WAIT_SEC$1);
411
+ const generateAudio = asBoolean(input.parameters.generate_audio) ?? true;
412
+ const returnLastFrame = asBoolean(input.parameters.return_last_frame) ?? true;
413
+ const cameraFixed = asBoolean(input.parameters.camera_fixed) ?? false;
414
+ const watermark = asBoolean(input.parameters.watermark) ?? false;
415
+ const seed = asNumber(input.parameters.seed);
416
+ const payload = {
417
+ model: input.declaration.model,
418
+ prompt
419
+ };
420
+ const metadata = {
421
+ duration,
422
+ fps,
423
+ generate_audio: generateAudio
424
+ };
425
+ if (seed !== void 0) metadata.seed = seed;
426
+ if (returnLastFrame) metadata.return_last_frame = true;
427
+ if (cameraFixed) metadata.camera_fixed = true;
428
+ if (watermark) metadata.watermark = true;
429
+ if (mode === "frame" || mode === "reference") {
430
+ metadata.content = buildMetadataContent(prompt, images, mode);
431
+ metadata.resolution = resolution;
432
+ metadata.ratio = aspectRatio;
433
+ } else {
434
+ const size = resolveSize(resolution, aspectRatio);
435
+ if (size) {
436
+ payload.width = size.width;
437
+ payload.height = size.height;
438
+ }
439
+ if (images[0]) payload.image = images[0].url;
440
+ }
441
+ payload.metadata = metadata;
442
+ const taskId = extractTaskId$1(await requestJson$1(input, "/v1/video/generations", {
443
+ method: "POST",
444
+ body: JSON.stringify(payload)
445
+ }));
446
+ const startedAt = Date.now();
447
+ while (Date.now() - startedAt <= maxWaitSec * 1e3) {
448
+ await sleep$1(pollIntervalSec * 1e3);
449
+ const rawStatus = await requestJson$1(input, `/v1/video/generations/${encodeURIComponent(taskId)}`, { method: "GET" });
450
+ const status = normalizeTaskStatus(rawStatus);
451
+ if (status.status === "succeeded") {
452
+ if (!status.videoUrl) throw new GenerationProviderError("Video generation succeeded but returned no video URL", { details: compactObject({
453
+ taskId,
454
+ rawStatus,
455
+ metadata: status.metadata
456
+ }) });
457
+ const output = [{
458
+ type: "video",
459
+ source: {
460
+ type: "url",
461
+ url: status.videoUrl
462
+ },
463
+ meta: {
464
+ task_id: taskId,
465
+ status: status.status,
466
+ ...status.metadata
467
+ }
468
+ }];
469
+ if (status.lastFrameUrl) output.push({
470
+ type: "image",
471
+ source: {
472
+ type: "url",
473
+ url: status.lastFrameUrl
474
+ },
475
+ meta: {
476
+ role: "last_frame",
477
+ task_id: taskId
478
+ }
479
+ });
480
+ return output;
481
+ }
482
+ if ([
483
+ "failed",
484
+ "expired",
485
+ "cancelled"
486
+ ].includes(status.status)) throw new GenerationProviderError(`Video generation ${status.status}`, { details: {
487
+ taskId,
488
+ rawStatus
489
+ } });
490
+ }
491
+ throw new GenerationTimeoutError("Timed out waiting for video generation", { taskId });
492
+ }
493
+
494
+ //#endregion
495
+ //#region src/adapters/gemini-generate-content.ts
496
+ const REQUEST_TIMEOUT_MS$2 = 3e5;
497
+ const IMAGE_FETCH_TIMEOUT_MS = 6e4;
498
+ const MAX_REFERENCE_IMAGE_BYTES = 10 * 1024 * 1024;
499
+ const DATA_URI_PATTERN = /^data:([^;]+);base64,(.+)$/s;
500
+ const MARKDOWN_IMAGE_DATA_URI_PATTERN = /!\[[^\]]*\]\(data:([^;]+);base64,([^)]+)\)/;
501
+ function dataUriToInlineData(value) {
502
+ const match = DATA_URI_PATTERN.exec(value);
503
+ if (!match) return null;
504
+ const [, mimeType, data] = match;
505
+ if (!mimeType || !data) return null;
506
+ return { inlineData: {
507
+ mimeType,
508
+ data
509
+ } };
510
+ }
511
+ async function urlToInlineData(fetchFn, url) {
512
+ const response = await fetchWithTimeout(fetchFn, url, {
513
+ method: "GET",
514
+ headers: { "User-Agent": "NetaGeneration/1.0" }
515
+ }, IMAGE_FETCH_TIMEOUT_MS);
516
+ if (!response.ok) throw new GenerationProviderError("Failed to fetch reference image", { status: response.status });
517
+ const contentLength = response.headers.get("content-length");
518
+ if (contentLength && Number(contentLength) > MAX_REFERENCE_IMAGE_BYTES) throw new GenerationValidationError("Reference image is too large");
519
+ const bytes = Buffer.from(await response.arrayBuffer());
520
+ if (bytes.byteLength > MAX_REFERENCE_IMAGE_BYTES) throw new GenerationValidationError("Reference image is too large");
521
+ const contentType = response.headers.get("content-type")?.split(";")[0]?.trim();
522
+ return { inlineData: {
523
+ mimeType: contentType?.startsWith("image/") ? contentType : "image/png",
524
+ data: bytes.toString("base64")
525
+ } };
526
+ }
527
+ async function sourceToInlineData(input, value) {
528
+ const inline = dataUriToInlineData(value);
529
+ if (inline) return inline;
530
+ if (value.startsWith("http://") || value.startsWith("https://")) return urlToInlineData(input.context.fetch, value);
531
+ throw new GenerationValidationError("Unsupported image source for Gemini image generation");
532
+ }
533
+ function extractMarkdownDataUriImage(text) {
534
+ const match = MARKDOWN_IMAGE_DATA_URI_PATTERN.exec(text);
535
+ if (!match) return null;
536
+ const [, mediaType, data] = match;
537
+ if (!mediaType || !data) return null;
538
+ return {
539
+ type: "image",
540
+ source: {
541
+ type: "base64",
542
+ mediaType,
543
+ data
544
+ }
545
+ };
546
+ }
547
+ function collectGeminiNoOutputDetails(raw) {
548
+ const candidates = raw.candidates ?? [];
549
+ return compactObject({
550
+ finishReasons: compactArray(candidates.map((candidate) => candidate.finishReason).filter((value) => value !== void 0)),
551
+ finishMessages: compactArray(candidates.map((candidate) => candidate.finishMessage).filter((value) => value !== void 0)),
552
+ safetyRatings: compactArray(candidates.flatMap((candidate) => candidate.safetyRatings ?? [])),
553
+ promptFeedback: raw.promptFeedback,
554
+ usageMetadata: raw.usageMetadata,
555
+ modelVersion: raw.modelVersion,
556
+ responseId: raw.responseId,
557
+ candidateCount: candidates.length,
558
+ candidates: compactArray(candidates.map((candidate) => compactObject({
559
+ index: candidate.index,
560
+ finishReason: candidate.finishReason,
561
+ finishMessage: candidate.finishMessage,
562
+ safetyRatings: candidate.safetyRatings,
563
+ citationMetadata: candidate.citationMetadata,
564
+ groundingMetadata: candidate.groundingMetadata,
565
+ avgLogprobs: candidate.avgLogprobs,
566
+ contentRole: candidate.content?.role,
567
+ partCount: candidate.content?.parts?.length
568
+ })))
569
+ });
570
+ }
571
+ function appendGeminiFileDataOutput(output, fileData) {
572
+ if (typeof fileData?.fileUri !== "string" || !fileData.fileUri) return false;
573
+ const mediaType = typeof fileData.mimeType === "string" ? fileData.mimeType : "image/png";
574
+ output.push({
575
+ type: "image",
576
+ source: {
577
+ type: "url",
578
+ url: fileData.fileUri
579
+ },
580
+ meta: { mediaType }
581
+ });
582
+ return true;
583
+ }
584
+ function appendGeminiPartOutput(output, part) {
585
+ if (typeof part.text === "string" && part.text.trim()) {
586
+ const image = extractMarkdownDataUriImage(part.text);
587
+ if (image) {
588
+ output.push(image);
589
+ return;
590
+ }
591
+ output.push({
592
+ type: "text",
593
+ text: part.text
594
+ });
595
+ return;
596
+ }
597
+ if (part.inlineData && typeof part.inlineData.data === "string" && part.inlineData.data) {
598
+ output.push({
599
+ type: "image",
600
+ source: {
601
+ type: "base64",
602
+ mediaType: typeof part.inlineData.mimeType === "string" ? part.inlineData.mimeType : "image/png",
603
+ data: part.inlineData.data
604
+ }
605
+ });
606
+ return;
607
+ }
608
+ appendGeminiFileDataOutput(output, part.fileData);
609
+ }
610
+ async function geminiGenerateContentAdapter(input) {
611
+ const prompt = mergeTextBlocks(input.declaration, input.request.content);
612
+ if (!prompt) throw new GenerationValidationError("Prompt text is required");
613
+ const imageParts = await Promise.all(input.request.content.filter((block) => block.type === "image").map(async (block) => sourceToInlineData(input, await input.context.resolveSource(block.source))));
614
+ const generationConfig = { responseModalities: ["IMAGE"] };
615
+ const aspectRatio = input.parameters.aspect_ratio;
616
+ const imageSize = input.parameters.image_size;
617
+ if (typeof aspectRatio === "string" || typeof imageSize === "string") {
618
+ const image = {};
619
+ if (typeof aspectRatio === "string") image.aspectRatio = aspectRatio;
620
+ if (typeof imageSize === "string") image.imageSize = imageSize;
621
+ generationConfig.responseFormat = { image };
622
+ }
623
+ const payload = {
624
+ contents: [{ parts: [{ text: prompt }, ...imageParts] }],
625
+ generationConfig
626
+ };
627
+ const response = await fetchWithTimeout(input.context.fetch, joinUrl(input.context.baseUrl, `/v1beta/models/${encodeURIComponent(input.declaration.model)}:generateContent`), {
628
+ method: "POST",
629
+ headers: {
630
+ Authorization: `Bearer ${input.context.apiKey}`,
631
+ "Content-Type": "application/json"
632
+ },
633
+ body: JSON.stringify(payload)
634
+ }, REQUEST_TIMEOUT_MS$2);
635
+ if (!response.ok) {
636
+ const body = await response.text().catch(() => response.statusText);
637
+ throw new GenerationProviderError("Gemini generation provider request failed", {
638
+ status: response.status,
639
+ body
640
+ });
641
+ }
642
+ const raw = await response.json();
643
+ const output = [];
644
+ for (const candidate of raw.candidates ?? []) for (const part of candidate.content?.parts ?? []) appendGeminiPartOutput(output, part);
645
+ if (output.length === 0) throw new GenerationProviderError("Gemini generation returned no output", { details: collectGeminiNoOutputDetails(raw) });
646
+ return output;
647
+ }
648
+
649
+ //#endregion
650
+ //#region src/adapters/openai-images.ts
651
+ const REQUEST_TIMEOUT_MS$1 = 3e5;
652
+ function collectOpenAiImagesNoOutputDetails(raw) {
653
+ const data = raw.data ?? [];
654
+ return compactObject({
655
+ created: raw.created,
656
+ usage: raw.usage,
657
+ background: raw.background,
658
+ outputFormat: raw.output_format,
659
+ quality: raw.quality,
660
+ size: raw.size,
661
+ dataCount: data.length,
662
+ data: compactArray(data.map((item) => compactObject({
663
+ hasUrl: typeof item.url === "string" && item.url.length > 0,
664
+ hasBase64Json: typeof item.b64_json === "string" && item.b64_json.length > 0,
665
+ revisedPrompt: item.revised_prompt
666
+ })))
667
+ });
668
+ }
669
+ async function openAiImagesAdapter(input) {
670
+ const prompt = mergeTextBlocks(input.declaration, input.request.content);
671
+ if (!prompt) throw new GenerationValidationError("Prompt text is required");
672
+ const images = await Promise.all(input.request.content.filter((block) => block.type === "image").map((block) => input.context.resolveSource(block.source)));
673
+ const payload = {
674
+ model: input.declaration.model,
675
+ prompt,
676
+ ...input.parameters
677
+ };
678
+ if (images.length > 0) payload.image = images;
679
+ const response = await fetchWithTimeout(input.context.fetch, joinUrl(input.context.baseUrl, "/v1/images/generations"), {
680
+ method: "POST",
681
+ headers: {
682
+ Authorization: `Bearer ${input.context.apiKey}`,
683
+ "Content-Type": "application/json"
684
+ },
685
+ body: JSON.stringify(payload)
686
+ }, REQUEST_TIMEOUT_MS$1);
687
+ if (!response.ok) {
688
+ const body = await response.text().catch(() => response.statusText);
689
+ throw new GenerationProviderError("Image generation provider request failed", {
690
+ status: response.status,
691
+ body
692
+ });
693
+ }
694
+ const raw = await response.json();
695
+ const output = [];
696
+ for (const item of raw.data ?? []) {
697
+ if (typeof item.url === "string" && item.url) output.push({
698
+ type: "image",
699
+ source: {
700
+ type: "url",
701
+ url: item.url
702
+ }
703
+ });
704
+ if (typeof item.b64_json === "string" && item.b64_json) output.push({
705
+ type: "image",
706
+ source: {
707
+ type: "base64",
708
+ mediaType: "image/png",
709
+ data: item.b64_json
710
+ }
711
+ });
712
+ if (typeof item.revised_prompt === "string" && item.revised_prompt.trim()) output.push({
713
+ type: "text",
714
+ text: item.revised_prompt,
715
+ meta: { role: "revised_prompt" }
716
+ });
717
+ }
718
+ if (output.length === 0) throw new GenerationProviderError("Image generation returned no output", { details: collectOpenAiImagesNoOutputDetails(raw) });
719
+ return output;
720
+ }
721
+
722
+ //#endregion
723
+ //#region src/adapters/suno-tasks.ts
724
+ const REQUEST_TIMEOUT_MS = 6e4;
725
+ const DEFAULT_POLL_INTERVAL_SEC = 5;
726
+ const DEFAULT_MAX_WAIT_SEC = 600;
727
+ const DEFAULT_MUSIC_VERSION = "chirp-v5-5";
728
+ const OPERATION_PATHS = {
729
+ music: {
730
+ path: "/suno/submit/music",
731
+ poll: true,
732
+ defaultMusicVersion: true,
733
+ textField: "prompt"
734
+ },
735
+ lyrics: {
736
+ path: "/suno/submit/lyrics",
737
+ poll: true,
738
+ textField: "prompt",
739
+ requireText: true
740
+ },
741
+ concat: {
742
+ path: "/suno/submit/concat",
743
+ poll: true
744
+ },
745
+ upsample_tags: {
746
+ path: "/suno/submit/upsample-tags",
747
+ poll: false,
748
+ textField: "original_tags",
749
+ requireText: true
750
+ },
751
+ upload_audio: {
752
+ path: "/suno/uploads/audio",
753
+ poll: true
754
+ }
755
+ };
756
+ const FINAL_SUCCESS_STATUSES = new Set([
757
+ "success",
758
+ "succeeded",
759
+ "completed"
760
+ ]);
761
+ const FINAL_FAILURE_STATUSES = new Set([
762
+ "failure",
763
+ "failed",
764
+ "error",
765
+ "cancelled",
766
+ "canceled",
767
+ "expired"
768
+ ]);
769
+ function sleep(ms) {
770
+ return new Promise((resolve) => setTimeout(resolve, ms));
771
+ }
772
+ function isRecord$1(value) {
773
+ return !!value && typeof value === "object" && !Array.isArray(value);
774
+ }
775
+ function asString(value) {
776
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
777
+ }
778
+ function asInteger(value, fallback) {
779
+ return typeof value === "number" && Number.isInteger(value) ? value : fallback;
780
+ }
781
+ function successCode(value) {
782
+ const code = String(value ?? "").trim().toLowerCase();
783
+ return code === "success" || code === "200" || code === "0";
784
+ }
785
+ function requireSuccess(response, fallbackMessage) {
786
+ if (!successCode(response.code)) throw new GenerationProviderError(fallbackMessage, { details: {
787
+ code: response.code,
788
+ message: response.message,
789
+ data: response.data
790
+ } });
791
+ return response.data;
792
+ }
793
+ function extractTaskId(value) {
794
+ if (typeof value === "string" && value.trim()) return value.trim();
795
+ if (!isRecord$1(value)) return void 0;
796
+ for (const key of [
797
+ "task_id",
798
+ "id",
799
+ "taskBatchId"
800
+ ]) {
801
+ const taskId = asString(value[key]);
802
+ if (taskId) return taskId;
803
+ }
804
+ }
805
+ function normalizeStatus(value) {
806
+ return String(value ?? "").trim().toLowerCase();
807
+ }
808
+ function normalizeTask(operation, data) {
809
+ if (Array.isArray(data)) return data.length > 0 ? normalizeTask(operation, data[0]) : null;
810
+ if (!isRecord$1(data)) return null;
811
+ if ("status" in data || "task_id" in data) return data;
812
+ if (Array.isArray(data.data) && data.data.length > 0) return normalizeTask(operation, data.data[0]);
813
+ return {
814
+ action: operation,
815
+ status: "SUCCESS",
816
+ data
817
+ };
818
+ }
819
+ function getOperation(input) {
820
+ const operation = asString(input.parameters.operation) ?? "music";
821
+ if (!OPERATION_PATHS[operation]) throw new GenerationValidationError(`Unsupported Suno operation: ${operation}`);
822
+ return operation;
823
+ }
824
+ async function buildPayload(input, operation) {
825
+ const payload = { ...input.meta };
826
+ const config = OPERATION_PATHS[operation];
827
+ const prompt = mergeTextBlocks(input.declaration, input.request.content);
828
+ if (prompt && config?.textField && payload[config.textField] === void 0) payload[config.textField] = prompt;
829
+ if (config?.textField && config.textField !== "prompt" && payload[config.textField] === void 0 && payload.prompt !== void 0) payload[config.textField] = payload.prompt;
830
+ const audioBlock = input.request.content.find((block) => block.type === "audio");
831
+ if (audioBlock && payload.url === void 0) payload.url = await input.context.resolveSource(audioBlock.source);
832
+ const imageBlock = input.request.content.find((block) => block.type === "image");
833
+ if (imageBlock && payload.image_url === void 0) payload.image_url = await input.context.resolveSource(imageBlock.source);
834
+ const videoBlock = input.request.content.find((block) => block.type === "video");
835
+ if (videoBlock && payload.video_url === void 0) payload.video_url = await input.context.resolveSource(videoBlock.source);
836
+ normalizeMusicTaskPayload(input, operation, payload);
837
+ if (config?.defaultMusicVersion && payload.mv === void 0 && payload.model_name === void 0) payload.mv = DEFAULT_MUSIC_VERSION;
838
+ validateSunoPayload(operation, payload);
839
+ return payload;
840
+ }
841
+ function contentCount(content, type) {
842
+ return content.filter((block) => block.type === type).length;
843
+ }
844
+ function normalizeMusicTaskPayload(input, operation, payload) {
845
+ if (operation !== "music") return;
846
+ const taskField = input.declaration.meta?.taskField ?? "task";
847
+ const task = asString(payload[taskField]);
848
+ if (!task) return;
849
+ const taskVariant = input.declaration.meta?.taskVariants?.[task];
850
+ if (!taskVariant) throw new GenerationValidationError(`Unsupported Suno music task: ${task}`);
851
+ for (const key of taskVariant.required ?? []) if (payload[key] === void 0 || payload[key] === null || payload[key] === "") throw new GenerationValidationError(`Suno task ${task} requires meta.${key}`);
852
+ for (const type of taskVariant.requiredContent ?? []) if (contentCount(input.request.content, type) === 0) throw new GenerationValidationError(`Suno task ${task} requires ${type} content`);
853
+ if (taskVariant.sendTask === false) delete payload[taskField];
854
+ }
855
+ function validateSunoPayload(operation, payload) {
856
+ const config = OPERATION_PATHS[operation];
857
+ const task = asString(payload.task);
858
+ if (operation === "music" && task) {
859
+ if (task === "lyrics") throw new GenerationValidationError("Use operation=lyrics instead of task=lyrics");
860
+ }
861
+ if (config?.requireText && !asString(payload[config.textField ?? "prompt"])) throw new GenerationValidationError(`${config.textField ?? "prompt"} text is required`);
862
+ if (operation === "upload_audio" && !asString(payload.url)) throw new GenerationValidationError("Audio url is required for Suno upload_audio");
863
+ if (operation === "concat" && !asString(payload.clip_id)) throw new GenerationValidationError("clip_id is required for Suno concat");
864
+ }
865
+ async function requestJson(input, path, init) {
866
+ const response = await fetchWithTimeout(input.context.fetch, joinUrl(input.context.baseUrl, path), {
867
+ ...init,
868
+ headers: {
869
+ Authorization: `Bearer ${input.context.apiKey}`,
870
+ "Content-Type": "application/json",
871
+ ...init.headers
872
+ }
873
+ }, REQUEST_TIMEOUT_MS);
874
+ const body = await response.text();
875
+ let parsed;
876
+ try {
877
+ parsed = body ? JSON.parse(body) : {};
878
+ } catch {
879
+ throw new GenerationProviderError("Suno provider returned invalid JSON", {
880
+ status: response.status,
881
+ body
882
+ });
883
+ }
884
+ if (!response.ok) throw new GenerationProviderError("Suno provider request failed", {
885
+ status: response.status,
886
+ details: { body: parsed }
887
+ });
888
+ return parsed;
889
+ }
890
+ function appendUrlBlock(output, type, url, meta) {
891
+ const value = asString(url);
892
+ if (!value) return;
893
+ output.push({
894
+ type,
895
+ source: {
896
+ type: "url",
897
+ url: value
898
+ },
899
+ meta: compactObject(meta)
900
+ });
901
+ }
902
+ function appendSunoContent(output, value, meta = {}) {
903
+ if (Array.isArray(value)) {
904
+ for (const item of value) appendSunoContent(output, item, meta);
905
+ return;
906
+ }
907
+ if (!isRecord$1(value)) return;
908
+ if (isRecord$1(value.clips)) for (const clip of Object.values(value.clips)) appendSunoContent(output, clip, meta);
909
+ if (Array.isArray(value.items)) for (const item of value.items) appendSunoContent(output, item, meta);
910
+ const itemMeta = compactObject({
911
+ ...meta,
912
+ id: value.id,
913
+ clip_id: value.clipId ?? value.clip_id ?? meta.clip_id,
914
+ task_id: value.task_id ?? value.taskId ?? meta.task_id,
915
+ title: value.title,
916
+ status: value.status ?? meta.status,
917
+ model: value.model_name ?? value.modelName,
918
+ duration: value.duration ?? (isRecord$1(value.metadata) ? value.metadata.duration : void 0),
919
+ tags: value.tags ?? (isRecord$1(value.metadata) ? value.metadata.tags : void 0),
920
+ prompt: value.prompt ?? (isRecord$1(value.metadata) ? value.metadata.prompt : void 0),
921
+ progress: value.progress ?? meta.progress,
922
+ progress_message: value.progressMsg ?? value.progress_msg,
923
+ task_batch_id: value.taskBatchId ?? value.task_batch_id,
924
+ continue_clip_id: value.continueClipId ?? value.continue_clip_id,
925
+ input_type: value.inputType ?? value.input_type,
926
+ make_instrumental: value.makeInstrumental ?? value.make_instrumental,
927
+ created_at: value.createTime ?? value.created_at
928
+ });
929
+ appendUrlBlock(output, "audio", value.audio_url ?? value.audioUrl ?? value.cld2AudioUrl ?? value.cld2_audio_url, itemMeta);
930
+ appendUrlBlock(output, "audio", value.vocal_audio_url ?? value.vocalAudioUrl, itemMeta);
931
+ appendUrlBlock(output, "audio", value.instrumental_audio_url ?? value.instrumentalAudioUrl, itemMeta);
932
+ appendUrlBlock(output, "audio", value.source_audio_url ?? value.sourceAudioUrl, itemMeta);
933
+ appendUrlBlock(output, "video", value.video_url ?? value.videoUrl ?? value.cld2VideoUrl ?? value.cld2_video_url, itemMeta);
934
+ appendUrlBlock(output, "image", value.image_large_url ?? value.image_url ?? value.imageUrl ?? value.cld2ImageUrl ?? value.cld2_image_url, itemMeta);
935
+ const text = asString(value.upsampled_tags) ?? asString(value.text) ?? asString(value.lyrics);
936
+ if (text) output.push({
937
+ type: "text",
938
+ text,
939
+ meta: itemMeta
940
+ });
941
+ if (isRecord$1(value.data)) appendSunoContent(output, value.data, itemMeta);
942
+ }
943
+ function buildImmediateResult(operation, data, raw) {
944
+ const output = [];
945
+ const metadata = compactObject({
946
+ operation,
947
+ raw
948
+ });
949
+ appendSunoContent(output, data, metadata);
950
+ if (output.length > 0) return output;
951
+ const value = extractTaskId(data) ?? asString(data);
952
+ if (value) output.push({
953
+ type: "text",
954
+ text: value,
955
+ meta: metadata
956
+ });
957
+ return requireSunoOutput(output, "Suno provider returned no output", {
958
+ operation,
959
+ raw
960
+ });
961
+ }
962
+ function requireSunoOutput(output, message, details) {
963
+ if (output.length === 0) throw new GenerationProviderError(message, { details });
964
+ return output;
965
+ }
966
+ function buildResult(operation, task, raw) {
967
+ const output = [];
968
+ const metadata = compactObject({
969
+ operation,
970
+ task_id: task.task_id,
971
+ action: task.action,
972
+ status: task.status,
973
+ fail_reason: task.fail_reason,
974
+ progress: task.progress,
975
+ submit_time: task.submit_time,
976
+ start_time: task.start_time,
977
+ finish_time: task.finish_time,
978
+ raw
979
+ });
980
+ appendSunoContent(output, task.data, metadata);
981
+ appendUrlBlock(output, "audio", task.result_url, metadata);
982
+ return requireSunoOutput(output, "Suno task succeeded but returned no output", {
983
+ operation,
984
+ task,
985
+ raw
986
+ });
987
+ }
988
+ async function pollSunoTask(input, operation, taskId, pollIntervalSec, maxWaitSec) {
989
+ const startedAt = Date.now();
990
+ while (Date.now() - startedAt <= maxWaitSec * 1e3) {
991
+ await sleep(pollIntervalSec * 1e3);
992
+ let raw;
993
+ try {
994
+ raw = await requestJson(input, `/suno/fetch/${encodeURIComponent(taskId)}`, { method: "GET" });
995
+ } catch (error) {
996
+ if (error instanceof GenerationProviderError) throw error;
997
+ continue;
998
+ }
999
+ const data = requireSuccess(raw, "Suno task fetch failed");
1000
+ const task = normalizeTask(operation, data);
1001
+ if (!task) throw new GenerationProviderError("Suno task fetch returned invalid task data", { details: { data } });
1002
+ const status = normalizeStatus(task.status);
1003
+ if (FINAL_SUCCESS_STATUSES.has(status)) return buildResult(operation, task, data);
1004
+ if (FINAL_FAILURE_STATUSES.has(status)) throw new GenerationProviderError("Suno task failed", { details: {
1005
+ task_id: taskId,
1006
+ status: task.status,
1007
+ fail_reason: task.fail_reason
1008
+ } });
1009
+ }
1010
+ throw new GenerationTimeoutError("Timed out waiting for Suno task", {
1011
+ task_id: taskId,
1012
+ operation
1013
+ });
1014
+ }
1015
+ async function sunoTasksAdapter(input) {
1016
+ const operation = getOperation(input);
1017
+ const payload = await buildPayload(input, operation);
1018
+ const config = OPERATION_PATHS[operation];
1019
+ if (!config) throw new GenerationValidationError(`Unsupported Suno operation: ${operation}`);
1020
+ const raw = await requestJson(input, config.path, {
1021
+ method: "POST",
1022
+ body: JSON.stringify(payload)
1023
+ });
1024
+ const data = requireSuccess(raw, "Suno task submit failed");
1025
+ if (!config.poll) return buildImmediateResult(operation, data, raw);
1026
+ const taskId = extractTaskId(data);
1027
+ if (!taskId) return buildImmediateResult(operation, data, raw);
1028
+ return pollSunoTask(input, operation, taskId, asInteger(input.parameters.poll_interval, DEFAULT_POLL_INTERVAL_SEC), asInteger(input.parameters.max_wait, DEFAULT_MAX_WAIT_SEC));
1029
+ }
1030
+
1031
+ //#endregion
1032
+ //#region src/adapters/index.ts
1033
+ const builtinGenerationAdapters = {
1034
+ "ark.videoGenerations": arkVideoGenerationsAdapter,
1035
+ "gemini.generateContent": geminiGenerateContentAdapter,
1036
+ "openai.images": openAiImagesAdapter,
1037
+ "suno.tasks": sunoTasksAdapter
1038
+ };
1039
+ function getGenerationAdapter(type, adapters = {}) {
1040
+ const adapter = adapters[type] ?? builtinGenerationAdapters[type];
1041
+ if (!adapter) throw new GenerationUnsupportedAdapterError(type);
1042
+ return adapter;
1043
+ }
1044
+
1045
+ //#endregion
1046
+ //#region src/config.ts
1047
+ const DECLARATION_EXTENSIONS = new Set([
1048
+ ".yaml",
1049
+ ".yml",
1050
+ ".json"
1051
+ ]);
1052
+ function isRecord(value) {
1053
+ return !!value && typeof value === "object" && !Array.isArray(value);
1054
+ }
1055
+ function isParameterSpec(value) {
1056
+ if (!isRecord(value)) return false;
1057
+ return [
1058
+ "string",
1059
+ "number",
1060
+ "integer",
1061
+ "boolean"
1062
+ ].includes(String(value.type));
1063
+ }
1064
+ function isMetaFieldSpec(value) {
1065
+ if (!isRecord(value)) return false;
1066
+ return isParameterSpec(value) || String(value.type) === "object";
1067
+ }
1068
+ function isMetaTaskVariantSpec(value) {
1069
+ if (!isRecord(value)) return false;
1070
+ return (value.description === void 0 || typeof value.description === "string") && (value.required === void 0 || Array.isArray(value.required) && value.required.every((item) => typeof item === "string")) && (value.requiredContent === void 0 || Array.isArray(value.requiredContent) && value.requiredContent.every((item) => typeof item === "string")) && (value.sendTask === void 0 || typeof value.sendTask === "boolean");
1071
+ }
1072
+ function isMetaSpec(value) {
1073
+ if (!isRecord(value)) return false;
1074
+ return (value.fields === void 0 || isRecord(value.fields) && Object.values(value.fields).every((field) => isMetaFieldSpec(field))) && (value.taskField === void 0 || typeof value.taskField === "string") && (value.taskVariants === void 0 || isRecord(value.taskVariants) && Object.values(value.taskVariants).every((variant) => isMetaTaskVariantSpec(variant)));
1075
+ }
1076
+ function isGenerationModelDeclaration(value) {
1077
+ if (!isRecord(value)) return false;
1078
+ const adapter = value.adapter;
1079
+ const content = value.content;
1080
+ const parameters = value.parameters;
1081
+ const meta = value.meta;
1082
+ const examples = value.examples;
1083
+ return value.schema === MODEL_SCHEMA && typeof value.model === "string" && value.model.trim().length > 0 && (value.allowUnknownParameters === void 0 || typeof value.allowUnknownParameters === "boolean") && isRecord(adapter) && typeof adapter.type === "string" && isRecord(content) && Array.isArray(content.input) && (parameters === void 0 || isRecord(parameters) && Object.values(parameters).every(isParameterSpec)) && (meta === void 0 || isMetaSpec(meta)) && (examples === void 0 || Array.isArray(examples));
1084
+ }
1085
+ function parseGenerationModelDeclaration(rawText, filePath = "model.yaml") {
1086
+ const parsed = extname(filePath) === ".json" ? JSON.parse(rawText) : parse(rawText);
1087
+ if (!isGenerationModelDeclaration(parsed)) throw new GenerationConfigError(`Invalid model declaration: ${filePath}`);
1088
+ return parsed;
1089
+ }
1090
+ function stringifyGenerationModelDeclaration(declaration, options = {}) {
1091
+ const value = cloneJson(declaration);
1092
+ if (options.format === "json") return `${JSON.stringify(value, null, 2)}\n`;
1093
+ return stringify(value, { lineWidth: 120 });
1094
+ }
1095
+ async function readGenerationModelDeclaration(filePath) {
1096
+ return parseGenerationModelDeclaration(await readFile(filePath, "utf-8"), filePath);
1097
+ }
1098
+ async function readGenerationModelDeclarationsFromFiles(filePaths) {
1099
+ return mergeGenerationModelDeclarations(await Promise.all(filePaths.map((filePath) => readGenerationModelDeclaration(filePath))));
1100
+ }
1101
+ async function readGenerationModelDeclarationsFromDirectory(directory) {
1102
+ return readGenerationModelDeclarationsFromFiles((await readdir(directory)).filter((entry) => DECLARATION_EXTENSIONS.has(extname(entry))).sort().map((entry) => join(directory, entry)));
1103
+ }
1104
+ function mergeGenerationModelDeclarations(declarations) {
1105
+ const byModel = /* @__PURE__ */ new Map();
1106
+ for (const declaration of declarations) byModel.set(declaration.model, declaration);
1107
+ return [...byModel.values()].sort((a, b) => a.model.localeCompare(b.model));
1108
+ }
1109
+ async function writeGenerationModelDeclaration(declaration, filePath, options = {}) {
1110
+ await writeFile(filePath, stringifyGenerationModelDeclaration(declaration, options));
1111
+ }
1112
+ async function writeGenerationModelDeclarations(declarations, directory, options = {}) {
1113
+ await mkdir(directory, { recursive: true });
1114
+ const ext = options.format === "json" ? "json" : "yaml";
1115
+ await Promise.all(declarations.map((declaration) => writeGenerationModelDeclaration(declaration, join(directory, `${slugifyFileName(declaration.model)}.${ext}`), options)));
1116
+ }
1117
+
1118
+ //#endregion
1119
+ //#region src/source.ts
1120
+ const defaultGenerationSourceResolver = (source) => {
1121
+ switch (source.type) {
1122
+ case "url": return source.url;
1123
+ case "base64": return `data:${source.mediaType};base64,${source.data}`;
1124
+ }
1125
+ };
1126
+
1127
+ //#endregion
1128
+ //#region src/client.ts
1129
+ const DEFAULT_BASE_URL = "https://router.neta.art";
1130
+ function redactDebugEvent(value) {
1131
+ if (Array.isArray(value)) return value.map((item) => redactDebugEvent(item));
1132
+ if (!value || typeof value !== "object") return value;
1133
+ const output = {};
1134
+ for (const [key, child] of Object.entries(value)) if (/^(authorization|api[-_]?key|token|b64_json|thoughtSignature)$/i.test(key) || key === "data") output[key] = "[REDACTED]";
1135
+ else output[key] = redactDebugEvent(child);
1136
+ return output;
1137
+ }
1138
+ function defaultDebugLogger(event) {
1139
+ console.error(JSON.stringify(event, null, 2));
1140
+ }
1141
+ function resolveDebugConfig(debug) {
1142
+ if (!debug) return void 0;
1143
+ if (debug === true) return {
1144
+ enabled: true,
1145
+ includeSensitive: false,
1146
+ includeResponseBody: true,
1147
+ logger: (event) => defaultDebugLogger(redactDebugEvent(event))
1148
+ };
1149
+ if (!debug.enabled) return void 0;
1150
+ const includeSensitive = debug.includeSensitive ?? false;
1151
+ const logger = debug.logger ?? defaultDebugLogger;
1152
+ return {
1153
+ enabled: true,
1154
+ includeSensitive,
1155
+ includeResponseBody: debug.includeResponseBody ?? true,
1156
+ logger: (event) => logger(includeSensitive ? event : redactDebugEvent(event))
1157
+ };
1158
+ }
1159
+ function resolveModels(options) {
1160
+ const models = [...options.includeBuiltinModels ?? !options.models ? builtinGenerationModels : [], ...options.models ?? []];
1161
+ const byModel = /* @__PURE__ */ new Map();
1162
+ for (const model of models) byModel.set(model.model, cloneJson(model));
1163
+ return [...byModel.values()].sort((a, b) => a.model.localeCompare(b.model));
1164
+ }
1165
+ function createGenerationClient(options = {}) {
1166
+ const models = resolveModels(options);
1167
+ const byModel = new Map(models.map((declaration) => [declaration.model, declaration]));
1168
+ const fetchFn = options.fetch ?? globalThis.fetch;
1169
+ if (!fetchFn) throw new GenerationConfigError("A fetch implementation is required");
1170
+ const debug = resolveDebugConfig(options.debug);
1171
+ const adapterFetch = debug ? createDebugFetch(fetchFn, debug) : fetchFn;
1172
+ function requireModel(model) {
1173
+ const declaration = byModel.get(model);
1174
+ if (!declaration) throw new GenerationConfigError(`Generation model is unavailable: ${model}`);
1175
+ return declaration;
1176
+ }
1177
+ return {
1178
+ validate(request) {
1179
+ const declaration = requireModel(request.model);
1180
+ validateGenerationContent(declaration, request.content);
1181
+ const parameters = resolveGenerationParameters(declaration, request.parameters);
1182
+ const meta = resolveGenerationMeta(declaration, mergeGenerationMeta({
1183
+ ...request.metadata ?? {},
1184
+ ...request.meta ?? {}
1185
+ }, request.content), request.content);
1186
+ return {
1187
+ declaration: cloneJson(declaration),
1188
+ request: cloneJson(request),
1189
+ parameters,
1190
+ meta
1191
+ };
1192
+ },
1193
+ async generate(request) {
1194
+ const resolved = this.validate(request);
1195
+ const apiKey = request.apiKey ?? options.apiKey;
1196
+ if (!apiKey) throw new GenerationConfigError("apiKey is required");
1197
+ return getGenerationAdapter(resolved.declaration.adapter.type, options.adapters)({
1198
+ ...resolved,
1199
+ context: {
1200
+ apiKey,
1201
+ baseUrl: request.baseUrl ?? options.baseUrl ?? DEFAULT_BASE_URL,
1202
+ fetch: adapterFetch,
1203
+ resolveSource: options.sourceResolver ?? defaultGenerationSourceResolver
1204
+ }
1205
+ });
1206
+ },
1207
+ listModels() {
1208
+ return cloneJson(models);
1209
+ },
1210
+ getModel(model) {
1211
+ const declaration = byModel.get(model);
1212
+ return declaration ? cloneJson(declaration) : null;
1213
+ },
1214
+ stringifyModelConfig(model, stringifyOptions = {}) {
1215
+ return stringifyGenerationModelDeclaration(requireModel(model), stringifyOptions);
1216
+ },
1217
+ exportModelConfig(model, filePath) {
1218
+ return writeGenerationModelDeclaration(requireModel(model), filePath);
1219
+ },
1220
+ exportModelConfigs(directory) {
1221
+ return writeGenerationModelDeclarations(models, directory);
1222
+ }
1223
+ };
1224
+ }
1225
+ async function createGenerationClientFromFiles(filePaths, options = {}) {
1226
+ const models = await readGenerationModelDeclarationsFromFiles(filePaths);
1227
+ return createGenerationClient({
1228
+ ...options,
1229
+ models,
1230
+ includeBuiltinModels: options.includeBuiltinModels ?? true
1231
+ });
1232
+ }
1233
+ async function createGenerationClientFromDirectory(directory, options = {}) {
1234
+ const models = await readGenerationModelDeclarationsFromDirectory(directory);
1235
+ return createGenerationClient({
1236
+ ...options,
1237
+ models,
1238
+ includeBuiltinModels: options.includeBuiltinModels ?? true
1239
+ });
1240
+ }
1241
+ async function createGenerationClientFromFile(filePath, options = {}) {
1242
+ return createGenerationClientFromFiles([filePath], options);
1243
+ }
1244
+
1245
+ //#endregion
1246
+ //#region src/export-config.ts
1247
+ function stringifyBuiltinModelConfig(model, options = {}) {
1248
+ const declaration = getBuiltinGenerationModel(model);
1249
+ if (!declaration) throw new GenerationConfigError(`Built-in model is unavailable: ${model}`);
1250
+ return stringifyGenerationModelDeclaration(declaration, options);
1251
+ }
1252
+ async function exportBuiltinModelConfig(model, filePath) {
1253
+ const declaration = getBuiltinGenerationModel(model);
1254
+ if (!declaration) throw new GenerationConfigError(`Built-in model is unavailable: ${model}`);
1255
+ await writeGenerationModelDeclaration(declaration, filePath);
1256
+ }
1257
+ async function exportBuiltinModelConfigs(directory) {
1258
+ await writeGenerationModelDeclarations(listBuiltinGenerationModels(), directory);
1259
+ }
1260
+
1261
+ //#endregion
1262
+ export { GenerationError as A, arkVideoGenerationsAdapter as C, resolveGenerationParameters as D, resolveGenerationMeta as E, GenerationTimeoutError as M, GenerationUnsupportedAdapterError as N, validateGenerationContent as O, GenerationValidationError as P, geminiGenerateContentAdapter as S, mergeTextBlocks as T, writeGenerationModelDeclarations as _, createGenerationClientFromDirectory as a, sunoTasksAdapter as b, defaultGenerationSourceResolver as c, parseGenerationModelDeclaration as d, readGenerationModelDeclaration as f, writeGenerationModelDeclaration as g, stringifyGenerationModelDeclaration as h, createGenerationClient as i, GenerationProviderError as j, GenerationConfigError as k, isGenerationModelDeclaration as l, readGenerationModelDeclarationsFromFiles as m, exportBuiltinModelConfigs as n, createGenerationClientFromFile as o, readGenerationModelDeclarationsFromDirectory as p, stringifyBuiltinModelConfig as r, createGenerationClientFromFiles as s, exportBuiltinModelConfig as t, mergeGenerationModelDeclarations as u, builtinGenerationAdapters as v, mergeGenerationMeta as w, openAiImagesAdapter as x, getGenerationAdapter as y };
1263
+ //# sourceMappingURL=export-config-D8By2_r7.js.map