Spaces:
Runtime error
Runtime error
| 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.") | |
| def home(): | |
| return { | |
| "name": "Dimercia AI", | |
| "version": "0.1.4", | |
| "status": "running" | |
| } | |
| def models(): | |
| return { | |
| "object": "list", | |
| "data": [ | |
| {"id": "dimercia-coder", "object": "model", "owned_by": "dimercia"} | |
| ] | |
| } | |
| 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) |