Token-basiertes Compacting mit Bestätigungs-Dialog

- Warnung bei ~40k Token (statt plumpe Nachrichtenanzahl)
- Dialog zeigt aktuelle Token-Schätzung
- User entscheidet ob kompaktiert wird
- Zeigt was nach Compacting übrig bleibt (30 neueste)
- "Später" Button um Dialog zu schließen

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eddy 2026-04-14 14:03:31 +02:00
parent d6fca9ff34
commit ab95af24ae

View file

@ -90,6 +90,14 @@
let editingMessageId: string | null = $state(null); let editingMessageId: string | null = $state(null);
let editingContent: string = $state(''); let editingContent: string = $state('');
// Auto-Compacting State
let lastMessageCount = 0;
let compactingWarningShown = false;
let showCompactingDialog = $state(false);
let estimatedTokens = $state(0);
const TOKEN_WARNING_THRESHOLD = 40000; // ~40k Token = Warnung zeigen
const KEEP_LAST_MESSAGES = 30;
async function scrollToBottom() { async function scrollToBottom() {
await tick(); await tick();
if (messagesContainer) { if (messagesContainer) {
@ -99,6 +107,17 @@
$: if ($messages.length) scrollToBottom(); $: if ($messages.length) scrollToBottom();
// Bei Session-Wechsel: Compacting-Flag zurücksetzen
$: if ($currentSessionId) {
compactingWarningShown = false;
showCompactingDialog = false;
}
// Token-Schätzung: ~4 Zeichen pro Token
function estimateTokensForMessages(msgs: Message[]): number {
return msgs.reduce((total, msg) => total + Math.ceil(msg.content.length / 4), 0);
}
// Nachricht in DB speichern // Nachricht in DB speichern
async function saveMessageToDb(msg: Message) { async function saveMessageToDb(msg: Message) {
const sessionId = get(currentSessionId); const sessionId = get(currentSessionId);
@ -112,8 +131,7 @@
} }
} }
// Neue Nachrichten automatisch speichern // Neue Nachrichten automatisch speichern + Token-Warnung
let lastMessageCount = 0;
const unsubscribe = messages.subscribe(async (msgs) => { const unsubscribe = messages.subscribe(async (msgs) => {
if (msgs.length > lastMessageCount && lastMessageCount > 0) { if (msgs.length > lastMessageCount && lastMessageCount > 0) {
// Neue Nachricht(en) hinzugefügt // Neue Nachricht(en) hinzugefügt
@ -125,9 +143,45 @@
} }
} }
} }
// Token-Schätzung aktualisieren
estimatedTokens = estimateTokensForMessages(msgs);
// Warnung zeigen wenn Token-Schwelle überschritten (einmalig pro Session)
if (!compactingWarningShown && estimatedTokens > TOKEN_WARNING_THRESHOLD && !$isProcessing) {
compactingWarningShown = true;
showCompactingDialog = true;
}
lastMessageCount = msgs.length; lastMessageCount = msgs.length;
}); });
async function performCompacting() {
const sessionId = get(currentSessionId);
if (!sessionId) return;
showCompactingDialog = false;
try {
const compacted: number = await invoke('compact_session', {
sessionId,
keepLast: KEEP_LAST_MESSAGES
});
if (compacted > 0) {
addMessage('system', `📦 Compacting: ${compacted} ältere Nachrichten wurden zusammengefasst. Die letzten ${KEEP_LAST_MESSAGES} bleiben erhalten.`);
}
} catch (err) {
console.error('Compacting fehlgeschlagen:', err);
addMessage('system', `⚠️ Compacting fehlgeschlagen: ${err}`);
}
}
function dismissCompactingDialog() {
showCompactingDialog = false;
// Warnung für diese Session nicht erneut zeigen
}
onDestroy(() => { onDestroy(() => {
unsubscribe(); unsubscribe();
}); });
@ -552,6 +606,49 @@
</div> </div>
{/if} {/if}
<!-- Compacting-Warnung Dialog -->
{#if showCompactingDialog}
<div class="modal-backdrop" onclick={dismissCompactingDialog}>
<div class="modal-dialog compact-dialog" onclick={(e) => e.stopPropagation()}>
<div class="modal-header warning">
<h3>⚠️ Hohe Token-Anzahl</h3>
<button class="close-btn" onclick={dismissCompactingDialog}>✕</button>
</div>
<div class="modal-body">
<p class="warning-text">
Diese Konversation hat bereits <strong>~{(estimatedTokens / 1000).toFixed(0)}k Token</strong>.
</p>
<p>
Um Kosten zu sparen und das Context-Limit nicht zu überschreiten, kannst du ältere Nachrichten zusammenfassen lassen.
</p>
<div class="compact-info">
<div class="info-row">
<span>Aktuelle Nachrichten:</span>
<strong>{$messages.length}</strong>
</div>
<div class="info-row">
<span>Nach Compacting behalten:</span>
<strong>{KEEP_LAST_MESSAGES} neueste</strong>
</div>
<div class="info-row">
<span>Geschätzte Token danach:</span>
<strong>~{Math.ceil(estimatedTokens * (KEEP_LAST_MESSAGES / $messages.length) / 1000)}k</strong>
</div>
</div>
<p class="hint">
Die älteren Nachrichten werden zu einer Zusammenfassung komprimiert, bleiben aber in der Datenbank erhalten.
</p>
</div>
<div class="modal-footer">
<button class="btn-secondary" onclick={dismissCompactingDialog}>Später</button>
<button class="btn-primary" onclick={performCompacting}>
📦 Jetzt kompaktieren
</button>
</div>
</div>
</div>
{/if}
<style> <style>
.chat-panel { .chat-panel {
display: flex; display: flex;
@ -1118,4 +1215,47 @@
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
/* Compacting Dialog */
.compact-dialog {
max-width: 420px;
}
.modal-header.warning {
background: rgba(234, 179, 8, 0.15);
border-bottom-color: rgba(234, 179, 8, 0.3);
}
.modal-header.warning h3 {
color: #eab308;
}
.warning-text {
font-size: 1rem;
margin-bottom: var(--spacing-sm);
}
.compact-info {
background: var(--bg-secondary);
border-radius: var(--radius-sm);
padding: var(--spacing-sm);
margin: var(--spacing-sm) 0;
}
.info-row {
display: flex;
justify-content: space-between;
padding: 0.25rem 0;
font-size: 0.8rem;
}
.info-row span {
color: var(--text-secondary);
}
.modal-body .hint {
font-size: 0.75rem;
color: var(--text-secondary);
margin-top: var(--spacing-sm);
}
</style> </style>