EnzGamers commited on
Commit
e879476
·
verified ·
1 Parent(s): 49ce3ab

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -39
app.py CHANGED
@@ -1,53 +1,135 @@
1
  from fastapi import FastAPI, Request
2
- from fastapi.responses import JSONResponse
 
 
 
 
 
3
  import json
 
4
 
5
- # --- Création de l'application API de diagnostic ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  app = FastAPI()
7
 
8
- # --- Définition des API de diagnostic ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
- @app.get("/models")
 
 
11
  async def list_models():
12
- """Répond à la requête GET /models pour que l'extension soit satisfaite."""
13
- print("--- REQUETE RECUE SUR /models ---")
14
- return {"object": "list", "data": [{"id": "deepseek-ai/deepseek-coder-1.3b-instruct", "object": "model"}]}
15
 
16
  @app.post("/chat/completions")
17
- async def debug_chat_completion(request: Request):
18
- """
19
- Endpoint qui ne fait qu'une seule chose :
20
- afficher le contenu exact de la requête envoyée par VS Code.
21
- """
22
- print("\n\n" + "="*50)
23
- print("=== REQUETE POST RECUE SUR /chat/completions ===")
24
-
25
- # On affiche les headers de la requête
26
- print("\n--- HEADERS ---")
27
- for name, value in request.headers.items():
28
- print(f"{name}: {value}")
29
 
30
- # On essaie de lire et d'afficher le corps (body) de la requête
31
- try:
32
- body = await request.json()
33
- print("\n--- BODY (JSON) ---")
34
- # On utilise json.dumps pour un affichage propre
35
- print(json.dumps(body, indent=2))
36
- except Exception as e:
37
- print(f"\n--- ERREUR LORS DE LA LECTURE DU BODY ---")
38
- print(f"L'erreur est : {e}")
39
- # On essaie de lire le corps comme du texte brut si le JSON échoue
40
- body_raw = await request.body()
41
- print("\n--- BODY (BRUT) ---")
42
- print(body_raw.decode('utf-8', errors='ignore'))
43
-
44
- print("="*50 + "\n\n")
 
45
 
