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

@@ -153,9 +153,34 @@
Enable Author's Note
</label>
</div>
<button type="button" id="save-authors-note-btn" class="btn-primary" style="width: 100%;">
<button type="button" id="save-authors-note-btn" class="btn-primary" style="width: 100%; margin-bottom: 20px;">
Save Author's Note
</button>
<!-- Message Examples Section -->
<div class="form-group" style="border-top: 1px solid var(--border); padding-top: 16px;">
<label>Message Examples</label>
<p style="color: var(--text-secondary); font-size: 12px; margin-bottom: 8px;">
Use character card's message examples to teach the AI the character's voice and style.
</p>
<label>
<input type="checkbox" id="examples-enabled" />
Enable Message Examples
</label>
</div>
<div class="form-group">
<label for="examples-position">Examples Position</label>
<select id="examples-position" style="width: 100%;">
<option value="after_system">After System Prompt (Recommended)</option>
<option value="before_history">Before Message History</option>
</select>
<p style="color: var(--text-secondary); font-size: 11px; margin-top: 4px;">
Where to inject examples in the context. After system prompt works best for most models.
</p>
</div>
<button type="button" id="save-examples-btn" class="btn-primary" style="width: 100%;">
Save Examples Settings
</button>
</div>
</div>
@@ -671,6 +696,10 @@
<span>Author's Note:</span>
<span id="token-authorsnote">0</span>
</div>
<div class="token-breakdown-item">
<span>Message Examples:</span>
<span id="token-examples">0</span>
</div>
<div class="token-breakdown-item">
<span>Message History:</span>
<span id="token-history">0</span>

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;