mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-14 10:48:35 +02:00
- Scaffold full-stack : backend FastAPI + frontend React/Vite/TS/Tailwind - Design system fidèle au thème (palette ambre/café, JetBrains Mono, tokens Tailwind) - Layout 3 panneaux : activity bar, historique/fichiers, chat, aperçu - Connexion Ollama : statut live, liste des modèles, pull avec progression SSE, sélecteur de modèle - Vue Configuration (Frame 2) : modèles, génération, outils, invite système - Dockerfile multi-stage + docker-compose (Ollama externe par défaut, profil ollama optionnel) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVay7z3y7q2gEe54ByAE6N
78 lines
2.3 KiB
Python
78 lines
2.3 KiB
Python
"""Routes liées à Ollama : statut de connexion, modèles, téléchargement."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import httpx
|
|
from fastapi import APIRouter
|
|
from fastapi.responses import StreamingResponse
|
|
from pydantic import BaseModel
|
|
|
|
from ..config import settings
|
|
from ..ollama_client import ollama
|
|
|
|
router = APIRouter(prefix="/api", tags=["ollama"])
|
|
|
|
|
|
@router.get("/status")
|
|
async def status() -> dict:
|
|
"""État de la connexion Ollama (point vert/rouge de la barre supérieure)."""
|
|
try:
|
|
version = await ollama.ping()
|
|
return {
|
|
"connected": True,
|
|
"host": ollama.host,
|
|
"version": version.get("version"),
|
|
"default_model": settings.default_model,
|
|
}
|
|
except (httpx.HTTPError, OSError) as exc:
|
|
return {
|
|
"connected": False,
|
|
"host": ollama.host,
|
|
"error": str(exc),
|
|
"default_model": settings.default_model,
|
|
}
|
|
|
|
|
|
@router.get("/models")
|
|
async def list_models() -> dict:
|
|
"""Modèles installés localement, formatés pour le sélecteur de l'UI."""
|
|
try:
|
|
raw = await ollama.list_models()
|
|
except (httpx.HTTPError, OSError):
|
|
return {"models": []}
|
|
|
|
models = []
|
|
for m in raw:
|
|
details = m.get("details", {})
|
|
size_go = round(m.get("size", 0) / 1_000_000_000, 1)
|
|
models.append(
|
|
{
|
|
"name": m.get("name"),
|
|
"size_go": size_go,
|
|
"parameter_size": details.get("parameter_size"),
|
|
"quantization": details.get("quantization_level"),
|
|
"family": details.get("family"),
|
|
}
|
|
)
|
|
return {"models": models, "default": settings.default_model}
|
|
|
|
|
|
class PullRequest(BaseModel):
|
|
name: str
|
|
|
|
|
|
@router.post("/models/pull")
|
|
async def pull_model(req: PullRequest) -> StreamingResponse:
|
|
"""Télécharge un modèle en streamant la progression (SSE)."""
|
|
|
|
async def event_stream():
|
|
try:
|
|
async for chunk in ollama.pull_model(req.name):
|
|
yield f"data: {json.dumps(chunk)}\n\n"
|
|
except (httpx.HTTPError, OSError) as exc:
|
|
yield f"data: {json.dumps({'error': str(exc)})}\n\n"
|
|
yield "data: [DONE]\n\n"
|
|
|
|
return StreamingResponse(event_stream(), media_type="text/event-stream")
|