Sticky Context Auto-Load beim App-Start
- init_sticky_context Tauri Command: Lädt Context aus DB, sendet an Bridge
- Frontend ruft Command beim Start auf (+layout.svelte)
- StickyContextInfo Store für Status-Tracking
- Context-Badge im Footer (📌 +XXctx Token)
- Zeigt Anzahl Einträge und Token-Schätzung
Bugfixes:
- context.rs: Typ-Annotationen in Closures (String statt str)
- db.rs: conn als pub(crate) für Module-Zugriff
- memory.rs: get_sticky_context → get_sticky_memory_entries (Namenskonflikt)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0d292179e2
commit
51239d6639
9 changed files with 1089 additions and 22 deletions
|
|
@ -180,9 +180,13 @@ Die App hat keinen direkten Zugriff auf die zentrale Wissensbasis (`claude` DB a
|
|||
|
||||
- ✅ **"Das merken" im Chat** — 💡 Button bei Nachrichten, Modal-Dialog (56eb2f5)
|
||||
|
||||
### Noch offen (niedrigere Priorität)
|
||||
### Nachträglich implementiert (14.04.2026)
|
||||
|
||||
- [ ] **Sticky Context** — Automatisch beim Chat-Start laden
|
||||
- ✅ **Sticky Context Auto-Load** — Context wird beim App-Start automatisch geladen und an die Bridge gesendet
|
||||
- `init_sticky_context` Tauri Command erstellt
|
||||
- Frontend ruft Command in `+layout.svelte` auf
|
||||
- Context-Status im Footer angezeigt (📌 +XXctx)
|
||||
- Enthält Anzahl Einträge und Token-Schätzung
|
||||
|
||||
### Verifikation
|
||||
```bash
|
||||
|
|
|
|||
948
src-tauri/Cargo.lock
generated
948
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -520,3 +520,82 @@ pub struct ModelInfo {
|
|||
pub name: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
/// Sticky Context Initialisierungs-Info
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct StickyContextInfo {
|
||||
pub loaded: bool,
|
||||
pub entries: usize,
|
||||
pub estimated_tokens: usize,
|
||||
pub has_user_info: bool,
|
||||
pub has_project: bool,
|
||||
pub credentials_count: usize,
|
||||
pub rules_count: usize,
|
||||
}
|
||||
|
||||
/// Sticky Context beim App-Start initialisieren
|
||||
/// Lädt den Context aus der DB und sendet ihn an die Bridge
|
||||
#[tauri::command]
|
||||
pub async fn init_sticky_context(app: AppHandle) -> Result<StickyContextInfo, String> {
|
||||
println!("📌 Initialisiere Sticky Context...");
|
||||
|
||||
// Context aus DB laden
|
||||
let context = load_sticky_context_for_prompt(&app);
|
||||
|
||||
let mut info = StickyContextInfo {
|
||||
loaded: false,
|
||||
entries: 0,
|
||||
estimated_tokens: 0,
|
||||
has_user_info: false,
|
||||
has_project: false,
|
||||
credentials_count: 0,
|
||||
rules_count: 0,
|
||||
};
|
||||
|
||||
// Details aus DB laden für Info
|
||||
if let Some(db_state) = app.try_state::<Arc<Mutex<crate::db::Database>>>() {
|
||||
let db = db_state.lock().unwrap();
|
||||
let _ = db.create_context_tables();
|
||||
|
||||
if let Ok(entries) = db.load_sticky_context() {
|
||||
info.entries = entries.len();
|
||||
|
||||
for (key, _value, _priority) in &entries {
|
||||
match key.as_str() {
|
||||
"user_info" => info.has_user_info = true,
|
||||
k if k.starts_with("cred:") => info.credentials_count += 1,
|
||||
k if k.starts_with("project:") => info.has_project = true,
|
||||
k if k.starts_with("rule:") => info.rules_count += 1,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref ctx) = context {
|
||||
info.loaded = true;
|
||||
info.estimated_tokens = ctx.len() / 4;
|
||||
|
||||
// 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)?;
|
||||
// Kurz warten bis Bridge bereit
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
||||
}
|
||||
|
||||
// Context an Bridge senden
|
||||
let _ = send_to_bridge(&app, "set-context", ctx);
|
||||
|
||||
println!("✅ Sticky Context geladen: {} Einträge, ~{} Token", info.entries, info.estimated_tokens);
|
||||
} else {
|
||||
println!("ℹ️ Kein Sticky Context konfiguriert");
|
||||
}
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -278,11 +278,11 @@ impl Database {
|
|||
Ok(ExtractedContext {
|
||||
session_id: row.get(0)?,
|
||||
extracted_at: row.get(1)?,
|
||||
decisions: decisions.and_then(|s| serde_json::from_str(&s).ok()).unwrap_or_default(),
|
||||
open_questions: questions.and_then(|s| serde_json::from_str(&s).ok()).unwrap_or_default(),
|
||||
key_insights: insights.and_then(|s| serde_json::from_str(&s).ok()).unwrap_or_default(),
|
||||
mentioned_files: files.and_then(|s| serde_json::from_str(&s).ok()).unwrap_or_default(),
|
||||
mentioned_tools: tools.and_then(|s| serde_json::from_str(&s).ok()).unwrap_or_default(),
|
||||
decisions: decisions.and_then(|s: String| serde_json::from_str(&s).ok()).unwrap_or_default(),
|
||||
open_questions: questions.and_then(|s: String| serde_json::from_str(&s).ok()).unwrap_or_default(),
|
||||
key_insights: insights.and_then(|s: String| serde_json::from_str(&s).ok()).unwrap_or_default(),
|
||||
mentioned_files: files.and_then(|s: String| serde_json::from_str(&s).ok()).unwrap_or_default(),
|
||||
mentioned_tools: tools.and_then(|s: String| serde_json::from_str(&s).ok()).unwrap_or_default(),
|
||||
})
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ pub struct MonitorEvent {
|
|||
|
||||
/// Datenbank-Wrapper
|
||||
pub struct Database {
|
||||
conn: Connection,
|
||||
pub(crate) conn: Connection,
|
||||
}
|
||||
|
||||
/// Datenbank-Statistiken
|
||||
|
|
|
|||
|
|
@ -32,9 +32,10 @@ pub fn run() {
|
|||
claude::set_model,
|
||||
claude::get_available_models,
|
||||
claude::get_current_model,
|
||||
claude::init_sticky_context,
|
||||
// Gedächtnis-System
|
||||
memory::load_memory,
|
||||
memory::get_sticky_context,
|
||||
memory::get_sticky_memory_entries,
|
||||
memory::save_pattern,
|
||||
memory::detect_issue,
|
||||
// Audit-Log
|
||||
|
|
|
|||
|
|
@ -147,9 +147,9 @@ pub async fn load_memory(app: AppHandle) -> Result<MemoryStats, String> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Holt den Sticky-Kontext für Claude
|
||||
/// Holt die Sticky-Memory-Einträge (veraltet, nutze context::get_sticky_context)
|
||||
#[tauri::command]
|
||||
pub async fn get_sticky_context(app: AppHandle) -> Result<Vec<MemoryEntry>, String> {
|
||||
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())?;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,19 @@ export const sessionStats = writable({
|
|||
messageCount: 0,
|
||||
});
|
||||
|
||||
// Sticky Context Status (beim App-Start geladen)
|
||||
export interface StickyContextInfo {
|
||||
loaded: boolean;
|
||||
entries: number;
|
||||
estimatedTokens: number;
|
||||
hasUserInfo: boolean;
|
||||
hasProject: boolean;
|
||||
credentialsCount: number;
|
||||
rulesCount: number;
|
||||
}
|
||||
|
||||
export const stickyContextInfo = writable<StickyContextInfo | null>(null);
|
||||
|
||||
// Abgeleitete Stores
|
||||
export const activeAgents = derived(agents, ($agents) =>
|
||||
$agents.filter((a) => a.status === 'active')
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import '../app.css';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { isProcessing, agentCount, currentModel, sessionStats, initEventListeners, cleanupEventListeners, currentSessionId, setMessagesFromDb, type DbMessage } from '$lib/stores';
|
||||
import { isProcessing, agentCount, currentModel, sessionStats, initEventListeners, cleanupEventListeners, currentSessionId, setMessagesFromDb, stickyContextInfo, type DbMessage, type StickyContextInfo } from '$lib/stores';
|
||||
import StopButton from '$lib/components/StopButton.svelte';
|
||||
|
||||
// Session-Typ vom Backend
|
||||
|
|
@ -21,6 +21,17 @@
|
|||
last_message: string | null;
|
||||
}
|
||||
|
||||
// Backend-Response für Sticky Context
|
||||
interface StickyContextResponse {
|
||||
loaded: boolean;
|
||||
entries: number;
|
||||
estimated_tokens: number;
|
||||
has_user_info: boolean;
|
||||
has_project: boolean;
|
||||
credentials_count: number;
|
||||
rules_count: number;
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
await initEventListeners();
|
||||
|
||||
|
|
@ -34,6 +45,25 @@
|
|||
console.warn('Modell konnte nicht geladen werden:', err);
|
||||
}
|
||||
|
||||
// Sticky Context beim Start laden und an Bridge senden
|
||||
try {
|
||||
const ctx: StickyContextResponse = await invoke('init_sticky_context');
|
||||
$stickyContextInfo = {
|
||||
loaded: ctx.loaded,
|
||||
entries: ctx.entries,
|
||||
estimatedTokens: ctx.estimated_tokens,
|
||||
hasUserInfo: ctx.has_user_info,
|
||||
hasProject: ctx.has_project,
|
||||
credentialsCount: ctx.credentials_count,
|
||||
rulesCount: ctx.rules_count,
|
||||
};
|
||||
if (ctx.loaded) {
|
||||
console.log(`📌 Sticky Context geladen: ${ctx.entries} Einträge, ~${ctx.estimated_tokens} Token`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Sticky Context konnte nicht geladen werden:', err);
|
||||
}
|
||||
|
||||
// Aktive Session automatisch laden (falls vorhanden)
|
||||
try {
|
||||
const activeSession: Session | null = await invoke('get_active_session');
|
||||
|
|
@ -125,6 +155,12 @@
|
|||
<footer class="footer" class:active={$isProcessing}>
|
||||
<StopButton on:click={handleStop} disabled={!$isProcessing} />
|
||||
<div class="footer-stats">
|
||||
{#if $stickyContextInfo?.loaded}
|
||||
<span class="context-badge" title="Sticky Context: {$stickyContextInfo.entries} Einträge, ~{$stickyContextInfo.estimatedTokens} Token">
|
||||
📌 +{$stickyContextInfo.estimatedTokens}ctx
|
||||
</span>
|
||||
<span class="sep">|</span>
|
||||
{/if}
|
||||
<span>Token: {formatTokens($sessionStats.totalTokensIn)} in / {formatTokens($sessionStats.totalTokensOut)} out</span>
|
||||
<span class="sep">|</span>
|
||||
<span>Kosten: {formatCost($sessionStats.totalCost)}</span>
|
||||
|
|
@ -242,4 +278,10 @@
|
|||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.footer-stats .context-badge {
|
||||
color: #22c55e;
|
||||
font-weight: 500;
|
||||
cursor: help;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in a new issue