Merge pull request #2512 from kwaroran/ccv3-write

CCv3 Partial Implementation
This commit is contained in:
Cohee 2024-07-14 23:18:27 +03:00 committed by GitHub
commit 623cab0fa5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 93 additions and 1 deletions

View File

@ -464,6 +464,7 @@ export function evaluateMacros(content, env) {
content = content.replace(/{{firstIncludedMessageId}}/gi, () => String(getFirstIncludedMessageId() ?? ''));
content = content.replace(/{{lastSwipeId}}/gi, () => String(getLastSwipeId() ?? ''));
content = content.replace(/{{currentSwipeId}}/gi, () => String(getCurrentSwipeId() ?? ''));
content = content.replace(/{{reverse\:(.+?)}}/gi, (_, str) => Array.from(str).reverse().join(''));
content = content.replace(/\{\{\/\/([\s\S]*?)\}\}/gm, '');

View File

@ -28,6 +28,7 @@
<li><tt>&lcub;&lcub;firstIncludedMessageId&rcub;&rcub;</tt> <span data-i18n="help_macros_22">the ID of the first message included in the context. Requires generation to be ran at least once in the current session.</span></li>
<li><tt>&lcub;&lcub;currentSwipeId&rcub;&rcub;</tt> <span data-i18n="help_macros_23">the 1-based ID of the current swipe in the last chat message. Empty string if the last message is user or prompt-hidden.</span></li>
<li><tt>&lcub;&lcub;lastSwipeId&rcub;&rcub;</tt> <span data-i18n="help_macros_24">the number of swipes in the last chat message. Empty string if the last message is user or prompt-hidden.</span></li>
<li><tt>&lcub;&lcub;reverse:(content)&rcub;&rcub;</tt> <span data-i18n="help_macros_reverse">reverses the content of the macro.</span></li>
<li><tt>&lcub;&lcub;// (note)&rcub;&rcub;</tt> <span data-i18n="help_macros_25">you can leave a note here, and the macro will be replaced with blank content. Not visible for the AI.</span></li>
<li><tt>&lcub;&lcub;time&rcub;&rcub;</tt> <span data-i18n="help_macros_26">the current time</span></li>
<li><tt>&lcub;&lcub;date&rcub;&rcub;</tt> <span data-i18n="help_macros_27">the current date</span></li>

View File

@ -107,6 +107,7 @@ const METADATA_KEY = 'world_info';
const DEFAULT_DEPTH = 4;
const DEFAULT_WEIGHT = 100;
const MAX_SCAN_DEPTH = 1000;
const KNOWN_DECORATORS = ['@@activate', '@@dont_activate'];
// Typedef area
/**
@ -123,6 +124,7 @@ const MAX_SCAN_DEPTH = 1000;
* @property {number} [sticky] The sticky value of the entry
* @property {number} [cooldown] The cooldown of the entry
* @property {number} [delay] The delay of the entry
* @property {string[]} [decorators] Array of decorators for the entry
*/
/**
@ -3534,6 +3536,12 @@ export async function getSortedEntries() {
// Chat lore always goes first
entries = [...chatLore.sort(sortFn), ...entries];
// Parse decorators
entries = entries.map((entry) => {
const [decorators, content] = parseDecorators(entry.content);
return { ...entry, decorators, content };
});
console.debug(`[WI] Found ${entries.length} world lore entries. Sorted by strategy`, Object.entries(world_info_insertion_strategy).find((x) => x[1] === world_info_character_strategy));
// Need to deep clone the entries to avoid modifying the cached data
@ -3545,6 +3553,62 @@ export async function getSortedEntries() {
}
}
/**
* Parse decorators from worldinfo content
* @param {string} content The content to parse
* @returns {[string[],string]} The decorators found in the content and the content without decorators
*/
function parseDecorators(content) {
/**
* Check if the decorator is known
* @param {string} data string to check
* @returns {boolean} true if the decorator is known
*/
const isKnownDecorator = (data) => {
if (data.startsWith('@@@')) {
data = data.substring(1);
}
for (let i = 0; i < KNOWN_DECORATORS.length; i++) {
if (data.startsWith(KNOWN_DECORATORS[i])) {
return true;
}
}
return false;
};
if (content.startsWith('@@')) {
let newContent = content;
const splited = content.split('\n');
let decorators = [];
let fallbacked = false;
for (let i = 0; i < splited.length; i++) {
if (splited[i].startsWith('@@')) {
if (splited[i].startsWith('@@@') && !fallbacked) {
continue;
}
if (isKnownDecorator(splited[i])) {
decorators.push(splited[i].startsWith('@@@') ? splited[i].substring(1) : splited[i]);
fallbacked = false;
}
else {
fallbacked = true;
}
} else {
newContent = splited.slice(i).join('\n');
break;
}
}
return [decorators, newContent];
}
return [[], content];
}
/**
* Performs a scan on the chat and returns the world info activated.
* @param {string[]} chat The chat messages to scan, in reverse order.
@ -3686,6 +3750,17 @@ async function checkWorldInfo(chat, maxContext, isDryRun) {
continue;
}
if (entry.decorators.includes('@@activate')) {
log('activated by @@activate decorator');
activatedNow.add(entry);
continue;
}
if (entry.decorators.includes('@@dont_activate')) {
log('suppressed by @@dont_activate decorator');
continue;
}
// Now do checks for immediate activations
if (entry.constant) {
log('activated because of constant');

View File

@ -23,9 +23,21 @@ const write = (image, data) => {
}
}
// Add new chunks before the IEND chunk
// Add new v2 chunk before the IEND chunk
const base64EncodedData = Buffer.from(data, 'utf8').toString('base64');
chunks.splice(-1, 0, PNGtext.encode('chara', base64EncodedData));
// Try adding v3 chunk before the IEND chunk
try {
//change v2 format to v3
const v3Data = JSON.parse(data);
v3Data.spec = 'chara_card_v3';
v3Data.spec_version = '3.0';
const base64EncodedData = Buffer.from(JSON.stringify(v3Data), 'utf8').toString('base64');
chunks.splice(-1, 0, PNGtext.encode('ccv3', base64EncodedData));
} catch (error) { }
const newBuffer = Buffer.from(encode(chunks));
return newBuffer;
};

View File

@ -408,6 +408,9 @@ function charaFormatData(data, directories) {
//_.set(char, 'data.extensions.avatar', 'none');
//_.set(char, 'data.extensions.chat', data.ch_name + ' - ' + humanizedISO8601DateTime());
// V3 fields
_.set(char, 'data.group_only_greetings', data.group_only_greetings ?? []);
if (data.world) {
try {
const file = readWorldInfoFile(directories, data.world, false);