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:
parent
d6fca9ff34
commit
ab95af24ae
1 changed files with 142 additions and 2 deletions
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue