Modell-Auswahl in Settings implementiert
- Neues SettingsPanel mit Modell-Auswahl (Haiku/Sonnet/Opus) - Modell wird in SQLite persistiert (claude_model Setting) - Bridge unterstützt set-model und get-models Commands - Modell kann zur Laufzeit gewechselt werden - Preisanzeige pro Modell im Settings-Panel - Aktuelles Modell wird beim App-Start geladen Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9129163876
commit
433e2de2b6
7 changed files with 459 additions and 13 deletions
|
|
@ -17,7 +17,14 @@ process.stdin.resume();
|
||||||
|
|
||||||
let activeAbort = null;
|
let activeAbort = null;
|
||||||
let currentAgentId = null;
|
let currentAgentId = null;
|
||||||
const MODEL = process.env.CLAUDE_MODEL || 'opus';
|
let currentModel = process.env.CLAUDE_MODEL || 'opus';
|
||||||
|
|
||||||
|
// Verfügbare Modelle
|
||||||
|
const AVAILABLE_MODELS = [
|
||||||
|
{ id: 'haiku', name: 'Claude Haiku', description: 'Schnell & günstig' },
|
||||||
|
{ id: 'sonnet', name: 'Claude Sonnet', description: 'Ausgewogen' },
|
||||||
|
{ id: 'opus', name: 'Claude Opus', description: 'Leistungsstark' },
|
||||||
|
];
|
||||||
|
|
||||||
// ============ Kommunikation mit Tauri ============
|
// ============ Kommunikation mit Tauri ============
|
||||||
|
|
||||||
|
|
@ -39,7 +46,10 @@ function sendError(id, error) {
|
||||||
|
|
||||||
// ============ Claude Agent SDK ============
|
// ============ Claude Agent SDK ============
|
||||||
|
|
||||||
async function sendMessage(message, requestId) {
|
async function sendMessage(message, requestId, model = null) {
|
||||||
|
// Modell für diese Anfrage (Parameter > State > Default)
|
||||||
|
const useModel = model || currentModel;
|
||||||
|
|
||||||
currentAgentId = randomUUID();
|
currentAgentId = randomUUID();
|
||||||
activeAbort = new AbortController();
|
activeAbort = new AbortController();
|
||||||
|
|
||||||
|
|
@ -47,19 +57,20 @@ async function sendMessage(message, requestId) {
|
||||||
id: currentAgentId,
|
id: currentAgentId,
|
||||||
type: 'Main',
|
type: 'Main',
|
||||||
task: message.substring(0, 100),
|
task: message.substring(0, 100),
|
||||||
|
model: useModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
sendResponse(requestId, { agentId: currentAgentId, status: 'gestartet' });
|
sendResponse(requestId, { agentId: currentAgentId, status: 'gestartet', model: useModel });
|
||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
let fullText = '';
|
let fullText = '';
|
||||||
let usedModel = MODEL;
|
let usedModel = useModel;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const conversation = query({
|
const conversation = query({
|
||||||
prompt: message,
|
prompt: message,
|
||||||
options: {
|
options: {
|
||||||
model: MODEL,
|
model: useModel,
|
||||||
maxTurns: 25,
|
maxTurns: 25,
|
||||||
abortController: activeAbort,
|
abortController: activeAbort,
|
||||||
},
|
},
|
||||||
|
|
@ -140,7 +151,8 @@ function handleCommand(msg) {
|
||||||
sendError(msg.id, 'Keine Nachricht angegeben');
|
sendError(msg.id, 'Keine Nachricht angegeben');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sendMessage(msg.message, msg.id);
|
// Modell kann pro Anfrage überschrieben werden
|
||||||
|
sendMessage(msg.message, msg.id, msg.model);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'stop':
|
case 'stop':
|
||||||
|
|
@ -150,10 +162,33 @@ function handleCommand(msg) {
|
||||||
sendResponse(msg.id, { status: 'gestoppt' });
|
sendResponse(msg.id, { status: 'gestoppt' });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'set-model':
|
||||||
|
if (!msg.model) {
|
||||||
|
sendError(msg.id, 'Kein Modell angegeben');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const validModels = AVAILABLE_MODELS.map(m => m.id);
|
||||||
|
if (!validModels.includes(msg.model)) {
|
||||||
|
sendError(msg.id, `Ungültiges Modell: ${msg.model}. Verfügbar: ${validModels.join(', ')}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentModel = msg.model;
|
||||||
|
sendResponse(msg.id, { model: currentModel, status: 'Modell geändert' });
|
||||||
|
sendEvent('model-changed', { model: currentModel });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'get-models':
|
||||||
|
sendResponse(msg.id, {
|
||||||
|
current: currentModel,
|
||||||
|
available: AVAILABLE_MODELS,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
case 'status':
|
case 'status':
|
||||||
sendResponse(msg.id, {
|
sendResponse(msg.id, {
|
||||||
model: MODEL,
|
model: currentModel,
|
||||||
isProcessing: !!currentAgentId,
|
isProcessing: !!currentAgentId,
|
||||||
|
availableModels: AVAILABLE_MODELS,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -186,4 +221,4 @@ process.on('SIGTERM', () => { clearInterval(keepAlive); process.exit(0); });
|
||||||
process.on('SIGINT', () => { clearInterval(keepAlive); process.exit(0); });
|
process.on('SIGINT', () => { clearInterval(keepAlive); process.exit(0); });
|
||||||
|
|
||||||
// Bereit
|
// Bereit
|
||||||
sendEvent('ready', { version: '1.0.0', pid: process.pid, model: MODEL });
|
sendEvent('ready', { version: '1.1.0', pid: process.pid, model: currentModel, availableModels: AVAILABLE_MODELS });
|
||||||
|
|
|
||||||
|
|
@ -264,11 +264,19 @@ fn send_to_bridge(app: &AppHandle, command: &str, message: &str) -> Result<Strin
|
||||||
state.request_counter += 1;
|
state.request_counter += 1;
|
||||||
let request_id = format!("req-{}", state.request_counter);
|
let request_id = format!("req-{}", state.request_counter);
|
||||||
|
|
||||||
let msg = serde_json::json!({
|
// Je nach Command unterschiedliche Payload-Struktur
|
||||||
|
let msg = match command {
|
||||||
|
"set-model" => serde_json::json!({
|
||||||
|
"command": command,
|
||||||
|
"id": request_id,
|
||||||
|
"model": message
|
||||||
|
}),
|
||||||
|
_ => serde_json::json!({
|
||||||
"command": command,
|
"command": command,
|
||||||
"id": request_id,
|
"id": request_id,
|
||||||
"message": message
|
"message": message
|
||||||
});
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(stdin) = &mut state.bridge_stdin {
|
if let Some(stdin) = &mut state.bridge_stdin {
|
||||||
writeln!(stdin, "{}", msg.to_string()).map_err(|e| e.to_string())?;
|
writeln!(stdin, "{}", msg.to_string()).map_err(|e| e.to_string())?;
|
||||||
|
|
@ -323,3 +331,75 @@ pub async fn get_agent_status(app: AppHandle) -> Result<Vec<AgentStatus>, String
|
||||||
let state = state.lock().unwrap();
|
let state = state.lock().unwrap();
|
||||||
Ok(state.agents.clone())
|
Ok(state.agents.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Modell wechseln
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_model(app: AppHandle, model: String) -> Result<String, String> {
|
||||||
|
println!("🔄 Modell wechseln zu: {}", model);
|
||||||
|
|
||||||
|
// Modell in Settings speichern
|
||||||
|
if let Some(db_state) = app.try_state::<Arc<Mutex<crate::db::Database>>>() {
|
||||||
|
let db = db_state.lock().unwrap();
|
||||||
|
let _ = db.set_setting("claude_model", &model);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bridge starten falls nicht aktiv
|
||||||
|
let needs_start = {
|
||||||
|
let state = app.state::<Arc<Mutex<ClaudeState>>>();
|
||||||
|
let state_guard = state.lock().unwrap();
|
||||||
|
state_guard.bridge_stdin.is_none()
|
||||||
|
};
|
||||||
|
|
||||||
|
if needs_start {
|
||||||
|
start_bridge(&app)?;
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modell an Bridge senden
|
||||||
|
send_to_bridge(&app, "set-model", &model)?;
|
||||||
|
|
||||||
|
Ok(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verfügbare Modelle abrufen
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_available_models() -> Result<Vec<ModelInfo>, String> {
|
||||||
|
Ok(vec![
|
||||||
|
ModelInfo {
|
||||||
|
id: "haiku".to_string(),
|
||||||
|
name: "Claude Haiku".to_string(),
|
||||||
|
description: "Schnell & günstig".to_string(),
|
||||||
|
},
|
||||||
|
ModelInfo {
|
||||||
|
id: "sonnet".to_string(),
|
||||||
|
name: "Claude Sonnet".to_string(),
|
||||||
|
description: "Ausgewogen".to_string(),
|
||||||
|
},
|
||||||
|
ModelInfo {
|
||||||
|
id: "opus".to_string(),
|
||||||
|
name: "Claude Opus".to_string(),
|
||||||
|
description: "Leistungsstark".to_string(),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Aktuelles Modell aus Settings laden
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_current_model(app: AppHandle) -> Result<String, String> {
|
||||||
|
if let Some(db_state) = app.try_state::<Arc<Mutex<crate::db::Database>>>() {
|
||||||
|
let db = db_state.lock().unwrap();
|
||||||
|
if let Ok(Some(model)) = db.get_setting("claude_model") {
|
||||||
|
return Ok(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Default
|
||||||
|
Ok("opus".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modell-Info Struct
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct ModelInfo {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub description: String,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -517,6 +517,15 @@ impl Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Lädt alle Einstellungen
|
||||||
|
pub fn get_all_settings(&self) -> SqlResult<Vec<(String, String)>> {
|
||||||
|
let mut stmt = self.conn.prepare("SELECT key, value FROM settings")?;
|
||||||
|
let settings = stmt.query_map([], |row| {
|
||||||
|
Ok((row.get(0)?, row.get(1)?))
|
||||||
|
})?.collect::<SqlResult<Vec<_>>>()?;
|
||||||
|
Ok(settings)
|
||||||
|
}
|
||||||
|
|
||||||
// ============ Statistiken ============
|
// ============ Statistiken ============
|
||||||
|
|
||||||
/// DB-Statistiken
|
/// DB-Statistiken
|
||||||
|
|
@ -604,3 +613,27 @@ pub async fn get_db_stats(app: AppHandle) -> Result<DbStats, String> {
|
||||||
let db = state.lock().unwrap();
|
let db = state.lock().unwrap();
|
||||||
db.stats().map_err(|e| e.to_string())
|
db.stats().map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Einstellung lesen
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_setting(app: AppHandle, key: String) -> Result<Option<String>, String> {
|
||||||
|
let state = app.state::<DbState>();
|
||||||
|
let db = state.lock().unwrap();
|
||||||
|
db.get_setting(&key).map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Einstellung speichern
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn set_setting(app: AppHandle, key: String, value: String) -> Result<(), String> {
|
||||||
|
let state = app.state::<DbState>();
|
||||||
|
let db = state.lock().unwrap();
|
||||||
|
db.set_setting(&key, &value).map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alle Einstellungen laden
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_all_settings(app: AppHandle) -> Result<Vec<(String, String)>, String> {
|
||||||
|
let state = app.state::<DbState>();
|
||||||
|
let db = state.lock().unwrap();
|
||||||
|
db.get_all_settings().map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@ pub fn run() {
|
||||||
claude::send_message,
|
claude::send_message,
|
||||||
claude::stop_all_agents,
|
claude::stop_all_agents,
|
||||||
claude::get_agent_status,
|
claude::get_agent_status,
|
||||||
|
claude::set_model,
|
||||||
|
claude::get_available_models,
|
||||||
|
claude::get_current_model,
|
||||||
// Gedächtnis-System
|
// Gedächtnis-System
|
||||||
memory::load_memory,
|
memory::load_memory,
|
||||||
memory::get_sticky_context,
|
memory::get_sticky_context,
|
||||||
|
|
@ -41,6 +44,10 @@ pub fn run() {
|
||||||
// Datenbank
|
// Datenbank
|
||||||
db::init_database,
|
db::init_database,
|
||||||
db::get_db_stats,
|
db::get_db_stats,
|
||||||
|
// Settings
|
||||||
|
db::get_setting,
|
||||||
|
db::set_setting,
|
||||||
|
db::get_all_settings,
|
||||||
// Sessions
|
// Sessions
|
||||||
session::create_session,
|
session::create_session,
|
||||||
session::update_session,
|
session::update_session,
|
||||||
|
|
|
||||||
277
src/lib/components/SettingsPanel.svelte
Normal file
277
src/lib/components/SettingsPanel.svelte
Normal file
|
|
@ -0,0 +1,277 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
import { currentModel } from '$lib/stores/app';
|
||||||
|
|
||||||
|
interface ModelInfo {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let availableModels: ModelInfo[] = [];
|
||||||
|
let selectedModel = '';
|
||||||
|
let loading = true;
|
||||||
|
let saving = false;
|
||||||
|
|
||||||
|
// Modell-Icons
|
||||||
|
const modelIcons: Record<string, string> = {
|
||||||
|
haiku: '🐦',
|
||||||
|
sonnet: '📝',
|
||||||
|
opus: '🎭',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Preise pro 1M Token (ungefähre Werte)
|
||||||
|
const modelPrices: Record<string, { input: number; output: number }> = {
|
||||||
|
haiku: { input: 0.25, output: 1.25 },
|
||||||
|
sonnet: { input: 3, output: 15 },
|
||||||
|
opus: { input: 15, output: 75 },
|
||||||
|
};
|
||||||
|
|
||||||
|
async function loadSettings() {
|
||||||
|
try {
|
||||||
|
availableModels = await invoke('get_available_models');
|
||||||
|
const current: string = await invoke('get_current_model');
|
||||||
|
selectedModel = current;
|
||||||
|
$currentModel = current;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Fehler beim Laden:', err);
|
||||||
|
}
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeModel(modelId: string) {
|
||||||
|
if (modelId === selectedModel) return;
|
||||||
|
saving = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await invoke('set_model', { model: modelId });
|
||||||
|
selectedModel = modelId;
|
||||||
|
$currentModel = modelId;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Fehler beim Speichern:', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
saving = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
loadSettings();
|
||||||
|
});
|
||||||
|
|
||||||
|
function formatPrice(price: number): string {
|
||||||
|
return `$${price.toFixed(2)}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="settings-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<h2>⚙️ Einstellungen</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if loading}
|
||||||
|
<div class="loading">Lade Einstellungen...</div>
|
||||||
|
{:else}
|
||||||
|
<div class="settings-content">
|
||||||
|
<!-- Modell-Auswahl -->
|
||||||
|
<section class="settings-section">
|
||||||
|
<h3>🤖 Claude-Modell</h3>
|
||||||
|
<p class="section-hint">Wähle das Modell für deine Anfragen</p>
|
||||||
|
|
||||||
|
<div class="model-list">
|
||||||
|
{#each availableModels as model}
|
||||||
|
<button
|
||||||
|
class="model-card"
|
||||||
|
class:selected={selectedModel === model.id}
|
||||||
|
class:saving={saving && selectedModel !== model.id}
|
||||||
|
on:click={() => changeModel(model.id)}
|
||||||
|
disabled={saving}
|
||||||
|
>
|
||||||
|
<div class="model-header">
|
||||||
|
<span class="model-icon">{modelIcons[model.id] || '🤖'}</span>
|
||||||
|
<span class="model-name">{model.name}</span>
|
||||||
|
{#if selectedModel === model.id}
|
||||||
|
<span class="model-active">✓ Aktiv</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="model-description">{model.description}</div>
|
||||||
|
{#if modelPrices[model.id]}
|
||||||
|
<div class="model-pricing">
|
||||||
|
<span>Input: {formatPrice(modelPrices[model.id].input)}/1M</span>
|
||||||
|
<span>Output: {formatPrice(modelPrices[model.id].output)}/1M</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Weitere Einstellungen (Platzhalter) -->
|
||||||
|
<section class="settings-section">
|
||||||
|
<h3>🎨 Darstellung</h3>
|
||||||
|
<div class="setting-row">
|
||||||
|
<span class="setting-label">Theme</span>
|
||||||
|
<span class="setting-value">AWL Dark (fest)</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="settings-section">
|
||||||
|
<h3>📁 Pfade</h3>
|
||||||
|
<div class="setting-row">
|
||||||
|
<span class="setting-label">Arbeitsverzeichnis</span>
|
||||||
|
<span class="setting-value placeholder">Wird aus Session geladen</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.settings-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: var(--spacing-sm) var(--spacing-md);
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-bottom: 1px solid var(--bg-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-header h2 {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
margin-bottom: var(--spacing-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section h3 {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-hint {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modell-Karten */
|
||||||
|
.model-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 2px solid var(--bg-tertiary);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card:hover:not(:disabled) {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card.selected {
|
||||||
|
border-color: var(--accent);
|
||||||
|
background: rgba(233, 69, 96, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card.saving {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
margin-bottom: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-icon {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-name {
|
||||||
|
font-weight: 600;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-active {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 2px 6px;
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-description {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-pricing {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-md);
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allgemeine Einstellungs-Zeilen */
|
||||||
|
.setting-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: var(--spacing-sm) 0;
|
||||||
|
border-bottom: 1px solid var(--bg-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-label {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-value {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-value.placeholder {
|
||||||
|
font-style: italic;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -7,6 +7,16 @@
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await initEventListeners();
|
await initEventListeners();
|
||||||
|
|
||||||
|
// Aktuelles Modell aus Settings laden
|
||||||
|
try {
|
||||||
|
const model: string = await invoke('get_current_model');
|
||||||
|
if (model) {
|
||||||
|
$currentModel = model;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('Modell konnte nicht geladen werden:', err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(async () => {
|
onDestroy(async () => {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
import MemoryPanel from '$lib/components/MemoryPanel.svelte';
|
import MemoryPanel from '$lib/components/MemoryPanel.svelte';
|
||||||
import AuditLog from '$lib/components/AuditLog.svelte';
|
import AuditLog from '$lib/components/AuditLog.svelte';
|
||||||
import GuardRailsPanel from '$lib/components/GuardRailsPanel.svelte';
|
import GuardRailsPanel from '$lib/components/GuardRailsPanel.svelte';
|
||||||
|
import SettingsPanel from '$lib/components/SettingsPanel.svelte';
|
||||||
|
|
||||||
let activeMiddleTab = 'activity';
|
let activeMiddleTab = 'activity';
|
||||||
let activeRightTab = 'agents';
|
let activeRightTab = 'agents';
|
||||||
|
|
@ -20,6 +21,7 @@
|
||||||
const rightTabs = [
|
const rightTabs = [
|
||||||
{ id: 'agents', label: 'Agents', icon: '🤖' },
|
{ id: 'agents', label: 'Agents', icon: '🤖' },
|
||||||
{ id: 'guards', label: 'Guard-Rails', icon: '🛡️' },
|
{ id: 'guards', label: 'Guard-Rails', icon: '🛡️' },
|
||||||
|
{ id: 'settings', label: 'Settings', icon: '⚙️' },
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -88,6 +90,8 @@
|
||||||
<AgentView />
|
<AgentView />
|
||||||
{:else if activeRightTab === 'guards'}
|
{:else if activeRightTab === 'guards'}
|
||||||
<GuardRailsPanel />
|
<GuardRailsPanel />
|
||||||
|
{:else if activeRightTab === 'settings'}
|
||||||
|
<SettingsPanel />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</Pane>
|
</Pane>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue