Compare commits
No commits in common. "4ba14a53e19830eb1b4b9df90c87b4e4a9dddd4d" and "9129163876922323ad75a4f871f283db0a7fc13b" have entirely different histories.
4ba14a53e1
...
9129163876
11 changed files with 25 additions and 709 deletions
|
|
@ -17,14 +17,7 @@ process.stdin.resume();
|
||||||
|
|
||||||
let activeAbort = null;
|
let activeAbort = null;
|
||||||
let currentAgentId = null;
|
let currentAgentId = null;
|
||||||
let currentModel = process.env.CLAUDE_MODEL || 'opus';
|
const MODEL = 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 ============
|
||||||
|
|
||||||
|
|
@ -46,10 +39,7 @@ function sendError(id, error) {
|
||||||
|
|
||||||
// ============ Claude Agent SDK ============
|
// ============ Claude Agent SDK ============
|
||||||
|
|
||||||
async function sendMessage(message, requestId, model = null) {
|
async function sendMessage(message, requestId) {
|
||||||
// Modell für diese Anfrage (Parameter > State > Default)
|
|
||||||
const useModel = model || currentModel;
|
|
||||||
|
|
||||||
currentAgentId = randomUUID();
|
currentAgentId = randomUUID();
|
||||||
activeAbort = new AbortController();
|
activeAbort = new AbortController();
|
||||||
|
|
||||||
|
|
@ -57,20 +47,19 @@ async function sendMessage(message, requestId, model = null) {
|
||||||
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', model: useModel });
|
sendResponse(requestId, { agentId: currentAgentId, status: 'gestartet' });
|
||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
let fullText = '';
|
let fullText = '';
|
||||||
let usedModel = useModel;
|
let usedModel = MODEL;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const conversation = query({
|
const conversation = query({
|
||||||
prompt: message,
|
prompt: message,
|
||||||
options: {
|
options: {
|
||||||
model: useModel,
|
model: MODEL,
|
||||||
maxTurns: 25,
|
maxTurns: 25,
|
||||||
abortController: activeAbort,
|
abortController: activeAbort,
|
||||||
},
|
},
|
||||||
|
|
@ -151,8 +140,7 @@ function handleCommand(msg) {
|
||||||
sendError(msg.id, 'Keine Nachricht angegeben');
|
sendError(msg.id, 'Keine Nachricht angegeben');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Modell kann pro Anfrage überschrieben werden
|
sendMessage(msg.message, msg.id);
|
||||||
sendMessage(msg.message, msg.id, msg.model);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'stop':
|
case 'stop':
|
||||||
|
|
@ -162,33 +150,10 @@ 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: currentModel,
|
model: MODEL,
|
||||||
isProcessing: !!currentAgentId,
|
isProcessing: !!currentAgentId,
|
||||||
availableModels: AVAILABLE_MODELS,
|
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -221,4 +186,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.1.0', pid: process.pid, model: currentModel, availableModels: AVAILABLE_MODELS });
|
sendEvent('ready', { version: '1.0.0', pid: process.pid, model: MODEL });
|
||||||
|
|
|
||||||
|
|
@ -264,19 +264,11 @@ 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);
|
||||||
|
|
||||||
// Je nach Command unterschiedliche Payload-Struktur
|
let msg = serde_json::json!({
|
||||||
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())?;
|
||||||
|
|
@ -331,75 +323,3 @@ 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,
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -27,17 +27,6 @@ pub struct Session {
|
||||||
pub last_message: Option<String>,
|
pub last_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eine Chat-Nachricht
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct ChatMessage {
|
|
||||||
pub id: String,
|
|
||||||
pub session_id: String,
|
|
||||||
pub role: String, // "user", "assistant", "system"
|
|
||||||
pub content: String,
|
|
||||||
pub model: Option<String>,
|
|
||||||
pub timestamp: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Datenbank-Wrapper
|
/// Datenbank-Wrapper
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
|
|
@ -152,18 +141,6 @@ impl Database {
|
||||||
value TEXT NOT NULL,
|
value TEXT NOT NULL,
|
||||||
updated_at TEXT NOT NULL
|
updated_at TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Chat-Nachrichten
|
|
||||||
CREATE TABLE IF NOT EXISTS messages (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
session_id TEXT NOT NULL,
|
|
||||||
role TEXT NOT NULL,
|
|
||||||
content TEXT NOT NULL,
|
|
||||||
model TEXT,
|
|
||||||
timestamp TEXT NOT NULL,
|
|
||||||
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, timestamp);
|
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -511,58 +488,10 @@ impl Database {
|
||||||
|
|
||||||
/// Löscht eine Session
|
/// Löscht eine Session
|
||||||
pub fn delete_session(&self, id: &str) -> SqlResult<()> {
|
pub fn delete_session(&self, id: &str) -> SqlResult<()> {
|
||||||
// Erst Nachrichten löschen (wegen Foreign Key)
|
|
||||||
self.conn.execute("DELETE FROM messages WHERE session_id = ?1", params![id])?;
|
|
||||||
self.conn.execute("DELETE FROM sessions WHERE id = ?1", params![id])?;
|
self.conn.execute("DELETE FROM sessions WHERE id = ?1", params![id])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Messages ============
|
|
||||||
|
|
||||||
/// Speichert eine Nachricht
|
|
||||||
pub fn save_message(&self, msg: &ChatMessage) -> SqlResult<()> {
|
|
||||||
self.conn.execute(
|
|
||||||
"INSERT OR REPLACE INTO messages (id, session_id, role, content, model, timestamp)
|
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
|
||||||
params![
|
|
||||||
msg.id,
|
|
||||||
msg.session_id,
|
|
||||||
msg.role,
|
|
||||||
msg.content,
|
|
||||||
msg.model,
|
|
||||||
msg.timestamp,
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lädt alle Nachrichten einer Session
|
|
||||||
pub fn load_messages(&self, session_id: &str) -> SqlResult<Vec<ChatMessage>> {
|
|
||||||
let mut stmt = self.conn.prepare(
|
|
||||||
"SELECT id, session_id, role, content, model, timestamp
|
|
||||||
FROM messages WHERE session_id = ?1 ORDER BY timestamp ASC"
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let messages = stmt.query_map(params![session_id], |row| {
|
|
||||||
Ok(ChatMessage {
|
|
||||||
id: row.get(0)?,
|
|
||||||
session_id: row.get(1)?,
|
|
||||||
role: row.get(2)?,
|
|
||||||
content: row.get(3)?,
|
|
||||||
model: row.get(4)?,
|
|
||||||
timestamp: row.get(5)?,
|
|
||||||
})
|
|
||||||
})?.collect::<SqlResult<Vec<_>>>()?;
|
|
||||||
|
|
||||||
Ok(messages)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Löscht alle Nachrichten einer Session
|
|
||||||
pub fn clear_messages(&self, session_id: &str) -> SqlResult<()> {
|
|
||||||
self.conn.execute("DELETE FROM messages WHERE session_id = ?1", params![session_id])?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============ Settings ============
|
// ============ Settings ============
|
||||||
|
|
||||||
/// Speichert eine Einstellung
|
/// Speichert eine Einstellung
|
||||||
|
|
@ -588,15 +517,6 @@ 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
|
||||||
|
|
@ -684,51 +604,3 @@ 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())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Nachricht speichern
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn save_message(app: AppHandle, message: ChatMessage) -> Result<(), String> {
|
|
||||||
let state = app.state::<DbState>();
|
|
||||||
let db = state.lock().unwrap();
|
|
||||||
db.save_message(&message).map_err(|e| e.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Nachrichten einer Session laden
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn load_messages(app: AppHandle, session_id: String) -> Result<Vec<ChatMessage>, String> {
|
|
||||||
let state = app.state::<DbState>();
|
|
||||||
let db = state.lock().unwrap();
|
|
||||||
db.load_messages(&session_id).map_err(|e| e.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alle Nachrichten einer Session löschen
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn clear_messages(app: AppHandle, session_id: String) -> Result<(), String> {
|
|
||||||
let state = app.state::<DbState>();
|
|
||||||
let db = state.lock().unwrap();
|
|
||||||
db.clear_messages(&session_id).map_err(|e| e.to_string())
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,6 @@ 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,
|
||||||
|
|
@ -44,10 +41,6 @@ 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,
|
||||||
|
|
@ -57,10 +50,6 @@ pub fn run() {
|
||||||
session::resume_session,
|
session::resume_session,
|
||||||
session::get_active_session,
|
session::get_active_session,
|
||||||
session::set_claude_session_id,
|
session::set_claude_session_id,
|
||||||
// Messages
|
|
||||||
db::save_message,
|
|
||||||
db::load_messages,
|
|
||||||
db::clear_messages,
|
|
||||||
])
|
])
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let handle = app.handle().clone();
|
let handle = app.handle().clone();
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { messages, currentInput, isProcessing, addMessage, currentSessionId, messageToDb, type Message } from '$lib/stores/app';
|
import { messages, currentInput, isProcessing, addMessage } from '$lib/stores/app';
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
import { tick, onDestroy } from 'svelte';
|
import { tick } from 'svelte';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
marked.setOptions({ breaks: true, gfm: true });
|
marked.setOptions({ breaks: true, gfm: true });
|
||||||
|
|
||||||
|
|
@ -26,56 +25,11 @@
|
||||||
|
|
||||||
$: if ($messages.length) scrollToBottom();
|
$: if ($messages.length) scrollToBottom();
|
||||||
|
|
||||||
// Nachricht in DB speichern
|
|
||||||
async function saveMessageToDb(msg: Message) {
|
|
||||||
const sessionId = get(currentSessionId);
|
|
||||||
if (!sessionId) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const dbMsg = messageToDb(msg, sessionId);
|
|
||||||
await invoke('save_message', { message: dbMsg });
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Fehler beim Speichern der Nachricht:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Neue Nachrichten automatisch speichern
|
|
||||||
let lastMessageCount = 0;
|
|
||||||
const unsubscribe = messages.subscribe(async (msgs) => {
|
|
||||||
if (msgs.length > lastMessageCount && lastMessageCount > 0) {
|
|
||||||
// Neue Nachricht(en) hinzugefügt
|
|
||||||
const newMessages = msgs.slice(lastMessageCount);
|
|
||||||
for (const msg of newMessages) {
|
|
||||||
// Nur speichern wenn Nachricht Content hat (nicht die leere Streaming-Nachricht)
|
|
||||||
if (msg.content && msg.content.trim()) {
|
|
||||||
await saveMessageToDb(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastMessageCount = msgs.length;
|
|
||||||
});
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
unsubscribe();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function sendMessage() {
|
async function sendMessage() {
|
||||||
const text = $currentInput.trim();
|
const text = $currentInput.trim();
|
||||||
if (!text || $isProcessing) return;
|
if (!text || $isProcessing) return;
|
||||||
|
|
||||||
// Nachricht hinzufügen (wird durch den Store-Subscriber gespeichert)
|
addMessage('user', text);
|
||||||
const msgId = crypto.randomUUID();
|
|
||||||
const msg: Message = {
|
|
||||||
id: msgId,
|
|
||||||
role: 'user',
|
|
||||||
content: text,
|
|
||||||
timestamp: new Date(),
|
|
||||||
};
|
|
||||||
messages.update((msgs) => [...msgs, msg]);
|
|
||||||
|
|
||||||
// Sofort speichern (nicht auf Subscriber warten)
|
|
||||||
await saveMessageToDb(msg);
|
|
||||||
|
|
||||||
$currentInput = '';
|
$currentInput = '';
|
||||||
$isProcessing = true;
|
$isProcessing = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { messages, clearAll, isProcessing, currentSessionId, setMessagesFromDb, type DbMessage } from '$lib/stores/app';
|
import { messages, clearAll, isProcessing } from '$lib/stores/app';
|
||||||
|
|
||||||
interface Session {
|
interface Session {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -29,28 +29,12 @@
|
||||||
sessions = await invoke('list_sessions', { limit: 50 });
|
sessions = await invoke('list_sessions', { limit: 50 });
|
||||||
const active: Session | null = await invoke('get_active_session');
|
const active: Session | null = await invoke('get_active_session');
|
||||||
activeSessionId = active?.id || null;
|
activeSessionId = active?.id || null;
|
||||||
$currentSessionId = activeSessionId;
|
|
||||||
|
|
||||||
// Wenn aktive Session existiert, Nachrichten laden
|
|
||||||
if (activeSessionId) {
|
|
||||||
await loadSessionMessages(activeSessionId);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Fehler beim Laden der Sessions:', err);
|
console.error('Fehler beim Laden der Sessions:', err);
|
||||||
}
|
}
|
||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadSessionMessages(sessionId: string) {
|
|
||||||
try {
|
|
||||||
const dbMessages: DbMessage[] = await invoke('load_messages', { sessionId });
|
|
||||||
setMessagesFromDb(dbMessages);
|
|
||||||
console.log(`📨 ${dbMessages.length} Nachrichten geladen`);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Fehler beim Laden der Nachrichten:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
loadSessions();
|
loadSessions();
|
||||||
});
|
});
|
||||||
|
|
@ -63,7 +47,6 @@
|
||||||
workingDir: null,
|
workingDir: null,
|
||||||
});
|
});
|
||||||
activeSessionId = session.id;
|
activeSessionId = session.id;
|
||||||
$currentSessionId = session.id;
|
|
||||||
clearAll();
|
clearAll();
|
||||||
newTitle = '';
|
newTitle = '';
|
||||||
showNewForm = false;
|
showNewForm = false;
|
||||||
|
|
@ -78,10 +61,8 @@
|
||||||
try {
|
try {
|
||||||
const session: Session = await invoke('resume_session', { id });
|
const session: Session = await invoke('resume_session', { id });
|
||||||
activeSessionId = session.id;
|
activeSessionId = session.id;
|
||||||
$currentSessionId = session.id;
|
|
||||||
clearAll();
|
clearAll();
|
||||||
// Nachrichten aus DB laden
|
// TODO: Nachrichten aus Session-Historie laden
|
||||||
await loadSessionMessages(session.id);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Fehler:', err);
|
console.error('Fehler:', err);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,277 +0,0 @@
|
||||||
<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>
|
|
||||||
|
|
@ -51,7 +51,6 @@ export const isProcessing = writable(false);
|
||||||
export const currentInput = writable('');
|
export const currentInput = writable('');
|
||||||
export const selectedAgentId = writable<string | null>(null);
|
export const selectedAgentId = writable<string | null>(null);
|
||||||
export const currentModel = writable('');
|
export const currentModel = writable('');
|
||||||
export const currentSessionId = writable<string | null>(null);
|
|
||||||
|
|
||||||
// Session-Statistiken (kumuliert)
|
// Session-Statistiken (kumuliert)
|
||||||
export const sessionStats = writable({
|
export const sessionStats = writable({
|
||||||
|
|
@ -157,41 +156,3 @@ export function clearAll() {
|
||||||
messages.set([]);
|
messages.set([]);
|
||||||
isProcessing.set(false);
|
isProcessing.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DB-Nachricht Format (für Tauri)
|
|
||||||
export interface DbMessage {
|
|
||||||
id: string;
|
|
||||||
session_id: string;
|
|
||||||
role: string;
|
|
||||||
content: string;
|
|
||||||
model: string | null;
|
|
||||||
timestamp: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Konvertierung: Store → DB
|
|
||||||
export function messageToDb(msg: Message, sessionId: string): DbMessage {
|
|
||||||
return {
|
|
||||||
id: msg.id,
|
|
||||||
session_id: sessionId,
|
|
||||||
role: msg.role,
|
|
||||||
content: msg.content,
|
|
||||||
model: msg.model || null,
|
|
||||||
timestamp: msg.timestamp.toISOString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Konvertierung: DB → Store
|
|
||||||
export function dbToMessage(db: DbMessage): Message {
|
|
||||||
return {
|
|
||||||
id: db.id,
|
|
||||||
role: db.role as Message['role'],
|
|
||||||
content: db.content,
|
|
||||||
model: db.model || undefined,
|
|
||||||
timestamp: new Date(db.timestamp),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nachrichten aus DB in Store laden
|
|
||||||
export function setMessagesFromDb(dbMessages: DbMessage[]) {
|
|
||||||
messages.set(dbMessages.map(dbToMessage));
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
// Empfängt Events vom Tauri-Backend und aktualisiert die Stores
|
// Empfängt Events vom Tauri-Backend und aktualisiert die Stores
|
||||||
|
|
||||||
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
|
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
|
||||||
import { get } from 'svelte/store';
|
|
||||||
import {
|
import {
|
||||||
agents,
|
agents,
|
||||||
toolCalls,
|
toolCalls,
|
||||||
|
|
@ -16,10 +14,7 @@ import {
|
||||||
completeToolCall,
|
completeToolCall,
|
||||||
clearAll,
|
clearAll,
|
||||||
currentModel,
|
currentModel,
|
||||||
sessionStats,
|
sessionStats
|
||||||
currentSessionId,
|
|
||||||
messageToDb,
|
|
||||||
type Message
|
|
||||||
} from './app';
|
} from './app';
|
||||||
|
|
||||||
// Event-Typen vom Backend
|
// Event-Typen vom Backend
|
||||||
|
|
@ -58,20 +53,6 @@ let listeners: UnlistenFn[] = [];
|
||||||
// Streaming: ID der aktuellen Live-Nachricht
|
// Streaming: ID der aktuellen Live-Nachricht
|
||||||
let streamingMessageId: string | null = null;
|
let streamingMessageId: string | null = null;
|
||||||
|
|
||||||
// Nachricht in DB speichern
|
|
||||||
async function saveMessageToDb(msg: Message) {
|
|
||||||
const sessionId = get(currentSessionId);
|
|
||||||
if (!sessionId) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const dbMsg = messageToDb(msg, sessionId);
|
|
||||||
await invoke('save_message', { message: dbMsg });
|
|
||||||
console.log('💾 Nachricht gespeichert:', msg.role);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Fehler beim Speichern der Nachricht:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Events initialisieren
|
// Events initialisieren
|
||||||
export async function initEventListeners(): Promise<void> {
|
export async function initEventListeners(): Promise<void> {
|
||||||
console.log('🎧 Initialisiere Event-Listener...');
|
console.log('🎧 Initialisiere Event-Listener...');
|
||||||
|
|
@ -180,31 +161,15 @@ export async function initEventListeners(): Promise<void> {
|
||||||
|
|
||||||
// Ergebnis (Kosten, Token, Modell)
|
// Ergebnis (Kosten, Token, Modell)
|
||||||
listeners.push(
|
listeners.push(
|
||||||
await listen<ResultEvent>('claude-result', async (event) => {
|
await listen<ResultEvent>('claude-result', (event) => {
|
||||||
const { cost, tokens, session_id, model } = event.payload;
|
const { cost, tokens, session_id, model } = event.payload;
|
||||||
console.log('📊 Ergebnis:', { cost: cost ? `$${cost.toFixed(4)}` : '-', tokens, model });
|
console.log('📊 Ergebnis:', { cost: cost ? `$${cost.toFixed(4)}` : '-', tokens, model });
|
||||||
|
|
||||||
// Modell an die Streaming-Nachricht anhängen und speichern
|
// Modell an die Streaming-Nachricht anhängen
|
||||||
if (streamingMessageId) {
|
if (model && streamingMessageId) {
|
||||||
let finalMessage: Message | null = null;
|
messages.update((msgs) =>
|
||||||
|
msgs.map((m) => m.id === streamingMessageId ? { ...m, model } : m)
|
||||||
messages.update((msgs) => {
|
);
|
||||||
return msgs.map((m) => {
|
|
||||||
if (m.id === streamingMessageId) {
|
|
||||||
finalMessage = { ...m, model: model || m.model };
|
|
||||||
return finalMessage;
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Nachricht in DB speichern (nur wenn Content vorhanden)
|
|
||||||
if (finalMessage && finalMessage.content && finalMessage.content.trim()) {
|
|
||||||
await saveMessageToDb(finalMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model) {
|
|
||||||
currentModel.set(model);
|
currentModel.set(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,6 @@
|
||||||
|
|
||||||
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,7 +7,6 @@
|
||||||
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';
|
||||||
|
|
@ -21,7 +20,6 @@
|
||||||
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>
|
||||||
|
|
||||||
|
|
@ -90,8 +88,6 @@
|
||||||
<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