@tangle-network/agent-app 0.45.65 → 0.46.1
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/dist/assistant/index.js +3 -3
- package/dist/chat-react/index.js +2 -2
- package/dist/chunk-2RTYQU4W.js +441 -0
- package/dist/chunk-2RTYQU4W.js.map +1 -0
- package/dist/{chunk-23J72UUA.js → chunk-DINGA2MO.js} +94 -560
- package/dist/chunk-DINGA2MO.js.map +1 -0
- package/dist/{chunk-AWM4H5XR.js → chunk-MBKNKAN2.js} +6 -4
- package/dist/{chunk-AWM4H5XR.js.map → chunk-MBKNKAN2.js.map} +1 -1
- package/dist/chunk-YDOQE47G.js +518 -0
- package/dist/chunk-YDOQE47G.js.map +1 -0
- package/dist/stories/studio/fixtures.d.ts +0 -4
- package/dist/studio/generation.d.ts +16 -74
- package/dist/studio/index.d.ts +2 -2
- package/dist/studio/index.js +27 -25
- package/dist/studio/model-options.d.ts +61 -0
- package/dist/studio-react/composer-option-controls.d.ts +140 -0
- package/dist/studio-react/generation-notice.d.ts +18 -0
- package/dist/studio-react/index.d.ts +6 -12
- package/dist/studio-react/index.js +902 -827
- package/dist/studio-react/index.js.map +1 -1
- package/dist/studio-react/studio-composer.d.ts +34 -0
- package/dist/studio-react/studio-workspace.d.ts +2 -4
- package/dist/studio-react/studio.css +51 -0
- package/dist/theme/tokens.css +18 -0
- package/dist/web-react/index.js +11 -11
- package/package.json +1 -1
- package/dist/chunk-23J72UUA.js.map +0 -1
- package/dist/chunk-HSBJB46C.js +0 -305
- package/dist/chunk-HSBJB46C.js.map +0 -1
- package/dist/chunk-MLG6XKPV.js +0 -44
- package/dist/chunk-MLG6XKPV.js.map +0 -1
- package/dist/studio-react/avatar-composer.d.ts +0 -8
- package/dist/studio-react/composer-hero.d.ts +0 -12
- package/dist/studio-react/composer-shell.d.ts +0 -18
- package/dist/studio-react/image-composer.d.ts +0 -8
- package/dist/studio-react/publish-package-composer.d.ts +0 -18
- package/dist/studio-react/speech-composer.d.ts +0 -4
- package/dist/studio-react/transcription-composer.d.ts +0 -12
- package/dist/studio-react/video-composer.d.ts +0 -10
package/dist/assistant/index.js
CHANGED
|
@@ -2,14 +2,14 @@ import {
|
|
|
2
2
|
ChatComposer,
|
|
3
3
|
ChatEmptyState,
|
|
4
4
|
ChatMessages
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-MBKNKAN2.js";
|
|
6
6
|
import "../chunk-FBVLEGEG.js";
|
|
7
7
|
import "../chunk-ENLRJYVW.js";
|
|
8
8
|
import "../chunk-GEYACSFW.js";
|
|
9
|
+
import "../chunk-DINGA2MO.js";
|
|
9
10
|
import {
|
|
10
11
|
ModelPicker
|
|
11
|
-
} from "../chunk-
|
|
12
|
-
import "../chunk-MLG6XKPV.js";
|
|
12
|
+
} from "../chunk-YDOQE47G.js";
|
|
13
13
|
import "../chunk-BATKJP3P.js";
|
|
14
14
|
import {
|
|
15
15
|
AsyncView
|
package/dist/chat-react/index.js
CHANGED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
// src/studio/generation.ts
|
|
2
|
+
var GENERATION_TYPES = ["image", "video", "avatar", "speech", "transcription"];
|
|
3
|
+
function isGenerationType(value) {
|
|
4
|
+
return GENERATION_TYPES.includes(value);
|
|
5
|
+
}
|
|
6
|
+
var MIN_IMAGE_COUNT = 1;
|
|
7
|
+
var MAX_IMAGE_COUNT = 8;
|
|
8
|
+
function relativeTime(date) {
|
|
9
|
+
if (!date) return "";
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
const diff = now - new Date(date).getTime();
|
|
12
|
+
const minutes = Math.floor(diff / 6e4);
|
|
13
|
+
if (minutes < 1) return "just now";
|
|
14
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
15
|
+
const hours = Math.floor(minutes / 60);
|
|
16
|
+
if (hours < 24) return `${hours}h ago`;
|
|
17
|
+
const days = Math.floor(hours / 24);
|
|
18
|
+
if (days < 7) return `${days}d ago`;
|
|
19
|
+
return new Date(date).toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
20
|
+
}
|
|
21
|
+
function outputPathFor(type) {
|
|
22
|
+
if (type === "image") return "generated/images";
|
|
23
|
+
if (type === "video") return "generated/videos";
|
|
24
|
+
if (type === "avatar") return "generated/avatars";
|
|
25
|
+
if (type === "speech") return "generated/audio";
|
|
26
|
+
return "generated/transcripts";
|
|
27
|
+
}
|
|
28
|
+
function generationVaultPath(generation) {
|
|
29
|
+
const value = generation.metadata?.vaultPath;
|
|
30
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
31
|
+
}
|
|
32
|
+
function selectedModelsWithDefaults(current, catalog) {
|
|
33
|
+
const next = { ...current };
|
|
34
|
+
for (const key of GENERATION_TYPES) {
|
|
35
|
+
const models = catalog.models[key] ?? [];
|
|
36
|
+
const currentOption = models.find((model) => model.id === next[key]);
|
|
37
|
+
if (!next[key] || !currentOption || currentOption.status === "unavailable") {
|
|
38
|
+
next[key] = preferredModelId(key, catalog) ?? "";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return next;
|
|
42
|
+
}
|
|
43
|
+
function preferredModelId(type, catalog) {
|
|
44
|
+
if (!catalog) return void 0;
|
|
45
|
+
const models = catalog.models[type] ?? [];
|
|
46
|
+
const preferred = catalog.defaults[type];
|
|
47
|
+
return models.find((model) => model.id === preferred)?.id ?? models.find((model) => model.status !== "unavailable")?.id ?? models[0]?.id;
|
|
48
|
+
}
|
|
49
|
+
function modelMessage(model, loading, count) {
|
|
50
|
+
if (loading) return "Loading media models...";
|
|
51
|
+
if (count === 0) return "No models are available for this media type.";
|
|
52
|
+
if (!model) return "Select a model.";
|
|
53
|
+
if (model.status === "unavailable") return model.reason ?? "This model is not configured.";
|
|
54
|
+
if (model.status === "limited") return model.reason ? `Limited: ${model.reason}` : "Limited availability.";
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
function buildGenerationRequestBody(fields) {
|
|
58
|
+
const body = {
|
|
59
|
+
workspaceId: fields.workspaceId,
|
|
60
|
+
clientRequestId: fields.clientRequestId,
|
|
61
|
+
type: fields.type,
|
|
62
|
+
model: fields.model,
|
|
63
|
+
prompt: fields.prompt.trim()
|
|
64
|
+
};
|
|
65
|
+
if (fields.type === "image") {
|
|
66
|
+
if (fields.image.size) body.size = fields.image.size;
|
|
67
|
+
if (fields.image.quality) body.quality = fields.image.quality;
|
|
68
|
+
body.n = fields.image.count;
|
|
69
|
+
}
|
|
70
|
+
if (fields.type === "video") {
|
|
71
|
+
if (fields.video.duration !== void 0) body.duration = fields.video.duration;
|
|
72
|
+
if (fields.video.resolution) body.resolution = fields.video.resolution;
|
|
73
|
+
if (fields.video.aspectRatio) body.aspectRatio = fields.video.aspectRatio;
|
|
74
|
+
if (fields.video.referenceImageUrl) body.referenceImageUrl = fields.video.referenceImageUrl;
|
|
75
|
+
if (fields.video.audio !== void 0) body.audio = fields.video.audio;
|
|
76
|
+
if (fields.video.mode) body.mode = fields.video.mode;
|
|
77
|
+
}
|
|
78
|
+
if (fields.type === "speech") {
|
|
79
|
+
if (fields.speech.voice) body.voice = fields.speech.voice;
|
|
80
|
+
if (fields.speech.speed !== void 0) body.speed = fields.speech.speed;
|
|
81
|
+
}
|
|
82
|
+
if (fields.type === "avatar" && fields.avatar) Object.assign(body, {
|
|
83
|
+
audioUrl: fields.avatar.audioUrl.trim(),
|
|
84
|
+
imageUrl: fields.avatar.imageUrl.trim() || void 0,
|
|
85
|
+
avatarId: fields.avatar.avatarId.trim() || void 0
|
|
86
|
+
});
|
|
87
|
+
if (fields.type === "transcription" && fields.transcription) {
|
|
88
|
+
const temperature = Number(fields.transcription.temperature);
|
|
89
|
+
Object.assign(body, {
|
|
90
|
+
audioUrl: fields.transcription.audioUrl.trim(),
|
|
91
|
+
language: fields.transcription.language.trim() || void 0,
|
|
92
|
+
responseFormat: fields.transcription.responseFormat,
|
|
93
|
+
// omit (let the API default) rather than serialize NaN → null on bad input
|
|
94
|
+
temperature: Number.isFinite(temperature) ? temperature : void 0
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return body;
|
|
98
|
+
}
|
|
99
|
+
function generationStatus(generation) {
|
|
100
|
+
const metadata = generation.metadata ?? {};
|
|
101
|
+
const status = typeof metadata.generationStatus === "string" ? metadata.generationStatus : "";
|
|
102
|
+
if (status === "pending" || status === "running" || status === "failed" || status === "succeeded") return status;
|
|
103
|
+
return generation.result ? "succeeded" : "pending";
|
|
104
|
+
}
|
|
105
|
+
function generationError(generation) {
|
|
106
|
+
const metadata = generation.metadata ?? {};
|
|
107
|
+
if (typeof metadata.providerError === "string" && metadata.providerError.trim()) {
|
|
108
|
+
return userSafeGenerationMessage(metadata.providerError);
|
|
109
|
+
}
|
|
110
|
+
if (typeof metadata.storageError === "string" && metadata.storageError.trim()) {
|
|
111
|
+
return metadata.storageError;
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
function generationClientRequestId(generation) {
|
|
116
|
+
const metadata = generation.metadata ?? {};
|
|
117
|
+
return typeof metadata.clientRequestId === "string" && metadata.clientRequestId.trim() ? metadata.clientRequestId : null;
|
|
118
|
+
}
|
|
119
|
+
function generationBatchSlotKey(generation) {
|
|
120
|
+
const metadata = generation.metadata ?? {};
|
|
121
|
+
const batchId = typeof metadata.batchId === "string" && metadata.batchId.trim() ? metadata.batchId : null;
|
|
122
|
+
return batchId && typeof metadata.outputIndex === "number" ? `${batchId}:${metadata.outputIndex}` : null;
|
|
123
|
+
}
|
|
124
|
+
function generationMergeKey(generation) {
|
|
125
|
+
return generationBatchSlotKey(generation) ?? generationClientRequestId(generation);
|
|
126
|
+
}
|
|
127
|
+
function mergeLiveGeneration(current, generation) {
|
|
128
|
+
const mergeKey = generationMergeKey(generation);
|
|
129
|
+
const existingIndex = current.findIndex((item) => item.id === generation.id || mergeKey && generationMergeKey(item) === mergeKey);
|
|
130
|
+
if (existingIndex === -1) return [generation, ...current];
|
|
131
|
+
const next = [...current];
|
|
132
|
+
next[existingIndex] = generation;
|
|
133
|
+
return next;
|
|
134
|
+
}
|
|
135
|
+
function mergeLoaderAndLive(loader, live) {
|
|
136
|
+
if (live.length === 0) return loader;
|
|
137
|
+
const leading = live.map((generation) => {
|
|
138
|
+
const mergeKey = generationMergeKey(generation);
|
|
139
|
+
return mergeKey ? loader.find((gen) => generationMergeKey(gen) === mergeKey) ?? generation : loader.find((gen) => gen.id === generation.id) ?? generation;
|
|
140
|
+
});
|
|
141
|
+
const leadingIds = new Set(leading.map((gen) => gen.id));
|
|
142
|
+
const leadingMergeKeys = new Set(leading.map((gen) => generationMergeKey(gen)).filter((id) => Boolean(id)));
|
|
143
|
+
return [
|
|
144
|
+
...leading,
|
|
145
|
+
...loader.filter((gen) => !leadingIds.has(gen.id) && !leadingMergeKeys.has(generationMergeKey(gen) ?? ""))
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
function isLocalGeneration(generation) {
|
|
149
|
+
return generation.id.startsWith("local-");
|
|
150
|
+
}
|
|
151
|
+
function generationOutputIndex(generation) {
|
|
152
|
+
const value = generation.metadata?.outputIndex;
|
|
153
|
+
return typeof value === "number" ? value : 0;
|
|
154
|
+
}
|
|
155
|
+
function latestBatchOf(generations) {
|
|
156
|
+
const first = generations[0];
|
|
157
|
+
if (!first) return [];
|
|
158
|
+
const key = generationClientRequestId(first);
|
|
159
|
+
const batch = key ? generations.filter((generation) => generationClientRequestId(generation) === key) : [first];
|
|
160
|
+
return [...batch].sort((a, b) => generationOutputIndex(a) - generationOutputIndex(b));
|
|
161
|
+
}
|
|
162
|
+
function userSafeGenerationMessage(message) {
|
|
163
|
+
if (!message) return "Generation failed";
|
|
164
|
+
if (/Tangle API key is invalid or expired/i.test(message)) return message;
|
|
165
|
+
if (/(api[_ -]?key|secret|token|credential|env|configured|configuration)/i.test(message)) {
|
|
166
|
+
return "Generation failed";
|
|
167
|
+
}
|
|
168
|
+
return message;
|
|
169
|
+
}
|
|
170
|
+
function optimisticGeneration({
|
|
171
|
+
type,
|
|
172
|
+
prompt,
|
|
173
|
+
model,
|
|
174
|
+
clientRequestId,
|
|
175
|
+
outputIndex,
|
|
176
|
+
outputCount
|
|
177
|
+
}) {
|
|
178
|
+
const batchId = outputIndex == null ? void 0 : clientRequestId;
|
|
179
|
+
return {
|
|
180
|
+
id: outputIndex == null ? `local-${clientRequestId}` : `local-${clientRequestId}-${outputIndex}`,
|
|
181
|
+
type,
|
|
182
|
+
prompt,
|
|
183
|
+
result: null,
|
|
184
|
+
model: model ?? null,
|
|
185
|
+
cost: null,
|
|
186
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
187
|
+
metadata: {
|
|
188
|
+
generationStatus: "pending",
|
|
189
|
+
provider: type,
|
|
190
|
+
clientRequestId,
|
|
191
|
+
batchId,
|
|
192
|
+
outputIndex,
|
|
193
|
+
outputCount
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function failedOptimisticGeneration(generation) {
|
|
198
|
+
return {
|
|
199
|
+
...generation,
|
|
200
|
+
metadata: {
|
|
201
|
+
...generation.metadata ?? {},
|
|
202
|
+
generationStatus: "failed",
|
|
203
|
+
providerError: "Generation failed"
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function normalizeImageCount(value) {
|
|
208
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
209
|
+
if (!Number.isFinite(numeric)) return MIN_IMAGE_COUNT;
|
|
210
|
+
return Math.min(Math.max(Math.trunc(numeric), MIN_IMAGE_COUNT), MAX_IMAGE_COUNT);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/studio/model-options.ts
|
|
214
|
+
var SEEDANCE_2_0 = {
|
|
215
|
+
duration: {
|
|
216
|
+
values: ["auto", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"],
|
|
217
|
+
default: "auto"
|
|
218
|
+
},
|
|
219
|
+
resolution: { values: ["480p", "720p", "1080p", "4k"], default: "720p" },
|
|
220
|
+
aspect_ratio: { values: ["auto", "21:9", "16:9", "4:3", "1:1", "3:4", "9:16"], default: "auto" },
|
|
221
|
+
audio: { default: true }
|
|
222
|
+
};
|
|
223
|
+
var FALLBACK_VIDEO_MODEL_OPTIONS = {
|
|
224
|
+
"runway/gen4.5": {
|
|
225
|
+
duration: { min: 2, max: 10, default: 5 },
|
|
226
|
+
aspect_ratio: { values: ["16:9", "9:16"], default: "16:9" },
|
|
227
|
+
resolution: { supported: false },
|
|
228
|
+
audio: { supported: false }
|
|
229
|
+
},
|
|
230
|
+
"runway/gen4_turbo": {
|
|
231
|
+
duration: { min: 2, max: 10, default: 5 },
|
|
232
|
+
aspect_ratio: { values: ["16:9", "9:16", "4:3", "3:4", "1:1", "21:9"], default: "16:9" },
|
|
233
|
+
resolution: { supported: false },
|
|
234
|
+
audio: { supported: false }
|
|
235
|
+
},
|
|
236
|
+
"kling/kling-v1-6": {
|
|
237
|
+
duration: { values: [5, 10], default: 5 },
|
|
238
|
+
aspect_ratio: { values: ["16:9", "9:16", "1:1"], default: "16:9" },
|
|
239
|
+
resolution: { supported: false },
|
|
240
|
+
audio: { supported: false },
|
|
241
|
+
mode: { values: ["std", "pro"], default: "std" }
|
|
242
|
+
},
|
|
243
|
+
"kling/kling-v2-master": {
|
|
244
|
+
duration: { values: [5, 10], default: 5 },
|
|
245
|
+
aspect_ratio: { values: ["16:9", "9:16", "1:1"], default: "16:9" },
|
|
246
|
+
resolution: { supported: false },
|
|
247
|
+
audio: { supported: false },
|
|
248
|
+
mode: { supported: false }
|
|
249
|
+
},
|
|
250
|
+
"bytedance/seedance-2.0/text-to-video": SEEDANCE_2_0,
|
|
251
|
+
"bytedance/seedance-2.0/image-to-video": SEEDANCE_2_0,
|
|
252
|
+
"fal-ai/kling-video/v3/pro/text-to-video": {
|
|
253
|
+
duration: { values: ["3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], default: "5" },
|
|
254
|
+
resolution: { supported: false },
|
|
255
|
+
aspect_ratio: { values: ["16:9", "9:16", "1:1"], default: "16:9" },
|
|
256
|
+
audio: { default: true }
|
|
257
|
+
},
|
|
258
|
+
"fal-ai/veo3.1": {
|
|
259
|
+
duration: { values: ["4s", "6s", "8s"], default: "8s" },
|
|
260
|
+
resolution: { values: ["720p", "1080p", "4k"], default: "720p" },
|
|
261
|
+
aspect_ratio: { values: ["16:9", "9:16"], default: "16:9" },
|
|
262
|
+
audio: { default: true }
|
|
263
|
+
},
|
|
264
|
+
"xai/grok-imagine-video/text-to-video": {
|
|
265
|
+
duration: { min: 1, max: 15, default: 6 },
|
|
266
|
+
resolution: { values: ["480p", "720p"], default: "720p" },
|
|
267
|
+
aspect_ratio: { values: ["16:9", "4:3", "3:2", "1:1", "2:3", "3:4", "9:16"], default: "16:9" },
|
|
268
|
+
audio: { supported: false }
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
var IMAGE_MODEL_OPTIONS = {
|
|
272
|
+
"gpt-image-2": {
|
|
273
|
+
size: { values: ["auto", "1024x1024", "1536x1024", "1024x1536"], default: "auto" },
|
|
274
|
+
quality: { values: ["low", "medium", "high", "auto"], default: "auto" },
|
|
275
|
+
n: { values: [1, 2, 4, 8], default: 1 }
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
var OPENAI_TTS_VOICES = ["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"];
|
|
279
|
+
var OPENAI_GPT4O_MINI_TTS_VOICES = [...OPENAI_TTS_VOICES, "ballad", "cedar", "marin", "verse"];
|
|
280
|
+
var GOOGLE_TTS_VOICE_ALIASES = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"];
|
|
281
|
+
var OPENAI_AUDIO_MODEL_OPTIONS = {
|
|
282
|
+
"tts-1": {
|
|
283
|
+
voice: { values: OPENAI_TTS_VOICES, default: "alloy" },
|
|
284
|
+
speed: { min: 0.25, max: 4, default: 1 }
|
|
285
|
+
},
|
|
286
|
+
"tts-1-hd": {
|
|
287
|
+
voice: { values: OPENAI_TTS_VOICES, default: "alloy" },
|
|
288
|
+
speed: { min: 0.25, max: 4, default: 1 }
|
|
289
|
+
},
|
|
290
|
+
"gpt-4o-mini-tts": {
|
|
291
|
+
voice: { values: OPENAI_GPT4O_MINI_TTS_VOICES, default: "alloy" },
|
|
292
|
+
speed: { min: 0.25, max: 4, default: 1 }
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
var GOOGLE_AUDIO_MODEL_OPTIONS = {
|
|
296
|
+
voice: { values: GOOGLE_TTS_VOICE_ALIASES, default: "alloy" },
|
|
297
|
+
speed: { supported: false }
|
|
298
|
+
};
|
|
299
|
+
var GPT_IMAGE_2_CUSTOM_SIZE = { multipleOf: 16, maxLongEdge: 3840, maxRatio: 3 };
|
|
300
|
+
function validateCustomImageSize(width, height) {
|
|
301
|
+
if (!Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0) {
|
|
302
|
+
return { ok: false, reason: "Width and height must be positive integers." };
|
|
303
|
+
}
|
|
304
|
+
if (width % GPT_IMAGE_2_CUSTOM_SIZE.multipleOf !== 0 || height % GPT_IMAGE_2_CUSTOM_SIZE.multipleOf !== 0) {
|
|
305
|
+
return { ok: false, reason: "Each side must be a multiple of 16." };
|
|
306
|
+
}
|
|
307
|
+
if (Math.max(width, height) > GPT_IMAGE_2_CUSTOM_SIZE.maxLongEdge) {
|
|
308
|
+
return { ok: false, reason: "The long edge must be 3840 pixels or less." };
|
|
309
|
+
}
|
|
310
|
+
if (Math.max(width / height, height / width) > GPT_IMAGE_2_CUSTOM_SIZE.maxRatio) {
|
|
311
|
+
return { ok: false, reason: "The aspect ratio must be between 1:3 and 3:1." };
|
|
312
|
+
}
|
|
313
|
+
return { ok: true };
|
|
314
|
+
}
|
|
315
|
+
var KNOWN_PROVIDER_ALIASES = /* @__PURE__ */ new Set([
|
|
316
|
+
"openai",
|
|
317
|
+
"google",
|
|
318
|
+
"gemini",
|
|
319
|
+
"fal",
|
|
320
|
+
"fal-ai",
|
|
321
|
+
"runway",
|
|
322
|
+
"kling",
|
|
323
|
+
"bytedance",
|
|
324
|
+
"xai"
|
|
325
|
+
]);
|
|
326
|
+
function bareSingleSlashId(modelId) {
|
|
327
|
+
const segments = modelId.split("/");
|
|
328
|
+
if (segments.length !== 2 || !KNOWN_PROVIDER_ALIASES.has(segments[0] ?? "")) return void 0;
|
|
329
|
+
return segments[1];
|
|
330
|
+
}
|
|
331
|
+
function audioOptions(modelId, provider) {
|
|
332
|
+
const bareId = bareSingleSlashId(modelId) ?? modelId;
|
|
333
|
+
const exact = OPENAI_AUDIO_MODEL_OPTIONS[modelId] ?? OPENAI_AUDIO_MODEL_OPTIONS[bareId];
|
|
334
|
+
if (exact) return exact;
|
|
335
|
+
const normalizedProvider = provider?.toLowerCase();
|
|
336
|
+
if (bareId.toLowerCase().startsWith("gemini") && bareId.toLowerCase().includes("tts") || normalizedProvider === "google" || normalizedProvider === "gemini") return GOOGLE_AUDIO_MODEL_OPTIONS;
|
|
337
|
+
return void 0;
|
|
338
|
+
}
|
|
339
|
+
function resolveComposerOptions(input) {
|
|
340
|
+
if (input.catalogOptions) return input.catalogOptions;
|
|
341
|
+
if (input.type === "speech") return audioOptions(input.modelId, input.provider);
|
|
342
|
+
const table = input.type === "image" ? IMAGE_MODEL_OPTIONS : FALLBACK_VIDEO_MODEL_OPTIONS;
|
|
343
|
+
const exact = table[input.modelId];
|
|
344
|
+
if (exact) return exact;
|
|
345
|
+
const bareId = bareSingleSlashId(input.modelId);
|
|
346
|
+
return bareId ? table[bareId] : void 0;
|
|
347
|
+
}
|
|
348
|
+
function supportsCustomImageSize(modelId) {
|
|
349
|
+
return modelId === "gpt-image-2" || bareSingleSlashId(modelId) === "gpt-image-2";
|
|
350
|
+
}
|
|
351
|
+
var IMAGE_TO_VIDEO_SIBLINGS = {
|
|
352
|
+
"bytedance/seedance-2.0/text-to-video": "bytedance/seedance-2.0/image-to-video"
|
|
353
|
+
};
|
|
354
|
+
function imageToVideoSibling(modelId) {
|
|
355
|
+
return IMAGE_TO_VIDEO_SIBLINGS[modelId];
|
|
356
|
+
}
|
|
357
|
+
function textToVideoSibling(modelId) {
|
|
358
|
+
return Object.entries(IMAGE_TO_VIDEO_SIBLINGS).find(([, sibling]) => sibling === modelId)?.[0];
|
|
359
|
+
}
|
|
360
|
+
function curateComposerModels(type, models) {
|
|
361
|
+
if (type === "image") return models.filter((model) => supportsCustomImageSize(model.id));
|
|
362
|
+
if (type === "video") {
|
|
363
|
+
const imageToVideoIds = new Set(Object.values(IMAGE_TO_VIDEO_SIBLINGS));
|
|
364
|
+
return models.filter((model) => !model.id.toLowerCase().includes("sora") && !imageToVideoIds.has(model.id));
|
|
365
|
+
}
|
|
366
|
+
return models;
|
|
367
|
+
}
|
|
368
|
+
function optionDefault(meta) {
|
|
369
|
+
return meta.default ?? meta.values?.[0] ?? meta.min;
|
|
370
|
+
}
|
|
371
|
+
function optionChoices(meta) {
|
|
372
|
+
if (meta.values) return meta.values;
|
|
373
|
+
if (meta.min == null || meta.max == null) return [];
|
|
374
|
+
const values = [];
|
|
375
|
+
for (let value = Math.ceil(meta.min); value <= Math.floor(meta.max); value += 1) values.push(value);
|
|
376
|
+
return values;
|
|
377
|
+
}
|
|
378
|
+
function isCustomSize(value) {
|
|
379
|
+
if (typeof value !== "string") return false;
|
|
380
|
+
const match = /^(\d+)x(\d+)$/.exec(value);
|
|
381
|
+
if (!match) return false;
|
|
382
|
+
return validateCustomImageSize(Number(match[1]), Number(match[2])).ok;
|
|
383
|
+
}
|
|
384
|
+
function isLegalOptionValue(meta, value) {
|
|
385
|
+
if (meta.values) return meta.values.includes(value);
|
|
386
|
+
if (typeof value === "number" && meta.min != null && meta.max != null) {
|
|
387
|
+
return value >= meta.min && value <= meta.max;
|
|
388
|
+
}
|
|
389
|
+
return meta.min == null && meta.max == null;
|
|
390
|
+
}
|
|
391
|
+
function reconcileOptionValues(options, current, opts) {
|
|
392
|
+
if (!options) return {};
|
|
393
|
+
const reconciled = {};
|
|
394
|
+
for (const [key, meta] of Object.entries(options)) {
|
|
395
|
+
if (meta.supported === false) continue;
|
|
396
|
+
const selected = current[key];
|
|
397
|
+
const customSizeIsLegal = key === "size" && selected !== void 0 && opts?.allowCustomSize === true && isCustomSize(selected);
|
|
398
|
+
const selectionIsLegal = selected !== void 0 && (isLegalOptionValue(meta, selected) || customSizeIsLegal);
|
|
399
|
+
const next = selectionIsLegal ? selected : optionDefault(meta);
|
|
400
|
+
if (next !== void 0) reconciled[key] = next;
|
|
401
|
+
}
|
|
402
|
+
return reconciled;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export {
|
|
406
|
+
GENERATION_TYPES,
|
|
407
|
+
isGenerationType,
|
|
408
|
+
MIN_IMAGE_COUNT,
|
|
409
|
+
MAX_IMAGE_COUNT,
|
|
410
|
+
relativeTime,
|
|
411
|
+
outputPathFor,
|
|
412
|
+
generationVaultPath,
|
|
413
|
+
selectedModelsWithDefaults,
|
|
414
|
+
preferredModelId,
|
|
415
|
+
modelMessage,
|
|
416
|
+
buildGenerationRequestBody,
|
|
417
|
+
generationStatus,
|
|
418
|
+
generationError,
|
|
419
|
+
generationMergeKey,
|
|
420
|
+
mergeLiveGeneration,
|
|
421
|
+
mergeLoaderAndLive,
|
|
422
|
+
isLocalGeneration,
|
|
423
|
+
latestBatchOf,
|
|
424
|
+
userSafeGenerationMessage,
|
|
425
|
+
optimisticGeneration,
|
|
426
|
+
failedOptimisticGeneration,
|
|
427
|
+
normalizeImageCount,
|
|
428
|
+
FALLBACK_VIDEO_MODEL_OPTIONS,
|
|
429
|
+
GPT_IMAGE_2_CUSTOM_SIZE,
|
|
430
|
+
validateCustomImageSize,
|
|
431
|
+
resolveComposerOptions,
|
|
432
|
+
supportsCustomImageSize,
|
|
433
|
+
IMAGE_TO_VIDEO_SIBLINGS,
|
|
434
|
+
imageToVideoSibling,
|
|
435
|
+
textToVideoSibling,
|
|
436
|
+
curateComposerModels,
|
|
437
|
+
optionDefault,
|
|
438
|
+
optionChoices,
|
|
439
|
+
reconcileOptionValues
|
|
440
|
+
};
|
|
441
|
+
//# sourceMappingURL=chunk-2RTYQU4W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/studio/generation.ts","../src/studio/model-options.ts"],"sourcesContent":["import type { ModelOptionsMetadata } from './model-options'\n\n/** Define generation categories for media including image, video, speech, avatar, and transcription */\nexport type GenerationType = 'image' | 'video' | 'speech' | 'avatar' | 'transcription'\n\n/** Define possible states representing the progress of a generation process */\nexport type GenerationStatus = 'pending' | 'running' | 'succeeded' | 'failed'\n\n/** Define possible status values for a media model's availability and accessibility */\nexport type MediaModelStatus = 'available' | 'limited' | 'unavailable'\n\n/** Define the structure for a generation entity including its metadata and creation details */\nexport interface Generation {\n id: string\n type: string\n prompt: string\n result: string | null\n model: string | null\n cost: number | null\n createdAt: Date | null\n metadata: Record<string, unknown> | null\n}\n\n/** Describe a catalog media model and its optional wire-level option metadata. */\nexport interface MediaModelOption {\n id: string\n name: string\n provider?: string\n type: GenerationType\n status: MediaModelStatus\n reason?: string\n options?: ModelOptionsMetadata\n}\n\n/** Represent media model catalog with default values, model options, and optional error message */\nexport interface MediaModelCatalogResponse {\n defaults: Record<GenerationType, string>\n models: Record<GenerationType, MediaModelOption[]>\n error?: string\n}\n\n// Order drives the library type filter tabs. The composer offers its own\n// subset (`COMPOSER_TYPES` in studio-react) while avatar/transcription are\n// disabled (#451).\n/** Provide an array of supported generation types for media and content processing */\nexport const GENERATION_TYPES: readonly GenerationType[] = ['image', 'video', 'avatar', 'speech', 'transcription']\n\n/** Resolve whether a string value matches a valid GenerationType */\nexport function isGenerationType(value: string): value is GenerationType {\n return (GENERATION_TYPES as readonly string[]).includes(value)\n}\n\n/** Define the minimum number of images required for processing or validation */\nexport const MIN_IMAGE_COUNT = 1\n/** Define the maximum number of images allowed for upload or display */\nexport const MAX_IMAGE_COUNT = 8\n\n/** Resolve a human-readable relative time string from a given date or return an empty string if null */\nexport function relativeTime(date: Date | null): string {\n if (!date) return ''\n const now = Date.now()\n const diff = now - new Date(date).getTime()\n const minutes = Math.floor(diff / 60000)\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes}m ago`\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return `${hours}h ago`\n const days = Math.floor(hours / 24)\n if (days < 7) return `${days}d ago`\n return new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n}\n\n/** Resolve the output directory path based on the specified generation type */\nexport function outputPathFor(type: GenerationType): string {\n if (type === 'image') return 'generated/images'\n if (type === 'video') return 'generated/videos'\n if (type === 'avatar') return 'generated/avatars'\n if (type === 'speech') return 'generated/audio'\n return 'generated/transcripts'\n}\n\n/** Resolve the vault path string from a Generation object or return null if unavailable */\nexport function generationVaultPath(generation: Generation): string | null {\n const value = generation.metadata?.vaultPath\n return typeof value === 'string' && value.trim() ? value.trim() : null\n}\n\n/** Resolve selected models by applying defaults for missing or unavailable entries in the catalog */\nexport function selectedModelsWithDefaults(\n current: Partial<Record<GenerationType, string>>,\n catalog: MediaModelCatalogResponse,\n): Partial<Record<GenerationType, string>> {\n const next = { ...current }\n for (const key of GENERATION_TYPES) {\n const models = catalog.models[key] ?? []\n const currentOption = models.find((model) => model.id === next[key])\n // Reset when: no selection, selection not in catalog, or selection is unavailable.\n // This ensures the Generate button is never stuck disabled when routeable\n // models exist but the stored default isn't one of them.\n if (!next[key] || !currentOption || currentOption.status === 'unavailable') {\n next[key] = preferredModelId(key, catalog) ?? ''\n }\n }\n return next\n}\n\n/** Resolve the preferred model ID for a given generation type from the media model catalog */\nexport function preferredModelId(type: GenerationType, catalog: MediaModelCatalogResponse | null): string | undefined {\n if (!catalog) return undefined\n const models = catalog.models[type] ?? []\n const preferred = catalog.defaults[type]\n return models.find((model) => model.id === preferred)?.id\n ?? models.find((model) => model.status !== 'unavailable')?.id\n ?? models[0]?.id\n}\n\n/** Resolve the appropriate status message for a media model based on loading state and availability */\nexport function modelMessage(model: MediaModelOption | undefined, loading: boolean, count: number): string | null {\n if (loading) return 'Loading media models...'\n if (count === 0) return 'No models are available for this media type.'\n if (!model) return 'Select a model.'\n if (model.status === 'unavailable') return model.reason ?? 'This model is not configured.'\n if (model.status === 'limited') return model.reason ? `Limited: ${model.reason}` : 'Limited availability.'\n return null\n}\n\n/** Define fields required to configure and request various types of media generation */\nexport interface GenerationRequestFields {\n workspaceId: string\n clientRequestId: string\n type: GenerationType\n model: string\n prompt: string\n // Every per-lane parameter except the image COUNT is optional, because the\n // composer only sends what the selected model publishes: a model whose\n // metadata omits `size` (or marks it `supported: false`) must send no `size`\n // at all, and a model that publishes nothing — `ltx-video` — sends only the\n // prompt. `count` stays required: it is the number of optimistic cards the\n // caller already drew, not a model parameter.\n image: { size?: string; quality?: string; count: number }\n video: {\n duration?: string | number\n resolution?: string\n aspectRatio?: string\n referenceImageUrl?: string\n audio?: boolean\n mode?: string\n }\n speech: { voice?: string; speed?: number }\n // Optional while the composer lanes are disabled (#451); the server capability stays.\n avatar?: { audioUrl: string; imageUrl: string; avatarId: string }\n transcription?: { audioUrl: string; language: string; responseFormat: string; temperature: string }\n}\n\n// image.count must already be normalized — it is also the optimistic-card count on the caller side\n/** Build the request body object for a generation operation from provided fields */\nexport function buildGenerationRequestBody(fields: GenerationRequestFields): Record<string, unknown> {\n const body: Record<string, unknown> = {\n workspaceId: fields.workspaceId,\n clientRequestId: fields.clientRequestId,\n type: fields.type,\n model: fields.model,\n prompt: fields.prompt.trim(),\n }\n if (fields.type === 'image') {\n if (fields.image.size) body.size = fields.image.size\n if (fields.image.quality) body.quality = fields.image.quality\n body.n = fields.image.count\n }\n if (fields.type === 'video') {\n if (fields.video.duration !== undefined) body.duration = fields.video.duration\n if (fields.video.resolution) body.resolution = fields.video.resolution\n if (fields.video.aspectRatio) body.aspectRatio = fields.video.aspectRatio\n if (fields.video.referenceImageUrl) body.referenceImageUrl = fields.video.referenceImageUrl\n if (fields.video.audio !== undefined) body.audio = fields.video.audio\n if (fields.video.mode) body.mode = fields.video.mode\n }\n if (fields.type === 'speech') {\n if (fields.speech.voice) body.voice = fields.speech.voice\n if (fields.speech.speed !== undefined) body.speed = fields.speech.speed\n }\n if (fields.type === 'avatar' && fields.avatar) Object.assign(body, {\n audioUrl: fields.avatar.audioUrl.trim(),\n imageUrl: fields.avatar.imageUrl.trim() || undefined,\n avatarId: fields.avatar.avatarId.trim() || undefined,\n })\n if (fields.type === 'transcription' && fields.transcription) {\n const temperature = Number(fields.transcription.temperature)\n Object.assign(body, {\n audioUrl: fields.transcription.audioUrl.trim(),\n language: fields.transcription.language.trim() || undefined,\n responseFormat: fields.transcription.responseFormat,\n // omit (let the API default) rather than serialize NaN → null on bad input\n temperature: Number.isFinite(temperature) ? temperature : undefined,\n })\n }\n return body\n}\n\n/** Resolve the current status of a generation based on its metadata and result fields */\nexport function generationStatus(generation: Generation): GenerationStatus {\n const metadata = generation.metadata ?? {}\n const status = typeof metadata.generationStatus === 'string' ? metadata.generationStatus : ''\n if (status === 'pending' || status === 'running' || status === 'failed' || status === 'succeeded') return status\n return generation.result ? 'succeeded' : 'pending'\n}\n\n/** Resolve and return the first user-safe error message from generation metadata or null if none exist */\nexport function generationError(generation: Generation): string | null {\n const metadata = generation.metadata ?? {}\n if (typeof metadata.providerError === 'string' && metadata.providerError.trim()) {\n return userSafeGenerationMessage(metadata.providerError)\n }\n if (typeof metadata.storageError === 'string' && metadata.storageError.trim()) {\n return metadata.storageError\n }\n return null\n}\n\nfunction generationClientRequestId(generation: Generation): string | null {\n const metadata = generation.metadata ?? {}\n return typeof metadata.clientRequestId === 'string' && metadata.clientRequestId.trim()\n ? metadata.clientRequestId\n : null\n}\n\nfunction generationBatchSlotKey(generation: Generation): string | null {\n const metadata = generation.metadata ?? {}\n const batchId = typeof metadata.batchId === 'string' && metadata.batchId.trim() ? metadata.batchId : null\n return batchId && typeof metadata.outputIndex === 'number'\n ? `${batchId}:${metadata.outputIndex}`\n : null\n}\n\n/** Resolve a unique merge key from a generation using batch slot or client request ID */\nexport function generationMergeKey(generation: Generation): string | null {\n return generationBatchSlotKey(generation) ?? generationClientRequestId(generation)\n}\n\n/** Merge a new generation into the current list by replacing or prepending it based on matching keys */\nexport function mergeLiveGeneration(current: Generation[], generation: Generation): Generation[] {\n const mergeKey = generationMergeKey(generation)\n const existingIndex = current.findIndex((item) => (\n item.id === generation.id\n || (mergeKey && generationMergeKey(item) === mergeKey)\n ))\n if (existingIndex === -1) return [generation, ...current]\n\n const next = [...current]\n next[existingIndex] = generation\n return next\n}\n\n// Overlay in-flight `live` generations on the loader's rows: each live row leads\n// (prefer the matching loader row by merge key / id so it carries the freshest\n// server state), then the remaining loader rows that no live row already\n// represents — deduped by BOTH id and merge key so a server row and its\n// optimistic twin never both appear. Returns `loader` unchanged when nothing is\n// live. Drives the canvas, library, and polling off one list.\n/** Merge two Generation arrays prioritizing live entries and matching by merge keys or IDs */\nexport function mergeLoaderAndLive(loader: Generation[], live: Generation[]): Generation[] {\n if (live.length === 0) return loader\n const leading = live.map((generation) => {\n const mergeKey = generationMergeKey(generation)\n return mergeKey\n ? loader.find((gen) => generationMergeKey(gen) === mergeKey) ?? generation\n : loader.find((gen) => gen.id === generation.id) ?? generation\n })\n const leadingIds = new Set(leading.map((gen) => gen.id))\n const leadingMergeKeys = new Set(leading\n .map((gen) => generationMergeKey(gen))\n .filter((id): id is string => Boolean(id)))\n return [\n ...leading,\n ...loader.filter((gen) => (\n !leadingIds.has(gen.id)\n && !leadingMergeKeys.has(generationMergeKey(gen) ?? '')\n )),\n ]\n}\n\n/** Determine if a generation ID indicates a local generation */\nexport function isLocalGeneration(generation: Generation): boolean {\n return generation.id.startsWith('local-')\n}\n\nfunction generationOutputIndex(generation: Generation): number {\n const value = generation.metadata?.outputIndex\n return typeof value === 'number' ? value : 0\n}\n\n// The most-recent run: all generations sharing the leading item's clientRequestId\n// (a multi-image batch), ordered by output slot. Falls back to the single leading\n// item when no request id is present. Drives the result canvas.\n/** Resolve and return the latest batch of generations grouped and sorted by client request ID and output index */\nexport function latestBatchOf(generations: Generation[]): Generation[] {\n const first = generations[0]\n if (!first) return []\n const key = generationClientRequestId(first)\n const batch = key\n ? generations.filter((generation) => generationClientRequestId(generation) === key)\n : [first]\n return [...batch].sort((a, b) => generationOutputIndex(a) - generationOutputIndex(b))\n}\n\n/** Resolve a user-safe generation message by filtering sensitive or error-related content */\nexport function userSafeGenerationMessage(message?: string): string {\n if (!message) return 'Generation failed'\n if (/Tangle API key is invalid or expired/i.test(message)) return message\n if (/(api[_ -]?key|secret|token|credential|env|configured|configuration)/i.test(message)) {\n return 'Generation failed'\n }\n return message\n}\n\n/** Generate content optimistically based on input parameters and optional model and output details */\nexport function optimisticGeneration({\n type,\n prompt,\n model,\n clientRequestId,\n outputIndex,\n outputCount,\n}: {\n type: GenerationType\n prompt: string\n model?: string\n clientRequestId: string\n outputIndex?: number\n outputCount?: number\n}): Generation {\n const batchId = outputIndex == null ? undefined : clientRequestId\n return {\n id: outputIndex == null ? `local-${clientRequestId}` : `local-${clientRequestId}-${outputIndex}`,\n type,\n prompt,\n result: null,\n model: model ?? null,\n cost: null,\n createdAt: new Date(),\n metadata: {\n generationStatus: 'pending',\n provider: type,\n clientRequestId,\n batchId,\n outputIndex,\n outputCount,\n },\n }\n}\n\n/** Mark a generation as failed with updated status and error information */\nexport function failedOptimisticGeneration(generation: Generation): Generation {\n return {\n ...generation,\n metadata: {\n ...(generation.metadata ?? {}),\n generationStatus: 'failed',\n providerError: 'Generation failed',\n },\n }\n}\n\n/** Normalize a value to a finite integer within the allowed image count range */\nexport function normalizeImageCount(value: unknown): number {\n const numeric = typeof value === 'number' ? value : Number(value)\n if (!Number.isFinite(numeric)) return MIN_IMAGE_COUNT\n return Math.min(Math.max(Math.trunc(numeric), MIN_IMAGE_COUNT), MAX_IMAGE_COUNT)\n}\n","import type { GenerationType, MediaModelOption } from './generation'\n\n/** A wire-typed value accepted by a model option. */\nexport type ModelOptionValue = string | number | boolean\n\n/** Per-parameter option metadata, structurally identical to tangle-router's\n * `ModelOptionMetadata` (lib/model-options.ts, shipped in router PR #429).\n * `supported: false` means the model lacks or ignores the parameter.\n * `values` is the exact wire-typed enum; `min` and `max` are inclusive.\n * `default` applies when the caller omits the parameter. An absent entry or\n * options object means unknown, so consumers must not invent a value. */\nexport interface ModelOptionMetadata {\n supported?: boolean\n values?: readonly ModelOptionValue[]\n min?: number\n max?: number\n default?: ModelOptionValue\n}\n\n/** Per-parameter model option metadata keyed by the provider's wire field. */\nexport type ModelOptionsMetadata = Readonly<Record<string, ModelOptionMetadata>>\n\nconst SEEDANCE_2_0: ModelOptionsMetadata = {\n duration: {\n values: ['auto', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15'],\n default: 'auto',\n },\n resolution: { values: ['480p', '720p', '1080p', '4k'], default: '720p' },\n aspect_ratio: { values: ['auto', '21:9', '16:9', '4:3', '1:1', '3:4', '9:16'], default: 'auto' },\n audio: { default: true },\n}\n\n// These values mirror tangle-router VIDEO_MODEL_OPTIONS from PR #429,\n// observedAt 2026-08-19. Catalog-provided live options always win.\n/** Fallback video options matching tangle-router's wire-exact metadata. */\nexport const FALLBACK_VIDEO_MODEL_OPTIONS: Readonly<Record<string, ModelOptionsMetadata>> = {\n 'runway/gen4.5': {\n duration: { min: 2, max: 10, default: 5 },\n aspect_ratio: { values: ['16:9', '9:16'], default: '16:9' },\n resolution: { supported: false },\n audio: { supported: false },\n },\n 'runway/gen4_turbo': {\n duration: { min: 2, max: 10, default: 5 },\n aspect_ratio: { values: ['16:9', '9:16', '4:3', '3:4', '1:1', '21:9'], default: '16:9' },\n resolution: { supported: false },\n audio: { supported: false },\n },\n 'kling/kling-v1-6': {\n duration: { values: [5, 10], default: 5 },\n aspect_ratio: { values: ['16:9', '9:16', '1:1'], default: '16:9' },\n resolution: { supported: false },\n audio: { supported: false },\n mode: { values: ['std', 'pro'], default: 'std' },\n },\n 'kling/kling-v2-master': {\n duration: { values: [5, 10], default: 5 },\n aspect_ratio: { values: ['16:9', '9:16', '1:1'], default: '16:9' },\n resolution: { supported: false },\n audio: { supported: false },\n mode: { supported: false },\n },\n 'bytedance/seedance-2.0/text-to-video': SEEDANCE_2_0,\n 'bytedance/seedance-2.0/image-to-video': SEEDANCE_2_0,\n 'fal-ai/kling-video/v3/pro/text-to-video': {\n duration: { values: ['3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15'], default: '5' },\n resolution: { supported: false },\n aspect_ratio: { values: ['16:9', '9:16', '1:1'], default: '16:9' },\n audio: { default: true },\n },\n 'fal-ai/veo3.1': {\n duration: { values: ['4s', '6s', '8s'], default: '8s' },\n resolution: { values: ['720p', '1080p', '4k'], default: '720p' },\n aspect_ratio: { values: ['16:9', '9:16'], default: '16:9' },\n audio: { default: true },\n },\n 'xai/grok-imagine-video/text-to-video': {\n duration: { min: 1, max: 15, default: 6 },\n resolution: { values: ['480p', '720p'], default: '720p' },\n aspect_ratio: { values: ['16:9', '4:3', '3:2', '1:1', '2:3', '3:4', '9:16'], default: '16:9' },\n audio: { supported: false },\n },\n}\n\n// Source: provider research recorded in router #420 and agent-app #449 on\n// 2026-08-18. Live catalog options remain authoritative when present.\nconst IMAGE_MODEL_OPTIONS: Readonly<Record<string, ModelOptionsMetadata>> = {\n 'gpt-image-2': {\n size: { values: ['auto', '1024x1024', '1536x1024', '1024x1536'], default: 'auto' },\n quality: { values: ['low', 'medium', 'high', 'auto'], default: 'auto' },\n n: { values: [1, 2, 4, 8], default: 1 },\n },\n}\n\nconst OPENAI_TTS_VOICES = ['alloy', 'ash', 'coral', 'echo', 'fable', 'onyx', 'nova', 'sage', 'shimmer'] as const\nconst OPENAI_GPT4O_MINI_TTS_VOICES = [...OPENAI_TTS_VOICES, 'ballad', 'cedar', 'marin', 'verse'] as const\n// These aliases are the router's GEMINI_VOICE_MAP keys. The router translates\n// them to Kore, Puck, Charon, Algenib, Aoede, and Leda respectively.\nconst GOOGLE_TTS_VOICE_ALIASES = ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'] as const\n\nconst OPENAI_AUDIO_MODEL_OPTIONS: Readonly<Record<string, ModelOptionsMetadata>> = {\n 'tts-1': {\n voice: { values: OPENAI_TTS_VOICES, default: 'alloy' },\n speed: { min: 0.25, max: 4, default: 1 },\n },\n 'tts-1-hd': {\n voice: { values: OPENAI_TTS_VOICES, default: 'alloy' },\n speed: { min: 0.25, max: 4, default: 1 },\n },\n 'gpt-4o-mini-tts': {\n voice: { values: OPENAI_GPT4O_MINI_TTS_VOICES, default: 'alloy' },\n speed: { min: 0.25, max: 4, default: 1 },\n },\n}\n\nconst GOOGLE_AUDIO_MODEL_OPTIONS: ModelOptionsMetadata = {\n voice: { values: GOOGLE_TTS_VOICE_ALIASES, default: 'alloy' },\n speed: { supported: false },\n}\n\n// Mistral's preset voices are enumerable only through its authenticated\n// /v1/audio/voices API; no publicly verifiable list existed at research time.\n// Unknown means show nothing invented, so Voxtral uses the router's provider\n// default (gb_jane_neutral) until router #420 publishes live catalog options.\n\n/** UI constraints for a custom gpt-image-2 size. */\nexport const GPT_IMAGE_2_CUSTOM_SIZE = { multipleOf: 16, maxLongEdge: 3840, maxRatio: 3 } as const\n\n/** Validate a custom gpt-image-2 size against its published UI constraints. */\nexport function validateCustomImageSize(width: number, height: number): { ok: true } | { ok: false; reason: string } {\n if (!Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0) {\n return { ok: false, reason: 'Width and height must be positive integers.' }\n }\n if (width % GPT_IMAGE_2_CUSTOM_SIZE.multipleOf !== 0 || height % GPT_IMAGE_2_CUSTOM_SIZE.multipleOf !== 0) {\n return { ok: false, reason: 'Each side must be a multiple of 16.' }\n }\n if (Math.max(width, height) > GPT_IMAGE_2_CUSTOM_SIZE.maxLongEdge) {\n return { ok: false, reason: 'The long edge must be 3840 pixels or less.' }\n }\n if (Math.max(width / height, height / width) > GPT_IMAGE_2_CUSTOM_SIZE.maxRatio) {\n return { ok: false, reason: 'The aspect ratio must be between 1:3 and 3:1.' }\n }\n return { ok: true }\n}\n\nconst KNOWN_PROVIDER_ALIASES = new Set([\n 'openai',\n 'google',\n 'gemini',\n 'fal',\n 'fal-ai',\n 'runway',\n 'kling',\n 'bytedance',\n 'xai',\n])\n\nfunction bareSingleSlashId(modelId: string): string | undefined {\n const segments = modelId.split('/')\n if (segments.length !== 2 || !KNOWN_PROVIDER_ALIASES.has(segments[0] ?? '')) return undefined\n return segments[1]\n}\n\nfunction audioOptions(modelId: string, provider?: string): ModelOptionsMetadata | undefined {\n const bareId = bareSingleSlashId(modelId) ?? modelId\n const exact = OPENAI_AUDIO_MODEL_OPTIONS[modelId] ?? OPENAI_AUDIO_MODEL_OPTIONS[bareId]\n if (exact) return exact\n\n const normalizedProvider = provider?.toLowerCase()\n if (\n (bareId.toLowerCase().startsWith('gemini') && bareId.toLowerCase().includes('tts'))\n || normalizedProvider === 'google'\n || normalizedProvider === 'gemini'\n ) return GOOGLE_AUDIO_MODEL_OPTIONS\n\n return undefined\n}\n\n/** Resolve live catalog options first, then exact or safe single-prefix fallbacks. */\nexport function resolveComposerOptions(input: {\n type: 'image' | 'video' | 'speech'\n modelId: string\n provider?: string\n catalogOptions?: ModelOptionsMetadata\n}): ModelOptionsMetadata | undefined {\n if (input.catalogOptions) return input.catalogOptions\n if (input.type === 'speech') return audioOptions(input.modelId, input.provider)\n\n const table = input.type === 'image' ? IMAGE_MODEL_OPTIONS : FALLBACK_VIDEO_MODEL_OPTIONS\n const exact = table[input.modelId]\n if (exact) return exact\n\n // Only a known provider prefix on an id with exactly one slash is stripped;\n // multi-slash fal ids must remain whole.\n const bareId = bareSingleSlashId(input.modelId)\n return bareId ? table[bareId] : undefined\n}\n\n/** Return whether a model supports the gpt-image-2 custom-size rule. */\nexport function supportsCustomImageSize(modelId: string): boolean {\n return modelId === 'gpt-image-2' || bareSingleSlashId(modelId) === 'gpt-image-2'\n}\n\n/** Map verified text-to-video model ids to their image-to-video siblings. */\nexport const IMAGE_TO_VIDEO_SIBLINGS: Readonly<Record<string, string>> = {\n 'bytedance/seedance-2.0/text-to-video': 'bytedance/seedance-2.0/image-to-video',\n}\n\n/** Resolve a verified image-to-video sibling for a text-to-video model. */\nexport function imageToVideoSibling(modelId: string): string | undefined {\n return IMAGE_TO_VIDEO_SIBLINGS[modelId]\n}\n\n/** Resolve the verified text-to-video sibling for an image-to-video model. */\nexport function textToVideoSibling(modelId: string): string | undefined {\n return Object.entries(IMAGE_TO_VIDEO_SIBLINGS).find(([, sibling]) => sibling === modelId)?.[0]\n}\n\n/** Curate catalog models for the issue #449 composer lanes. */\nexport function curateComposerModels(\n type: GenerationType,\n models: MediaModelOption[],\n): MediaModelOption[] {\n if (type === 'image') return models.filter((model) => supportsCustomImageSize(model.id))\n if (type === 'video') {\n const imageToVideoIds = new Set(Object.values(IMAGE_TO_VIDEO_SIBLINGS))\n return models.filter((model) => !model.id.toLowerCase().includes('sora') && !imageToVideoIds.has(model.id))\n }\n return models\n}\n\n/** Resolve an option default from its default, values, or lower bound. */\nexport function optionDefault(meta: ModelOptionMetadata): ModelOptionValue | undefined {\n return meta.default ?? meta.values?.[0] ?? meta.min\n}\n\n/** Return exact enum choices or an inclusive integer range. */\nexport function optionChoices(meta: ModelOptionMetadata): readonly ModelOptionValue[] {\n if (meta.values) return meta.values\n if (meta.min == null || meta.max == null) return []\n const values: number[] = []\n for (let value = Math.ceil(meta.min); value <= Math.floor(meta.max); value += 1) values.push(value)\n return values\n}\n\nfunction isCustomSize(value: ModelOptionValue): boolean {\n if (typeof value !== 'string') return false\n const match = /^(\\d+)x(\\d+)$/.exec(value)\n if (!match) return false\n return validateCustomImageSize(Number(match[1]), Number(match[2])).ok\n}\n\nfunction isLegalOptionValue(meta: ModelOptionMetadata, value: ModelOptionValue): boolean {\n if (meta.values) return meta.values.includes(value)\n if (typeof value === 'number' && meta.min != null && meta.max != null) {\n return value >= meta.min && value <= meta.max\n }\n return meta.min == null && meta.max == null\n}\n\n/** Reconcile selections against supported options and their wire-typed defaults.\n * `allowCustomSize` keeps a legal off-enum `WxH` size selection (gpt-image-2's\n * custom-size rule) — the caller decides via {@link supportsCustomImageSize},\n * so the check holds even when the options came from the live catalog. */\nexport function reconcileOptionValues(\n options: ModelOptionsMetadata | undefined,\n current: Readonly<Record<string, ModelOptionValue>>,\n opts?: { allowCustomSize?: boolean },\n): Record<string, ModelOptionValue> {\n if (!options) return {}\n const reconciled: Record<string, ModelOptionValue> = {}\n for (const [key, meta] of Object.entries(options)) {\n if (meta.supported === false) continue\n const selected = current[key]\n const customSizeIsLegal = key === 'size'\n && selected !== undefined\n && opts?.allowCustomSize === true\n && isCustomSize(selected)\n const selectionIsLegal = selected !== undefined\n && (isLegalOptionValue(meta, selected) || customSizeIsLegal)\n const next = selectionIsLegal ? selected : optionDefault(meta)\n if (next !== undefined) reconciled[key] = next\n }\n return reconciled\n}\n"],"mappings":"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","names":[]}
|