@rslsp1/fa-app-tools 2.0.81 → 2.0.83

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.
@@ -95,13 +95,13 @@ function noticeFor(ergebnis, was) {
95
95
  };
96
96
  }
97
97
  case "rejected": {
98
- const fehler3 = ergebnis.errors ?? [];
98
+ const fehler4 = ergebnis.errors ?? [];
99
99
  const ungeprueft = ergebnis.unchecked ?? [];
100
100
  return {
101
101
  tone: "rejected",
102
- text: `${was}: abgelehnt \u2014 ${fehler3.length} Befund${fehler3.length === 1 ? "" : "e"}.`,
102
+ text: `${was}: abgelehnt \u2014 ${fehler4.length} Befund${fehler4.length === 1 ? "" : "e"}.`,
103
103
  details: [
104
- ...fehler3.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
104
+ ...fehler4.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
105
105
  // Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
106
106
  // sondern ungeprüft.
107
107
  ...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
@@ -4422,7 +4422,30 @@ var SZENEN_EVENT_TYPES = {
4422
4422
  * ebenfalls keine Bilddaten — nur Status, Zaehlung und die Item-Kennung,
4423
4423
  * falls eine entstand.
4424
4424
  */
4425
- imageGenerationFinished: "szenen.image_generation_finished"
4425
+ imageGenerationFinished: "szenen.image_generation_finished",
4426
+ /**
4427
+ * Ein Bild-Batch ist kanonisch gespeichert — Stufe 6.9.2, Gate D
4428
+ * (D-6.9.2-6).
4429
+ *
4430
+ * Erscheint erst, **nachdem** jedes Bild des Batches im Bildbestand liegt
4431
+ * (`hfUploadImage` + `image_added`, dieselbe Reihenfolge wie
4432
+ * `hfImageLibrary.putImage`) — kein Bild gilt als vorhanden, bevor sein
4433
+ * Upload bestätigt ist (R3). Trägt bewusst **keine Bilddaten**, nur den
4434
+ * vollständigen Metadatennachweis, den `listBatchesForPromptRun()` /
4435
+ * `getBatch()` ohne MySQL rekonstruieren können müssen: `item_id`,
4436
+ * `prompt_run_id`, `generation_batch_id`, `source_kind`, je Bild
4437
+ * `position`, `media_ref`, `asset_id`, `model`, `seed`, `width`/`height`.
4438
+ *
4439
+ * Additiv wie jedes Ereignis dieser Domain: ein zweiter Batch desselben
4440
+ * Laufs ist ein **weiteres** Ereignis mit derselben `item_id`, nicht eine
4441
+ * Änderung am ersten (D-113/D-121 — ein Lauf bleibt ein Item). Gilt für
4442
+ * `generated_in_app` **und** `external_import` — nur `source_kind`
4443
+ * unterscheidet die beiden Schreiber (D-116/D-117/D-120).
4444
+ *
4445
+ * Der Elementreducer ignoriert auch diesen Typ (`default:`-Zweig); gelesen
4446
+ * wird mit `collectImageBatches` (`imageBatchStore.ts`).
4447
+ */
4448
+ imageBatchPersisted: "szenen.image_batch_persisted"
4426
4449
  };
4427
4450
  function sortForReplay(events) {
4428
4451
  return [...events].sort((a, b) => {
@@ -6132,7 +6155,7 @@ var ERKLAERUNG = {
6132
6155
  };
6133
6156
  var CapabilityPanel = ({ port, elementId, setupPayload }) => {
6134
6157
  const [bericht, setBericht] = useState4(null);
6135
- const [fehler3, setFehler] = useState4(null);
6158
+ const [fehler4, setFehler] = useState4(null);
6136
6159
  useEffect2(() => {
6137
6160
  let abgemeldet = false;
6138
6161
  setBericht(null);
@@ -6154,10 +6177,10 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
6154
6177
  abgemeldet = true;
6155
6178
  };
6156
6179
  }, [port, elementId, setupPayload]);
6157
- if (fehler3) {
6180
+ if (fehler4) {
6158
6181
  return /* @__PURE__ */ jsxs3("p", { "data-testid": "capability-fehler", role: "alert", children: [
6159
6182
  "F\xE4higkeitsbericht nicht erstellbar: ",
6160
- fehler3
6183
+ fehler4
6161
6184
  ] });
6162
6185
  }
6163
6186
  if (!bericht) return /* @__PURE__ */ jsx3("p", { "data-testid": "capability-laden", children: "F\xE4higkeitsbericht wird erstellt \u2026" });
@@ -6659,10 +6682,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
6659
6682
  setNotiz(
6660
6683
  ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
6661
6684
  );
6662
- } catch (fehler3) {
6685
+ } catch (fehler4) {
6663
6686
  setNotiz({
6664
6687
  tone: "error",
6665
- text: `Externe Bilder importieren: ${fehler3 instanceof Error ? fehler3.message : String(fehler3)}`
6688
+ text: `Externe Bilder importieren: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
6666
6689
  });
6667
6690
  }
6668
6691
  setLaeuft(false);
@@ -11138,6 +11161,532 @@ function createHfImageLibrary(options) {
11138
11161
  };
11139
11162
  }
11140
11163
 
11164
+ // src/lib/szenen/flowImageProvider.ts
11165
+ var FLOW_MODEL_DISPLAY_NAMES = {
11166
+ "imagen-4.0-generate-001": "Imagen 4"
11167
+ };
11168
+ function flowImageProviderHasAccess(model) {
11169
+ return model.callable_model_id in FLOW_MODEL_DISPLAY_NAMES;
11170
+ }
11171
+ function istNichtleererText(wert) {
11172
+ return typeof wert === "string" && wert.trim().length > 0;
11173
+ }
11174
+ function createFlowImageProvider(generateImage) {
11175
+ return {
11176
+ async generate(input) {
11177
+ const modelDisplayName = FLOW_MODEL_DISPLAY_NAMES[input.callableModelId];
11178
+ if (!modelDisplayName) {
11179
+ throw new Error(
11180
+ `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).`
11181
+ );
11182
+ }
11183
+ if (istNichtleererText(input.negativePrompt)) {
11184
+ throw new Error(
11185
+ "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)."
11186
+ );
11187
+ }
11188
+ const erzeugt = [];
11189
+ let ersterFehler = null;
11190
+ for (let position = 0; position < input.imageCount; position++) {
11191
+ try {
11192
+ const antwort = await generateImage({
11193
+ prompt: input.positivePrompt,
11194
+ modelDisplayName,
11195
+ ...input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}
11196
+ });
11197
+ if (!antwort || typeof antwort.base64 !== "string" || antwort.base64.length === 0) {
11198
+ throw new Error("Flow.generate.image hat kein Bild geliefert.");
11199
+ }
11200
+ erzeugt.push({
11201
+ imageBase64: antwort.base64,
11202
+ // Das tatsaechlich gerufene Modell, nicht der Flow-Display-Name —
11203
+ // im Bestand soll stehen, was bezahlt wurde (wie reactImageProvider.ts).
11204
+ model: input.callableModelId
11205
+ // Kein Seed: weder als Eingabe dokumentiert noch als Ausgabe
11206
+ // geliefert — nichts wird erfunden (D-107).
11207
+ });
11208
+ } catch (fehler4) {
11209
+ ersterFehler = fehler4 instanceof Error ? fehler4.message : String(fehler4);
11210
+ break;
11211
+ }
11212
+ }
11213
+ if (erzeugt.length === 0) {
11214
+ throw new Error(
11215
+ ersterFehler ?? `Flow.generate.image hat zu ${input.callableModelId} kein verwertbares Bild geliefert.`
11216
+ );
11217
+ }
11218
+ return { images: erzeugt };
11219
+ }
11220
+ };
11221
+ }
11222
+
11223
+ // src/lib/szenen/imageGenerationOrderStore.ts
11224
+ function istText3(wert) {
11225
+ return typeof wert === "string" && wert.trim().length > 0;
11226
+ }
11227
+ function collectImageGenerationOrders(events) {
11228
+ const byBatchId = /* @__PURE__ */ new Map();
11229
+ for (const event of events) {
11230
+ if (event.type !== SZENEN_EVENT_TYPES.imageGenerationStarted) continue;
11231
+ const payload = event.payload;
11232
+ if (!payload || !istText3(payload.generation_batch_id) || !istText3(payload.prompt_run_id)) continue;
11233
+ if (byBatchId.has(payload.generation_batch_id)) continue;
11234
+ byBatchId.set(payload.generation_batch_id, {
11235
+ generation_batch_id: payload.generation_batch_id,
11236
+ prompt_run_id: payload.prompt_run_id,
11237
+ source_kind: "generated_in_app",
11238
+ request: payload.request,
11239
+ created_at: payload.created_at ?? "",
11240
+ status: "started"
11241
+ });
11242
+ }
11243
+ for (const event of events) {
11244
+ if (event.type !== SZENEN_EVENT_TYPES.imageGenerationFinished) continue;
11245
+ const payload = event.payload;
11246
+ if (!payload || !istText3(payload.generation_batch_id)) continue;
11247
+ const auftrag = byBatchId.get(payload.generation_batch_id);
11248
+ if (!auftrag) continue;
11249
+ if (auftrag.status !== "started") continue;
11250
+ if (payload.status !== "succeeded" && payload.status !== "partial" && payload.status !== "failed") continue;
11251
+ byBatchId.set(payload.generation_batch_id, {
11252
+ ...auftrag,
11253
+ status: payload.status,
11254
+ ...payload.item_id ? { item_id: payload.item_id } : {},
11255
+ produced_count: payload.produced_count ?? 0,
11256
+ ...payload.diagnostic ? { diagnostic: payload.diagnostic } : {},
11257
+ finished_at: payload.finished_at ?? ""
11258
+ });
11259
+ }
11260
+ return [...byBatchId.values()];
11261
+ }
11262
+ function ordersForPromptRun(events, runId) {
11263
+ return collectImageGenerationOrders(events).filter((o) => o.prompt_run_id === runId);
11264
+ }
11265
+ function findImageGenerationOrder(events, batchId) {
11266
+ return collectImageGenerationOrders(events).find((o) => o.generation_batch_id === batchId) ?? null;
11267
+ }
11268
+
11269
+ // src/lib/szenen/imageBatchStore.ts
11270
+ var istText4 = (wert) => typeof wert === "string" && wert.trim().length > 0;
11271
+ function alsBilder(rohe) {
11272
+ if (!Array.isArray(rohe)) return [];
11273
+ return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText4(b.media_ref) && typeof b.position === "number").map((b) => ({
11274
+ media_ref: b.media_ref,
11275
+ position: b.position,
11276
+ ...istText4(b.asset_id) ? { asset_id: b.asset_id } : {},
11277
+ model: b.model ?? null,
11278
+ seed: b.seed ?? null,
11279
+ width: b.width ?? null,
11280
+ height: b.height ?? null
11281
+ }));
11282
+ }
11283
+ function collectImageBatches(events) {
11284
+ const byItemId = /* @__PURE__ */ new Map();
11285
+ for (const event of events) {
11286
+ if (event.type !== SZENEN_EVENT_TYPES.imageBatchPersisted) continue;
11287
+ const payload = event.payload;
11288
+ if (!payload || !istText4(payload.item_id) || !istText4(payload.prompt_run_id) || !istText4(payload.generation_batch_id)) continue;
11289
+ if (payload.source_kind !== "generated_in_app" && payload.source_kind !== "external_import") continue;
11290
+ const bestehend = byItemId.get(payload.item_id);
11291
+ if (bestehend?.generation_batch_ids.includes(payload.generation_batch_id)) continue;
11292
+ const bilder = alsBilder(payload.images);
11293
+ if (!bestehend) {
11294
+ byItemId.set(payload.item_id, {
11295
+ item_id: payload.item_id,
11296
+ prompt_run_id: payload.prompt_run_id,
11297
+ generation_batch_ids: [payload.generation_batch_id],
11298
+ source_kind: payload.source_kind,
11299
+ title: istText4(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
11300
+ images: bilder
11301
+ });
11302
+ continue;
11303
+ }
11304
+ byItemId.set(payload.item_id, {
11305
+ ...bestehend,
11306
+ generation_batch_ids: [...bestehend.generation_batch_ids, payload.generation_batch_id],
11307
+ images: [...bestehend.images, ...bilder]
11308
+ });
11309
+ }
11310
+ return [...byItemId.values()];
11311
+ }
11312
+ function batchesForPromptRun(events, runId) {
11313
+ return collectImageBatches(events).filter((b) => b.prompt_run_id === runId);
11314
+ }
11315
+ function findImageBatch(events, batchId) {
11316
+ return collectImageBatches(events).find((b) => b.generation_batch_ids.includes(batchId)) ?? null;
11317
+ }
11318
+
11319
+ // src/lib/szenen/hfImageBatchHistory.ts
11320
+ function lesefehler(aktion, error) {
11321
+ return {
11322
+ kind: "error",
11323
+ message: `${aktion}: ${error instanceof Error ? error.message : String(error)}`,
11324
+ retryable: true
11325
+ };
11326
+ }
11327
+ function createHfImageBatchHistory(options) {
11328
+ const { channel } = options;
11329
+ return {
11330
+ async listBatchesForPromptRun(runId) {
11331
+ try {
11332
+ const events = await channel.readEvents();
11333
+ return { kind: "ok", value: batchesForPromptRun(events, runId) };
11334
+ } catch (error) {
11335
+ return lesefehler(`Batches fuer Lauf ${runId} konnten nicht gelesen werden`, error);
11336
+ }
11337
+ },
11338
+ async getBatch(batchId) {
11339
+ try {
11340
+ const events = await channel.readEvents();
11341
+ return { kind: "ok", value: findImageBatch(events, batchId) };
11342
+ } catch (error) {
11343
+ return lesefehler(`Batch ${batchId} konnte nicht gelesen werden`, error);
11344
+ }
11345
+ },
11346
+ async listOrdersForPromptRun(runId) {
11347
+ try {
11348
+ const events = await channel.readEvents();
11349
+ return { kind: "ok", value: ordersForPromptRun(events, runId) };
11350
+ } catch (error) {
11351
+ return lesefehler(`Bildgenerierungsauftraege fuer Lauf ${runId} konnten nicht gelesen werden`, error);
11352
+ }
11353
+ }
11354
+ };
11355
+ }
11356
+
11357
+ // src/lib/szenen/hfImageBatchPersistence.ts
11358
+ function fehler3(nachricht, retryable = true) {
11359
+ return { kind: "error", message: nachricht, retryable };
11360
+ }
11361
+ async function legeBatchAb(options, input) {
11362
+ const { namespace, token, channel } = options;
11363
+ const newImageId = options.newImageId ?? ((batchId, position) => `${batchId}-${position}`);
11364
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
11365
+ const itemId = itemIdForRun(input.runId);
11366
+ let vorhandeneEvents;
11367
+ try {
11368
+ vorhandeneEvents = await channel.readEvents();
11369
+ } catch (error) {
11370
+ return fehler3(
11371
+ `Bild-Batches von Lauf ${input.runId} konnten vor dem Ablegen nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`
11372
+ );
11373
+ }
11374
+ const bestehend = batchesForPromptRun(vorhandeneEvents, input.runId)[0] ?? null;
11375
+ if (bestehend?.generation_batch_ids.includes(input.generationBatchId)) {
11376
+ return { kind: "ok", value: bestehend };
11377
+ }
11378
+ const startPosition = bestehend?.images.length ?? 0;
11379
+ const abgelegt = [];
11380
+ for (let offset = 0; offset < input.images.length; offset++) {
11381
+ const bild = input.images[offset];
11382
+ const id = newImageId(input.generationBatchId, startPosition + offset);
11383
+ try {
11384
+ await hfUploadImage(bild.imageBase64, id, token, bild.mimeType);
11385
+ await writeHFEvent(namespace, token, "image_added", {
11386
+ id,
11387
+ prompt: input.title ?? `Batch ${input.generationBatchId}`,
11388
+ tags: [],
11389
+ timestamp: now().getTime(),
11390
+ mimeType: bild.mimeType
11391
+ }, []);
11392
+ } catch (error) {
11393
+ return fehler3(
11394
+ `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.`
11395
+ );
11396
+ }
11397
+ abgelegt.push({
11398
+ position: startPosition + offset,
11399
+ media_ref: id,
11400
+ asset_id: id,
11401
+ model: bild.model ?? null,
11402
+ seed: bild.seed ?? null,
11403
+ width: bild.width ?? null,
11404
+ height: bild.height ?? null
11405
+ });
11406
+ }
11407
+ const titel = bestehend?.title ?? (input.title?.trim() || `Batch aus Lauf ${input.runId}`);
11408
+ const payload = {
11409
+ item_id: itemId,
11410
+ prompt_run_id: input.runId,
11411
+ generation_batch_id: input.generationBatchId,
11412
+ source_kind: input.sourceKind,
11413
+ title: titel,
11414
+ images: abgelegt,
11415
+ created_at: now().toISOString()
11416
+ };
11417
+ try {
11418
+ await channel.appendEvent(SZENEN_EVENT_TYPES.imageBatchPersisted, payload);
11419
+ } catch (error) {
11420
+ return fehler3(
11421
+ `Die Batch-Metadaten fuer ${input.generationBatchId} konnten nicht protokolliert werden, obwohl die Bilder bereits im Bestand liegen: ${error instanceof Error ? error.message : String(error)}`
11422
+ );
11423
+ }
11424
+ const zusammengefuehrt = {
11425
+ item_id: itemId,
11426
+ prompt_run_id: input.runId,
11427
+ generation_batch_ids: bestehend ? [...bestehend.generation_batch_ids, input.generationBatchId] : [input.generationBatchId],
11428
+ source_kind: bestehend?.source_kind ?? input.sourceKind,
11429
+ title: titel,
11430
+ images: [...bestehend?.images ?? [], ...abgelegt]
11431
+ };
11432
+ return { kind: "ok", value: zusammengefuehrt };
11433
+ }
11434
+ function createHfImageGenerationBatchPersistence(options) {
11435
+ return {
11436
+ async persist(input) {
11437
+ return legeBatchAb(options, {
11438
+ runId: input.runId,
11439
+ generationBatchId: input.generationBatchId,
11440
+ sourceKind: "generated_in_app",
11441
+ images: input.images.map((bild) => ({
11442
+ imageBase64: bild.imageBase64,
11443
+ // Flow liefert PNG-Base64 (bestehende Konvention, s. AvatarArchitectApp.tsx
11444
+ // `data:image/png;base64,${base64}`); der gemeinsame Vertrag führt
11445
+ // keinen MIME-Typ je Bild.
11446
+ mimeType: "image/png",
11447
+ model: bild.model ?? null,
11448
+ seed: bild.seed ?? null,
11449
+ width: bild.width ?? null,
11450
+ height: bild.height ?? null
11451
+ }))
11452
+ });
11453
+ }
11454
+ };
11455
+ }
11456
+ function createHfImageBatchWrite(options) {
11457
+ return {
11458
+ async importBatch(input) {
11459
+ return legeBatchAb(options, {
11460
+ runId: input.runId,
11461
+ generationBatchId: input.generationBatchId,
11462
+ sourceKind: "external_import",
11463
+ title: input.title,
11464
+ images: input.images.map((bild) => ({
11465
+ imageBase64: bild.imageBase64,
11466
+ mimeType: "image/png",
11467
+ model: bild.model ?? null,
11468
+ seed: bild.seed ?? null,
11469
+ width: bild.width ?? null,
11470
+ height: bild.height ?? null
11471
+ }))
11472
+ });
11473
+ },
11474
+ async attachLibraryItem(input) {
11475
+ let events;
11476
+ try {
11477
+ events = await options.channel.readEvents();
11478
+ } catch (error) {
11479
+ return {
11480
+ kind: "error",
11481
+ message: `Batches konnten nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`,
11482
+ retryable: true
11483
+ };
11484
+ }
11485
+ const item = collectImageBatches(events).find((b) => b.item_id === input.itemId);
11486
+ if (!item) {
11487
+ return { kind: "error", message: `Item ${input.itemId} ist unbekannt.`, retryable: false };
11488
+ }
11489
+ if (item.prompt_run_id !== input.runId) {
11490
+ return {
11491
+ kind: "conflict",
11492
+ message: `Item ${input.itemId} geh\xF6rt bereits zu Lauf ${item.prompt_run_id}.`,
11493
+ existingPromptRunId: item.prompt_run_id
11494
+ };
11495
+ }
11496
+ return { kind: "ok", value: item };
11497
+ }
11498
+ };
11499
+ }
11500
+
11501
+ // src/lib/szenen/imageGenerationCapability.ts
11502
+ var isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
11503
+ var IMAGE_MODEL_CATALOGUE = {
11504
+ imagen_4: {
11505
+ callable_model_id: "imagen-4.0-generate-001",
11506
+ provider: "google",
11507
+ max_images: 4,
11508
+ aspect_ratios: ["1:1", "3:4", "4:3", "9:16", "16:9"],
11509
+ supports_seed: true
11510
+ },
11511
+ imagen_3: {
11512
+ callable_model_id: "imagen-3.0-generate-001",
11513
+ provider: "google",
11514
+ max_images: 4,
11515
+ aspect_ratios: ["1:1", "3:4", "4:3", "9:16", "16:9"],
11516
+ supports_seed: true
11517
+ }
11518
+ };
11519
+ function callableImageModelFor(targetModelKey) {
11520
+ return IMAGE_MODEL_CATALOGUE[targetModelKey] ?? null;
11521
+ }
11522
+ function targetModelKeyOf(targetModel) {
11523
+ if (!isObject9(targetModel)) return null;
11524
+ return typeof targetModel.key === "string" && targetModel.key.length > 0 ? targetModel.key : null;
11525
+ }
11526
+ function capabilityForTargetModel(targetModel, hasAccess) {
11527
+ const key = targetModelKeyOf(targetModel);
11528
+ if (!key) {
11529
+ return {
11530
+ kind: "unsupported",
11531
+ target_model_key: "(unbekannt)",
11532
+ reason: 'Der eingefrorene Lauf tr\xE4gt kein "target_model.key" \u2014 es gibt nichts zuzuordnen.'
11533
+ };
11534
+ }
11535
+ const modell = callableImageModelFor(key);
11536
+ if (!modell) {
11537
+ return {
11538
+ kind: "unsupported",
11539
+ target_model_key: key,
11540
+ reason: `F\xFCr das Zielmodell "${key}" ist in diesem Host kein Bildpfad katalogisiert.`
11541
+ };
11542
+ }
11543
+ if (!hasAccess(modell)) {
11544
+ return {
11545
+ kind: "unsupported",
11546
+ target_model_key: key,
11547
+ reason: `Dieser Host hat keinen Zugang zu ${modell.callable_model_id} (${modell.provider}).`
11548
+ };
11549
+ }
11550
+ return {
11551
+ kind: "supported",
11552
+ target_model_key: key,
11553
+ provider: modell.provider,
11554
+ callable_model_id: modell.callable_model_id,
11555
+ max_images: modell.max_images,
11556
+ aspect_ratios: modell.aspect_ratios,
11557
+ supports_seed: modell.supports_seed
11558
+ };
11559
+ }
11560
+
11561
+ // src/lib/szenen/imageGenerationPort.ts
11562
+ function queryError2(error, action) {
11563
+ return {
11564
+ kind: "error",
11565
+ message: `${action}: ${error instanceof Error ? error.message : String(error)}`,
11566
+ retryable: true
11567
+ };
11568
+ }
11569
+ function createImageGenerationPort(options) {
11570
+ const { runs, provider, batches, events, hasAccess } = options;
11571
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
11572
+ async function readRun(runId) {
11573
+ try {
11574
+ return await runs.getRun(runId);
11575
+ } catch (error) {
11576
+ return queryError2(error, `Lauf ${runId} konnte nicht gelesen werden`);
11577
+ }
11578
+ }
11579
+ return {
11580
+ async capabilityForRun(runId) {
11581
+ const gelesen = await readRun(runId);
11582
+ if (gelesen.kind !== "ok") return gelesen;
11583
+ if (!gelesen.value) {
11584
+ return { kind: "error", message: `Lauf ${runId} ist unbekannt.`, retryable: false };
11585
+ }
11586
+ const targetModel = gelesen.value.input_payload?.target_model;
11587
+ return { kind: "ok", value: capabilityForTargetModel(targetModel, hasAccess) };
11588
+ },
11589
+ async startFromPromptRun(input) {
11590
+ const gelesen = await readRun(input.runId);
11591
+ if (gelesen.kind !== "ok") return { kind: "rejected", message: gelesen.message };
11592
+ if (!gelesen.value) {
11593
+ return { kind: "rejected", message: `Lauf ${input.runId} ist unbekannt.` };
11594
+ }
11595
+ const run = gelesen.value;
11596
+ if (run.status !== "succeeded" || !run.output) {
11597
+ return {
11598
+ kind: "rejected",
11599
+ message: `Lauf ${input.runId} ist nicht erfolgreich \u2014 ein Bildauftrag braucht einen eingefrorenen Prompt (D-103).`
11600
+ };
11601
+ }
11602
+ const targetModel = run.input_payload?.target_model;
11603
+ const capability = capabilityForTargetModel(targetModel, hasAccess);
11604
+ if (capability.kind !== "supported") {
11605
+ return { kind: "unsupported", targetModelKey: capability.target_model_key, reason: capability.reason };
11606
+ }
11607
+ const startedPayload = {
11608
+ generation_batch_id: input.generationBatchId,
11609
+ prompt_run_id: input.runId,
11610
+ source_kind: "generated_in_app",
11611
+ request: {
11612
+ image_count: input.imageCount,
11613
+ ...input.aspectRatio ? { aspect_ratio: input.aspectRatio } : {},
11614
+ ...input.seeds ? { seeds: input.seeds } : {},
11615
+ callable_model_id: capability.callable_model_id
11616
+ },
11617
+ created_at: now().toISOString()
11618
+ };
11619
+ try {
11620
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationStarted, startedPayload);
11621
+ } catch (error) {
11622
+ return {
11623
+ kind: "failed",
11624
+ message: `Der Auftrag ${input.generationBatchId} konnte nicht protokolliert werden: ${error instanceof Error ? error.message : String(error)}`
11625
+ };
11626
+ }
11627
+ let produced = [];
11628
+ let providerFehler = null;
11629
+ try {
11630
+ const antwort = await provider.generate({
11631
+ callableModelId: capability.callable_model_id,
11632
+ provider: capability.provider,
11633
+ positivePrompt: run.output.positive,
11634
+ negativePrompt: run.output.negative,
11635
+ imageCount: input.imageCount,
11636
+ ...input.aspectRatio ? { aspectRatio: input.aspectRatio } : {},
11637
+ ...input.seeds ? { seeds: input.seeds } : {}
11638
+ });
11639
+ produced = antwort.images;
11640
+ } catch (error) {
11641
+ providerFehler = error instanceof Error ? error.message : String(error);
11642
+ }
11643
+ if (providerFehler || produced.length === 0) {
11644
+ const finishedPayload2 = {
11645
+ generation_batch_id: input.generationBatchId,
11646
+ status: "failed",
11647
+ produced_count: 0,
11648
+ diagnostic: providerFehler ?? "Der Provider hat kein verwertbares Bild geliefert.",
11649
+ finished_at: now().toISOString()
11650
+ };
11651
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload2).catch(() => {
11652
+ });
11653
+ return {
11654
+ kind: "failed",
11655
+ message: finishedPayload2.diagnostic
11656
+ };
11657
+ }
11658
+ const persistiert = await batches.persist({
11659
+ runId: input.runId,
11660
+ generationBatchId: input.generationBatchId,
11661
+ images: produced
11662
+ });
11663
+ if (persistiert.kind !== "ok") {
11664
+ const finishedPayload2 = {
11665
+ generation_batch_id: input.generationBatchId,
11666
+ status: "failed",
11667
+ produced_count: 0,
11668
+ diagnostic: persistiert.kind === "error" ? persistiert.message : `Batch ${input.generationBatchId} konnte nicht gespeichert werden (${persistiert.kind}).`,
11669
+ finished_at: now().toISOString()
11670
+ };
11671
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload2).catch(() => {
11672
+ });
11673
+ return { kind: "failed", message: finishedPayload2.diagnostic };
11674
+ }
11675
+ const istPartial = produced.length < input.imageCount;
11676
+ const finishedPayload = {
11677
+ generation_batch_id: input.generationBatchId,
11678
+ status: istPartial ? "partial" : "succeeded",
11679
+ item_id: persistiert.value.item_id,
11680
+ produced_count: produced.length,
11681
+ finished_at: now().toISOString()
11682
+ };
11683
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload).catch(() => {
11684
+ });
11685
+ return istPartial ? { kind: "partial", batch: persistiert.value, requested: input.imageCount, produced: produced.length } : { kind: "succeeded", batch: persistiert.value };
11686
+ }
11687
+ };
11688
+ }
11689
+
11141
11690
  // src/lib/szenen/flowViewPort.ts
11142
11691
  var defaultContext = {
11143
11692
  async chainFor(contextId) {
@@ -11185,8 +11734,36 @@ function createFlowSzenenViewPort(options) {
11185
11734
  ...options.newRunId ? { newRunId: options.newRunId } : {},
11186
11735
  ...options.now ? { now: options.now } : {}
11187
11736
  });
11188
- const port = { elements, context, runs, derivation };
11737
+ const imageBatchHistory = createHfImageBatchHistory({ channel });
11738
+ const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
11739
+ const imageBatchWrite = hatHfZugangsdaten ? createHfImageBatchWrite({
11740
+ namespace: options.namespace,
11741
+ token: options.token,
11742
+ channel,
11743
+ ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
11744
+ ...options.now ? { now: options.now } : {}
11745
+ }) : void 0;
11746
+ const bildProvider = options.imageProvider ?? (options.generateImage ? createFlowImageProvider(options.generateImage) : void 0);
11747
+ const imageGeneration = bildProvider && imageBatchWrite ? createImageGenerationPort({
11748
+ runs: { async getRun(runId) {
11749
+ return runs.getRun(runId);
11750
+ } },
11751
+ provider: bildProvider,
11752
+ batches: createHfImageGenerationBatchPersistence({
11753
+ namespace: options.namespace,
11754
+ token: options.token,
11755
+ channel,
11756
+ ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
11757
+ ...options.now ? { now: options.now } : {}
11758
+ }),
11759
+ events: channel,
11760
+ hasAccess: options.imageProviderHasAccess ?? flowImageProviderHasAccess,
11761
+ ...options.now ? { now: options.now } : {}
11762
+ }) : void 0;
11763
+ const port = { elements, context, runs, derivation, imageBatchHistory };
11189
11764
  if (imageLibrary) port.imageLibrary = imageLibrary;
11765
+ if (imageBatchWrite) port.imageBatchWrite = imageBatchWrite;
11766
+ if (imageGeneration) port.imageGeneration = imageGeneration;
11190
11767
  return port;
11191
11768
  }
11192
11769
 
@@ -11306,5 +11883,20 @@ export {
11306
11883
  createEventLogRunPersistence,
11307
11884
  createClientRunPort,
11308
11885
  createHfImageLibrary,
11886
+ FLOW_MODEL_DISPLAY_NAMES,
11887
+ flowImageProviderHasAccess,
11888
+ createFlowImageProvider,
11889
+ collectImageGenerationOrders,
11890
+ ordersForPromptRun,
11891
+ findImageGenerationOrder,
11892
+ collectImageBatches,
11893
+ batchesForPromptRun,
11894
+ findImageBatch,
11895
+ createHfImageBatchHistory,
11896
+ createHfImageGenerationBatchPersistence,
11897
+ createHfImageBatchWrite,
11898
+ callableImageModelFor,
11899
+ capabilityForTargetModel,
11900
+ createImageGenerationPort,
11309
11901
  createFlowSzenenViewPort
11310
11902
  };
package/dist/index.d.mts CHANGED
@@ -6,7 +6,7 @@ import { Node, Edge } from '@xyflow/react';
6
6
  import * as zustand_vanilla from 'zustand/vanilla';
7
7
  import { F as FeatureSliceId, b as SliceContract, a as PersistenceDescriptor, W as WorkspacePersistence, P as PersistedSlice, L as LoadResult, f as WriteResult, e as WorkspaceTabId, C as CommandResult, R as RequestContext, Q as QueryResult } from './contracts-DdYOMBl9.mjs';
8
8
 
9
- var version = "2.0.81";
9
+ var version = "2.0.83";
10
10
 
11
11
  declare function useOnClickOutside(ref: RefObject<HTMLElement | null>, handler: (e: MouseEvent | TouchEvent) => void): void;
12
12
 
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ import { Node, Edge } from '@xyflow/react';
6
6
  import * as zustand_vanilla from 'zustand/vanilla';
7
7
  import { F as FeatureSliceId, b as SliceContract, a as PersistenceDescriptor, W as WorkspacePersistence, P as PersistedSlice, L as LoadResult, f as WriteResult, e as WorkspaceTabId, C as CommandResult, R as RequestContext, Q as QueryResult } from './contracts-DdYOMBl9.js';
8
8
 
9
- var version = "2.0.81";
9
+ var version = "2.0.83";
10
10
 
11
11
  declare function useOnClickOutside(ref: RefObject<HTMLElement | null>, handler: (e: MouseEvent | TouchEvent) => void): void;
12
12