claude-desktop/src-tauri/src/memory.rs
Eddy f51241efa6 Phase 10 Sprach-Interface + Phase 9 Nacharbeiten
Voice (Phase 10):
- voice.rs: OpenAI Whisper (STT) + TTS Backend
- ChatPanel: Mikrofon-Button, VAD (Pause 1.5s), Live-Pegel
- SettingsPanel: OpenAI-Key Konfiguration

Phase 9 Nacharbeiten:
- Auto-Extract vor Compacting (Entscheidungen/TODOs/Insights)
- get_tool_hints() - relevante KB-Eintraege bei Tool-Start
- activeKnowledgeHints Store, Anzeige im KnowledgePanel

Tech-Schulden:
- Dead-Code in memory.rs entfernt (MemorySystem struct)
- cargo-check Warnings behoben

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 18:24:28 +02:00

132 lines
4.3 KiB
Rust

// Claude Desktop — Autonomes Gedächtnis-System
// Lädt Zugänge, Patterns, Vorgehensweisen automatisch und behält sie im Kontext
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tauri::{AppHandle, Manager};
use crate::db;
/// Kategorien für Sticky Context (werden nie vergessen)
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ContextCategory {
Critical, // API-Keys, Server-URLs, Projekt-Pfade
Pattern, // Bekannte Fehler und Workarounds
Preference, // Benutzer-Präferenzen
GuardRail, // Freigabe-Regeln
Hook, // Aktive Hooks
Skill, // Verfügbare Skills
}
/// Ein Eintrag im Gedächtnis
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryEntry {
pub id: String,
pub category: ContextCategory,
pub key: String,
pub value: serde_json::Value,
pub sticky: bool, // Wird nie durch Compacting entfernt
pub auto_load: bool, // Wird beim Start automatisch geladen
pub last_used: Option<String>,
pub use_count: u32,
}
// MemorySystem Struct entfernt - Dead Code, Funktionalität läuft über Tauri-Commands
#[derive(Debug, Serialize, Deserialize)]
pub struct MemoryStats {
pub total: usize,
pub sticky: usize,
pub by_category: HashMap<String, usize>,
}
/// Vorgehensweise / Pattern
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pattern {
pub id: String,
pub name: String,
pub description: String,
pub trigger: String, // Wann wird es angewendet
pub old_approach: String, // Was wurde vorher gemacht
pub new_approach: String, // Was ist die Korrektur
pub reason: String, // Warum wurde korrigiert
pub occurrence_count: u32, // Wie oft ist das Problem aufgetreten
pub auto_corrected: bool, // Wurde automatisch korrigiert
pub created_at: String,
pub updated_at: String,
}
// Tauri-Commands
/// Lädt das Gedächtnis beim Start
#[tauri::command]
pub async fn load_memory(app: AppHandle) -> Result<MemoryStats, String> {
println!("🧠 Lade Gedächtnis-System...");
let state = app.state::<Arc<Mutex<db::Database>>>();
let db_lock = state.lock().unwrap();
let entries = db_lock.load_memory_entries().map_err(|e| e.to_string())?;
let mut by_category: HashMap<String, usize> = HashMap::new();
let mut sticky_count = 0;
for entry in &entries {
let cat = format!("{:?}", entry.category);
*by_category.entry(cat).or_insert(0) += 1;
if entry.sticky {
sticky_count += 1;
}
}
println!("🧠 {} Einträge geladen ({} sticky)", entries.len(), sticky_count);
Ok(MemoryStats {
total: entries.len(),
sticky: sticky_count,
by_category,
})
}
/// Holt die Sticky-Memory-Einträge (veraltet, nutze context::get_sticky_context)
#[tauri::command]
pub async fn get_sticky_memory_entries(app: AppHandle) -> Result<Vec<MemoryEntry>, String> {
let state = app.state::<Arc<Mutex<db::Database>>>();
let db_lock = state.lock().unwrap();
let entries = db_lock.load_memory_entries().map_err(|e| e.to_string())?;
Ok(entries.into_iter().filter(|e| e.sticky).collect())
}
/// Speichert eine neue Vorgehensweise
#[tauri::command]
pub async fn save_pattern(app: AppHandle, pattern: Pattern) -> Result<(), String> {
println!("📝 Speichere Vorgehensweise: {}", pattern.name);
let state = app.state::<Arc<Mutex<db::Database>>>();
let db_lock = state.lock().unwrap();
db_lock.save_pattern(&pattern).map_err(|e| e.to_string())
}
/// Erkennt ein Problem und schlägt Korrektur vor
#[tauri::command]
pub async fn detect_issue(
app: AppHandle,
error_message: String,
_context: String,
) -> Result<Option<Pattern>, String> {
let preview_len = error_message.len().min(50);
println!("🔍 Prüfe auf bekannte Probleme: {}", &error_message[..preview_len]);
let state = app.state::<Arc<Mutex<db::Database>>>();
let db_lock = state.lock().unwrap();
let patterns = db_lock.load_patterns().map_err(|e| e.to_string())?;
// Einfaches Pattern-Matching: Trigger im Fehlertext suchen
let error_lower = error_message.to_lowercase();
for pattern in patterns {
if error_lower.contains(&pattern.trigger.to_lowercase()) {
return Ok(Some(pattern));
}
}
Ok(None)
}