Electro0023 commited on
Commit
4621b74
Β·
verified Β·
1 Parent(s): e9e49b8

Replace Gradio SmolVLM app with DeepSeek-7B-base GGUF OpenAI-compatible API (Docker)

Browse files
Files changed (2) hide show
  1. README.md +11 -12
  2. main.py +27 -8
README.md CHANGED
@@ -9,13 +9,14 @@ pinned: false
9
  license: apache-2.0
10
  ---
11
 
12
- # DeepSeek-LLM-7B-base GGUF API (Docker Space)
13
 
14
  An OpenAI-compatible FastAPI server that runs
15
- **`TheBloke/deepseek-llm-7B-base-GGUF` β†’ `deepseek-llm-7b-base.Q4_K_M.gguf`**
16
- with `llama-cpp-python`. The model file is **not** stored in this repo β€” it is
17
- downloaded from the Hugging Face model hub on container startup, so this code
18
- repo stays tiny.
 
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-base","messages":[{"role":"user","content":"Newton'\''s second law is"}],"max_tokens":64}'
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 for instruct models (e.g. `llama-3`, `chatml`);
53
- leave empty for base models
54
  - `N_CTX`, `N_THREADS`, `N_GPU_LAYERS`, `DEFAULT_MAX_TOKENS` β€” tuning knobs
55
 
56
  ## Notes
57
 
58
- - **This default model is a BASE model** (raw text completion, not
59
- instruction-tuned). For chat/instruction behaviour, switch to
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-base (Q4_K_M), a PUBLIC GGUF β€” so the Space
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-base-GGUF").strip()
46
- FILENAME = os.getenv("FILENAME", "deepseek-llm-7b-base.Q4_K_M.gguf").strip()
47
  HF_TOKEN = os.getenv("HF_TOKEN") or None # None => anonymous (public repo)
48
- MODEL_ID = os.getenv("MODEL_ID", "") or "deepseek-7b-base" # name reported to clients
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-llm-7b-BASE has no chat template (it is a raw completion model), so no
56
- # chat format is forced by default; llama-cpp falls back to its generic template.
57
- # If you swap to an instruct/chat GGUF, set CHAT_FORMAT accordingly (e.g. "llama-3").
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).