Implement {{charFirstMessage}} / {{greeting}} macro with alternate greeting indexing and substitution (#5220)
* Initial plan
* Implement {{firstMessage}} / {{greeting}} macro for character's first message
Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>
* Simplify firstMessage resolver to use || instead of ?? for consistency
Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>
* Remove {{firstMessage}} alias, add 0-based greeting index for alternate greetings
Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>
* Add alternateGreetings to env, apply substitution to greetings
Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>
* Remove legacy greeting macro, keep only new engine implementation
Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Cohee1207 <18619528+Cohee1207@users.noreply.github.com>
Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
This commit is contained in:
@@ -3230,6 +3230,8 @@ export function baseChatReplace(value, name1Override = null, name2Override = nul
|
||||
* @property {string} version Character version
|
||||
* @property {string} charDepthPrompt Character depth note
|
||||
* @property {string} creatorNotes Character creator notes
|
||||
* @property {string} firstMessage Character first message / greeting
|
||||
* @property {string[]} alternateGreetings Character alternate greetings
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -3316,6 +3318,17 @@ export function getCharacterCardFieldsLazy({ chid = undefined } = {}) {
|
||||
const exampleDialog = chat_metadata.mes_example || character.mes_example || '';
|
||||
return baseChatReplace(exampleDialog.trim());
|
||||
},
|
||||
firstMessage: () => {
|
||||
if (!character) return '';
|
||||
const firstMes = character.first_mes?.trim() || '';
|
||||
return baseChatReplace(firstMes);
|
||||
},
|
||||
alternateGreetings: () => {
|
||||
if (!character) return [];
|
||||
const altGreetings = character.data?.alternate_greetings;
|
||||
if (!Array.isArray(altGreetings)) return [];
|
||||
return altGreetings.map(greeting => baseChatReplace(greeting?.trim()));
|
||||
},
|
||||
};
|
||||
|
||||
return createLazyFields(resolvers);
|
||||
@@ -3342,6 +3355,8 @@ export function getCharacterCardFields({ chid = undefined } = {}) {
|
||||
version: lazy.version,
|
||||
charDepthPrompt: lazy.charDepthPrompt,
|
||||
creatorNotes: lazy.creatorNotes,
|
||||
firstMessage: lazy.firstMessage,
|
||||
alternateGreetings: lazy.alternateGreetings,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +140,30 @@ export function registerEnvMacros() {
|
||||
handler: ({ env }) => env.character.creatorNotes ?? '',
|
||||
});
|
||||
|
||||
MacroRegistry.registerMacro('charFirstMessage', {
|
||||
aliases: [{ alias: 'greeting' }],
|
||||
category: MacroCategory.CHARACTER,
|
||||
unnamedArgs: [
|
||||
{
|
||||
name: 'index',
|
||||
optional: true,
|
||||
defaultValue: '0',
|
||||
type: MacroValueType.INTEGER,
|
||||
description: '0-based index. 0 (default) returns the main greeting, 1 and up return alternate greetings.',
|
||||
},
|
||||
],
|
||||
description: 'The character\'s first message / greeting. Optionally specify an index to access alternate greetings.',
|
||||
returns: 'Character greeting at the given index, or empty string if out of bounds.',
|
||||
exampleUsage: ['{{greeting}}', '{{greeting::0}}', '{{greeting::1}}'],
|
||||
handler: ({ env, unnamedArgs: [index] }) => {
|
||||
const i = Number(index ?? 0);
|
||||
if (i === 0) return env.character.firstMessage ?? '';
|
||||
const altGreetings = env.character.alternateGreetings;
|
||||
if (!Array.isArray(altGreetings)) return '';
|
||||
return altGreetings[i - 1] ?? '';
|
||||
},
|
||||
});
|
||||
|
||||
// Character version macros (legacy variants and documented {{charVersion}})
|
||||
MacroRegistry.registerMacro('charVersion', {
|
||||
aliases: [
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
* @property {string} [charDepthPrompt]
|
||||
* @property {string} [creatorNotes]
|
||||
* @property {string} [version]
|
||||
* @property {string} [firstMessage]
|
||||
* @property {string[]} [alternateGreetings]
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -109,10 +109,19 @@ class MacroEnvBuilder {
|
||||
['version', 'version'],
|
||||
['charDepthPrompt', 'charDepthPrompt'],
|
||||
['creatorNotes', 'creatorNotes'],
|
||||
['firstMessage', 'firstMessage'],
|
||||
['alternateGreetings', 'alternateGreetings'],
|
||||
]);
|
||||
for (const [envKey, fieldKey] of fieldMappings) {
|
||||
Object.defineProperty(env.character, envKey, {
|
||||
get() { return fields[fieldKey] || ''; },
|
||||
get() {
|
||||
const value = fields[fieldKey];
|
||||
// alternateGreetings should default to [] instead of ''
|
||||
if (envKey === 'alternateGreetings') {
|
||||
return Array.isArray(value) ? value : [];
|
||||
}
|
||||
return value || '';
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
@@ -98,6 +98,8 @@ test.describe('MacroEnvBuilder', () => {
|
||||
'version',
|
||||
'charDepthPrompt',
|
||||
'creatorNotes',
|
||||
'firstMessage',
|
||||
'alternateGreetings',
|
||||
]));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user