dieumercimvemba commited on
Commit
cd72ff2
·
verified ·
1 Parent(s): 1ec48cb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +35 -27
app.py CHANGED
@@ -7,27 +7,27 @@ from llama_cpp import Llama
7
 
8
  app = FastAPI(
9
  title="Dimercia AI",
10
- version="0.1.3",
11
- description="API OpenAI compatible souveraine - Version Multi-Thread"
12
  )
13
 
14
  # --- Initialisation globale ---
15
  MODEL_PATH = "/app/models/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf"
16
 
17
- print("Chargement de Dimercia AI v0.1.3 en mémoire RAM...")
18
  llm = Llama(
19
  model_path=MODEL_PATH,
20
- n_ctx=16384, # 16k : le juste milieu parfait pour l'agilité sur CPU gratuit
21
- n_threads=4, # Exploite au mieux les ressources de calcul
22
  verbose=False
23
  )
24
- print("Dimercia AI v0.1.3 est prêt.")
25
 
26
  @app.get("/")
27
  def home():
28
  return {
29
  "name": "Dimercia AI",
30
- "version": "0.1.3",
31
  "status": "running"
32
  }
33
 
@@ -36,11 +36,7 @@ def models():
36
  return {
37
  "object": "list",
38
  "data": [
39
- {
40
- "id": "dimercia-coder",
41
- "object": "model",
42
- "owned_by": "dimercia"
43
- }
44
  ]
45
  }
46
 
@@ -51,7 +47,7 @@ async def chat(request: Request):
51
  except Exception:
52
  return JSONResponse(status_code=400, content={"detail": "JSON invalide"})
53
 
54
- # --- Nettoyage adaptatif du Payload (Anti-422 Cline/Cursor) ---
55
  raw_messages = body.get("messages", [])
56
  cleaned_messages = []
57
 
@@ -71,22 +67,35 @@ async def chat(request: Request):
71
  max_tokens = body.get("max_tokens", 512)
72
  stream = body.get("stream", False)
73
 
74
- # Paramètres de calcul convertis proprement
75
  temp_val = float(temperature) if temperature is not None else 0.2
76
  tokens_val = int(max_tokens) if max_tokens is not None else 512
77
 
78
- # --- Gestion du mode STREAMING (Non-bloquant) ---
79
  if stream:
80
- # On instancie l'itérateur synchrone dans un thread séparé
81
- iterator = await asyncio.to_thread(
82
- llm.create_chat_completion,
83
- messages=cleaned_messages,
84
- temperature=temp_val,
85
- max_tokens=tokens_val,
86
- stream=True
87
- )
88
-
89
  async def chunk_generator():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  def get_next_chunk(it):
91
  try:
92
  return next(it)
@@ -96,7 +105,6 @@ async def chat(request: Request):
96
  return ex
97
 
98
  while True:
99
- # On consomme l'itérateur token par token sans jamais bloquer l'Event Loop
100
  chunk = await asyncio.to_thread(get_next_chunk, iterator)
101
  if chunk is None:
102
  break
@@ -107,14 +115,14 @@ async def chat(request: Request):
107
  if "model" in chunk:
108
  chunk["model"] = "dimercia-coder"
109
  yield f"data: {json.dumps(chunk)}\n\n"
 
110
  yield "data: [DONE]\n\n"
111
 
112
  return StreamingResponse(chunk_generator(), media_type="text/event-stream")
113
 
114
- # --- Gestion du mode STANDARD (Non-bloquant) ---
115
  else:
116
  try:
117
- # Déportation du calcul lourd dans le pool de threads d'FastAPI
118
  response = await asyncio.to_thread(
119
  llm.create_chat_completion,
120
  messages=cleaned_messages,
 
7
 
8
  app = FastAPI(
9
  title="Dimercia AI",
10
+ version="0.1.4",
11
+ description="API OpenAI compatible souveraine - Anti-Timeout pour Cline"
12
  )
13
 
14
  # --- Initialisation globale ---
15
  MODEL_PATH = "/app/models/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf"
16
 
17
+ print("Chargement de Dimercia AI v0.1.4 en mémoire RAM...")
18
  llm = Llama(
19
  model_path=MODEL_PATH,
20
+ n_ctx=16384, # 16k conserve un parfait équilibre sur CPU
21
+ n_threads=4, # Exploite à fond le calcul parallèle
22
  verbose=False
23
  )
24
+ print("Dimercia AI v0.1.4 est prêt.")
25
 
26
  @app.get("/")
27
  def home():
28
  return {
29
  "name": "Dimercia AI",
30
+ "version": "0.1.4",
31
  "status": "running"
32
  }
33
 
 
36
  return {
37
  "object": "list",
38
  "data": [
39
+ {"id": "dimercia-coder", "object": "model", "owned_by": "dimercia"}
 
 
 
 
40
  ]
41
  }
42
 
 
47
  except Exception:
48
  return JSONResponse(status_code=400, content={"detail": "JSON invalide"})
49
 
50
+ # --- Nettoyage adaptatif du Payload ---
51
  raw_messages = body.get("messages", [])
52
  cleaned_messages = []
53
 
 
67
  max_tokens = body.get("max_tokens", 512)
68
  stream = body.get("stream", False)
69
 
 
70
  temp_val = float(temperature) if temperature is not None else 0.2
71
  tokens_val = int(max_tokens) if max_tokens is not None else 512
72
 
73
+ # --- Gestion du mode STREAMING (Avec système anti-timeout) ---
74
  if stream:
 
 
 
 
 
 
 
 
 
75
  async def chunk_generator():
76
+ # Étape 1 : Lancer la création de l'itérateur dans un thread séparé (non-bloquant)
77
+ task = asyncio.create_task(asyncio.to_thread(
78
+ llm.create_chat_completion,
79
+ messages=cleaned_messages,
80
+ temperature=temp_val,
81
+ max_tokens=tokens_val,
82
+ stream=True
83
+ ))
84
+
85
+ # Étape 2 : Tant que llama.cpp calcule le prefill, on envoie des pings invisibles à Cline
86
+ while not task.done():
87
+ # Envoi d'un chunk de commentaire SSE pour garder la connexion ouverte
88
+ yield ": heartbeat\n\n"
89
+ await asyncio.sleep(1.0) # Attendre 1 seconde avant le prochain ping
90
+
91
+ try:
92
+ iterator = task.result()
93
+ except Exception as e:
94
+ yield f"data: {json.dumps({'error': str(e)})}\n\n"
95
+ yield "data: [DONE]\n\n"
96
+ return
97
+
98
+ # Étape 3 : Consommer les tokens normalement dès qu'ils sont prêts
99
  def get_next_chunk(it):
100
  try:
101
  return next(it)
 
105
  return ex
106
 
107
  while True:
 
108
  chunk = await asyncio.to_thread(get_next_chunk, iterator)
109
  if chunk is None:
110
  break
 
115
  if "model" in chunk:
116
  chunk["model"] = "dimercia-coder"
117
  yield f"data: {json.dumps(chunk)}\n\n"
118
+
119
  yield "data: [DONE]\n\n"
120
 
121
  return StreamingResponse(chunk_generator(), media_type="text/event-stream")
122
 
123
+ # --- Gestion du mode STANDARD ---
124
  else:
125
  try:
 
126
  response = await asyncio.to_thread(
127
  llm.create_chat_completion,
128
  messages=cleaned_messages,