Skip to content

feat: refonte complète vers assistant niveau Iron Man - #1

Open
Adam-Blf wants to merge 4 commits into
mainfrom
claude/improve-project-functionality-bNygu
Open

feat: refonte complète vers assistant niveau Iron Man#1
Adam-Blf wants to merge 4 commits into
mainfrom
claude/improve-project-functionality-bNygu

Conversation

@Adam-Blf

Copy link
Copy Markdown
Owner

Frontend:

  • Zustand store persistant (settings, sessionId, stats)
  • Reconnaissance vocale native (Web Speech API)
  • Synthèse vocale TTS avec sélection de voix française
  • Rendu Markdown complet: bold, italic, code, listes, tableaux, blockquotes
  • CommandPalette (Ctrl+K) avec navigation clavier et fuzzy search
  • Chat: streaming amélioré, copie de messages, quick commands ML
  • Chat: textarea auto-resize, voice visualizer, scroll-to-bottom
  • Home: stats live (uptime, connexions, sessions), agents virtuels
  • Settings: persistance automatique Zustand, toggles, voix, vitesse
  • System: chargement réel des docs markdown depuis le backend
  • App: transitions entre pages, raccourcis globaux, toasts stylisés
  • CSS: nouvelles animations (glitch-flicker, voice-pulse, border-rotate)

Backend:

  • GET /api/status: uptime, connexions, sessions, état des services
  • GET /api/docs: liste les fichiers markdown disponibles
  • GET /api/docs/{id}: sert le contenu d'un fichier markdown
  • Prompt système amélioré style Jarvis avec date/heure dynamiques
  • Réponses mock intelligentes par catégorie (heure, statut, aide)
  • Session ID = client_id pour persistance cross-refresh
  • Compteur de messages et heartbeat amélioré

https://claude.ai/code/session_01QojiFU5TdqBzFEENVaePSK

claude added 4 commits April 14, 2026 02:03
Frontend:
- Zustand store persistant (settings, sessionId, stats)
- Reconnaissance vocale native (Web Speech API)
- Synthèse vocale TTS avec sélection de voix française
- Rendu Markdown complet: bold, italic, code, listes, tableaux, blockquotes
- CommandPalette (Ctrl+K) avec navigation clavier et fuzzy search
- Chat: streaming amélioré, copie de messages, quick commands ML
- Chat: textarea auto-resize, voice visualizer, scroll-to-bottom
- Home: stats live (uptime, connexions, sessions), agents virtuels
- Settings: persistance automatique Zustand, toggles, voix, vitesse
- System: chargement réel des docs markdown depuis le backend
- App: transitions entre pages, raccourcis globaux, toasts stylisés
- CSS: nouvelles animations (glitch-flicker, voice-pulse, border-rotate)

Backend:
- GET /api/status: uptime, connexions, sessions, état des services
- GET /api/docs: liste les fichiers markdown disponibles
- GET /api/docs/{id}: sert le contenu d'un fichier markdown
- Prompt système amélioré style Jarvis avec date/heure dynamiques
- Réponses mock intelligentes par catégorie (heure, statut, aide)
- Session ID = client_id pour persistance cross-refresh
- Compteur de messages et heartbeat amélioré

https://claude.ai/code/session_01QojiFU5TdqBzFEENVaePSK
LLM Multi-Provider (zéro coût):
- Ollama: modèles locaux (Llama 3.2, Mistral, Phi-3, DeepSeek, Gemma 2...)
  → 100% gratuit, privé, offline, aucune limite de requêtes
- Groq: cloud gratuit (14 400 req/jour sur Llama 3.3 70B)
  → Ultra-rapide (LPU), open-source, zéro coût
- OpenAI: gardé en fallback optionnel
- Auto-détection: Ollama → Groq → OpenAI → Mock

Agent avec outils (tous gratuits, sans clé API):
- 🔍 search_web: DuckDuckGo (recherche temps réel)
- 🌤️ get_weather: wttr.in (météo mondiale)
- 🧮 calculate: calculatrice scientifique sécurisée (ast parse)
- 📅 get_datetime: date et heure actuelles
- 💱 convert_currency: Frankfurter API (taux de change)
- 🌐 fetch_url: lecture et résumé de pages web

