pakito312 commited on
Commit
850166a
·
1 Parent(s): afb5ae8
Files changed (2) hide show
  1. Dockerfile +7 -23
  2. api.py +14 -253
Dockerfile CHANGED
@@ -1,34 +1,18 @@
1
  FROM python:3.10-slim
2
 
3
- # Installer les dépendances système
4
- RUN apt-get update && apt-get install -y \
5
- curl \
6
- ca-certificates \
7
- zstd \
8
- && apt-get clean \
9
- && rm -rf /var/lib/apt/lists/*
10
 
11
  # Installer Ollama
12
  RUN curl -fsSL https://ollama.ai/install.sh | sh
13
 
14
- # Créer un utilisateur non-root
15
- RUN useradd -m -u 1000 user
16
- USER user
17
- WORKDIR /home/user
18
 
19
- # Copier l'application
20
- COPY --chown=user:user api.py .
21
- COPY --chown=user:user requirements.txt .
22
-
23
- # Installer TOUTES les dépendances Python
24
- RUN pip install --no-cache-dir --user \
25
- fastapi==0.104.1 \
26
- uvicorn[standard]==0.24.0 \
27
- pydantic==2.5.0 \
28
- aiohttp==3.9.1 \
29
- requests==2.31.0
30
 
31
  EXPOSE 7860
32
 
33
  # Démarrer
34
- CMD ["sh", "-c", "ollama serve & sleep 30 && ollama pull deepseek-coder:1.3b && uvicorn api:app --host 0.0.0.0 --port 7860"]
 
1
  FROM python:3.10-slim
2
 
3
+ # Installer curl et zstd pour Ollama
4
+ RUN apt-get update && apt-get install -y curl zstd && rm -rf /var/lib/apt/lists/*
 
 
 
 
 
5
 
6
  # Installer Ollama
7
  RUN curl -fsSL https://ollama.ai/install.sh | sh
8
 
9
+ # Copier l'API
10
+ COPY api.py .
 
 
11
 
12
+ # Installer seulement FastAPI et requests
13
+ RUN pip install fastapi uvicorn requests
 
 
 
 
 
 
 
 
 
14
 
15
  EXPOSE 7860
16
 
17
  # Démarrer
18
+ CMD ["sh", "-c", "ollama serve & sleep 40 && ollama pull deepseek-coder:1.3b && uvicorn api:app --host 0.0.0.0 --port 7860"]
api.py CHANGED
@@ -1,262 +1,23 @@
1
- """
2
- API Ollama + DeepSeek-Coder 1.3B - Version simplifiée
3
- """
4
- import asyncio
5
- import json
6
  import time
7
- import aiohttp
8
- from typing import Optional, List, Dict, Any
9
- from contextlib import asynccontextmanager
10
 
11
- from fastapi import FastAPI, HTTPException
12
- from fastapi.middleware.cors import CORSMiddleware
13
- from fastapi.responses import JSONResponse, StreamingResponse
14
- from pydantic import BaseModel
15
 
16
- # ========== MODÈLES ==========
17
- class GenerateRequest(BaseModel):
18
- prompt: str
19
- model: str = "deepseek-coder:1.3b"
20
- stream: bool = False
21
- temperature: float = 0.2
22
- max_tokens: int = 512
23
-
24
- class ChatMessage(BaseModel):
25
- role: str
26
- content: str
27
-
28
- class ChatRequest(BaseModel):
29
- messages: List[ChatMessage]
30
- model: str = "deepseek-coder:1.3b"
31
- stream: bool = False
32
- temperature: float = 0.2
33
- max_tokens: int = 512
34
-
35
- # ========== CLIENT OLLAMA ==========
36
- class OllamaClient:
37
- def __init__(self):
38
- self.base_url = "http://localhost:11434"
39
- self.session = None
40
-
41
- async def get_session(self):
42
- if not self.session or self.session.closed:
43
- self.session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=300))
44
- return self.session
45
-
46
- async def close(self):
47
- if self.session and not self.session.closed:
48
- await self.session.close()
49
-
50
- async def health_check(self):
51
- """Vérifier si Ollama est en ligne"""
52
- try:
53
- session = await self.get_session()
54
- async with session.get(f"{self.base_url}/api/tags", timeout=10) as resp:
55
- return resp.status == 200
56
- except:
57
- return False
58
-
59
- async def generate(self, request: Dict) -> Dict:
60
- """Génération simple"""
61
- try:
62
- session = await self.get_session()
63
- async with session.post(
64
- f"{self.base_url}/api/generate",
65
- json=request,
66
- timeout=120
67
- ) as response:
68
- if response.status == 200:
69
- return await response.json()
70
- else:
71
- return {"error": f"HTTP {response.status}: {await response.text()}"}
72
- except Exception as e:
73
- return {"error": str(e)}
74
-
75
- async def generate_stream(self, request: Dict):
76
- """Génération en streaming"""
77
- async def stream_generator():
78
- try:
79
- session = await self.get_session()
80
- async with session.post(
81
- f"{self.base_url}/api/generate",
82
- json=request,
83
- timeout=300
84
- ) as response:
85
- async for line in response.content:
86
- if line:
87
- yield f"data: {line.decode()}\n\n"
88
- except Exception as e:
89
- yield f"data: {json.dumps({'error': str(e)})}\n\n"
90
-
91
- return StreamingResponse(
92
- stream_generator(),
93
- media_type="text/event-stream"
94
- )
95
-
96
- async def chat(self, request: Dict) -> Dict:
97
- """Chat"""
98
- try:
99
- session = await self.get_session()
100
- async with session.post(
101
- f"{self.base_url}/api/chat",
102
- json=request,
103
- timeout=120
104
- ) as response:
105
- if response.status == 200:
106
- return await response.json()
107
- else:
108
- return {"error": f"HTTP {response.status}: {await response.text()}"}
109
- except Exception as e:
110
- return {"error": str(e)}
111
-
112
- async def list_models(self) -> List[str]:
113
- """Lister les modèles"""
114
- try:
115
- session = await self.get_session()
116
- async with session.get(f"{self.base_url}/api/tags") as response:
117
- if response.status == 200:
118
- data = await response.json()
119
- return [m["name"] for m in data.get("models", [])]
120
- return []
121
- except:
122
- return []
123
-
124
- # ========== APPLICATION ==========
125
- @asynccontextmanager
126
- async def lifespan(app: FastAPI):
127
- # Démarrage
128
- app.state.client = OllamaClient()
129
- app.state.start_time = time.time()
130
-
131
- print("⏳ Attente d'Ollama...")
132
- for i in range(30):
133
- if await app.state.client.health_check():
134
- print("✅ Ollama prêt!")
135
- break
136
- print(f" Tentative {i+1}/30")
137
- await asyncio.sleep(2)
138
-
139
- yield
140
-
141
- # Nettoyage
142
- await app.state.client.close()
143
-
144
- # Créer l'app
145
- app = FastAPI(
146
- title="DeepSeek-Coder API",
147
- description="API pour DeepSeek-Coder 1.3B via Ollama",
148
- version="1.0.0",
149
- lifespan=lifespan
150
- )
151
-
152
- # CORS
153
- app.add_middleware(
154
- CORSMiddleware,
155
- allow_origins=["*"],
156
- allow_methods=["*"],
157
- allow_headers=["*"],
158
- )
159
-
160
- # ========== ROUTES ==========
161
  @app.get("/")
162
- async def root():
163
- return {
164
- "service": "DeepSeek-Coder 1.3B API",
165
- "endpoints": {
166
- "GET /": "Cette page",
167
- "GET /health": "Vérifier la santé",
168
- "GET /models": "Liste des modèles",
169
- "POST /generate": "Générer du texte",
170
- "POST /chat": "Chat conversationnel",
171
- "GET /docs": "Documentation Swagger"
172
- },
173
- "model": "deepseek-coder:1.3b",
174
- "note": "Le modèle peut prendre 2-3 minutes pour démarrer"
175
- }
176
-
177
- @app.get("/health")
178
- async def health():
179
- ollama_ready = await app.state.client.health_check()
180
- models = await app.state.client.list_models()
181
-
182
- return {
183
- "status": "healthy" if ollama_ready else "starting",
184
- "ollama_ready": ollama_ready,
185
- "models": models,
186
- "uptime": time.time() - app.state.start_time
187
- }
188
-
189
- @app.get("/models")
190
- async def get_models():
191
- models = await app.state.client.list_models()
192
- return {"models": models}
193
 
194
  @app.post("/generate")
195
- async def generate(request: GenerateRequest):
196
- """Générer du texte/code"""
197
-
198
- payload = {
199
- "model": request.model,
200
- "prompt": request.prompt,
201
- "stream": request.stream,
202
- "options": {
203
- "temperature": request.temperature,
204
- "num_predict": request.max_tokens
205
- }
206
- }
207
-
208
- if request.stream:
209
- return await app.state.client.generate_stream(payload)
210
- else:
211
- result = await app.state.client.generate(payload)
212
- return JSONResponse(content=result)
213
-
214
- @app.post("/chat")
215
- async def chat(request: ChatRequest):
216
- """Chat conversationnel"""
217
-
218
- payload = {
219
- "model": request.model,
220
- "messages": [msg.dict() for msg in request.messages],
221
- "stream": request.stream,
222
- "options": {
223
- "temperature": request.temperature,
224
- "num_predict": request.max_tokens
225
- }
226
- }
227
-
228
- if request.stream:
229
- return await app.state.client.generate_stream(payload)
230
- else:
231
- result = await app.state.client.chat(payload)
232
- return JSONResponse(content=result)
233
-
234
- # Routes compatibles Ollama
235
- @app.get("/api/tags")
236
- async def ollama_tags():
237
- models = await app.state.client.list_models()
238
- return {"models": [{"name": m} for m in models]}
239
-
240
- @app.post("/api/generate")
241
- async def ollama_generate(request: dict):
242
- """Route compatible avec l'API Ollama"""
243
- if request.get("stream", False):
244
- return await app.state.client.generate_stream(request)
245
- else:
246
- result = await app.state.client.generate(request)
247
- return JSONResponse(content=result)
248
-
249
- @app.post("/api/chat")
250
- async def ollama_chat(request: dict):
251
- """Route compatible avec l'API Ollama"""
252
- if request.get("stream", False):
253
- return await app.state.client.generate_stream(request)
254
- else:
255
- result = await app.state.client.chat(request)
256
- return JSONResponse(content=result)
257
 
258
- # ========== DÉMARRAGE ==========
259
  if __name__ == "__main__":
260
  import uvicorn
261
- print("🚀 Démarrage de l'API DeepSeek-Coder...")
262
- uvicorn.run(app, host="0.0.0.0", port=7860, log_level="info")
 
1
+ from fastapi import FastAPI
2
+ import requests
 
 
 
3
  import time
 
 
 
4
 
5
+ app = FastAPI()
6
+ OLLAMA_URL = "http://localhost:11434"
 
 
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  @app.get("/")
9
+ def root():
10
+ return {"message": "DeepSeek-Coder API"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  @app.post("/generate")
13
+ def generate(prompt: str):
14
+ response = requests.post(f"{OLLAMA_URL}/api/generate", json={
15
+ "model": "deepseek-coder:1.3b",
16
+ "prompt": prompt,
17
+ "stream": False
18
+ })
19
+ return response.json()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
 
21
  if __name__ == "__main__":
22
  import uvicorn
23
+ uvicorn.run(app, host="0.0.0.0", port=7860)