46
- # On renvoie une erreur 422 volontairement pour pouvoir retenter facilement.
47
- # Le but n'est pas que ça marche, mais de voir les logs.
48
- error_message = {"error": "Mode diagnostic actif. Vérifiez les logs du Space."}
49
- return JSONResponse(status_code=422, content=error_message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
  @app.get("/")
52
  def root():
53
- return {"status": "API en mode diagnostic. Vérifiez les logs."}
 
 
 
 
1
  from fastapi import FastAPI, Request
2
+ from fastapi.responses import StreamingResponse
3
+ from pydantic import BaseModel, Extra
4
+ import torch
5
+ from transformers import AutoModelForCausalLM, AutoTokenizer
6
+ import time
7
+ import uuid
8
  import json
9
+ from typing import Optional, List, Union, Dict, Any
10
 
11
+ # --- Configuration ---
12
+ MODEL_ID = "deepseek-ai/deepseek-coder-1.3b-instruct"
13
+ DEVICE = "cpu"
14
+
15
+ # --- Chargement du modèle ---
16
+ print(f"Début du chargement du modèle : {MODEL_ID}")
17
+ model = AutoModelForCausalLM.from_pretrained(
18
+ MODEL_ID,
19
+ torch_dtype=torch.bfloat16,
20
+ device_map=DEVICE
21
+ )
22
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
23
+ print("Modèle et tokenizer chargés avec succès sur le CPU.")
24
+
25
+ # --- Création de l'application API ---
26
  app = FastAPI()
27
 
28
+ # --- Modèles de données pour accepter la structure complexe de l'extension ---
29
+ class ContentPart(BaseModel):
30
+ type: str
31
+ text: str
32
+
33
+ class ChatMessage(BaseModel):
34
+ role: str
35
+ content: Union[str, List[ContentPart]]
36
+
37
+ class ChatCompletionRequest(BaseModel):
38
+ model: Optional[str] = None
39
+ messages: List[ChatMessage]
40
+ stream: Optional[bool] = False
41
+
42
+ class Config:
43
+ extra = Extra.ignore
44
+
45
+ class ModelData(BaseModel):
46
+ id: str
47
+ object: str = "model"
48
+ owned_by: str = "user"
49
+
50
+ class ModelList(BaseModel):
51
+ object: str = "list"
52
+ data: List[ModelData]
53
 
54
+ # --- Définition des API ---
55
+
56
+ @app.get("/models", response_model=ModelList)
57
  async def list_models():
58
+ """Répond à la requête GET /models pour satisfaire l'extension."""
59
+ return ModelList(data=[ModelData(id=MODEL_ID)])
 
60
 
61
  @app.post("/chat/completions")
62
+ async def create_chat_completion(request: ChatCompletionRequest):
63
+ """Endpoint principal qui gère la génération de texte en streaming."""
 
 
 
 
 
 
 
 
 
 
64
 
65
+ # On extrait le prompt de l'utilisateur de la structure complexe
66
+ user_prompt = ""
67
+ last_message = request.messages[-1]
68
+ if isinstance(last_message.content, list):
69
+ for part in last_message.content:
70
+ if part.type == 'text':
71
+ user_prompt += part.text + "\n"
72
+ elif isinstance(last_message.content, str):
73
+ user_prompt = last_message.content
74
+
75
+ if not user_prompt:
76
+ return {"error": "Prompt non trouvé."}
77
+
78
+ # Préparation pour le modèle DeepSeek
79
+ messages_for_model = [{'role': 'user', 'content': user_prompt}]
80
+ inputs = tokenizer.apply_chat_template(messages_for_model, add_generation_prompt=True, return_tensors="pt").to(DEVICE)
81
 
82
+ # Génération de la réponse complète
83
+ outputs = model.generate(inputs, max_new_tokens=250, do_sample=True, temperature=0.2, top_k=50, top_p=0.95, num_return_sequences=1, eos_token_id=tokenizer.eos_token_id)
84
+ response_text = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=True)
85
+
86
+ # Fonction génératrice pour le streaming
87
+ async def stream_generator():
88
+ response_id = f"chatcmpl-{uuid.uuid4()}"
89
+
90
+ # On envoie la réponse caractère par caractère, au format attendu
91
+ for char in response_text:
92
+ chunk = {
93
+ "id": response_id,
94
+ "object": "chat.completion.chunk",
95
+ "created": int(time.time()),
96
+ "model": MODEL_ID,
97
+ "choices": [{
98
+ "index": 0,
99
+ "delta": {"content": char},
100
+ "finish_reason": None
101
+ }]
102
+ }
103
+ yield f"data: {json.dumps(chunk)}\n\n"
104
+ await asyncio.sleep(0.01) # Petite pause pour simuler un flux
105
+
106
+ # On envoie le chunk final de fin
107
+ final_chunk = {
108
+ "id": response_id,
109
+ "object": "chat.completion.chunk",
110
+ "created": int(time.time()),
111
+ "model": MODEL_ID,
112
+ "choices": [{
113
+ "index": 0,
114
+ "delta": {},
115
+ "finish_reason": "stop"
116
+ }]
117
+ }
118
+ yield f"data: {json.dumps(final_chunk)}\n\n"
119
+
120
+ # On envoie le signal [DONE]
121
+ yield "data: [DONE]\n\n"
122
+
123
+ # Si l'extension demande un stream, on renvoie le générateur
124
+ if request.stream:
125
+ return StreamingResponse(stream_generator(), media_type="text/event-stream")
126
+ else:
127
+ # Code de secours si le stream n'est pas demandé (peu probable)
128
+ return {"choices": [{"message": {"role": "assistant", "content": response_text}}]}
129
 
130
  @app.get("/")
131
  def root():
132
+ return {"status": "API compatible OpenAI en ligne (avec streaming)", "model_id": MODEL_ID}
133
+
134
+ # On a besoin de asyncio pour la pause dans le stream
135
+ import asyncio