bilder gen

This commit is contained in:
2026-08-17 23:41:49 +02:00
parent 9ecb292c26
commit f4aac46ea0
9 changed files with 1381 additions and 93 deletions

View File

@@ -283,7 +283,11 @@ const TABLES = [
str('merkmale', 255, { array: true }),
str('change_reason', 512),
id('won_against_version_id'), // Vorher/Nachher-Vote
id('reference_file_ids', { array: true }), // → Bucket asset-references
id('reference_file_ids', { array: true }), // EINGABE: hochgeladene Referenzen
// ERGEBNIS: das für diese Version erzeugte Bild. Bewusst getrennt von
// `reference_file_ids` sonst ließe sich später nicht mehr unterscheiden,
// was der Nutzer beigesteuert und was das KI-Modell erzeugt hat.
id('bild_file_id'), // → Bucket generated-images
id('einwilligung_file_id'), // → Bucket consents
],
indexes: [{ key: 'idx_asset', type: 'key', columns: ['asset_id', 'version_no'] }],
@@ -362,9 +366,19 @@ const TABLES = [
enm('typ', [
'bild_gen', 'video_gen', 'tagging', 'vergleich', 'script',
'slot_analyse', 'ordner_vorschlag', 'werbetext', // P19, P20, P21
// Eigener Typ statt `bild_gen` mitzubenutzen: Modell-Generierung hat
// andere Kosten, andere Fehlerbilder und einen anderen Ergebnis-Screen.
// Alles über `refs` zu unterscheiden, macht Kostenauswertung und
// Prompt-Debugging unnötig mühsam.
'modell_gen',
], { required: true }),
str('prompt_template_key', 16), // welcher Prompt …
int('prompt_template_version'), // … in welcher Version lief
// 🔒 Was wirklich ans Modell ging. projekt-uebersicht.md §9 führt
// `jobs.prompt_sent` als eines der drei Felder, die exklusiv dem internen
// Team gehören im Schema fehlte es bisher. Ohne dieses Feld ist bei
// Job-Typen ohne eigene Ergebnis-Tabelle kein Prompt-Debugging möglich.
md('prompt_sent'),
enm('status', ['wartend', 'laeuft', 'fertig', 'fehler'], { default: 'wartend' }),
flt('cost_usd'), int('tokens'),
str('error', 4096),
@@ -540,6 +554,63 @@ const BUCKETS = [
{ id: 'generated-images', name: 'Generierte Bilder' },
];
/**
* Zieht fehlende Enum-Werte auf einer bestehenden Spalte nach.
*
* Der Plan hat das lange als gefährlich geführt (§2.6: „je nach Version Löschen
* und Neuanlegen der Spalte, also Datenverlust"). **Für Appwrite 1.8.1 ist das
* am 15.08.2026 gegen eine Wegwerf-Tabelle geprüft worden:** `PATCH` auf die
* Enum-Spalte liefert 200, alle Bestandswerte überleben, der neue Wert ist
* sofort nutzbar. Bei einem Server-Upgrade gehört dieser Test wiederholt.
*
* Bewusst nur **additiv**: Werte, die live existieren und im Skript fehlen,
* werden gemeldet, aber nie entfernt sonst verlören bestehende Zeilen ihren
* Wert, und genau davor warnt der Plan zu Recht.
*/
/**
* Enum-Spalten, die **nie** automatisch nachgezogen werden.
*
* `prompt_templates.key` ist mit 22 Zeilen gefüllt und trägt die Identität
* jedes Prompts. Selbst wenn der additive PATCH technisch hält hier ist jede
* Enum-Berührung ein Migrationsprojekt mit Prüfung, kein Skriptlauf.
*/
const ENUM_TABU = new Set(['prompt_templates.key']);
async function enumNachziehen(tableId, c, live) {
const key = c.body.key;
if (c.type !== 'enum') { skipped++; console.log(` = ${tableId}.${key} (existiert)`); return; }
if (ENUM_TABU.has(`${tableId}.${key}`)) {
const fehlt = (c.body.elements ?? []).filter((e) => !(live.elements ?? []).includes(e));
if (fehlt.length) {
warned++;
console.warn(` ! ${tableId}.${key}: [${fehlt.join(', ')}] fehlen NICHT automatisch geändert (Tabu-Liste), bitte bewusst migrieren`);
} else { skipped++; console.log(` = ${tableId}.${key} (existiert)`); }
return;
}
const soll = c.body.elements ?? [];
const ist = live.elements ?? [];
const fehlend = soll.filter((e) => !ist.includes(e));
const zusaetzlich = ist.filter((e) => !soll.includes(e));
if (zusaetzlich.length) {
warned++;
console.warn(` ! ${tableId}.${key}: live zusätzlich [${zusaetzlich.join(', ')}] bleibt stehen, wird nicht entfernt`);
}
if (!fehlend.length) { skipped++; console.log(` = ${tableId}.${key} (existiert)`); return; }
const body = {
elements: [...ist, ...fehlend],
required: !!c.body.required,
default: c.body.required ? null : (c.body.default ?? null),
};
await ensure(`${tableId}.${key} +[${fehlend.join(',')}]`, 'PATCH',
`/tablesdb/${DB_ID}/tables/${tableId}/columns/enum/${key}`, body);
updated++; created--; // ensure() zählt als "angelegt" hier ist es eine Änderung
await waitForColumn(tableId, key);
}
// ---- Ablauf ---------------------------------------------------------------
async function waitForColumn(tableId, key) {
for (let i = 0; i < 30; i++) {
@@ -592,8 +663,11 @@ async function main() {
for (const c of t.columns) {
// Erst GET: Appwrite prüft das Zeilengrößen-Limit VOR dem Duplikat-Check,
// ein erneutes POST auf große Spalten gäbe sonst 400 statt 409.
const { status } = await api('GET', `/tablesdb/${DB_ID}/tables/${t.id}/columns/${c.body.key}`);
if (status === 200) { skipped++; console.log(` = ${t.id}.${c.body.key} (existiert)`); continue; }
const { status, json } = await api('GET', `/tablesdb/${DB_ID}/tables/${t.id}/columns/${c.body.key}`);
if (status === 200) {
await enumNachziehen(t.id, c, json);
continue;
}
await ensure(`${t.id}.${c.body.key}`, 'POST', `/tablesdb/${DB_ID}/tables/${t.id}/columns/${c.type}`, c.body);
}
}