@rslsp1/fa-app-tools 2.0.78 → 2.0.82
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-BYD4RMYM.mjs → chunk-VBWQP7PO.mjs} +113 -69
- 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-JKEVTY66.mjs → hfStateService-WKQLNDHO.mjs} +3 -1
- package/dist/index.d.mts +47 -169
- package/dist/index.d.ts +47 -169
- package/dist/index.js +11916 -538
- package/dist/index.mjs +674 -379
- 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 +12 -2
|
@@ -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,40 +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
|
-
|
|
117
|
-
|
|
118
|
-
meta: JSON.parse(metaStr),
|
|
119
|
-
openTasks: openTasksStr ? JSON.parse(openTasksStr) : {}
|
|
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
|
+
}
|
|
120
117
|
};
|
|
118
|
+
await Promise.all(Array.from({ length: Math.min(limit, count) }, worker));
|
|
119
|
+
if (fehlerAufgetreten) throw ersterFehler;
|
|
121
120
|
}
|
|
122
121
|
async function loadPendingEvents(namespace, token, sinceTs, knownPaths) {
|
|
123
122
|
const files = await hfListDir(namespace, "events", token);
|
|
124
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));
|
|
125
124
|
if (!pending.length) return [];
|
|
126
|
-
const raw =
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
})
|
|
132
|
-
);
|
|
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);
|
|
133
130
|
return raw.flatMap((e) => Array.isArray(e) ? e : [e]);
|
|
134
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
|
+
}
|
|
135
136
|
var SESSION_CLIENT_ID = `client-${crypto.randomUUID().slice(0, 8)}`;
|
|
136
137
|
function getSessionClientId() {
|
|
137
138
|
return SESSION_CLIENT_ID;
|
|
@@ -139,8 +140,12 @@ function getSessionClientId() {
|
|
|
139
140
|
function tsToFilename(ts) {
|
|
140
141
|
return new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-");
|
|
141
142
|
}
|
|
142
|
-
|
|
143
|
-
|
|
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;
|
|
144
149
|
const uuid = crypto.randomUUID().slice(0, 8);
|
|
145
150
|
const event = {
|
|
146
151
|
v: CURRENT_EVENT_VERSION,
|
|
@@ -150,11 +155,49 @@ async function writeHFEvent(namespace, token, type, payload, prevTs) {
|
|
|
150
155
|
clientId: SESSION_CLIENT_ID,
|
|
151
156
|
payload
|
|
152
157
|
};
|
|
153
|
-
const
|
|
158
|
+
const suffix = options?.ordered ? `${String(++writeSeq % 1e6).padStart(6, "0")}${uuid}` : uuid;
|
|
159
|
+
const filename = `${tsToFilename(ts)}_${suffix}.json`;
|
|
154
160
|
const repoPath = `${namespace}events/${filename}`;
|
|
155
161
|
await hfUploadSmallFile(repoPath, JSON.stringify(event, null, 2), token, `Event: ${type}`);
|
|
156
162
|
return event;
|
|
157
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
|
+
}
|
|
158
201
|
async function hfBootstrapFromLegacy(namespace, token, onProgress) {
|
|
159
202
|
const log = (msg) => {
|
|
160
203
|
if (onProgress) onProgress(msg);
|
|
@@ -323,19 +366,20 @@ async function hfLoadImageAsBase64(id, token, namespace, filename, on404, mimeTy
|
|
|
323
366
|
}
|
|
324
367
|
|
|
325
368
|
export {
|
|
326
|
-
HF_TOKEN_KEY,
|
|
327
|
-
getHFToken,
|
|
328
|
-
setHFToken,
|
|
329
369
|
hfListDir,
|
|
330
370
|
hfDownloadJsonByPath,
|
|
331
371
|
hfDownloadBinaryByPath,
|
|
332
372
|
hfUploadSmallFile,
|
|
333
373
|
hfBatchArchive,
|
|
334
374
|
tsFromEventPath,
|
|
335
|
-
loadHFState,
|
|
336
375
|
loadPendingEvents,
|
|
376
|
+
listEventPaths,
|
|
337
377
|
getSessionClientId,
|
|
338
378
|
writeHFEvent,
|
|
379
|
+
HF_TOKEN_KEY,
|
|
380
|
+
getHFToken,
|
|
381
|
+
setHFToken,
|
|
382
|
+
loadHFState,
|
|
339
383
|
hfBootstrapFromLegacy,
|
|
340
384
|
hfListProjects,
|
|
341
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 };
|