narraeon 0.7.10 → 0.8.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.
Files changed (44) hide show
  1. package/dist/node/protocol/modelConnections.js +15 -0
  2. package/dist/node/protocol/modelUsage.js +4 -0
  3. package/dist/node/protocol/sillyTavernContent.js +1 -0
  4. package/dist/node/protocol/v1.js +75 -0
  5. package/dist/node/runtime/V1Runtime.js +71 -1
  6. package/dist/node/runtime/authoring/AuthoringRequestSnapshot.js +15 -3
  7. package/dist/node/runtime/authoring/DamagedGoogleContinuation.js +68 -0
  8. package/dist/node/runtime/authoring/ModelToolConversation.js +47 -22
  9. package/dist/node/runtime/content/ContentWorkspace.js +3 -0
  10. package/dist/node/runtime/content/SillyTavernContent.js +796 -0
  11. package/dist/node/runtime/model/AiFailureLog.js +8 -1
  12. package/dist/node/runtime/model/FileNativeModelAdapters.js +610 -18
  13. package/dist/node/runtime/model/GoogleHopJournal.js +90 -0
  14. package/dist/node/runtime/model/GoogleInteractionsStream.js +242 -0
  15. package/dist/node/runtime/model/GoogleRequestPreview.js +184 -0
  16. package/dist/node/runtime/model/ModelConnectionStore.js +4 -1
  17. package/dist/node/runtime/model/ProviderExchangeState.js +44 -1
  18. package/dist/node/runtime/model/ProviderStream.js +4 -2
  19. package/dist/node/runtime/play/FileNativePlayAdvanceStore.js +4 -4
  20. package/dist/node/runtime/play/FileNativePlayPresetStore.js +150 -78
  21. package/dist/node/runtime/play/FileNativePlayTimelineStore.js +150 -10
  22. package/dist/node/runtime/play/OrderedPlayPrompts.js +139 -178
  23. package/dist/node/runtime/play/PlayAdvanceLifecycle.js +33 -9
  24. package/dist/node/runtime/play/PlayCallChain.js +76 -4
  25. package/dist/node/runtime/play/PlayFollowupRequests.js +23 -3
  26. package/dist/node/runtime/prompt/PromptCompilationCodec.js +2 -1
  27. package/dist/node/runtime/setting/FileNativeSettingImprovementStore.js +112 -8
  28. package/dist/node/runtime/setting/SettingImprovementSession.js +48 -7
  29. package/dist/node/runtime/world-revision/FileNativeWorldRevisionStore.js +84 -8
  30. package/dist/node/runtime/world-revision/WorldRevisionSession.js +125 -17
  31. package/dist/node/shared/application-preset-files.js +29 -0
  32. package/dist/node/shared/default-preset-host.js +50 -38
  33. package/dist/node/shared/first-party-action-choices.js +7 -11
  34. package/dist/node/shared/first-party-generic-panels.js +7 -11
  35. package/dist/node/shared/first-party-player-view.js +7 -11
  36. package/dist/node/shared/ordered-author-prompts.js +6 -4
  37. package/dist/node/shared/ordered-play-prompts.js +4 -2
  38. package/dist/node/shared/prompt-arrangement.js +205 -0
  39. package/dist/node/shared/sillytavern-preset.js +454 -0
  40. package/dist/web/assets/{index-DH2BVsYd.css → index-BKA1D0Rz.css} +1 -1
  41. package/dist/web/assets/index-C_Qta8UL.js +1432 -0
  42. package/dist/web/index.html +2 -2
  43. package/package.json +1 -1
  44. package/dist/web/assets/index-B2kbaOGe.js +0 -1408
