first push
This commit is contained in:
48
.gitignore
vendored
Normal file
48
.gitignore
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
|
||||||
|
# Chainlit
|
||||||
|
.chainlit/
|
||||||
|
|
||||||
|
# Application files
|
||||||
|
.files/
|
||||||
|
|
||||||
|
# Tests / coverage
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# Type checkers / linters
|
||||||
|
.mypy_cache/
|
||||||
|
.ruff_cache/
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# IDEs
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
|
# Jupyter
|
||||||
|
.ipynb_checkpoints/
|
||||||
136
README.md
Normal file
136
README.md
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
# Projet ARC
|
||||||
|
|
||||||
|
## Contexte du Projet
|
||||||
|
ARC est une plateforme d'automatisation du développement logiciel basée sur un workflow multi-agents (IA). Le système orchestre plusieurs modèles d'IA spécialisés pour transformer un besoin utilisateur en un code source validé, testé et stocké.
|
||||||
|
___
|
||||||
|
### Étape 0 : Préparation de l'environnement du projet
|
||||||
|
Tâches :
|
||||||
|
- Créer backend minimal
|
||||||
|
- Créer modèle de données simple
|
||||||
|
- langGraph
|
||||||
|
___
|
||||||
|
### Étape 1 : Analyse du Besoin & Qualification (Agent PM / Business Analyst)
|
||||||
|
- L'utilisateur entre une demande en langage naturel.
|
||||||
|
- **Agent 1 (PM)** analyse la demande. Si des informations manquent pour coder, il pose des questions clarificatrices à l'utilisateur jusqu'à obtenir un cahier des charges complet.
|
||||||
|
- **Vérification BDD :** Avant de coder, le système cherche dans une base de données vectorielle si un projet similaire existe déjà.
|
||||||
|
- *Si oui :* On propose le lien à l'utilisateur. Si l'utilisateur valide, le workflow s'arrête ici.
|
||||||
|
- *Si non (ou si l'utilisateur rejette l'existant) :* On passe à l'étape 2.
|
||||||
|
|
||||||
|
Outils :
|
||||||
|
- LangGraph -> LangGraph est adapté aux workflows multi‑agents avec états, transitions conditionnelles, persistance et human‑in‑the‑loop
|
||||||
|
- Python -> backend, les agents, les appels LLM, les tests, les embeddings et les intégrations + compatible avec autres tools
|
||||||
|
- Pydantic AI / Pydantic -> forcer l’Agent PM à produire un cahier des charges structuré
|
||||||
|
- Chainlit -> adapté aux interfaces conversationnelles
|
||||||
|
BDD :
|
||||||
|
- Qdrant -> adapté à la recherche sémantique + stocker les embeddings de projets/scripts
|
||||||
|
- Snowflake Arctic Embed 2.0 -> modèle d’embedding
|
||||||
|
- Redis (optionnel) -> cache de recherche ;sessions utilisateur ;état temporaire ;verrouillage d’un workflow ;file d’attente simple
|
||||||
|
___
|
||||||
|
### Étape 2 : Génération de Code (Agent Développeur)
|
||||||
|
- **Agent 2 (Dev)** reçoit le cahier des charges validé et génère l'arborescence et le code source du projet.
|
||||||
|
|
||||||
|
Outils :
|
||||||
|
- LangGraph -> LangGraph est adapté aux workflows multi‑agents avec états, transitions conditionnelles, persistance et human‑in‑the‑loop
|
||||||
|
- Mistral ou Gemma (modèle trop généraliste/léger->tache simple) -> DeepSeek Coder/Qwen2.5
|
||||||
|
- vLLM -> meilleur choix qu’Ollama pour une plateforme plus industrialisée. Llama.cpp modèles quantifiés sur CPU ou machines modestes + moins adapté à une plateforme multi‑utilisateur
|
||||||
|
- Pydantic
|
||||||
|
___
|
||||||
|
### Étape 3 : Tests et Assurance Qualité (Agent QA / Testeur)
|
||||||
|
- **Agent 3 (QA)** récupère le code de l'Agent 2. Il doit exécuter le code (via une sandbox sécurisée) ou générer/exécuter des tests unitaires pour vérifier la qualité, la sécurité et le fonctionnement.
|
||||||
|
- **Boucle de correction automatique (Loop 1) :** Si les tests échouent, l'Agent 3 renvoie les erreurs à l'Agent 2 avec les logs. L'Agent 2 corrige et renvoie à l'Agent 3. Cette boucle tourne au maximum 3 fois jusqu'à ce que le code soit "vert".
|
||||||
|
- **EXTENSION FUTURE** si les tests échouent 3 fois, envoyé à une IA plus puissante.
|
||||||
|
|
||||||
|
Outils :
|
||||||
|
- Docker
|
||||||
|
- Ruff -> qualité de code
|
||||||
|
- Bandit -> sécurité
|
||||||
|
- Semgrep (optionnel) -> règles de sécurité et qualité plus larges
|
||||||
|
Boucle correction :
|
||||||
|
- LangGraph
|
||||||
|
- Pydantic
|
||||||
|
___
|
||||||
|
### Étape 4 : Livraison & Feedback Utilisateur (Boucle Humaine)
|
||||||
|
- Une fois le code validé par l'Agent 3, il est présenté à l'utilisateur.
|
||||||
|
- L'utilisateur teste et valide.
|
||||||
|
- *Si Validé :* Le projet est sauvegardé dans la base de données (pour la recherche de l'Étape 1) et livré (ex: zip ou dépôt GitHub).
|
||||||
|
- *Si Refusé :* L'utilisateur indique ce qui ne va pas. Tout le contexte (code actuel + retours) est renvoyé à l'**Étape 1** pour réanalyse, et le cycle recommence.
|
||||||
|
|
||||||
|
Outils :
|
||||||
|
- Chainlit
|
||||||
|
- Git
|
||||||
|
___
|
||||||
|
### Étape finale :
|
||||||
|
|
||||||
|
Tâches :
|
||||||
|
- Tests fonctionnels de bout en bout
|
||||||
|
- Sécurité minimale
|
||||||
|
- Documentation finale
|
||||||
|
- Présentation
|
||||||
|
|
||||||
|
## Structure du projet
|
||||||
|
```bash
|
||||||
|
backend/
|
||||||
|
│
|
||||||
|
├── app/
|
||||||
|
│ ├── api/
|
||||||
|
│ │ ├── routes/
|
||||||
|
│ │ │ ├── health.py
|
||||||
|
│ │ │ └── workflow.py
|
||||||
|
│ │ └── deps.py
|
||||||
|
│ │
|
||||||
|
│ ├── core/
|
||||||
|
│ │ ├── config.py
|
||||||
|
│ │ ├── logging.py
|
||||||
|
│ │ └── security.py
|
||||||
|
│ │
|
||||||
|
│ ├── graph/ # LangGraph
|
||||||
|
│ │ ├── state.py
|
||||||
|
│ │ ├── nodes.py
|
||||||
|
│ │ └── workflow.py
|
||||||
|
│ │
|
||||||
|
│ ├── agents/ # Gestion agents IA
|
||||||
|
│ │ ├── pm_agent.py
|
||||||
|
│ │ ├── dev_agent.py
|
||||||
|
│ │ └── qa_agent.py
|
||||||
|
│ │
|
||||||
|
│ ├── schemas/ # Pydantic
|
||||||
|
│ │ ├── api.py
|
||||||
|
│ │ ├── spec.py
|
||||||
|
│ │ └── project.py
|
||||||
|
│ │
|
||||||
|
│ ├── models/ # modèles métier / persistance (métadonnées d’un projet/version/statut/lien Git/hash/tags)
|
||||||
|
│ │ └── project.py
|
||||||
|
│ │
|
||||||
|
│ ├── services/
|
||||||
|
│ │ ├── workflow_service.py
|
||||||
|
│ │ ├── embedding_service.py
|
||||||
|
│ │ ├── retrieval_service.py
|
||||||
|
│ │ └── delivery_service.py
|
||||||
|
│ │
|
||||||
|
│ ├── repositories/ # accès externes, Qdrant / Redis / stockage
|
||||||
|
│ │ ├── qdrant_repository.py
|
||||||
|
│ │ ├── redis_repository.py
|
||||||
|
│ │ └── project_repository.py
|
||||||
|
│ │
|
||||||
|
│ ├── llm/ # appels modèles
|
||||||
|
│ │ ├── client.py # wrapper d’appel
|
||||||
|
│ │ ├── prompts.py # prompts centralisés
|
||||||
|
│ │ └── providers.py # Gemma/llama.cpp....
|
||||||
|
│ │
|
||||||
|
│ ├── sandbox/
|
||||||
|
│ │ └── docker_runner.py
|
||||||
|
│ │
|
||||||
|
│ ├── main.py
|
||||||
|
│ └── __init__.py
|
||||||
|
│
|
||||||
|
├── chainlit_app.py
|
||||||
|
├── tests/
|
||||||
|
│ ├── test_health.py
|
||||||
|
│ ├── test_workflow.py
|
||||||
|
│ └── test_agents.py
|
||||||
|
│
|
||||||
|
├── .env
|
||||||
|
├── requirements.txt
|
||||||
|
├── Dockerfile
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
23
backend/Dockerfile
Normal file
23
backend/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM python:3.13.13
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir \
|
||||||
|
--trusted-host pypi.org \
|
||||||
|
--trusted-host pypi.python.org \
|
||||||
|
--trusted-host files.pythonhosted.org \
|
||||||
|
-r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN chmod +x start.sh
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
EXPOSE 8001
|
||||||
|
|
||||||
|
CMD ["./start.sh"]
|
||||||
39
backend/README.md
Normal file
39
backend/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# ARC Backend
|
||||||
|
|
||||||
|
Backend minimal pour le projet ARC :
|
||||||
|
- API FastAPI
|
||||||
|
- orchestration LangGraph
|
||||||
|
- agents PM / Dev / QA
|
||||||
|
- interface Chainlit
|
||||||
|
- intégration future Qdrant / Redis / vLLM
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m venv .venv
|
||||||
|
.venv\Scripts\activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
uvicorn app.main:app --reload --port 8000
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lancer Chainlit
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chainlit run chainlit_app.py --port 8001
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lancement auto
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python .\tests\test_snowflake.py
|
||||||
|
docker compose exec app python tests/test_qdrant.py
|
||||||
|
```
|
||||||
|
|
||||||
|
API dispo sur :
|
||||||
|
- http://127.0.0.1:8001
|
||||||
14
backend/app/agents/dev_agent.py
Normal file
14
backend/app/agents/dev_agent.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
async def run_dev_agent(spec: dict, qa_feedback: list = None) -> dict:
|
||||||
|
"""
|
||||||
|
Agent Dev minimal :
|
||||||
|
- retourne une pseudo arborescence + un code exemple
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"tree": [
|
||||||
|
"main.py",
|
||||||
|
"README.md",
|
||||||
|
"app/__init__.py",
|
||||||
|
],
|
||||||
|
"code": 'print("Hello from ARC generated project")',
|
||||||
|
"spec_title": spec.get("title"),
|
||||||
|
}
|
||||||
15
backend/app/agents/pm_agent.py
Normal file
15
backend/app/agents/pm_agent.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from app.schemas.spec import ProjectSpec
|
||||||
|
|
||||||
|
|
||||||
|
async def run_pm_agent(user_input: str) -> ProjectSpec:
|
||||||
|
"""
|
||||||
|
Agent PM minimal :
|
||||||
|
- transforme l'entrée utilisateur en cahier des charges structuré
|
||||||
|
"""
|
||||||
|
return ProjectSpec(
|
||||||
|
title="Projet généré depuis demande utilisateur",
|
||||||
|
description=user_input,
|
||||||
|
requirements=["MVP minimal", "Architecture modulaire"],
|
||||||
|
constraints=["Python", "LangGraph", "Pydantic"],
|
||||||
|
target_stack="Python",
|
||||||
|
)
|
||||||
10
backend/app/agents/qa_agent.py
Normal file
10
backend/app/agents/qa_agent.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
async def run_qa_agent(generated_code: dict) -> dict:
|
||||||
|
"""
|
||||||
|
Agent QA minimal :
|
||||||
|
- renvoie un statut de validation simulé
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"status": "passed",
|
||||||
|
"logs": [],
|
||||||
|
"checked_files": generated_code.get("tree", []),
|
||||||
|
}
|
||||||
0
backend/app/api/deps.py
Normal file
0
backend/app/api/deps.py
Normal file
8
backend/app/api/routes/health.py
Normal file
8
backend/app/api/routes/health.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
router = APIRouter(tags=["health"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/health")
|
||||||
|
def health():
|
||||||
|
return {"status": "ok"}
|
||||||
11
backend/app/api/routes/workflow.py
Normal file
11
backend/app/api/routes/workflow.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
from app.schemas.api import WorkflowRequest, WorkflowResponse
|
||||||
|
from app.services.workflow_service import run_arc_workflow
|
||||||
|
|
||||||
|
router = APIRouter(tags=["workflow"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/workflow/run", response_model=WorkflowResponse)
|
||||||
|
async def run_workflow(payload: WorkflowRequest):
|
||||||
|
result = await run_arc_workflow(payload.user_input)
|
||||||
|
return WorkflowResponse(**result)
|
||||||
25
backend/app/core/config.py
Normal file
25
backend/app/core/config.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
app_name: str = "ARC Backend"
|
||||||
|
app_env: str = "dev"
|
||||||
|
app_host: str = "0.0.0.0"
|
||||||
|
app_port: int = 8000
|
||||||
|
|
||||||
|
qdrant_url: str = "http://localhost:6333"
|
||||||
|
qdrant_collection: str = "arc_projects"
|
||||||
|
|
||||||
|
redis_url: str = "redis://localhost:6379/0"
|
||||||
|
|
||||||
|
llm_base_url: str = "http://gemma-server:8080/v1"
|
||||||
|
llm_api_key: str = "llama-cpp-local"
|
||||||
|
# llm_model: str = "gemma-4-E4B-it-UD-Q4_K_XL.gguf"
|
||||||
|
|
||||||
|
embedding_base_url: str = "http://localhost:8002/v1"
|
||||||
|
embedding_model: str = "snowflake-arctic-embed-m-v1.5"
|
||||||
|
|
||||||
|
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
8
backend/app/core/logging.py
Normal file
8
backend/app/core/logging.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging() -> None:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
|
||||||
|
)
|
||||||
0
backend/app/core/security.py
Normal file
0
backend/app/core/security.py
Normal file
56
backend/app/graph/nodes.py
Normal file
56
backend/app/graph/nodes.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from app.agents.pm_agent import run_pm_agent
|
||||||
|
from app.agents.dev_agent import run_dev_agent
|
||||||
|
from app.agents.qa_agent import run_qa_agent
|
||||||
|
from app.services.retrieval_service import find_existing_project
|
||||||
|
from app.graph.state import WorkflowState
|
||||||
|
|
||||||
|
async def pm_node(state: WorkflowState):
|
||||||
|
prompt = state["user_input"]
|
||||||
|
if state.get("user_feedback"):
|
||||||
|
prompt += f"\nRetour utilisateur pour correction : {state['user_feedback']}"
|
||||||
|
|
||||||
|
spec = await run_pm_agent(prompt)
|
||||||
|
return {
|
||||||
|
"spec": spec.model_dump(),
|
||||||
|
"status": "spec_ready",
|
||||||
|
"loop_count": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
async def retrieval_node(state: WorkflowState):
|
||||||
|
existing_project = await find_existing_project(state["user_input"])
|
||||||
|
return {
|
||||||
|
"existing_project": existing_project,
|
||||||
|
"status": "existing_found" if existing_project else "no_existing_project",
|
||||||
|
}
|
||||||
|
|
||||||
|
async def dev_node(state: WorkflowState):
|
||||||
|
qa_logs = state.get("qa_result", {}).get("logs", "") if state.get("qa_result") else None
|
||||||
|
|
||||||
|
generated_code = await run_dev_agent(state["spec"], qa_feedback=qa_logs)
|
||||||
|
return {
|
||||||
|
"generated_code": generated_code,
|
||||||
|
"status": "code_generated",
|
||||||
|
}
|
||||||
|
|
||||||
|
async def qa_node(state: WorkflowState):
|
||||||
|
qa_result = await run_qa_agent(state["generated_code"])
|
||||||
|
current_loops = state.get("loop_count", 0)
|
||||||
|
|
||||||
|
is_success = True
|
||||||
|
|
||||||
|
clean_qa_result = {"success": is_success, "raw": qa_result}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"qa_result": clean_qa_result,
|
||||||
|
"loop_count": current_loops if is_success else current_loops + 1,
|
||||||
|
"status": "qa_done",
|
||||||
|
}
|
||||||
|
|
||||||
|
async def human_review_node(state: WorkflowState):
|
||||||
|
print("[Human Review] Passage en mode automatique (Mock)...")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"existing_project_approved": True,
|
||||||
|
"is_completed": True,
|
||||||
|
"status": "approved_by_human"
|
||||||
|
}
|
||||||
13
backend/app/graph/state.py
Normal file
13
backend/app/graph/state.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from typing import TypedDict, Optional, Any, Dict
|
||||||
|
|
||||||
|
class WorkflowState(TypedDict, total=False):
|
||||||
|
user_input: str
|
||||||
|
spec: dict
|
||||||
|
existing_project: Optional[dict]
|
||||||
|
existing_project_approved: Optional[bool] # Choix utilisateur si projet similaire trouvé
|
||||||
|
generated_code: Optional[Dict[str, str]] # Arborescence et code
|
||||||
|
qa_result: Optional[dict] # Contient les clés 'success' et 'logs'
|
||||||
|
loop_count: int # Compteur pour la Loop 1 (Dev <-> QA)
|
||||||
|
user_feedback: Optional[str] # Retours si l'utilisateur refuse le code final
|
||||||
|
is_completed: bool # Statut de livraison finale
|
||||||
|
status: str
|
||||||
92
backend/app/graph/workflow.py
Normal file
92
backend/app/graph/workflow.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import warnings
|
||||||
|
from langchain_core._api.deprecation import LangChainPendingDeprecationWarning
|
||||||
|
warnings.filterwarnings("ignore", category=LangChainPendingDeprecationWarning)
|
||||||
|
|
||||||
|
from langgraph.graph import StateGraph, END
|
||||||
|
from app.graph.state import WorkflowState
|
||||||
|
from app.graph.nodes import (
|
||||||
|
pm_node,
|
||||||
|
retrieval_node,
|
||||||
|
dev_node,
|
||||||
|
qa_node,
|
||||||
|
human_review_node,
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Fonctions de Routage (Conditional Edges) ---
|
||||||
|
|
||||||
|
def route_after_retrieval(state: WorkflowState):
|
||||||
|
# Si un projet existe, on demande d'abord à l'humain (via le nœud de review)
|
||||||
|
if state.get("existing_project"):
|
||||||
|
return "human_review"
|
||||||
|
return "dev"
|
||||||
|
|
||||||
|
def route_after_qa(state: WorkflowState):
|
||||||
|
qa_res = state.get("qa_result", {})
|
||||||
|
|
||||||
|
# Loop 1 : Si échec des tests ET qu'on a pas dépassé 3 essais -> On renvoie chez le Dev
|
||||||
|
if not qa_res.get("success") and state.get("loop_count", 0) < 3:
|
||||||
|
return "dev"
|
||||||
|
|
||||||
|
# Si c'est vert (ou trop d'échecs), on présente le résultat à l'utilisateur
|
||||||
|
# EXTENSION FUTURE : si trop d'échecs, on pourrait envoyer à une IA plus puissante
|
||||||
|
return "human_review"
|
||||||
|
|
||||||
|
def route_after_human(state: WorkflowState):
|
||||||
|
# Cas d'un projet existant proposé
|
||||||
|
if state.get("existing_project") and not state.get("generated_code"):
|
||||||
|
if state.get("existing_project_approved") == True:
|
||||||
|
return END # L'utilisateur est satisfait du projet existant
|
||||||
|
return "dev" # L'utilisateur refuse l'existant, on génère du neuf
|
||||||
|
|
||||||
|
# Cas du code généré
|
||||||
|
if state.get("is_completed") == True:
|
||||||
|
return END
|
||||||
|
|
||||||
|
# Si l'utilisateur a refusé le code -> Retour à la case PM avec ses commentaires
|
||||||
|
return "pm"
|
||||||
|
|
||||||
|
# --- Assemblage du Graphe ---
|
||||||
|
|
||||||
|
graph = StateGraph(WorkflowState)
|
||||||
|
|
||||||
|
graph.add_node("pm", pm_node)
|
||||||
|
graph.add_node("retrieval", retrieval_node)
|
||||||
|
graph.add_node("dev", dev_node)
|
||||||
|
graph.add_node("qa", qa_node)
|
||||||
|
graph.add_node("human_review", human_review_node)
|
||||||
|
|
||||||
|
graph.set_entry_point("pm")
|
||||||
|
graph.add_edge("pm", "retrieval")
|
||||||
|
|
||||||
|
# Étape 1 : Choix après recherche vectorielle
|
||||||
|
graph.add_conditional_edges(
|
||||||
|
"retrieval",
|
||||||
|
route_after_retrieval,
|
||||||
|
{
|
||||||
|
"dev": "dev",
|
||||||
|
"human_review": "human_review",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Étape 2 & 3 : Boucle Dev <-> QA (Loop 1)
|
||||||
|
graph.add_edge("dev", "qa")
|
||||||
|
graph.add_conditional_edges(
|
||||||
|
"qa",
|
||||||
|
route_after_qa,
|
||||||
|
{
|
||||||
|
"dev": "dev",
|
||||||
|
"human_review": "human_review",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Étape 4 : Boucle de Feedback Humain (Loop 2) ou Clôture
|
||||||
|
graph.add_conditional_edges(
|
||||||
|
"human_review",
|
||||||
|
route_after_human,
|
||||||
|
{
|
||||||
|
"pm": "pm",
|
||||||
|
"dev": "dev",
|
||||||
|
END: END,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
compiled_graph = graph.compile()
|
||||||
12
backend/app/llm/client.py
Normal file
12
backend/app/llm/client.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from openai import AsyncOpenAI
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
|
def get_llm_client() -> AsyncOpenAI:
|
||||||
|
"""
|
||||||
|
Initialise le client de génération (LLM) compatible OpenAI.
|
||||||
|
Configuré pour pointer vers notre instance locale llama.cpp (Gemma 4).
|
||||||
|
"""
|
||||||
|
return AsyncOpenAI(
|
||||||
|
base_url=settings.llm_base_url,
|
||||||
|
api_key=settings.llm_api_key,
|
||||||
|
)
|
||||||
0
backend/app/llm/prompts.py
Normal file
0
backend/app/llm/prompts.py
Normal file
0
backend/app/llm/providers.py
Normal file
0
backend/app/llm/providers.py
Normal file
34
backend/app/main.py
Normal file
34
backend/app/main.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from app.api.routes.health import router as health_router
|
||||||
|
from app.api.routes.workflow import router as workflow_router
|
||||||
|
from app.core.config import settings
|
||||||
|
from app.core.logging import setup_logging
|
||||||
|
from app.repositories.qdrant_repository import QdrantRepository
|
||||||
|
|
||||||
|
setup_logging()
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
print("[Startup] Initialisation automatique de Qdrant dans Docker...")
|
||||||
|
qdrant_repo = QdrantRepository()
|
||||||
|
try:
|
||||||
|
await qdrant_repo.init_collection(vector_size=1024)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Startup] Erreur lors de l'initialisation de Qdrant : {e}")
|
||||||
|
yield
|
||||||
|
|
||||||
|
print("[Shutdown] Fermeture propre de la connexion Qdrant...")
|
||||||
|
await qdrant_repo.close()
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title=settings.app_name,
|
||||||
|
docs_url=None,
|
||||||
|
redoc_url=None,
|
||||||
|
openapi_url=None,
|
||||||
|
lifespan=lifespan
|
||||||
|
)
|
||||||
|
|
||||||
|
app.include_router(health_router, prefix="/api")
|
||||||
|
app.include_router(workflow_router, prefix="/api")
|
||||||
10
backend/app/models/project.py
Normal file
10
backend/app/models/project.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectRecord(BaseModel):
|
||||||
|
id: Optional[str] = None
|
||||||
|
title: str
|
||||||
|
summary: str
|
||||||
|
tags: List[str] = []
|
||||||
|
repository_url: Optional[str] = None
|
||||||
0
backend/app/repositories/project_repository.py
Normal file
0
backend/app/repositories/project_repository.py
Normal file
58
backend/app/repositories/qdrant_repository.py
Normal file
58
backend/app/repositories/qdrant_repository.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# backend/app/repositories/qdrant_repository.py
|
||||||
|
from typing import Optional, List
|
||||||
|
from qdrant_client import AsyncQdrantClient
|
||||||
|
from qdrant_client.http import models
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
|
class QdrantRepository:
|
||||||
|
def __init__(self):
|
||||||
|
# Initialisation du client asynchrone
|
||||||
|
self.client = AsyncQdrantClient(
|
||||||
|
url=settings.qdrant_url,
|
||||||
|
# api_key=getattr(settings, "qdrant_api_key", None) # Qdrant Cloud
|
||||||
|
)
|
||||||
|
self.collection_name = settings.qdrant_collection
|
||||||
|
|
||||||
|
async def init_collection(self, vector_size: int = 1024):
|
||||||
|
"""
|
||||||
|
Crée la collection si elle n'existe pas encore.
|
||||||
|
1024 correspond à la taille des vecteurs de Snowflake Arctic Embed 2.0 (large).
|
||||||
|
"""
|
||||||
|
exists = await self.client.collection_exists(collection_name=self.collection_name)
|
||||||
|
if not exists:
|
||||||
|
print(f"[Qdrant] Création de la collection '{self.collection_name}'...")
|
||||||
|
await self.client.create_collection(
|
||||||
|
collection_name=self.collection_name,
|
||||||
|
vectors_config=models.VectorParams(
|
||||||
|
size=vector_size,
|
||||||
|
distance=models.Distance.COSINE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print("[Qdrant] Collection créée avec succès.")
|
||||||
|
else:
|
||||||
|
print(f"[Qdrant] La collection '{self.collection_name}' existe déjà.")
|
||||||
|
|
||||||
|
async def search_similar_project(self, query_vector: List[float], limit: int = 1) -> Optional[dict]:
|
||||||
|
"""
|
||||||
|
Effectue la vraie recherche vectorielle.
|
||||||
|
Note : On passe un 'query_vector' (généré par ton embedding_service) et non du texte brut.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
results = await self.client.search(
|
||||||
|
collection_name=self.collection_name,
|
||||||
|
query_vector=query_vector,
|
||||||
|
limit=limit
|
||||||
|
)
|
||||||
|
|
||||||
|
if results:
|
||||||
|
# On retourne le payload (les métadonnées du projet) du meilleur match
|
||||||
|
return results[0].payload
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Qdrant] Erreur lors de la recherche : {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
"""Ferme proprement la connexion au client"""
|
||||||
|
await self.client.close()
|
||||||
13
backend/app/repositories/redis_repository.py
Normal file
13
backend/app/repositories/redis_repository.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
|
|
||||||
|
class RedisRepository:
|
||||||
|
"""
|
||||||
|
Stub minimal Redis (optionnel).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.url = settings.redis_url
|
||||||
|
|
||||||
|
async def ping(self) -> bool:
|
||||||
|
return True
|
||||||
8
backend/app/sandbox/docker_runner.py
Normal file
8
backend/app/sandbox/docker_runner.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
async def run_in_sandbox(code: str) -> dict:
|
||||||
|
"""
|
||||||
|
Stub minimal pour future exécution sécurisée dans Docker.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"status": "not_implemented",
|
||||||
|
"logs": ["Sandbox Docker non branchée à l'étape 0."],
|
||||||
|
}
|
||||||
14
backend/app/schemas/api.py
Normal file
14
backend/app/schemas/api.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional, Any
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowRequest(BaseModel):
|
||||||
|
user_input: str
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowResponse(BaseModel):
|
||||||
|
status: str
|
||||||
|
spec: Optional[dict] = None
|
||||||
|
existing_project: Optional[dict] = None
|
||||||
|
generated_code: Optional[Any] = None
|
||||||
|
qa_result: Optional[Any] = None
|
||||||
10
backend/app/schemas/project.py
Normal file
10
backend/app/schemas/project.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectSummary(BaseModel):
|
||||||
|
id: Optional[str] = None
|
||||||
|
title: str
|
||||||
|
summary: str
|
||||||
|
tags: List[str] = []
|
||||||
|
repository_url: Optional[str] = None
|
||||||
10
backend/app/schemas/spec.py
Normal file
10
backend/app/schemas/spec.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectSpec(BaseModel):
|
||||||
|
title: str = Field(default="Projet ARC")
|
||||||
|
description: str
|
||||||
|
requirements: List[str] = Field(default_factory=list)
|
||||||
|
constraints: List[str] = Field(default_factory=list)
|
||||||
|
target_stack: Optional[str] = "Python"
|
||||||
0
backend/app/services/delivery_service.py
Normal file
0
backend/app/services/delivery_service.py
Normal file
40
backend/app/services/embedding_service.py
Normal file
40
backend/app/services/embedding_service.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import httpx
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
|
|
||||||
|
async def build_embedding(text: str) -> dict:
|
||||||
|
"""
|
||||||
|
Génère un vecteur d'embedding en interrogeant le conteneur local llama.cpp
|
||||||
|
"""
|
||||||
|
url = f"{settings.embedding_base_url}/embeddings"
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"input": text,
|
||||||
|
"model": settings.embedding_model
|
||||||
|
}
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||||
|
try:
|
||||||
|
response = await client.post(url, json=payload, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
vector = data["data"][0]["embedding"]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"model": settings.embedding_model,
|
||||||
|
"text_length": len(text),
|
||||||
|
"vector": vector,
|
||||||
|
}
|
||||||
|
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
print(f"Erreur lors de la génération de l'embedding : {e}")
|
||||||
|
return {
|
||||||
|
"model": settings.embedding_model,
|
||||||
|
"text_length": len(text),
|
||||||
|
"vector": [],
|
||||||
|
}
|
||||||
11
backend/app/services/retrieval_service.py
Normal file
11
backend/app/services/retrieval_service.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from app.repositories.qdrant_repository import QdrantRepository
|
||||||
|
from app.services.embedding_service import build_embedding
|
||||||
|
|
||||||
|
|
||||||
|
qdrant_repository = QdrantRepository()
|
||||||
|
|
||||||
|
|
||||||
|
async def find_existing_project(user_input: str):
|
||||||
|
# query_vector = await build_embedding.get_mesh_embedding(user_input)
|
||||||
|
dummy_vector = [0.0] * 1024 # A modifier avec un vrai embedding plus tard TODO
|
||||||
|
return await qdrant_repository.search_similar_project(query_vector=dummy_vector)
|
||||||
6
backend/app/services/workflow_service.py
Normal file
6
backend/app/services/workflow_service.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from app.graph.workflow import compiled_graph
|
||||||
|
|
||||||
|
|
||||||
|
async def run_arc_workflow(user_input: str) -> dict:
|
||||||
|
result = await compiled_graph.ainvoke({"user_input": user_input})
|
||||||
|
return result
|
||||||
0
backend/chainlit.md
Normal file
0
backend/chainlit.md
Normal file
25
backend/chainlit_app.py
Normal file
25
backend/chainlit_app.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import chainlit as cl
|
||||||
|
import httpx
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
@cl.on_chat_start
|
||||||
|
async def on_chat_start():
|
||||||
|
await cl.Message(
|
||||||
|
content="Bonjour 👋 Je suis ARC. Décris-moi ton besoin logiciel."
|
||||||
|
).send()
|
||||||
|
|
||||||
|
|
||||||
|
@cl.on_message
|
||||||
|
async def on_message(message: cl.Message):
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.post(
|
||||||
|
"http://127.0.0.1:8000/api/workflow/run",
|
||||||
|
json={"user_input": message.content},
|
||||||
|
)
|
||||||
|
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
await cl.Message(
|
||||||
|
content=f"Résultat workflow :\n```json\n{json.dumps(result, indent=2, ensure_ascii=False)}\n```"
|
||||||
|
).send()
|
||||||
102
backend/docker-compose.yml
Normal file
102
backend/docker-compose.yml
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
services:
|
||||||
|
qdrant:
|
||||||
|
image: qdrant/qdrant:latest
|
||||||
|
container_name: qdrant-arc
|
||||||
|
ports:
|
||||||
|
- "6333:6333"
|
||||||
|
- "6334:6334"
|
||||||
|
environment:
|
||||||
|
- QDRANT__TELEMETRY_DISABLED=true
|
||||||
|
volumes:
|
||||||
|
- qdrant_storage:/qdrant/storage
|
||||||
|
networks:
|
||||||
|
- arc-network
|
||||||
|
|
||||||
|
download-model:
|
||||||
|
image: alpine:latest
|
||||||
|
container_name: download-embedding-model
|
||||||
|
volumes:
|
||||||
|
- model_storage:/models
|
||||||
|
command: >
|
||||||
|
sh -c "
|
||||||
|
if [ ! -f /models/snowflake-arctic-embed-m-v1.5-f16.gguf ]; then
|
||||||
|
echo 'Téléchargement du modèle (Contournement SSL Proxy activé)...';
|
||||||
|
wget --no-check-certificate 'https://huggingface.co/Snowflake/snowflake-arctic-embed-m-v1.5/resolve/main/gguf/snowflake-arctic-embed-m-v1.5-f16.gguf' -O /models/snowflake-arctic-embed-m-v1.5-f16.gguf;
|
||||||
|
echo 'Téléchargement terminé avec succès !';
|
||||||
|
else
|
||||||
|
echo 'Le modèle est déjà présent.';
|
||||||
|
fi
|
||||||
|
"
|
||||||
|
|
||||||
|
embedding-server:
|
||||||
|
image: ghcr.io/ggml-org/llama.cpp:server
|
||||||
|
container_name: embedding-arc
|
||||||
|
volumes:
|
||||||
|
- model_storage:/models
|
||||||
|
ports:
|
||||||
|
- "8002:8080"
|
||||||
|
command: "-m /models/snowflake-arctic-embed-m-v1.5-f16.gguf --embedding --host 0.0.0.0 --port 8080"
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- arc-network
|
||||||
|
depends_on:
|
||||||
|
download-model:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
|
||||||
|
download-gemma:
|
||||||
|
image: alpine:latest
|
||||||
|
container_name: download-gemma-model
|
||||||
|
volumes:
|
||||||
|
- model_storage:/models
|
||||||
|
command: >
|
||||||
|
sh -c "
|
||||||
|
if [ ! -f /models/gemma-4-E4B-it-UD-Q4_K_XL.gguf ]; then
|
||||||
|
echo 'Téléchargement de Gemma 4 (Contournement SSL Proxy)...';
|
||||||
|
wget --no-check-certificate 'https://huggingface.co/unsloth/gemma-4-E4B-it-GGUF/resolve/main/gemma-4-E4B-it-UD-Q4_K_XL.gguf' -O /models/gemma-4-E4B-it-UD-Q4_K_XL.gguf;
|
||||||
|
echo 'Téléchargement de Gemma 4 terminé !';
|
||||||
|
else
|
||||||
|
echo 'Le modèle Gemma 4 est déjà présent.';
|
||||||
|
fi
|
||||||
|
"
|
||||||
|
|
||||||
|
gemma-server:
|
||||||
|
image: ghcr.io/ggml-org/llama.cpp:server
|
||||||
|
container_name: gemma-arc
|
||||||
|
volumes:
|
||||||
|
- model_storage:/models
|
||||||
|
ports:
|
||||||
|
- "8003:8080"
|
||||||
|
command: "-m /models/gemma-4-E4B-it-UD-Q4_K_XL.gguf --host 0.0.0.0 --port 8080 -c 4096"
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- arc-network
|
||||||
|
depends_on:
|
||||||
|
download-gemma:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
container_name: arc-app
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
- "8001:8001"
|
||||||
|
volumes:
|
||||||
|
- .:/workspace
|
||||||
|
environment:
|
||||||
|
- PYTHONPATH=/workspace
|
||||||
|
- QDRANT_URL=http://qdrant:6333
|
||||||
|
- QDRANT_COLLECTION=arc_projects
|
||||||
|
- EMBEDDING_SERVER_URL=http://embedding-server:8080
|
||||||
|
depends_on:
|
||||||
|
- qdrant
|
||||||
|
- embedding-server
|
||||||
|
networks:
|
||||||
|
- arc-network
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
qdrant_storage:
|
||||||
|
model_storage:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
arc-network:
|
||||||
|
driver: bridge
|
||||||
BIN
backend/public/logo_dark.png
Normal file
BIN
backend/public/logo_dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
backend/public/logo_light.png
Normal file
BIN
backend/public/logo_light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
16
backend/requirements.txt
Normal file
16
backend/requirements.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
fastapi==0.117.0
|
||||||
|
uvicorn[standard]==0.35.0
|
||||||
|
anyio>=4.6.0
|
||||||
|
pydantic==2.12
|
||||||
|
pydantic-settings==2.10.1
|
||||||
|
langgraph==0.2.39
|
||||||
|
chainlit==2.11.0
|
||||||
|
qdrant-client==1.11.3
|
||||||
|
redis==5.0.8
|
||||||
|
httpx==0.27.2
|
||||||
|
openai==1.51.2
|
||||||
|
python-dotenv==1.0.1
|
||||||
|
pytest==8.3.3
|
||||||
|
ruff==0.6.8
|
||||||
|
bandit==1.7.10
|
||||||
|
requests
|
||||||
7
backend/start.sh
Normal file
7
backend/start.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Démarrage du Backend FastAPI sur le port 8000..."
|
||||||
|
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload &
|
||||||
|
|
||||||
|
echo "Démarrage de Chainlit sur le port 8001..."
|
||||||
|
chainlit run chainlit_app.py --host 0.0.0.0 --port 8001
|
||||||
0
backend/tests/test_agents.py
Normal file
0
backend/tests/test_agents.py
Normal file
28
backend/tests/test_gemma.py
Normal file
28
backend/tests/test_gemma.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
def tester_gemma():
|
||||||
|
url = "http://localhost:8003/v1/chat/completions"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"messages": [
|
||||||
|
{"role": "user", "content": "Donne-moi une astuce de code Python originale."}
|
||||||
|
],
|
||||||
|
"temperature": 0.7
|
||||||
|
}
|
||||||
|
|
||||||
|
print("🧠 Envoi de la requête à Gemma 4...")
|
||||||
|
try:
|
||||||
|
response = requests.post(url, json=payload)
|
||||||
|
response.raise_for_status()
|
||||||
|
answer = response.json()["choices"][0]["message"]["content"]
|
||||||
|
|
||||||
|
print("\n🤖 Réponse de Gemma 4 :")
|
||||||
|
print("-" * 40)
|
||||||
|
print(answer)
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Erreur : {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
tester_gemma()
|
||||||
10
backend/tests/test_health.py
Normal file
10
backend/tests/test_health.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from fastapi.testclient import TestClient
|
||||||
|
from app.main import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
|
||||||
|
def test_health():
|
||||||
|
response = client.get("/api/health")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["status"] == "ok"
|
||||||
55
backend/tests/test_qdrant.py
Normal file
55
backend/tests/test_qdrant.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import asyncio
|
||||||
|
import random
|
||||||
|
from app.repositories.qdrant_repository import QdrantRepository
|
||||||
|
from qdrant_client.http import models
|
||||||
|
|
||||||
|
async def test_pipeline():
|
||||||
|
print("--- Test de connexion Qdrant ---")
|
||||||
|
repo = QdrantRepository()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 1. Tester la connexion et initialiser la collection
|
||||||
|
await repo.init_collection(vector_size=1024)
|
||||||
|
|
||||||
|
# 2. Insérer un faux projet pour valider le fonctionnement (Upsert)
|
||||||
|
print("\n[Test] Insertion d'un faux projet indexé...")
|
||||||
|
mock_vector = [random.uniform(-1.0, 1.0) for _ in range(1024)]
|
||||||
|
|
||||||
|
await repo.client.upsert(
|
||||||
|
collection_name=repo.collection_name,
|
||||||
|
points=[
|
||||||
|
models.PointStruct(
|
||||||
|
id=1,
|
||||||
|
vector=mock_vector,
|
||||||
|
payload={
|
||||||
|
"title": "Application E-commerce de test",
|
||||||
|
"description": "Un projet test généré pour valider Qdrant",
|
||||||
|
"git_url": "https://github.com/test/test"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
print("[Test] Faux projet inséré.")
|
||||||
|
|
||||||
|
# 3. Tester la recherche vectorielle
|
||||||
|
print("\n[Test] Lancement de la recherche vectorielle...")
|
||||||
|
project_found = await repo.search_similar_project(query_vector=mock_vector)
|
||||||
|
|
||||||
|
if project_found:
|
||||||
|
print(f"🎉 Succès ! Projet trouvé en BDD : {project_found['title']} ({project_found['git_url']})")
|
||||||
|
else:
|
||||||
|
print("❌ Erreur : Aucun projet trouvé alors qu'on vient d'en insérer un.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Échec critique du test : {e}")
|
||||||
|
print("Vérifie que ton conteneur Qdrant est bien lancé et que l'URL dans ton .env est correcte.")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
await repo.close()
|
||||||
|
print("\n--- Fin du test ---")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
asyncio.run(test_pipeline())
|
||||||
42
backend/tests/test_snowflake.py
Normal file
42
backend/tests/test_snowflake.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
def test_embedding_server():
|
||||||
|
url = "http://localhost:8002/v1/embeddings"
|
||||||
|
|
||||||
|
phrase = "Ceci est un test."
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"input": phrase
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Envoi de la phrase au serveur Snowflake Arctic local...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, json=payload, headers=headers)
|
||||||
|
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
resultat = response.json()
|
||||||
|
|
||||||
|
vecteur = resultat["data"][0]["embedding"]
|
||||||
|
tokens_utilises = resultat["usage"]["total_tokens"]
|
||||||
|
|
||||||
|
print("\n[SUCCÈS] Le serveur d'embedding répond parfaitement !")
|
||||||
|
print(f"Texte analysé : '{phrase}'")
|
||||||
|
print(f"Nombre de tokens consommés : {tokens_utilises}")
|
||||||
|
print(f"Dimension du vecteur : {len(vecteur)} (Attendu : 768)")
|
||||||
|
print(f"Début du vecteur (5 premiers chiffres) : {vecteur[:5]}")
|
||||||
|
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
print("\n[ERREUR] Impossible de joindre le serveur d'embedding.")
|
||||||
|
print("Vérifie que ton Docker Compose est bien démarré avec 'docker compose up'.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n[ERREUR] Une erreur inattendue est survenue : {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_embedding_server()
|
||||||
0
backend/tests/test_workflow.py
Normal file
0
backend/tests/test_workflow.py
Normal file
42
ressources/Etape0.md
Normal file
42
ressources/Etape0.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Projet ARC
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section 0 - Préparation de l'environnement du projet
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
section Initialisation
|
||||||
|
Créer structure projet :a1, 2026-06-09, 0.5d
|
||||||
|
Backend + structure :a2, 2026-06-09, 0.5d
|
||||||
|
|
||||||
|
section LangGraph
|
||||||
|
Installer LangGraph :b1, 2026-06-09, 0.5d
|
||||||
|
Créer structure workflow :b2, 2026-06-09, 1d
|
||||||
|
Définir state global :b3, 2026-06-09, 1d
|
||||||
|
|
||||||
|
section Setup Qdrant
|
||||||
|
Installer Qdrant :c1, after b3, 0.5d
|
||||||
|
Tester connexion Python :c2, after b3, 0.5d
|
||||||
|
|
||||||
|
section Setup modèle d’embedding
|
||||||
|
Installer Snowflake :d1, after b3, 0.5d
|
||||||
|
Implémenter embedding() :d2, after b3, 0.5d
|
||||||
|
|
||||||
|
section Setup LLM
|
||||||
|
Installer llama.cpp :e1, after b3, 0.5d
|
||||||
|
Tester appel modèle :e2, after b3, 0.5d
|
||||||
|
|
||||||
|
section Setup Chainlit
|
||||||
|
Installer Chainlit :f1, after e2, 0.5d
|
||||||
|
Lancer app test :f2, after e2, 0.5d
|
||||||
|
|
||||||
|
section Organisation du code
|
||||||
|
Créer dossiers agents :g1, after e2, 0.5d
|
||||||
|
Créer dossiers services :g2, after e2, 0.5d
|
||||||
|
|
||||||
|
section Logging et debug
|
||||||
|
Logs simples :h1, after e2, 0.5d
|
||||||
|
Structuration logs :h2, after e2, 0.5d
|
||||||
|
```
|
||||||
53
ressources/Etape1.md
Normal file
53
ressources/Etape1.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Projet ARC
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section 1 - Analyse du Besoin & Qualification
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
section Interface utilisateur
|
||||||
|
Créer interface Chainlit :a1, 2026-06-12, 1d
|
||||||
|
Connecter Chainlit → backend Python :a2, 2026-06-12, 0.5d
|
||||||
|
|
||||||
|
section Modèle cahier des charges
|
||||||
|
Définir structure JSON :b1, after a2, 0.5d
|
||||||
|
Schéma Pydantic :b2, after a2, 1d
|
||||||
|
|
||||||
|
section Agent PM
|
||||||
|
Prompt Agent PM :c1, after b2, 0.5d
|
||||||
|
Sortie structurée Pydantic :c2, after b2, 0.5d
|
||||||
|
Gestion erreurs :c3, after c2, 0.5d
|
||||||
|
|
||||||
|
section Questions clarificatrices
|
||||||
|
Détection champs manquants :d1, after c2, 0.5d
|
||||||
|
Génération questions LLM :d2, after d1, 0.5d
|
||||||
|
Boucle interaction Chainlit :d3, after d1, 1d
|
||||||
|
|
||||||
|
section Validation du cahier des charges
|
||||||
|
Affichage CDC :e1, after d3, 0.5d
|
||||||
|
Boutons validation/refus :e2, after d3, 0.5d
|
||||||
|
|
||||||
|
section Qdrant (BDD vectorielle)
|
||||||
|
Installer Qdrant :f1, after e2, 0.5d
|
||||||
|
Créer collection :f2, after e2, 0.5d
|
||||||
|
Structure payload :f3, after f2, 0.5d
|
||||||
|
|
||||||
|
section Embedding
|
||||||
|
Intégrer Snowflake Arctic :g1, after f2, 0.5d
|
||||||
|
Fonction embedding :g2, after f2, 0.5d
|
||||||
|
|
||||||
|
section Recherche d'existant
|
||||||
|
Recherche projets similaires :h1, after g2, 0.5d
|
||||||
|
Filtres payload :h2, after g2, 0.5d
|
||||||
|
Formatage résultats :h3, after h2, 0.5d
|
||||||
|
|
||||||
|
section Proposition utilisateur
|
||||||
|
Affichage résultats Chainlit :i1, after h2, 0.5d
|
||||||
|
Bouton "utiliser projet" :i2, after h2, 0.5d
|
||||||
|
Bouton "continuer génération" :i3, after h2, 0.5d
|
||||||
|
|
||||||
|
section Redis (optionnel)
|
||||||
|
Cache recherche :j1, after i3, 1d
|
||||||
|
```
|
||||||
50
ressources/Etape2.md
Normal file
50
ressources/Etape2.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Projet ARC
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section 2 - Génération de Code
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
section Préparation des entrées
|
||||||
|
Récupérer cahier des charges :a1, 2026-06-29, 0.5d
|
||||||
|
Normaliser / valider données :a2, 2026-06-29, 0.5d
|
||||||
|
|
||||||
|
section Format de sortie du code
|
||||||
|
Structure projet JSON/Pydantic :b1, after a2, 0.5d
|
||||||
|
Modèle Pydantic sortie code :b2, after a2, 1d
|
||||||
|
|
||||||
|
section Prompt Agent Dev
|
||||||
|
Prompt génération code :c1, after b2, 1d
|
||||||
|
Contraintes strictes :c2, after b2, 0.5d
|
||||||
|
|
||||||
|
section Intégration LLM
|
||||||
|
Setup llama.cpp :d1, after c2, 0.5d
|
||||||
|
Connexion LangGraph → LLM :d2, after c2, 0.5d
|
||||||
|
|
||||||
|
section Arborescence projet
|
||||||
|
Génération structure fichiers :e1, after d2, 0.5d
|
||||||
|
Vérification structure :e2, after d2, 0.5d
|
||||||
|
|
||||||
|
section Génération code source
|
||||||
|
Génération fichiers Python :f1, after e2, 2d
|
||||||
|
Conformité structure :f2, after f1, 1d
|
||||||
|
|
||||||
|
section Fichiers complémentaires
|
||||||
|
README.md :g1, after f2, 0.5d
|
||||||
|
requirements.txt :g2, after f2, 0.5d
|
||||||
|
Instructions exécution :g3, after f2, 0.5d
|
||||||
|
|
||||||
|
section Validation backend
|
||||||
|
Vérification fichiers :h1, after g3, 0.5d
|
||||||
|
Nettoyage output LLM :h2, after g3, 0.5d
|
||||||
|
|
||||||
|
section Intégration LangGraph
|
||||||
|
Ajouter noeud Agent Dev :i1, after h2, 0.5d
|
||||||
|
Connecter PM → Dev :i2, after h2, 0.5d
|
||||||
|
|
||||||
|
section Préparation QA
|
||||||
|
Formatter code sandbox :j1, after i2, 0.5d
|
||||||
|
Transmettre au state :j2, after i2, 0.5d
|
||||||
|
```
|
||||||
59
ressources/Etape3.md
Normal file
59
ressources/Etape3.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Projet ARC
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section 3 - Tests et Assurance Qualité
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
section Préparation entrées
|
||||||
|
Récupérer projet Agent Dev :a1, 2026-07-15, 0.5d
|
||||||
|
|
||||||
|
section Sandbox Docker
|
||||||
|
Dockerfile générique :b1, 2026-07-15, 1d
|
||||||
|
Script build/run :b2, after b1, 1d
|
||||||
|
Isolation environnement :b3, after b1, 0.5d
|
||||||
|
|
||||||
|
section Exécution sandbox
|
||||||
|
Lancer exécution :c1, after b3, 0.5d
|
||||||
|
Capturer logs :c2, after b3, 0.5d
|
||||||
|
|
||||||
|
section Ruff (qualité)
|
||||||
|
Installer Ruff :d1, after c2, 0.5d
|
||||||
|
Ruff check :d2, after c2, 0.5d
|
||||||
|
|
||||||
|
section Bandit (sécurité)
|
||||||
|
Installer Bandit :e1, after c2, 0.5d
|
||||||
|
Scan projet :e2, after c2, 0.5d
|
||||||
|
|
||||||
|
section Semgrep (optionnel)
|
||||||
|
Installer Semgrep :f1, after c2, 0.5d
|
||||||
|
Analyse règles :f2, after c2, 0.5d
|
||||||
|
|
||||||
|
section Structuration QA
|
||||||
|
Modèle Pydantic rapport :g1, after f2, 1d
|
||||||
|
Parser résultats outils :g2, after g1, 0.5d
|
||||||
|
|
||||||
|
section Agent QA
|
||||||
|
Prompt Agent QA :h1, after g2, 1d
|
||||||
|
Résumé intelligible :h2, after h1, 0.5d
|
||||||
|
Traduction erreurs → dev :h3, after h2, 1d
|
||||||
|
|
||||||
|
section Boucle Dev ↔ QA
|
||||||
|
Implémenter boucle LangGraph :i1, after h3, 1.5d
|
||||||
|
Condition stop :i2, after h3, 0.5d
|
||||||
|
Limite itérations :i3, after h3, 1d
|
||||||
|
|
||||||
|
section Logs pour correction
|
||||||
|
Structurer logs :j1, after i3, 0.5d
|
||||||
|
Injecter logs dans Agent Dev :j2, after i3, 0.5d
|
||||||
|
|
||||||
|
section Intégration LangGraph
|
||||||
|
Ajouter noeud QA :k1, after j2, 0.5d
|
||||||
|
Connecter Dev → QA → loop :k2, after j2, 0.5d
|
||||||
|
|
||||||
|
section Sécurisation minimale
|
||||||
|
Limiter temps exécution :l1, after k2, 0.5d
|
||||||
|
Bloquer accès disque/réseau :l2, after k2, 0.5d
|
||||||
|
```
|
||||||
53
ressources/Etape4.md
Normal file
53
ressources/Etape4.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Projet ARC
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section 4 - Livraison & Feedback Utilisateur
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
section Préparation affichage
|
||||||
|
Récupérer code validé :a1, 2026-08-03, 0.5d
|
||||||
|
Récupérer rapport QA :a2, 2026-08-03, 0.5d
|
||||||
|
|
||||||
|
section Affichage Chainlit
|
||||||
|
Afficher code :b1, after a2, 0.5d
|
||||||
|
Afficher rapport QA :b2, after a2, 0.5d
|
||||||
|
Afficher instructions exécution :b3, after a2, 0.5d
|
||||||
|
|
||||||
|
section Actions utilisateur
|
||||||
|
Bouton valider :c1, after a2, 0.5d
|
||||||
|
Bouton refuser :c2, after a2, 0.5d
|
||||||
|
|
||||||
|
section Gestion refus
|
||||||
|
Champ feedback :d1, after c2, 0.5d
|
||||||
|
Structuration Pydantic :d2, after c2, 0.5d
|
||||||
|
Injection LangGraph :d3, after c2, 0.5d
|
||||||
|
|
||||||
|
section Boucle retour PM
|
||||||
|
Transition QA → PM :e1, after d3, 0.5d
|
||||||
|
Conserver contexte + feedback :e2, after d3, 0.5d
|
||||||
|
|
||||||
|
section Génération ZIP
|
||||||
|
Créer archive :f1, after e2, 0.5d
|
||||||
|
Vérifier structure :f2, after e2, 0.5d
|
||||||
|
|
||||||
|
section Téléchargement
|
||||||
|
Bouton téléchargement ZIP :g1, after e2, 0.5d
|
||||||
|
|
||||||
|
section Sauvegarde projet
|
||||||
|
Sauvegarder code + metadata :h1, after g1, 0.5d
|
||||||
|
Flag validated :h2, after g1, 0.5d
|
||||||
|
|
||||||
|
section Réindexation Qdrant
|
||||||
|
Générer embedding :i1, after h2, 0.5d
|
||||||
|
Ajouter dans Qdrant :i2, after h2, 0.5d
|
||||||
|
|
||||||
|
section Git (optionnel)
|
||||||
|
Init dépôt :j1, after i2, 0.5d
|
||||||
|
|
||||||
|
section Intégration finale LangGraph
|
||||||
|
Ajouter noeud Delivery :k1, after i2, 0.5d
|
||||||
|
Connecter QA → Delivery → fin :k2, after i2, 0.5d
|
||||||
|
```
|
||||||
13
ressources/EtapeFinale.md
Normal file
13
ressources/EtapeFinale.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Projet ARC
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section finale - Rendu
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
Tests fonctionnels de bout en bout :a1, 2026-08-13, 0.5d
|
||||||
|
Documentation finale :a2, 2026-08-13, 0.5d
|
||||||
|
Présentation :a3, after a2, 1d
|
||||||
|
```
|
||||||
88
ressources/Projet.md
Normal file
88
ressources/Projet.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
### Étape 0 : Préparation de l'environnement du projet
|
||||||
|
Tâches :
|
||||||
|
- Créer backend minimal
|
||||||
|
- Créer modèle de données simple
|
||||||
|
- langGraph
|
||||||
|
___
|
||||||
|
|
||||||
|
### Étape 1 : Analyse du Besoin & Qualification (Agent PM / Business Analyst)
|
||||||
|
- L'utilisateur entre une demande en langage naturel.
|
||||||
|
- **Agent 1 (PM)** analyse la demande. Si des informations manquent pour coder, il pose des questions clarificatrices à l'utilisateur jusqu'à obtenir un cahier des charges complet.
|
||||||
|
- **Vérification BDD :** Avant de coder, le système cherche dans une base de données vectorielle si un projet similaire existe déjà.
|
||||||
|
- *Si oui :* On propose le lien à l'utilisateur. Si l'utilisateur valide, le workflow s'arrête ici.
|
||||||
|
- *Si non (ou si l'utilisateur rejette l'existant) :* On passe à l'étape 2.
|
||||||
|
|
||||||
|
Outils :
|
||||||
|
- LangGraph -> LangGraph est adapté aux workflows multi‑agents avec états, transitions conditionnelles, persistance et human‑in‑the‑loop
|
||||||
|
- Python -> backend, les agents, les appels LLM, les tests, les embeddings et les intégrations + compatible avec autres tools
|
||||||
|
- Pydantic AI / Pydantic -> forcer l’Agent PM à produire un cahier des charges structuré
|
||||||
|
- Chainlit -> adapté aux interfaces conversationnelles
|
||||||
|
BDD :
|
||||||
|
- Qdrant -> adapté à la recherche sémantique + stocker les embeddings de projets/scripts
|
||||||
|
- Snowflake Arctic Embed 2.0 -> modèle d’embedding
|
||||||
|
- Redis (optionnel) -> cache de recherche ;sessions utilisateur ;état temporaire ;verrouillage d’un workflow ;file d’attente simple
|
||||||
|
|
||||||
|
Tâches :
|
||||||
|
- Interface Chainlit
|
||||||
|
- Créer le prompt Agent PM
|
||||||
|
- Créer le schéma Pydantic du cahier des charges
|
||||||
|
- Gérer les questions clarificatrices
|
||||||
|
- Valider le cahier des charges
|
||||||
|
- Recherche d’existant (Qdrant/Snowflake Arctic Embed 2.0/Redis)
|
||||||
|
___
|
||||||
|
|
||||||
|
### Étape 2 : Génération de Code (Agent Développeur)
|
||||||
|
- **Agent 2 (Dev)** reçoit le cahier des charges validé et génère l'arborescence et le code source du projet.
|
||||||
|
|
||||||
|
Outils :
|
||||||
|
- LangGraph -> LangGraph est adapté aux workflows multi‑agents avec états, transitions conditionnelles, persistance et human‑in‑the‑loop
|
||||||
|
- Mistral ou Gemma (modèle trop généraliste/léger->tache simple) -> DeepSeek Coder/Qwen2.5
|
||||||
|
- vLLM -> meilleur choix qu’Ollama pour une plateforme plus industrialisée. Llama.cpp modèles quantifiés sur CPU ou machines modestes + moins adapté à une plateforme multi‑utilisateur
|
||||||
|
- Pydantic
|
||||||
|
|
||||||
|
Tâches :
|
||||||
|
- Créer le prompt Agent Dev
|
||||||
|
- Partie dev
|
||||||
|
___
|
||||||
|
|
||||||
|
### Étape 3 : Tests et Assurance Qualité (Agent QA / Testeur)
|
||||||
|
- **Agent 3 (QA)** récupère le code de l'Agent 2. Il doit exécuter le code (via une sandbox sécurisée) ou générer/exécuter des tests unitaires pour vérifier la qualité, la sécurité et le fonctionnement.
|
||||||
|
- **Boucle de correction automatique (Loop 1) :** Si les tests échouent, l'Agent 3 renvoie les erreurs à l'Agent 2 avec les logs. L'Agent 2 corrige et renvoie à l'Agent 3. Cette boucle tourne au maximum X fois jusqu'à ce que le code soit "vert".
|
||||||
|
|
||||||
|
Outils :
|
||||||
|
- Docker
|
||||||
|
- Ruff -> qualité de code
|
||||||
|
- Bandit -> sécurité
|
||||||
|
- Semgrep (optionnel) -> règles de sécurité et qualité plus larges
|
||||||
|
Boucle correction :
|
||||||
|
- LangGraph
|
||||||
|
- Pydantic
|
||||||
|
|
||||||
|
Tâches :
|
||||||
|
- QA et sandbox
|
||||||
|
- Intégration Ruff/Bandit/Semgrep
|
||||||
|
- Boucle automatique Dev-QA
|
||||||
|
___
|
||||||
|
|
||||||
|
### Étape 4 : Livraison & Feedback Utilisateur (Boucle Humaine)
|
||||||
|
- Une fois le code validé par l'Agent 3, il est présenté à l'utilisateur.
|
||||||
|
- L'utilisateur teste et valide.
|
||||||
|
- *Si Validé :* Le projet est sauvegardé dans la base de données (pour la recherche de l'Étape 1) et livré (ex: zip ou dépôt GitHub).
|
||||||
|
- *Si Refusé :* L'utilisateur indique ce qui ne va pas. Tout le contexte (code actuel + retours) est renvoyé à l'**Étape 1** pour réanalyse, et le cycle recommence.
|
||||||
|
|
||||||
|
Outils :
|
||||||
|
- Chainlit
|
||||||
|
- Git (optionnel)
|
||||||
|
|
||||||
|
Tâches :
|
||||||
|
- Interface Chainlit
|
||||||
|
- Livraison
|
||||||
|
___
|
||||||
|
|
||||||
|
### Étape finale :
|
||||||
|
|
||||||
|
Tâches :
|
||||||
|
- Tests fonctionnels de bout en bout
|
||||||
|
- Sécurité minimale
|
||||||
|
- Documentation finale
|
||||||
|
- Présentation
|
||||||
288
ressources/test.md
Normal file
288
ressources/test.md
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
|
||||||
|
# Projet ARC
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Diagramme Gantt du projet ARC
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker on
|
||||||
|
|
||||||
|
Etape 0 - Initialisation :a1, 2026-06-08, 2026-06-11
|
||||||
|
Etape 1 - Analyse du Besoin & Qualification :a2, 2026-06-11, 2026-06-27
|
||||||
|
Etape 2 - Génération de Code :a2, 2026-06-29, 2026-07-15
|
||||||
|
Etape 3 - Tests et Assurance Qualité :a2, 2026-07-15, 2026-08-01
|
||||||
|
Etape 4 - Livraison & Feedback Utilisateur :a2, 2026-08-01, 2026-08-13
|
||||||
|
Etape finale :a2, 2026-08-13, 2026-08-15
|
||||||
|
|
||||||
|
```
|
||||||
|
---
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section 0 - Préparation de l'environnement du projet
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
section Initialisation
|
||||||
|
Créer structure projet :a1, 2026-06-09, 0.5d
|
||||||
|
Backend + structure :a2, 2026-06-09, 0.5d
|
||||||
|
|
||||||
|
section LangGraph
|
||||||
|
Installer LangGraph :b1, 2026-06-09, 0.5d
|
||||||
|
Créer structure workflow :b2, 2026-06-09, 1d
|
||||||
|
Définir state global :b3, 2026-06-09, 1d
|
||||||
|
|
||||||
|
section Setup Qdrant
|
||||||
|
Installer Qdrant :c1, after b3, 0.5d
|
||||||
|
Tester connexion Python :c2, after b3, 0.5d
|
||||||
|
|
||||||
|
section Setup modèle d’embedding
|
||||||
|
Installer Snowflake :d1, after b3, 0.5d
|
||||||
|
Implémenter embedding() :d2, after b3, 0.5d
|
||||||
|
|
||||||
|
section Setup LLM
|
||||||
|
Installer llama.cpp :e1, after b3, 0.5d
|
||||||
|
Tester appel modèle :e2, after b3, 0.5d
|
||||||
|
|
||||||
|
section Setup Chainlit
|
||||||
|
Installer Chainlit :f1, after e2, 0.5d
|
||||||
|
Lancer app test :f2, after e2, 0.5d
|
||||||
|
|
||||||
|
section Organisation du code
|
||||||
|
Créer dossiers agents :g1, after e2, 0.5d
|
||||||
|
Créer dossiers services :g2, after e2, 0.5d
|
||||||
|
|
||||||
|
section Logging et debug
|
||||||
|
Logs simples :h1, after e2, 0.5d
|
||||||
|
Structuration logs :h2, after e2, 0.5d
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section 1 - Analyse du Besoin & Qualification
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
section Interface utilisateur
|
||||||
|
Créer interface Chainlit :a1, 2026-06-12, 1d
|
||||||
|
Connecter Chainlit → backend Python :a2, 2026-06-12, 0.5d
|
||||||
|
|
||||||
|
section Modèle cahier des charges
|
||||||
|
Définir structure JSON :b1, after a2, 0.5d
|
||||||
|
Schéma Pydantic :b2, after a2, 1d
|
||||||
|
|
||||||
|
section Agent PM
|
||||||
|
Prompt Agent PM :c1, after b2, 0.5d
|
||||||
|
Sortie structurée Pydantic :c2, after b2, 0.5d
|
||||||
|
Gestion erreurs :c3, after c2, 0.5d
|
||||||
|
|
||||||
|
section Questions clarificatrices
|
||||||
|
Détection champs manquants :d1, after c2, 0.5d
|
||||||
|
Génération questions LLM :d2, after d1, 0.5d
|
||||||
|
Boucle interaction Chainlit :d3, after d1, 1d
|
||||||
|
|
||||||
|
section Validation du cahier des charges
|
||||||
|
Affichage CDC :e1, after d3, 0.5d
|
||||||
|
Boutons validation/refus :e2, after d3, 0.5d
|
||||||
|
|
||||||
|
section Qdrant (BDD vectorielle)
|
||||||
|
Installer Qdrant :f1, after e2, 0.5d
|
||||||
|
Créer collection :f2, after e2, 0.5d
|
||||||
|
Structure payload :f3, after f2, 0.5d
|
||||||
|
|
||||||
|
section Embedding
|
||||||
|
Intégrer Snowflake Arctic :g1, after f2, 0.5d
|
||||||
|
Fonction embedding :g2, after f2, 0.5d
|
||||||
|
|
||||||
|
section Recherche d'existant
|
||||||
|
Recherche projets similaires :h1, after g2, 0.5d
|
||||||
|
Filtres payload :h2, after g2, 0.5d
|
||||||
|
Formatage résultats :h3, after h2, 0.5d
|
||||||
|
|
||||||
|
section Proposition utilisateur
|
||||||
|
Affichage résultats Chainlit :i1, after h2, 0.5d
|
||||||
|
Bouton "utiliser projet" :i2, after h2, 0.5d
|
||||||
|
Bouton "continuer génération" :i3, after h2, 0.5d
|
||||||
|
|
||||||
|
section Redis (optionnel)
|
||||||
|
Cache recherche :j1, after i3, 1d
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section 2 - Génération de Code
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
section Préparation des entrées
|
||||||
|
Récupérer cahier des charges :a1, 2026-06-29, 0.5d
|
||||||
|
Normaliser / valider données :a2, 2026-06-29, 0.5d
|
||||||
|
|
||||||
|
section Format de sortie du code
|
||||||
|
Structure projet JSON/Pydantic :b1, after a2, 0.5d
|
||||||
|
Modèle Pydantic sortie code :b2, after a2, 1d
|
||||||
|
|
||||||
|
section Prompt Agent Dev
|
||||||
|
Prompt génération code :c1, after b2, 1d
|
||||||
|
Contraintes strictes :c2, after b2, 0.5d
|
||||||
|
|
||||||
|
section Intégration LLM
|
||||||
|
Setup llama.cpp :d1, after c2, 0.5d
|
||||||
|
Connexion LangGraph → LLM :d2, after c2, 0.5d
|
||||||
|
|
||||||
|
section Arborescence projet
|
||||||
|
Génération structure fichiers :e1, after d2, 0.5d
|
||||||
|
Vérification structure :e2, after d2, 0.5d
|
||||||
|
|
||||||
|
section Génération code source
|
||||||
|
Génération fichiers Python :f1, after e2, 2d
|
||||||
|
Conformité structure :f2, after f1, 1d
|
||||||
|
|
||||||
|
section Fichiers complémentaires
|
||||||
|
README.md :g1, after f2, 0.5d
|
||||||
|
requirements.txt :g2, after f2, 0.5d
|
||||||
|
Instructions exécution :g3, after f2, 0.5d
|
||||||
|
|
||||||
|
section Validation backend
|
||||||
|
Vérification fichiers :h1, after g3, 0.5d
|
||||||
|
Nettoyage output LLM :h2, after g3, 0.5d
|
||||||
|
|
||||||
|
section Intégration LangGraph
|
||||||
|
Ajouter noeud Agent Dev :i1, after h2, 0.5d
|
||||||
|
Connecter PM → Dev :i2, after h2, 0.5d
|
||||||
|
|
||||||
|
section Préparation QA
|
||||||
|
Formatter code sandbox :j1, after i2, 0.5d
|
||||||
|
Transmettre au state :j2, after i2, 0.5d
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section 3 - Tests et Assurance Qualité
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
section Préparation entrées
|
||||||
|
Récupérer projet Agent Dev :a1, 2026-07-15, 0.5d
|
||||||
|
|
||||||
|
section Sandbox Docker
|
||||||
|
Dockerfile générique :b1, 2026-07-15, 1d
|
||||||
|
Script build/run :b2, after b1, 1d
|
||||||
|
Isolation environnement :b3, after b1, 0.5d
|
||||||
|
|
||||||
|
section Exécution sandbox
|
||||||
|
Lancer exécution :c1, after b3, 0.5d
|
||||||
|
Capturer logs :c2, after b3, 0.5d
|
||||||
|
|
||||||
|
section Ruff (qualité)
|
||||||
|
Installer Ruff :d1, after c2, 0.5d
|
||||||
|
Ruff check :d2, after c2, 0.5d
|
||||||
|
|
||||||
|
section Bandit (sécurité)
|
||||||
|
Installer Bandit :e1, after c2, 0.5d
|
||||||
|
Scan projet :e2, after c2, 0.5d
|
||||||
|
|
||||||
|
section Semgrep (optionnel)
|
||||||
|
Installer Semgrep :f1, after c2, 0.5d
|
||||||
|
Analyse règles :f2, after c2, 0.5d
|
||||||
|
|
||||||
|
section Structuration QA
|
||||||
|
Modèle Pydantic rapport :g1, after f2, 1d
|
||||||
|
Parser résultats outils :g2, after g1, 0.5d
|
||||||
|
|
||||||
|
section Agent QA
|
||||||
|
Prompt Agent QA :h1, after g2, 1d
|
||||||
|
Résumé intelligible :h2, after h1, 0.5d
|
||||||
|
Traduction erreurs → dev :h3, after h2, 1d
|
||||||
|
|
||||||
|
section Boucle Dev ↔ QA
|
||||||
|
Implémenter boucle LangGraph :i1, after h3, 1.5d
|
||||||
|
Condition stop :i2, after h3, 0.5d
|
||||||
|
Limite itérations :i3, after h3, 1d
|
||||||
|
|
||||||
|
section Logs pour correction
|
||||||
|
Structurer logs :j1, after i3, 0.5d
|
||||||
|
Injecter logs dans Agent Dev :j2, after i3, 0.5d
|
||||||
|
|
||||||
|
section Intégration LangGraph
|
||||||
|
Ajouter noeud QA :k1, after j2, 0.5d
|
||||||
|
Connecter Dev → QA → loop :k2, after j2, 0.5d
|
||||||
|
|
||||||
|
section Sécurisation minimale
|
||||||
|
Limiter temps exécution :l1, after k2, 0.5d
|
||||||
|
Bloquer accès disque/réseau :l2, after k2, 0.5d
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section 4 - Livraison & Feedback Utilisateur
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
section Préparation affichage
|
||||||
|
Récupérer code validé :a1, 2026-08-03, 0.5d
|
||||||
|
Récupérer rapport QA :a2, 2026-08-03, 0.5d
|
||||||
|
|
||||||
|
section Affichage Chainlit
|
||||||
|
Afficher code :b1, after a2, 0.5d
|
||||||
|
Afficher rapport QA :b2, after a2, 0.5d
|
||||||
|
Afficher instructions exécution :b3, after a2, 0.5d
|
||||||
|
|
||||||
|
section Actions utilisateur
|
||||||
|
Bouton valider :c1, after a2, 0.5d
|
||||||
|
Bouton refuser :c2, after a2, 0.5d
|
||||||
|
|
||||||
|
section Gestion refus
|
||||||
|
Champ feedback :d1, after c2, 0.5d
|
||||||
|
Structuration Pydantic :d2, after c2, 0.5d
|
||||||
|
Injection LangGraph :d3, after c2, 0.5d
|
||||||
|
|
||||||
|
section Boucle retour PM
|
||||||
|
Transition QA → PM :e1, after d3, 0.5d
|
||||||
|
Conserver contexte + feedback :e2, after d3, 0.5d
|
||||||
|
|
||||||
|
section Génération ZIP
|
||||||
|
Créer archive :f1, after e2, 0.5d
|
||||||
|
Vérifier structure :f2, after e2, 0.5d
|
||||||
|
|
||||||
|
section Téléchargement
|
||||||
|
Bouton téléchargement ZIP :g1, after e2, 0.5d
|
||||||
|
|
||||||
|
section Sauvegarde projet
|
||||||
|
Sauvegarder code + metadata :h1, after g1, 0.5d
|
||||||
|
Flag validated :h2, after g1, 0.5d
|
||||||
|
|
||||||
|
section Réindexation Qdrant
|
||||||
|
Générer embedding :i1, after h2, 0.5d
|
||||||
|
Ajouter dans Qdrant :i2, after h2, 0.5d
|
||||||
|
|
||||||
|
section Git (optionnel)
|
||||||
|
Init dépôt :j1, after i2, 0.5d
|
||||||
|
|
||||||
|
section Intégration finale LangGraph
|
||||||
|
Ajouter noeud Delivery :k1, after i2, 0.5d
|
||||||
|
Connecter QA → Delivery → fin :k2, after i2, 0.5d
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Section finale - Rendu
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
excludes weekends
|
||||||
|
todayMarker off
|
||||||
|
|
||||||
|
Tests fonctionnels de bout en bout :a1, 2026-08-13, 0.5d
|
||||||
|
Documentation finale :a2, 2026-08-13, 0.5d
|
||||||
|
Présentation :a3, after a2, 1d
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user