Spaces:
Running
Running
Replace Gradio SmolVLM app with DeepSeek-7B-base GGUF OpenAI-compatible API (Docker)
Browse files
main.py
CHANGED
|
@@ -166,6 +166,44 @@ def _require_model() -> Llama:
|
|
| 166 |
return _llm
|
| 167 |
|
| 168 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
# ββ Endpoints βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 170 |
@app.get("/health")
|
| 171 |
def health() -> Dict[str, Any]:
|
|
@@ -201,6 +239,15 @@ def chat_completions(req: ChatCompletionRequest) -> Dict[str, Any]:
|
|
| 201 |
if not req.messages:
|
| 202 |
raise HTTPException(status_code=400, detail="`messages` must not be empty.")
|
| 203 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
kwargs: Dict[str, Any] = dict(
|
| 205 |
messages=[m.model_dump() for m in req.messages],
|
| 206 |
temperature=req.temperature,
|
|
@@ -232,11 +279,20 @@ def generate(req: GenerateRequest) -> Dict[str, Any]:
|
|
| 232 |
t0 = time.time()
|
| 233 |
try:
|
| 234 |
with _llm_lock:
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
except Exception as exc:
|
| 241 |
raise HTTPException(status_code=500, detail=f"generation failed: {exc}") from exc
|
| 242 |
|
|
|
|
| 166 |
return _llm
|
| 167 |
|
| 168 |
|
| 169 |
+
def _messages_to_raw_prompt(messages: List["ChatMessage"]) -> str:
|
| 170 |
+
"""Flatten chat messages into a plain-text prompt for BASE (non-chat) models.
|
| 171 |
+
|
| 172 |
+
A base model was never trained on a chat template β wrapping its input in
|
| 173 |
+
one (e.g. llama-2 [INST] tags) makes it emit only <s>/</s> tokens. Instead
|
| 174 |
+
we join the message contents into plain text and let the model continue it.
|
| 175 |
+
"""
|
| 176 |
+
parts = [m.content.strip() for m in messages if m.content and m.content.strip()]
|
| 177 |
+
return "\n\n".join(parts)
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def _raw_chat_completion(llm: Llama, req: "ChatCompletionRequest") -> Dict[str, Any]:
|
| 181 |
+
"""Text-completion path used when CHAT_FORMAT is empty (base model)."""
|
| 182 |
+
prompt = _messages_to_raw_prompt(req.messages)
|
| 183 |
+
result = llm.create_completion(
|
| 184 |
+
prompt=prompt,
|
| 185 |
+
temperature=req.temperature,
|
| 186 |
+
top_p=req.top_p,
|
| 187 |
+
max_tokens=req.max_tokens if req.max_tokens is not None else DEFAULT_MAX_TOKENS,
|
| 188 |
+
stop=req.stop,
|
| 189 |
+
)
|
| 190 |
+
text = result["choices"][0]["text"]
|
| 191 |
+
# Re-shape the completion result into an OpenAI chat-completion response so
|
| 192 |
+
# clients (which always call /v1/chat/completions) need no changes.
|
| 193 |
+
return {
|
| 194 |
+
"id": f"chatcmpl-{uuid.uuid4().hex}",
|
| 195 |
+
"object": "chat.completion",
|
| 196 |
+
"created": int(time.time()),
|
| 197 |
+
"model": MODEL_ID,
|
| 198 |
+
"choices": [{
|
| 199 |
+
"index": 0,
|
| 200 |
+
"message": {"role": "assistant", "content": text},
|
| 201 |
+
"finish_reason": result["choices"][0].get("finish_reason", "stop"),
|
| 202 |
+
}],
|
| 203 |
+
"usage": result.get("usage", {}),
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
|
| 207 |
# ββ Endpoints βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 208 |
@app.get("/health")
|
| 209 |
def health() -> Dict[str, Any]:
|
|
|
|
| 239 |
if not req.messages:
|
| 240 |
raise HTTPException(status_code=400, detail="`messages` must not be empty.")
|
| 241 |
|
| 242 |
+
# Base model (no chat template): use raw text completion instead of a chat
|
| 243 |
+
# template the model was never trained on.
|
| 244 |
+
if not CHAT_FORMAT:
|
| 245 |
+
try:
|
| 246 |
+
with _llm_lock:
|
| 247 |
+
return _raw_chat_completion(llm, req)
|
| 248 |
+
except Exception as exc:
|
| 249 |
+
raise HTTPException(status_code=500, detail=f"generation failed: {exc}") from exc
|
| 250 |
+
|
| 251 |
kwargs: Dict[str, Any] = dict(
|
| 252 |
messages=[m.model_dump() for m in req.messages],
|
| 253 |
temperature=req.temperature,
|
|
|
|
| 279 |
t0 = time.time()
|
| 280 |
try:
|
| 281 |
with _llm_lock:
|
| 282 |
+
if not CHAT_FORMAT: # base model -> raw continuation of the prompt
|
| 283 |
+
raw = llm.create_completion(prompt=req.prompt,
|
| 284 |
+
temperature=req.temperature,
|
| 285 |
+
max_tokens=req.max_tokens)
|
| 286 |
+
result = {
|
| 287 |
+
"choices": [{"message": {"content": raw["choices"][0]["text"]}}],
|
| 288 |
+
"usage": raw.get("usage", {}),
|
| 289 |
+
}
|
| 290 |
+
else:
|
| 291 |
+
result = llm.create_chat_completion(
|
| 292 |
+
messages=[{"role": "user", "content": req.prompt}],
|
| 293 |
+
temperature=req.temperature,
|
| 294 |
+
max_tokens=req.max_tokens,
|
| 295 |
+
)
|
| 296 |
except Exception as exc:
|
| 297 |
raise HTTPException(status_code=500, detail=f"generation failed: {exc}") from exc
|
| 298 |
|