Backend:
- GET /api/models: liste Ollama installés + Groq/OpenAI dispo
- Auto-initialisation async avec détection Ollama via HTTP
- Prompt système dynamique (date/heure) style Jarvis

Frontend:
- Page /models: guide installation Ollama, modèles recommandés
- Badge modèle actif dans Chat (vert=Ollama, orange=Groq, violet=OpenAI)
- Provider + model exposés via useAbelChat hook
- CommandPalette: commande "Modèles LLM"
- Home: bouton raccourci vers /models
- .env.example complet avec documentation

https://claude.ai/code/session_01QojiFU5TdqBzFEENVaePSK
- start.sh: all-in-one launcher (backend + frontend), shows LAN IP for iPhone
- vite.config.ts: host 0.0.0.0 to expose dev server on local network
- requirements.txt: use >= versions to resolve pip conflicts (openai, supabase, langchain)
- .env.example: full documentation of all config options

https://claude.ai/code/session_01QojiFU5TdqBzFEENVaePSK
database.py was creating the Supabase client at module import time,
crashing the backend when SUPABASE_URL is empty. Now uses lazy
initialization and returns None gracefully when credentials are absent.

https://claude.ai/code/session_01QojiFU5TdqBzFEENVaePSK
Copilot AI review requested due to automatic review settings April 14, 2026 02:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refonte majeure de l’assistant A.B.E.L côté frontend et backend, avec ajout d’un orchestrateur multi-provider (Ollama/Groq/OpenAI/Mock), d’un système d’outils “agent”, et d’une UI type “command center” (voix, markdown, palette de commandes, pages système/modèles).

Changes:

  • Backend: détection automatique du provider LLM, endpoints /api/status, /api/models, /api/docs, et exécution d’outils (recherche, météo, calcul, etc.).
  • Frontend: store Zustand persisté (settings/session/stats), chat amélioré (streaming, markdown, copy, TTS/STT), Command Palette (Ctrl+K), pages Home/System/Models enrichies.
  • DX/UI: script start.sh, configuration .env.example enrichie, thème/animations CSS étendues, Vite exposé sur le réseau.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
start.sh Script de démarrage frontend+backend et affichage URL LAN
server/requirements.txt Ajout dépendances LangChain providers + outils (DDG) et passage en versions >=
server/app/services/tools.py Ajout de la toolbox (search/weather/calc/currency/url) + parsing/exécution
server/app/services/brain.py Orchestration LLM multi-provider + boucle d’outils + streaming
server/app/main.py Nouveaux endpoints status/models/docs + websocket amélioré + métriques
server/app/core/database.py DB optionnelle (Supabase) + health check robuste
server/app/core/config.py Config LLM/providers + flags tools + CORS étendu
server/.env.example Template complet (providers, tools, CORS, sécurité)
client/vite.config.ts Exposition réseau + proxy API/WS + preview host/port
client/src/styles/globals.css Thème/animations “Iron Man edition” + styles prose/chat
client/src/store/useStore.ts Store Zustand persisté (settings + messageCount) + sessionId persistant
client/src/pages/System.tsx Chargement docs depuis backend + statut live + download
client/src/pages/Settings.tsx Settings branchés sur store + toggles + TTS options
client/src/pages/Models.tsx Nouvelle page modèles/providers (Ollama/Groq/OpenAI)
client/src/pages/Home.tsx Dashboard live (status, agents, actions rapides)
client/src/pages/Chat.tsx Chat UI streaming + STT/TTS + markdown + copy + quick commands
client/src/hooks/useVoice.ts Web Speech API (STT+TTS) + nettoyage markdown
client/src/hooks/useAbelChat.ts WS avec session persistée + streaming amélioré + backoff
client/src/components/chat/MarkdownMessage.tsx Renderer markdown custom (inline, blocks, tables, code)
client/src/components/CommandPalette.tsx Palette de commandes (Ctrl/Cmd+K) + navigation clavier
client/src/App.tsx Transitions de pages + Toaster + Command Palette globale + route Models

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +14 to +27
def get_supabase_client():
"""Get Supabase client singleton. Returns None if not configured."""
if not _has_supabase_config():
return None
try:
from supabase import create_client
from functools import lru_cache

@lru_cache(maxsize=1)
def _client():
return create_client(settings.SUPABASE_URL, settings.SUPABASE_ANON_KEY)

