feat: unified_search + ai_proactive plugins with Ollama Cloud DeepSeek V4
- unified_search: Hybride Suche (PostgreSQL FTS + pgvector + RRF Fusion) - 5 Search Providers (Contact, Company, Mail, File, Event) - KI Query Understanding (Fuzzy, Facetten via LiteLLM) - DMS Text-Extraction (PDF, DOCX, XLSX, PPTX) - Embedding Pipeline (ollama/nomic-embed-text, 768 Dim) - Background Jobs für Indexierung - Plugin-basierte Provider Registry - ai_proactive: Proaktiver KI-Agent - Context-Tracking (Frontend → Backend → Event Bus) - Proactive Engine mit LLM Suggestion-Generierung - SSE Real-time Push an Frontend - 6 AI Tools für Tool Registry - Rate-Limiting + User Settings - Deep Analysis Background Jobs - Frontend Integration: - useAIContext Hook, SuggestionSidebar, SuggestionBadge - ProactiveAISettings Page, Search API Client - Globale Suche auf neue API umgestellt - Tests: test_unified_search.py + test_ai_proactive.py (alle bestanden) - Config: Ollama Cloud DeepSeek V4 als Default, konfigurierbar - Dependencies: PyMuPDF, python-docx, python-pptx, pgvector - Bugfixes: notification type_key length, migration IF NOT EXISTS
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { apiClient } from '@/api/client';
|
||||
|
||||
interface SuggestionBadgeProps {
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export function SuggestionBadge({ onClick }: SuggestionBadgeProps) {
|
||||
const [count, setCount] = useState(0);
|
||||
const [pulsing, setPulsing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Initial count
|
||||
apiClient.get('/api/v1/ai-proactive/suggestions').then(r => {
|
||||
setCount(r.data.items.length);
|
||||
}).catch(() => {});
|
||||
|
||||
// SSE for real-time updates
|
||||
const eventSource = new EventSource('/api/v1/ai-proactive/suggestions/stream');
|
||||
eventSource.onmessage = () => {
|
||||
setCount(prev => prev + 1);
|
||||
setPulsing(true);
|
||||
setTimeout(() => setPulsing(false), 2000);
|
||||
};
|
||||
|
||||
return () => eventSource.close();
|
||||
}, []);
|
||||
|
||||
if (count === 0) {
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className="relative p-2 text-gray-500 hover:text-gray-700 transition-colors"
|
||||
title="KI Vorschläge"
|
||||
>
|
||||
🤖
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`relative p-2 text-gray-500 hover:text-gray-700 transition-colors ${
|
||||
pulsing ? 'animate-pulse' : ''
|
||||
}`}
|
||||
title={`${count} KI Vorschläge`}
|
||||
>
|
||||
🤖
|
||||
<span
|
||||
className={`absolute -top-0 -right-0 min-w-[18px] h-[18px] flex items-center justify-center text-[10px] font-bold text-white rounded-full ${
|
||||
pulsing ? 'bg-red-500 animate-bounce' : 'bg-blue-500'
|
||||
}`}
|
||||
>
|
||||
{count > 99 ? '99+' : count}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user