kibali-api / tools /todo.py
lojol469-cmd
Initial commit: Kibali AI with RTX 5090 Blackwell support and CUDA 13.0 Nightly
f3a56a5
import time
from typing import List, Optional
import re
import os
from dotenv import load_dotenv
load_dotenv()
def analyze_query_type(prompt: str) -> dict:
"""Analyse le type de requête pour adapter la stratégie de réflexion"""
prompt_lower = prompt.lower()
analysis = {
"type": "general",
"needs_web": False,
"needs_memory": False,
"needs_docs": False,
"complexity": "simple",
"temporal": False,
"geographical": False
}
temporal_keywords = ["aujourd'hui", "maintenant", "récent", "actuel", "dernier", "2024", "2025"]
if any(kw in prompt_lower for kw in temporal_keywords):
analysis["temporal"] = True
analysis["needs_web"] = True
geo_keywords = ["gabon", "libreville", "port-gentil", "franceville", "oyem", "où", "localisation"]
if any(kw in prompt_lower for kw in geo_keywords):
analysis["geographical"] = True
doc_keywords = ["selon le document", "d'après le pdf", "dans le fichier", "uploadé"]
if any(kw in prompt_lower for kw in doc_keywords):
analysis["needs_docs"] = True
analysis["type"] = "document_query"
continuation_keywords = ["ils", "elles", "lui", "leur", "donc", "alors", "ensuite", "aussi", "également"]
if any(kw in prompt_lower for kw in continuation_keywords) or len(prompt.split()) < 5:
analysis["needs_memory"] = True
analysis["type"] = "continuation"
if len(prompt.split()) > 15 or (prompt.count("?") > 1):
analysis["complexity"] = "complex"
elif any(kw in prompt_lower for kw in ["pourquoi", "comment", "expliquer"]):
analysis["complexity"] = "medium"
web_keywords = ["actualité", "news", "prix", "cours", "météo", "horaire"]
if any(kw in prompt_lower for kw in web_keywords):
analysis["needs_web"] = True
analysis["type"] = "real_time"
return analysis
def detect_subject_shift(prompt: str, current_subject: str, subject_keywords: List[str]) -> dict:
"""Détecte un changement de sujet et évalue la force du changement"""
if not current_subject or not subject_keywords:
return {"shift_detected": False, "shift_strength": 0.0, "new_subject_detected": True, "reason": "Init"}
prompt_lower = prompt.lower()
prompt_words = set(re.findall(r'\b\w{4,}\b', prompt_lower))
keyword_overlap = len(prompt_words.intersection(set(subject_keywords)))
overlap_ratio = keyword_overlap / max(len(subject_keywords), 1)
shift_markers = ["maintenant", "sinon", "autre chose", "parlons de", "passons à", "nouveau sujet"]
has_shift_marker = any(marker in prompt_lower for marker in shift_markers)
shift_strength = 0.0
if overlap_ratio < 0.2: shift_strength += 0.5
if has_shift_marker: shift_strength += 0.3
return {
"shift_detected": shift_strength > 0.4,
"shift_strength": shift_strength,
"new_subject_detected": shift_strength > 0.6,
"reason": f"Overlap: {overlap_ratio:.1%}"
}
def generate_search_strategy(analysis: dict, subject_keywords: List[str], geo_info: dict) -> dict:
"""Génère une stratégie de recherche optimisée"""
strategy = {
"use_rag": analysis["needs_docs"],
"use_memory": analysis["needs_memory"],
"use_web": analysis["needs_web"],
"memory_k": 5, "rag_k": 3,
"web_enhanced": False, "search_query_suffix": ""
}
if analysis["complexity"] == "complex":
strategy.update({"memory_k": 8, "rag_k": 5})
if analysis["needs_web"]:
strategy["web_enhanced"] = True
suffix = " ".join(subject_keywords[:3]) if subject_keywords else ""
strategy["search_query_suffix"] = f"{suffix} {geo_info.get('city', 'Gabon')}"
return strategy
def execute_reflection_plan(
prompt: str,
geo_info: Optional[dict] = None,
messages: Optional[List] = None,
current_subject: Optional[str] = None,
subject_keywords: Optional[List[str]] = None
):
"""Phase de réflexion structurée compatible FastAPI (sans Streamlit)"""
geo_info = geo_info or {}
subject_keywords = subject_keywords or []
query_analysis = analyze_query_type(prompt)
subject_shift = detect_subject_shift(prompt, current_subject, subject_keywords)
search_strategy = generate_search_strategy(query_analysis, subject_keywords, geo_info)
# Logs internes (visibles dans Docker)
print(f"🧠 [REFLECTION] Type: {query_analysis['type']} | Web: {search_strategy['use_web']}")
return {
"analysis": query_analysis,
"subject_shift": subject_shift,
"strategy": search_strategy,
"execution_plan_ready": True
}