narraeon 0.7.9 → 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.
- package/dist/node/protocol/modelConnections.js +15 -0
- package/dist/node/protocol/modelUsage.js +4 -0
- package/dist/node/protocol/sillyTavernContent.js +1 -0
- package/dist/node/protocol/v1.js +75 -0
- package/dist/node/runtime/V1Runtime.js +71 -1
- package/dist/node/runtime/authoring/AuthoringRequestSnapshot.js +15 -3
- package/dist/node/runtime/authoring/DamagedGoogleContinuation.js +68 -0
- package/dist/node/runtime/authoring/ModelToolConversation.js +47 -22
- package/dist/node/runtime/content/ContentWorkspace.js +3 -0
- package/dist/node/runtime/content/SillyTavernContent.js +796 -0
- package/dist/node/runtime/model/AiFailureLog.js +8 -1
- package/dist/node/runtime/model/FileNativeModelAdapters.js +610 -18
- package/dist/node/runtime/model/GoogleHopJournal.js +90 -0
- package/dist/node/runtime/model/GoogleInteractionsStream.js +242 -0
- package/dist/node/runtime/model/GoogleRequestPreview.js +184 -0
- package/dist/node/runtime/model/ModelConnectionStore.js +4 -1
- package/dist/node/runtime/model/ProviderExchangeState.js +44 -1
- package/dist/node/runtime/model/ProviderStream.js +4 -2
- package/dist/node/runtime/play/FileNativePlayAdvanceStore.js +4 -4
- package/dist/node/runtime/play/FileNativePlayPresetStore.js +150 -78
- package/dist/node/runtime/play/FileNativePlayTimelineStore.js +150 -10
- package/dist/node/runtime/play/OrderedPlayPrompts.js +139 -178
- package/dist/node/runtime/play/PlayAdvanceLifecycle.js +33 -9
- package/dist/node/runtime/play/PlayCallChain.js +76 -4
- package/dist/node/runtime/play/PlayFollowupRequests.js +23 -3
- package/dist/node/runtime/prompt/PromptCompilationCodec.js +2 -1
- package/dist/node/runtime/setting/FileNativeSettingImprovementStore.js +112 -8
- package/dist/node/runtime/setting/SettingAuthoringTransaction.js +100 -41
- package/dist/node/runtime/setting/SettingImprovementSession.js +48 -7
- package/dist/node/runtime/world-revision/FileNativeWorldRevisionStore.js +84 -8
- package/dist/node/runtime/world-revision/WorldRevisionSession.js +125 -17
- package/dist/node/shared/application-preset-files.js +29 -0
- package/dist/node/shared/default-preset-host.js +50 -38
- package/dist/node/shared/first-party-action-choices.js +7 -11
- package/dist/node/shared/first-party-generic-panels.js +7 -11
- package/dist/node/shared/first-party-player-view.js +7 -11
- package/dist/node/shared/ordered-author-prompts.js +6 -4
- package/dist/node/shared/ordered-play-prompts.js +4 -2
- package/dist/node/shared/prompt-arrangement.js +205 -0
- package/dist/node/shared/sillytavern-preset.js +454 -0
- package/dist/web/assets/{index-DH2BVsYd.css → index-BKA1D0Rz.css} +1 -1
- package/dist/web/assets/index-C_Qta8UL.js +1432 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- 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
|
+
}
|