* docs: prepare Vendoo 1.41.2 git-ignore boundary hotfix * fix: track native source modules with root-anchored runtime ignores
138 lines
5.7 KiB
JavaScript
138 lines
5.7 KiB
JavaScript
import http from 'http';
|
|
import { once } from 'events';
|
|
import { readFileSync } from 'fs';
|
|
import { resolve } from 'path';
|
|
import {
|
|
generateWithLocalFlux,
|
|
getLocalFluxRuntimeInfo,
|
|
interruptLocalFlux,
|
|
cancelLocalFluxPrompt,
|
|
} from '../lib/ai-local-images.mjs';
|
|
|
|
let submittedWorkflow = null;
|
|
let promptSubmissions = 0;
|
|
let interrupted = false;
|
|
let deletedQueueIds = [];
|
|
const imageBytes = Buffer.from('vendoo-slice-30-mock-image');
|
|
|
|
const server = http.createServer(async (req, res) => {
|
|
const sendJson = (status, payload) => {
|
|
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
res.end(JSON.stringify(payload));
|
|
};
|
|
if (req.method === 'GET' && req.url === '/system_stats') {
|
|
return sendJson(200, { system: { os: 'mock-os', comfyui_version: 'mock-1' }, devices: [{ name: 'Mock GPU', vram_total: 16 * 1024 ** 3, vram_free: 12 * 1024 ** 3 }] });
|
|
}
|
|
if (req.method === 'GET' && req.url === '/queue') {
|
|
return sendJson(200, {
|
|
queue_running: [[1, 'mock-running', {}, {}, []]],
|
|
queue_pending: [[2, 'mock-pending', {}, {}, []]],
|
|
});
|
|
}
|
|
if (req.method === 'POST' && req.url === '/queue') {
|
|
let body = '';
|
|
for await (const chunk of req) body += chunk;
|
|
deletedQueueIds = JSON.parse(body || '{}').delete || [];
|
|
return sendJson(200, { ok: true });
|
|
}
|
|
if (req.method === 'GET' && req.url.startsWith('/history?')) {
|
|
return sendJson(200, { 'mock-prompt': { outputs: {} } });
|
|
}
|
|
if (req.method === 'POST' && req.url === '/interrupt') {
|
|
interrupted = true;
|
|
return sendJson(200, { ok: true });
|
|
}
|
|
if (req.method === 'POST' && req.url === '/prompt') {
|
|
promptSubmissions += 1;
|
|
let body = '';
|
|
for await (const chunk of req) body += chunk;
|
|
submittedWorkflow = JSON.parse(body).prompt;
|
|
return sendJson(200, { prompt_id: 'slice30-prompt-id' });
|
|
}
|
|
if (req.method === 'GET' && req.url === '/history/slice30-prompt-id') {
|
|
return sendJson(200, {
|
|
'slice30-prompt-id': {
|
|
outputs: {
|
|
9: { images: [{ filename: 'slice30.png', subfolder: '', type: 'output' }] },
|
|
},
|
|
},
|
|
});
|
|
}
|
|
if (req.method === 'GET' && req.url.startsWith('/view?')) {
|
|
res.writeHead(200, { 'Content-Type': 'image/png' });
|
|
return res.end(imageBytes);
|
|
}
|
|
return sendJson(404, { error: `Unhandled ${req.method} ${req.url}` });
|
|
});
|
|
|
|
server.listen(0, '127.0.0.1');
|
|
await once(server, 'listening');
|
|
const address = server.address();
|
|
const settings = {
|
|
ai_model_photo_local_enabled: '1',
|
|
ai_model_photo_local_url: `http://127.0.0.1:${address.port}`,
|
|
ai_model_photo_local_workflow_path: resolve('local-ai/workflows/vendoo_flux_schnell_api.json'),
|
|
ai_model_photo_use_source_reference: '0',
|
|
ai_model_photo_local_resolution: '768',
|
|
};
|
|
|
|
try {
|
|
const phases = [];
|
|
const result = await generateWithLocalFlux({
|
|
prompt: 'Studio product photo with soft light',
|
|
settings,
|
|
mode: 'prompt',
|
|
sourceDataUrl: '',
|
|
width: 768,
|
|
height: 768,
|
|
seed: 1234,
|
|
steps: 4,
|
|
onPhase: (phase) => phases.push(phase),
|
|
});
|
|
if (result.prompt_id !== 'slice30-prompt-id') throw new Error('Prompt-ID wurde nicht zurückgegeben.');
|
|
if (Buffer.from(result.base64, 'base64').compare(imageBytes) !== 0) throw new Error('Mock-Bild stimmt nicht.');
|
|
const classTypes = Object.values(submittedWorkflow || {}).map(node => node?.class_type).filter(Boolean);
|
|
if (classTypes.includes('LoadImage')) throw new Error('Promptmodus enthält unerlaubten LoadImage-Knoten.');
|
|
if (!phases.includes('submitted') || !phases.includes('rendering') || !phases.includes('completed')) throw new Error(`Phasen unvollständig: ${phases.join(', ')}`);
|
|
|
|
let cancelledBeforeSubmit = false;
|
|
try {
|
|
await generateWithLocalFlux({
|
|
prompt: 'This job must not be submitted', settings, mode: 'prompt', isCancelled: () => true,
|
|
});
|
|
} catch (error) {
|
|
cancelledBeforeSubmit = error?.code === 'FLUX_CANCELLED';
|
|
}
|
|
if (!cancelledBeforeSubmit || promptSubmissions !== 1) throw new Error('Abbruch vor der ComfyUI-Übergabe ist nicht sicher.');
|
|
|
|
const runtime = await getLocalFluxRuntimeInfo(settings, { historyLimit: 20 });
|
|
if (!runtime.reachable || runtime.queue_summary.running !== 1 || runtime.queue_summary.pending !== 1) throw new Error('Queue/System-Diagnose ist falsch.');
|
|
if (runtime.system_stats?.devices?.[0]?.name !== 'Mock GPU') throw new Error('GPU-Systemdaten fehlen.');
|
|
|
|
const pendingCancel = await cancelLocalFluxPrompt('mock-pending', settings);
|
|
if (pendingCancel.mode !== 'dequeued' || !deletedQueueIds.includes('mock-pending')) throw new Error('Gezieltes Entfernen eines wartenden Auftrags ist fehlgeschlagen.');
|
|
const runningCancel = await cancelLocalFluxPrompt('mock-running', settings);
|
|
if (runningCancel.mode !== 'interrupted' || !interrupted) throw new Error('Laufender Auftrag wurde nicht unterbrochen.');
|
|
await interruptLocalFlux(settings);
|
|
if (!interrupted) throw new Error('Interrupt-Endpunkt wurde nicht aufgerufen.');
|
|
|
|
const rawWorkflow = JSON.parse(readFileSync(settings.ai_model_photo_local_workflow_path, 'utf8').replace(/^\uFEFF/, ''));
|
|
if (Object.values(rawWorkflow).some(node => node?.class_type === 'LoadImage')) throw new Error('Ausgelieferter freier Promptworkflow enthält LoadImage.');
|
|
|
|
console.log(JSON.stringify({
|
|
ok: true,
|
|
prompt_id: result.prompt_id,
|
|
phases,
|
|
queue_summary: runtime.queue_summary,
|
|
gpu: runtime.system_stats.devices[0].name,
|
|
interrupt: interrupted,
|
|
targeted_pending_cancel: deletedQueueIds.includes('mock-pending'),
|
|
targeted_running_cancel: runningCancel.mode === 'interrupted',
|
|
cancelled_before_submit: cancelledBeforeSubmit,
|
|
prompt_submissions: promptSubmissions,
|
|
load_image_in_prompt_workflow: false,
|
|
}, null, 2));
|
|
} finally {
|
|
server.close();
|
|
}
|