@rslsp1/fa-app-tools 7.0.1 → 7.4.0
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-B2YV4TNI.mjs → chunk-HUGUH4VB.mjs} +704 -46
- package/dist/index.d.mts +26 -4
- package/dist/index.d.ts +26 -4
- package/dist/index.js +654 -101
- package/dist/index.mjs +25 -55
- package/dist/szenen.d.mts +894 -301
- package/dist/szenen.d.ts +894 -301
- package/dist/szenen.js +879 -70
- package/dist/szenen.mjs +162 -3
- package/package.json +6 -2
|
@@ -5,6 +5,85 @@ import {
|
|
|
5
5
|
resolvePreferredHead
|
|
6
6
|
} from "./chunk-FUY2OCVC.mjs";
|
|
7
7
|
|
|
8
|
+
// src/lib/szenen/szenenSnapshot.ts
|
|
9
|
+
var SZENEN_SNAPSHOT_SCHEMA_VERSION = 1;
|
|
10
|
+
var SZENEN_SNAPSHOT_PREFIX = "szenen-state-";
|
|
11
|
+
var SZENEN_SNAPSHOT_FILE = "szenen-state.json";
|
|
12
|
+
function szenenSnapshotFilename(maxTs) {
|
|
13
|
+
const stamp = new Date(maxTs).toISOString().replace(/:/g, "-").replace(".", "-");
|
|
14
|
+
return `${SZENEN_SNAPSHOT_PREFIX}${stamp}.json`;
|
|
15
|
+
}
|
|
16
|
+
function istSzenenSnapshotDatei(name) {
|
|
17
|
+
return name.startsWith(SZENEN_SNAPSHOT_PREFIX) && name.endsWith(".json");
|
|
18
|
+
}
|
|
19
|
+
var maxTsOf = (events) => {
|
|
20
|
+
let max = 0;
|
|
21
|
+
for (const event of events) if (event.ts > max) max = event.ts;
|
|
22
|
+
return max;
|
|
23
|
+
};
|
|
24
|
+
function buildSzenenSnapshot(base, neue, neuePfade = []) {
|
|
25
|
+
const events = [...base?.events ?? [], ...neue];
|
|
26
|
+
const paths = [...base?.consumed.paths ?? [], ...neuePfade.map(basisname)];
|
|
27
|
+
return {
|
|
28
|
+
schema_version: SZENEN_SNAPSHOT_SCHEMA_VERSION,
|
|
29
|
+
consumed: { count: events.length, max_ts: maxTsOf(events), paths },
|
|
30
|
+
events
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function basisname(pfad) {
|
|
34
|
+
return pfad.split("/").pop() ?? pfad;
|
|
35
|
+
}
|
|
36
|
+
function aufgenommeneDateien(snapshot) {
|
|
37
|
+
return new Set(snapshot ? snapshot.consumed.paths : []);
|
|
38
|
+
}
|
|
39
|
+
function parseSzenenSnapshot(raw, herkunft) {
|
|
40
|
+
if (!raw || typeof raw !== "object") {
|
|
41
|
+
throw new Error(`Szenen-Snapshot ${herkunft}: kein Objekt, sondern ${typeof raw}.`);
|
|
42
|
+
}
|
|
43
|
+
const obj = raw;
|
|
44
|
+
if (obj.schema_version !== SZENEN_SNAPSHOT_SCHEMA_VERSION) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Szenen-Snapshot ${herkunft}: schema_version ${String(obj.schema_version)} wird nicht gelesen, erwartet ${SZENEN_SNAPSHOT_SCHEMA_VERSION}.`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (!Array.isArray(obj.events)) {
|
|
50
|
+
throw new Error(`Szenen-Snapshot ${herkunft}: "events" fehlt oder ist kein Array.`);
|
|
51
|
+
}
|
|
52
|
+
const events = obj.events;
|
|
53
|
+
for (const [i, event] of events.entries()) {
|
|
54
|
+
if (!event || typeof event.type !== "string" || typeof event.ts !== "number") {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Szenen-Snapshot ${herkunft}: Ereignis ${i} hat keinen Typ oder keinen Zeitstempel.`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const consumed = obj.consumed;
|
|
61
|
+
if (!consumed || typeof consumed.count !== "number" || typeof consumed.max_ts !== "number") {
|
|
62
|
+
throw new Error(`Szenen-Snapshot ${herkunft}: "consumed" fehlt oder ist unvollstaendig.`);
|
|
63
|
+
}
|
|
64
|
+
if (!Array.isArray(consumed.paths)) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Szenen-Snapshot ${herkunft}: "consumed.paths" fehlt. Ohne die aufgenommenen Dateinamen laesst sich nicht entscheiden, welche Datei in events/ noch aussteht.`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (consumed.count !== events.length) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Szenen-Snapshot ${herkunft}: consumed.count ${consumed.count} passt nicht zu ${events.length} Ereignis(sen) in der Datei \u2014 die Datei ist unvollstaendig.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const echt = maxTsOf(events);
|
|
75
|
+
if (consumed.max_ts !== echt) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Szenen-Snapshot ${herkunft}: consumed.max_ts ${consumed.max_ts} passt nicht zum groessten Zeitstempel ${echt} in der Datei.`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return { schema_version: SZENEN_SNAPSHOT_SCHEMA_VERSION, consumed, events };
|
|
81
|
+
}
|
|
82
|
+
function mergeSnapshotAndPending(snapshot, pending, bereitsImSnapshot = 0) {
|
|
83
|
+
if (!snapshot) return { events: pending, bereitsImSnapshot };
|
|
84
|
+
return { events: [...snapshot.events, ...pending], bereitsImSnapshot };
|
|
85
|
+
}
|
|
86
|
+
|
|
8
87
|
// src/lib/hfEventTypes.ts
|
|
9
88
|
var HF_BASE = "https://huggingface.co";
|
|
10
89
|
var HF_REPO = "RolandSch/fa-app-state";
|
|
@@ -17,9 +96,9 @@ function bildEndungFuer(mimeType) {
|
|
|
17
96
|
}
|
|
18
97
|
|
|
19
98
|
// src/lib/hfEventLog.ts
|
|
20
|
-
function treeUrl(namespace, subdir = "") {
|
|
99
|
+
function treeUrl(namespace, subdir = "", revision = "main") {
|
|
21
100
|
const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
|
|
22
|
-
return `${HF_BASE}/api/datasets/${HF_REPO}/tree
|
|
101
|
+
return `${HF_BASE}/api/datasets/${HF_REPO}/tree/${encodeURIComponent(revision)}${parts ? "/" + parts : ""}`;
|
|
23
102
|
}
|
|
24
103
|
var DROSSEL_VERSUCHE = 4;
|
|
25
104
|
var DROSSEL_BASIS_MS = 400;
|
|
@@ -52,8 +131,8 @@ async function mitGeduld(anfrage) {
|
|
|
52
131
|
}
|
|
53
132
|
return letzte;
|
|
54
133
|
}
|
|
55
|
-
async function hfListDir(namespace, subdir, token) {
|
|
56
|
-
const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir), {
|
|
134
|
+
async function hfListDir(namespace, subdir, token, revision = "main") {
|
|
135
|
+
const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir, revision), {
|
|
57
136
|
headers: { Authorization: `Bearer ${token}` }
|
|
58
137
|
}));
|
|
59
138
|
if (res.status === 404) return [];
|
|
@@ -82,17 +161,46 @@ function toBase64(text) {
|
|
|
82
161
|
bytes.forEach((b) => binary += String.fromCharCode(b));
|
|
83
162
|
return btoa(binary);
|
|
84
163
|
}
|
|
85
|
-
|
|
164
|
+
var HfPreconditionFailedError = class extends Error {
|
|
165
|
+
constructor(message) {
|
|
166
|
+
super(message);
|
|
167
|
+
this.name = "HfPreconditionFailed";
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
async function hfRepoHead(token, revision = "main") {
|
|
171
|
+
const res = await mitGeduld(() => holeMitZeitlimit(
|
|
172
|
+
`${HF_BASE}/api/datasets/${HF_REPO}/revision/${encodeURIComponent(revision)}`,
|
|
173
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
174
|
+
));
|
|
175
|
+
if (!res.ok) {
|
|
176
|
+
throw new Error(`HF revision failed: ${res.status} ${res.statusText} (${revision})`);
|
|
177
|
+
}
|
|
178
|
+
const daten = await res.json();
|
|
179
|
+
if (typeof daten.sha !== "string" || daten.sha.length === 0) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`HF revision ${revision}: die Antwort traegt kein "sha". Erhaltene Felder: ${Object.keys(daten || {}).join(", ") || "(keine)"}.`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return daten.sha;
|
|
185
|
+
}
|
|
186
|
+
async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${repoPath}`, options) {
|
|
187
|
+
const kopf = { summary, description: "" };
|
|
188
|
+
if (options?.parentCommit) kopf.parentCommit = options.parentCommit;
|
|
86
189
|
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
87
190
|
method: "POST",
|
|
88
191
|
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
89
192
|
body: [
|
|
90
|
-
JSON.stringify({ key: "header", value:
|
|
193
|
+
JSON.stringify({ key: "header", value: kopf }),
|
|
91
194
|
JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: toBase64(content) } })
|
|
92
195
|
].join("\n")
|
|
93
196
|
});
|
|
94
197
|
if (!res.ok) {
|
|
95
198
|
const err = await res.text().catch(() => "");
|
|
199
|
+
if (options?.parentCommit && (res.status === 412 || res.status === 409)) {
|
|
200
|
+
throw new HfPreconditionFailedError(
|
|
201
|
+
`HF commit abgelehnt: Der Branch hat sich seit ${options.parentCommit} bewegt (${res.status}). Es wurde nichts geschrieben.`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
96
204
|
throw new Error(`HF upload failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
|
|
97
205
|
}
|
|
98
206
|
}
|
|
@@ -142,8 +250,8 @@ async function mitBegrenzterParallelitaet(count, limit, task) {
|
|
|
142
250
|
await Promise.all(Array.from({ length: Math.min(limit, count) }, worker));
|
|
143
251
|
if (fehlerAufgetreten) throw ersterFehler;
|
|
144
252
|
}
|
|
145
|
-
async function loadPendingEvents(namespace, token, sinceTs, knownPaths) {
|
|
146
|
-
const files = await hfListDir(namespace, "events", token);
|
|
253
|
+
async function loadPendingEvents(namespace, token, sinceTs, knownPaths, revision = "main") {
|
|
254
|
+
const files = await hfListDir(namespace, "events", token, revision);
|
|
147
255
|
const pending = files.filter((f) => f.type === "file" && tsFromEventPath(f.path) > sinceTs && !knownPaths?.has(f.path)).sort((a, b) => a.path.localeCompare(b.path));
|
|
148
256
|
if (!pending.length) return [];
|
|
149
257
|
const raw = new Array(pending.length);
|
|
@@ -153,6 +261,16 @@ async function loadPendingEvents(namespace, token, sinceTs, knownPaths) {
|
|
|
153
261
|
for (const f of pending) knownPaths?.add(f.path);
|
|
154
262
|
return raw.flatMap((e) => Array.isArray(e) ? e : [e]);
|
|
155
263
|
}
|
|
264
|
+
async function findLatestSzenenSnapshotPath(namespace, token) {
|
|
265
|
+
const files = await hfListDir(namespace, "", token);
|
|
266
|
+
const treffer = files.filter((f) => f.type === "file" && istSzenenSnapshotDatei(f.path.split("/").pop() || "")).map((f) => f.path).sort((a, b) => b.localeCompare(a));
|
|
267
|
+
return treffer[0] ?? null;
|
|
268
|
+
}
|
|
269
|
+
async function loadLatestSzenenSnapshot(namespace, token) {
|
|
270
|
+
const pfad = await findLatestSzenenSnapshotPath(namespace, token);
|
|
271
|
+
if (!pfad) return null;
|
|
272
|
+
return parseSzenenSnapshot(await hfDownloadJsonByPath(pfad, token), pfad);
|
|
273
|
+
}
|
|
156
274
|
var SESSION_CLIENT_ID = `client-${crypto.randomUUID().slice(0, 8)}`;
|
|
157
275
|
function getSessionClientId() {
|
|
158
276
|
return SESSION_CLIENT_ID;
|
|
@@ -178,7 +296,13 @@ async function writeHFEvent(namespace, token, type, payload, prevTs, options) {
|
|
|
178
296
|
const suffix = options?.ordered ? `${String(++writeSeq % 1e6).padStart(6, "0")}${uuid}` : uuid;
|
|
179
297
|
const filename = `${tsToFilename(ts)}_${suffix}.json`;
|
|
180
298
|
const repoPath = `${namespace}events/${filename}`;
|
|
181
|
-
await hfUploadSmallFile(
|
|
299
|
+
await hfUploadSmallFile(
|
|
300
|
+
repoPath,
|
|
301
|
+
JSON.stringify(event, null, 2),
|
|
302
|
+
token,
|
|
303
|
+
`Event: ${type}`,
|
|
304
|
+
options?.parentCommit ? { parentCommit: options.parentCommit } : void 0
|
|
305
|
+
);
|
|
182
306
|
return event;
|
|
183
307
|
}
|
|
184
308
|
|
|
@@ -318,7 +442,8 @@ function capabilitiesOf(port) {
|
|
|
318
442
|
imageBatchHistory: typeof port.imageBatchHistory?.listBatchesForPromptRun === "function",
|
|
319
443
|
imageOrderHistory: typeof port.imageBatchHistory?.listOrdersForPromptRun === "function",
|
|
320
444
|
imageBatchWrite: typeof port.imageBatchWrite?.importBatch === "function",
|
|
321
|
-
imageGeneration: typeof port.imageGeneration?.startFromPromptRun === "function"
|
|
445
|
+
imageGeneration: typeof port.imageGeneration?.startFromPromptRun === "function",
|
|
446
|
+
imageJobs: typeof port.imageJobs?.claimAndRun === "function"
|
|
322
447
|
};
|
|
323
448
|
}
|
|
324
449
|
function unsupportedReason(capability) {
|
|
@@ -3468,13 +3593,13 @@ function noticeFor(ergebnis, was) {
|
|
|
3468
3593
|
};
|
|
3469
3594
|
}
|
|
3470
3595
|
case "rejected": {
|
|
3471
|
-
const
|
|
3596
|
+
const fehler5 = ergebnis.errors ?? [];
|
|
3472
3597
|
const ungeprueft = ergebnis.unchecked ?? [];
|
|
3473
3598
|
return {
|
|
3474
3599
|
tone: "rejected",
|
|
3475
|
-
text: `${was}: abgelehnt \u2014 ${
|
|
3600
|
+
text: `${was}: abgelehnt \u2014 ${fehler5.length} Befund${fehler5.length === 1 ? "" : "e"}.`,
|
|
3476
3601
|
details: [
|
|
3477
|
-
...
|
|
3602
|
+
...fehler5.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
|
|
3478
3603
|
// Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
|
|
3479
3604
|
// sondern ungeprüft.
|
|
3480
3605
|
...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
|
|
@@ -5248,7 +5373,60 @@ var SZENEN_EVENT_TYPES = {
|
|
|
5248
5373
|
* Der Elementreducer ignoriert auch diesen Typ (`default:`-Zweig); gelesen
|
|
5249
5374
|
* wird mit `collectImageBatches` (`imageBatchStore.ts`).
|
|
5250
5375
|
*/
|
|
5251
|
-
imageBatchPersisted: "szenen.image_batch_persisted"
|
|
5376
|
+
imageBatchPersisted: "szenen.image_batch_persisted",
|
|
5377
|
+
/**
|
|
5378
|
+
* Ein vorbereiteter Promptvorgang — der Durchstich „Szene → ChatGPT → Lauf".
|
|
5379
|
+
*
|
|
5380
|
+
* Der bestehende Promptpfad ruft das Modell selbst
|
|
5381
|
+
* (`runPromptDerivation()`); ein aufrufendes Chatmodell formuliert dagegen
|
|
5382
|
+
* **zwischen** zwei Werkzeugaufrufen. Damit zerfaellt der eine Vorgang in
|
|
5383
|
+
* zwei, und der zweite braucht genau den Stand, den der erste eingefroren
|
|
5384
|
+
* hat — sonst loeste der Abschluss die inzwischen geaenderte Szene neu auf
|
|
5385
|
+
* und der Lauf behauptete eine Herkunft, die er nicht hat (D-22).
|
|
5386
|
+
*
|
|
5387
|
+
* Deshalb ein eigenes Ereignis und **kein** fluechtiger Prozesszustand: Der
|
|
5388
|
+
* Snapshot ueberlebt Neustart, Hostwechsel und Sitzungsende, weil er im
|
|
5389
|
+
* selben autoritativen Log liegt wie der Lauf, der daraus entsteht.
|
|
5390
|
+
*
|
|
5391
|
+
* Der Elementreducer ignoriert diesen Typ (`default:`-Zweig); gelesen wird
|
|
5392
|
+
* mit `collectPromptOperations` (`scenePromptOperation.ts`).
|
|
5393
|
+
*/
|
|
5394
|
+
promptOperationPrepared: "szenen.prompt_operation_prepared",
|
|
5395
|
+
/**
|
|
5396
|
+
* Ein Bildauftrag wartet auf einen ausfuehrenden Flow-Host.
|
|
5397
|
+
*
|
|
5398
|
+
* Die vorhandene Auftragshistorie (`imageGenerationStarted`) beginnt
|
|
5399
|
+
* **unmittelbar vor** dem Provideraufruf und kennt deshalb keinen wartenden
|
|
5400
|
+
* Auftrag. Genau der entsteht hier: Ein Chatmodell beauftragt ueber MCP ein
|
|
5401
|
+
* Bild, der Auftrag wird dauerhaft abgelegt, und der Werkzeugaufruf endet
|
|
5402
|
+
* sofort mit seiner Kennung — er haelt keine Antwort minutenlang offen.
|
|
5403
|
+
*
|
|
5404
|
+
* Traegt **keinen** Prompt: Der steht im referenzierten Lauf, und ihn hier
|
|
5405
|
+
* zu kopieren waere eine zweite Wahrheit ueber denselben eingefrorenen Text
|
|
5406
|
+
* (D-103).
|
|
5407
|
+
*/
|
|
5408
|
+
imageJobRequested: "szenen.image_job_requested",
|
|
5409
|
+
/**
|
|
5410
|
+
* Ein Flow-Host beansprucht einen wartenden Bildauftrag.
|
|
5411
|
+
*
|
|
5412
|
+
* Ein append-only Log kennt kein Lock: Zwei Tabs koennen beide anhaengen.
|
|
5413
|
+
* Deshalb ist die Uebernahme **nicht** der Append, sondern das Ergebnis
|
|
5414
|
+
* einer deterministischen Schiedsregel darueber (`winningClaimOf`) — der
|
|
5415
|
+
* erste Anspruch in Logreihenfolge gewinnt, und jeder Host liest nach
|
|
5416
|
+
* seinem Append erneut, bevor er einen bezahlten Provideraufruf startet.
|
|
5417
|
+
* Ein unterlegener Anspruch bleibt sichtbar stehen; er wird nicht geloescht,
|
|
5418
|
+
* weil das Log das nicht kann und weil der Versuch ein Beleg ist.
|
|
5419
|
+
*/
|
|
5420
|
+
imageJobClaimed: "szenen.image_job_claimed",
|
|
5421
|
+
/**
|
|
5422
|
+
* Der Bildauftrag ist erledigt — mit dem Ausgang, den der Host gesehen hat.
|
|
5423
|
+
*
|
|
5424
|
+
* Getrennt von `imageGenerationFinished`: Jenes gehoert zum bezahlten
|
|
5425
|
+
* Provideraufruf, dieses zur Zustellung. Ein Auftrag kann fehlschlagen,
|
|
5426
|
+
* **bevor** ein Provider gerufen wurde (abgelehntes Profil), und dann gibt
|
|
5427
|
+
* es kein `imageGenerationFinished`, das den Wartenden erloesen wuerde.
|
|
5428
|
+
*/
|
|
5429
|
+
imageJobSettled: "szenen.image_job_settled"
|
|
5252
5430
|
};
|
|
5253
5431
|
function sortForReplay(events) {
|
|
5254
5432
|
return [...events].sort((a, b) => {
|
|
@@ -5611,9 +5789,9 @@ async function leseBilderAusZip(datei) {
|
|
|
5611
5789
|
);
|
|
5612
5790
|
}
|
|
5613
5791
|
const { default: JSZip2 } = await import("jszip");
|
|
5614
|
-
const archiv = await JSZip2.loadAsync(datei).catch((
|
|
5792
|
+
const archiv = await JSZip2.loadAsync(datei).catch((fehler5) => {
|
|
5615
5793
|
throw new Error(
|
|
5616
|
-
`${name} konnte nicht als ZIP gelesen werden: ${
|
|
5794
|
+
`${name} konnte nicht als ZIP gelesen werden: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
|
|
5617
5795
|
);
|
|
5618
5796
|
});
|
|
5619
5797
|
const pfade = [];
|
|
@@ -8492,7 +8670,7 @@ var ERKLAERUNG = {
|
|
|
8492
8670
|
};
|
|
8493
8671
|
var CapabilityPanel = ({ port, elementId, setupPayload }) => {
|
|
8494
8672
|
const [bericht, setBericht] = useState5(null);
|
|
8495
|
-
const [
|
|
8673
|
+
const [fehler5, setFehler] = useState5(null);
|
|
8496
8674
|
useEffect2(() => {
|
|
8497
8675
|
let abgemeldet = false;
|
|
8498
8676
|
setBericht(null);
|
|
@@ -8514,10 +8692,10 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
|
|
|
8514
8692
|
abgemeldet = true;
|
|
8515
8693
|
};
|
|
8516
8694
|
}, [port, elementId, setupPayload]);
|
|
8517
|
-
if (
|
|
8695
|
+
if (fehler5) {
|
|
8518
8696
|
return /* @__PURE__ */ jsxs4("p", { "data-testid": "capability-fehler", role: "alert", children: [
|
|
8519
8697
|
"F\xE4higkeitsbericht nicht erstellbar: ",
|
|
8520
|
-
|
|
8698
|
+
fehler5
|
|
8521
8699
|
] });
|
|
8522
8700
|
}
|
|
8523
8701
|
if (!bericht) return /* @__PURE__ */ jsx4("p", { "data-testid": "capability-laden", children: "F\xE4higkeitsbericht wird erstellt \u2026" });
|
|
@@ -8924,6 +9102,7 @@ var RunImagesPanel = ({ port, lauf }) => {
|
|
|
8924
9102
|
const neuLesen = () => setNachladen((n) => n + 1);
|
|
8925
9103
|
const [erzeugenOffen, setErzeugenOffen] = useState8(null);
|
|
8926
9104
|
const [zuordnenOffen, setZuordnenOffen] = useState8(null);
|
|
9105
|
+
const [chatAuftraegeOffen, setChatAuftraegeOffen] = useState8(null);
|
|
8927
9106
|
const quelle = sourceOf(lauf);
|
|
8928
9107
|
const [wahlPhase] = usePortQuery(
|
|
8929
9108
|
() => port.imageGeneration ? port.imageGeneration.imageModelChoiceForRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: null }),
|
|
@@ -8938,10 +9117,17 @@ var RunImagesPanel = ({ port, lauf }) => {
|
|
|
8938
9117
|
() => port.imageBatchHistory ? port.imageBatchHistory.listBatchesForPromptRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: [] }),
|
|
8939
9118
|
[port, lauf.run_id, nachladen]
|
|
8940
9119
|
);
|
|
9120
|
+
const [chatAuftraegePhase] = usePortQuery(
|
|
9121
|
+
() => port.imageJobs ? port.imageJobs.listJobs(lauf.run_id) : Promise.resolve({ kind: "ok", value: [] }),
|
|
9122
|
+
[port, lauf.run_id, nachladen]
|
|
9123
|
+
);
|
|
8941
9124
|
const auftraege = auftraegePhase.status === "ready" ? auftraegePhase.value : [];
|
|
8942
9125
|
const batches = batchesPhase.status === "ready" ? batchesPhase.value : [];
|
|
9126
|
+
const chatAuftraege = chatAuftraegePhase.status === "ready" ? chatAuftraegePhase.value : [];
|
|
9127
|
+
const offeneChatAuftraege = chatAuftraege.filter((a) => a.status === "pending").length;
|
|
8943
9128
|
const auffaellige = auftraege.filter((a) => a.status !== "succeeded");
|
|
8944
9129
|
const erzeugenAuf = erzeugenOffen ?? batches.length === 0;
|
|
9130
|
+
const chatAuftraegeAuf = chatAuftraegeOffen ?? offeneChatAuftraege > 0;
|
|
8945
9131
|
return /* @__PURE__ */ jsxs8("div", { className: "szenen-bilder", "data-testid": `bilder-${lauf.run_id}`, children: [
|
|
8946
9132
|
/* @__PURE__ */ jsxs8("section", { className: "szenen-bilder__eingang", "data-testid": `eingang-${lauf.run_id}`, children: [
|
|
8947
9133
|
/* @__PURE__ */ jsx8("h5", { children: "Eingang \u2014 woraus dieser Prompt entstand" }),
|
|
@@ -9062,6 +9248,43 @@ var RunImagesPanel = ({ port, lauf }) => {
|
|
|
9062
9248
|
}
|
|
9063
9249
|
)
|
|
9064
9250
|
] }),
|
|
9251
|
+
faehig.imageJobs && /* @__PURE__ */ jsxs8(
|
|
9252
|
+
"details",
|
|
9253
|
+
{
|
|
9254
|
+
className: "szenen-bilder__aktion",
|
|
9255
|
+
"data-testid": `auftraege-chat-bereich-${lauf.run_id}`,
|
|
9256
|
+
open: chatAuftraegeAuf,
|
|
9257
|
+
children: [
|
|
9258
|
+
/* @__PURE__ */ jsxs8(
|
|
9259
|
+
"summary",
|
|
9260
|
+
{
|
|
9261
|
+
onClick: (e) => {
|
|
9262
|
+
e.preventDefault();
|
|
9263
|
+
setChatAuftraegeOffen(!chatAuftraegeAuf);
|
|
9264
|
+
},
|
|
9265
|
+
children: [
|
|
9266
|
+
"Bildauftraege aus dem Chat",
|
|
9267
|
+
offeneChatAuftraege > 0 ? ` (${offeneChatAuftraege} wartend)` : ""
|
|
9268
|
+
]
|
|
9269
|
+
}
|
|
9270
|
+
),
|
|
9271
|
+
/* @__PURE__ */ jsx8(
|
|
9272
|
+
ChatAuftraegeAktion,
|
|
9273
|
+
{
|
|
9274
|
+
port,
|
|
9275
|
+
lauf,
|
|
9276
|
+
auftraege: chatAuftraege,
|
|
9277
|
+
fehler: chatAuftraegePhase.status === "error" ? chatAuftraegePhase.message : null,
|
|
9278
|
+
laedt: chatAuftraegePhase.status === "loading",
|
|
9279
|
+
onFertig: () => {
|
|
9280
|
+
setChatAuftraegeOffen(true);
|
|
9281
|
+
neuLesen();
|
|
9282
|
+
}
|
|
9283
|
+
}
|
|
9284
|
+
)
|
|
9285
|
+
]
|
|
9286
|
+
}
|
|
9287
|
+
),
|
|
9065
9288
|
/* @__PURE__ */ jsxs8(
|
|
9066
9289
|
"details",
|
|
9067
9290
|
{
|
|
@@ -9245,7 +9468,7 @@ function useBildQuellen(port, bilder) {
|
|
|
9245
9468
|
erneutVersuchen: () => setVersuch((n) => n + 1)
|
|
9246
9469
|
};
|
|
9247
9470
|
}
|
|
9248
|
-
var BildDetails = ({ bild, fehler:
|
|
9471
|
+
var BildDetails = ({ bild, fehler: fehler5, aufloesbar }) => /* @__PURE__ */ jsxs8("span", { className: "szenen-bilder__meta", "data-testid": `batchbild-${bild.media_ref}`, children: [
|
|
9249
9472
|
/* @__PURE__ */ jsx8("code", { children: bild.media_ref }),
|
|
9250
9473
|
/* @__PURE__ */ jsxs8("span", { children: [
|
|
9251
9474
|
"Position ",
|
|
@@ -9258,7 +9481,7 @@ var BildDetails = ({ bild, fehler: fehler4, aufloesbar }) => /* @__PURE__ */ jsx
|
|
|
9258
9481
|
"\xD7",
|
|
9259
9482
|
bild.height
|
|
9260
9483
|
] }) : null,
|
|
9261
|
-
|
|
9484
|
+
fehler5 && /* @__PURE__ */ jsx8("em", { "data-testid": `batchbild-fehler-${bild.media_ref}`, children: fehler5 }),
|
|
9262
9485
|
!aufloesbar && // Capability-Grenze, kein Fehler: Dieser Host kann Batches lesen,
|
|
9263
9486
|
// aber keine Bilder auflösen (E). Kein erfundener Fallback auf eine
|
|
9264
9487
|
// Serverroute (A6).
|
|
@@ -9274,18 +9497,18 @@ var HERKUNFT_KURZ = {
|
|
|
9274
9497
|
external_import: "extern importiert"
|
|
9275
9498
|
};
|
|
9276
9499
|
var BatchKachel = ({ port, batch }) => {
|
|
9277
|
-
const { quellen, fehler:
|
|
9500
|
+
const { quellen, fehler: fehler5, wiederholbar, erneutVersuchen } = useBildQuellen(port, batch.images);
|
|
9278
9501
|
const aufloesbar = Boolean(port.imageLibrary);
|
|
9279
9502
|
const bilder = batch.images.map((bild) => ({
|
|
9280
9503
|
id: bild.media_ref,
|
|
9281
9504
|
quelle: quellen.get(bild.media_ref) ?? null,
|
|
9282
9505
|
titel: `Bild ${bild.position + 1}`,
|
|
9283
|
-
...
|
|
9506
|
+
...fehler5.get(bild.media_ref) ? { leerText: "Nicht ladbar" } : {},
|
|
9284
9507
|
details: /* @__PURE__ */ jsx8(
|
|
9285
9508
|
BildDetails,
|
|
9286
9509
|
{
|
|
9287
9510
|
bild,
|
|
9288
|
-
fehler:
|
|
9511
|
+
fehler: fehler5.get(bild.media_ref) ?? null,
|
|
9289
9512
|
aufloesbar
|
|
9290
9513
|
}
|
|
9291
9514
|
)
|
|
@@ -9345,7 +9568,7 @@ var BatchKachel = ({ port, batch }) => {
|
|
|
9345
9568
|
BildDetails,
|
|
9346
9569
|
{
|
|
9347
9570
|
bild,
|
|
9348
|
-
fehler:
|
|
9571
|
+
fehler: fehler5.get(bild.media_ref) ?? null,
|
|
9349
9572
|
aufloesbar
|
|
9350
9573
|
}
|
|
9351
9574
|
) }, bild.media_ref)) })
|
|
@@ -9614,11 +9837,11 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
9614
9837
|
setSeedListe(DEFAULT_SEED_VORLAGE.seeds);
|
|
9615
9838
|
setVorlageKey(DEFAULT_VORLAGE_KEY);
|
|
9616
9839
|
}
|
|
9617
|
-
} catch (
|
|
9840
|
+
} catch (fehler5) {
|
|
9618
9841
|
if (veraltet()) return;
|
|
9619
9842
|
setNotiz({
|
|
9620
9843
|
tone: "error",
|
|
9621
|
-
text:
|
|
9844
|
+
text: fehler5 instanceof Error ? fehler5.message : String(fehler5)
|
|
9622
9845
|
});
|
|
9623
9846
|
}
|
|
9624
9847
|
if (veraltet()) return;
|
|
@@ -9637,10 +9860,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
9637
9860
|
setNotiz(
|
|
9638
9861
|
ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
|
|
9639
9862
|
);
|
|
9640
|
-
} catch (
|
|
9863
|
+
} catch (fehler5) {
|
|
9641
9864
|
setNotiz({
|
|
9642
9865
|
tone: "error",
|
|
9643
|
-
text: `Externe Bilder importieren: ${
|
|
9866
|
+
text: `Externe Bilder importieren: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
|
|
9644
9867
|
});
|
|
9645
9868
|
}
|
|
9646
9869
|
setLaeuft(false);
|
|
@@ -9662,10 +9885,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
9662
9885
|
...seed !== void 0 ? { seed } : {}
|
|
9663
9886
|
});
|
|
9664
9887
|
}
|
|
9665
|
-
} catch (
|
|
9888
|
+
} catch (fehler5) {
|
|
9666
9889
|
setNotiz({
|
|
9667
9890
|
tone: "error",
|
|
9668
|
-
text: `Externe Bilder importieren: ${
|
|
9891
|
+
text: `Externe Bilder importieren: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
|
|
9669
9892
|
});
|
|
9670
9893
|
return;
|
|
9671
9894
|
}
|
|
@@ -10001,6 +10224,101 @@ function readAsBase64(datei) {
|
|
|
10001
10224
|
leser.readAsDataURL(datei);
|
|
10002
10225
|
});
|
|
10003
10226
|
}
|
|
10227
|
+
var CHAT_AUFTRAG_TEXT = {
|
|
10228
|
+
pending: "wartet auf \xDCbernahme",
|
|
10229
|
+
// Wie bei `started` (D-119): laufend und abgebrochen sind aus dem Log
|
|
10230
|
+
// nicht unterscheidbar, und es gibt keinen automatischen Retry, der die
|
|
10231
|
+
// Unterscheidung bräuchte.
|
|
10232
|
+
claimed: "\xFCbernommen (l\xE4uft oder unterbrochen)",
|
|
10233
|
+
succeeded: "erfolgreich",
|
|
10234
|
+
partial: "teilweise",
|
|
10235
|
+
failed: "fehlgeschlagen",
|
|
10236
|
+
rejected: "abgelehnt \u2014 es wurde nichts ausgef\xFChrt"
|
|
10237
|
+
};
|
|
10238
|
+
var ChatAuftraegeAktion = ({
|
|
10239
|
+
port,
|
|
10240
|
+
lauf,
|
|
10241
|
+
auftraege,
|
|
10242
|
+
fehler: fehler5,
|
|
10243
|
+
laedt,
|
|
10244
|
+
onFertig
|
|
10245
|
+
}) => {
|
|
10246
|
+
const [laufend, setLaufend] = useState8(null);
|
|
10247
|
+
const [notiz, setNotiz] = useState8(null);
|
|
10248
|
+
async function uebernehmen(jobId) {
|
|
10249
|
+
if (!port.imageJobs) return;
|
|
10250
|
+
setLaufend(jobId);
|
|
10251
|
+
setNotiz(null);
|
|
10252
|
+
try {
|
|
10253
|
+
const ergebnis = await port.imageJobs.claimAndRun(jobId);
|
|
10254
|
+
if (ergebnis.kind === "ok") {
|
|
10255
|
+
setNotiz({
|
|
10256
|
+
tone: ergebnis.job.status === "succeeded" ? "ok" : "rejected",
|
|
10257
|
+
text: `Auftrag ${jobId}: ${CHAT_AUFTRAG_TEXT[ergebnis.job.status]}.` + (ergebnis.job.diagnostic ? ` ${ergebnis.job.diagnostic}` : "")
|
|
10258
|
+
});
|
|
10259
|
+
} else if (ergebnis.kind === "lost") {
|
|
10260
|
+
setNotiz({ tone: "rejected", text: ergebnis.message });
|
|
10261
|
+
} else {
|
|
10262
|
+
setNotiz({ tone: "error", text: ergebnis.message });
|
|
10263
|
+
}
|
|
10264
|
+
} catch (e) {
|
|
10265
|
+
setNotiz({
|
|
10266
|
+
tone: "error",
|
|
10267
|
+
text: `Die \xDCbernahme von Auftrag ${jobId} ist technisch gescheitert: ` + (e instanceof Error ? e.message : String(e))
|
|
10268
|
+
});
|
|
10269
|
+
} finally {
|
|
10270
|
+
setLaufend(null);
|
|
10271
|
+
onFertig();
|
|
10272
|
+
}
|
|
10273
|
+
}
|
|
10274
|
+
return /* @__PURE__ */ jsxs8("div", { className: "szenen-bilder__aktion-inhalt", "data-testid": `chat-auftraege-${lauf.run_id}`, children: [
|
|
10275
|
+
/* @__PURE__ */ jsx8("p", { className: "szenen-area__lead", children: "Auftr\xE4ge, die \xFCber den MCP aus dem Chat kamen. Der Prompt wird nicht kopiert \u2014 ausgef\xFChrt wird der eingefrorene Prompt dieses Laufs." }),
|
|
10276
|
+
fehler5 && /* @__PURE__ */ jsxs8("p", { role: "alert", "data-testid": `chat-auftraege-fehler-${lauf.run_id}`, children: [
|
|
10277
|
+
"Bildauftr\xE4ge aus dem Chat konnten nicht gelesen werden: ",
|
|
10278
|
+
fehler5
|
|
10279
|
+
] }),
|
|
10280
|
+
!fehler5 && !laedt && auftraege.length === 0 && /* @__PURE__ */ jsx8("p", { "data-testid": `chat-auftraege-leer-${lauf.run_id}`, children: "F\xFCr diesen Lauf liegt kein Auftrag aus dem Chat vor." }),
|
|
10281
|
+
notiz && /* @__PURE__ */ jsx8(
|
|
10282
|
+
"p",
|
|
10283
|
+
{
|
|
10284
|
+
role: notiz.tone === "error" ? "alert" : void 0,
|
|
10285
|
+
"data-testid": `chat-auftraege-notiz-${lauf.run_id}`,
|
|
10286
|
+
children: notiz.text
|
|
10287
|
+
}
|
|
10288
|
+
),
|
|
10289
|
+
/* @__PURE__ */ jsx8("ul", { className: "szenen-bilder__auftraege", children: auftraege.map((auftrag) => /* @__PURE__ */ jsxs8("li", { "data-testid": `chat-auftrag-${auftrag.job_id}`, children: [
|
|
10290
|
+
/* @__PURE__ */ jsx8("span", { children: CHAT_AUFTRAG_TEXT[auftrag.status] }),
|
|
10291
|
+
" \u2014 ",
|
|
10292
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
10293
|
+
auftrag.image_count,
|
|
10294
|
+
" ",
|
|
10295
|
+
auftrag.image_count === 1 ? "Bild" : "Bilder",
|
|
10296
|
+
" mit",
|
|
10297
|
+
" ",
|
|
10298
|
+
/* @__PURE__ */ jsx8("code", { children: auftrag.execution_target_model_key }),
|
|
10299
|
+
" / ",
|
|
10300
|
+
/* @__PURE__ */ jsx8("code", { children: auftrag.execution_profile_id }),
|
|
10301
|
+
auftrag.aspect_ratio ? ` im Format ${auftrag.aspect_ratio}` : ""
|
|
10302
|
+
] }),
|
|
10303
|
+
auftrag.diagnostic && /* @__PURE__ */ jsxs8("span", { children: [
|
|
10304
|
+
" ",
|
|
10305
|
+
auftrag.diagnostic
|
|
10306
|
+
] }),
|
|
10307
|
+
/* @__PURE__ */ jsx8("br", {}),
|
|
10308
|
+
/* @__PURE__ */ jsx8("code", { className: "szenen-bilder__auftragskennung", children: auftrag.job_id }),
|
|
10309
|
+
auftrag.status === "pending" && /* @__PURE__ */ jsx8(
|
|
10310
|
+
"button",
|
|
10311
|
+
{
|
|
10312
|
+
type: "button",
|
|
10313
|
+
disabled: laufend !== null,
|
|
10314
|
+
onClick: () => void uebernehmen(auftrag.job_id),
|
|
10315
|
+
"data-testid": `chat-auftrag-uebernehmen-${auftrag.job_id}`,
|
|
10316
|
+
children: laufend === auftrag.job_id ? "\xDCbernahme l\xE4uft\u2026" : "\xDCbernehmen und ausf\xFChren (kostet einen Bildaufruf)"
|
|
10317
|
+
}
|
|
10318
|
+
)
|
|
10319
|
+
] }, auftrag.job_id)) })
|
|
10320
|
+
] });
|
|
10321
|
+
};
|
|
10004
10322
|
|
|
10005
10323
|
// src/features/szenen/areas/RunsPanel.tsx
|
|
10006
10324
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
@@ -13701,12 +14019,21 @@ function createHfElementStore(options) {
|
|
|
13701
14019
|
|
|
13702
14020
|
// src/lib/szenen/hfEventChannel.ts
|
|
13703
14021
|
function createHfEventChannel(options) {
|
|
13704
|
-
const { namespace, token, sinceTs = 0 } = options;
|
|
14022
|
+
const { namespace, token, sinceTs = 0, ohneSnapshot = false } = options;
|
|
13705
14023
|
const knownPaths = /* @__PURE__ */ new Set();
|
|
13706
14024
|
let cachedRawEvents = [];
|
|
13707
14025
|
let syncInFlight = null;
|
|
13708
|
-
|
|
13709
|
-
|
|
14026
|
+
let snapshot = null;
|
|
14027
|
+
let snapshotGeladen = ohneSnapshot;
|
|
14028
|
+
const refresh = async (revision = "main") => {
|
|
14029
|
+
if (!snapshotGeladen) {
|
|
14030
|
+
snapshot = await loadLatestSzenenSnapshot(namespace, token);
|
|
14031
|
+
snapshotGeladen = true;
|
|
14032
|
+
for (const name of aufgenommeneDateien(snapshot)) {
|
|
14033
|
+
knownPaths.add(`${namespace}events/${name}`);
|
|
14034
|
+
}
|
|
14035
|
+
}
|
|
14036
|
+
const neue = await loadPendingEvents(namespace, token, sinceTs, knownPaths, revision);
|
|
13710
14037
|
if (neue.length) cachedRawEvents = [...cachedRawEvents, ...neue];
|
|
13711
14038
|
};
|
|
13712
14039
|
const sync = () => {
|
|
@@ -13717,15 +14044,34 @@ function createHfEventChannel(options) {
|
|
|
13717
14044
|
}
|
|
13718
14045
|
return syncInFlight;
|
|
13719
14046
|
};
|
|
14047
|
+
const sicht = () => {
|
|
14048
|
+
const pending = cachedRawEvents.filter((event) => typeof event.type === "string" && event.type.startsWith(SZENEN_EVENT_PREFIX)).map((event) => ({
|
|
14049
|
+
type: event.type,
|
|
14050
|
+
payload: event.payload,
|
|
14051
|
+
clientId: event.clientId,
|
|
14052
|
+
ts: event.ts
|
|
14053
|
+
}));
|
|
14054
|
+
return mergeSnapshotAndPending(snapshot, pending).events;
|
|
14055
|
+
};
|
|
13720
14056
|
return {
|
|
13721
14057
|
async readEvents() {
|
|
13722
14058
|
await sync();
|
|
13723
|
-
return
|
|
13724
|
-
|
|
13725
|
-
|
|
13726
|
-
|
|
13727
|
-
|
|
13728
|
-
|
|
14059
|
+
return sicht();
|
|
14060
|
+
},
|
|
14061
|
+
async readHead() {
|
|
14062
|
+
return hfRepoHead(token);
|
|
14063
|
+
},
|
|
14064
|
+
async readEventsAt(revision) {
|
|
14065
|
+
await refresh(revision);
|
|
14066
|
+
return sicht();
|
|
14067
|
+
},
|
|
14068
|
+
async appendEventAt(type, payload, parentCommit) {
|
|
14069
|
+
if (!type.startsWith(SZENEN_EVENT_PREFIX)) {
|
|
14070
|
+
throw new Error(
|
|
14071
|
+
`Event-Typ "${type}" gehoert nicht zur Domain szenen. Der geteilte Kanal wird ueber das Praefix "${SZENEN_EVENT_PREFIX}" getrennt.`
|
|
14072
|
+
);
|
|
14073
|
+
}
|
|
14074
|
+
await writeHFEvent(namespace, token, type, payload, [], { ordered: true, parentCommit });
|
|
13729
14075
|
},
|
|
13730
14076
|
async appendEvent(type, payload) {
|
|
13731
14077
|
if (!type.startsWith(SZENEN_EVENT_PREFIX)) {
|
|
@@ -13909,11 +14255,11 @@ async function resolveShotPayload(input) {
|
|
|
13909
14255
|
};
|
|
13910
14256
|
}
|
|
13911
14257
|
const shot = shotPayload;
|
|
13912
|
-
for (const
|
|
14258
|
+
for (const fehler5 of ohneKettenReferenz(
|
|
13913
14259
|
validatePayload("shot", shotPayload).errors,
|
|
13914
14260
|
chain ? "scene_setup_ref" : null
|
|
13915
14261
|
)) {
|
|
13916
|
-
diagnostics.push(diagnosticOfSchemaError(
|
|
14262
|
+
diagnostics.push(diagnosticOfSchemaError(fehler5, `Shot ${shotElementId}`));
|
|
13917
14263
|
}
|
|
13918
14264
|
let setup = null;
|
|
13919
14265
|
let report = null;
|
|
@@ -14025,8 +14371,8 @@ async function resolveScenePayload(input) {
|
|
|
14025
14371
|
"Kein Scene Setup, kein Shot: Der Lauf entsteht aus der Szene allein. Bildaufbau, Kamera und Zielmodell wurden nicht ergaenzt, sondern sind nicht vorhanden (D-135)."
|
|
14026
14372
|
];
|
|
14027
14373
|
const diagnostics = [];
|
|
14028
|
-
for (const
|
|
14029
|
-
diagnostics.push(diagnosticOfSchemaError(
|
|
14374
|
+
for (const fehler5 of validatePayload("scene", scenePayload).errors) {
|
|
14375
|
+
diagnostics.push(diagnosticOfSchemaError(fehler5, `Szene ${sceneElementId}`));
|
|
14030
14376
|
}
|
|
14031
14377
|
const faehig = sceneOnlyCapabilities(scenePayload);
|
|
14032
14378
|
return komponiere({
|
|
@@ -15547,6 +15893,44 @@ function gruppiereZuItems(bilder) {
|
|
|
15547
15893
|
items.sort((a, b) => b.juengstes - a.juengstes);
|
|
15548
15894
|
return items.map((e) => e.item);
|
|
15549
15895
|
}
|
|
15896
|
+
function locatorAus(itemId, position, anzahl) {
|
|
15897
|
+
return anzahl === 1 ? itemId : `${itemId}-${position}`;
|
|
15898
|
+
}
|
|
15899
|
+
function mimeTypAus(dateiname) {
|
|
15900
|
+
const n = dateiname.toLowerCase();
|
|
15901
|
+
if (n.endsWith(".png")) return "image/png";
|
|
15902
|
+
if (n.endsWith(".webp")) return "image/webp";
|
|
15903
|
+
return "image/jpeg";
|
|
15904
|
+
}
|
|
15905
|
+
function passt(item, suche) {
|
|
15906
|
+
const f = suche.trim().toLowerCase();
|
|
15907
|
+
if (!f) return true;
|
|
15908
|
+
return (item.title ?? "").toLowerCase().includes(f) || item.id.toLowerCase().includes(f);
|
|
15909
|
+
}
|
|
15910
|
+
function itemsAusBestand(items, options = {}) {
|
|
15911
|
+
const suche = options.search ?? "";
|
|
15912
|
+
const werke = [];
|
|
15913
|
+
const passende = items.filter((i) => i && typeof i.id === "string" && i.id.length > 0 && passt(i, suche)).sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0) || (a.id < b.id ? 1 : a.id > b.id ? -1 : 0));
|
|
15914
|
+
for (const item of passende) {
|
|
15915
|
+
const galerie = Array.isArray(item.gallery) ? item.gallery : [];
|
|
15916
|
+
const titel = item.title || item.id;
|
|
15917
|
+
const bilder = [];
|
|
15918
|
+
galerie.forEach((eintrag, position) => {
|
|
15919
|
+
const datei = eintrag?.file;
|
|
15920
|
+
if (!datei) return;
|
|
15921
|
+
bilder.push({
|
|
15922
|
+
mediaRef: locatorAus(item.id, position, galerie.length),
|
|
15923
|
+
title: galerie.length === 1 ? titel : `${titel} \xB7 Bild ${position + 1}`,
|
|
15924
|
+
mimeType: mimeTypAus(String(datei)),
|
|
15925
|
+
thumbUrl: options.thumbUrl ? options.thumbUrl(String(datei)) : null
|
|
15926
|
+
});
|
|
15927
|
+
});
|
|
15928
|
+
if (bilder.length === 0) continue;
|
|
15929
|
+
werke.push({ itemRef: item.id, title: titel, images: bilder });
|
|
15930
|
+
if (options.limit && werke.length >= options.limit) break;
|
|
15931
|
+
}
|
|
15932
|
+
return werke;
|
|
15933
|
+
}
|
|
15550
15934
|
|
|
15551
15935
|
// src/lib/szenen/hfImageLibrary.ts
|
|
15552
15936
|
function fehler2(nachricht, retryable = false) {
|
|
@@ -15677,8 +16061,8 @@ function createFlowImageProvider(generateImage) {
|
|
|
15677
16061
|
// Kein Seed: weder als Eingabe dokumentiert noch als Ausgabe
|
|
15678
16062
|
// geliefert — nichts wird erfunden (D-107).
|
|
15679
16063
|
});
|
|
15680
|
-
} catch (
|
|
15681
|
-
ersterFehler =
|
|
16064
|
+
} catch (fehler5) {
|
|
16065
|
+
ersterFehler = fehler5 instanceof Error ? fehler5.message : String(fehler5);
|
|
15682
16066
|
break;
|
|
15683
16067
|
}
|
|
15684
16068
|
}
|
|
@@ -16207,6 +16591,252 @@ function createImageGenerationPort(options) {
|
|
|
16207
16591
|
};
|
|
16208
16592
|
}
|
|
16209
16593
|
|
|
16594
|
+
// src/lib/szenen/imageJobStore.ts
|
|
16595
|
+
var istText7 = (wert) => typeof wert === "string" && wert.trim().length > 0;
|
|
16596
|
+
var SETTLED_STATUS = ["succeeded", "partial", "failed", "rejected"];
|
|
16597
|
+
function collectImageJobs(events) {
|
|
16598
|
+
const nachId = /* @__PURE__ */ new Map();
|
|
16599
|
+
for (const event of events) {
|
|
16600
|
+
if (event.type !== SZENEN_EVENT_TYPES.imageJobRequested) continue;
|
|
16601
|
+
const payload = event.payload;
|
|
16602
|
+
if (!payload || !istText7(payload.job_id) || !istText7(payload.prompt_run_id)) continue;
|
|
16603
|
+
if (nachId.has(payload.job_id)) continue;
|
|
16604
|
+
nachId.set(payload.job_id, {
|
|
16605
|
+
job_id: payload.job_id,
|
|
16606
|
+
prompt_run_id: payload.prompt_run_id,
|
|
16607
|
+
execution_target_model_key: payload.execution_target_model_key ?? "",
|
|
16608
|
+
execution_profile_id: payload.execution_profile_id ?? "",
|
|
16609
|
+
image_count: typeof payload.image_count === "number" ? payload.image_count : 0,
|
|
16610
|
+
...payload.aspect_ratio ? { aspect_ratio: payload.aspect_ratio } : {},
|
|
16611
|
+
requested_by: payload.requested_by ?? "",
|
|
16612
|
+
store_id: payload.store_id ?? "",
|
|
16613
|
+
created_at: payload.created_at ?? "",
|
|
16614
|
+
status: "pending",
|
|
16615
|
+
claim_attempts: []
|
|
16616
|
+
});
|
|
16617
|
+
}
|
|
16618
|
+
for (const event of events) {
|
|
16619
|
+
if (event.type !== SZENEN_EVENT_TYPES.imageJobClaimed) continue;
|
|
16620
|
+
const payload = event.payload;
|
|
16621
|
+
if (!payload || !istText7(payload.job_id) || !istText7(payload.claim_id)) continue;
|
|
16622
|
+
const auftrag = nachId.get(payload.job_id);
|
|
16623
|
+
if (!auftrag) continue;
|
|
16624
|
+
const anspruch = {
|
|
16625
|
+
job_id: payload.job_id,
|
|
16626
|
+
claim_id: payload.claim_id,
|
|
16627
|
+
client_id: payload.client_id ?? "",
|
|
16628
|
+
claimed_at: payload.claimed_at ?? ""
|
|
16629
|
+
};
|
|
16630
|
+
nachId.set(payload.job_id, {
|
|
16631
|
+
...auftrag,
|
|
16632
|
+
claim: auftrag.claim ?? anspruch,
|
|
16633
|
+
claim_attempts: [...auftrag.claim_attempts, anspruch],
|
|
16634
|
+
status: auftrag.status === "pending" ? "claimed" : auftrag.status
|
|
16635
|
+
});
|
|
16636
|
+
}
|
|
16637
|
+
for (const event of events) {
|
|
16638
|
+
if (event.type !== SZENEN_EVENT_TYPES.imageJobSettled) continue;
|
|
16639
|
+
const payload = event.payload;
|
|
16640
|
+
if (!payload || !istText7(payload.job_id)) continue;
|
|
16641
|
+
const auftrag = nachId.get(payload.job_id);
|
|
16642
|
+
if (!auftrag) continue;
|
|
16643
|
+
if (!auftrag.claim || auftrag.claim.claim_id !== payload.claim_id) continue;
|
|
16644
|
+
if (auftrag.status !== "claimed") continue;
|
|
16645
|
+
if (!SETTLED_STATUS.includes(payload.status)) continue;
|
|
16646
|
+
nachId.set(payload.job_id, {
|
|
16647
|
+
...auftrag,
|
|
16648
|
+
status: payload.status,
|
|
16649
|
+
...payload.generation_batch_id ? { generation_batch_id: payload.generation_batch_id } : {},
|
|
16650
|
+
...payload.item_id ? { item_id: payload.item_id } : {},
|
|
16651
|
+
...typeof payload.produced_count === "number" ? { produced_count: payload.produced_count } : {},
|
|
16652
|
+
...payload.diagnostic ? { diagnostic: payload.diagnostic } : {},
|
|
16653
|
+
settled_at: payload.settled_at ?? ""
|
|
16654
|
+
});
|
|
16655
|
+
}
|
|
16656
|
+
return [...nachId.values()];
|
|
16657
|
+
}
|
|
16658
|
+
function jobsForPromptRun(events, runId) {
|
|
16659
|
+
return collectImageJobs(events).filter((j) => j.prompt_run_id === runId);
|
|
16660
|
+
}
|
|
16661
|
+
function findImageJob(events, jobId) {
|
|
16662
|
+
return collectImageJobs(events).find((j) => j.job_id === jobId) ?? null;
|
|
16663
|
+
}
|
|
16664
|
+
function claimWon(job, claimId) {
|
|
16665
|
+
return Boolean(job && job.claim && job.claim.claim_id === claimId);
|
|
16666
|
+
}
|
|
16667
|
+
function generationBatchIdOf(jobId) {
|
|
16668
|
+
return `job-${jobId}`;
|
|
16669
|
+
}
|
|
16670
|
+
|
|
16671
|
+
// src/lib/szenen/imageJobPort.ts
|
|
16672
|
+
var ANSPRUCH_VERSUCHE = 4;
|
|
16673
|
+
function fehler4(error, aktion) {
|
|
16674
|
+
return {
|
|
16675
|
+
message: `${aktion}: ${error instanceof Error ? error.message : String(error)}`
|
|
16676
|
+
};
|
|
16677
|
+
}
|
|
16678
|
+
function createImageJobPort(options) {
|
|
16679
|
+
const { channel, imageGeneration, storeId, clientId } = options;
|
|
16680
|
+
const newClaimId = options.newClaimId ?? (() => crypto.randomUUID());
|
|
16681
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
16682
|
+
return {
|
|
16683
|
+
async listJobs(runId) {
|
|
16684
|
+
try {
|
|
16685
|
+
return { kind: "ok", value: jobsForPromptRun(await channel.readEvents(), runId) };
|
|
16686
|
+
} catch (error) {
|
|
16687
|
+
return {
|
|
16688
|
+
kind: "error",
|
|
16689
|
+
...fehler4(error, `Die Bildauftraege des Laufs ${runId} konnten nicht gelesen werden`),
|
|
16690
|
+
retryable: true
|
|
16691
|
+
};
|
|
16692
|
+
}
|
|
16693
|
+
},
|
|
16694
|
+
async claimAndRun(jobId) {
|
|
16695
|
+
let letzterGrund = "";
|
|
16696
|
+
for (let versuch = 1; versuch <= ANSPRUCH_VERSUCHE; versuch += 1) {
|
|
16697
|
+
let kopf;
|
|
16698
|
+
try {
|
|
16699
|
+
kopf = await channel.readHead();
|
|
16700
|
+
} catch (error) {
|
|
16701
|
+
return {
|
|
16702
|
+
kind: "error",
|
|
16703
|
+
...fehler4(error, `Der Kopf-Commit des Bestands war nicht lesbar; Bildauftrag ${jobId} wurde nicht uebernommen`),
|
|
16704
|
+
retryable: true
|
|
16705
|
+
};
|
|
16706
|
+
}
|
|
16707
|
+
let auftrag;
|
|
16708
|
+
try {
|
|
16709
|
+
auftrag = findImageJob(await channel.readEventsAt(kopf), jobId);
|
|
16710
|
+
} catch (error) {
|
|
16711
|
+
return {
|
|
16712
|
+
kind: "error",
|
|
16713
|
+
...fehler4(error, `Der Bildauftrag ${jobId} konnte nicht gelesen werden`),
|
|
16714
|
+
retryable: true
|
|
16715
|
+
};
|
|
16716
|
+
}
|
|
16717
|
+
if (!auftrag) {
|
|
16718
|
+
return {
|
|
16719
|
+
kind: "rejected",
|
|
16720
|
+
message: `Der Bildauftrag ${jobId} steht nicht im autoritativen Log dieses Bestands.`
|
|
16721
|
+
};
|
|
16722
|
+
}
|
|
16723
|
+
if (auftrag.store_id !== storeId) {
|
|
16724
|
+
return {
|
|
16725
|
+
kind: "rejected",
|
|
16726
|
+
message: `Der Bildauftrag ${jobId} gilt fuer den Bestand "${auftrag.store_id}", dieser Host arbeitet auf "${storeId}". Nutzer, Umgebung und Bestand muessen zusammenpassen.`
|
|
16727
|
+
};
|
|
16728
|
+
}
|
|
16729
|
+
if (auftrag.status !== "pending") {
|
|
16730
|
+
return {
|
|
16731
|
+
kind: "lost",
|
|
16732
|
+
job: auftrag,
|
|
16733
|
+
message: `Der Bildauftrag ${jobId} ist bereits "${auftrag.status}"` + (auftrag.claim ? ` (Client ${auftrag.claim.client_id})` : "") + ". Er wird nicht erneut ausgefuehrt \u2014 ein zweiter Lauf waere ein zweites Mal bezahlte Rechenzeit."
|
|
16734
|
+
};
|
|
16735
|
+
}
|
|
16736
|
+
const anspruch = {
|
|
16737
|
+
job_id: jobId,
|
|
16738
|
+
claim_id: newClaimId(),
|
|
16739
|
+
client_id: clientId,
|
|
16740
|
+
claimed_at: now().toISOString()
|
|
16741
|
+
};
|
|
16742
|
+
try {
|
|
16743
|
+
await channel.appendEventAt(
|
|
16744
|
+
SZENEN_EVENT_TYPES.imageJobClaimed,
|
|
16745
|
+
anspruch,
|
|
16746
|
+
kopf
|
|
16747
|
+
);
|
|
16748
|
+
} catch (error) {
|
|
16749
|
+
if (istPreconditionFehler(error)) {
|
|
16750
|
+
letzterGrund = error instanceof Error ? error.message : String(error);
|
|
16751
|
+
continue;
|
|
16752
|
+
}
|
|
16753
|
+
return {
|
|
16754
|
+
kind: "error",
|
|
16755
|
+
...fehler4(error, `Der Anspruch auf Bildauftrag ${jobId} konnte nicht angehaengt werden`),
|
|
16756
|
+
retryable: true
|
|
16757
|
+
};
|
|
16758
|
+
}
|
|
16759
|
+
const gewonnen = {
|
|
16760
|
+
...auftrag,
|
|
16761
|
+
status: "claimed",
|
|
16762
|
+
claim: anspruch,
|
|
16763
|
+
claim_attempts: [...auftrag.claim_attempts, anspruch]
|
|
16764
|
+
};
|
|
16765
|
+
return await fuehreAus(gewonnen, anspruch, jobId);
|
|
16766
|
+
}
|
|
16767
|
+
return {
|
|
16768
|
+
kind: "error",
|
|
16769
|
+
message: `Der Bildauftrag ${jobId} liess sich in ${ANSPRUCH_VERSUCHE} Versuchen nicht exklusiv uebernehmen \u2014 der Bestand wird gerade von anderer Seite beschrieben (${letzterGrund}). Es wurde nichts ausgefuehrt und nichts bezahlt; ein neuer Versuch ist gefahrlos.`,
|
|
16770
|
+
retryable: true
|
|
16771
|
+
};
|
|
16772
|
+
}
|
|
16773
|
+
};
|
|
16774
|
+
async function fuehreAus(gewonnen, anspruch, jobId) {
|
|
16775
|
+
const ergebnis = await imageGeneration.startFromPromptRun({
|
|
16776
|
+
runId: gewonnen.prompt_run_id,
|
|
16777
|
+
generationBatchId: generationBatchIdOf(jobId),
|
|
16778
|
+
executionTargetModelKey: gewonnen.execution_target_model_key,
|
|
16779
|
+
executionProfileId: gewonnen.execution_profile_id,
|
|
16780
|
+
imageCount: gewonnen.image_count,
|
|
16781
|
+
...gewonnen.aspect_ratio ? { aspectRatio: gewonnen.aspect_ratio } : {}
|
|
16782
|
+
});
|
|
16783
|
+
const abschluss = {
|
|
16784
|
+
job_id: jobId,
|
|
16785
|
+
claim_id: anspruch.claim_id,
|
|
16786
|
+
...abschlussFelder(ergebnis, jobId),
|
|
16787
|
+
settled_at: now().toISOString()
|
|
16788
|
+
};
|
|
16789
|
+
try {
|
|
16790
|
+
await channel.appendEvent(SZENEN_EVENT_TYPES.imageJobSettled, abschluss);
|
|
16791
|
+
} catch {
|
|
16792
|
+
}
|
|
16793
|
+
const gelesen = await channel.readEvents().catch(() => null);
|
|
16794
|
+
return {
|
|
16795
|
+
kind: "ok",
|
|
16796
|
+
job: (gelesen && findImageJob(gelesen, jobId)) ?? {
|
|
16797
|
+
...gewonnen,
|
|
16798
|
+
status: abschluss.status,
|
|
16799
|
+
...abschluss.generation_batch_id ? { generation_batch_id: abschluss.generation_batch_id } : {},
|
|
16800
|
+
...abschluss.diagnostic ? { diagnostic: abschluss.diagnostic } : {},
|
|
16801
|
+
settled_at: abschluss.settled_at
|
|
16802
|
+
}
|
|
16803
|
+
};
|
|
16804
|
+
}
|
|
16805
|
+
}
|
|
16806
|
+
function istPreconditionFehler(error) {
|
|
16807
|
+
return Boolean(error && typeof error === "object" && error.name === "HfPreconditionFailed");
|
|
16808
|
+
}
|
|
16809
|
+
function abschlussFelder(ergebnis, jobId) {
|
|
16810
|
+
const batchId = generationBatchIdOf(jobId);
|
|
16811
|
+
switch (ergebnis.kind) {
|
|
16812
|
+
case "succeeded":
|
|
16813
|
+
return {
|
|
16814
|
+
status: "succeeded",
|
|
16815
|
+
generation_batch_id: batchId,
|
|
16816
|
+
item_id: ergebnis.batch.item_id,
|
|
16817
|
+
produced_count: ergebnis.batch.images.length
|
|
16818
|
+
};
|
|
16819
|
+
case "partial":
|
|
16820
|
+
return {
|
|
16821
|
+
status: "partial",
|
|
16822
|
+
generation_batch_id: batchId,
|
|
16823
|
+
item_id: ergebnis.batch.item_id,
|
|
16824
|
+
produced_count: ergebnis.produced,
|
|
16825
|
+
diagnostic: `Angefordert ${ergebnis.requested}, geliefert ${ergebnis.produced}.`
|
|
16826
|
+
};
|
|
16827
|
+
case "failed":
|
|
16828
|
+
return { status: "failed", produced_count: 0, diagnostic: ergebnis.message };
|
|
16829
|
+
case "unsupported":
|
|
16830
|
+
return {
|
|
16831
|
+
status: "rejected",
|
|
16832
|
+
produced_count: 0,
|
|
16833
|
+
diagnostic: `Zielmodell "${ergebnis.targetModelKey}" ist in dieser Umgebung nicht ausfuehrbar: ${ergebnis.reason}`
|
|
16834
|
+
};
|
|
16835
|
+
default:
|
|
16836
|
+
return { status: "rejected", produced_count: 0, diagnostic: ergebnis.message };
|
|
16837
|
+
}
|
|
16838
|
+
}
|
|
16839
|
+
|
|
16210
16840
|
// src/lib/szenen/flowViewPort.ts
|
|
16211
16841
|
var defaultContext = {
|
|
16212
16842
|
async chainFor(contextId) {
|
|
@@ -16305,14 +16935,33 @@ function createFlowSzenenViewPort(options) {
|
|
|
16305
16935
|
},
|
|
16306
16936
|
...options.now ? { now: options.now } : {}
|
|
16307
16937
|
}) : void 0;
|
|
16938
|
+
const kanalMitRevision = typeof channel.readHead === "function" && typeof channel.readEventsAt === "function" && typeof channel.appendEventAt === "function";
|
|
16939
|
+
const imageJobs = imageGeneration && hatHfZugangsdaten && kanalMitRevision ? createImageJobPort({
|
|
16940
|
+
channel,
|
|
16941
|
+
imageGeneration,
|
|
16942
|
+
// Dieselbe Form, in der der Server seinen Bestand kennt (`storeId`).
|
|
16943
|
+
storeId: `hf:${options.namespace}`,
|
|
16944
|
+
clientId: options.imageJobClientId ?? crypto.randomUUID(),
|
|
16945
|
+
...options.newImageJobClaimId ? { newClaimId: options.newImageJobClaimId } : {},
|
|
16946
|
+
...options.now ? { now: options.now } : {}
|
|
16947
|
+
}) : void 0;
|
|
16308
16948
|
const port = { elements, context, runs, derivation, imageBatchHistory };
|
|
16309
16949
|
if (imageLibrary) port.imageLibrary = imageLibrary;
|
|
16310
16950
|
if (imageBatchWrite) port.imageBatchWrite = imageBatchWrite;
|
|
16311
16951
|
if (imageGeneration) port.imageGeneration = imageGeneration;
|
|
16952
|
+
if (imageJobs) port.imageJobs = imageJobs;
|
|
16312
16953
|
return port;
|
|
16313
16954
|
}
|
|
16314
16955
|
|
|
16315
16956
|
export {
|
|
16957
|
+
SZENEN_SNAPSHOT_SCHEMA_VERSION,
|
|
16958
|
+
SZENEN_SNAPSHOT_PREFIX,
|
|
16959
|
+
SZENEN_SNAPSHOT_FILE,
|
|
16960
|
+
szenenSnapshotFilename,
|
|
16961
|
+
istSzenenSnapshotDatei,
|
|
16962
|
+
buildSzenenSnapshot,
|
|
16963
|
+
parseSzenenSnapshot,
|
|
16964
|
+
mergeSnapshotAndPending,
|
|
16316
16965
|
hfListDir,
|
|
16317
16966
|
hfDownloadBinaryByPath,
|
|
16318
16967
|
hfUploadSmallFile,
|
|
@@ -16473,6 +17122,9 @@ export {
|
|
|
16473
17122
|
createHfImageStore,
|
|
16474
17123
|
locatorTeile,
|
|
16475
17124
|
gruppiereZuItems,
|
|
17125
|
+
locatorAus,
|
|
17126
|
+
mimeTypAus,
|
|
17127
|
+
itemsAusBestand,
|
|
16476
17128
|
createHfImageLibrary,
|
|
16477
17129
|
FLOW_EXECUTION_PATH,
|
|
16478
17130
|
createFlowImageProvider,
|
|
@@ -16487,5 +17139,11 @@ export {
|
|
|
16487
17139
|
createHfImageGenerationBatchPersistence,
|
|
16488
17140
|
createHfImageBatchWrite,
|
|
16489
17141
|
createImageGenerationPort,
|
|
17142
|
+
collectImageJobs,
|
|
17143
|
+
jobsForPromptRun,
|
|
17144
|
+
findImageJob,
|
|
17145
|
+
claimWon,
|
|
17146
|
+
generationBatchIdOf,
|
|
17147
|
+
createImageJobPort,
|
|
16490
17148
|
createFlowSzenenViewPort
|
|
16491
17149
|
};
|