@rslsp1/fa-app-tools 7.2.0 → 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-5QA6DTJ6.mjs → chunk-HUGUH4VB.mjs} +597 -46
- package/dist/index.d.mts +26 -4
- package/dist/index.d.ts +26 -4
- package/dist/index.js +548 -47
- package/dist/index.mjs +2 -2
- package/dist/szenen.d.mts +485 -2
- package/dist/szenen.d.ts +485 -2
- package/dist/szenen.js +760 -68
- package/dist/szenen.mjs +142 -1
- package/package.json +6 -2
|
@@ -96,9 +96,9 @@ function bildEndungFuer(mimeType) {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
// src/lib/hfEventLog.ts
|
|
99
|
-
function treeUrl(namespace, subdir = "") {
|
|
99
|
+
function treeUrl(namespace, subdir = "", revision = "main") {
|
|
100
100
|
const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
|
|
101
|
-
return `${HF_BASE}/api/datasets/${HF_REPO}/tree
|
|
101
|
+
return `${HF_BASE}/api/datasets/${HF_REPO}/tree/${encodeURIComponent(revision)}${parts ? "/" + parts : ""}`;
|
|
102
102
|
}
|
|
103
103
|
var DROSSEL_VERSUCHE = 4;
|
|
104
104
|
var DROSSEL_BASIS_MS = 400;
|
|
@@ -131,8 +131,8 @@ async function mitGeduld(anfrage) {
|
|
|
131
131
|
}
|
|
132
132
|
return letzte;
|
|
133
133
|
}
|
|
134
|
-
async function hfListDir(namespace, subdir, token) {
|
|
135
|
-
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), {
|
|
136
136
|
headers: { Authorization: `Bearer ${token}` }
|
|
137
137
|
}));
|
|
138
138
|
if (res.status === 404) return [];
|
|
@@ -161,17 +161,46 @@ function toBase64(text) {
|
|
|
161
161
|
bytes.forEach((b) => binary += String.fromCharCode(b));
|
|
162
162
|
return btoa(binary);
|
|
163
163
|
}
|
|
164
|
-
|
|
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;
|
|
165
189
|
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
166
190
|
method: "POST",
|
|
167
191
|
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
168
192
|
body: [
|
|
169
|
-
JSON.stringify({ key: "header", value:
|
|
193
|
+
JSON.stringify({ key: "header", value: kopf }),
|
|
170
194
|
JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: toBase64(content) } })
|
|
171
195
|
].join("\n")
|
|
172
196
|
});
|
|
173
197
|
if (!res.ok) {
|
|
174
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
|
+
}
|
|
175
204
|
throw new Error(`HF upload failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
|
|
176
205
|
}
|
|
177
206
|
}
|
|
@@ -221,8 +250,8 @@ async function mitBegrenzterParallelitaet(count, limit, task) {
|
|
|
221
250
|
await Promise.all(Array.from({ length: Math.min(limit, count) }, worker));
|
|
222
251
|
if (fehlerAufgetreten) throw ersterFehler;
|
|
223
252
|
}
|
|
224
|
-
async function loadPendingEvents(namespace, token, sinceTs, knownPaths) {
|
|
225
|
-
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);
|
|
226
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));
|
|
227
256
|
if (!pending.length) return [];
|
|
228
257
|
const raw = new Array(pending.length);
|
|
@@ -267,7 +296,13 @@ async function writeHFEvent(namespace, token, type, payload, prevTs, options) {
|
|
|
267
296
|
const suffix = options?.ordered ? `${String(++writeSeq % 1e6).padStart(6, "0")}${uuid}` : uuid;
|
|
268
297
|
const filename = `${tsToFilename(ts)}_${suffix}.json`;
|
|
269
298
|
const repoPath = `${namespace}events/${filename}`;
|
|
270
|
-
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
|
+
);
|
|
271
306
|
return event;
|
|
272
307
|
}
|
|
273
308
|
|
|
@@ -407,7 +442,8 @@ function capabilitiesOf(port) {
|
|
|
407
442
|
imageBatchHistory: typeof port.imageBatchHistory?.listBatchesForPromptRun === "function",
|
|
408
443
|
imageOrderHistory: typeof port.imageBatchHistory?.listOrdersForPromptRun === "function",
|
|
409
444
|
imageBatchWrite: typeof port.imageBatchWrite?.importBatch === "function",
|
|
410
|
-
imageGeneration: typeof port.imageGeneration?.startFromPromptRun === "function"
|
|
445
|
+
imageGeneration: typeof port.imageGeneration?.startFromPromptRun === "function",
|
|
446
|
+
imageJobs: typeof port.imageJobs?.claimAndRun === "function"
|
|
411
447
|
};
|
|
412
448
|
}
|
|
413
449
|
function unsupportedReason(capability) {
|
|
@@ -3557,13 +3593,13 @@ function noticeFor(ergebnis, was) {
|
|
|
3557
3593
|
};
|
|
3558
3594
|
}
|
|
3559
3595
|
case "rejected": {
|
|
3560
|
-
const
|
|
3596
|
+
const fehler5 = ergebnis.errors ?? [];
|
|
3561
3597
|
const ungeprueft = ergebnis.unchecked ?? [];
|
|
3562
3598
|
return {
|
|
3563
3599
|
tone: "rejected",
|
|
3564
|
-
text: `${was}: abgelehnt \u2014 ${
|
|
3600
|
+
text: `${was}: abgelehnt \u2014 ${fehler5.length} Befund${fehler5.length === 1 ? "" : "e"}.`,
|
|
3565
3601
|
details: [
|
|
3566
|
-
...
|
|
3602
|
+
...fehler5.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
|
|
3567
3603
|
// Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
|
|
3568
3604
|
// sondern ungeprüft.
|
|
3569
3605
|
...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
|
|
@@ -5337,7 +5373,60 @@ var SZENEN_EVENT_TYPES = {
|
|
|
5337
5373
|
* Der Elementreducer ignoriert auch diesen Typ (`default:`-Zweig); gelesen
|
|
5338
5374
|
* wird mit `collectImageBatches` (`imageBatchStore.ts`).
|
|
5339
5375
|
*/
|
|
5340
|
-
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"
|
|
5341
5430
|
};
|
|
5342
5431
|
function sortForReplay(events) {
|
|
5343
5432
|
return [...events].sort((a, b) => {
|
|
@@ -5700,9 +5789,9 @@ async function leseBilderAusZip(datei) {
|
|
|
5700
5789
|
);
|
|
5701
5790
|
}
|
|
5702
5791
|
const { default: JSZip2 } = await import("jszip");
|
|
5703
|
-
const archiv = await JSZip2.loadAsync(datei).catch((
|
|
5792
|
+
const archiv = await JSZip2.loadAsync(datei).catch((fehler5) => {
|
|
5704
5793
|
throw new Error(
|
|
5705
|
-
`${name} konnte nicht als ZIP gelesen werden: ${
|
|
5794
|
+
`${name} konnte nicht als ZIP gelesen werden: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
|
|
5706
5795
|
);
|
|
5707
5796
|
});
|
|
5708
5797
|
const pfade = [];
|
|
@@ -8581,7 +8670,7 @@ var ERKLAERUNG = {
|
|
|
8581
8670
|
};
|
|
8582
8671
|
var CapabilityPanel = ({ port, elementId, setupPayload }) => {
|
|
8583
8672
|
const [bericht, setBericht] = useState5(null);
|
|
8584
|
-
const [
|
|
8673
|
+
const [fehler5, setFehler] = useState5(null);
|
|
8585
8674
|
useEffect2(() => {
|
|
8586
8675
|
let abgemeldet = false;
|
|
8587
8676
|
setBericht(null);
|
|
@@ -8603,10 +8692,10 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
|
|
|
8603
8692
|
abgemeldet = true;
|
|
8604
8693
|
};
|
|
8605
8694
|
}, [port, elementId, setupPayload]);
|
|
8606
|
-
if (
|
|
8695
|
+
if (fehler5) {
|
|
8607
8696
|
return /* @__PURE__ */ jsxs4("p", { "data-testid": "capability-fehler", role: "alert", children: [
|
|
8608
8697
|
"F\xE4higkeitsbericht nicht erstellbar: ",
|
|
8609
|
-
|
|
8698
|
+
fehler5
|
|
8610
8699
|
] });
|
|
8611
8700
|
}
|
|
8612
8701
|
if (!bericht) return /* @__PURE__ */ jsx4("p", { "data-testid": "capability-laden", children: "F\xE4higkeitsbericht wird erstellt \u2026" });
|
|
@@ -9013,6 +9102,7 @@ var RunImagesPanel = ({ port, lauf }) => {
|
|
|
9013
9102
|
const neuLesen = () => setNachladen((n) => n + 1);
|
|
9014
9103
|
const [erzeugenOffen, setErzeugenOffen] = useState8(null);
|
|
9015
9104
|
const [zuordnenOffen, setZuordnenOffen] = useState8(null);
|
|
9105
|
+
const [chatAuftraegeOffen, setChatAuftraegeOffen] = useState8(null);
|
|
9016
9106
|
const quelle = sourceOf(lauf);
|
|
9017
9107
|
const [wahlPhase] = usePortQuery(
|
|
9018
9108
|
() => port.imageGeneration ? port.imageGeneration.imageModelChoiceForRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: null }),
|
|
@@ -9027,10 +9117,17 @@ var RunImagesPanel = ({ port, lauf }) => {
|
|
|
9027
9117
|
() => port.imageBatchHistory ? port.imageBatchHistory.listBatchesForPromptRun(lauf.run_id) : Promise.resolve({ kind: "ok", value: [] }),
|
|
9028
9118
|
[port, lauf.run_id, nachladen]
|
|
9029
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
|
+
);
|
|
9030
9124
|
const auftraege = auftraegePhase.status === "ready" ? auftraegePhase.value : [];
|
|
9031
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;
|
|
9032
9128
|
const auffaellige = auftraege.filter((a) => a.status !== "succeeded");
|
|
9033
9129
|
const erzeugenAuf = erzeugenOffen ?? batches.length === 0;
|
|
9130
|
+
const chatAuftraegeAuf = chatAuftraegeOffen ?? offeneChatAuftraege > 0;
|
|
9034
9131
|
return /* @__PURE__ */ jsxs8("div", { className: "szenen-bilder", "data-testid": `bilder-${lauf.run_id}`, children: [
|
|
9035
9132
|
/* @__PURE__ */ jsxs8("section", { className: "szenen-bilder__eingang", "data-testid": `eingang-${lauf.run_id}`, children: [
|
|
9036
9133
|
/* @__PURE__ */ jsx8("h5", { children: "Eingang \u2014 woraus dieser Prompt entstand" }),
|
|
@@ -9151,6 +9248,43 @@ var RunImagesPanel = ({ port, lauf }) => {
|
|
|
9151
9248
|
}
|
|
9152
9249
|
)
|
|
9153
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
|
+
),
|
|
9154
9288
|
/* @__PURE__ */ jsxs8(
|
|
9155
9289
|
"details",
|
|
9156
9290
|
{
|
|
@@ -9334,7 +9468,7 @@ function useBildQuellen(port, bilder) {
|
|
|
9334
9468
|
erneutVersuchen: () => setVersuch((n) => n + 1)
|
|
9335
9469
|
};
|
|
9336
9470
|
}
|
|
9337
|
-
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: [
|
|
9338
9472
|
/* @__PURE__ */ jsx8("code", { children: bild.media_ref }),
|
|
9339
9473
|
/* @__PURE__ */ jsxs8("span", { children: [
|
|
9340
9474
|
"Position ",
|
|
@@ -9347,7 +9481,7 @@ var BildDetails = ({ bild, fehler: fehler4, aufloesbar }) => /* @__PURE__ */ jsx
|
|
|
9347
9481
|
"\xD7",
|
|
9348
9482
|
bild.height
|
|
9349
9483
|
] }) : null,
|
|
9350
|
-
|
|
9484
|
+
fehler5 && /* @__PURE__ */ jsx8("em", { "data-testid": `batchbild-fehler-${bild.media_ref}`, children: fehler5 }),
|
|
9351
9485
|
!aufloesbar && // Capability-Grenze, kein Fehler: Dieser Host kann Batches lesen,
|
|
9352
9486
|
// aber keine Bilder auflösen (E). Kein erfundener Fallback auf eine
|
|
9353
9487
|
// Serverroute (A6).
|
|
@@ -9363,18 +9497,18 @@ var HERKUNFT_KURZ = {
|
|
|
9363
9497
|
external_import: "extern importiert"
|
|
9364
9498
|
};
|
|
9365
9499
|
var BatchKachel = ({ port, batch }) => {
|
|
9366
|
-
const { quellen, fehler:
|
|
9500
|
+
const { quellen, fehler: fehler5, wiederholbar, erneutVersuchen } = useBildQuellen(port, batch.images);
|
|
9367
9501
|
const aufloesbar = Boolean(port.imageLibrary);
|
|
9368
9502
|
const bilder = batch.images.map((bild) => ({
|
|
9369
9503
|
id: bild.media_ref,
|
|
9370
9504
|
quelle: quellen.get(bild.media_ref) ?? null,
|
|
9371
9505
|
titel: `Bild ${bild.position + 1}`,
|
|
9372
|
-
...
|
|
9506
|
+
...fehler5.get(bild.media_ref) ? { leerText: "Nicht ladbar" } : {},
|
|
9373
9507
|
details: /* @__PURE__ */ jsx8(
|
|
9374
9508
|
BildDetails,
|
|
9375
9509
|
{
|
|
9376
9510
|
bild,
|
|
9377
|
-
fehler:
|
|
9511
|
+
fehler: fehler5.get(bild.media_ref) ?? null,
|
|
9378
9512
|
aufloesbar
|
|
9379
9513
|
}
|
|
9380
9514
|
)
|
|
@@ -9434,7 +9568,7 @@ var BatchKachel = ({ port, batch }) => {
|
|
|
9434
9568
|
BildDetails,
|
|
9435
9569
|
{
|
|
9436
9570
|
bild,
|
|
9437
|
-
fehler:
|
|
9571
|
+
fehler: fehler5.get(bild.media_ref) ?? null,
|
|
9438
9572
|
aufloesbar
|
|
9439
9573
|
}
|
|
9440
9574
|
) }, bild.media_ref)) })
|
|
@@ -9703,11 +9837,11 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
9703
9837
|
setSeedListe(DEFAULT_SEED_VORLAGE.seeds);
|
|
9704
9838
|
setVorlageKey(DEFAULT_VORLAGE_KEY);
|
|
9705
9839
|
}
|
|
9706
|
-
} catch (
|
|
9840
|
+
} catch (fehler5) {
|
|
9707
9841
|
if (veraltet()) return;
|
|
9708
9842
|
setNotiz({
|
|
9709
9843
|
tone: "error",
|
|
9710
|
-
text:
|
|
9844
|
+
text: fehler5 instanceof Error ? fehler5.message : String(fehler5)
|
|
9711
9845
|
});
|
|
9712
9846
|
}
|
|
9713
9847
|
if (veraltet()) return;
|
|
@@ -9726,10 +9860,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
9726
9860
|
setNotiz(
|
|
9727
9861
|
ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
|
|
9728
9862
|
);
|
|
9729
|
-
} catch (
|
|
9863
|
+
} catch (fehler5) {
|
|
9730
9864
|
setNotiz({
|
|
9731
9865
|
tone: "error",
|
|
9732
|
-
text: `Externe Bilder importieren: ${
|
|
9866
|
+
text: `Externe Bilder importieren: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
|
|
9733
9867
|
});
|
|
9734
9868
|
}
|
|
9735
9869
|
setLaeuft(false);
|
|
@@ -9751,10 +9885,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
|
|
|
9751
9885
|
...seed !== void 0 ? { seed } : {}
|
|
9752
9886
|
});
|
|
9753
9887
|
}
|
|
9754
|
-
} catch (
|
|
9888
|
+
} catch (fehler5) {
|
|
9755
9889
|
setNotiz({
|
|
9756
9890
|
tone: "error",
|
|
9757
|
-
text: `Externe Bilder importieren: ${
|
|
9891
|
+
text: `Externe Bilder importieren: ${fehler5 instanceof Error ? fehler5.message : String(fehler5)}`
|
|
9758
9892
|
});
|
|
9759
9893
|
return;
|
|
9760
9894
|
}
|
|
@@ -10090,6 +10224,101 @@ function readAsBase64(datei) {
|
|
|
10090
10224
|
leser.readAsDataURL(datei);
|
|
10091
10225
|
});
|
|
10092
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
|
+
};
|
|
10093
10322
|
|
|
10094
10323
|
// src/features/szenen/areas/RunsPanel.tsx
|
|
10095
10324
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
@@ -13796,7 +14025,7 @@ function createHfEventChannel(options) {
|
|
|
13796
14025
|
let syncInFlight = null;
|
|
13797
14026
|
let snapshot = null;
|
|
13798
14027
|
let snapshotGeladen = ohneSnapshot;
|
|
13799
|
-
const refresh = async () => {
|
|
14028
|
+
const refresh = async (revision = "main") => {
|
|
13800
14029
|
if (!snapshotGeladen) {
|
|
13801
14030
|
snapshot = await loadLatestSzenenSnapshot(namespace, token);
|
|
13802
14031
|
snapshotGeladen = true;
|
|
@@ -13804,7 +14033,7 @@ function createHfEventChannel(options) {
|
|
|
13804
14033
|
knownPaths.add(`${namespace}events/${name}`);
|
|
13805
14034
|
}
|
|
13806
14035
|
}
|
|
13807
|
-
const neue = await loadPendingEvents(namespace, token, sinceTs, knownPaths);
|
|
14036
|
+
const neue = await loadPendingEvents(namespace, token, sinceTs, knownPaths, revision);
|
|
13808
14037
|
if (neue.length) cachedRawEvents = [...cachedRawEvents, ...neue];
|
|
13809
14038
|
};
|
|
13810
14039
|
const sync = () => {
|
|
@@ -13815,16 +14044,34 @@ function createHfEventChannel(options) {
|
|
|
13815
14044
|
}
|
|
13816
14045
|
return syncInFlight;
|
|
13817
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
|
+
};
|
|
13818
14056
|
return {
|
|
13819
14057
|
async readEvents() {
|
|
13820
14058
|
await sync();
|
|
13821
|
-
|
|
13822
|
-
|
|
13823
|
-
|
|
13824
|
-
|
|
13825
|
-
|
|
13826
|
-
|
|
13827
|
-
|
|
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 });
|
|
13828
14075
|
},
|
|
13829
14076
|
async appendEvent(type, payload) {
|
|
13830
14077
|
if (!type.startsWith(SZENEN_EVENT_PREFIX)) {
|
|
@@ -14008,11 +14255,11 @@ async function resolveShotPayload(input) {
|
|
|
14008
14255
|
};
|
|
14009
14256
|
}
|
|
14010
14257
|
const shot = shotPayload;
|
|
14011
|
-
for (const
|
|
14258
|
+
for (const fehler5 of ohneKettenReferenz(
|
|
14012
14259
|
validatePayload("shot", shotPayload).errors,
|
|
14013
14260
|
chain ? "scene_setup_ref" : null
|
|
14014
14261
|
)) {
|
|
14015
|
-
diagnostics.push(diagnosticOfSchemaError(
|
|
14262
|
+
diagnostics.push(diagnosticOfSchemaError(fehler5, `Shot ${shotElementId}`));
|
|
14016
14263
|
}
|
|
14017
14264
|
let setup = null;
|
|
14018
14265
|
let report = null;
|
|
@@ -14124,8 +14371,8 @@ async function resolveScenePayload(input) {
|
|
|
14124
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)."
|
|
14125
14372
|
];
|
|
14126
14373
|
const diagnostics = [];
|
|
14127
|
-
for (const
|
|
14128
|
-
diagnostics.push(diagnosticOfSchemaError(
|
|
14374
|
+
for (const fehler5 of validatePayload("scene", scenePayload).errors) {
|
|
14375
|
+
diagnostics.push(diagnosticOfSchemaError(fehler5, `Szene ${sceneElementId}`));
|
|
14129
14376
|
}
|
|
14130
14377
|
const faehig = sceneOnlyCapabilities(scenePayload);
|
|
14131
14378
|
return komponiere({
|
|
@@ -15646,6 +15893,44 @@ function gruppiereZuItems(bilder) {
|
|
|
15646
15893
|
items.sort((a, b) => b.juengstes - a.juengstes);
|
|
15647
15894
|
return items.map((e) => e.item);
|
|
15648
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
|
+
}
|
|
15649
15934
|
|
|
15650
15935
|
// src/lib/szenen/hfImageLibrary.ts
|
|
15651
15936
|
function fehler2(nachricht, retryable = false) {
|
|
@@ -15776,8 +16061,8 @@ function createFlowImageProvider(generateImage) {
|
|
|
15776
16061
|
// Kein Seed: weder als Eingabe dokumentiert noch als Ausgabe
|
|
15777
16062
|
// geliefert — nichts wird erfunden (D-107).
|
|
15778
16063
|
});
|
|
15779
|
-
} catch (
|
|
15780
|
-
ersterFehler =
|
|
16064
|
+
} catch (fehler5) {
|
|
16065
|
+
ersterFehler = fehler5 instanceof Error ? fehler5.message : String(fehler5);
|
|
15781
16066
|
break;
|
|
15782
16067
|
}
|
|
15783
16068
|
}
|
|
@@ -16306,6 +16591,252 @@ function createImageGenerationPort(options) {
|
|
|
16306
16591
|
};
|
|
16307
16592
|
}
|
|
16308
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
|
+
|
|
16309
16840
|
// src/lib/szenen/flowViewPort.ts
|
|
16310
16841
|
var defaultContext = {
|
|
16311
16842
|
async chainFor(contextId) {
|
|
@@ -16404,10 +16935,21 @@ function createFlowSzenenViewPort(options) {
|
|
|
16404
16935
|
},
|
|
16405
16936
|
...options.now ? { now: options.now } : {}
|
|
16406
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;
|
|
16407
16948
|
const port = { elements, context, runs, derivation, imageBatchHistory };
|
|
16408
16949
|
if (imageLibrary) port.imageLibrary = imageLibrary;
|
|
16409
16950
|
if (imageBatchWrite) port.imageBatchWrite = imageBatchWrite;
|
|
16410
16951
|
if (imageGeneration) port.imageGeneration = imageGeneration;
|
|
16952
|
+
if (imageJobs) port.imageJobs = imageJobs;
|
|
16411
16953
|
return port;
|
|
16412
16954
|
}
|
|
16413
16955
|
|
|
@@ -16580,6 +17122,9 @@ export {
|
|
|
16580
17122
|
createHfImageStore,
|
|
16581
17123
|
locatorTeile,
|
|
16582
17124
|
gruppiereZuItems,
|
|
17125
|
+
locatorAus,
|
|
17126
|
+
mimeTypAus,
|
|
17127
|
+
itemsAusBestand,
|
|
16583
17128
|
createHfImageLibrary,
|
|
16584
17129
|
FLOW_EXECUTION_PATH,
|
|
16585
17130
|
createFlowImageProvider,
|
|
@@ -16594,5 +17139,11 @@ export {
|
|
|
16594
17139
|
createHfImageGenerationBatchPersistence,
|
|
16595
17140
|
createHfImageBatchWrite,
|
|
16596
17141
|
createImageGenerationPort,
|
|
17142
|
+
collectImageJobs,
|
|
17143
|
+
jobsForPromptRun,
|
|
17144
|
+
findImageJob,
|
|
17145
|
+
claimWon,
|
|
17146
|
+
generationBatchIdOf,
|
|
17147
|
+
createImageJobPort,
|
|
16597
17148
|
createFlowSzenenViewPort
|
|
16598
17149
|
};
|