videobook 0.2.3 → 0.2.4
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/CHANGELOG.md +34 -0
- package/README.md +3 -2
- package/dist/command-reference.js +16 -2
- package/dist/env-secrets.js +6 -4
- package/dist/jobs/complete-provider-task.js +7 -1
- package/dist/jobs/generate-media.js +4 -0
- package/dist/jobs/render-project.js +124 -31
- package/dist/media/adapters/index.js +4 -0
- package/dist/media/adapters/machgen.js +48 -0
- package/dist/media/adapters/types.js +1 -0
- package/dist/media/catalog.js +70 -0
- package/dist/media/complete.js +1 -1
- package/dist/media/frontier-gaps.js +49 -0
- package/dist/media/frontier.js +30 -0
- package/dist/media/listing.js +9 -2
- package/dist/media/machgen.js +261 -0
- package/dist/media/model-id.js +1 -1
- package/dist/media/preview.js +2 -0
- package/dist/media/prompt-dialects.js +2 -0
- package/dist/media/seedance-draft.js +48 -0
- package/dist/media/wire.js +3 -0
- package/dist/notebook-action-catalog.js +5 -0
- package/dist/one-password.js +13 -7
- package/dist/provider-status-link.js +1 -1
- package/dist/provider-task-status.js +6 -3
- package/dist/provider-usage.js +5 -3
- package/dist/queue.js +3 -0
- package/dist/tools/commands.js +1 -1
- package/dist/tools/estimate-cost.js +3 -1
- package/dist/tools/provider-key-status.js +2 -0
- package/dist/tools/reconcile-project-work.js +69 -1
- package/dist/usage-providers.js +4 -0
- package/dist/workspace-action-catalog.js +1 -1
- package/package.json +14 -13
- package/ui/assets/{alive-captions-4QySEGdt.js → alive-captions-Bp6dxosy.js} +1 -1
- package/ui/assets/confirm-bodies-Ds1HtaGc.js +1 -0
- package/ui/assets/{fullscreen-CzTnPPG5.js → fullscreen-BCeVHjvY.js} +1 -1
- package/ui/assets/{goal-ui-CosrK_ko.js → goal-ui-DbAGwgMN.js} +2 -2
- package/ui/assets/index-D1d-F1y9.js +37 -0
- package/ui/assets/index-DWfR79lA.css +1 -0
- package/ui/assets/{inspector-BN2fezh4.js → inspector-BJTC0luH.js} +1 -1
- package/ui/assets/{jobs-ui-C4Wgy6I1.js → jobs-ui-C1cbw-xG.js} +1 -1
- package/ui/assets/{music-video-BSj8SgbQ.js → music-video-CWtxPlxq.js} +1 -1
- package/ui/assets/ui-CXDi5Omq.js +2 -0
- package/ui/assets/vendor-BFupLz37.js +19 -0
- package/ui/assets/{vendor-flow-zBavaNEW.js → vendor-flow-CKiuzaeM.js} +1 -1
- package/ui/assets/{vendor-mcp-DQ7EusT9.js → vendor-mcp-DQEeVZH0.js} +1 -1
- package/ui/assets/videobook-J-j9bSjM.js +20 -0
- package/ui/index.html +12 -12
- package/ui/assets/confirm-bodies-Bw818V9J.js +0 -1
- package/ui/assets/index-C9xjFEGL.js +0 -37
- package/ui/assets/index-DzPAnCUj.css +0 -1
- package/ui/assets/ui-Cpr6zMTA.js +0 -2
- package/ui/assets/vendor-yctIZ9Dz.js +0 -19
- package/ui/assets/videobook-B5G0jMoi.js +0 -20
package/dist/media/listing.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import { modelFrontierRankings } from "./frontier.js";
|
|
2
|
+
import { PROVIDER_ENV_KEYS } from "./adapters/index.js";
|
|
1
3
|
import { MODEL_ENTRIES } from "./catalog.js";
|
|
2
4
|
import { resolveModel } from "./descriptor.js";
|
|
5
|
+
export function modelIsHidden(entry) {
|
|
6
|
+
return entry.hidden === true && modelFrontierRankings(entry.id).length === 0;
|
|
7
|
+
}
|
|
3
8
|
export function modelListing(model) {
|
|
4
9
|
return {
|
|
5
10
|
id: model.id,
|
|
@@ -12,12 +17,14 @@ export function modelListing(model) {
|
|
|
12
17
|
refs: model.slots.map(({ role, min, max }) => ({ role, min, max })),
|
|
13
18
|
modes: model.modes,
|
|
14
19
|
options: model.options,
|
|
15
|
-
hidden: model.entry
|
|
20
|
+
hidden: modelIsHidden(model.entry),
|
|
21
|
+
available: Boolean(process.env[PROVIDER_ENV_KEYS[model.provider]]?.trim()),
|
|
22
|
+
frontier: modelFrontierRankings(model.id),
|
|
16
23
|
};
|
|
17
24
|
}
|
|
18
25
|
export function listModels(filter = {}) {
|
|
19
26
|
return MODEL_ENTRIES
|
|
20
|
-
.filter((entry) => filter.includeHidden || !entry
|
|
27
|
+
.filter((entry) => filter.includeHidden || !modelIsHidden(entry))
|
|
21
28
|
.filter((entry) => !filter.media || entry.media === filter.media)
|
|
22
29
|
.filter((entry) => !filter.operation || entry.operations.includes(filter.operation))
|
|
23
30
|
.map((entry) => resolveModel(entry.id));
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { MachGenGenerateRequestSchema } from "@apicity/machgen/zod";
|
|
3
|
+
const DEFAULT_DURATION_SECONDS = 5;
|
|
4
|
+
const SEEDANCE_ASPECT_RATIOS = ["adaptive", "21:9", "16:9", "9:16", "1:1", "4:3", "3:4"];
|
|
5
|
+
const ALL_REFERENCES = ["image", "video", "audio"];
|
|
6
|
+
export const MACHGEN_MODELS = {
|
|
7
|
+
"MiniMax-H3": {
|
|
8
|
+
taskTypes: ["T2V", "I2V", "R2V"],
|
|
9
|
+
resolutions: { "480p": 480, "768p": 768, "1440p": 1440 },
|
|
10
|
+
defaultResolution: "768p",
|
|
11
|
+
durations: { min: 1, max: 30, smart: false },
|
|
12
|
+
aspectRatios: ["16:9", "9:16", "1:1", "4:3", "3:4", "21:9"],
|
|
13
|
+
audioToggle: true,
|
|
14
|
+
seed: true,
|
|
15
|
+
lastFrame: "alone",
|
|
16
|
+
references: ALL_REFERENCES,
|
|
17
|
+
visualReference: false,
|
|
18
|
+
promptRequiredFor: ["T2V", "I2V", "R2V"],
|
|
19
|
+
maxPromptChars: 65535,
|
|
20
|
+
maxSecondsWithBothFrames: 15,
|
|
21
|
+
maxSecondsAt: { "1440p": 15 },
|
|
22
|
+
},
|
|
23
|
+
"Seedance-2.5": {
|
|
24
|
+
taskTypes: ["T2V", "I2V", "R2V"],
|
|
25
|
+
resolutions: { "480p": 480, "720p": 720, "1080p": 1080 },
|
|
26
|
+
defaultResolution: "720p",
|
|
27
|
+
durations: { min: 4, max: 30, smart: true },
|
|
28
|
+
aspectRatios: SEEDANCE_ASPECT_RATIOS,
|
|
29
|
+
audioToggle: true,
|
|
30
|
+
seed: false,
|
|
31
|
+
lastFrame: "with_first",
|
|
32
|
+
references: ALL_REFERENCES,
|
|
33
|
+
visualReference: false,
|
|
34
|
+
promptRequiredFor: ["T2V", "R2V"],
|
|
35
|
+
maxPromptChars: 25000,
|
|
36
|
+
smartEditNeedsAdaptive: true,
|
|
37
|
+
},
|
|
38
|
+
"Seedance-2.0": {
|
|
39
|
+
taskTypes: ["T2V", "I2V", "R2V"],
|
|
40
|
+
resolutions: { "480p": 480, "720p": 720, "1080p": 1080, "2160p": 2160 },
|
|
41
|
+
defaultResolution: "720p",
|
|
42
|
+
durations: { min: 4, max: 15, smart: true },
|
|
43
|
+
aspectRatios: SEEDANCE_ASPECT_RATIOS,
|
|
44
|
+
audioToggle: true,
|
|
45
|
+
seed: true,
|
|
46
|
+
lastFrame: "with_first",
|
|
47
|
+
references: ALL_REFERENCES,
|
|
48
|
+
visualReference: true,
|
|
49
|
+
promptRequiredFor: ["T2V"],
|
|
50
|
+
},
|
|
51
|
+
"Alibaba-Wan-3.0": {
|
|
52
|
+
taskTypes: ["T2V", "I2V", "R2V"],
|
|
53
|
+
resolutions: { "480p": 480, "720p": 720, "1080p": 1080 },
|
|
54
|
+
defaultResolution: "720p",
|
|
55
|
+
durations: { min: 2, max: 30, smart: true },
|
|
56
|
+
aspectRatios: [],
|
|
57
|
+
videoConfig: { aspect_ratio: "adaptive", fps: 30, audio: true },
|
|
58
|
+
audioToggle: false,
|
|
59
|
+
seed: true,
|
|
60
|
+
lastFrame: "with_first",
|
|
61
|
+
references: ALL_REFERENCES,
|
|
62
|
+
visualReference: false,
|
|
63
|
+
promptRequiredFor: ["T2V"],
|
|
64
|
+
},
|
|
65
|
+
"HappyHorse-1.1": {
|
|
66
|
+
taskTypes: ["T2V", "I2V", "R2V"],
|
|
67
|
+
resolutions: { "720p": 720, "1080p": 1080 },
|
|
68
|
+
defaultResolution: "720p",
|
|
69
|
+
durations: { min: 3, max: 15, smart: false },
|
|
70
|
+
aspectRatios: ["16:9", "9:16", "1:1", "4:3", "21:9"],
|
|
71
|
+
framesFollowImage: true,
|
|
72
|
+
audioToggle: false,
|
|
73
|
+
seed: true,
|
|
74
|
+
lastFrame: "none",
|
|
75
|
+
references: ["image"],
|
|
76
|
+
visualReference: true,
|
|
77
|
+
promptRequiredFor: ["T2V"],
|
|
78
|
+
},
|
|
79
|
+
"Grok-Imagine-Video-1.5": {
|
|
80
|
+
taskTypes: ["I2V"],
|
|
81
|
+
resolutions: { "480p": 480, "720p": 720, "1080p": 1080 },
|
|
82
|
+
defaultResolution: "720p",
|
|
83
|
+
durations: { min: 1, max: 15, smart: false },
|
|
84
|
+
aspectRatios: [],
|
|
85
|
+
audioToggle: false,
|
|
86
|
+
seed: false,
|
|
87
|
+
lastFrame: "none",
|
|
88
|
+
references: [],
|
|
89
|
+
visualReference: true,
|
|
90
|
+
promptRequiredFor: [],
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
const REFERENCE_FIELDS = {
|
|
94
|
+
image: "reference_image_urls",
|
|
95
|
+
video: "reference_video_urls",
|
|
96
|
+
audio: "reference_audio_urls",
|
|
97
|
+
};
|
|
98
|
+
const SOURCE_FIELDS = {
|
|
99
|
+
image: "src_image_urls",
|
|
100
|
+
video: "src_video_urls",
|
|
101
|
+
audio: "src_audio_urls",
|
|
102
|
+
};
|
|
103
|
+
function text(value) {
|
|
104
|
+
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
105
|
+
}
|
|
106
|
+
function list(value) {
|
|
107
|
+
return Array.isArray(value) ? value.filter((item) => text(item) !== null) : [];
|
|
108
|
+
}
|
|
109
|
+
function referencesOf(spec, value) {
|
|
110
|
+
const found = { image: [], video: [], audio: [] };
|
|
111
|
+
for (const kind of spec.references)
|
|
112
|
+
found[kind] = list(value[REFERENCE_FIELDS[kind]]);
|
|
113
|
+
return found;
|
|
114
|
+
}
|
|
115
|
+
function hasFrames(value) {
|
|
116
|
+
return text(value.first_frame_url) !== null || text(value.last_frame_url) !== null;
|
|
117
|
+
}
|
|
118
|
+
function taskTypeOf(spec, value) {
|
|
119
|
+
if (hasFrames(value))
|
|
120
|
+
return "I2V";
|
|
121
|
+
const references = referencesOf(spec, value);
|
|
122
|
+
return ALL_REFERENCES.some((kind) => references[kind].length > 0) ? "R2V" : "T2V";
|
|
123
|
+
}
|
|
124
|
+
function promptField(spec) {
|
|
125
|
+
const prompt = spec.maxPromptChars === undefined ? z.string() : z.string().max(spec.maxPromptChars);
|
|
126
|
+
return spec.taskTypes.every((task) => spec.promptRequiredFor.includes(task)) ? prompt.min(1) : prompt.optional();
|
|
127
|
+
}
|
|
128
|
+
function durationField(spec) {
|
|
129
|
+
const { min, max, smart } = spec.durations;
|
|
130
|
+
const seconds = z.number().int().min(min).max(max);
|
|
131
|
+
return (smart ? z.union([z.literal(-1), seconds]) : seconds)
|
|
132
|
+
.default(DEFAULT_DURATION_SECONDS)
|
|
133
|
+
.describe(smart ? `Seconds (${min}-${max}); -1 lets the model choose the length` : `Seconds (${min}-${max})`);
|
|
134
|
+
}
|
|
135
|
+
function frameFields(spec) {
|
|
136
|
+
if (!spec.taskTypes.includes("I2V"))
|
|
137
|
+
return {};
|
|
138
|
+
const first = z.string().min(1);
|
|
139
|
+
return {
|
|
140
|
+
first_frame_url: spec.taskTypes.length === 1 ? first : first.optional(),
|
|
141
|
+
...(spec.lastFrame === "none" ? {} : { last_frame_url: z.string().min(1).optional() }),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function referenceFields(spec) {
|
|
145
|
+
const source = MachGenGenerateRequestSchema.shape;
|
|
146
|
+
return Object.fromEntries(spec.references.map((kind) => [REFERENCE_FIELDS[kind], source[SOURCE_FIELDS[kind]]]));
|
|
147
|
+
}
|
|
148
|
+
function settingFields(spec) {
|
|
149
|
+
const [firstAspect, ...otherAspects] = spec.aspectRatios;
|
|
150
|
+
return {
|
|
151
|
+
duration: durationField(spec),
|
|
152
|
+
resolution: z.enum(Object.keys(spec.resolutions))
|
|
153
|
+
.default(spec.defaultResolution)
|
|
154
|
+
.describe("Output resolution"),
|
|
155
|
+
...(firstAspect === undefined ? {} : {
|
|
156
|
+
aspect_ratio: z.enum([firstAspect, ...otherAspects]).default(firstAspect).describe("Output aspect ratio"),
|
|
157
|
+
}),
|
|
158
|
+
...(spec.audioToggle ? { generate_audio: z.boolean().default(true).describe("Generate a soundtrack with the video") } : {}),
|
|
159
|
+
...(spec.seed ? { seed: z.number().int().min(0).max(2_147_483_647).optional().describe("Seed for reproducible output") } : {}),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function addIssue(ctx, path, message) {
|
|
163
|
+
ctx.addIssue({ code: "custom", message, path: path ? [path] : [] });
|
|
164
|
+
}
|
|
165
|
+
function checkFrames(spec, value, ctx) {
|
|
166
|
+
if (!hasFrames(value))
|
|
167
|
+
return;
|
|
168
|
+
const references = referencesOf(spec, value);
|
|
169
|
+
for (const kind of ALL_REFERENCES) {
|
|
170
|
+
if (references[kind].length > 0)
|
|
171
|
+
addIssue(ctx, REFERENCE_FIELDS[kind], "cannot be combined with first_frame_url or last_frame_url");
|
|
172
|
+
}
|
|
173
|
+
if (text(value.first_frame_url) === null && spec.lastFrame !== "alone")
|
|
174
|
+
addIssue(ctx, "last_frame_url", "last_frame_url requires first_frame_url");
|
|
175
|
+
const both = text(value.first_frame_url) !== null && text(value.last_frame_url) !== null;
|
|
176
|
+
const limit = spec.maxSecondsWithBothFrames;
|
|
177
|
+
if (both && limit !== undefined && typeof value.duration === "number" && value.duration > limit) {
|
|
178
|
+
addIssue(ctx, "duration", `a first and last frame allow at most ${limit} seconds`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function checkReferences(spec, value, task, ctx) {
|
|
182
|
+
if (task !== "R2V")
|
|
183
|
+
return;
|
|
184
|
+
const references = referencesOf(spec, value);
|
|
185
|
+
if (spec.visualReference && references.image.length === 0 && references.video.length === 0) {
|
|
186
|
+
addIssue(ctx, "", "reference-to-video needs at least one reference image or video");
|
|
187
|
+
}
|
|
188
|
+
if (spec.smartEditNeedsAdaptive && value.duration === -1 && references.video.length > 0 && value.aspect_ratio !== "adaptive") {
|
|
189
|
+
addIssue(ctx, "aspect_ratio", "-1 duration with a reference video edits that video and needs the adaptive aspect ratio");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function checkSettings(spec, value, task, ctx) {
|
|
193
|
+
if (!spec.taskTypes.includes(task))
|
|
194
|
+
addIssue(ctx, "", `${task} is not supported; use ${spec.taskTypes.join(", ")}`);
|
|
195
|
+
if (spec.promptRequiredFor.includes(task) && text(value.prompt) === null)
|
|
196
|
+
addIssue(ctx, "prompt", "Required");
|
|
197
|
+
const limit = typeof value.resolution === "string" ? spec.maxSecondsAt?.[value.resolution] : undefined;
|
|
198
|
+
if (limit !== undefined && typeof value.duration === "number" && value.duration > limit) {
|
|
199
|
+
addIssue(ctx, "duration", `${String(value.resolution)} allows at most ${limit} seconds`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function machgenInput(spec) {
|
|
203
|
+
return z.object({
|
|
204
|
+
prompt: promptField(spec),
|
|
205
|
+
...frameFields(spec),
|
|
206
|
+
...referenceFields(spec),
|
|
207
|
+
...settingFields(spec),
|
|
208
|
+
}).superRefine((value, ctx) => {
|
|
209
|
+
const record = value;
|
|
210
|
+
const task = taskTypeOf(spec, record);
|
|
211
|
+
checkSettings(spec, record, task, ctx);
|
|
212
|
+
checkFrames(spec, record, ctx);
|
|
213
|
+
checkReferences(spec, record, task, ctx);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function frameSources(value) {
|
|
217
|
+
const first = text(value.first_frame_url);
|
|
218
|
+
const last = text(value.last_frame_url);
|
|
219
|
+
if (first === null && last !== null)
|
|
220
|
+
return { src_image_urls: [last], keyframe_indices: [-1] };
|
|
221
|
+
return { src_image_urls: [first, last].filter((url) => url !== null) };
|
|
222
|
+
}
|
|
223
|
+
function referenceSources(spec, value) {
|
|
224
|
+
const references = referencesOf(spec, value);
|
|
225
|
+
return Object.fromEntries(ALL_REFERENCES
|
|
226
|
+
.filter((kind) => references[kind].length > 0)
|
|
227
|
+
.map((kind) => [SOURCE_FIELDS[kind], references[kind]]));
|
|
228
|
+
}
|
|
229
|
+
function videoConfig(spec, value, task) {
|
|
230
|
+
const aspect = text(value.aspect_ratio);
|
|
231
|
+
return {
|
|
232
|
+
height: spec.resolutions[String(value.resolution)],
|
|
233
|
+
duration_secs: value.duration,
|
|
234
|
+
...(aspect !== null && !(task === "I2V" && spec.framesFollowImage) ? { aspect_ratio: aspect } : {}),
|
|
235
|
+
...(typeof value.generate_audio === "boolean" ? { audio: value.generate_audio } : {}),
|
|
236
|
+
...spec.videoConfig,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function machgenRequest(apiId, spec, value) {
|
|
240
|
+
const task = taskTypeOf(spec, value);
|
|
241
|
+
return {
|
|
242
|
+
model: apiId,
|
|
243
|
+
task_type: task,
|
|
244
|
+
prompt: typeof value.prompt === "string" ? value.prompt : "",
|
|
245
|
+
video_config: videoConfig(spec, value, task),
|
|
246
|
+
...(typeof value.seed === "number" ? { seed: value.seed } : {}),
|
|
247
|
+
...(task === "I2V" ? frameSources(value) : {}),
|
|
248
|
+
...(task === "R2V" ? referenceSources(spec, value) : {}),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
export function machgenWire(apiId) {
|
|
252
|
+
const spec = MACHGEN_MODELS[apiId];
|
|
253
|
+
if (!spec)
|
|
254
|
+
return null;
|
|
255
|
+
return {
|
|
256
|
+
input: machgenInput(spec),
|
|
257
|
+
fixed: {},
|
|
258
|
+
wrap: (value) => machgenRequest(apiId, spec, value),
|
|
259
|
+
request: MachGenGenerateRequestSchema,
|
|
260
|
+
};
|
|
261
|
+
}
|
package/dist/media/model-id.js
CHANGED
package/dist/media/preview.js
CHANGED
|
@@ -27,6 +27,8 @@ function providerPath(model) {
|
|
|
27
27
|
return "alibaba.post.api.v1.services.aigc.multimodalGeneration.generation(request)";
|
|
28
28
|
case "elevenlabs":
|
|
29
29
|
return ELEVENLABS_PATHS[model.apiId] ?? "elevenlabs";
|
|
30
|
+
case "machgen":
|
|
31
|
+
return "machgen.api.v0.generate(request)";
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
function placeholderRefs(refs) {
|
|
@@ -43,6 +43,8 @@ function rewrite(dialect, prompt, refs) {
|
|
|
43
43
|
return natural(prompt, refs).replace(/@image([1-9]\d*)\b/giu, "reference image $1");
|
|
44
44
|
case "natural_kinds":
|
|
45
45
|
return natural(prompt, refs).replace(/@(image|audio)([1-9]\d*)(?![\w-])/giu, (_match, kind, index) => `reference ${kind.toLowerCase()} ${index}`);
|
|
46
|
+
case "natural_strip_frames":
|
|
47
|
+
return stripRefMentions(natural(prompt, refs), frames(refs));
|
|
46
48
|
case "natural_source_video":
|
|
47
49
|
return replaceNaturalAssetMentions(prompt, {
|
|
48
50
|
imageRefs: refsFor(refs, "image"),
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const SEEDANCE_DRAFT_COMPLETE_MODEL = "kie:bytedance/seedance-2-5-draft";
|
|
2
|
+
const SEEDANCE_DRAFT_SOURCE_MODEL = "bytedance/seedance-2-5";
|
|
3
|
+
function record(value) {
|
|
4
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
5
|
+
}
|
|
6
|
+
function seconds(value) {
|
|
7
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
8
|
+
}
|
|
9
|
+
async function readRecord(engine, artifactId, key) {
|
|
10
|
+
const result = await engine.metadata.artifacts.read(artifactId, key);
|
|
11
|
+
return result.ok ? record(result.value) : {};
|
|
12
|
+
}
|
|
13
|
+
async function draftArtifact(engine, taskId) {
|
|
14
|
+
for (const artifact of engine.artifacts.list()) {
|
|
15
|
+
if (artifact.kind !== "video")
|
|
16
|
+
continue;
|
|
17
|
+
const stored = await readRecord(engine, artifact.artifactId, "kie-task-id");
|
|
18
|
+
if (stored.taskId === taskId)
|
|
19
|
+
return artifact.artifactId;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function inputSeconds(input, hints) {
|
|
24
|
+
const stored = hints.inputDurationSeconds;
|
|
25
|
+
if (typeof stored === "number" && Number.isFinite(stored) && stored >= 0)
|
|
26
|
+
return stored;
|
|
27
|
+
const videos = input.reference_video_urls;
|
|
28
|
+
return Array.isArray(videos) && videos.length > 0 ? undefined : 0;
|
|
29
|
+
}
|
|
30
|
+
export async function seedanceDraftCostHints(engine, model, options) {
|
|
31
|
+
if (model !== SEEDANCE_DRAFT_COMPLETE_MODEL)
|
|
32
|
+
return {};
|
|
33
|
+
const taskId = typeof options.draft_task_id === "string" ? options.draft_task_id.trim() : "";
|
|
34
|
+
const artifactId = taskId ? await draftArtifact(engine, taskId) : null;
|
|
35
|
+
if (!artifactId)
|
|
36
|
+
return {};
|
|
37
|
+
const payload = await readRecord(engine, artifactId, "generation-payload");
|
|
38
|
+
const input = record(payload.input);
|
|
39
|
+
if (payload.model !== SEEDANCE_DRAFT_SOURCE_MODEL || input.draft !== true)
|
|
40
|
+
return {};
|
|
41
|
+
const generated = seconds((await readRecord(engine, artifactId, "original")).duration);
|
|
42
|
+
const durationSeconds = seconds(input.duration) ?? (generated === undefined ? undefined : Math.round(generated));
|
|
43
|
+
const inputDurationSeconds = inputSeconds(input, await readRecord(engine, artifactId, "generation-cost-hints"));
|
|
44
|
+
return {
|
|
45
|
+
...(durationSeconds !== undefined ? { durationSeconds } : {}),
|
|
46
|
+
...(inputDurationSeconds !== undefined ? { inputDurationSeconds } : {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
package/dist/media/wire.js
CHANGED
|
@@ -4,6 +4,7 @@ import { FAL_ENDPOINT_REQUEST_SCHEMAS } from "@apicity/fal/zod";
|
|
|
4
4
|
import { XaiVideoGenerateRequestSchema } from "@apicity/xai/zod";
|
|
5
5
|
import { AlibabaMultimodalGenerationParametersSchema, AlibabaQwenImageEditStableModelSchema, AlibabaQwenImageGenerationStableModelSchema, } from "@apicity/alibaba/zod";
|
|
6
6
|
import { ElevenLabsComposeMusicRequestSchema, ElevenLabsSoundGenerationRequestSchema, ElevenLabsTextToSpeechRequestSchema, ElevenLabsVideoToMusicRequestSchema, } from "@apicity/elevenlabs/zod";
|
|
7
|
+
import { machgenWire } from "./machgen.js";
|
|
7
8
|
const KIE_VEO_MODELS = new Set(["veo3", "veo3_fast", "veo3_lite"]);
|
|
8
9
|
const SUNO_CALLBACK_URL = "https://localhost/videobook/suno-callback";
|
|
9
10
|
function objectShape(schema) {
|
|
@@ -157,5 +158,7 @@ export function wireSpec(parsed) {
|
|
|
157
158
|
return alibabaWire(parsed.apiId);
|
|
158
159
|
case "elevenlabs":
|
|
159
160
|
return elevenLabsWire(parsed.apiId);
|
|
161
|
+
case "machgen":
|
|
162
|
+
return machgenWire(parsed.apiId);
|
|
160
163
|
}
|
|
161
164
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { LIGHTNING_SCENE_MODEL, LIGHTNING_VIDEO_MODEL } from "./lightning-defaults.js";
|
|
2
2
|
import { notebookCellTableRows } from "videobook-engine";
|
|
3
3
|
import { notebookGridTag } from "videobook-engine/notebook-grid";
|
|
4
|
+
import { modelFrontierRankings } from "./media/frontier.js";
|
|
5
|
+
import { PROVIDER_ENV_KEYS } from "./media/adapters/index.js";
|
|
4
6
|
import { listModels } from "./media/listing.js";
|
|
5
7
|
import { notebookGenerationInputContract } from "./notebook-generation-contract.js";
|
|
6
8
|
import { controlsForModel } from "./notebook-generation-controls.js";
|
|
@@ -265,6 +267,7 @@ const providerLabels = {
|
|
|
265
267
|
xai: "xAI",
|
|
266
268
|
alibaba: "Alibaba",
|
|
267
269
|
elevenlabs: "ElevenLabs",
|
|
270
|
+
machgen: "MachGen",
|
|
268
271
|
};
|
|
269
272
|
function hasDualFrame(model) {
|
|
270
273
|
const roles = new Set(model.slots.map((slot) => slot.role));
|
|
@@ -297,6 +300,8 @@ function generationAction(model) {
|
|
|
297
300
|
...(DEFAULT_MODELS.has(model.id) ? { defaultPriority: 0, isDefault: true } : {}),
|
|
298
301
|
inputContract: notebookGenerationInputContract(model),
|
|
299
302
|
controls: controlsForModel(model),
|
|
303
|
+
available: Boolean(process.env[PROVIDER_ENV_KEYS[model.provider]]?.trim()),
|
|
304
|
+
frontier: modelFrontierRankings(model.id),
|
|
300
305
|
};
|
|
301
306
|
}
|
|
302
307
|
export function listNotebookActions(options = {}) {
|
package/dist/one-password.js
CHANGED
|
@@ -35,14 +35,19 @@ export function readOnePasswordSettings(env) {
|
|
|
35
35
|
export function redactToken(text, token) {
|
|
36
36
|
return token ? text.split(token).join("***") : text;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
/** Every vault item titled like an environment variable fills that variable, except the vault settings themselves. */
|
|
39
|
+
function isVaultEnvTitle(title) {
|
|
40
|
+
return ENV_NAME.test(title) && title !== OP_VAULT_SETTING && title !== OP_TOKEN_SETTING;
|
|
41
|
+
}
|
|
42
|
+
export function planReferences(env, vault, vaultTitles) {
|
|
39
43
|
const explicit = Object.entries(env)
|
|
40
44
|
.filter((entry) => isOpReference(entry[1]))
|
|
41
45
|
.map(([key, reference]) => [key, reference.trim()]);
|
|
42
46
|
const covered = new Set(explicit.map(([key]) => key));
|
|
43
47
|
const convention = vault
|
|
44
|
-
?
|
|
45
|
-
.filter((key) => !covered.has(key) && !env[key]?.trim()
|
|
48
|
+
? [...vaultTitles]
|
|
49
|
+
.filter((key) => isVaultEnvTitle(key) && !covered.has(key) && !env[key]?.trim())
|
|
50
|
+
.sort()
|
|
46
51
|
.map((key) => [key, vaultReference(vault, key)])
|
|
47
52
|
: [];
|
|
48
53
|
return { explicit, convention };
|
|
@@ -98,8 +103,9 @@ export function listVaultItemTitles(vault, env, token) {
|
|
|
98
103
|
if (!Array.isArray(parsed))
|
|
99
104
|
throw new Error("op item list did not return a list");
|
|
100
105
|
return new Set(parsed.flatMap((item) => {
|
|
101
|
-
const title = item && typeof item === "object" ? item
|
|
102
|
-
|
|
106
|
+
const { title, category } = item && typeof item === "object" ? item : {};
|
|
107
|
+
// SSH keys carry no password field, so op://<vault>/<title>/password cannot resolve them.
|
|
108
|
+
return typeof title === "string" && category !== "SSH_KEY" ? [title] : [];
|
|
103
109
|
}));
|
|
104
110
|
}
|
|
105
111
|
function injectBatch(entries, env, token) {
|
|
@@ -169,13 +175,13 @@ function emptyResolution(settings) {
|
|
|
169
175
|
error: null,
|
|
170
176
|
};
|
|
171
177
|
}
|
|
172
|
-
export function resolveOnePasswordEnv(env
|
|
178
|
+
export function resolveOnePasswordEnv(env) {
|
|
173
179
|
const settings = readOnePasswordSettings(env);
|
|
174
180
|
const hasReferences = Object.values(env).some((value) => isOpReference(value));
|
|
175
181
|
if (!hasReferences && !settings.vault)
|
|
176
182
|
return emptyResolution(settings);
|
|
177
183
|
const listing = vaultTitles(settings, env);
|
|
178
|
-
const plan = planReferences(env,
|
|
184
|
+
const plan = planReferences(env, settings.vault, listing.titles);
|
|
179
185
|
const result = resolveEntries([...plan.explicit, ...plan.convention], env, settings.token.value);
|
|
180
186
|
for (const [key] of plan.explicit) {
|
|
181
187
|
const value = result.values.get(key);
|
|
@@ -11,7 +11,7 @@ export function parseProviderTaskStatus(value) {
|
|
|
11
11
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
12
12
|
return null;
|
|
13
13
|
const record = value;
|
|
14
|
-
if (record.provider !== "kie" && record.provider !== "fal")
|
|
14
|
+
if (record.provider !== "kie" && record.provider !== "fal" && record.provider !== "machgen")
|
|
15
15
|
return null;
|
|
16
16
|
return {
|
|
17
17
|
provider: record.provider,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MEDIA_TASK_TYPES } from "./media/adapters/types.js";
|
|
1
2
|
import { parseModelId } from "./media/model-id.js";
|
|
2
3
|
import { legacyMediaForTaskType } from "./media/legacy-task-types.js";
|
|
3
4
|
import { readProviderTaskRecord } from "./provider-task-record.js";
|
|
@@ -22,14 +23,16 @@ export async function readProviderTaskStatus(engine, artifactId, hints = {}) {
|
|
|
22
23
|
return null;
|
|
23
24
|
const model = firstString(endpoint, payload.model, catalogModel?.apiId, saved?.meta.apiModel, saved?.meta.model, hints.model);
|
|
24
25
|
const taskId = firstString(saved?.taskId, storedId.taskId, hints.taskId);
|
|
26
|
+
const machgenTask = saved?.taskType === MEDIA_TASK_TYPES.machgen;
|
|
25
27
|
if (saved && /^(?:xai|alibaba)_/u.test(saved.taskType))
|
|
26
28
|
return null;
|
|
27
|
-
if (saved && saved.taskType !== "media_kie" && saved.taskType !== "isolate_vocals"
|
|
29
|
+
if (saved && !machgenTask && saved.taskType !== "media_kie" && saved.taskType !== "isolate_vocals"
|
|
28
30
|
&& !saved.taskType.startsWith("fal_") && legacyMediaForTaskType(saved.taskType) === null)
|
|
29
31
|
return null;
|
|
30
32
|
const provider = endpoint || saved?.taskType.startsWith("fal_") ? "fal"
|
|
31
|
-
:
|
|
32
|
-
|
|
33
|
+
: machgenTask ? "machgen"
|
|
34
|
+
: catalogModel?.provider ?? hints.provider ?? (storedId.taskId || saved ? "kie" : null);
|
|
35
|
+
if (provider !== "kie" && provider !== "fal" && provider !== "machgen")
|
|
33
36
|
return null;
|
|
34
37
|
return { provider, model, taskId: taskId === "queued" ? null : taskId };
|
|
35
38
|
}
|
package/dist/provider-usage.js
CHANGED
|
@@ -146,6 +146,8 @@ function isBillable(provider, path, method) {
|
|
|
146
146
|
return path.includes("/services/aigc/");
|
|
147
147
|
if (provider === "elevenlabs")
|
|
148
148
|
return /\/(text-to-speech|speech-to-text|speech-to-speech|sound-generation|audio-isolation|music)(\/|$)/u.test(path);
|
|
149
|
+
if (provider === "machgen")
|
|
150
|
+
return path === "/api/v0/generate";
|
|
149
151
|
return /\/(videos|images)(\/|$)/u.test(path);
|
|
150
152
|
}
|
|
151
153
|
async function payloadOf(input, init) {
|
|
@@ -177,7 +179,7 @@ function remoteId(body, url, method) {
|
|
|
177
179
|
?? (/\/(requests|tasks|jobs|videos)\/([^/]+)(?:\/status)?$/u.exec(url.pathname)?.[2] ?? null);
|
|
178
180
|
if (method === "GET" && pathId)
|
|
179
181
|
return decodeURIComponent(pathId);
|
|
180
|
-
return text(data.taskId) ?? text(data.task_id) ?? text(body.request_id) ?? text(body.job_id)
|
|
182
|
+
return text(data.taskId) ?? text(data.task_id) ?? text(body.task_id) ?? text(body.request_id) ?? text(body.job_id)
|
|
181
183
|
?? text(output.task_id) ?? text(body.id);
|
|
182
184
|
}
|
|
183
185
|
function responseStatus(body, accepted) {
|
|
@@ -247,7 +249,7 @@ export function providerUsageFetch(provider, transport = fetch) {
|
|
|
247
249
|
catch {
|
|
248
250
|
void 0;
|
|
249
251
|
}
|
|
250
|
-
const activity = activityFor(model, url.pathname, context.operation);
|
|
252
|
+
const activity = provider === "machgen" ? "video" : activityFor(model, url.pathname, context.operation);
|
|
251
253
|
entry = {
|
|
252
254
|
id: randomUUID(), provider, model, activity, status: "pending",
|
|
253
255
|
tokenBased: /\/(responses|completions|embeddings)$/u.test(url.pathname) || rateFor(provider, model)?.kind === "tokens",
|
|
@@ -270,7 +272,7 @@ export function providerUsageFetch(provider, transport = fetch) {
|
|
|
270
272
|
const body = json ? object(await response.clone().json()) : {};
|
|
271
273
|
const externalId = remoteId(body, url, method);
|
|
272
274
|
if (entry) {
|
|
273
|
-
const accepted = !!externalId && (provider === "kie" || provider === "fal" || /videos|tasks/u.test(url.pathname) || !!object(body.output).task_id);
|
|
275
|
+
const accepted = !!externalId && (provider === "kie" || provider === "fal" || provider === "machgen" || /videos|tasks/u.test(url.pathname) || !!object(body.output).task_id);
|
|
274
276
|
const status = response.ok ? responseStatus(body, accepted) : "failed";
|
|
275
277
|
safeRecord(engine, { ...entry, ...(externalId ? { externalId } : {}), ...observedPatch(provider, entry, body, status) });
|
|
276
278
|
}
|
package/dist/queue.js
CHANGED
|
@@ -443,6 +443,9 @@ async function createBookRunner(bookId) {
|
|
|
443
443
|
const runner = engine.jobs.queue.createRunner({
|
|
444
444
|
concurrency: 5,
|
|
445
445
|
pollIntervalMs: 250,
|
|
446
|
+
// The engine default lease is 30s. Remotion can stall the event loop
|
|
447
|
+
// longer than that, and the reaper then fails the subtitle render.
|
|
448
|
+
leaseMs: 10 * 60_000,
|
|
446
449
|
preferredJobTypes: SEARCH_REFERENCE_JOB_TYPES,
|
|
447
450
|
resolveHandler: makeWrappedResolver(bookId, engine),
|
|
448
451
|
});
|
package/dist/tools/commands.js
CHANGED
|
@@ -1088,7 +1088,7 @@ const CANONICAL_COMMANDS = [
|
|
|
1088
1088
|
},
|
|
1089
1089
|
{
|
|
1090
1090
|
command: "/transcribe",
|
|
1091
|
-
description: "
|
|
1091
|
+
description: "Open a chat review to transcribe a video or audio clip",
|
|
1092
1092
|
example: "/transcribe @a1",
|
|
1093
1093
|
category: "action",
|
|
1094
1094
|
icon: "Mic",
|
|
@@ -6,6 +6,7 @@ import { DEFAULT_IMAGE_EDIT_MODEL_ID, findImageEditModel } from "../image-edit-m
|
|
|
6
6
|
import { tryResolveModel } from "../media/descriptor.js";
|
|
7
7
|
import { estimateGeneration } from "../media/cost.js";
|
|
8
8
|
import { checkSeedanceEditSource, normalizeRefs } from "../media/refs.js";
|
|
9
|
+
import { seedanceDraftCostHints } from "../media/seedance-draft.js";
|
|
9
10
|
import { REF_ROLES } from "../media/types.js";
|
|
10
11
|
const GENERATION_TOOLS = new Set(["generate_image", "generate_video", "generate_audio"]);
|
|
11
12
|
const REWRITE_MODEL = "gpt-5.6-sol";
|
|
@@ -70,12 +71,13 @@ async function estimateMedia(engine, input) {
|
|
|
70
71
|
const sourceError = await checkSeedanceEditSource(engine, model, refs, input.mode);
|
|
71
72
|
if (sourceError)
|
|
72
73
|
return unavailable(sourceError);
|
|
74
|
+
const hints = { ...await mediaHints(engine, refs), ...await seedanceDraftCostHints(engine, model.id, options) };
|
|
73
75
|
const result = await estimateGeneration(model, {
|
|
74
76
|
prompt: text(input.prompt),
|
|
75
77
|
refUrls: placeholderRefs(refs),
|
|
76
78
|
options,
|
|
77
79
|
mode: text(input.mode),
|
|
78
|
-
},
|
|
80
|
+
}, hints);
|
|
79
81
|
return result.ok ? result.estimate : unavailable(result.error);
|
|
80
82
|
}
|
|
81
83
|
async function estimateImageEdit(input) {
|
|
@@ -15,6 +15,7 @@ const KEY_REGISTRY = {
|
|
|
15
15
|
FAL_API_KEY: { provider: "fal", label: "FAL" },
|
|
16
16
|
FAL_ADMIN_API_KEY: { provider: "fal", label: "FAL admin" },
|
|
17
17
|
DASHSCOPE_API_KEY: { provider: "dashscope", label: "DashScope" },
|
|
18
|
+
MACHGEN_API_KEY: { provider: "machgen", label: "MachGen" },
|
|
18
19
|
B2_ACCESS_KEY_ID: { provider: "b2", label: "Backblaze B2" },
|
|
19
20
|
B2_SECRET_ACCESS_KEY: { provider: "b2", label: "Backblaze B2" },
|
|
20
21
|
B2_REGION: { provider: "b2", label: "Backblaze B2" },
|
|
@@ -35,6 +36,7 @@ const ROW_ORDER = [
|
|
|
35
36
|
"FAL_API_KEY",
|
|
36
37
|
"FAL_ADMIN_API_KEY",
|
|
37
38
|
"DASHSCOPE_API_KEY",
|
|
39
|
+
"MACHGEN_API_KEY",
|
|
38
40
|
"B2_ACCESS_KEY_ID",
|
|
39
41
|
"B2_SECRET_ACCESS_KEY",
|
|
40
42
|
"B2_REGION",
|