gandora: hedge a stalled render after 60 s with a second queue, take whichever lands first
Three timed renders through the worker took 8-24 s; the one that sat for 2+ min was Venice's queue stalling that job. A second copy after 60 s bounds the wait at about normal + 60 s for an extra 3 cents on a bad day. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6897PWcUEwHiqZMcVnNF6
This commit is contained in:
1 parent
52edca2774
commit
050aa296a5
1 file changed
+24
-14
+24
-14
@@ -304,23 +304,33 @@ async function askDJ() {
|
||||
return spec;
|
||||
}
|
||||
|
||||
/* ACE-Step on Venice: queue, poll, download. 60 s costs about $0.03. */
|
||||
/* ACE-Step on Venice: queue, poll, download. 60 s costs about $0.03. Venice usually answers in 10-30 s but its queue
|
||||
sometimes stalls a job for minutes, so after HEDGE_AT a second copy is queued and whichever lands first is taken. */
|
||||
const HEDGE_AT = 60000;
|
||||
async function renderTrack(spec, onStatus) {
|
||||
const q = await venice("/audio/queue", { model: "ace-step-15", prompt: spec.tags, duration_seconds: 60, ...(spec.lyrics ? { lyrics_prompt: spec.lyrics } : {}) });
|
||||
const body = { model: "ace-step-15", prompt: spec.tags, duration_seconds: 60, ...(spec.lyrics ? { lyrics_prompt: spec.lyrics } : {}) };
|
||||
const jobs = [await venice("/audio/queue", body)];
|
||||
const started = Date.now();
|
||||
let hedged = false;
|
||||
for (let i = 0; i < 90; i++) {
|
||||
await sleep(i < 4 ? 2500 : 4000);
|
||||
const res = await venice("/audio/retrieve", { model: q.model || "ace-step-15", queue_id: q.queue_id, delete_media_on_completion: true }, { raw: true });
|
||||
const type = res.headers.get("content-type") || "";
|
||||
if (/^audio\//i.test(type) || /octet-stream/i.test(type)) {
|
||||
const blob = await res.blob();
|
||||
state.cost += 0.03; localStorage.setItem(LS.cost, String(state.cost));
|
||||
return blob;
|
||||
const elapsed = Date.now() - started;
|
||||
if (!hedged && elapsed > HEDGE_AT) { hedged = true; try { jobs.push(await venice("/audio/queue", body)); } catch {} }
|
||||
let expected = 0;
|
||||
for (const q of jobs) {
|
||||
const res = await venice("/audio/retrieve", { model: q.model || "ace-step-15", queue_id: q.queue_id, delete_media_on_completion: true }, { raw: true });
|
||||
const type = res.headers.get("content-type") || "";
|
||||
if (/^audio\//i.test(type) || /octet-stream/i.test(type)) {
|
||||
const blob = await res.blob();
|
||||
state.cost += 0.03 * jobs.length; localStorage.setItem(LS.cost, String(state.cost));
|
||||
return blob;
|
||||
}
|
||||
const d = await res.json();
|
||||
const st = String(d.status || "").toUpperCase();
|
||||
if (/FAIL|ERROR|CANCEL|REJECT/.test(st)) { if (jobs.length === 1) throw new Error("Venice couldn't make the track (" + st.toLowerCase() + ")."); jobs.splice(jobs.indexOf(q), 1); break; }
|
||||
expected = Math.max(expected, d.average_execution_time || 0);
|
||||
}
|
||||
const d = await res.json();
|
||||
const st = String(d.status || "").toUpperCase();
|
||||
if (/FAIL|ERROR|CANCEL|REJECT/.test(st)) throw new Error("Venice couldn't make the track (" + st.toLowerCase() + ").");
|
||||
onStatus && onStatus(Math.round((Date.now() - started) / 1000), Math.round((d.average_execution_time || 0) / 1000));
|
||||
onStatus && onStatus(Math.round(elapsed / 1000), Math.round(expected / 1000), hedged);
|
||||
}
|
||||
throw new Error("That render took too long.");
|
||||
}
|
||||
@@ -337,8 +347,8 @@ let seq = 0;
|
||||
function makeJob(spec) {
|
||||
const job = { id: ++seq, spec, blob: null, err: null, done: null };
|
||||
job.done = (async () => {
|
||||
try { job.blob = await renderTrack(spec, (el, exp) => {
|
||||
const t = `COOKING ${el}s${exp ? " / ~" + exp + "s" : ""}`;
|
||||
try { job.blob = await renderTrack(spec, (el, exp, hedged) => {
|
||||
const t = `COOKING ${el}s${exp ? " / ~" + exp + "s" : ""}${hedged ? " · SECOND OVEN LIT" : ""}`;
|
||||
if (state.current === job) $("#cover .veil").textContent = t;
|
||||
else if (state.nextJob === job && state.current) $("#next").textContent = "NEXT · " + spec.title.toUpperCase() + " · " + t;
|
||||
}); }
|
||||
|
||||
Reference in new issue
Block a user