Update 7 - Erweitertes Research
release-tag / release-image (push) Successful in 2m22s

This commit is contained in:
2026-08-05 05:44:06 +02:00
parent b4388d1ebc
commit 8dc4de3eae
27 changed files with 3043 additions and 255 deletions
+56 -6
View File
@@ -1341,6 +1341,7 @@
function handleResearchAnimation(evt) {
if (!evt?.type?.includes('research')) return;
if (!evt?.metadata?.research_id) return;
const timestamp = Date.parse(evt.timestamp || '') || Date.now();
const id = researchEventID(evt);
let animation = state.researchAnimations.get(id);
@@ -1391,10 +1392,15 @@
animation.until = Number.POSITIVE_INFINITY;
researchSourcePoints(animation, animation.resultCount || resultIDs.length || 4);
refreshResearchNodes(animation, minMS);
} else if (evt.type.endsWith('.failed')) {
} else if (evt.type === 'research.failed' || evt.type === 'article.research.failed' || evt.type === 'research.test.failed') {
animation.stage = 'failed';
animation.error = String(evt.metadata?.error || 'Recherche fehlgeschlagen');
animation.until = now + minMS;
} else if (evt.type === 'article.research.completed') {
animation.resultCount = Number(evt.metadata?.accepted_count || animation.resultCount || 0);
animation.titles = Array.isArray(evt.metadata?.result_titles) ? evt.metadata.result_titles.map(String).slice(0, 5) : animation.titles;
animation.stage = animation.resultCount > 0 ? 'ingested' : 'empty';
animation.until = now + minMS;
}
}
@@ -2004,9 +2010,9 @@
function shouldLog(evt) {
if (!evt || evt.type === 'brain.idle' || evt.type === 'node.activated' || evt.type === 'edges.traversed') return false;
const important = new Set(['scan.started', 'graph.updated', 'embedding.batch', 'query.started', 'query.completed', 'think.queued', 'think.cycle.started', 'think.cycle.completed', 'think.cycle.failed', 'think.no_candidate', 'think.started', 'think.relation.created', 'think.rejected', 'think.failed', 'think.paused', 'research.started', 'research.results', 'research.ingested', 'research.failed', 'research.test.started', 'research.test.results', 'research.test.failed', 'article.plan.started', 'article.plan.skipped', 'article.research.started', 'article.research.results', 'article.research.ingested', 'article.research.failed', 'article.draft.started', 'article.draft.rejected', 'article.created', 'article.duplicate', 'article.skipped', 'article.failed', 'agent.run', 'glpi.kb.synced', 'glpi.kb.failed', 'persistence.flushed', 'persistence.failed']);
const important = new Set(['scan.started', 'graph.updated', 'embedding.batch', 'query.started', 'query.completed', 'think.queued', 'think.cycle.started', 'think.cycle.completed', 'think.cycle.failed', 'think.no_candidate', 'think.started', 'think.relation.created', 'think.rejected', 'think.failed', 'think.paused', 'research.started', 'research.results', 'research.ingested', 'research.failed', 'research.test.started', 'research.test.results', 'research.test.failed', 'article.plan.started', 'article.plan.skipped', 'article.research.round.started', 'article.research.round.completed', 'article.research.reused', 'article.research.started', 'article.research.results', 'article.research.candidates', 'article.research.fetch.started', 'article.research.fetch.completed', 'article.research.fetch.failed', 'article.research.evidence.accepted', 'article.research.evidence.rejected', 'article.research.ingested', 'article.research.learned', 'article.research.completed', 'article.research.failed', 'article.draft.started', 'article.draft.rejected', 'article.created', 'article.duplicate', 'article.skipped', 'article.failed', 'agent.run', 'glpi.kb.synced', 'glpi.kb.failed', 'persistence.flushed', 'persistence.failed']);
if (!important.has(evt.type) && !(evt.source === 'agent' || evt.source === 'knowledgebase' || evt.source === 'external' || evt.query)) return false;
const fingerprint = `${evt.type}|${evt.message || ''}|${evt.query || ''}|${evt.source || ''}`;
const fingerprint = `${evt.type}|${evt.message || ''}|${evt.query || evt.metadata?.research_query || ''}|${evt.source || ''}|${evt.metadata?.research_id || ''}|${evt.metadata?.result_url || ''}|${evt.metadata?.round || evt.metadata?.research_round || ''}`;
const last = state.lastLogFingerprint.get(fingerprint) || 0;
const now = Date.now();
const cooldown = evt.type === 'embedding.batch' ? 45000 : evt.type === 'graph.updated' ? 15000 : 4000;
@@ -2045,9 +2051,20 @@
'research.test.failed': 'SearXNG-Test fehlgeschlagen',
'article.plan.started': 'Artikelmehrwert wird geprüft',
'article.plan.skipped': 'Artikelsynthese übersprungen',
'article.research.round.started': 'Recherche-Runde gestartet',
'article.research.round.completed': 'Recherche-Runde bewertet',
'article.research.reused': 'Gelernte Webbelege wiederverwendet',
'article.research.started': 'Artikelrecherche gestartet',
'article.research.results': 'Artikelquellen gefunden',
'article.research.ingested': 'Artikelquellen verknüpft',
'article.research.results': 'SearXNG-Kandidaten gefunden',
'article.research.candidates': 'Quellenkandidaten bewertet',
'article.research.fetch.started': 'Webquelle wird geladen',
'article.research.fetch.completed': 'Webquelle als Volltext extrahiert',
'article.research.fetch.failed': 'Webquelle nicht nutzbar',
'article.research.evidence.accepted': 'Webquelle als Beleg akzeptiert',
'article.research.evidence.rejected': 'Webquelle fachlich verworfen',
'article.research.ingested': 'Geprüfte Artikelquellen verknüpft',
'article.research.learned': 'Webbelege semantisch gelernt',
'article.research.completed': 'Artikelrecherche abgeschlossen',
'article.research.failed': 'Artikelrecherche fehlgeschlagen',
'article.draft.started': 'KB-Entwurf wird geschrieben',
'article.draft.rejected': 'KB-Entwurf abgelehnt',
@@ -2076,7 +2093,36 @@
if (evt.metadata?.confidence !== undefined) meta.push(`${Math.round(Number(evt.metadata.confidence) * 100)}% Konfidenz`);
if (evt.metadata?.relation_type) meta.push(String(evt.metadata.relation_type));
if (evt.metadata?.research_result_count) meta.push(`${Number(evt.metadata.research_result_count)} Webquellen`);
if (evt.metadata?.reused_count !== undefined) meta.push(`${Number(evt.metadata.reused_count)} gespeicherte Volltextbelege`);
if (evt.metadata?.result_count !== undefined) meta.push(`${Number(evt.metadata.result_count)} SearXNG-Treffer`);
if (evt.metadata?.research_round !== undefined || evt.metadata?.round !== undefined) meta.push(`Runde ${Number(evt.metadata.research_round ?? evt.metadata.round)}`);
if (evt.metadata?.question_count !== undefined) meta.push(`${Number(evt.metadata.question_count)} Forschungsfragen`);
if (evt.metadata?.candidate_count !== undefined) meta.push(`${Number(evt.metadata.candidate_count)} Kandidaten`);
if (evt.metadata?.eligible_count !== undefined) meta.push(`${Number(evt.metadata.eligible_count)} fachlich geeignet`);
if (evt.metadata?.selected_count !== undefined) meta.push(`${Number(evt.metadata.selected_count)} zum Volltextabruf`);
if (evt.metadata?.gate_rejected_count !== undefined) meta.push(`${Number(evt.metadata.gate_rejected_count)} am Gate verworfen`);
if (evt.metadata?.duplicate_skipped_count) meta.push(`${Number(evt.metadata.duplicate_skipped_count)} bereits geprüft`);
if (evt.metadata?.fetch_limit_skipped_count) meta.push(`${Number(evt.metadata.fetch_limit_skipped_count)} wegen Fetch-Limit zurückgestellt`);
if (evt.metadata?.fetched_count !== undefined) meta.push(`${Number(evt.metadata.fetched_count)} Volltexte`);
if (evt.metadata?.accepted_count !== undefined) meta.push(`${Number(evt.metadata.accepted_count)} Belege akzeptiert`);
if (evt.metadata?.rejected_count !== undefined) meta.push(`${Number(evt.metadata.rejected_count)} Quellen verworfen`);
if (evt.metadata?.remaining_critical_gaps !== undefined) {
const value = Array.isArray(evt.metadata.remaining_critical_gaps) ? evt.metadata.remaining_critical_gaps.length : Number(evt.metadata.remaining_critical_gaps);
meta.push(`${value} kritische Lücken offen`);
}
if (evt.metadata?.resolved_critical_gaps !== undefined) meta.push(`${Number(evt.metadata.resolved_critical_gaps)} kritische Lücken gelöst`);
if (evt.metadata?.characters !== undefined) meta.push(`${Number(evt.metadata.characters).toLocaleString('de-DE')} Zeichen Volltext`);
if (evt.metadata?.dimensions !== undefined) meta.push(`${Number(evt.metadata.dimensions)} Dimensionen`);
if (evt.metadata?.relevance !== undefined) meta.push(`${Math.round(Number(evt.metadata.relevance) * 100)}% relevant`);
if (evt.metadata?.source_quality) meta.push(String(evt.metadata.source_quality));
if (evt.metadata?.source_quality_score !== undefined) meta.push(`${Math.round(Number(evt.metadata.source_quality_score) * 100)}% Quellenqualität`);
const researchURL = evt.metadata?.final_url || evt.metadata?.result_url || '';
if (researchURL) {
try { meta.push(new URL(String(researchURL)).hostname); } catch {}
}
if (evt.metadata?.actionable === true) meta.push('konkrete Schritte');
if (evt.metadata?.language) meta.push(String(evt.metadata.language));
if (evt.metadata?.gap_id) meta.push(`Lücke ${String(evt.metadata.gap_id)}`);
if (Array.isArray(evt.metadata?.result_domains) && evt.metadata.result_domains.length) meta.push(evt.metadata.result_domains.slice(0, 3).join(' · '));
if (evt.metadata?.searxng_http_status) meta.push(`HTTP ${Number(evt.metadata.searxng_http_status)}`);
if (evt.metadata?.error_kind) meta.push(String(evt.metadata.error_kind));
@@ -2105,12 +2151,16 @@
const eventQuery = evt.query || evt.metadata?.research_query || '';
if (eventQuery) meta.push('Query');
let message = evt.message || eventQuery || evt.type;
if (evt.metadata?.assessment_reason && (evt.type === 'article.research.evidence.accepted' || evt.type === 'article.research.evidence.rejected')) {
message = `${message} · ${String(evt.metadata.assessment_reason)}`;
}
if ((evt.type === 'think.started' || evt.type === 'think.relation.created' || evt.type === 'think.rejected' || evt.type === 'research.started' || evt.type?.startsWith('article.')) && nodes.length >= 2) {
message = `${message} · ${nodes.slice(0, 2).join(' ↔ ')}`;
} else if (evt.type === 'query.started' && evt.query) {
message = `${evt.source === 'agent' ? 'Agent' : evt.source === 'knowledgebase' ? 'Knowledgebase' : 'Brain'} verarbeitet eine Anfrage.`;
}
const sources = Array.isArray(evt.metadata?.result_titles) ? evt.metadata.result_titles.map(String).slice(0, 4) : [];
const sourceTitles = Array.isArray(evt.metadata?.result_titles) ? evt.metadata.result_titles : Array.isArray(evt.metadata?.selected_titles) ? evt.metadata.selected_titles : evt.metadata?.result_title ? [evt.metadata.result_title] : [];
const sources = sourceTitles.map(String).slice(0, 4);
return {time, title, message, meta, query: eventQuery, regions, sources, error: evt.metadata?.error ? String(evt.metadata.error) : '', endpoint: evt.metadata?.searxng_base_url ? String(evt.metadata.searxng_base_url) : '', cls: evt.type?.includes('think') || evt.type?.startsWith('article.') ? (evt.type?.includes('research') ? 'research' : 'think') : evt.type?.includes('research') ? 'research' : evt.type === 'graph.updated' || evt.type === 'scan.started' || evt.type?.startsWith('glpi.kb') || evt.type?.startsWith('persistence.') ? 'graph' : evt.source === 'agent' ? 'agent' : ''};
}