Spaces:
Running
Running
Replace Gradio SmolVLM app with DeepSeek-7B-base GGUF OpenAI-compatible API (Docker)
Browse files
README.md
CHANGED
|
@@ -9,13 +9,14 @@ pinned: false
|
|
| 9 |
license: apache-2.0
|
| 10 |
---
|
| 11 |
|
| 12 |
-
# DeepSeek-LLM-7B-
|
| 13 |
|
| 14 |
An OpenAI-compatible FastAPI server that runs
|
| 15 |
-
**`TheBloke/deepseek-llm-7B-
|
| 16 |
-
with `llama-cpp-python`
|
| 17 |
-
|
| 18 |
-
repo
|
|
|
|
| 19 |
|
| 20 |
> β οΈ **The YAML block at the very top of this file is required.** Hugging Face
|
| 21 |
> reads `sdk: docker` and `app_port: 7860` from it to know how to build and
|
|
@@ -39,7 +40,7 @@ curl https://electro0023-model.hf.space/health
|
|
| 39 |
|
| 40 |
curl -X POST https://electro0023-model.hf.space/v1/chat/completions \
|
| 41 |
-H "Content-Type: application/json" \
|
| 42 |
-
-d '{"model":"deepseek-7b-
|
| 43 |
```
|
| 44 |
|
| 45 |
## Swapping models (no code changes)
|
|
@@ -49,16 +50,14 @@ Set Space **Variables** (Settings β Variables and secrets) and restart:
|
|
| 49 |
- `REPO_ID` β HF model repo holding the GGUF
|
| 50 |
- `FILENAME` β the exact `.gguf` file inside that repo
|
| 51 |
- `MODEL_ID` β name reported to API clients
|
| 52 |
-
- `CHAT_FORMAT` β chat template
|
| 53 |
-
|
| 54 |
- `N_CTX`, `N_THREADS`, `N_GPU_LAYERS`, `DEFAULT_MAX_TOKENS` β tuning knobs
|
| 55 |
|
| 56 |
## Notes
|
| 57 |
|
| 58 |
-
-
|
| 59 |
-
|
| 60 |
-
`TheBloke/deepseek-llm-7B-chat-GGUF` / `deepseek-llm-7b-chat.Q4_K_M.gguf`
|
| 61 |
-
with `CHAT_FORMAT=deepseek` β or any other instruct GGUF.
|
| 62 |
- On the free `cpu-basic` tier (2 vCPU, 16 GB RAM) a 7B Q4_K_M generates
|
| 63 |
roughly 1β3 tokens/sec. Expect long response times; set generous client
|
| 64 |
timeouts.
|
|
|
|
| 9 |
license: apache-2.0
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# DeepSeek-LLM-7B-chat GGUF API (Docker Space)
|
| 13 |
|
| 14 |
An OpenAI-compatible FastAPI server that runs
|
| 15 |
+
**`TheBloke/deepseek-llm-7B-chat-GGUF` β `deepseek-llm-7b-chat.Q4_K_M.gguf`**
|
| 16 |
+
with `llama-cpp-python`, using DeepSeek's official chat template (registered in
|
| 17 |
+
`main.py` as chat format `"deepseek"`). The model file is **not** stored in this
|
| 18 |
+
repo β it is downloaded from the Hugging Face model hub on container startup,
|
| 19 |
+
so this code repo stays tiny.
|
| 20 |
|
| 21 |
> β οΈ **The YAML block at the very top of this file is required.** Hugging Face
|
| 22 |
> reads `sdk: docker` and `app_port: 7860` from it to know how to build and
|
|
|
|
| 40 |
|
| 41 |
curl -X POST https://electro0023-model.hf.space/v1/chat/completions \
|
| 42 |
-H "Content-Type: application/json" \
|
| 43 |
+
-d '{"model":"deepseek-7b-chat","messages":[{"role":"user","content":"State Newton'\''s second law in one sentence."}],"max_tokens":64}'
|
| 44 |
```
|
| 45 |
|
| 46 |
## Swapping models (no code changes)
|
|
|
|
| 50 |
- `REPO_ID` β HF model repo holding the GGUF
|
| 51 |
- `FILENAME` β the exact `.gguf` file inside that repo
|
| 52 |
- `MODEL_ID` β name reported to API clients
|
| 53 |
+
- `CHAT_FORMAT` β chat template (`deepseek` = default, or built-ins like
|
| 54 |
+
`llama-3`, `chatml`); set to empty for BASE models (raw completion mode)
|
| 55 |
- `N_CTX`, `N_THREADS`, `N_GPU_LAYERS`, `DEFAULT_MAX_TOKENS` β tuning knobs
|
| 56 |
|
| 57 |
## Notes
|
| 58 |
|
| 59 |
+
- To run the BASE variant instead, set `REPO_ID=TheBloke/deepseek-llm-7B-base-GGUF`,
|
| 60 |
+
`FILENAME=deepseek-llm-7b-base.Q4_K_M.gguf`, `CHAT_FORMAT=` (empty).
|
|
|
|
|
|
|
| 61 |
- On the free `cpu-basic` tier (2 vCPU, 16 GB RAM) a 7B Q4_K_M generates
|
| 62 |
roughly 1β3 tokens/sec. Expect long response times; set generous client
|
| 63 |
timeouts.
|
main.py
CHANGED
|
@@ -34,28 +34,47 @@ from fastapi import FastAPI, HTTPException
|
|
| 34 |
from pydantic import BaseModel, Field
|
| 35 |
from huggingface_hub import hf_hub_download
|
| 36 |
from llama_cpp import Llama
|
|
|
|
| 37 |
|
| 38 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
| 39 |
log = logging.getLogger("gguf-api")
|
| 40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
# ββ Configuration (all via environment) βββββββββββββββββββββββββββββββββββββββ
|
| 42 |
-
# Defaults point at DeepSeek-LLM-7B-
|
| 43 |
# runs with zero setup. Override REPO_ID/FILENAME in the Space settings to swap
|
| 44 |
# models without touching the code.
|
| 45 |
-
REPO_ID = os.getenv("REPO_ID", "TheBloke/deepseek-llm-7B-
|
| 46 |
-
FILENAME = os.getenv("FILENAME", "deepseek-llm-7b-
|
| 47 |
HF_TOKEN = os.getenv("HF_TOKEN") or None # None => anonymous (public repo)
|
| 48 |
-
MODEL_ID = os.getenv("MODEL_ID", "") or "deepseek-7b-
|
| 49 |
N_CTX = int(os.getenv("N_CTX", "4096"))
|
| 50 |
# Default to the number of *usable* CPUs (respects cgroup cpuset), NOT os.cpu_count()
|
| 51 |
# which over-reports the host's cores inside a container and causes thread thrash.
|
| 52 |
_USABLE_CPUS = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else (os.cpu_count() or 4)
|
| 53 |
N_THREADS = int(os.getenv("N_THREADS", str(_USABLE_CPUS)))
|
| 54 |
N_GPU_LAYERS = int(os.getenv("N_GPU_LAYERS", "0")) # 0 = CPU only; -1 = all layers on GPU
|
| 55 |
-
# deepseek
|
| 56 |
-
#
|
| 57 |
-
#
|
| 58 |
-
CHAT_FORMAT = os.getenv("CHAT_FORMAT", "").strip()
|
| 59 |
DEFAULT_MAX_TOKENS = int(os.getenv("DEFAULT_MAX_TOKENS", "512"))
|
| 60 |
|
| 61 |
# The loaded model lives here. It is None until startup finishes (or if it failed).
|
|
|
|
| 34 |
from pydantic import BaseModel, Field
|
| 35 |
from huggingface_hub import hf_hub_download
|
| 36 |
from llama_cpp import Llama
|
| 37 |
+
from llama_cpp import llama_chat_format
|
| 38 |
|
| 39 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
| 40 |
log = logging.getLogger("gguf-api")
|
| 41 |
|
| 42 |
+
# DeepSeek-LLM chat template β NOT built into llama-cpp-python (and TheBloke's
|
| 43 |
+
# 2023 GGUFs predate embedded chat_template metadata), so register it ourselves.
|
| 44 |
+
# Official format: [system\n\n]User: {msg}\n\nAssistant: {reply}<eos>...Assistant:
|
| 45 |
+
@llama_chat_format.register_chat_format("deepseek")
|
| 46 |
+
def _format_deepseek(messages, **kwargs) -> llama_chat_format.ChatFormatterResponse:
|
| 47 |
+
prompt = ""
|
| 48 |
+
for m in messages:
|
| 49 |
+
role, content = m["role"], (m["content"] or "").strip()
|
| 50 |
+
if role == "system":
|
| 51 |
+
prompt += content + "\n\n"
|
| 52 |
+
elif role == "user":
|
| 53 |
+
prompt += f"User: {content}\n\n"
|
| 54 |
+
elif role == "assistant":
|
| 55 |
+
prompt += f"Assistant: {content}<ο½endβofβsentenceο½>"
|
| 56 |
+
prompt += "Assistant:"
|
| 57 |
+
return llama_chat_format.ChatFormatterResponse(prompt=prompt, stop=["User:"])
|
| 58 |
+
|
| 59 |
+
|
| 60 |
# ββ Configuration (all via environment) βββββββββββββββββββββββββββββββββββββββ
|
| 61 |
+
# Defaults point at DeepSeek-LLM-7B-CHAT (Q4_K_M), a PUBLIC GGUF β so the Space
|
| 62 |
# runs with zero setup. Override REPO_ID/FILENAME in the Space settings to swap
|
| 63 |
# models without touching the code.
|
| 64 |
+
REPO_ID = os.getenv("REPO_ID", "TheBloke/deepseek-llm-7B-chat-GGUF").strip()
|
| 65 |
+
FILENAME = os.getenv("FILENAME", "deepseek-llm-7b-chat.Q4_K_M.gguf").strip()
|
| 66 |
HF_TOKEN = os.getenv("HF_TOKEN") or None # None => anonymous (public repo)
|
| 67 |
+
MODEL_ID = os.getenv("MODEL_ID", "") or "deepseek-7b-chat" # name reported to clients
|
| 68 |
N_CTX = int(os.getenv("N_CTX", "4096"))
|
| 69 |
# Default to the number of *usable* CPUs (respects cgroup cpuset), NOT os.cpu_count()
|
| 70 |
# which over-reports the host's cores inside a container and causes thread thrash.
|
| 71 |
_USABLE_CPUS = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else (os.cpu_count() or 4)
|
| 72 |
N_THREADS = int(os.getenv("N_THREADS", str(_USABLE_CPUS)))
|
| 73 |
N_GPU_LAYERS = int(os.getenv("N_GPU_LAYERS", "0")) # 0 = CPU only; -1 = all layers on GPU
|
| 74 |
+
# "deepseek" = the custom template registered above, matching the default chat
|
| 75 |
+
# model. Set to "" (empty) when running a BASE model β the endpoints then bypass
|
| 76 |
+
# chat templating and use raw text completion instead.
|
| 77 |
+
CHAT_FORMAT = os.getenv("CHAT_FORMAT", "deepseek").strip()
|
| 78 |
DEFAULT_MAX_TOKENS = int(os.getenv("DEFAULT_MAX_TOKENS", "512"))
|
| 79 |
|
| 80 |
# The loaded model lives here. It is None until startup finishes (or if it failed).
|