Spaces:
Runtime error
Runtime error
File size: 4,708 Bytes
1b52c48 cf8503a 1ec48cb cf8503a 1b52c48 2624719 cd72ff2 2624719 cf8503a 1b52c48 2624719 cd72ff2 1b52c48 cd72ff2 1ec48cb 2624719 cd72ff2 2624719 1b52c48 cd72ff2 1b52c48 2624719 1b52c48 cd72ff2 2624719 cf8503a cd72ff2 cf8503a 1ec48cb cf8503a 1ec48cb cd72ff2 1ec48cb cf8503a cd72ff2 1ec48cb cd72ff2 1ec48cb cf8503a 1ec48cb cf8503a cd72ff2 1ec48cb 2624719 1b52c48 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | import uvicorn
import json
import asyncio
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, StreamingResponse
from llama_cpp import Llama
app = FastAPI(
title="Dimercia AI",
version="0.1.4",
description="API OpenAI compatible souveraine - Anti-Timeout pour Cline"
)
# --- Initialisation globale ---
MODEL_PATH = "/app/models/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf"
print("Chargement de Dimercia AI v0.1.4 en mémoire RAM...")
llm = Llama(
model_path=MODEL_PATH,
n_ctx=16384, # 16k conserve un parfait équilibre sur CPU
n_threads=4, # Exploite à fond le calcul parallèle
verbose=False
)
print("Dimercia AI v0.1.4 est prêt.")
@app.get("/")
def home():
return {
"name": "Dimercia AI",
"version": "0.1.4",
"status": "running"
}
@app.get("/v1/models")
def models():
return {
"object": "list",
"data": [
{"id": "dimercia-coder", "object": "model", "owned_by": "dimercia"}
]
}
@app.post("/v1/chat/completions")
async def chat(request: Request):
try:
body = await request.json()
except Exception:
return JSONResponse(status_code=400, content={"detail": "JSON invalide"})
# --- Nettoyage adaptatif du Payload ---
raw_messages = body.get("messages", [])
cleaned_messages = []
for msg in raw_messages:
role = msg.get("role", "user")
raw_content = msg.get("content", "")
if isinstance(raw_content, list):
text_pieces = [piece.get("text", "") if isinstance(piece, dict) else str(piece) for piece in raw_content]
content = "".join(text_pieces)
else:
content = str(raw_content)
cleaned_messages.append({"role": role, "content": content})
temperature = body.get("temperature", 0.2)
max_tokens = body.get("max_tokens", 512)
stream = body.get("stream", False)
temp_val = float(temperature) if temperature is not None else 0.2
tokens_val = int(max_tokens) if max_tokens is not None else 512
# --- Gestion du mode STREAMING (Avec système anti-timeout) ---
if stream:
async def chunk_generator():
# Étape 1 : Lancer la création de l'itérateur dans un thread séparé (non-bloquant)
task = asyncio.create_task(asyncio.to_thread(
llm.create_chat_completion,
messages=cleaned_messages,
temperature=temp_val,
max_tokens=tokens_val,
stream=True
))
# Étape 2 : Tant que llama.cpp calcule le prefill, on envoie des pings invisibles à Cline
while not task.done():
# Envoi d'un chunk de commentaire SSE pour garder la connexion ouverte
yield ": heartbeat\n\n"
await asyncio.sleep(1.0) # Attendre 1 seconde avant le prochain ping
try:
iterator = task.result()
except Exception as e:
yield f"data: {json.dumps({'error': str(e)})}\n\n"
yield "data: [DONE]\n\n"
return
# Étape 3 : Consommer les tokens normalement dès qu'ils sont prêts
def get_next_chunk(it):
try:
return next(it)
except StopIteration:
return None
except Exception as ex:
return ex
while True:
chunk = await asyncio.to_thread(get_next_chunk, iterator)
if chunk is None:
break
if isinstance(chunk, Exception):
yield f"data: {json.dumps({'error': str(chunk)})}\n\n"
break
if "model" in chunk:
chunk["model"] = "dimercia-coder"
yield f"data: {json.dumps(chunk)}\n\n"
yield "data: [DONE]\n\n"
return StreamingResponse(chunk_generator(), media_type="text/event-stream")
# --- Gestion du mode STANDARD ---
else:
try:
response = await asyncio.to_thread(
llm.create_chat_completion,
messages=cleaned_messages,
temperature=temp_val,
max_tokens=tokens_val,
stream=False
)
if isinstance(response, dict):
response["model"] = "dimercia-coder"
return response
except Exception as e:
return JSONResponse(status_code=500, content={"detail": str(e)})
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860) |