@rslsp1/fa-app-tools 2.0.82 → 2.0.84
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/dist/{chunk-GEGJD43Y.mjs → chunk-4FOEFCKM.mjs} +630 -10
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +622 -13
- package/dist/index.mjs +5 -4
- package/dist/szenen.d.mts +193 -8
- package/dist/szenen.d.ts +193 -8
- package/dist/szenen.js +416 -28
- package/dist/szenen.mjs +47 -258
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -786,7 +786,7 @@ __export(index_exports, {
|
|
|
786
786
|
module.exports = __toCommonJS(index_exports);
|
|
787
787
|
|
|
788
788
|
// package.json
|
|
789
|
-
var version = "2.0.
|
|
789
|
+
var version = "2.0.84";
|
|
790
790
|
|
|
791
791
|
// src/hooks/useOnClickOutside.ts
|
|
792
792
|
var import_react = require("react");
|
|
@@ -6593,13 +6593,13 @@ function noticeFor(ergebnis, was) {
|
|
|
6593
6593
|
};
|
|
6594
6594
|
}
|
|
6595
6595
|
case "rejected": {
|
|
6596
|
-
const
|
|
6596
|
+
const fehler4 = ergebnis.errors ?? [];
|
|
6597
6597
|
const ungeprueft = ergebnis.unchecked ?? [];
|
|
6598
6598
|
return {
|
|
6599
6599
|
tone: "rejected",
|
|
6600
|
-
text: `${was}: abgelehnt \u2014 ${
|
|
6600
|
+
text: `${was}: abgelehnt \u2014 ${fehler4.length} Befund${fehler4.length === 1 ? "" : "e"}.`,
|
|
6601
6601
|
details: [
|
|
6602
|
-
...
|
|
6602
|
+
...fehler4.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
|
|
6603
6603
|
// Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
|
|
6604
6604
|
// sondern ungeprüft.
|
|
6605
6605
|
...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
|
|
@@ -11294,7 +11294,7 @@ var ERKLAERUNG = {
|
|
|
11294
11294
|
};
|
|
11295
11295
|
var CapabilityPanel = ({ port, elementId, setupPayload }) => {
|
|
11296
11296
|
const [bericht, setBericht] = (0, import_react36.useState)(null);
|
|
11297
|
-
const [
|
|
11297
|
+
const [fehler4, setFehler] = (0, import_react36.useState)(null);
|
|
11298
11298
|
(0, import_react36.useEffect)(() => {
|
|
11299
11299
|
let abgemeldet = false;
|
|
11300
11300
|
setBericht(null);
|
|
@@ -11316,10 +11316,10 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
|
|
|
11316
11316
|
abgemeldet = true;
|
|
11317
11317
|
};
|
|
11318
11318
|
}, [port, elementId, setupPayload]);
|
|
11319
|
-
if (
|
|
11319
|
+
if (fehler4) {
|
|
11320
11320
|
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { "data-testid": "capability-fehler", role: "alert", children: [
|
|
11321
11321
|
"F\xE4higkeitsbericht nicht erstellbar: ",
|
|
11322
|
-
|
|
11322
|
+
fehler4
|
|
11323
11323
|
] });
|
|
11324
11324
|
}
|
|
11325
11325
|
if (!bericht) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { "data-testid": "capability-laden", children: "F\xE4higkeitsbericht wird erstellt \u2026" });
|
|
@@ -11429,6 +11429,10 @@ var ChainPromptAction = ({
|
|
|
11429
11429
|
var import_react39 = require("react");
|
|
11430
11430
|
|
|
11431
11431
|
// src/lib/szenen/itemDerivation.ts
|
|
11432
|
+
var SZENEN_ITEM_PREFIX = "szn-";
|
|
11433
|
+
function itemIdForRun(runId) {
|
|
11434
|
+
return `${SZENEN_ITEM_PREFIX}${runId}`;
|
|
11435
|
+
}
|
|
11432
11436
|
function roundOfRun(run, runsOfShot) {
|
|
11433
11437
|
return runsOfShot.findIndex((kandidat) => kandidat.run_id === run.run_id) + 1;
|
|
11434
11438
|
}
|
|
@@ -11530,7 +11534,30 @@ var SZENEN_EVENT_TYPES = {
|
|
|
11530
11534
|
* ebenfalls keine Bilddaten — nur Status, Zaehlung und die Item-Kennung,
|
|
11531
11535
|
* falls eine entstand.
|
|
11532
11536
|
*/
|
|
11533
|
-
imageGenerationFinished: "szenen.image_generation_finished"
|
|
11537
|
+
imageGenerationFinished: "szenen.image_generation_finished",
|
|
11538
|
+
/**
|
|
11539
|
+
* Ein Bild-Batch ist kanonisch gespeichert — Stufe 6.9.2, Gate D
|
|
11540
|
+
* (D-6.9.2-6).
|
|
11541
|
+
*
|
|
11542
|
+
* Erscheint erst, **nachdem** jedes Bild des Batches im Bildbestand liegt
|
|
11543
|
+
* (`hfUploadImage` + `image_added`, dieselbe Reihenfolge wie
|
|
11544
|
+
* `hfImageLibrary.putImage`) — kein Bild gilt als vorhanden, bevor sein
|
|
11545
|
+
* Upload bestätigt ist (R3). Trägt bewusst **keine Bilddaten**, nur den
|
|
11546
|
+
* vollständigen Metadatennachweis, den `listBatchesForPromptRun()` /
|
|
11547
|
+
* `getBatch()` ohne MySQL rekonstruieren können müssen: `item_id`,
|
|
11548
|
+
* `prompt_run_id`, `generation_batch_id`, `source_kind`, je Bild
|
|
11549
|
+
* `position`, `media_ref`, `asset_id`, `model`, `seed`, `width`/`height`.
|
|
11550
|
+
*
|
|
11551
|
+
* Additiv wie jedes Ereignis dieser Domain: ein zweiter Batch desselben
|
|
11552
|
+
* Laufs ist ein **weiteres** Ereignis mit derselben `item_id`, nicht eine
|
|
11553
|
+
* Änderung am ersten (D-113/D-121 — ein Lauf bleibt ein Item). Gilt für
|
|
11554
|
+
* `generated_in_app` **und** `external_import` — nur `source_kind`
|
|
11555
|
+
* unterscheidet die beiden Schreiber (D-116/D-117/D-120).
|
|
11556
|
+
*
|
|
11557
|
+
* Der Elementreducer ignoriert auch diesen Typ (`default:`-Zweig); gelesen
|
|
11558
|
+
* wird mit `collectImageBatches` (`imageBatchStore.ts`).
|
|
11559
|
+
*/
|
|
11560
|
+
imageBatchPersisted: "szenen.image_batch_persisted"
|
|
11534
11561
|
};
|
|
11535
11562
|
function sortForReplay(events) {
|
|
11536
11563
|
return [...events].sort((a, b) => {
|
|
@@ -12179,10 +12206,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
12179
12206
|
setNotiz(
|
|
12180
12207
|
ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
|
|
12181
12208
|
);
|
|
12182
|
-
} catch (
|
|
12209
|
+
} catch (fehler4) {
|
|
12183
12210
|
setNotiz({
|
|
12184
12211
|
tone: "error",
|
|
12185
|
-
text: `Externe Bilder importieren: ${
|
|
12212
|
+
text: `Externe Bilder importieren: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
|
|
12186
12213
|
});
|
|
12187
12214
|
}
|
|
12188
12215
|
setLaeuft(false);
|
|
@@ -17492,6 +17519,559 @@ function createHfImageLibrary(options) {
|
|
|
17492
17519
|
};
|
|
17493
17520
|
}
|
|
17494
17521
|
|
|
17522
|
+
// src/lib/szenen/flowImageProvider.ts
|
|
17523
|
+
var FLOW_MODEL_DISPLAY_NAMES = {
|
|
17524
|
+
"imagen-4.0-generate-001": "Imagen 4",
|
|
17525
|
+
"flow-nano-banana-pro": "\u{1F34C} Nano Banana Pro",
|
|
17526
|
+
"flow-nano-banana-2": "\u{1F34C} Nano Banana 2"
|
|
17527
|
+
};
|
|
17528
|
+
function flowImageProviderHasAccess(model) {
|
|
17529
|
+
return model.callable_model_id in FLOW_MODEL_DISPLAY_NAMES;
|
|
17530
|
+
}
|
|
17531
|
+
function istNichtleererText(wert) {
|
|
17532
|
+
return typeof wert === "string" && wert.trim().length > 0;
|
|
17533
|
+
}
|
|
17534
|
+
function createFlowImageProvider(generateImage) {
|
|
17535
|
+
return {
|
|
17536
|
+
async generate(input) {
|
|
17537
|
+
const modelDisplayName = FLOW_MODEL_DISPLAY_NAMES[input.callableModelId];
|
|
17538
|
+
if (!modelDisplayName) {
|
|
17539
|
+
throw new Error(
|
|
17540
|
+
`Der Flow-Host kennt keinen Flow-Display-Namen zu "${input.callableModelId}". Bekannt sind: ${Object.keys(FLOW_MODEL_DISPLAY_NAMES).join(", ")}. Es wird kein anderes Modell ersatzweise gerufen (D-115/D-6.9.2-3).`
|
|
17541
|
+
);
|
|
17542
|
+
}
|
|
17543
|
+
if (istNichtleererText(input.negativePrompt)) {
|
|
17544
|
+
throw new Error(
|
|
17545
|
+
"Flow.generate.image kennt keinen dokumentierten Parameter f\xFCr einen negativen Prompt. Der Auftrag wird deshalb als fehlgeschlagen protokolliert, statt den negativen Prompt still zu verlieren (D-6.9.2-3)."
|
|
17546
|
+
);
|
|
17547
|
+
}
|
|
17548
|
+
const erzeugt = [];
|
|
17549
|
+
let ersterFehler = null;
|
|
17550
|
+
for (let position = 0; position < input.imageCount; position++) {
|
|
17551
|
+
try {
|
|
17552
|
+
const antwort = await generateImage({
|
|
17553
|
+
prompt: input.positivePrompt,
|
|
17554
|
+
modelDisplayName,
|
|
17555
|
+
...input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}
|
|
17556
|
+
});
|
|
17557
|
+
if (!antwort || typeof antwort.base64 !== "string" || antwort.base64.length === 0) {
|
|
17558
|
+
throw new Error("Flow.generate.image hat kein Bild geliefert.");
|
|
17559
|
+
}
|
|
17560
|
+
erzeugt.push({
|
|
17561
|
+
imageBase64: antwort.base64,
|
|
17562
|
+
// Das tatsaechlich gerufene Modell, nicht der Flow-Display-Name —
|
|
17563
|
+
// im Bestand soll stehen, was bezahlt wurde (wie reactImageProvider.ts).
|
|
17564
|
+
model: input.callableModelId
|
|
17565
|
+
// Kein Seed: weder als Eingabe dokumentiert noch als Ausgabe
|
|
17566
|
+
// geliefert — nichts wird erfunden (D-107).
|
|
17567
|
+
});
|
|
17568
|
+
} catch (fehler4) {
|
|
17569
|
+
ersterFehler = fehler4 instanceof Error ? fehler4.message : String(fehler4);
|
|
17570
|
+
break;
|
|
17571
|
+
}
|
|
17572
|
+
}
|
|
17573
|
+
if (erzeugt.length === 0) {
|
|
17574
|
+
throw new Error(
|
|
17575
|
+
ersterFehler ?? `Flow.generate.image hat zu ${input.callableModelId} kein verwertbares Bild geliefert.`
|
|
17576
|
+
);
|
|
17577
|
+
}
|
|
17578
|
+
return { images: erzeugt };
|
|
17579
|
+
}
|
|
17580
|
+
};
|
|
17581
|
+
}
|
|
17582
|
+
|
|
17583
|
+
// src/lib/szenen/imageGenerationOrderStore.ts
|
|
17584
|
+
function istText2(wert) {
|
|
17585
|
+
return typeof wert === "string" && wert.trim().length > 0;
|
|
17586
|
+
}
|
|
17587
|
+
function collectImageGenerationOrders(events) {
|
|
17588
|
+
const byBatchId = /* @__PURE__ */ new Map();
|
|
17589
|
+
for (const event of events) {
|
|
17590
|
+
if (event.type !== SZENEN_EVENT_TYPES.imageGenerationStarted) continue;
|
|
17591
|
+
const payload = event.payload;
|
|
17592
|
+
if (!payload || !istText2(payload.generation_batch_id) || !istText2(payload.prompt_run_id)) continue;
|
|
17593
|
+
if (byBatchId.has(payload.generation_batch_id)) continue;
|
|
17594
|
+
byBatchId.set(payload.generation_batch_id, {
|
|
17595
|
+
generation_batch_id: payload.generation_batch_id,
|
|
17596
|
+
prompt_run_id: payload.prompt_run_id,
|
|
17597
|
+
source_kind: "generated_in_app",
|
|
17598
|
+
request: payload.request,
|
|
17599
|
+
created_at: payload.created_at ?? "",
|
|
17600
|
+
status: "started"
|
|
17601
|
+
});
|
|
17602
|
+
}
|
|
17603
|
+
for (const event of events) {
|
|
17604
|
+
if (event.type !== SZENEN_EVENT_TYPES.imageGenerationFinished) continue;
|
|
17605
|
+
const payload = event.payload;
|
|
17606
|
+
if (!payload || !istText2(payload.generation_batch_id)) continue;
|
|
17607
|
+
const auftrag = byBatchId.get(payload.generation_batch_id);
|
|
17608
|
+
if (!auftrag) continue;
|
|
17609
|
+
if (auftrag.status !== "started") continue;
|
|
17610
|
+
if (payload.status !== "succeeded" && payload.status !== "partial" && payload.status !== "failed") continue;
|
|
17611
|
+
byBatchId.set(payload.generation_batch_id, {
|
|
17612
|
+
...auftrag,
|
|
17613
|
+
status: payload.status,
|
|
17614
|
+
...payload.item_id ? { item_id: payload.item_id } : {},
|
|
17615
|
+
produced_count: payload.produced_count ?? 0,
|
|
17616
|
+
...payload.diagnostic ? { diagnostic: payload.diagnostic } : {},
|
|
17617
|
+
finished_at: payload.finished_at ?? ""
|
|
17618
|
+
});
|
|
17619
|
+
}
|
|
17620
|
+
return [...byBatchId.values()];
|
|
17621
|
+
}
|
|
17622
|
+
function ordersForPromptRun(events, runId) {
|
|
17623
|
+
return collectImageGenerationOrders(events).filter((o) => o.prompt_run_id === runId);
|
|
17624
|
+
}
|
|
17625
|
+
|
|
17626
|
+
// src/lib/szenen/imageBatchStore.ts
|
|
17627
|
+
var istText3 = (wert) => typeof wert === "string" && wert.trim().length > 0;
|
|
17628
|
+
function alsBilder(rohe) {
|
|
17629
|
+
if (!Array.isArray(rohe)) return [];
|
|
17630
|
+
return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText3(b.media_ref) && typeof b.position === "number").map((b) => ({
|
|
17631
|
+
media_ref: b.media_ref,
|
|
17632
|
+
position: b.position,
|
|
17633
|
+
...istText3(b.asset_id) ? { asset_id: b.asset_id } : {},
|
|
17634
|
+
model: b.model ?? null,
|
|
17635
|
+
seed: b.seed ?? null,
|
|
17636
|
+
width: b.width ?? null,
|
|
17637
|
+
height: b.height ?? null
|
|
17638
|
+
}));
|
|
17639
|
+
}
|
|
17640
|
+
function collectImageBatches(events) {
|
|
17641
|
+
const byItemId = /* @__PURE__ */ new Map();
|
|
17642
|
+
for (const event of events) {
|
|
17643
|
+
if (event.type !== SZENEN_EVENT_TYPES.imageBatchPersisted) continue;
|
|
17644
|
+
const payload = event.payload;
|
|
17645
|
+
if (!payload || !istText3(payload.item_id) || !istText3(payload.prompt_run_id) || !istText3(payload.generation_batch_id)) continue;
|
|
17646
|
+
if (payload.source_kind !== "generated_in_app" && payload.source_kind !== "external_import") continue;
|
|
17647
|
+
const bestehend = byItemId.get(payload.item_id);
|
|
17648
|
+
if (bestehend?.generation_batch_ids.includes(payload.generation_batch_id)) continue;
|
|
17649
|
+
const bilder = alsBilder(payload.images);
|
|
17650
|
+
if (!bestehend) {
|
|
17651
|
+
byItemId.set(payload.item_id, {
|
|
17652
|
+
item_id: payload.item_id,
|
|
17653
|
+
prompt_run_id: payload.prompt_run_id,
|
|
17654
|
+
generation_batch_ids: [payload.generation_batch_id],
|
|
17655
|
+
source_kind: payload.source_kind,
|
|
17656
|
+
title: istText3(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
|
|
17657
|
+
images: bilder
|
|
17658
|
+
});
|
|
17659
|
+
continue;
|
|
17660
|
+
}
|
|
17661
|
+
byItemId.set(payload.item_id, {
|
|
17662
|
+
...bestehend,
|
|
17663
|
+
generation_batch_ids: [...bestehend.generation_batch_ids, payload.generation_batch_id],
|
|
17664
|
+
images: [...bestehend.images, ...bilder]
|
|
17665
|
+
});
|
|
17666
|
+
}
|
|
17667
|
+
return [...byItemId.values()];
|
|
17668
|
+
}
|
|
17669
|
+
function batchesForPromptRun(events, runId) {
|
|
17670
|
+
return collectImageBatches(events).filter((b) => b.prompt_run_id === runId);
|
|
17671
|
+
}
|
|
17672
|
+
function findImageBatch(events, batchId) {
|
|
17673
|
+
return collectImageBatches(events).find((b) => b.generation_batch_ids.includes(batchId)) ?? null;
|
|
17674
|
+
}
|
|
17675
|
+
|
|
17676
|
+
// src/lib/szenen/hfImageBatchHistory.ts
|
|
17677
|
+
function lesefehler(aktion, error) {
|
|
17678
|
+
return {
|
|
17679
|
+
kind: "error",
|
|
17680
|
+
message: `${aktion}: ${error instanceof Error ? error.message : String(error)}`,
|
|
17681
|
+
retryable: true
|
|
17682
|
+
};
|
|
17683
|
+
}
|
|
17684
|
+
function createHfImageBatchHistory(options) {
|
|
17685
|
+
const { channel } = options;
|
|
17686
|
+
return {
|
|
17687
|
+
async listBatchesForPromptRun(runId) {
|
|
17688
|
+
try {
|
|
17689
|
+
const events = await channel.readEvents();
|
|
17690
|
+
return { kind: "ok", value: batchesForPromptRun(events, runId) };
|
|
17691
|
+
} catch (error) {
|
|
17692
|
+
return lesefehler(`Batches fuer Lauf ${runId} konnten nicht gelesen werden`, error);
|
|
17693
|
+
}
|
|
17694
|
+
},
|
|
17695
|
+
async getBatch(batchId) {
|
|
17696
|
+
try {
|
|
17697
|
+
const events = await channel.readEvents();
|
|
17698
|
+
return { kind: "ok", value: findImageBatch(events, batchId) };
|
|
17699
|
+
} catch (error) {
|
|
17700
|
+
return lesefehler(`Batch ${batchId} konnte nicht gelesen werden`, error);
|
|
17701
|
+
}
|
|
17702
|
+
},
|
|
17703
|
+
async listOrdersForPromptRun(runId) {
|
|
17704
|
+
try {
|
|
17705
|
+
const events = await channel.readEvents();
|
|
17706
|
+
return { kind: "ok", value: ordersForPromptRun(events, runId) };
|
|
17707
|
+
} catch (error) {
|
|
17708
|
+
return lesefehler(`Bildgenerierungsauftraege fuer Lauf ${runId} konnten nicht gelesen werden`, error);
|
|
17709
|
+
}
|
|
17710
|
+
}
|
|
17711
|
+
};
|
|
17712
|
+
}
|
|
17713
|
+
|
|
17714
|
+
// src/lib/szenen/hfImageBatchPersistence.ts
|
|
17715
|
+
init_hfStateService();
|
|
17716
|
+
init_hfEventLog();
|
|
17717
|
+
function fehler3(nachricht, retryable = true) {
|
|
17718
|
+
return { kind: "error", message: nachricht, retryable };
|
|
17719
|
+
}
|
|
17720
|
+
async function legeBatchAb(options, input) {
|
|
17721
|
+
const { namespace, token, channel } = options;
|
|
17722
|
+
const newImageId = options.newImageId ?? ((batchId, position) => `${batchId}-${position}`);
|
|
17723
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
17724
|
+
const itemId = itemIdForRun(input.runId);
|
|
17725
|
+
let vorhandeneEvents;
|
|
17726
|
+
try {
|
|
17727
|
+
vorhandeneEvents = await channel.readEvents();
|
|
17728
|
+
} catch (error) {
|
|
17729
|
+
return fehler3(
|
|
17730
|
+
`Bild-Batches von Lauf ${input.runId} konnten vor dem Ablegen nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`
|
|
17731
|
+
);
|
|
17732
|
+
}
|
|
17733
|
+
const bestehend = batchesForPromptRun(vorhandeneEvents, input.runId)[0] ?? null;
|
|
17734
|
+
if (bestehend?.generation_batch_ids.includes(input.generationBatchId)) {
|
|
17735
|
+
return { kind: "ok", value: bestehend };
|
|
17736
|
+
}
|
|
17737
|
+
const startPosition = bestehend?.images.length ?? 0;
|
|
17738
|
+
const abgelegt = [];
|
|
17739
|
+
for (let offset = 0; offset < input.images.length; offset++) {
|
|
17740
|
+
const bild = input.images[offset];
|
|
17741
|
+
const id = newImageId(input.generationBatchId, startPosition + offset);
|
|
17742
|
+
try {
|
|
17743
|
+
await hfUploadImage(bild.imageBase64, id, token, bild.mimeType);
|
|
17744
|
+
await writeHFEvent(namespace, token, "image_added", {
|
|
17745
|
+
id,
|
|
17746
|
+
prompt: input.title ?? `Batch ${input.generationBatchId}`,
|
|
17747
|
+
tags: [],
|
|
17748
|
+
timestamp: now().getTime(),
|
|
17749
|
+
mimeType: bild.mimeType
|
|
17750
|
+
}, []);
|
|
17751
|
+
} catch (error) {
|
|
17752
|
+
return fehler3(
|
|
17753
|
+
`Bild ${offset + 1}/${input.images.length} des Batches ${input.generationBatchId} konnte nicht im Bildbestand abgelegt werden: ${error instanceof Error ? error.message : String(error)}. Bereits abgelegte Bilder dieses Versuchs (${abgelegt.length}) bleiben im Bestand, geh\xF6ren aber zu keinem Batch, solange das Batch-Ereignis nicht geschrieben ist.`
|
|
17754
|
+
);
|
|
17755
|
+
}
|
|
17756
|
+
abgelegt.push({
|
|
17757
|
+
position: startPosition + offset,
|
|
17758
|
+
media_ref: id,
|
|
17759
|
+
asset_id: id,
|
|
17760
|
+
model: bild.model ?? null,
|
|
17761
|
+
seed: bild.seed ?? null,
|
|
17762
|
+
width: bild.width ?? null,
|
|
17763
|
+
height: bild.height ?? null
|
|
17764
|
+
});
|
|
17765
|
+
}
|
|
17766
|
+
const titel = bestehend?.title ?? (input.title?.trim() || `Batch aus Lauf ${input.runId}`);
|
|
17767
|
+
const payload = {
|
|
17768
|
+
item_id: itemId,
|
|
17769
|
+
prompt_run_id: input.runId,
|
|
17770
|
+
generation_batch_id: input.generationBatchId,
|
|
17771
|
+
source_kind: input.sourceKind,
|
|
17772
|
+
title: titel,
|
|
17773
|
+
images: abgelegt,
|
|
17774
|
+
created_at: now().toISOString()
|
|
17775
|
+
};
|
|
17776
|
+
try {
|
|
17777
|
+
await channel.appendEvent(SZENEN_EVENT_TYPES.imageBatchPersisted, payload);
|
|
17778
|
+
} catch (error) {
|
|
17779
|
+
return fehler3(
|
|
17780
|
+
`Die Batch-Metadaten fuer ${input.generationBatchId} konnten nicht protokolliert werden, obwohl die Bilder bereits im Bestand liegen: ${error instanceof Error ? error.message : String(error)}`
|
|
17781
|
+
);
|
|
17782
|
+
}
|
|
17783
|
+
const zusammengefuehrt = {
|
|
17784
|
+
item_id: itemId,
|
|
17785
|
+
prompt_run_id: input.runId,
|
|
17786
|
+
generation_batch_ids: bestehend ? [...bestehend.generation_batch_ids, input.generationBatchId] : [input.generationBatchId],
|
|
17787
|
+
source_kind: bestehend?.source_kind ?? input.sourceKind,
|
|
17788
|
+
title: titel,
|
|
17789
|
+
images: [...bestehend?.images ?? [], ...abgelegt]
|
|
17790
|
+
};
|
|
17791
|
+
return { kind: "ok", value: zusammengefuehrt };
|
|
17792
|
+
}
|
|
17793
|
+
function createHfImageGenerationBatchPersistence(options) {
|
|
17794
|
+
return {
|
|
17795
|
+
async persist(input) {
|
|
17796
|
+
return legeBatchAb(options, {
|
|
17797
|
+
runId: input.runId,
|
|
17798
|
+
generationBatchId: input.generationBatchId,
|
|
17799
|
+
sourceKind: "generated_in_app",
|
|
17800
|
+
images: input.images.map((bild) => ({
|
|
17801
|
+
imageBase64: bild.imageBase64,
|
|
17802
|
+
// Flow liefert PNG-Base64 (bestehende Konvention, s. AvatarArchitectApp.tsx
|
|
17803
|
+
// `data:image/png;base64,${base64}`); der gemeinsame Vertrag führt
|
|
17804
|
+
// keinen MIME-Typ je Bild.
|
|
17805
|
+
mimeType: "image/png",
|
|
17806
|
+
model: bild.model ?? null,
|
|
17807
|
+
seed: bild.seed ?? null,
|
|
17808
|
+
width: bild.width ?? null,
|
|
17809
|
+
height: bild.height ?? null
|
|
17810
|
+
}))
|
|
17811
|
+
});
|
|
17812
|
+
}
|
|
17813
|
+
};
|
|
17814
|
+
}
|
|
17815
|
+
function createHfImageBatchWrite(options) {
|
|
17816
|
+
return {
|
|
17817
|
+
async importBatch(input) {
|
|
17818
|
+
return legeBatchAb(options, {
|
|
17819
|
+
runId: input.runId,
|
|
17820
|
+
generationBatchId: input.generationBatchId,
|
|
17821
|
+
sourceKind: "external_import",
|
|
17822
|
+
title: input.title,
|
|
17823
|
+
images: input.images.map((bild) => ({
|
|
17824
|
+
imageBase64: bild.imageBase64,
|
|
17825
|
+
mimeType: "image/png",
|
|
17826
|
+
model: bild.model ?? null,
|
|
17827
|
+
seed: bild.seed ?? null,
|
|
17828
|
+
width: bild.width ?? null,
|
|
17829
|
+
height: bild.height ?? null
|
|
17830
|
+
}))
|
|
17831
|
+
});
|
|
17832
|
+
},
|
|
17833
|
+
async attachLibraryItem(input) {
|
|
17834
|
+
let events;
|
|
17835
|
+
try {
|
|
17836
|
+
events = await options.channel.readEvents();
|
|
17837
|
+
} catch (error) {
|
|
17838
|
+
return {
|
|
17839
|
+
kind: "error",
|
|
17840
|
+
message: `Batches konnten nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`,
|
|
17841
|
+
retryable: true
|
|
17842
|
+
};
|
|
17843
|
+
}
|
|
17844
|
+
const item = collectImageBatches(events).find((b) => b.item_id === input.itemId);
|
|
17845
|
+
if (!item) {
|
|
17846
|
+
return { kind: "error", message: `Item ${input.itemId} ist unbekannt.`, retryable: false };
|
|
17847
|
+
}
|
|
17848
|
+
if (item.prompt_run_id !== input.runId) {
|
|
17849
|
+
return {
|
|
17850
|
+
kind: "conflict",
|
|
17851
|
+
message: `Item ${input.itemId} geh\xF6rt bereits zu Lauf ${item.prompt_run_id}.`,
|
|
17852
|
+
existingPromptRunId: item.prompt_run_id
|
|
17853
|
+
};
|
|
17854
|
+
}
|
|
17855
|
+
return { kind: "ok", value: item };
|
|
17856
|
+
}
|
|
17857
|
+
};
|
|
17858
|
+
}
|
|
17859
|
+
|
|
17860
|
+
// src/lib/szenen/imageGenerationCapability.ts
|
|
17861
|
+
var isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
17862
|
+
var IMAGE_MODEL_CATALOGUE = {
|
|
17863
|
+
imagen_4: {
|
|
17864
|
+
callable_model_id: "imagen-4.0-generate-001",
|
|
17865
|
+
provider: "google",
|
|
17866
|
+
max_images: 4,
|
|
17867
|
+
aspect_ratios: ["1:1", "3:4", "4:3", "9:16", "16:9"],
|
|
17868
|
+
supports_seed: true
|
|
17869
|
+
},
|
|
17870
|
+
imagen_3: {
|
|
17871
|
+
callable_model_id: "imagen-3.0-generate-001",
|
|
17872
|
+
provider: "google",
|
|
17873
|
+
max_images: 4,
|
|
17874
|
+
aspect_ratios: ["1:1", "3:4", "4:3", "9:16", "16:9"],
|
|
17875
|
+
supports_seed: true
|
|
17876
|
+
},
|
|
17877
|
+
/**
|
|
17878
|
+
* Stufe 6.9.2, Nachtrag — real gegen Flow geprüft (2026-08-17): Der
|
|
17879
|
+
* Modellwähler im Flow-„Generieren"-Tab zeigt aktuell nur "Nano Banana Pro"
|
|
17880
|
+
* und "Nano Banana 2" als wählbar an, "Imagen 4" erscheint dort ausgegraut.
|
|
17881
|
+
* `provider: 'flow'` statt `'google'` ist bewusst: Diese Modelle sind nur
|
|
17882
|
+
* über `Flow.generate.image` erreichbar, nicht über die direkte
|
|
17883
|
+
* Google-Imagen-REST-Route, die der React-Host benutzt (`reactImageProvider.ts`)
|
|
17884
|
+
* — React meldet sie deshalb korrekt als `unsupported`, ohne eine eigene
|
|
17885
|
+
* Host-Verzweigung zu brauchen (D-115/R-1: `hasAccess` prüft `provider`).
|
|
17886
|
+
*/
|
|
17887
|
+
nano_banana_pro: {
|
|
17888
|
+
callable_model_id: "flow-nano-banana-pro",
|
|
17889
|
+
provider: "flow",
|
|
17890
|
+
max_images: 4,
|
|
17891
|
+
// Flow.generate.image dokumentiert nur '1:1' | '16:9' | '9:16' (howto-flow-sdk.md).
|
|
17892
|
+
aspect_ratios: ["1:1", "16:9", "9:16"],
|
|
17893
|
+
// Weder als Eingabe- noch als Ausgabeparameter dokumentiert — nichts erfunden (D-107).
|
|
17894
|
+
supports_seed: false
|
|
17895
|
+
},
|
|
17896
|
+
nano_banana_2: {
|
|
17897
|
+
callable_model_id: "flow-nano-banana-2",
|
|
17898
|
+
provider: "flow",
|
|
17899
|
+
max_images: 4,
|
|
17900
|
+
aspect_ratios: ["1:1", "16:9", "9:16"],
|
|
17901
|
+
supports_seed: false
|
|
17902
|
+
}
|
|
17903
|
+
};
|
|
17904
|
+
function callableImageModelFor(targetModelKey) {
|
|
17905
|
+
return IMAGE_MODEL_CATALOGUE[targetModelKey] ?? null;
|
|
17906
|
+
}
|
|
17907
|
+
function targetModelKeyOf(targetModel) {
|
|
17908
|
+
if (!isObject9(targetModel)) return null;
|
|
17909
|
+
return typeof targetModel.key === "string" && targetModel.key.length > 0 ? targetModel.key : null;
|
|
17910
|
+
}
|
|
17911
|
+
function capabilityForTargetModel(targetModel, hasAccess) {
|
|
17912
|
+
const key = targetModelKeyOf(targetModel);
|
|
17913
|
+
if (!key) {
|
|
17914
|
+
return {
|
|
17915
|
+
kind: "unsupported",
|
|
17916
|
+
target_model_key: "(unbekannt)",
|
|
17917
|
+
reason: 'Der eingefrorene Lauf tr\xE4gt kein "target_model.key" \u2014 es gibt nichts zuzuordnen.'
|
|
17918
|
+
};
|
|
17919
|
+
}
|
|
17920
|
+
const modell = callableImageModelFor(key);
|
|
17921
|
+
if (!modell) {
|
|
17922
|
+
return {
|
|
17923
|
+
kind: "unsupported",
|
|
17924
|
+
target_model_key: key,
|
|
17925
|
+
reason: `F\xFCr das Zielmodell "${key}" ist in diesem Host kein Bildpfad katalogisiert.`
|
|
17926
|
+
};
|
|
17927
|
+
}
|
|
17928
|
+
if (!hasAccess(modell)) {
|
|
17929
|
+
return {
|
|
17930
|
+
kind: "unsupported",
|
|
17931
|
+
target_model_key: key,
|
|
17932
|
+
reason: `Dieser Host hat keinen Zugang zu ${modell.callable_model_id} (${modell.provider}).`
|
|
17933
|
+
};
|
|
17934
|
+
}
|
|
17935
|
+
return {
|
|
17936
|
+
kind: "supported",
|
|
17937
|
+
target_model_key: key,
|
|
17938
|
+
provider: modell.provider,
|
|
17939
|
+
callable_model_id: modell.callable_model_id,
|
|
17940
|
+
max_images: modell.max_images,
|
|
17941
|
+
aspect_ratios: modell.aspect_ratios,
|
|
17942
|
+
supports_seed: modell.supports_seed
|
|
17943
|
+
};
|
|
17944
|
+
}
|
|
17945
|
+
|
|
17946
|
+
// src/lib/szenen/imageGenerationPort.ts
|
|
17947
|
+
function queryError2(error, action) {
|
|
17948
|
+
return {
|
|
17949
|
+
kind: "error",
|
|
17950
|
+
message: `${action}: ${error instanceof Error ? error.message : String(error)}`,
|
|
17951
|
+
retryable: true
|
|
17952
|
+
};
|
|
17953
|
+
}
|
|
17954
|
+
function createImageGenerationPort(options) {
|
|
17955
|
+
const { runs, provider, batches, events, hasAccess } = options;
|
|
17956
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
17957
|
+
async function readRun(runId) {
|
|
17958
|
+
try {
|
|
17959
|
+
return await runs.getRun(runId);
|
|
17960
|
+
} catch (error) {
|
|
17961
|
+
return queryError2(error, `Lauf ${runId} konnte nicht gelesen werden`);
|
|
17962
|
+
}
|
|
17963
|
+
}
|
|
17964
|
+
return {
|
|
17965
|
+
async capabilityForRun(runId) {
|
|
17966
|
+
const gelesen = await readRun(runId);
|
|
17967
|
+
if (gelesen.kind !== "ok") return gelesen;
|
|
17968
|
+
if (!gelesen.value) {
|
|
17969
|
+
return { kind: "error", message: `Lauf ${runId} ist unbekannt.`, retryable: false };
|
|
17970
|
+
}
|
|
17971
|
+
const targetModel = gelesen.value.input_payload?.target_model;
|
|
17972
|
+
return { kind: "ok", value: capabilityForTargetModel(targetModel, hasAccess) };
|
|
17973
|
+
},
|
|
17974
|
+
async startFromPromptRun(input) {
|
|
17975
|
+
const gelesen = await readRun(input.runId);
|
|
17976
|
+
if (gelesen.kind !== "ok") return { kind: "rejected", message: gelesen.message };
|
|
17977
|
+
if (!gelesen.value) {
|
|
17978
|
+
return { kind: "rejected", message: `Lauf ${input.runId} ist unbekannt.` };
|
|
17979
|
+
}
|
|
17980
|
+
const run = gelesen.value;
|
|
17981
|
+
if (run.status !== "succeeded" || !run.output) {
|
|
17982
|
+
return {
|
|
17983
|
+
kind: "rejected",
|
|
17984
|
+
message: `Lauf ${input.runId} ist nicht erfolgreich \u2014 ein Bildauftrag braucht einen eingefrorenen Prompt (D-103).`
|
|
17985
|
+
};
|
|
17986
|
+
}
|
|
17987
|
+
const targetModel = run.input_payload?.target_model;
|
|
17988
|
+
const capability = capabilityForTargetModel(targetModel, hasAccess);
|
|
17989
|
+
if (capability.kind !== "supported") {
|
|
17990
|
+
return { kind: "unsupported", targetModelKey: capability.target_model_key, reason: capability.reason };
|
|
17991
|
+
}
|
|
17992
|
+
const startedPayload = {
|
|
17993
|
+
generation_batch_id: input.generationBatchId,
|
|
17994
|
+
prompt_run_id: input.runId,
|
|
17995
|
+
source_kind: "generated_in_app",
|
|
17996
|
+
request: {
|
|
17997
|
+
image_count: input.imageCount,
|
|
17998
|
+
...input.aspectRatio ? { aspect_ratio: input.aspectRatio } : {},
|
|
17999
|
+
...input.seeds ? { seeds: input.seeds } : {},
|
|
18000
|
+
callable_model_id: capability.callable_model_id
|
|
18001
|
+
},
|
|
18002
|
+
created_at: now().toISOString()
|
|
18003
|
+
};
|
|
18004
|
+
try {
|
|
18005
|
+
await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationStarted, startedPayload);
|
|
18006
|
+
} catch (error) {
|
|
18007
|
+
return {
|
|
18008
|
+
kind: "failed",
|
|
18009
|
+
message: `Der Auftrag ${input.generationBatchId} konnte nicht protokolliert werden: ${error instanceof Error ? error.message : String(error)}`
|
|
18010
|
+
};
|
|
18011
|
+
}
|
|
18012
|
+
let produced = [];
|
|
18013
|
+
let providerFehler = null;
|
|
18014
|
+
try {
|
|
18015
|
+
const antwort = await provider.generate({
|
|
18016
|
+
callableModelId: capability.callable_model_id,
|
|
18017
|
+
provider: capability.provider,
|
|
18018
|
+
positivePrompt: run.output.positive,
|
|
18019
|
+
negativePrompt: run.output.negative,
|
|
18020
|
+
imageCount: input.imageCount,
|
|
18021
|
+
...input.aspectRatio ? { aspectRatio: input.aspectRatio } : {},
|
|
18022
|
+
...input.seeds ? { seeds: input.seeds } : {}
|
|
18023
|
+
});
|
|
18024
|
+
produced = antwort.images;
|
|
18025
|
+
} catch (error) {
|
|
18026
|
+
providerFehler = error instanceof Error ? error.message : String(error);
|
|
18027
|
+
}
|
|
18028
|
+
if (providerFehler || produced.length === 0) {
|
|
18029
|
+
const finishedPayload2 = {
|
|
18030
|
+
generation_batch_id: input.generationBatchId,
|
|
18031
|
+
status: "failed",
|
|
18032
|
+
produced_count: 0,
|
|
18033
|
+
diagnostic: providerFehler ?? "Der Provider hat kein verwertbares Bild geliefert.",
|
|
18034
|
+
finished_at: now().toISOString()
|
|
18035
|
+
};
|
|
18036
|
+
await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload2).catch(() => {
|
|
18037
|
+
});
|
|
18038
|
+
return {
|
|
18039
|
+
kind: "failed",
|
|
18040
|
+
message: finishedPayload2.diagnostic
|
|
18041
|
+
};
|
|
18042
|
+
}
|
|
18043
|
+
const persistiert = await batches.persist({
|
|
18044
|
+
runId: input.runId,
|
|
18045
|
+
generationBatchId: input.generationBatchId,
|
|
18046
|
+
images: produced
|
|
18047
|
+
});
|
|
18048
|
+
if (persistiert.kind !== "ok") {
|
|
18049
|
+
const finishedPayload2 = {
|
|
18050
|
+
generation_batch_id: input.generationBatchId,
|
|
18051
|
+
status: "failed",
|
|
18052
|
+
produced_count: 0,
|
|
18053
|
+
diagnostic: persistiert.kind === "error" ? persistiert.message : `Batch ${input.generationBatchId} konnte nicht gespeichert werden (${persistiert.kind}).`,
|
|
18054
|
+
finished_at: now().toISOString()
|
|
18055
|
+
};
|
|
18056
|
+
await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload2).catch(() => {
|
|
18057
|
+
});
|
|
18058
|
+
return { kind: "failed", message: finishedPayload2.diagnostic };
|
|
18059
|
+
}
|
|
18060
|
+
const istPartial = produced.length < input.imageCount;
|
|
18061
|
+
const finishedPayload = {
|
|
18062
|
+
generation_batch_id: input.generationBatchId,
|
|
18063
|
+
status: istPartial ? "partial" : "succeeded",
|
|
18064
|
+
item_id: persistiert.value.item_id,
|
|
18065
|
+
produced_count: produced.length,
|
|
18066
|
+
finished_at: now().toISOString()
|
|
18067
|
+
};
|
|
18068
|
+
await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload).catch(() => {
|
|
18069
|
+
});
|
|
18070
|
+
return istPartial ? { kind: "partial", batch: persistiert.value, requested: input.imageCount, produced: produced.length } : { kind: "succeeded", batch: persistiert.value };
|
|
18071
|
+
}
|
|
18072
|
+
};
|
|
18073
|
+
}
|
|
18074
|
+
|
|
17495
18075
|
// src/lib/szenen/flowViewPort.ts
|
|
17496
18076
|
var defaultContext = {
|
|
17497
18077
|
async chainFor(contextId) {
|
|
@@ -17539,8 +18119,36 @@ function createFlowSzenenViewPort(options) {
|
|
|
17539
18119
|
...options.newRunId ? { newRunId: options.newRunId } : {},
|
|
17540
18120
|
...options.now ? { now: options.now } : {}
|
|
17541
18121
|
});
|
|
17542
|
-
const
|
|
18122
|
+
const imageBatchHistory = createHfImageBatchHistory({ channel });
|
|
18123
|
+
const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
|
|
18124
|
+
const imageBatchWrite = hatHfZugangsdaten ? createHfImageBatchWrite({
|
|
18125
|
+
namespace: options.namespace,
|
|
18126
|
+
token: options.token,
|
|
18127
|
+
channel,
|
|
18128
|
+
...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
|
|
18129
|
+
...options.now ? { now: options.now } : {}
|
|
18130
|
+
}) : void 0;
|
|
18131
|
+
const bildProvider = options.imageProvider ?? (options.generateImage ? createFlowImageProvider(options.generateImage) : void 0);
|
|
18132
|
+
const imageGeneration = bildProvider && imageBatchWrite ? createImageGenerationPort({
|
|
18133
|
+
runs: { async getRun(runId) {
|
|
18134
|
+
return runs.getRun(runId);
|
|
18135
|
+
} },
|
|
18136
|
+
provider: bildProvider,
|
|
18137
|
+
batches: createHfImageGenerationBatchPersistence({
|
|
18138
|
+
namespace: options.namespace,
|
|
18139
|
+
token: options.token,
|
|
18140
|
+
channel,
|
|
18141
|
+
...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
|
|
18142
|
+
...options.now ? { now: options.now } : {}
|
|
18143
|
+
}),
|
|
18144
|
+
events: channel,
|
|
18145
|
+
hasAccess: options.imageProviderHasAccess ?? flowImageProviderHasAccess,
|
|
18146
|
+
...options.now ? { now: options.now } : {}
|
|
18147
|
+
}) : void 0;
|
|
18148
|
+
const port = { elements, context, runs, derivation, imageBatchHistory };
|
|
17543
18149
|
if (imageLibrary) port.imageLibrary = imageLibrary;
|
|
18150
|
+
if (imageBatchWrite) port.imageBatchWrite = imageBatchWrite;
|
|
18151
|
+
if (imageGeneration) port.imageGeneration = imageGeneration;
|
|
17544
18152
|
return port;
|
|
17545
18153
|
}
|
|
17546
18154
|
|
|
@@ -17650,8 +18258,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
17650
18258
|
const szenenPort = (0, import_react47.useMemo)(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
|
|
17651
18259
|
generateText: onGeneratePrompt,
|
|
17652
18260
|
token: hfToken,
|
|
17653
|
-
namespace: effectiveNamespace
|
|
17654
|
-
|
|
18261
|
+
namespace: effectiveNamespace,
|
|
18262
|
+
generateImage: onGenerateImage
|
|
18263
|
+
}) : null, [hfToken, effectiveNamespace, onGeneratePrompt, onGenerateImage]);
|
|
17655
18264
|
const {
|
|
17656
18265
|
state: hfState,
|
|
17657
18266
|
isLoading: isHfRefreshing,
|