narraeon 0.7.10 → 0.8.1

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 (49) 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 +97 -0
  5. package/dist/node/runtime/V1Runtime.js +105 -3
  6. package/dist/node/runtime/authoring/AuthoringEditPlan.js +79 -0
  7. package/dist/node/runtime/authoring/AuthoringRequestSnapshot.js +15 -3
  8. package/dist/node/runtime/authoring/AuthoringRetry.js +59 -0
  9. package/dist/node/runtime/authoring/DamagedGoogleContinuation.js +68 -0
  10. package/dist/node/runtime/authoring/ModelToolConversation.js +66 -25
  11. package/dist/node/runtime/content/ContentWorkspace.js +10 -2
  12. package/dist/node/runtime/content/CurrentTreeContentLibrary.js +14 -8
  13. package/dist/node/runtime/content/SillyTavernContent.js +796 -0
  14. package/dist/node/runtime/model/AiFailureLog.js +8 -1
  15. package/dist/node/runtime/model/DispatchFailure.js +8 -0
  16. package/dist/node/runtime/model/FileNativeModelAdapters.js +627 -18
  17. package/dist/node/runtime/model/GoogleHopJournal.js +90 -0
  18. package/dist/node/runtime/model/GoogleInteractionsStream.js +242 -0
  19. package/dist/node/runtime/model/GoogleRequestPreview.js +184 -0
  20. package/dist/node/runtime/model/ModelConnectionStore.js +4 -1
  21. package/dist/node/runtime/model/ProviderExchangeState.js +44 -1
  22. package/dist/node/runtime/model/ProviderStream.js +4 -2
  23. package/dist/node/runtime/play/FileNativePlayAdvanceStore.js +4 -4
  24. package/dist/node/runtime/play/FileNativePlayPresetStore.js +150 -78
  25. package/dist/node/runtime/play/FileNativePlayTimelineStore.js +199 -10
  26. package/dist/node/runtime/play/OrderedPlayPrompts.js +139 -178
  27. package/dist/node/runtime/play/PlayAdvanceLifecycle.js +51 -23
  28. package/dist/node/runtime/play/PlayCallChain.js +92 -8
  29. package/dist/node/runtime/play/PlayFollowupRequests.js +23 -3
  30. package/dist/node/runtime/prompt/PromptCompilationCodec.js +2 -1
  31. package/dist/node/runtime/setting/FileNativeSettingImprovementStore.js +321 -15
  32. package/dist/node/runtime/setting/SettingImprovementSession.js +285 -21
  33. package/dist/node/runtime/world-revision/FileNativeWorldRevisionStore.js +236 -15
  34. package/dist/node/runtime/world-revision/WorldRevisionSession.js +324 -50
  35. package/dist/node/runtime/world-revision/WorldRevisionWorkspace.js +47 -1
  36. package/dist/node/shared/application-preset-files.js +29 -0
  37. package/dist/node/shared/default-preset-host.js +50 -38
  38. package/dist/node/shared/first-party-action-choices.js +7 -11
  39. package/dist/node/shared/first-party-generic-panels.js +7 -11
  40. package/dist/node/shared/first-party-player-view.js +7 -11
  41. package/dist/node/shared/ordered-author-prompts.js +6 -4
  42. package/dist/node/shared/ordered-play-prompts.js +4 -2
  43. package/dist/node/shared/prompt-arrangement.js +205 -0
  44. package/dist/node/shared/sillytavern-preset.js +454 -0
  45. package/dist/web/assets/index-BthiDIQu.js +1432 -0
  46. package/dist/web/assets/{index-DH2BVsYd.css → index-wO9Y4HT1.css} +1 -1
  47. package/dist/web/index.html +2 -2
  48. package/package.json +1 -1
  49. package/dist/web/assets/index-B2kbaOGe.js +0 -1408
