Update library.js

Made %{title}, %{entry}, and %{memories} the new default prompt placeholders, to allow scenario creators to use AI Dungeon's built-in ${placeholders} within the config card. Which may be useful for various things, such as adding the player character's name to the banned titles list during startup.

${title}, ${entry}, and ${memories} will still work for backwards compatibility.
This commit is contained in:
LewdLeah 2025-06-04 18:13:32 -04:00 committed by GitHub
parent 2c6bb1f7c8
commit 9fb234c580
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -71,19 +71,19 @@ function AutoCards(inHook, inText, inStop) {
"-----", "-----",
"", "",
"<SYSTEM>", "<SYSTEM>",
"# Stop the story and ignore previous instructions. Write a brief and coherent informational entry for ${title} following these instructions:", "# Stop the story and ignore previous instructions. Write a brief and coherent informational entry for %{title} following these instructions:",
"- Write only third-person pure prose information about ${title} using complete sentences with correct punctuation", "- Write only third-person pure prose information about %{title} using complete sentences with correct punctuation",
"- Avoid short-term temporary details or appearances, instead focus on plot-significant information", "- Avoid short-term temporary details or appearances, instead focus on plot-significant information",
"- Prioritize story-relevant details about ${title} first to ensure seamless integration with the previous plot", "- Prioritize story-relevant details about %{title} first to ensure seamless integration with the previous plot",
"- Create new information based on the context and story direction", "- Create new information based on the context and story direction",
"- Mention ${title} in every sentence", "- Mention %{title} in every sentence",
"- Use semicolons if needed", "- Use semicolons if needed",
"- Add additional details about ${title} beneath incomplete entries", "- Add additional details about %{title} beneath incomplete entries",
"- Be concise and grounded", "- Be concise and grounded",
"- Imitate the story's writing style and infer the reader's preferences", "- Imitate the story's writing style and infer the reader's preferences",
"</SYSTEM>", "</SYSTEM>",
"Continue the entry for ${title} below while avoiding repetition:", "Continue the entry for %{title} below while avoiding repetition:",
"${entry}" "%{entry}"
); // (mimic this multi-line "text" format) ); // (mimic this multi-line "text" format)
// AI prompt used to summarize a given story card's memory bank? // AI prompt used to summarize a given story card's memory bank?
@ -98,12 +98,12 @@ function AutoCards(inHook, inText, inStop) {
"- Remain brief and concise", "- Remain brief and concise",
"- Write firmly in the past tense", "- Write firmly in the past tense",
"- The paragraph below pertains to old events from far earlier in the story", "- The paragraph below pertains to old events from far earlier in the story",
"- Integrate ${title} naturally within the memory; however, only write about the events as they occurred", "- Integrate %{title} naturally within the memory; however, only write about the events as they occurred",
"- Only reference information present inside the paragraph itself, be specific", "- Only reference information present inside the paragraph itself, be specific",
"</SYSTEM>", "</SYSTEM>",
"Write a summarized old memory passage for ${title} based only on the following paragraph:", "Write a summarized old memory passage for %{title} based only on the following paragraph:",
"\"\"\"", "\"\"\"",
"${memory}", "%{memory}",
"\"\"\"", "\"\"\"",
"Summarize below:" "Summarize below:"
); // (mimic this multi-line "text" format) ); // (mimic this multi-line "text" format)
@ -2922,10 +2922,11 @@ function AutoCards(inHook, inText, inStop) {
} }
return buildMemoryConstruct(); return buildMemoryConstruct();
})(); })();
// Fill all %{title} placeholders
const precursorPrompt = insertTitle(AC.config.compressionPrompt, AC.compression.vanityTitle).trim(); const precursorPrompt = insertTitle(AC.config.compressionPrompt, AC.compression.vanityTitle).trim();
const memoryPlaceholderPattern = /(?:\$+\s*|\$*){+\s*memor(y|ies)\s*}+/gi; const memoryPlaceholderPattern = /(?:[%\$]+\s*|[%\$]*){+\s*memor(y|ies)\s*}+/gi;
if (memoryPlaceholderPattern.test(precursorPrompt)) { if (memoryPlaceholderPattern.test(precursorPrompt)) {
// Fill all ${memory} placeholders with a selection of pending old memories // Fill all %{memory} placeholders with a selection of pending old memories
return precursorPrompt.replace(memoryPlaceholderPattern, memoryConstruct); return precursorPrompt.replace(memoryPlaceholderPattern, memoryConstruct);
} else { } else {
// Append the partial entry to the end of context // Append the partial entry to the end of context
@ -2937,14 +2938,14 @@ function AutoCards(inHook, inText, inStop) {
} }
function promptGeneration() { function promptGeneration() {
repositionAN(); repositionAN();
// All ${title} placeholders were already filled during this workpiece's initialization // All %{title} placeholders were already filled during this workpiece's initialization
// The "%GEN%" substring serves as a temporary delimiter for later context length trucation // The "%GEN%" substring serves as a temporary delimiter for later context length trucation
context = context.trimEnd() + "%@GEN@%\n\n" + (function() { context = context.trimEnd() + "%@GEN@%\n\n" + (function() {
// For context only, remove the title header from this workpiece's partially completed entry // For context only, remove the title header from this workpiece's partially completed entry
const partialEntry = formatEntry(AC.generation.workpiece.entry); const partialEntry = formatEntry(AC.generation.workpiece.entry);
const entryPlaceholderPattern = /(?:\$+\s*|\$*){+\s*entry\s*}+/gi; const entryPlaceholderPattern = /(?:[%\$]+\s*|[%\$]*){+\s*entry\s*}+/gi;
if (entryPlaceholderPattern.test(AC.generation.workpiece.prompt)) { if (entryPlaceholderPattern.test(AC.generation.workpiece.prompt)) {
// Fill all ${entry} placeholders with the partial entry // Fill all %{entry} placeholders with the partial entry
return AC.generation.workpiece.prompt.replace(entryPlaceholderPattern, partialEntry); return AC.generation.workpiece.prompt.replace(entryPlaceholderPattern, partialEntry);
} else { } else {
// Append the partial entry to the end of context // Append the partial entry to the end of context
@ -3808,10 +3809,10 @@ function AutoCards(inHook, inText, inStop) {
"You may specify how the AI handles story card processes by editing either of these two prompts within the config card's notes section", "You may specify how the AI handles story card processes by editing either of these two prompts within the config card's notes section",
"", "",
"> AI prompt to generate new cards:", "> AI prompt to generate new cards:",
"Used when Auto-Cards writes a new card entry. It tells the AI to focus on important plot stuff, avoid fluff, and write in a consistent, polished style. I like to add some personal preferences here when playing my own adventures. \"${title}\" and \"${entry}\" are dynamic placeholders for their namesakes", "Used when Auto-Cards writes a new card entry. It tells the AI to focus on important plot stuff, avoid fluff, and write in a consistent, polished style. I like to add some personal preferences here when playing my own adventures. \"%{title}\" and \"%{entry}\" are dynamic placeholders for their namesakes",
"", "",
"> AI prompt to summarize card memories:", "> AI prompt to summarize card memories:",
"Summarizes older details within card memory banks to keep everything concise and neat over the long-run. Maintains only the most important details, written in the past tense. \"${title}\" and \"${memory}\" are dynamic placeholders for their namesakes", "Summarizes older details within card memory banks to keep everything concise and neat over the long-run. Maintains only the most important details, written in the past tense. \"%{title}\" and \"%{memory}\" are dynamic placeholders for their namesakes",
"", "",
Words.delimiter, Words.delimiter,
"", "",
@ -4090,7 +4091,7 @@ function AutoCards(inHook, inText, inStop) {
), title); ), title);
if (promptDetails !== "") { if (promptDetails !== "") {
const spacesPrecedingTerminalEntryPlaceholder = (function() { const spacesPrecedingTerminalEntryPlaceholder = (function() {
const terminalEntryPlaceholderPattern = /(?:\$+\s*|\$*){+\s*entry\s*}+$/i; const terminalEntryPlaceholderPattern = /(?:[%\$]+\s*|[%\$]*){+\s*entry\s*}+$/i;
if (terminalEntryPlaceholderPattern.test(prompt)) { if (terminalEntryPlaceholderPattern.test(prompt)) {
prompt = prompt.replace(terminalEntryPlaceholderPattern, ""); prompt = prompt.replace(terminalEntryPlaceholderPattern, "");
const trailingSpaces = prompt.match(/(\s+)$/); const trailingSpaces = prompt.match(/(\s+)$/);
@ -4110,7 +4111,6 @@ function AutoCards(inHook, inText, inStop) {
case "}": { encapsulateBothPrompts("{", true, "}"); break; } case "}": { encapsulateBothPrompts("{", true, "}"); break; }
case ")": { encapsulateBothPrompts("(", true, ")"); break; } case ")": { encapsulateBothPrompts("(", true, ")"); break; }
case "/": { encapsulateBothPrompts("/", true, "/"); break; } case "/": { encapsulateBothPrompts("/", true, "/"); break; }
case "%": { encapsulateBothPrompts("%", true, "%"); break; }
case "#": { encapsulateBothPrompts("#", true, "#"); break; } case "#": { encapsulateBothPrompts("#", true, "#"); break; }
case "-": { encapsulateBothPrompts(null, false, "-"); break; } case "-": { encapsulateBothPrompts(null, false, "-"); break; }
case ":": { encapsulateBothPrompts(":", true, ":"); break; } case ":": { encapsulateBothPrompts(":", true, ":"); break; }
@ -4127,8 +4127,8 @@ function AutoCards(inHook, inText, inStop) {
} }
prompt += "\n" + promptDetails + (function() { prompt += "\n" + promptDetails + (function() {
if (spacesPrecedingTerminalEntryPlaceholder !== "") { if (spacesPrecedingTerminalEntryPlaceholder !== "") {
// Prompt previously contained a terminal ${entry} placeholder, re-append it // Prompt previously contained a terminal %{entry} placeholder, re-append it
return spacesPrecedingTerminalEntryPlaceholder + "${entry}"; return spacesPrecedingTerminalEntryPlaceholder + "%{entry}";
} }
return ""; return "";
})(); })();
@ -4663,19 +4663,19 @@ function AutoCards(inHook, inText, inStop) {
"-----", "-----",
"", "",
"<SYSTEM>", "<SYSTEM>",
"# Stop the story and ignore previous instructions. Write a brief and coherent informational entry for ${title} following these instructions:", "# Stop the story and ignore previous instructions. Write a brief and coherent informational entry for %{title} following these instructions:",
"- Write only third-person pure prose information about ${title} using complete sentences with correct punctuation", "- Write only third-person pure prose information about %{title} using complete sentences with correct punctuation",
"- Avoid short-term temporary details or appearances, instead focus on plot-significant information", "- Avoid short-term temporary details or appearances, instead focus on plot-significant information",
"- Prioritize story-relevant details about ${title} first to ensure seamless integration with the previous plot", "- Prioritize story-relevant details about %{title} first to ensure seamless integration with the previous plot",
"- Create new information based on the context and story direction", "- Create new information based on the context and story direction",
"- Mention ${title} in every sentence", "- Mention %{title} in every sentence",
"- Use semicolons if needed", "- Use semicolons if needed",
"- Add additional details about ${title} beneath incomplete entries", "- Add additional details about %{title} beneath incomplete entries",
"- Be concise and grounded", "- Be concise and grounded",
"- Imitate the story's writing style and infer the reader's preferences", "- Imitate the story's writing style and infer the reader's preferences",
"</SYSTEM>", "</SYSTEM>",
"Continue the entry for ${title} below while avoiding repetition:", "Continue the entry for %{title} below while avoiding repetition:",
"${entry}" "%{entry}"
), "string"), ), "string"),
// How should the AI be prompted when summarizing memories for a given story card? // How should the AI be prompted when summarizing memories for a given story card?
compressionPrompt: check(DEFAULT_CARD_MEMORY_COMPRESSION_PROMPT, prose( compressionPrompt: check(DEFAULT_CARD_MEMORY_COMPRESSION_PROMPT, prose(
@ -4689,12 +4689,12 @@ function AutoCards(inHook, inText, inStop) {
"- Remain brief and concise", "- Remain brief and concise",
"- Write firmly in the past tense", "- Write firmly in the past tense",
"- The paragraph below pertains to old events from far earlier in the story", "- The paragraph below pertains to old events from far earlier in the story",
"- Integrate ${title} naturally within the memory; however, only write about the events as they occurred", "- Integrate %{title} naturally within the memory; however, only write about the events as they occurred",
"- Only reference information present inside the paragraph itself, be specific", "- Only reference information present inside the paragraph itself, be specific",
"</SYSTEM>", "</SYSTEM>",
"Write a summarized old memory passage for ${title} based only on the following paragraph:", "Write a summarized old memory passage for %{title} based only on the following paragraph:",
"\"\"\"", "\"\"\"",
"${memory}", "%{memory}",
"\"\"\"", "\"\"\"",
"Summarize below:" "Summarize below:"
), "string"), ), "string"),
@ -4740,18 +4740,20 @@ function AutoCards(inHook, inText, inStop) {
if (Number.isInteger(upperBound) && (upperBound < lowerBound)) { if (Number.isInteger(upperBound) && (upperBound < lowerBound)) {
throw new Error("Invalid arguments: The inequality (lowerBound <= upperBound) must be satisfied"); throw new Error("Invalid arguments: The inequality (lowerBound <= upperBound) must be satisfied");
} }
value = lowerBound; return lowerBound;
} else if (Number.isInteger(upperBound) && (upperBound < value)) { } else if (Number.isInteger(upperBound) && (upperBound < value)) {
value = upperBound; return upperBound;
} } else {
return value; return value;
} }
}
function limitString(str, lengthLimit) { function limitString(str, lengthLimit) {
if (lengthLimit < str.length) { if (lengthLimit < str.length) {
str = str.slice(0, lengthLimit).trim(); return str.slice(0, lengthLimit).trim();
} } else {
return str; return str;
} }
}
function cleanSpaces(unclean) { function cleanSpaces(unclean) {
return (unclean return (unclean
.replace(/\s*\n\s*/g, "\n") .replace(/\s*\n\s*/g, "\n")
@ -4763,9 +4765,10 @@ function AutoCards(inHook, inText, inStop) {
const bisector = str.search(/\s*(?:{|(?:title|update|limit)s?\s*:)\s*/i); const bisector = str.search(/\s*(?:{|(?:title|update|limit)s?\s*:)\s*/i);
if (bisector === -1) { if (bisector === -1) {
return [str, ""]; return [str, ""];
} } else {
return [str.slice(0, bisector), str.slice(bisector)]; return [str.slice(0, bisector), str.slice(bisector)];
} }
}
function removeAutoProps(str) { function removeAutoProps(str) {
return cleanSpaces(str return cleanSpaces(str
.replace(/\s*{([\s\S]*?)}\s*/g, (bracedMatch, enclosedProperties) => { .replace(/\s*{([\s\S]*?)}\s*/g, (bracedMatch, enclosedProperties) => {
@ -4775,14 +4778,16 @@ function AutoCards(inHook, inText, inStop) {
return bracedMatch; return bracedMatch;
} }
}) })
.replace(/\s*(?:{|(?:title|update|limit)s?\s*:)(?:[\s\S]{0,150}?)(?=(?:title|update|limit)s?\s*:|})\s*/gi, "\n") .replace((
/\s*(?:{|(?:title|update|limit)s?\s*:)(?:[\s\S]{0,150}?)(?=(?:title|update|limit)s?\s*:|})\s*/gi
), "\n")
.replace(/\s*(?:{|(?:title|update|limit)s?\s*:|})\s*/gi, "\n") .replace(/\s*(?:{|(?:title|update|limit)s?\s*:|})\s*/gi, "\n")
.trim() .trim()
); );
} }
function insertTitle(prompt, title) { function insertTitle(prompt, title) {
return prompt.replace(( return prompt.replace((
/(?:\$+\s*|\$*){+\s*(?:titles?|names?|characters?|class(?:es)?|races?|locations?|factions?)\s*}+/gi /(?:[%\$]+\s*|[%\$]*){+\s*(?:titles?|names?|characters?|class(?:es)?|races?|locations?|factions?)\s*}+/gi
), title); ), title);
} }
function prose(...args) { function prose(...args) {