@rslsp1/fa-app-tools 2.0.73 → 2.0.81
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-43TWCAG5.mjs +1299 -0
- package/dist/chunk-FUY2OCVC.mjs +157 -0
- package/dist/chunk-GEGJD43Y.mjs +11310 -0
- package/dist/{chunk-PVLOJQWS.mjs → chunk-VBWQP7PO.mjs} +113 -66
- package/dist/contracts-DdYOMBl9.d.mts +166 -0
- package/dist/contracts-DdYOMBl9.d.ts +166 -0
- package/dist/flowTextGenerationPort-CeDUFutP.d.mts +73 -0
- package/dist/flowTextGenerationPort-KHjCGt_S.d.ts +73 -0
- package/dist/{hfStateService-E5MHAF3M.mjs → hfStateService-WKQLNDHO.mjs} +3 -1
- package/dist/index.d.mts +397 -102
- package/dist/index.d.ts +397 -102
- package/dist/index.js +14013 -1365
- package/dist/index.mjs +2763 -1223
- package/dist/storeContract-B2BDgvhN.d.mts +1379 -0
- package/dist/storeContract-D7vBM8nv.d.ts +1379 -0
- package/dist/szenen-contract.d.mts +12 -0
- package/dist/szenen-contract.d.ts +12 -0
- package/dist/szenen-contract.js +1450 -0
- package/dist/szenen-contract.mjs +27 -0
- package/dist/szenen.d.mts +2747 -0
- package/dist/szenen.d.ts +2747 -0
- package/dist/szenen.js +14677 -0
- package/dist/szenen.mjs +1811 -0
- package/package.json +17 -4
|
@@ -3,64 +3,68 @@ import JSZip from "jszip";
|
|
|
3
3
|
import { uploadFile } from "@huggingface/hub";
|
|
4
4
|
|
|
5
5
|
// src/lib/hfEventTypes.ts
|
|
6
|
-
var CURRENT_EVENT_VERSION = { major: 1, minor: 0 };
|
|
7
|
-
|
|
8
|
-
// src/lib/hfStateService.ts
|
|
9
6
|
var HF_BASE = "https://huggingface.co";
|
|
10
7
|
var HF_REPO = "RolandSch/fa-app-state";
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return localStorage.getItem(HF_TOKEN_KEY);
|
|
15
|
-
} catch {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function setHFToken(token) {
|
|
20
|
-
try {
|
|
21
|
-
localStorage.setItem(HF_TOKEN_KEY, token);
|
|
22
|
-
} catch {
|
|
23
|
-
}
|
|
24
|
-
}
|
|
8
|
+
var CURRENT_EVENT_VERSION = { major: 1, minor: 0 };
|
|
9
|
+
|
|
10
|
+
// src/lib/hfEventLog.ts
|
|
25
11
|
function treeUrl(namespace, subdir = "") {
|
|
26
12
|
const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
|
|
27
13
|
return `${HF_BASE}/api/datasets/${HF_REPO}/tree/main${parts ? "/" + parts : ""}`;
|
|
28
14
|
}
|
|
15
|
+
var DROSSEL_VERSUCHE = 4;
|
|
16
|
+
var DROSSEL_BASIS_MS = 400;
|
|
17
|
+
var schlafen = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
18
|
+
async function mitGeduld(anfrage) {
|
|
19
|
+
let letzte = null;
|
|
20
|
+
for (let versuch = 0; versuch < DROSSEL_VERSUCHE; versuch += 1) {
|
|
21
|
+
const res = await anfrage();
|
|
22
|
+
if (res.status !== 429 && res.status < 500) return res;
|
|
23
|
+
letzte = res;
|
|
24
|
+
if (versuch === DROSSEL_VERSUCHE - 1) break;
|
|
25
|
+
const angesagt = Number(res.headers.get("retry-after"));
|
|
26
|
+
const warten = Number.isFinite(angesagt) && angesagt > 0 ? angesagt * 1e3 : DROSSEL_BASIS_MS * 2 ** versuch;
|
|
27
|
+
await schlafen(warten);
|
|
28
|
+
}
|
|
29
|
+
return letzte;
|
|
30
|
+
}
|
|
29
31
|
async function hfListDir(namespace, subdir, token) {
|
|
30
|
-
const res = await fetch(treeUrl(namespace, subdir), {
|
|
32
|
+
const res = await mitGeduld(() => fetch(treeUrl(namespace, subdir), {
|
|
31
33
|
headers: { Authorization: `Bearer ${token}` }
|
|
32
|
-
});
|
|
34
|
+
}));
|
|
33
35
|
if (res.status === 404) return [];
|
|
34
36
|
if (!res.ok) throw new Error(`HF list failed: ${res.status} ${res.statusText}`);
|
|
35
37
|
return res.json();
|
|
36
38
|
}
|
|
37
39
|
async function hfDownloadJsonByPath(repoPath, token) {
|
|
38
|
-
const res = await fetch(
|
|
40
|
+
const res = await mitGeduld(() => fetch(
|
|
39
41
|
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
|
|
40
42
|
{ headers: { Authorization: `Bearer ${token}` } }
|
|
41
|
-
);
|
|
43
|
+
));
|
|
42
44
|
if (!res.ok) throw new Error(`HF download failed: ${res.status}`);
|
|
43
45
|
return res.json();
|
|
44
46
|
}
|
|
45
47
|
async function hfDownloadBinaryByPath(repoPath, token) {
|
|
46
|
-
const res = await fetch(
|
|
48
|
+
const res = await mitGeduld(() => fetch(
|
|
47
49
|
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
|
|
48
50
|
{ headers: { Authorization: `Bearer ${token}` } }
|
|
49
|
-
);
|
|
51
|
+
));
|
|
50
52
|
if (!res.ok) throw new Error(`HF download binary failed: ${res.status}`);
|
|
51
53
|
return new Uint8Array(await res.arrayBuffer());
|
|
52
54
|
}
|
|
53
|
-
|
|
54
|
-
const bytes = new TextEncoder().encode(
|
|
55
|
+
function toBase64(text) {
|
|
56
|
+
const bytes = new TextEncoder().encode(text);
|
|
55
57
|
let binary = "";
|
|
56
58
|
bytes.forEach((b) => binary += String.fromCharCode(b));
|
|
57
|
-
|
|
59
|
+
return btoa(binary);
|
|
60
|
+
}
|
|
61
|
+
async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${repoPath}`) {
|
|
58
62
|
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
59
63
|
method: "POST",
|
|
60
64
|
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
61
65
|
body: [
|
|
62
66
|
JSON.stringify({ key: "header", value: { summary, description: "" } }),
|
|
63
|
-
JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content:
|
|
67
|
+
JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: toBase64(content) } })
|
|
64
68
|
].join("\n")
|
|
65
69
|
});
|
|
66
70
|
if (!res.ok) {
|
|
@@ -71,15 +75,10 @@ async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${r
|
|
|
71
75
|
async function hfBatchArchive(moves, token, summary) {
|
|
72
76
|
const lines = [
|
|
73
77
|
JSON.stringify({ key: "header", value: { summary, description: "" } }),
|
|
74
|
-
...moves.flatMap(({ from, to, content }) =>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return [
|
|
79
|
-
JSON.stringify({ key: "file", value: { path: to, encoding: "base64", content: btoa(binary) } }),
|
|
80
|
-
JSON.stringify({ key: "deletedFile", value: { path: from } })
|
|
81
|
-
];
|
|
82
|
-
})
|
|
78
|
+
...moves.flatMap(({ from, to, content }) => [
|
|
79
|
+
JSON.stringify({ key: "file", value: { path: to, encoding: "base64", content: toBase64(content) } }),
|
|
80
|
+
JSON.stringify({ key: "deletedFile", value: { path: from } })
|
|
81
|
+
])
|
|
83
82
|
];
|
|
84
83
|
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
85
84
|
method: "POST",
|
|
@@ -98,37 +97,42 @@ function tsFromEventPath(repoPath) {
|
|
|
98
97
|
const ts = Date.parse(iso);
|
|
99
98
|
return isNaN(ts) ? 0 : ts;
|
|
100
99
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
100
|
+
var EVENTDOWNLOAD_PARALLELITAET = 3;
|
|
101
|
+
async function mitBegrenzterParallelitaet(count, limit, task) {
|
|
102
|
+
let cursor = 0;
|
|
103
|
+
let ersterFehler;
|
|
104
|
+
let fehlerAufgetreten = false;
|
|
105
|
+
const worker = async () => {
|
|
106
|
+
while (cursor < count) {
|
|
107
|
+
const index = cursor++;
|
|
108
|
+
try {
|
|
109
|
+
await task(index);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (!fehlerAufgetreten) {
|
|
112
|
+
fehlerAufgetreten = true;
|
|
113
|
+
ersterFehler = error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
117
|
};
|
|
118
|
+
await Promise.all(Array.from({ length: Math.min(limit, count) }, worker));
|
|
119
|
+
if (fehlerAufgetreten) throw ersterFehler;
|
|
118
120
|
}
|
|
119
121
|
async function loadPendingEvents(namespace, token, sinceTs, knownPaths) {
|
|
120
122
|
const files = await hfListDir(namespace, "events", token);
|
|
121
123
|
const pending = files.filter((f) => f.type === "file" && tsFromEventPath(f.path) > sinceTs && !knownPaths?.has(f.path)).sort((a, b) => a.path.localeCompare(b.path));
|
|
122
124
|
if (!pending.length) return [];
|
|
123
|
-
const raw =
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
})
|
|
129
|
-
);
|
|
125
|
+
const raw = new Array(pending.length);
|
|
126
|
+
await mitBegrenzterParallelitaet(pending.length, EVENTDOWNLOAD_PARALLELITAET, async (index) => {
|
|
127
|
+
raw[index] = await hfDownloadJsonByPath(pending[index].path, token);
|
|
128
|
+
});
|
|
129
|
+
for (const f of pending) knownPaths?.add(f.path);
|
|
130
130
|
return raw.flatMap((e) => Array.isArray(e) ? e : [e]);
|
|
131
131
|
}
|
|
132
|
+
async function listEventPaths(namespace, token) {
|
|
133
|
+
const files = await hfListDir(namespace, "events", token);
|
|
134
|
+
return files.filter((f) => f.type === "file").map((f) => f.path).sort((a, b) => a.localeCompare(b));
|
|
135
|
+
}
|
|
132
136
|
var SESSION_CLIENT_ID = `client-${crypto.randomUUID().slice(0, 8)}`;
|
|
133
137
|
function getSessionClientId() {
|
|
134
138
|
return SESSION_CLIENT_ID;
|
|
@@ -136,8 +140,12 @@ function getSessionClientId() {
|
|
|
136
140
|
function tsToFilename(ts) {
|
|
137
141
|
return new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-");
|
|
138
142
|
}
|
|
139
|
-
|
|
140
|
-
|
|
143
|
+
var writeSeq = 0;
|
|
144
|
+
var lastOrderedTs = 0;
|
|
145
|
+
async function writeHFEvent(namespace, token, type, payload, prevTs, options) {
|
|
146
|
+
const wallTs = Date.now();
|
|
147
|
+
const ts = options?.ordered ? Math.max(wallTs, lastOrderedTs) : wallTs;
|
|
148
|
+
if (options?.ordered) lastOrderedTs = ts;
|
|
141
149
|
const uuid = crypto.randomUUID().slice(0, 8);
|
|
142
150
|
const event = {
|
|
143
151
|
v: CURRENT_EVENT_VERSION,
|
|
@@ -147,11 +155,49 @@ async function writeHFEvent(namespace, token, type, payload, prevTs) {
|
|
|
147
155
|
clientId: SESSION_CLIENT_ID,
|
|
148
156
|
payload
|
|
149
157
|
};
|
|
150
|
-
const
|
|
158
|
+
const suffix = options?.ordered ? `${String(++writeSeq % 1e6).padStart(6, "0")}${uuid}` : uuid;
|
|
159
|
+
const filename = `${tsToFilename(ts)}_${suffix}.json`;
|
|
151
160
|
const repoPath = `${namespace}events/${filename}`;
|
|
152
161
|
await hfUploadSmallFile(repoPath, JSON.stringify(event, null, 2), token, `Event: ${type}`);
|
|
153
162
|
return event;
|
|
154
163
|
}
|
|
164
|
+
|
|
165
|
+
// src/lib/hfStateService.ts
|
|
166
|
+
var HF_TOKEN_KEY = "hf-token";
|
|
167
|
+
function getHFToken() {
|
|
168
|
+
try {
|
|
169
|
+
return localStorage.getItem(HF_TOKEN_KEY);
|
|
170
|
+
} catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function setHFToken(token) {
|
|
175
|
+
try {
|
|
176
|
+
localStorage.setItem(HF_TOKEN_KEY, token);
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function loadHFState(namespace, token) {
|
|
181
|
+
const files = await hfListDir(namespace, "", token);
|
|
182
|
+
const stateFiles = files.filter((f) => f.type === "file" && /^state-[\dT\-]+Z\.zip$/.test(f.path.split("/").pop() || "")).sort((a, b) => b.path.localeCompare(a.path));
|
|
183
|
+
if (!stateFiles.length) return null;
|
|
184
|
+
const zipBytes = await hfDownloadBinaryByPath(stateFiles[0].path, token);
|
|
185
|
+
const zip = await JSZip.loadAsync(zipBytes);
|
|
186
|
+
const [metadataStr, tagsStr, metaStr, openTasksStr] = await Promise.all([
|
|
187
|
+
zip.file("metadata.json")?.async("string"),
|
|
188
|
+
zip.file("tags.json")?.async("string"),
|
|
189
|
+
zip.file("state_meta.json")?.async("string"),
|
|
190
|
+
zip.file("open_tasks.json")?.async("string")
|
|
191
|
+
// optional — ältere Snapshots haben es nicht
|
|
192
|
+
]);
|
|
193
|
+
if (!metadataStr || !tagsStr || !metaStr) throw new Error("state.zip: fehlende Pflicht-Dateien");
|
|
194
|
+
return {
|
|
195
|
+
metadata: JSON.parse(metadataStr),
|
|
196
|
+
tags: JSON.parse(tagsStr),
|
|
197
|
+
meta: JSON.parse(metaStr),
|
|
198
|
+
openTasks: openTasksStr ? JSON.parse(openTasksStr) : {}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
155
201
|
async function hfBootstrapFromLegacy(namespace, token, onProgress) {
|
|
156
202
|
const log = (msg) => {
|
|
157
203
|
if (onProgress) onProgress(msg);
|
|
@@ -320,19 +366,20 @@ async function hfLoadImageAsBase64(id, token, namespace, filename, on404, mimeTy
|
|
|
320
366
|
}
|
|
321
367
|
|
|
322
368
|
export {
|
|
323
|
-
HF_TOKEN_KEY,
|
|
324
|
-
getHFToken,
|
|
325
|
-
setHFToken,
|
|
326
369
|
hfListDir,
|
|
327
370
|
hfDownloadJsonByPath,
|
|
328
371
|
hfDownloadBinaryByPath,
|
|
329
372
|
hfUploadSmallFile,
|
|
330
373
|
hfBatchArchive,
|
|
331
374
|
tsFromEventPath,
|
|
332
|
-
loadHFState,
|
|
333
375
|
loadPendingEvents,
|
|
376
|
+
listEventPaths,
|
|
334
377
|
getSessionClientId,
|
|
335
378
|
writeHFEvent,
|
|
379
|
+
HF_TOKEN_KEY,
|
|
380
|
+
getHFToken,
|
|
381
|
+
setHFToken,
|
|
382
|
+
loadHFState,
|
|
336
383
|
hfBootstrapFromLegacy,
|
|
337
384
|
hfListProjects,
|
|
338
385
|
hfDownloadProject,
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/** Was der Port zurückgeben kann. Kein stilles `undefined`. */
|
|
2
|
+
type TextGenerationResult = {
|
|
3
|
+
kind: 'ok';
|
|
4
|
+
text: string;
|
|
5
|
+
model_id: string;
|
|
6
|
+
provider: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'failed';
|
|
9
|
+
/** Fehlerklassen aus `prompt-derivation.md` §6. */
|
|
10
|
+
class: 'safety_blocked' | 'finish_reason' | 'empty_content' | 'provider_error';
|
|
11
|
+
message: string;
|
|
12
|
+
model_id: string;
|
|
13
|
+
provider: string;
|
|
14
|
+
/** Provider-Angaben **unverändert**, nicht zusammengefasst. */
|
|
15
|
+
provider_detail?: unknown;
|
|
16
|
+
};
|
|
17
|
+
/** Ein Bild als Modelleingabe — Daten und der Typ, unter dem sie zu lesen sind. */
|
|
18
|
+
interface TextGenerationImage {
|
|
19
|
+
/** Base64, ohne `data:`-Praefix. */
|
|
20
|
+
data: string;
|
|
21
|
+
/** Der tatsaechliche Typ, etwa `image/png`. Nie geraten. */
|
|
22
|
+
mime_type: string;
|
|
23
|
+
}
|
|
24
|
+
interface TextGenerationRequest {
|
|
25
|
+
systemInstruction: string;
|
|
26
|
+
prompt: string;
|
|
27
|
+
/** `'json'` schaltet jede für Prosa gedachte Nachbereinigung ab (D-56). */
|
|
28
|
+
responseFormat: 'text' | 'json';
|
|
29
|
+
/**
|
|
30
|
+
* Optionale Bilder als Eingabe (D-53). Seit Stufe 6.5 mit Konsument: die
|
|
31
|
+
* Szenenrekonstruktion.
|
|
32
|
+
*
|
|
33
|
+
* Bis dahin waren es nackte Base64-Zeichenketten. Der Serveradapter musste
|
|
34
|
+
* daraus einen MIME-Typ raten und schrieb `image/jpeg` an jedes Bild — ein
|
|
35
|
+
* PNG wurde damit falsch deklariert. Der Typ gehört zum Bild, nicht zur
|
|
36
|
+
* Vermutung des Adapters.
|
|
37
|
+
*/
|
|
38
|
+
images?: readonly TextGenerationImage[];
|
|
39
|
+
/** Voreinstellung bleibt die des Hosts; der Renderer hebt sie an (D-56). */
|
|
40
|
+
thinkingLevel?: 'low' | 'medium' | 'high';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Textgenerierung mit optionalen Bildern — die einzige Fähigkeit, die der
|
|
44
|
+
* Prompt-Lauf voraussetzt (D-53).
|
|
45
|
+
*/
|
|
46
|
+
interface TextGenerationPort {
|
|
47
|
+
generate(request: TextGenerationRequest): Promise<TextGenerationResult>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Eine deterministische Antwort zur Renderer-Eingabe.
|
|
51
|
+
*
|
|
52
|
+
* Sie nennt **jeden** Knoten (D-21) und richtet sich nach den Fähigkeiten des
|
|
53
|
+
* Zielmodells — dadurch läuft der echte Parser- und Schemapfad, nur ohne
|
|
54
|
+
* Provider. Eine Simulation, die die Prüfungen umginge, wiese nichts nach.
|
|
55
|
+
*
|
|
56
|
+
* Sie liegt **hier**, in der Testfläche der Bibliothek, und nicht im
|
|
57
|
+
* Serveradapter. Der Unterschied ist nicht kosmetisch: Ein Produktivadapter,
|
|
58
|
+
* der bei fehlender Konfiguration selbst eine gültige Antwort erfindet, macht
|
|
59
|
+
* aus einem Konfigurationsfehler einen gespeicherten Lauf. Genau das war der
|
|
60
|
+
* Befund des Umsetzungsreviews.
|
|
61
|
+
*/
|
|
62
|
+
declare function simulatedAnswerFor(request: TextGenerationRequest): string | null;
|
|
63
|
+
/** Kennzeichnung eines simulierten Laufs — im Bestand unverwechselbar. */
|
|
64
|
+
declare const SIMULATION_PROVIDER = "simulation";
|
|
65
|
+
/**
|
|
66
|
+
* Ein deterministischer Port ohne Provider.
|
|
67
|
+
*
|
|
68
|
+
* Für Tests **und** für eine ausdrücklich angeforderte Simulation in der
|
|
69
|
+
* Entwicklung. Er ist nie ein Rückfall: Wer ihn benutzt, hat ihn gewählt, und
|
|
70
|
+
* der gespeicherte Lauf trägt `provider: 'simulation'`.
|
|
71
|
+
*/
|
|
72
|
+
declare function createSimulationTextGenerationPort(): TextGenerationPort;
|
|
73
|
+
/**
|
|
74
|
+
* Ein Port mit frei wählbarer Antwort — für Tests, die einen bestimmten
|
|
75
|
+
* Ausgang brauchen. Protokolliert, was er bekommen hat.
|
|
76
|
+
*/
|
|
77
|
+
declare function createFakeTextGenerationPort(antwort: TextGenerationResult | ((request: TextGenerationRequest) => TextGenerationResult)): TextGenerationPort & {
|
|
78
|
+
calls: TextGenerationRequest[];
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
type FeatureSliceId = 'library' | 'tags' | 'prompt.builder' | 'prompt.negative' | 'labs.compare' | 'labs.remix' | 'labs.loop' | 'labs.blend' | 'labs.frameExtractor';
|
|
82
|
+
type WorkspaceTabId = `react.${string}` | `flow.${string}`;
|
|
83
|
+
interface PersistenceDescriptor<I extends FeatureSliceId = FeatureSliceId> {
|
|
84
|
+
sliceId: I;
|
|
85
|
+
tabId: WorkspaceTabId;
|
|
86
|
+
schemaVersion: number;
|
|
87
|
+
}
|
|
88
|
+
interface PersistedSlice<T> {
|
|
89
|
+
workspaceId: string;
|
|
90
|
+
tabId: WorkspaceTabId;
|
|
91
|
+
schemaVersion: number;
|
|
92
|
+
revision: number;
|
|
93
|
+
data: T;
|
|
94
|
+
updatedAt: number;
|
|
95
|
+
resetAt?: number;
|
|
96
|
+
}
|
|
97
|
+
type LoadResult<T> = {
|
|
98
|
+
kind: 'loaded';
|
|
99
|
+
slice: PersistedSlice<T>;
|
|
100
|
+
} | {
|
|
101
|
+
kind: 'notFound';
|
|
102
|
+
} | {
|
|
103
|
+
kind: 'invalidVersion';
|
|
104
|
+
found: number;
|
|
105
|
+
supported: number;
|
|
106
|
+
} | {
|
|
107
|
+
kind: 'transportError';
|
|
108
|
+
message: string;
|
|
109
|
+
retryable: boolean;
|
|
110
|
+
};
|
|
111
|
+
type WriteResult<T> = {
|
|
112
|
+
kind: 'saved';
|
|
113
|
+
slice: PersistedSlice<T>;
|
|
114
|
+
} | {
|
|
115
|
+
kind: 'conflict';
|
|
116
|
+
expectedRevision: number;
|
|
117
|
+
current: PersistedSlice<T>;
|
|
118
|
+
} | {
|
|
119
|
+
kind: 'notFound';
|
|
120
|
+
} | {
|
|
121
|
+
kind: 'invalidVersion';
|
|
122
|
+
found: number;
|
|
123
|
+
supported: number;
|
|
124
|
+
} | {
|
|
125
|
+
kind: 'transportError';
|
|
126
|
+
message: string;
|
|
127
|
+
retryable: boolean;
|
|
128
|
+
};
|
|
129
|
+
interface WorkspacePersistence {
|
|
130
|
+
load<T>(workspaceId: string, descriptor: PersistenceDescriptor, signal: AbortSignal): Promise<LoadResult<T>>;
|
|
131
|
+
save<T>(slice: PersistedSlice<T>, expectedRevision: number, signal: AbortSignal): Promise<WriteResult<T>>;
|
|
132
|
+
reset<T>(sliceWithDefault: PersistedSlice<T>, expectedRevision: number, signal: AbortSignal): Promise<WriteResult<T>>;
|
|
133
|
+
}
|
|
134
|
+
interface SliceContract<T> {
|
|
135
|
+
id: FeatureSliceId;
|
|
136
|
+
schemaVersion: number;
|
|
137
|
+
createDefault(): T;
|
|
138
|
+
serialize(value: T): unknown;
|
|
139
|
+
validate(value: unknown): T;
|
|
140
|
+
migrate(fromVersion: number, value: unknown): T;
|
|
141
|
+
}
|
|
142
|
+
interface RequestContext {
|
|
143
|
+
requestId: string;
|
|
144
|
+
signal: AbortSignal;
|
|
145
|
+
}
|
|
146
|
+
type QueryResult<T> = {
|
|
147
|
+
kind: 'ok';
|
|
148
|
+
value: T;
|
|
149
|
+
} | {
|
|
150
|
+
kind: 'error';
|
|
151
|
+
message: string;
|
|
152
|
+
retryable: boolean;
|
|
153
|
+
};
|
|
154
|
+
type CommandResult<T> = {
|
|
155
|
+
kind: 'ok';
|
|
156
|
+
value: T;
|
|
157
|
+
} | {
|
|
158
|
+
kind: 'unsupported';
|
|
159
|
+
capability: string;
|
|
160
|
+
} | {
|
|
161
|
+
kind: 'error';
|
|
162
|
+
message: string;
|
|
163
|
+
retryable: boolean;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export { type CommandResult as C, type FeatureSliceId as F, type LoadResult as L, type PersistedSlice as P, type QueryResult as Q, type RequestContext as R, SIMULATION_PROVIDER as S, type TextGenerationPort as T, type WorkspacePersistence as W, type PersistenceDescriptor as a, type SliceContract as b, type TextGenerationRequest as c, type TextGenerationResult as d, type WorkspaceTabId as e, type WriteResult as f, createFakeTextGenerationPort as g, createSimulationTextGenerationPort as h, simulatedAnswerFor as s };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/** Was der Port zurückgeben kann. Kein stilles `undefined`. */
|
|
2
|
+
type TextGenerationResult = {
|
|
3
|
+
kind: 'ok';
|
|
4
|
+
text: string;
|
|
5
|
+
model_id: string;
|
|
6
|
+
provider: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'failed';
|
|
9
|
+
/** Fehlerklassen aus `prompt-derivation.md` §6. */
|
|
10
|
+
class: 'safety_blocked' | 'finish_reason' | 'empty_content' | 'provider_error';
|
|
11
|
+
message: string;
|
|
12
|
+
model_id: string;
|
|
13
|
+
provider: string;
|
|
14
|
+
/** Provider-Angaben **unverändert**, nicht zusammengefasst. */
|
|
15
|
+
provider_detail?: unknown;
|
|
16
|
+
};
|
|
17
|
+
/** Ein Bild als Modelleingabe — Daten und der Typ, unter dem sie zu lesen sind. */
|
|
18
|
+
interface TextGenerationImage {
|
|
19
|
+
/** Base64, ohne `data:`-Praefix. */
|
|
20
|
+
data: string;
|
|
21
|
+
/** Der tatsaechliche Typ, etwa `image/png`. Nie geraten. */
|
|
22
|
+
mime_type: string;
|
|
23
|
+
}
|
|
24
|
+
interface TextGenerationRequest {
|
|
25
|
+
systemInstruction: string;
|
|
26
|
+
prompt: string;
|
|
27
|
+
/** `'json'` schaltet jede für Prosa gedachte Nachbereinigung ab (D-56). */
|
|
28
|
+
responseFormat: 'text' | 'json';
|
|
29
|
+
/**
|
|
30
|
+
* Optionale Bilder als Eingabe (D-53). Seit Stufe 6.5 mit Konsument: die
|
|
31
|
+
* Szenenrekonstruktion.
|
|
32
|
+
*
|
|
33
|
+
* Bis dahin waren es nackte Base64-Zeichenketten. Der Serveradapter musste
|
|
34
|
+
* daraus einen MIME-Typ raten und schrieb `image/jpeg` an jedes Bild — ein
|
|
35
|
+
* PNG wurde damit falsch deklariert. Der Typ gehört zum Bild, nicht zur
|
|
36
|
+
* Vermutung des Adapters.
|
|
37
|
+
*/
|
|
38
|
+
images?: readonly TextGenerationImage[];
|
|
39
|
+
/** Voreinstellung bleibt die des Hosts; der Renderer hebt sie an (D-56). */
|
|
40
|
+
thinkingLevel?: 'low' | 'medium' | 'high';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Textgenerierung mit optionalen Bildern — die einzige Fähigkeit, die der
|
|
44
|
+
* Prompt-Lauf voraussetzt (D-53).
|
|
45
|
+
*/
|
|
46
|
+
interface TextGenerationPort {
|
|
47
|
+
generate(request: TextGenerationRequest): Promise<TextGenerationResult>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Eine deterministische Antwort zur Renderer-Eingabe.
|
|
51
|
+
*
|
|
52
|
+
* Sie nennt **jeden** Knoten (D-21) und richtet sich nach den Fähigkeiten des
|
|
53
|
+
* Zielmodells — dadurch läuft der echte Parser- und Schemapfad, nur ohne
|
|
54
|
+
* Provider. Eine Simulation, die die Prüfungen umginge, wiese nichts nach.
|
|
55
|
+
*
|
|
56
|
+
* Sie liegt **hier**, in der Testfläche der Bibliothek, und nicht im
|
|
57
|
+
* Serveradapter. Der Unterschied ist nicht kosmetisch: Ein Produktivadapter,
|
|
58
|
+
* der bei fehlender Konfiguration selbst eine gültige Antwort erfindet, macht
|
|
59
|
+
* aus einem Konfigurationsfehler einen gespeicherten Lauf. Genau das war der
|
|
60
|
+
* Befund des Umsetzungsreviews.
|
|
61
|
+
*/
|
|
62
|
+
declare function simulatedAnswerFor(request: TextGenerationRequest): string | null;
|
|
63
|
+
/** Kennzeichnung eines simulierten Laufs — im Bestand unverwechselbar. */
|
|
64
|
+
declare const SIMULATION_PROVIDER = "simulation";
|
|
65
|
+
/**
|
|
66
|
+
* Ein deterministischer Port ohne Provider.
|
|
67
|
+
*
|
|
68
|
+
* Für Tests **und** für eine ausdrücklich angeforderte Simulation in der
|
|
69
|
+
* Entwicklung. Er ist nie ein Rückfall: Wer ihn benutzt, hat ihn gewählt, und
|
|
70
|
+
* der gespeicherte Lauf trägt `provider: 'simulation'`.
|
|
71
|
+
*/
|
|
72
|
+
declare function createSimulationTextGenerationPort(): TextGenerationPort;
|
|
73
|
+
/**
|
|
74
|
+
* Ein Port mit frei wählbarer Antwort — für Tests, die einen bestimmten
|
|
75
|
+
* Ausgang brauchen. Protokolliert, was er bekommen hat.
|
|
76
|
+
*/
|
|
77
|
+
declare function createFakeTextGenerationPort(antwort: TextGenerationResult | ((request: TextGenerationRequest) => TextGenerationResult)): TextGenerationPort & {
|
|
78
|
+
calls: TextGenerationRequest[];
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
type FeatureSliceId = 'library' | 'tags' | 'prompt.builder' | 'prompt.negative' | 'labs.compare' | 'labs.remix' | 'labs.loop' | 'labs.blend' | 'labs.frameExtractor';
|
|
82
|
+
type WorkspaceTabId = `react.${string}` | `flow.${string}`;
|
|
83
|
+
interface PersistenceDescriptor<I extends FeatureSliceId = FeatureSliceId> {
|
|
84
|
+
sliceId: I;
|
|
85
|
+
tabId: WorkspaceTabId;
|
|
86
|
+
schemaVersion: number;
|
|
87
|
+
}
|
|
88
|
+
interface PersistedSlice<T> {
|
|
89
|
+
workspaceId: string;
|
|
90
|
+
tabId: WorkspaceTabId;
|
|
91
|
+
schemaVersion: number;
|
|
92
|
+
revision: number;
|
|
93
|
+
data: T;
|
|
94
|
+
updatedAt: number;
|
|
95
|
+
resetAt?: number;
|
|
96
|
+
}
|
|
97
|
+
type LoadResult<T> = {
|
|
98
|
+
kind: 'loaded';
|
|
99
|
+
slice: PersistedSlice<T>;
|
|
100
|
+
} | {
|
|
101
|
+
kind: 'notFound';
|
|
102
|
+
} | {
|
|
103
|
+
kind: 'invalidVersion';
|
|
104
|
+
found: number;
|
|
105
|
+
supported: number;
|
|
106
|
+
} | {
|
|
107
|
+
kind: 'transportError';
|
|
108
|
+
message: string;
|
|
109
|
+
retryable: boolean;
|
|
110
|
+
};
|
|
111
|
+
type WriteResult<T> = {
|
|
112
|
+
kind: 'saved';
|
|
113
|
+
slice: PersistedSlice<T>;
|
|
114
|
+
} | {
|
|
115
|
+
kind: 'conflict';
|
|
116
|
+
expectedRevision: number;
|
|
117
|
+
current: PersistedSlice<T>;
|
|
118
|
+
} | {
|
|
119
|
+
kind: 'notFound';
|
|
120
|
+
} | {
|
|
121
|
+
kind: 'invalidVersion';
|
|
122
|
+
found: number;
|
|
123
|
+
supported: number;
|
|
124
|
+
} | {
|
|
125
|
+
kind: 'transportError';
|
|
126
|
+
message: string;
|
|
127
|
+
retryable: boolean;
|
|
128
|
+
};
|
|
129
|
+
interface WorkspacePersistence {
|
|
130
|
+
load<T>(workspaceId: string, descriptor: PersistenceDescriptor, signal: AbortSignal): Promise<LoadResult<T>>;
|
|
131
|
+
save<T>(slice: PersistedSlice<T>, expectedRevision: number, signal: AbortSignal): Promise<WriteResult<T>>;
|
|
132
|
+
reset<T>(sliceWithDefault: PersistedSlice<T>, expectedRevision: number, signal: AbortSignal): Promise<WriteResult<T>>;
|
|
133
|
+
}
|
|
134
|
+
interface SliceContract<T> {
|
|
135
|
+
id: FeatureSliceId;
|
|
136
|
+
schemaVersion: number;
|
|
137
|
+
createDefault(): T;
|
|
138
|
+
serialize(value: T): unknown;
|
|
139
|
+
validate(value: unknown): T;
|
|
140
|
+
migrate(fromVersion: number, value: unknown): T;
|
|
141
|
+
}
|
|
142
|
+
interface RequestContext {
|
|
143
|
+
requestId: string;
|
|
144
|
+
signal: AbortSignal;
|
|
145
|
+
}
|
|
146
|
+
type QueryResult<T> = {
|
|
147
|
+
kind: 'ok';
|
|
148
|
+
value: T;
|
|
149
|
+
} | {
|
|
150
|
+
kind: 'error';
|
|
151
|
+
message: string;
|
|
152
|
+
retryable: boolean;
|
|
153
|
+
};
|
|
154
|
+
type CommandResult<T> = {
|
|
155
|
+
kind: 'ok';
|
|
156
|
+
value: T;
|
|
157
|
+
} | {
|
|
158
|
+
kind: 'unsupported';
|
|
159
|
+
capability: string;
|
|
160
|
+
} | {
|
|
161
|
+
kind: 'error';
|
|
162
|
+
message: string;
|
|
163
|
+
retryable: boolean;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export { type CommandResult as C, type FeatureSliceId as F, type LoadResult as L, type PersistedSlice as P, type QueryResult as Q, type RequestContext as R, SIMULATION_PROVIDER as S, type TextGenerationPort as T, type WorkspacePersistence as W, type PersistenceDescriptor as a, type SliceContract as b, type TextGenerationRequest as c, type TextGenerationResult as d, type WorkspaceTabId as e, type WriteResult as f, createFakeTextGenerationPort as g, createSimulationTextGenerationPort as h, simulatedAnswerFor as s };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { T as TextGenerationPort } from './contracts-DdYOMBl9.mjs';
|
|
2
|
+
|
|
3
|
+
declare function buildGenerationPrompt(hierarchyText: string, mode?: 'literal' | 'creative'): string;
|
|
4
|
+
declare function buildFallbackPrompt(hierarchyText: string): string;
|
|
5
|
+
declare function cleanAiResponse(text: string): string;
|
|
6
|
+
declare function buildImageGenerationOptions(prompt: string, aspectRatio: string, model: string, referenceMediaId?: string): Record<string, any>;
|
|
7
|
+
interface SelectedTag {
|
|
8
|
+
label: string;
|
|
9
|
+
value: string;
|
|
10
|
+
category: string;
|
|
11
|
+
}
|
|
12
|
+
declare function buildPromptTabPayload(selectedTags: SelectedTag[], instructions: string, rules: string, treeText?: string): string;
|
|
13
|
+
declare function parsePromptResponse(raw: string): {
|
|
14
|
+
prompt: string;
|
|
15
|
+
feedback: string | null;
|
|
16
|
+
};
|
|
17
|
+
interface FlowSdkImage {
|
|
18
|
+
base64: string;
|
|
19
|
+
mimeType: string;
|
|
20
|
+
}
|
|
21
|
+
interface FlowTextGenerationOptions {
|
|
22
|
+
images?: FlowSdkImage[];
|
|
23
|
+
systemInstruction?: string;
|
|
24
|
+
responseFormat?: 'text' | 'json';
|
|
25
|
+
thinkingLevel?: 'low' | 'medium' | 'high';
|
|
26
|
+
}
|
|
27
|
+
interface FlowSdk {
|
|
28
|
+
generate: {
|
|
29
|
+
image: (options: Record<string, any>) => Promise<{
|
|
30
|
+
base64: string;
|
|
31
|
+
mediaId: string;
|
|
32
|
+
}>;
|
|
33
|
+
text: (prompt: string, options?: {
|
|
34
|
+
systemInstruction?: string;
|
|
35
|
+
thinkingLevel?: string;
|
|
36
|
+
images?: FlowSdkImage[];
|
|
37
|
+
}) => Promise<{
|
|
38
|
+
text: string;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
download: (args: {
|
|
42
|
+
base64: string;
|
|
43
|
+
mimeType: string;
|
|
44
|
+
filename: string;
|
|
45
|
+
}) => Promise<void>;
|
|
46
|
+
media: {
|
|
47
|
+
selectMultiple: (args: {
|
|
48
|
+
filter: string;
|
|
49
|
+
}) => Promise<any[]>;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
declare function createFlowServices(Flow: FlowSdk): {
|
|
53
|
+
generateImage: (options: Record<string, any>) => Promise<{
|
|
54
|
+
base64: string;
|
|
55
|
+
mediaId: string;
|
|
56
|
+
}>;
|
|
57
|
+
generateText: (text: string, options?: FlowTextGenerationOptions) => Promise<string>;
|
|
58
|
+
};
|
|
59
|
+
declare function interpretSdkError(err: any): Error;
|
|
60
|
+
|
|
61
|
+
type FlowGenerateText = (prompt: string, options?: FlowTextGenerationOptions) => Promise<string>;
|
|
62
|
+
interface CreateFlowTextGenerationPortOptions {
|
|
63
|
+
provider?: string;
|
|
64
|
+
modelId?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Adapts the existing Flow SDK service to the host-neutral Szenen contract.
|
|
68
|
+
* It deliberately does not parse or validate the response: that happens once
|
|
69
|
+
* in `runPromptDerivation`, identically for every host.
|
|
70
|
+
*/
|
|
71
|
+
declare function createFlowTextGenerationPort(generateText: FlowGenerateText, options?: CreateFlowTextGenerationPortOptions): TextGenerationPort;
|
|
72
|
+
|
|
73
|
+
export { type CreateFlowTextGenerationPortOptions as C, type FlowGenerateText as F, type SelectedTag as S, type FlowSdk as a, type FlowTextGenerationOptions as b, buildFallbackPrompt as c, buildGenerationPrompt as d, buildImageGenerationOptions as e, buildPromptTabPayload as f, cleanAiResponse as g, createFlowServices as h, createFlowTextGenerationPort as i, interpretSdkError as j, parsePromptResponse as p };
|