class DatabaseError(Exception):
"""Custom database error."""
pass
return _client()
except Exception:

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_supabase_client()/get_supabase_admin() prétendent retourner un singleton mais le @lru_cache est défini à l’intérieur de la fonction : chaque appel recrée une nouvelle fonction + cache, donc le client peut être recréé à chaque fois. Déplacer le cache au niveau module (ou utiliser une variable globale) afin de réellement réutiliser la même instance entre appels.

Copilot uses AI. Check for mistakes.
Comment on lines +175 to +184
async def search_web(query: str, max_results: int = 4) -> str:
"""Recherche sur le web via DuckDuckGo (100% gratuit, sans clé API)."""
try:
from duckduckgo_search import DDGS

results = []
with DDGS() as ddgs:
for r in ddgs.text(query, max_results=max_results, region="fr-fr"):
results.append(r)

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

search_web() est async mais exécute la recherche DuckDuckGo de manière synchrone (DDGS().text(...)) dans la boucle event-loop. Ça peut bloquer FastAPI pendant la requête réseau/CPU. Exécuter cette partie dans un thread (ex: asyncio.to_thread) ou rendre search_web sync et le faire passer par l’executor dans execute_tool.

Copilot uses AI. Check for mistakes.
Comment on lines +327 to +328
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, lambda: tool(**params))

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dans execute_tool(), asyncio.get_event_loop() est déconseillé en contexte async (et peut poser problème selon la policy/versions). Préférer asyncio.get_running_loop() (ou asyncio.to_thread) pour exécuter les outils sync dans un executor.

Suggested change
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, lambda: tool(**params))
return await asyncio.to_thread(lambda: tool(**params))

Copilot uses AI. Check for mistakes.
Comment on lines +281 to +326
# First pass: check for tool calls
first_response = ""
async for chunk in self._llm.astream(messages):
if hasattr(chunk, "content") and chunk.content:
first_response += chunk.content

tool_calls = parse_tool_calls(first_response)

if tool_calls:
# Signal tool usage
yield f"\n*🔧 Utilisation d'outils...*\n\n"

# Execute tools
tool_results = []
for call in tool_calls[:settings.MAX_TOOL_ITERATIONS]:
logger.info(f"Tool: {call['name']} params={call['params']}")
result = await execute_tool(call["name"], call["params"])
tool_results.append(f"[{call['name']}]\n{result}")
yield f"*✅ {call['name']} terminé*\n"

# Second pass: synthesize
clean_first = strip_tool_calls(first_response)
context_msg = "\n\n".join(tool_results)
followup = [
*messages,
AIMessage(content=clean_first if clean_first else "Outil exécuté."),
HumanMessage(content=f"Résultats:\n\n{context_msg}\n\nSynthétise en une réponse claire.")
]
yield "\n"
full_final = ""
async for chunk in self._llm.astream(followup):
if hasattr(chunk, "content") and chunk.content:
full_final += chunk.content
yield chunk.content

self._add_to_history(session_id, "user", message)
self._add_to_history(session_id, "assistant", full_final)

else:
# No tools needed - stream directly
full_response = ""
async for chunk in self._llm.astream(messages):
if hasattr(chunk, "content") and chunk.content:
full_response += chunk.content
yield chunk.content

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stream_message() fait un “first pass” complet pour détecter des tool calls puis relance ensuite un second astream() même quand aucun outil n’est requis (double invocation du modèle, latence et coût x2, et le client ne reçoit rien pendant le first pass). Il faudrait parser les tool calls pendant le streaming (buffer + yield) ou utiliser une unique invocation et décider après coup sans relancer le modèle.

Copilot uses AI. Check for mistakes.
Comment thread server/app/main.py
Comment on lines +219 to +227
@app.get("/api/docs/{doc_id}")
async def get_doc(doc_id: str):
safe_id = re.sub(r"[^a-zA-Z0-9_\-]", "", doc_id)
candidates = [
DOCS_DIR / f"{safe_id}.md",
DOCS_DIR / f"{safe_id.upper()}.md",
ROOT_DIR / f"{safe_id}.md",
ROOT_DIR / f"{safe_id.upper()}.md",
]

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