@@ -0,0 +1,205 @@
1
+ import { defaultEnabledBuiltin, } from "./ordered-play-prompts.js";
2
+ export const retiredBuiltinReplacements = {
3
+ "play.style": [{ builtin: "play.prose" }, { builtin: "play.scene" }],
4
+ "play.adjudication": [
5
+ { builtin: "play.agency-default" },
6
+ { builtin: "play.agency-strict", selected: false },
7
+ { builtin: "play.agency-open", selected: false },
8
+ { builtin: "play.facts" },
9
+ { builtin: "play.npc" },
10
+ ],
11
+ "play.narrative": [
12
+ { builtin: "play.stop-beat" },
13
+ { builtin: "play.stop-scene", selected: false },
14
+ { builtin: "play.ending" },
15
+ { builtin: "play.output" },
16
+ ],
17
+ "play.state": [
18
+ { builtin: "play.save-timing" },
19
+ { builtin: "play.ownership" },
20
+ { builtin: "play.situation" },
21
+ { builtin: "play.checkpoint-policy" },
22
+ ],
23
+ };
24
+ export function promptArrangementKey(entry) {
25
+ return entry.kind === "builtin"
26
+ ? `builtin:${entry.builtin}`
27
+ : `entry:${entry.id}`;
28
+ }
29
+ function reserveBuiltinPromptId(preferred, builtin, used) {
30
+ let id = preferred;
31
+ let suffix = 0;
32
+ while (used.has(id))
33
+ id = `builtin-${builtin}-${++suffix}`;
34
+ used.add(id);
35
+ return id;
36
+ }
37
+ /** Expand retired identities in place; an explicit successor always wins. */
38
+ export function expandRetiredPrompts(entries) {
39
+ const explicit = new Set(entries.flatMap((e) => (e.kind === "builtin" ? [e.builtin] : [])));
40
+ const usedIds = new Set(entries
41
+ .filter((entry) => entry.kind !== "builtin" ||
42
+ !retiredBuiltinReplacements[entry.builtin])
43
+ .map((entry) => entry.id));
44
+ return entries.flatMap((entry) => {
45
+ if (entry.kind !== "builtin")
46
+ return [entry];
47
+ const replacements = retiredBuiltinReplacements[entry.builtin];
48
+ if (!replacements)
49
+ return [entry];
50
+ return replacements
51
+ .filter((r) => !explicit.has(r.builtin))
52
+ .map(({ builtin, selected }) => {
53
+ const id = reserveBuiltinPromptId(builtin, builtin, usedIds);
54
+ return {
55
+ ...entry,
56
+ id,
57
+ builtin,
58
+ enabled: selected === false ? false : entry.enabled,
59
+ };
60
+ });
61
+ });
62
+ }
63
+ /**
64
+ * Missing defaults follow the nearest preceding catalog neighbor, otherwise the
65
+ * nearest following neighbor. Existing entries never move relative to each other.
66
+ */
67
+ export function resolvePromptArrangement(arrangement, catalog, worldRequired = true) {
68
+ const defaults = catalog.map((item) => ({
69
+ id: item.id,
70
+ kind: "builtin",
71
+ builtin: item.id,
72
+ enabled: defaultEnabledBuiltin(item, catalog),
73
+ }));
74
+ if (worldRequired)
75
+ defaults.push({ id: "world", kind: "world" });
76
+ const overrides = expandRetiredPrompts((arrangement.overrides ?? []).map((o) => ({
77
+ ...o,
78
+ id: o.id ?? o.builtin,
79
+ kind: "builtin",
80
+ enabled: o.enabled ??
81
+ (catalog.some((c) => c.id === o.builtin)
82
+ ? defaultEnabledBuiltin(catalog.find((c) => c.id === o.builtin), catalog)
83
+ : true),
84
+ })));
85
+ const values = defaults.map((entry) => {
86
+ if (entry.kind !== "builtin")
87
+ return entry;
88
+ const override = overrides.find((o) => o.kind === "builtin" && o.builtin === entry.builtin);
89
+ const item = catalog.find((c) => c.id === entry.builtin);
90
+ const selection = item.group === undefined ? undefined : arrangement.groups?.[item.group];
91
+ const selected = selection?.flatMap((id) => retiredBuiltinReplacements[id]
92
+ ?.filter((r) => r.selected !== false)
93
+ .map((r) => r.builtin) ?? [id]);
94
+ return {
95
+ ...entry,
96
+ ...override,
97
+ ...(selected === undefined
98
+ ? {}
99
+ : { enabled: selected.includes(item.id) }),
100
+ };
101
+ });
102
+ const custom = structuredClone(arrangement.entries ?? []);
103
+ const world = custom.find((e) => e.kind === "world");
104
+ if (world !== undefined)
105
+ values.splice(values.findIndex((e) => e.kind === "world"), 1, world);
106
+ values.push(...custom.filter((e) => e.kind !== "world"));
107
+ // A newly shipped identity must not collide with an existing user's identity.
108
+ const aliases = new Map((arrangement.overrides ?? []).flatMap((override) => override.id === undefined ||
109
+ !catalog.some((item) => item.id === override.builtin)
110
+ ? []
111
+ : [[override.builtin, override.id]]));
112
+ const usedIds = new Set([...custom.map((e) => e.id), ...aliases.values()]);
113
+ for (const entry of values) {
114
+ if (entry.kind !== "builtin")
115
+ continue;
116
+ entry.id =
117
+ aliases.get(entry.builtin) ??
118
+ reserveBuiltinPromptId(entry.id, entry.builtin, usedIds);
119
+ }
120
+ const byKey = new Map(values.map((e) => [promptArrangementKey(e), e]));
121
+ const explicitOrder = new Set(arrangement.order ?? []);
122
+ const order = (arrangement.order ?? []).flatMap((key) => {
123
+ const old = key.startsWith("builtin:")
124
+ ? retiredBuiltinReplacements[key.slice(8)]
125
+ : undefined;
126
+ return old
127
+ ? old
128
+ .filter((r) => !explicitOrder.has(`builtin:${r.builtin}`))
129
+ .map((r) => `builtin:${r.builtin}`)
130
+ : [key];
131
+ });
132
+ const result = [];
133
+ for (const key of order) {
134
+ const entry = byKey.get(key);
135
+ if (entry && !result.includes(entry))
136
+ result.push(entry);
137
+ }
138
+ const defaultKeys = defaults.map(promptArrangementKey);
139
+ for (const entry of values) {
140
+ if (result.includes(entry))
141
+ continue;
142
+ const position = defaultKeys.indexOf(promptArrangementKey(entry));
143
+ const previous = defaultKeys
144
+ .slice(0, position)
145
+ .reverse()
146
+ .map((key) => result.findIndex((e) => promptArrangementKey(e) === key))
147
+ .find((i) => i >= 0);
148
+ const following = defaultKeys
149
+ .slice(position + 1)
150
+ .map((key) => result.findIndex((e) => promptArrangementKey(e) === key))
151
+ .find((i) => i >= 0);
152
+ const at = position < 0
153
+ ? result.length
154
+ : previous !== undefined
155
+ ? previous + 1
156
+ : (following ?? result.length);
157
+ result.splice(at, 0, entry);
158
+ }
159
+ return result;
160
+ }
161
+ export function customizePromptArrangement(input, catalog, worldRequired = true) {
162
+ const entries = expandRetiredPrompts(input);
163
+ const arrangement = {};
164
+ const custom = entries.filter((e) => e.kind !== "builtin" &&
165
+ !(e.kind === "world" && e.id === "world" && e.messageRole === undefined));
166
+ if (custom.length)
167
+ arrangement.entries = structuredClone(custom);
168
+ const overrides = [];
169
+ for (const entry of entries) {
170
+ if (entry.kind !== "builtin")
171
+ continue;
172
+ const item = catalog.find((c) => c.id === entry.builtin);
173
+ if (!item)
174
+ throw new Error(`Unknown builtin: ${entry.builtin}`);
175
+ const override = {
176
+ builtin: entry.builtin,
177
+ };
178
+ if (entry.id !== entry.builtin)
179
+ override.id = entry.id;
180
+ if (item.group === undefined &&
181
+ (!entry.enabled || entry.enabled !== defaultEnabledBuiltin(item, catalog)))
182
+ override.enabled = entry.enabled;
183
+ if (entry.messageRole !== undefined)
184
+ override.messageRole = entry.messageRole;
185
+ if (entry.turnGuide !== undefined)
186
+ override.turnGuide = entry.turnGuide;
187
+ if (Object.keys(override).length > 1)
188
+ overrides.push(override);
189
+ if (item.group !== undefined) {
190
+ arrangement.groups ??= {};
191
+ arrangement.groups[item.group] ??= [];
192
+ if (entry.enabled)
193
+ arrangement.groups[item.group].push(item.id);
194
+ }
195
+ }
196
+ if (overrides.length)
197
+ arrangement.overrides = overrides;
198
+ const keys = entries.map(promptArrangementKey);
199
+ const effective = resolvePromptArrangement(arrangement, catalog, worldRequired);
200
+ const existing = new Set(keys);
201
+ if (JSON.stringify(keys) !==
202
+ JSON.stringify(effective.map(promptArrangementKey).filter((key) => existing.has(key))))
203
+ arrangement.order = keys;
204
+ return arrangement;
205
+ }
@@ -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
+ }