Implement comprehensive LLM provider system with global cost protection

- Add multi-provider LLM architecture supporting OpenRouter, OpenAI, Gemini, and custom providers
- Implement global LLM on/off switch with default DISABLED state for cost protection
- Add per-character LLM configuration with provider-specific models and settings
- Create performance-optimized caching system for LLM enabled status checks
- Add API key validation before enabling LLM providers to prevent broken configurations
- Implement audit logging for all LLM enable/disable actions for cost accountability
- Create comprehensive admin UI with prominent cost warnings and confirmation dialogs
- Add visual indicators in character list for custom AI model configurations
- Build character-specific LLM client system with global fallback mechanism
- Add database schema support for per-character LLM settings
- Implement graceful fallback responses when LLM is globally disabled
- Create provider testing and validation system for reliable connections
This commit is contained in:
root
2025-07-08 07:35:48 -07:00
parent 004f0325ec
commit 10563900a3
59 changed files with 6686 additions and 791 deletions

View File

@@ -515,9 +515,19 @@
<div class="character-item">
<div>
<div class="character-name">${char.name}</div>
<div class="character-info">${char.total_messages || 0} messages • ${char.status || 'Unknown'}</div>
<div class="character-info">${char.total_messages || 0} messages • ${char.is_active ? 'Active' : 'Disabled'}</div>
</div>
<div style="display: flex; gap: 10px; align-items: center;">
<button onclick="editCharacter('${char.name}')"
style="padding: 5px 10px; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; background: #667eea; color: white;">
Edit
</button>
<button onclick="toggleCharacter('${char.name}', ${char.is_active || false})"
style="padding: 5px 10px; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; ${char.is_active ? 'background: #ef4444; color: white;' : 'background: #10b981; color: white;'}">
${char.is_active ? 'Disable' : 'Enable'}
</button>
<span class="status ${char.is_active ? 'online' : 'offline'}">${char.is_active ? 'Enabled' : 'Disabled'}</span>
</div>
<span class="status ${char.status?.toLowerCase() || 'offline'}">${char.status || 'Offline'}</span>
</div>
`).join('');
@@ -1160,6 +1170,49 @@
}, 5000);
}
// Character management functions
async function toggleCharacter(characterName, isCurrentlyActive) {
try {
const newStatus = !isCurrentlyActive;
await apiCall(`/api/characters/${characterName}/toggle`, {
method: 'POST',
body: JSON.stringify({ is_active: newStatus })
});
showMessage(`Character ${characterName} ${newStatus ? 'enabled' : 'disabled'} successfully!`, 'success');
loadDashboardData(); // Refresh the display
} catch (error) {
showMessage(`Failed to toggle ${characterName}: ${error.message}`, 'error');
}
}
async function editCharacter(characterName) {
try {
// Get character details
const character = await apiCall(`/api/characters/${characterName}`);
// Create a simple edit form
const newGoals = prompt(`Edit goals for ${characterName} (comma-separated):`,
character.current_goals ? character.current_goals.join(', ') : '');
if (newGoals !== null) {
const updatedCharacter = {
...character,
current_goals: newGoals.split(',').map(g => g.trim()).filter(g => g)
};
await apiCall(`/api/characters/${characterName}`, {
method: 'PUT',
body: JSON.stringify(updatedCharacter)
});
showMessage(`Character ${characterName} updated successfully!`, 'success');
loadDashboardData(); // Refresh the display
}
} catch (error) {
showMessage(`Failed to edit ${characterName}: ${error.message}`, 'error');
}
}
// Auto-refresh dashboard
setInterval(() => {
const activeTab = document.querySelector('.tab-content.active');