@@ -0,0 +1,454 @@
1
+ import { stringify } from "yaml";
2
+ import { applicationPresetFilesForLocale } from "./application-preset-files.js";
3
+ import { builtinPlayPrompts, } from "./ordered-play-prompts.js";
4
+ import { customizePromptArrangement } from "./prompt-arrangement.js";
5
+ const builtins = new Set([
6
+ "main",
7
+ "nsfw",
8
+ "jailbreak",
9
+ "dialogueExamples",
10
+ "chatHistory",
11
+ "worldInfoAfter",
12
+ "worldInfoBefore",
13
+ "enhanceDefinitions",
14
+ "charDescription",
15
+ "charPersonality",
16
+ "scenario",
17
+ "personaDescription",
18
+ ]);
19
+ const fields = new Set([
20
+ "identifier",
21
+ "name",
22
+ "role",
23
+ "content",
24
+ "system_prompt",
25
+ "marker",
26
+ "injection_position",
27
+ "injection_depth",
28
+ "injection_order",
29
+ "injection_trigger",
30
+ "forbid_overrides",
31
+ "extension",
32
+ "position",
33
+ "enabled",
34
+ ]);
35
+ const structuralErrorCodes = {
36
+ "expected object": "st_expected_object",
37
+ "expected array": "st_expected_array",
38
+ "expected string": "st_expected_string",
39
+ "expected boolean": "st_expected_boolean",
40
+ "expected number": "st_expected_number",
41
+ "invalid JSON": "st_invalid_json",
42
+ "legacy prompt format is unsupported": "st_legacy_prompt_format_is_unsupported",
43
+ "unsupported preset format": "st_unsupported_preset_format",
44
+ "expected nonempty string": "st_expected_nonempty_string",
45
+ "duplicate identifier": "st_duplicate_identifier",
46
+ "expected string or number": "st_expected_string_or_number",
47
+ "expected string array": "st_expected_string_array",
48
+ "expected prompt content": "st_expected_prompt_content",
49
+ "unsupported role": "st_unsupported_role",
50
+ "unknown order field": "st_unknown_order_field",
51
+ "duplicate order identifier": "st_duplicate_order_identifier",
52
+ "unknown prompt reference": "st_unknown_prompt_reference",
53
+ "unknown group field": "st_unknown_group_field",
54
+ "expected character identifier": "st_expected_character_identifier",
55
+ "duplicate character group": "st_duplicate_character_group",
56
+ "unknown group": "st_unknown_group",
57
+ "choose a character group": "st_choose_a_character_group",
58
+ "invalid selection": "st_invalid_selection",
59
+ };
60
+ function fail(path, message) {
61
+ throw new Error(`${path}: ${message} [${structuralErrorCodes[message]}]`);
62
+ }
63
+ function object(value, path) {
64
+ if (typeof value !== "object" || value === null || Array.isArray(value))
65
+ fail(path, "expected object");
66
+ return value;
67
+ }
68
+ function array(value, path) {
69
+ if (!Array.isArray(value))
70
+ fail(path, "expected array");
71
+ return value;
72
+ }
73
+ function optional(value, type, path) {
74
+ if (value !== undefined &&
75
+ (typeof value !== type || (type === "number" && !Number.isFinite(value))))
76
+ fail(path, `expected ${type}`);
77
+ }
78
+ function parse(raw, groupId) {
79
+ let parsed;
80
+ try {
81
+ parsed = JSON.parse(raw);
82
+ }
83
+ catch {
84
+ return fail("$", "invalid JSON");
85
+ }
86
+ const root = object(parsed, "$");
87
+ if ("main_prompt" in root ||
88
+ "nsfw_prompt" in root ||
89
+ "jailbreak_prompt" in root)
90
+ fail("$", "legacy prompt format is unsupported");
91
+ const manager = root.version === 1 && (root.type === "full" || root.type === "character");
92
+ const data = manager ? object(root.data, "$.data") : root;
93
+ if (!manager &&
94
+ ("version" in root ||
95
+ "type" in root ||
96
+ !Array.isArray(root.prompts) ||
97
+ !Array.isArray(root.prompt_order)))
98
+ fail("$", "unsupported preset format");
99
+ const prefix = manager ? "$.data" : "$";
100
+ const prompts = array(data.prompts, `${prefix}.prompts`);
101
+ const byId = new Map();
102
+ prompts.forEach((entry, index) => {
103
+ const path = `${prefix}.prompts[${index}]`;
104
+ const value = object(entry, path);
105
+ if (typeof value.identifier !== "string" || !value.identifier)
106
+ fail(`${path}.identifier`, "expected nonempty string");
107
+ if (byId.has(value.identifier))
108
+ fail(`${path}.identifier`, "duplicate identifier");
109
+ for (const key of ["name", "role", "content"])
110
+ optional(value[key], "string", `${path}.${key}`);
111
+ for (const key of [
112
+ "system_prompt",
113
+ "marker",
114
+ "forbid_overrides",
115
+ "extension",
116
+ "enabled",
117
+ ])
118
+ optional(value[key], "boolean", `${path}.${key}`);
119
+ for (const key of [
120
+ "injection_position",
121
+ "injection_depth",
122
+ "injection_order",
123
+ ])
124
+ optional(value[key], "number", `${path}.${key}`);
125
+ if (value.position !== undefined &&
126
+ typeof value.position !== "number" &&
127
+ typeof value.position !== "string")
128
+ fail(`${path}.position`, "expected string or number");
129
+ if (value.injection_trigger !== undefined &&
130
+ (!Array.isArray(value.injection_trigger) ||
131
+ value.injection_trigger.some((v) => typeof v !== "string")))
132
+ fail(`${path}.injection_trigger`, "expected string array");
133
+ if (value.marker !== true && typeof value.content !== "string")
134
+ fail(`${path}.content`, "expected prompt content");
135
+ if (value.role !== undefined &&
136
+ !["system", "user", "assistant"].includes(value.role))
137
+ fail(`${path}.role`, "unsupported role");
138
+ byId.set(value.identifier, { value, path });
139
+ });
140
+ function order(value, path) {
141
+ const seen = new Set();
142
+ return array(value, path).map((entry, index) => {
143
+ const itemPath = `${path}[${index}]`;
144
+ const item = object(entry, itemPath);
145
+ if (Object.keys(item).some((key) => !["identifier", "enabled"].includes(key)))
146
+ fail(itemPath, "unknown order field");
147
+ if (typeof item.identifier !== "string" || !item.identifier)
148
+ fail(`${itemPath}.identifier`, "expected nonempty string");
149
+ if (typeof item.enabled !== "boolean")
150
+ fail(`${itemPath}.enabled`, "expected boolean");
151
+ if (seen.has(item.identifier))
152
+ fail(`${itemPath}.identifier`, "duplicate order identifier");
153
+ seen.add(item.identifier);
154
+ if (!byId.has(item.identifier) && !builtins.has(item.identifier))
155
+ fail(`${itemPath}.identifier`, "unknown prompt reference");
156
+ return { id: item.identifier, enabled: item.enabled, path: itemPath };
157
+ });
158
+ }
159
+ const groups = [];
160
+ const orders = new Map();
161
+ if (manager)
162
+ orders.set("manager", order(data.prompt_order, `${prefix}.prompt_order`));
163
+ else
164
+ array(data.prompt_order, "$.prompt_order").forEach((entry, index) => {
165
+ const path = `$.prompt_order[${index}]`;
166
+ const value = object(entry, path);
167
+ if (Object.keys(value).some((key) => !["character_id", "order"].includes(key)))
168
+ fail(path, "unknown group field");
169
+ if ((typeof value.character_id !== "string" &&
170
+ typeof value.character_id !== "number") ||
171
+ String(value.character_id).length === 0)
172
+ fail(`${path}.character_id`, "expected character identifier");
173
+ const id = String(value.character_id);
174
+ if (orders.has(id))
175
+ fail(`${path}.character_id`, "duplicate character group");
176
+ groups.push({ id, label: id });
177
+ orders.set(id, order(value.order, `${path}.order`));
178
+ });
179
+ const selectedId = manager
180
+ ? "manager"
181
+ : (groupId ??
182
+ (orders.has("100001")
183
+ ? "100001"
184
+ : groups.length === 1
185
+ ? groups[0].id
186
+ : undefined));
187
+ if (groupId !== undefined && (!orders.has(groupId) || manager))
188
+ fail("$.prompt_order", "unknown group");
189
+ const selectedOrder = selectedId === undefined ? undefined : orders.get(selectedId);
190
+ const result = [];
191
+ const seen = new Set();
192
+ function add(id, ordered, enabled, orderPath) {
193
+ seen.add(id);
194
+ const found = byId.get(id);
195
+ if (!found) {
196
+ result.push({
197
+ id,
198
+ path: orderPath,
199
+ name: id,
200
+ enabled,
201
+ ordered,
202
+ marker: false,
203
+ missing: true,
204
+ macros: [],
205
+ reasons: ["missing_builtin"],
206
+ unsupportedPaths: [],
207
+ });
208
+ return;
209
+ }
210
+ const { value, path } = found;
211
+ const content = value.content;
212
+ const macros = [
213
+ ...new Set(content?.match(/\{\{[^{}]*\}\}|<(?:USER|BOT|CHAR|CHARIFNOTGROUP|GROUP)>/giu) ?? []),
214
+ ];
215
+ const reasons = [];
216
+ const unsupportedPaths = Object.keys(value)
217
+ .filter((key) => !fields.has(key))
218
+ .map((key) => `${path}.${key}`);
219
+ if (value.marker === true)
220
+ reasons.push("marker");
221
+ if (macros.length)
222
+ reasons.push("unsupported_macro");
223
+ if (value.injection_position !== undefined &&
224
+ value.injection_position !== 0)
225
+ reasons.push("depth_injection");
226
+ if (Array.isArray(value.injection_trigger) &&
227
+ value.injection_trigger.length)
228
+ reasons.push("trigger");
229
+ if ((id === "main" || id === "jailbreak") &&
230
+ value.forbid_overrides !== true)
231
+ reasons.push("overrides");
232
+ if (value.extension === true || unsupportedPaths.length > 0)
233
+ reasons.push("unsupported_fields");
234
+ if (!ordered)
235
+ reasons.push("unplaced");
236
+ result.push({
237
+ id,
238
+ path,
239
+ name: value.name ?? id,
240
+ role: value.role,
241
+ content,
242
+ enabled,
243
+ ordered,
244
+ marker: value.marker === true,
245
+ missing: false,
246
+ injectionPosition: value.injection_position,
247
+ injectionDepth: value.injection_depth,
248
+ injectionOrder: value.injection_order,
249
+ triggers: value.injection_trigger,
250
+ macros,
251
+ forbidOverrides: value.forbid_overrides,
252
+ reasons,
253
+ unsupportedPaths,
254
+ systemPrompt: value.system_prompt,
255
+ extension: value.extension,
256
+ position: value.position,
257
+ });
258
+ }
259
+ selectedOrder?.forEach((entry) => add(entry.id, true, entry.enabled, entry.path));
260
+ if (selectedOrder)
261
+ for (const id of byId.keys())
262
+ if (!seen.has(id))
263
+ add(id, false, undefined);
264
+ function settingsCategory(key) {
265
+ if (/password|key|token|proxy|url|endpoint|header|auth/iu.test(key) &&
266
+ !/max_tokens/iu.test(key))
267
+ return "connection";
268
+ if (/temperature|penalty|top_|min_p|repetition|context|tokens|sampl/iu.test(key))
269
+ return "sampling";
270
+ if (/model|source|api/iu.test(key))
271
+ return "model";
272
+ if (/extension|regex|script/iu.test(key))
273
+ return "extension";
274
+ return "generation";
275
+ }
276
+ const safeNumericSettings = new Set([
277
+ "temperature",
278
+ "top_p",
279
+ "top_k",
280
+ "top_a",
281
+ "min_p",
282
+ "frequency_penalty",
283
+ "presence_penalty",
284
+ "repetition_penalty",
285
+ "openai_max_context",
286
+ "openai_max_tokens",
287
+ ]);
288
+ const setting = (key, path, value) => ({
289
+ path,
290
+ category: settingsCategory(key),
291
+ ...(safeNumericSettings.has(key) &&
292
+ typeof value === "number" &&
293
+ Number.isFinite(value)
294
+ ? { value }
295
+ : {}),
296
+ });
297
+ const settings = [
298
+ ...Object.keys(root)
299
+ .filter((key) => manager
300
+ ? !["version", "type", "data"].includes(key)
301
+ : !["prompts", "prompt_order"].includes(key))
302
+ .map((key) => setting(key, `$.${key}`, root[key])),
303
+ ...(manager
304
+ ? Object.keys(data)
305
+ .filter((key) => !["prompts", "prompt_order"].includes(key))
306
+ .map((key) => setting(key, `$.data.${key}`, data[key]))
307
+ : []),
308
+ ];
309
+ return {
310
+ inspection: {
311
+ groups,
312
+ selectedGroupId: manager ? undefined : selectedId,
313
+ prompts: result,
314
+ settings,
315
+ },
316
+ selected: selectedOrder !== undefined,
317
+ };
318
+ }
319
+ export function inspectSillyTavernPreset(raw, groupId) {
320
+ return parse(raw, groupId).inspection;
321
+ }
322
+ export function convertSillyTavernPreset(raw, options) {
323
+ const { inspection, selected } = parse(raw, options.groupId);
324
+ if (!selected)
325
+ fail("$.prompt_order", "choose a character group");
326
+ if (new Set(options.selected).size !== options.selected.length ||
327
+ options.selected.some((id) => !inspection.prompts.some((item) => item.id === id)))
328
+ fail("selection", "invalid selection");
329
+ const catalog = builtinPlayPrompts(options.locale);
330
+ const entries = catalog
331
+ .filter((item) => item.required)
332
+ .map((item) => ({
333
+ id: item.id,
334
+ kind: "builtin",
335
+ builtin: item.id,
336
+ enabled: true,
337
+ }));
338
+ const report = inspection.settings.map((setting) => ({
339
+ code: "ignored_setting",
340
+ path: setting.path,
341
+ result: "ignored",
342
+ }));
343
+ const targetIds = new Map();
344
+ for (const item of inspection.prompts) {
345
+ const chosen = options.selected.includes(item.id);
346
+ report.push({
347
+ code: "prompt_selection",
348
+ path: item.path,
349
+ name: item.name,
350
+ result: !chosen
351
+ ? "not_selected"
352
+ : item.marker || item.missing
353
+ ? "not_imported"
354
+ : item.enabled === true && item.reasons.length === 0
355
+ ? "enabled"
356
+ : "disabled",
357
+ });
358
+ for (const code of item.reasons)
359
+ report.push({
360
+ code,
361
+ path: item.path,
362
+ name: item.name,
363
+ result: chosen && !item.marker && !item.missing ? "disabled" : "not_imported",
364
+ });
365
+ for (const path of item.unsupportedPaths)
366
+ report.push({
367
+ code: "unsupported_field",
368
+ path,
369
+ name: item.name,
370
+ result: chosen ? "disabled" : "not_imported",
371
+ });
372
+ if (!chosen || item.marker || item.missing)
373
+ continue;
374
+ const targetId = `st-${entries.length}`;
375
+ targetIds.set(item.id, targetId);
376
+ entries.push({
377
+ id: targetId,
378
+ kind: "user",
379
+ name: item.name,
380
+ body: item.content,
381
+ messageRole: (item.role ?? "system"),
382
+ enabled: item.enabled === true && item.reasons.length === 0,
383
+ });
384
+ }
385
+ entries.push({ id: "world", kind: "world" });
386
+ entries.push(...catalog
387
+ .filter((item) => !item.required)
388
+ .map((item) => ({
389
+ id: item.id,
390
+ kind: "builtin",
391
+ builtin: item.id,
392
+ enabled: false,
393
+ })));
394
+ const arrangement = customizePromptArrangement(entries, catalog);
395
+ const files = applicationPresetFilesForLocale(options.locale);
396
+ files["preset.yaml"] = stringify({
397
+ format: "narraeon.play-preset/v3",
398
+ name: options.name,
399
+ callChain: "call-chain.yaml",
400
+ playPrompts: arrangement,
401
+ authorPrompts: {},
402
+ mergeConsecutiveMessages: false,
403
+ mounts: [],
404
+ extensions: [],
405
+ });
406
+ files["assets/sillytavern-import-report.json"] = JSON.stringify({
407
+ format: "narraeon.sillytavern-import/v1",
408
+ sourceGroupId: inspection.selectedGroupId,
409
+ prompts: inspection.prompts.flatMap((item, index) => options.selected.includes(item.id)
410
+ ? [
411
+ {
412
+ sourceId: item.id,
413
+ path: item.path,
414
+ name: item.name,
415
+ role: item.role,
416
+ sourceEnabled: item.enabled,
417
+ ordered: item.ordered,
418
+ orderIndex: item.ordered ? index : undefined,
419
+ marker: item.marker,
420
+ missing: item.missing,
421
+ injectionPosition: item.injectionPosition,
422
+ injectionDepth: item.injectionDepth,
423
+ injectionOrder: item.injectionOrder,
424
+ triggers: item.triggers,
425
+ macros: item.macros,
426
+ forbidOverrides: item.forbidOverrides,
427
+ systemPrompt: item.systemPrompt,
428
+ extension: item.extension,
429
+ position: item.position,
430
+ unsupportedPaths: item.unsupportedPaths,
431
+ targetId: targetIds.get(item.id),
432
+ },
433
+ ]
434
+ : []),
435
+ report: report.filter((item) => item.code === "ignored_setting" ||
436
+ (item.name !== undefined &&
437
+ inspection.prompts.some((prompt) => options.selected.includes(prompt.id) &&
438
+ (item.path === prompt.path ||
439
+ item.path.startsWith(`${prompt.path}.`))))),
440
+ }, null, 2);
441
+ return {
442
+ files,
443
+ report,
444
+ target: entries.map((entry) => ({
445
+ name: entry.kind === "user"
446
+ ? entry.name
447
+ : entry.kind === "builtin"
448
+ ? catalog.find((item) => item.id === entry.builtin).name
449
+ : "World",
450
+ kind: entry.kind,
451
+ enabled: entry.kind === "world" || entry.enabled,
452
+ })),
453
+ };
454
+ }