launch-calcium commited on
Commit
1fe9077
·
verified ·
1 Parent(s): eead13e

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. Dockerfile +10 -28
  2. README.md +8 -8
  3. app.py +118 -149
Dockerfile CHANGED
@@ -1,43 +1,25 @@
1
  FROM python:3.11-slim
2
 
3
  ENV DEBIAN_FRONTEND=noninteractive \
4
- MODEL_REPO=NANI-Nithin/K2-Horizon-0.9B-GGUF \
5
- MODEL_FILE=K2-Horizon-0.9B-Q4_K_M.gguf \
6
- MODEL_DIR=/data/models/k2-horizon \
7
- LLAMA_VERSION=b9592 \
8
- LLAMA_DIR=/opt/llama.cpp \
9
- LLAMA_SERVER_BIN=/opt/llama.cpp/llama-server \
10
- LD_LIBRARY_PATH=/opt/llama.cpp \
11
- LLAMA_HOST=0.0.0.0 \
12
- LLAMA_PORT=7860 \
13
- THREADS=8 \
14
- CTX_SIZE=4096 \
15
- BATCH_SIZE=default \
16
- UBATCH_SIZE=default \
17
- CACHE_TYPE_K=default \
18
- CACHE_TYPE_V=default \
19
- GPU_LAYERS=0 \
20
- TEMPERATURE=0.6 \
21
- TOP_P=0.95 \
22
- TOP_K=40 \
23
- REPEAT_PENALTY=1.1 \
24
- HF_XET_HIGH_PERFORMANCE=1 \
25
- PYTHONUNBUFFERED=1
26
 
27
  RUN apt-get update && apt-get install -y --no-install-recommends \
28
  ca-certificates \
29
  curl \
 
30
  libgomp1 \
31
  libstdc++6 \
