mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-16 19:59:56 +02:00
Backend : - tools.py : read_file / write_file / list_dir confinés au workspace (anti-traversal) - agent.py : boucle de tool-calling itérative au-dessus d'Ollama, génère des événements (token, tool_call, tool_result, final) - routes/chat.py : pilote run_agent, relaie les événements en SSE, persiste la réponse finale + le récap des outils (meta) - routes/files.py : arborescence et contenu du workspace (confiné) - db.py : colonne meta sur messages (+ migration douce), list_messages_for_model Frontend : - ToolCard : carte d'appel d'outil dans le fil (statut en cours/terminé/échec) - streamChat : événements tool_call / tool_result - store : streamTools, fileTree, preview (ouverture auto d'un HTML écrit) - LeftPanel : arborescence réelle du workspace, clic -> aperçu - PreviewPanel : rendu HTML en iframe + onglet Code - ChatPanel : rendu des outils dans les bulles agent (streaming + persisté) Tests manuels : outils + confinement, boucle agent (mock Ollama), chat HTTP de bout en bout (SSE, persistance meta, fichiers écrits, confinement route). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVay7z3y7q2gEe54ByAE6N
50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
"""Routes de lecture du workspace (arborescence + contenu d'un fichier)."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
|
|
from ..config import settings
|
|
from ..tools import ToolError, _safe_path
|
|
|
|
router = APIRouter(prefix="/api/files", tags=["files"])
|
|
|
|
|
|
def _tree(path: str) -> list[dict]:
|
|
"""Arborescence triée (dossiers d'abord) du workspace."""
|
|
items = []
|
|
for name in sorted(os.listdir(path)):
|
|
if name.startswith("."):
|
|
continue
|
|
full = os.path.join(path, name)
|
|
rel = os.path.relpath(full, os.path.abspath(settings.workspace_dir))
|
|
if os.path.isdir(full):
|
|
items.append({"name": name, "path": rel, "type": "dir",
|
|
"children": _tree(full)})
|
|
else:
|
|
items.append({"name": name, "path": rel, "type": "file",
|
|
"size": os.path.getsize(full)})
|
|
# Dossiers avant fichiers
|
|
items.sort(key=lambda x: (x["type"] != "dir", x["name"]))
|
|
return items
|
|
|
|
|
|
@router.get("")
|
|
async def list_files() -> dict:
|
|
root = os.path.abspath(settings.workspace_dir)
|
|
os.makedirs(root, exist_ok=True)
|
|
return {"tree": _tree(root)}
|
|
|
|
|
|
@router.get("/content")
|
|
async def file_content(path: str) -> dict:
|
|
try:
|
|
target = _safe_path(path)
|
|
except ToolError as exc:
|
|
raise HTTPException(400, str(exc))
|
|
if not os.path.isfile(target):
|
|
raise HTTPException(404, "fichier introuvable")
|
|
with open(target, "r", encoding="utf-8", errors="replace") as f:
|
|
return {"path": path, "content": f.read()}
|