Files
vendoo/lib/ai-model-jobs.mjs
Masterluke77andGitHub 6f48827f4d Vendoo 1.41.2 – Git Ignore Boundary & Module Tracking Hotfix (#18)
* docs: prepare Vendoo 1.41.2 git-ignore boundary hotfix

* fix: track native source modules with root-anchored runtime ignores
2026-07-09 17:09:00 +02:00

136 lines
4.4 KiB
JavaScript

import {
createAiModelGeneration,
updateAiModelGeneration,
createAiModelPhoto,
createAiModelJob,
updateAiModelJob,
getAiModelJob,
getPendingAiModelJobs,
} from './db.mjs';
import { generateAiModelPhotos } from './ai-model-photos.mjs';
let processing = false;
let loopStarted = false;
let loopTimer = null;
async function processOneJob(job) {
let lastProgress = -1;
let lastMessage = '';
const reportProgress = (progress, message = '') => {
const normalized = Math.max(1, Math.min(99, Math.round(Number(progress || 0))));
const cleanMessage = String(message || '').trim();
if (normalized === lastProgress && cleanMessage === lastMessage) return;
lastProgress = normalized;
lastMessage = cleanMessage;
updateAiModelJob(job.id, {
status: 'running',
progress: normalized,
progress_message: cleanMessage || 'AI-Bild wird erzeugt …',
error_message: null,
});
};
reportProgress(4, 'Job wird vorbereitet …');
try {
const payload = job.payload_json || {};
const result = await generateAiModelPhotos({ ...payload, settings: payload.settings || {} }, reportProgress);
reportProgress(94, 'Ergebnis wird gespeichert …');
const generation = createAiModelGeneration({
listing_id: payload.listing_id || null,
context: payload.listing_id ? 'listing' : 'generator',
mode: payload.mode || result.mode || 'flatlay',
preset: result.precheck.preset,
variants: result.photos.length,
source_photos: Array.isArray(payload.photos) ? payload.photos : [],
generated_photos: result.photos.map(photo => photo.filename),
prompt_summary: result.prompt_summary,
provider: result.provider?.provider || null,
provider_model: result.provider?.model || null,
moderation_notes: result.photos.map(photo => photo.moderation?.notes).filter(Boolean).join(' | ') || null,
safety_status: 'safe',
status: 'completed',
});
const photos = result.photos.map(photo => createAiModelPhoto({
generation_id: generation.id,
listing_id: payload.listing_id || null,
variant_index: photo.variant_index,
mode: photo.mode || payload.mode || 'flatlay',
preset: photo.preset,
image_path: photo.filename,
source_photo: photo.source_photo,
prompt: photo.prompt,
provider: photo.provider || result.provider?.provider || null,
provider_model: photo.provider_model || result.provider?.model || null,
moderation_result: JSON.stringify(photo.moderation || {}),
safety_status: photo.moderation?.safe === false ? 'blocked' : 'safe',
}));
updateAiModelGeneration(generation.id, {
generated_photos: photos.map(photo => photo.image_path),
provider: result.provider?.provider || null,
provider_model: result.provider?.model || null,
mode: payload.mode || result.mode || 'flatlay',
});
updateAiModelJob(job.id, {
status: 'completed',
progress: 100,
progress_message: 'AI-Bilder sind fertig.',
generation_id: generation.id,
});
return { generation, photos, provider: result.provider || null };
} catch (err) {
updateAiModelJob(job.id, {
status: 'failed',
progress: 100,
progress_message: 'AI-Bildgenerierung fehlgeschlagen.',
error_message: err.message || 'Unbekannter Fehler',
});
throw err;
}
}
async function processQueue() {
if (processing) return;
processing = true;
try {
const pending = getPendingAiModelJobs(5);
for (const job of pending) {
try {
await processOneJob(job);
} catch {}
}
} finally {
processing = false;
}
}
export function startAiModelJobLoop() {
if (loopStarted) return;
loopStarted = true;
loopTimer = setInterval(() => { processQueue().catch(() => {}); }, 2500);
loopTimer.unref?.();
}
export function stopAiModelJobLoop() {
if (loopTimer) clearInterval(loopTimer);
loopTimer = null;
loopStarted = false;
}
export function enqueueAiModelJob(payload = {}) {
const job = createAiModelJob({
listing_id: payload.listing_id || null,
context: payload.listing_id ? 'listing' : 'generator',
mode: payload.mode || 'model',
preset: payload.preset || 'mixed',
variants: payload.variants || 1,
status: 'queued',
progress: 0,
payload_json: payload,
});
processQueue().catch(() => {});
return job;
}
export function getAiModelJobWithRelations(id) {
return getAiModelJob(id);
}