32
  && rm -rf /var/lib/apt/lists/*
33
 
34
- RUN mkdir -p "${LLAMA_DIR}" \
35
- && curl -fL "https://github.com/ggml-org/llama.cpp/releases/download/${LLAMA_VERSION}/llama-${LLAMA_VERSION}-bin-ubuntu-x64.tar.gz" \
36
- | tar -xz --strip-components=1 -C "${LLAMA_DIR}" \
37
- && chmod +x "${LLAMA_SERVER_BIN}"
38
-
39
  RUN pip install --no-cache-dir \
40
- huggingface_hub
 
 
 
 
 
 
41
 
42
  WORKDIR /app
43
  COPY app.py /app/app.py
 
1
  FROM python:3.11-slim
2
 
3
  ENV DEBIAN_FRONTEND=noninteractive \
4
+ PYTHONUNBUFFERED=1 \
5
+ HF_HUB_ENABLE_HF_TRANSFER=0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  RUN apt-get update && apt-get install -y --no-install-recommends \
8
  ca-certificates \
9
  curl \
10
+ git \
11
  libgomp1 \
12
  libstdc++6 \
13
  && rm -rf /var/lib/apt/lists/*
14
 
 
 
 
 
 
15
  RUN pip install --no-cache-dir \
16
+ "torch>=2.0.0" \
17
+ "transformers>=4.40.0" \
18
+ "gradio>=4.0.0" \
19
+ "huggingface_hub" \
20
+ "accelerate" \
21
+ "einops" \
22
+ "jinja2"
23
 
24
  WORKDIR /app
25
  COPY app.py /app/app.py
README.md CHANGED
@@ -10,18 +10,18 @@ pinned: false
10
  models:
11
  - IFM/K2-Horizon-0.9B
12
  tags:
13
- - llama.cpp
14
- - gguf
15
  - k2-horizon
 
 
 
16
  - cpu
17
  ---
18
 
19
- # K2-Horizon-0.9B Chat
20
 
21
- Hugging Face Space running K2-Horizon-0.9B with llama.cpp on CPU.
22
 
23
- - Model: `IFM/K2-Horizon-0.9B` (GGUF quant: `NANI-Nithin/K2-Horizon-0.9B-GGUF`)
24
- - Default quant: `K2-Horizon-0.9B-Q4_K_M.gguf`
25
- - Backend: `llama.cpp` `llama-server`
26
- - UI: native `llama.cpp` web UI
27
  - Target: HF Spaces Docker CPU
 
10
  models:
11
  - IFM/K2-Horizon-0.9B
12
  tags:
 
 
13
  - k2-horizon
14
+ - transformers
15
+ - pytorch
16
+ - gradio
17
  - cpu
18
  ---
19
 
20
+ # K2-Horizon-0.9B Chat Demo
21
 
22
+ Hugging Face Space running `IFM/K2-Horizon-0.9B` with Transformers + Gradio UI on CPU.
23
 
24
+ - Model: `IFM/K2-Horizon-0.9B`
25
+ - Backend: PyTorch / Hugging Face Transformers (`AutoModelForCausalLM`)
26
+ - UI: Gradio ChatInterface
 
27
  - Target: HF Spaces Docker CPU
app.py CHANGED
@@ -1,159 +1,128 @@
1
  from __future__ import annotations
2
 
3
- import json
4
  import os
5
  import sys
6
- import urllib.parse
7
- import urllib.request
8
- from pathlib import Path
9
-
10
- from huggingface_hub import hf_hub_download
11
-
12
-
13
- MODEL_REPO = os.getenv("MODEL_REPO", "NANI-Nithin/K2-Horizon-0.9B-GGUF")
14
- MODEL_FILE = os.getenv("MODEL_FILE", "K2-Horizon-0.9B-Q4_K_M.gguf")
15
- MODEL_DIR = Path(os.getenv("MODEL_DIR", "/data/models/k2-horizon"))
16
- CHAT_TEMPLATE_FILE = Path(os.getenv("CHAT_TEMPLATE_FILE", "/data/models/k2-horizon/chat_template.jinja"))
17
-
18
- LLAMA_SERVER_BIN = os.getenv("LLAMA_SERVER_BIN", "/opt/llama.cpp/llama-server")
19
- LLAMA_HOST = os.getenv("LLAMA_HOST", "0.0.0.0")
20
- LLAMA_PORT = os.getenv("LLAMA_PORT", "7860")
21
-
22
- THREADS = os.getenv("THREADS", "8")
23
- CTX_SIZE = os.getenv("CTX_SIZE", "4096")
24
- BATCH_SIZE = os.getenv("BATCH_SIZE", "default")
25
- UBATCH_SIZE = os.getenv("UBATCH_SIZE", "default")
26
- GPU_LAYERS = os.getenv("GPU_LAYERS", "0")
27
- FLASH_ATTN = os.getenv("FLASH_ATTN", "default")
28
- CACHE_TYPE_K = os.getenv("CACHE_TYPE_K", "default")
29
- CACHE_TYPE_V = os.getenv("CACHE_TYPE_V", "default")
30
-
31
- TEMPERATURE = os.getenv("TEMPERATURE", "0.6")
32
- TOP_P = os.getenv("TOP_P", "0.95")
33
- TOP_K = os.getenv("TOP_K", "40")
34
- REPEAT_PENALTY = os.getenv("REPEAT_PENALTY", "1.1")
35
-
36
-
37
- def log(message: str) -> None:
38
- print(f"[startup] {message}", flush=True)
39
-
40
-
41
- def download_model() -> str:
42
- MODEL_DIR.mkdir(parents=True, exist_ok=True)
43
- local_file = MODEL_DIR / MODEL_FILE
44
- if local_file.exists():
45
- log(f"Using cached model: {local_file}")
46
- return str(local_file)
47
-
48
- log(f"Downloading {MODEL_REPO}/{MODEL_FILE}")
49
- model_path = hf_hub_download(
50
- repo_id=MODEL_REPO,
51
- filename=MODEL_FILE,
52
- local_dir=str(MODEL_DIR),
53
- )
54
- log(f"Model ready: {model_path}")
55
- return model_path
56
-
57
-
58
- def download_chat_template() -> str | None:
59
- if CHAT_TEMPLATE_FILE.exists() and CHAT_TEMPLATE_FILE.stat().st_size > 0:
60
- log(f"Using cached chat template: {CHAT_TEMPLATE_FILE}")
61
- return str(CHAT_TEMPLATE_FILE)
62
-
63
- base_repo = "IFM/K2-Horizon-0.9B"
64
- encoded_repo = urllib.parse.quote(base_repo, safe="/")
65
- api_url = f"https://huggingface.co/api/models/{encoded_repo}"
66
- log(f"Fetching chat template from {base_repo} metadata")
67
-
68
  try:
69
- with urllib.request.urlopen(api_url, timeout=30) as response:
70
- metadata = json.loads(response.read().decode("utf-8"))
71
- except Exception as exc:
72
- log(f"Could not fetch chat template metadata: {exc}")
73
- return None
74
-
75
- template = (metadata.get("cardData") or {}).get("chat_template") or metadata.get("chat_template")
76
- if not template:
77
- log("No chat template found in model metadata; llama-server will use GGUF metadata")
78
- return None
79
-
80
- CHAT_TEMPLATE_FILE.parent.mkdir(parents=True, exist_ok=True)
81
- CHAT_TEMPLATE_FILE.write_text(template, encoding="utf-8")
82
- log(f"Chat template ready: {CHAT_TEMPLATE_FILE}")
83
- return str(CHAT_TEMPLATE_FILE)
84
-
85
-
86
- def build_command(model_path: str, template_path: str | None) -> list[str]:
87
- def has_custom_value(value: str) -> bool:
88
- return value.strip().lower() not in {"", "default", "auto", "none", "off"}
89
-
90
- def add_optional_pair(flag: str, value: str) -> None:
91
- if has_custom_value(value):
92
- cmd.extend([flag, value])
93
-
94
- cmd = [
95
- LLAMA_SERVER_BIN,
96
- "-m",
97
- model_path,
98
- "--host",
99
- LLAMA_HOST,
100
- "--port",
101
- LLAMA_PORT,
102
- "--threads",
103
- THREADS,
104
- "--ctx-size",
105
- CTX_SIZE,
106
- "--n-gpu-layers",
107
- GPU_LAYERS,
108
- "--parallel",
109
- "1",
110
- "--cont-batching",
111
- "--temp",
112
- TEMPERATURE,
113
- "--top-p",
114
- TOP_P,
115
- "--top-k",
116
- TOP_K,
117
- "--repeat-penalty",
118
- REPEAT_PENALTY,
119
- ]
120
-
121
- add_optional_pair("--batch-size", BATCH_SIZE)
122
- add_optional_pair("--ubatch-size", UBATCH_SIZE)
123
- add_optional_pair("--cache-type-k", CACHE_TYPE_K)
124
- add_optional_pair("--cache-type-v", CACHE_TYPE_V)
125
- if has_custom_value(FLASH_ATTN):
126
- cmd.extend(["-fa", FLASH_ATTN])
127
-
128
- if template_path:
129
- cmd.extend(["--chat-template-file", template_path])
130
-
131
- return cmd
132
-
133
-
134
- def main() -> None:
135
- binary_dir = str(Path(LLAMA_SERVER_BIN).parent)
136
- existing_library_path = os.environ.get("LD_LIBRARY_PATH")
137
- os.environ["LD_LIBRARY_PATH"] = (
138
- binary_dir if not existing_library_path else f"{binary_dir}:{existing_library_path}"
139
  )
140
 
141
- os.environ.setdefault("OMP_NUM_THREADS", THREADS)
142
- os.environ.setdefault("OPENBLAS_NUM_THREADS", THREADS)
143
- os.environ.setdefault("MKL_NUM_THREADS", THREADS)
144
-
145
- model_path = download_model()
146
- template_path = download_chat_template()
147
- cmd = build_command(model_path, template_path)
148
-
149
- log("Starting native llama.cpp web UI")
150
- log(" ".join(cmd))
151
- os.execvpe(cmd[0], cmd, os.environ)
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
  if __name__ == "__main__":
155
- try:
156
- main()
157
- except Exception as exc:
158
- print(f"[fatal] {exc}", file=sys.stderr, flush=True)
159
- raise
 
1
  from __future__ import annotations
2
 
 
3
  import os
4
  import sys
5
+ import threading
6
+ from typing import Generator
7
+
8
+ import gradio as gr
9
+ import torch
10
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
11
+
12
+ MODEL_ID = os.getenv("MODEL_ID", "IFM/K2-Horizon-0.9B")
13
+
14
+ print(f"[startup] Loading tokenizer for {MODEL_ID}...", flush=True)
15
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
16
+
17
+ print(f"[startup] Loading model {MODEL_ID}...", flush=True)
18
+ model = AutoModelForCausalLM.from_pretrained(
19
+ MODEL_ID,
20
+ torch_dtype=torch.float32,
21
+ device_map="cpu",
22
+ low_cpu_mem_usage=True,
23
+ trust_remote_code=True,
24
+ )
25
+ model.eval()
26
+ print("[startup] Model loaded successfully!", flush=True)
27
+
28
+
29
+ def chat_response(
30
+ message: str,
31
+ history: list[dict[str, str]],
32
+ system_prompt: str,
33
+ temperature: float,
34
+ top_p: float,
35
+ max_tokens: int,
36
+ reasoning_effort: str,
37
+ ) -> Generator[str, None, None]:
38
+ messages = []
39
+ if system_prompt.strip():
40
+ messages.append({"role": "system", "content": system_prompt})
41
+
42
+ for item in history:
43
+ messages.append(item)
44
+
45
+ messages.append({"role": "user", "content": message})
46
+
47
+ chat_kwargs = {"reasoning_effort": reasoning_effort} if reasoning_effort else {}
48
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  try:
50
+ prompt = tokenizer.apply_chat_template(
51
+ messages,
52
+ tokenize=False,
53
+ add_generation_prompt=True,
54
+ chat_template_kwargs=chat_kwargs if chat_kwargs else None,
55
+ )
56
+ except Exception:
57
+ prompt = tokenizer.apply_chat_template(
58
+ messages,
59
+ tokenize=False,
60
+ add_generation_prompt=True,
61
+ )
62
+
63
+ inputs = tokenizer([prompt], return_tensors="pt")
64
+ inputs.pop("token_type_ids", None)
65
+
66
+ streamer = TextIteratorStreamer(
67
+ tokenizer, timeout=30.0, skip_prompt=True, skip_special_tokens=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  )
69
 
70
+ generate_kwargs = dict(
71
+ **inputs,
72
+ streamer=streamer,
73
+ max_new_tokens=int(max_tokens),
74
+ do_sample=temperature > 0,
75
+ temperature=float(temperature) if temperature > 0 else 1.0,
76
+ top_p=float(top_p),
77
+ )
 
 
 
78
 
79
+ thread = threading.Thread(target=model.generate, kwargs=generate_kwargs)
80
+ thread.start()
81
+
82
+ partial_text = ""
83
+ for new_text in streamer:
84
+ partial_text += new_text
85
+ yield partial_text
86
+
87
+
88
+ demo = gr.ChatInterface(
89
+ fn=chat_response,
90
+ type="messages",
91
+ title="K2-Horizon-0.9B Chat Demo",
92
+ description="Interactive demo for [IFM/K2-Horizon-0.9B](https://huggingface.co/IFM/K2-Horizon-0.9B) using PyTorch and Transformers on CPU.",
93
+ additional_inputs=[
94
+ gr.Textbox(
95
+ value="You are a helpful and harmless assistant.",
96
+ label="System Prompt",
97
+ ),
98
+ gr.Slider(
99
+ minimum=0.0,
100
+ maximum=2.0,
101
+ value=0.6,
102
+ step=0.1,
103
+ label="Temperature",
104
+ ),
105
+ gr.Slider(
106
+ minimum=0.1,
107
+ maximum=1.0,
108
+ value=0.95,
109
+ step=0.05,
110
+ label="Top-P",
111
+ ),
112
+ gr.Slider(
113
+ minimum=128,
114
+ maximum=8192,
115
+ value=2048,
116
+ step=128,
117
+ label="Max Tokens",
118
+ ),
119
+ gr.Dropdown(
120
+ choices=["high", "medium", "low"],
121
+ value="high",
122
+ label="Reasoning Effort",
123
+ ),
124
+ ],
125
+ )
126
 
127
  if __name__ == "__main__":
128
+ demo.queue().launch(server_name="0.0.0.0", server_port=7860)