safe_id peut devenir vide après sanitization (ex: doc_id ne contenant que des caractères filtrés). Dans ce cas on construit des candidats comme .md, ce qui est ambigu et peut mener à des comportements inattendus. Retourner une 400 si safe_id est vide (ou invalide) avant de chercher des fichiers.

Copilot uses AI. Check for mistakes.
Comment thread server/requirements.txt
tiktoken>=0.8.0

# ─── Agent Tools ──────────────────────────────────────────────────────────
duckduckgo-search>=7.3.2 # Recherche web gratuite (pas de clé API)

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Le passage à des contraintes >= (langchain/supabase/openai/...) rend l’environnement non reproductible et risque d’introduire des régressions lors d’installations futures. Garder des versions pinnées (ou introduire un fichier constraints.txt/lock) pour les déploiements, surtout côté backend.

Suggested change
duckduckgo-search>=7.3.2 # Recherche web gratuite (pas de clé API)
duckduckgo-search==7.3.2 # Recherche web gratuite (pas de clé API)

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +137
const speak = useCallback((text: string, speed = 1, voiceType = 'default') => {
if (!('speechSynthesis' in window)) return

const synth = window.speechSynthesis
synth.cancel()

const clean = stripMarkdown(text)
if (!clean.trim()) return

const utterance = new SpeechSynthesisUtterance(clean)
utterance.rate = Math.max(0.5, Math.min(2, speed))
utterance.pitch = 1
utterance.volume = 1
utterance.lang = language

// Try to select an appropriate voice
const loadVoices = () => {
const voices = synth.getVoices()
if (voices.length > 0) {
// Prefer French voices
const frenchVoice = voices.find(
(v) => v.lang.startsWith('fr') && !v.name.includes('Google')
) || voices.find((v) => v.lang.startsWith('fr'))

if (frenchVoice) {
utterance.voice = frenchVoice
}
}
}

if (synth.getVoices().length > 0) {
loadVoices()
} else {
synth.onvoiceschanged = loadVoices
}

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Le paramètre voiceType de speak() n’est jamais utilisé : l’UI permet de choisir Onyx/Nova/etc mais ça n’influence pas la voix Web Speech. Soit mapper voiceType vers une SpeechSynthesisVoice (par nom/lang), soit supprimer le paramètre/setting pour éviter une option non-fonctionnelle.

Copilot uses AI. Check for mistakes.
Comment thread server/app/core/config.py
Comment on lines +47 to +49
ENABLE_WEB_SEARCH: bool = True # DuckDuckGo (free, no key)
ENABLE_WEATHER: bool = True # wttr.in (free, no key)
ENABLE_CALCULATOR: bool = True # Safe Python eval

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Les flags ENABLE_WEB_SEARCH / ENABLE_WEATHER / ENABLE_CALCULATOR sont exposés dans la config mais ne sont jamais consultés dans l’exécution des outils (tout est toujours disponible dans TOOL_REGISTRY). Soit les appliquer (filtrer/valider dans execute_tool/registry), soit les retirer pour éviter une config trompeuse.

Suggested change
ENABLE_WEB_SEARCH: bool = True # DuckDuckGo (free, no key)
ENABLE_WEATHER: bool = True # wttr.in (free, no key)
ENABLE_CALCULATOR: bool = True # Safe Python eval

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 18
@@ -9,62 +9,96 @@ import {
Moon,
Sun,
Save,
RotateCcw
RotateCcw,
Mic,
Zap,
Eye,
Clock,
Languages,
} from 'lucide-react'

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imports inutilisés : useEffect n’est pas utilisé et plusieurs icônes (Mic, Zap, Eye, Clock) ne semblent pas référencées dans le composant. À nettoyer pour éviter warnings/erreurs de lint et réduire le bundle.

Copilot uses AI. Check for mistakes.
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const host = window.location.hostname
const port = import.meta.env.VITE_BACKEND_PORT || '8000'
// In dev, backend is proxied via vite — use relative path

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Le commentaire dit « en dev, backend est proxifié via Vite — use relative path », mais getWsUrl() construit en dev une URL directe ws://${host}:${port} (pas relative, pas de proxy). Corriger le commentaire ou utiliser effectivement une URL relative (ex: ${protocol}//${window.location.host}/ws/...) pour éviter la confusion.

Suggested change
// In dev, backend is proxied via vite — use relative path
// In dev, connect directly to the backend host/port configured for the API.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants