mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-21 14:08:11 +02:00
Moteur code façon Claude Code : Aider intégré avec routage automatique
- coder.py : enveloppe Aider (version figée 0.86.2), commits git auto dans le workspace, verrou d'exécution, dégradation propre si absent - router.py : classification automatique de chaque message (heuristique lexicale + micro-classification LLM sur les cas ambigus) — invisible pour l'utilisateur - routes/chat.py : routage code/agent, chemin code avec keepalive SSE, cartes fichier + commit dans le fil, meta.engine persisté - agent.py : outil code_task exécuté en thread — l'agent peut coder lui-même - tools/agent_config : code_task enregistré (actif par défaut), invite système mise à jour - main.py : workspace auto-initialisé en dépôt git au démarrage - Dockerfile : git ; requirements : aider-chat==0.86.2 - UI : glyphe code_task, description outil, aperçu d'instruction Tests : heuristiques 7/7, routage HTTP code+agent de bout en bout (SSE, meta persistée), agent appelant code_task, build front. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVay7z3y7q2gEe54ByAE6N
This commit is contained in:
12 files changed
+356
-15
No files matched your search
@@ -0,0 +1,106 @@
|
||||
"""Moteur code : enveloppe Aider pour l'édition multi-fichiers fiable.
|
||||
|
||||
Aider apporte ce qui fait la force de Claude Code/Codex : formats d'édition
|
||||
diff/search-replace robustes (même avec de petits modèles), repo map, et
|
||||
commits git automatiques dans le workspace.
|
||||
|
||||
L'API Python d'Aider n'étant pas officiellement stable, la version est FIGÉE
|
||||
dans requirements.txt (aider-chat==0.86.2) et tout l'import est local à la
|
||||
fonction (l'app démarre même si Aider est absent).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
from .config import settings
|
||||
|
||||
# Aider utilise le répertoire courant : un seul run à la fois.
|
||||
_RUN_LOCK = threading.Lock()
|
||||
|
||||
|
||||
def ensure_git(root: str) -> None:
|
||||
"""Initialise un dépôt git dans le workspace (requis pour les commits Aider)."""
|
||||
os.makedirs(root, exist_ok=True)
|
||||
if os.path.isdir(os.path.join(root, ".git")):
|
||||
return
|
||||
subprocess.run(["git", "init", "-q"], cwd=root, check=False)
|
||||
subprocess.run(["git", "config", "user.email", "loki@local"], cwd=root, check=False)
|
||||
subprocess.run(["git", "config", "user.name", "Loki"], cwd=root, check=False)
|
||||
|
||||
|
||||
def available() -> bool:
|
||||
"""Aider est-il installé ?"""
|
||||
try:
|
||||
import aider # noqa: F401
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
|
||||
def run_code_task(
|
||||
instruction: str,
|
||||
model: str,
|
||||
files: list[str] | None = None,
|
||||
) -> dict:
|
||||
"""Exécute une tâche de code via Aider (synchrone — lancer dans un thread).
|
||||
|
||||
Renvoie {ok, text, files, commit, commit_message, summary}.
|
||||
"""
|
||||
if not available():
|
||||
return {
|
||||
"ok": False,
|
||||
"summary": "moteur code indisponible (aider non installé)",
|
||||
"text": "", "files": [], "commit": None,
|
||||
}
|
||||
|
||||
from aider.coders import Coder
|
||||
from aider.io import InputOutput
|
||||
from aider.models import Model
|
||||
|
||||
root = os.path.abspath(settings.workspace_dir)
|
||||
ensure_git(root)
|
||||
|
||||
# Aider parle à Ollama via litellm : on pointe vers notre instance.
|
||||
os.environ["OLLAMA_API_BASE"] = settings.ollama_host
|
||||
|
||||
with _RUN_LOCK:
|
||||
prev_cwd = os.getcwd()
|
||||
os.chdir(root)
|
||||
try:
|
||||
io = InputOutput(yes=True, pretty=False, fancy_input=False)
|
||||
coder = Coder.create(
|
||||
main_model=Model(f"ollama_chat/{model}"),
|
||||
io=io,
|
||||
fnames=[os.path.join(root, f) for f in (files or [])],
|
||||
auto_commits=True,
|
||||
stream=False,
|
||||
use_git=True,
|
||||
suggest_shell_commands=False,
|
||||
detect_urls=False,
|
||||
)
|
||||
text = coder.run(instruction) or ""
|
||||
edited = sorted(coder.aider_edited_files or [])
|
||||
commit = getattr(coder, "last_aider_commit_hash", None)
|
||||
commit_msg = getattr(coder, "last_aider_commit_message", None)
|
||||
except Exception as exc: # aider peut lever des erreurs variées
|
||||
return {
|
||||
"ok": False,
|
||||
"summary": f"échec moteur code : {exc}",
|
||||
"text": "", "files": [], "commit": None,
|
||||
}
|
||||
finally:
|
||||
os.chdir(prev_cwd)
|
||||
|
||||
summary = f"{len(edited)} fichier(s) modifié(s)"
|
||||
if commit:
|
||||
summary += f" · commit {str(commit)[:7]}"
|
||||
return {
|
||||
"ok": True,
|
||||
"text": text.strip(),
|
||||
"files": edited,
|
||||
"commit": commit,
|
||||
"commit_message": commit_msg,
|
||||
"summary": summary,
|
||||
}
|
||||
Reference in new issue
Block a user