feat: implement message examples usage from character cards

Add support for using mes_example field from character cards to teach the AI the character's voice and writing style. Examples are parsed, processed with template variable replacement, and injected into the context at a configurable position.

Backend changes:
- Extended RoleplaySettings with examples_enabled and examples_position fields
- Implemented parse_message_examples() to parse <START>-delimited example blocks
- Added example injection in build_api_messages() with position control
- Integrated examples into token counter with accurate counting
- Created update_examples_settings command for saving settings

Frontend changes:
- Added Message Examples UI controls in Author's Note tab
- Checkbox to enable/disable examples
- Dropdown to select injection position (after_system/before_history)
- Save button with success/error feedback
- Token breakdown now shows examples token count
- Settings load/save integrated with roleplay panel

Message examples help the AI understand character personality, speaking patterns, and response style by providing concrete examples of how the character should respond.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-16 17:33:50 -07:00
parent b9230772ed
commit d8cb4a768b
3 changed files with 189 additions and 2 deletions

View File

@@ -1561,6 +1561,7 @@ function setupAppControls() {
document.getElementById('add-worldinfo-btn').addEventListener('click', handleAddWorldInfoEntry);
document.getElementById('save-authors-note-btn').addEventListener('click', handleSaveAuthorsNote);
document.getElementById('save-persona-btn').addEventListener('click', handleSavePersona);
document.getElementById('save-examples-btn').addEventListener('click', handleSaveExamples);
// Setup recursion depth change handler
document.getElementById('recursion-depth').addEventListener('change', handleRecursionDepthChange);
@@ -1622,6 +1623,7 @@ async function updateTokenCount() {
document.getElementById('token-persona').textContent = tokenData.persona;
document.getElementById('token-worldinfo').textContent = tokenData.world_info;
document.getElementById('token-authorsnote').textContent = tokenData.authors_note;
document.getElementById('token-examples').textContent = tokenData.message_examples;
document.getElementById('token-history').textContent = tokenData.message_history;
document.getElementById('token-input').textContent = tokenData.current_input;
document.getElementById('token-total-detail').textContent = tokenData.total;
@@ -2012,6 +2014,10 @@ async function loadRoleplaySettings() {
document.getElementById('persona-description').value = settings.persona_description || '';
document.getElementById('persona-enabled').checked = settings.persona_enabled || false;
// Load Message Examples
document.getElementById('examples-enabled').checked = settings.examples_enabled || false;
document.getElementById('examples-position').value = settings.examples_position || 'after_system';
// Load Presets
await loadPresets();
} catch (error) {
@@ -2243,6 +2249,29 @@ async function handleSavePersona() {
}
}
// Save Message Examples Settings
async function handleSaveExamples() {
if (!currentCharacter) return;
const enabled = document.getElementById('examples-enabled').checked;
const position = document.getElementById('examples-position').value;
try {
await invoke('update_examples_settings', {
characterId: currentCharacter.id,
enabled,
position
});
// Show success message
setStatus('Message Examples settings saved', 'success');
setTimeout(() => setStatus('Ready'), 2000);
} catch (error) {
console.error('Failed to save Message Examples settings:', error);
setStatus('Failed to save Message Examples settings', 'error');
}
}
// Handle recursion depth change
async function handleRecursionDepthChange() {
if (!currentCharacter) return;