kirikir13 commited on
Commit
e2ed8fd
·
verified ·
1 Parent(s): 9c9e519

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +271 -42
app.py CHANGED
@@ -1,53 +1,282 @@
1
- """
2
- llama-cpp-python bootstrap — MUST import before gradio or anything else.
3
-
4
- 2026-07-27: simplified after the great wheel hunt.
5
- - PyPI = source-only (compiles 20+ min — the "runs and runs" bug).
6
- - abetlen CUDA indexes = abandoned (newest cu121 wheel is v0.2.59). Do NOT attempt.
7
- - abetlen CPU index = current prebuilt wheels. That is the only fast path.
8
- GPU speed returns via the Level-2 llama-server route (official llama.cpp CUDA
9
- binaries + MTP flags) — see the Level 2 spec, not this file.
10
- """
11
-
12
- from __future__ import annotations
13
 
 
14
  import os
15
- import subprocess
16
  import sys
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
- CPU_WHEEL_INDEX = "https://abetlen.github.io/llama-cpp-python/whl/cpu"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
 
 
 
 
 
 
 
 
 
20
 
21
- def _pip(args: list) -> None:
22
- cmd = [sys.executable, "-m", "pip", *args]
23
- print(f"[LLAMA] $ {' '.join(cmd)}", flush=True)
24
- env = os.environ.copy()
25
- env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
26
- env["PIP_ROOT_USER_ACTION"] = "ignore"
27
- subprocess.check_call(cmd, env=env)
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- def try_import_llama():
31
- import llama_cpp # noqa: F401
32
- return llama_cpp
 
 
 
 
 
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
- def bootstrap_llama_cpp():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  try:
37
- mod = try_import_llama()
38
- print("[LLAMA] Ready (prebuilt wheel already installed)", flush=True)
39
- except ImportError:
40
- print("[LLAMA] Not installed — installing CPU wheel (fast path)...", flush=True)
41
- _pip([
42
- "install", "--no-cache-dir", "llama-cpp-python==0.3.34",
43
- "--extra-index-url", CPU_WHEEL_INDEX,
44
- ])
45
- mod = try_import_llama()
46
-
47
- # CPU wheel => CPU inference. Be explicit so nothing pretends otherwise.
48
- os.environ.setdefault("DAVIDAU_N_GPU_LAYERS", "0")
49
- print("[LLAMA] CPU mode (n_gpu_layers=0) — GPU speed is the Level-2 llama-server job", flush=True)
50
- return mod
51
-
52
-
53
- bootstrap_llama_cpp()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import llama_bootstrap # noqa: F401 — MUST be first
 
 
 
 
 
 
 
 
 
 
 
2
 
3
+ import gradio as gr
4
  import os
5
+ import json
6
  import sys
7
+ import threading
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ MODEL_REPO = os.environ.get("MODEL_REPO", "DavidAU/Qwen3.6-27B-Heretic-Uncensored-FINETUNE-NEO-CODE-Di-IMatrix-MAX-GGUF")
12
+ MODEL_FILE = os.environ.get("MODEL_FILE", "Qwen3.6-27B-NEO-CODE-HERE-2T-OT-Q4_K_M.gguf")
13
+ N_CTX = int(os.environ.get("N_CTX", "16384"))
14
+ MAX_TOKENS = int(os.environ.get("MAX_TOKENS", "4096"))
15
+ N_GPU = int(os.environ.get("N_GPU_LAYERS", os.environ.get("DAVIDAU_N_GPU_LAYERS", "0")))
16
+
17
+ SYSTEM_PROMPT = """You are codeMax, an uncensored 27B coding assistant. You excel at:
18
+ - Writing production code in Python, TypeScript, JavaScript, Rust, Go, C/C++, SQL, shell
19
+ - Debugging, refactoring, reviewing complex codebases
20
+ - Explaining algorithms, architecture, system design
21
+ - Reading shared files and answering about them
22
+ Rules: put code in ```language blocks. Be precise and thorough."""
23
+
24
+ CSS = """footer { display: none !important; }"""
25
+
26
+ _llm = None
27
+ _model_path: Optional[str] = None
28
+ _load_lock = threading.Lock() # one load, ever — HF imports app.py twice; this makes that harmless
29
+
30
+ def _download():
31
+ global _model_path
32
+ if _model_path is not None:
33
+ return _model_path
34
+ from huggingface_hub import hf_hub_download
35
+ print(f"[MODEL] Downloading {MODEL_FILE}...", file=sys.stderr, flush=True)
36
+ _model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
37
+ print(f"[MODEL] Cached -> {_model_path}", file=sys.stderr, flush=True)
38
+ return _model_path
39
 
40
+ def _load():
41
+ """LAZY: nothing loads at import. First chat message triggers the one and only load."""
42
+ global _llm
43
+ if _llm is not None:
44
+ return _llm
45
+ with _load_lock:
46
+ if _llm is not None:
47
+ return _llm
48
+ from llama_cpp import Llama
49
+ path = _download()
50
+ print(f"[MODEL] Loading (n_gpu_layers={N_GPU}, n_ctx={N_CTX})...", file=sys.stderr, flush=True)
51
+ _llm = Llama(
52
+ model_path=path,
53
+ n_ctx=N_CTX,
54
+ n_gpu_layers=N_GPU,
55
+ chat_format="chatml",
56
+ verbose=False,
57
+ seed=-1,
58
+ )
59
+ print("[MODEL] Ready.", file=sys.stderr, flush=True)
60
+ return _llm
61
 
62
+ def _read_text(path, enc="utf-8"):
63
+ for e in [enc, "utf-8-sig", "latin-1", "cp1252", "utf-16"]:
64
+ try:
65
+ with open(path, "r", encoding=e) as f:
66
+ return f.read()
67
+ except UnicodeError:
68
+ continue
69
+ with open(path, "r", encoding="utf-8", errors="replace") as f:
70
+ return f.read()
71
 
72
+ def parse_file(file_path, file_name):
73
+ suffix = Path(file_name).suffix.lower()
74
+ name = Path(file_name).name
 
 
 
 
75
 
76
+ if suffix in (
77
+ ".txt", ".md", ".py", ".ts", ".tsx", ".js", ".jsx", ".mjs",
78
+ ".css", ".scss", ".less", ".html", ".htm", ".xml", ".svg",
79
+ ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf",
80
+ ".sh", ".bash", ".zsh", ".fish", ".ps1", ".bat",
81
+ ".sql", ".prisma",
82
+ ".c", ".cpp", ".cc", ".cxx", ".h", ".hpp", ".hh",
83
+ ".java", ".kt", ".kts", ".scala", ".groovy",
84
+ ".go", ".rs", ".rb", ".php", ".pl", ".pm",
85
+ ".swift", ".r", ".lua", ".zig", ".nim", ".dart",
86
+ ".env", ".gitignore", ".editorconfig",
87
+ ".tf", ".tfvars", ".hcl", ".vue", ".svelte", ".astro",
88
+ ):
89
+ content = _read_text(file_path)
90
+ lang = suffix.lstrip(".")
91
+ if suffix == ".md": lang = "markdown"
92
+ elif suffix in (".yml",): lang = "yaml"
93
+ elif suffix in (".tf", ".tfvars"): lang = "hcl"
94
+ elif suffix in (".htm",): lang = "html"
95
+ return f"### `{name}`\n```{lang}\n{content}\n```\n\n---\n"
96
 
97
+ if suffix == ".json":
98
+ content = _read_text(file_path)
99
+ try:
100
+ parsed = json.loads(content)
101
+ content = json.dumps(parsed, indent=2, ensure_ascii=False)
102
+ except Exception:
103
+ pass
104
+ return f"### `{name}`\n```json\n{content}\n```\n\n---\n"
105
 
106
+ if suffix == ".csv":
107
+ import csv, io
108
+ content = _read_text(file_path)
109
+ rows = list(csv.reader(io.StringIO(content)))
110
+ if len(rows) > 51:
111
+ rows = rows[:50] + [[f"... {len(rows) - 50} rows truncated"]]
112
+ widths = [max(len(str(c)) for c in col) for col in zip(*rows)]
113
+ sep = "|-" + "-|-".join("-" * x for x in widths) + "-|"
114
+ table = "\n".join(
115
+ "| " + " | ".join(str(c).ljust(widths[i]) for i, c in enumerate(row)) + " |"
116
+ for row in rows
117
+ )
118
+ table = table.split("\n", 1)
119
+ table.insert(1, sep)
120
+ return f"### `{name}`\n" + "\n".join(table) + "\n\n---\n"
121
 
122
+ if suffix == ".docx":
123
+ try:
124
+ import docx
125
+ doc = docx.Document(file_path)
126
+ text = "\n\n".join(p.text for p in doc.paragraphs if p.text.strip())
127
+ return f"### `{name}`\n{text}\n\n---\n"
128
+ except Exception as e:
129
+ return f"### `{name}` (DOCX error: {e})\n\n---\n"
130
+
131
+ if suffix == ".pdf":
132
+ try:
133
+ import pdfplumber
134
+ with pdfplumber.open(file_path) as pdf:
135
+ text = "\n\n".join(page.extract_text() or "" for page in pdf.pages)
136
+ return f"### `{name}`\n{text}\n\n---\n"
137
+ except Exception as e:
138
+ return f"### `{name}` (PDF error: {e})\n\n---\n"
139
+
140
+ try:
141
+ content = _read_text(file_path)
142
+ return f"### `{name}`\n```\n{content[:10000]}\n```\n\n---\n"
143
+ except Exception as e:
144
+ return f"### `{name}` (could not read: {e})\n\n---\n"
145
+
146
+ def parse_all_files(files):
147
+ if not files:
148
+ return ""
149
+ parts = []
150
+ for f in files:
151
+ if isinstance(f, dict):
152
+ path = f.get("path") or f.get("name")
153
+ name = f.get("orig_name") or f.get("name", "unknown")
154
+ elif hasattr(f, "name"):
155
+ path = f.name
156
+ name = getattr(f, "orig_name", Path(path).name)
157
+ else:
158
+ path = str(f)
159
+ name = Path(path).name
160
+ try:
161
+ parts.append(parse_file(path, name))
162
+ except Exception as e:
163
+ parts.append(f"### `{name}`\nParse error: {e}\n\n---\n")
164
+ return "\n".join(parts)
165
+
166
+ def respond(message, history, uploaded_files):
167
+ if _llm is None:
168
+ yield history + [
169
+ {"role": "user", "content": message},
170
+ {"role": "assistant", "content": "⏳ **First run:** downloading + loading the 27B brain — a few minutes, one time only. I'm on it…"},
171
+ ]
172
  try:
173
+ llm = _load()
174
+ except Exception as e:
175
+ yield history + [
176
+ {"role": "user", "content": message},
177
+ {"role": "assistant", "content": f"⚠️ Model failed to load: `{type(e).__name__}: {e}`\n\nMost likely: RAM too small for this quant (Q4_K_M needs a 32GB space) — or the download hiccuped, just send again."},
178
+ ]
179
+ return
180
+
181
+ file_context = parse_all_files(uploaded_files)
182
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
183
+
184
+ if file_context:
185
+ messages.append({"role": "user", "content": "[Uploaded files]\n\n" + file_context})
186
+ messages.append({"role": "assistant", "content": "Got it! I’ve read through all the uploaded files."})
187
+
188
+ for entry in history:
189
+ role = entry.get("role", "user")
190
+ content = entry.get("content", "")
191
+ if isinstance(content, str) and content:
192
+ messages.append({"role": role, "content": content})
193
+
194
+ messages.append({"role": "user", "content": message})
195
+
196
+ stream = llm.create_chat_completion(
197
+ messages=messages,
198
+ temperature=0.6,
199
+ top_p=0.8,
200
+ top_k=20,
201
+ max_tokens=MAX_TOKENS,
202
+ stream=True,
203
+ )
204
+
205
+ partial = ""
206
+ for chunk in stream:
207
+ if chunk.get("choices"):
208
+ delta = chunk["choices"][0].get("delta", {})
209
+ content = delta.get("content", "")
210
+ if content:
211
+ partial += content
212
+ yield history + [
213
+ {"role": "user", "content": message},
214
+ {"role": "assistant", "content": partial},
215
+ ]
216
+
217
+ def create_demo():
218
+ with gr.Blocks(title="codeMax — Qwen 3.6 27B Coder", css=CSS) as demo:
219
+ gr.Markdown(
220
+ "# codeMax\n"
221
+ "**Qwen 3.6 27B · Uncensored · Code-optimized · Dedicated GPU**\n"
222
+ "Drop code, docs, JSON, CSVs — chat with a 27B coding LLM."
223
+ )
224
+
225
+ with gr.Row(equal_height=True):
226
+ with gr.Column(scale=3):
227
+ chatbot = gr.Chatbot(
228
+ label="Chat",
229
+ height=580,
230
+ type="messages",
231
+ avatar_images=(None, "https://huggingface.co/front/assets/huggingface_logo-noborder.svg"),
232
+ )
233
+ with gr.Row():
234
+ msg = gr.Textbox(placeholder="Ask about code, share files, or chat...", scale=8, show_label=False, container=False)
235
+ send = gr.Button(">", scale=1, variant="primary", min_width=48)
236
+ clear_btn = gr.Button("Clear", size="sm")
237
+
238
+ with gr.Column(scale=1):
239
+ gr.Markdown("### Drop Files")
240
+ files = gr.File(
241
+ file_count="multiple",
242
+ label="Code, docs, data...",
243
+ file_types=[
244
+ ".py", ".ts", ".tsx", ".js", ".jsx", ".json",
245
+ ".md", ".txt", ".csv", ".yaml", ".yml", ".toml",
246
+ ".html", ".css", ".xml", ".sql", ".sh", ".go",
247
+ ".rs", ".rb", ".java", ".c", ".cpp", ".h",
248
+ ".docx", ".pdf", ".env", ".cfg", ".ini",
249
+ ],
250
+ )
251
+ uploaded_info = gr.Markdown("_No files uploaded._")
252
+
253
+ def update_info(uploaded):
254
+ if not uploaded:
255
+ return "_No files uploaded._"
256
+ names = []
257
+ for f in uploaded:
258
+ if isinstance(f, dict):
259
+ names.append(f.get("orig_name", "?"))
260
+ else:
261
+ names.append(getattr(f, "orig_name", Path(str(f)).name))
262
+ return "**Uploaded:**\n" + "\n".join(f"- `{n}`" for n in names)
263
+
264
+ def stream_response(message, history, current_files):
265
+ for h in respond(message, history, current_files):
266
+ yield h
267
+
268
+ msg.submit(stream_response, [msg, chatbot, files], [chatbot]).then(lambda: "", None, [msg])
269
+ send.click(stream_response, [msg, chatbot, files], [chatbot]).then(lambda: "", None, [msg])
270
+ clear_btn.click(lambda: [], None, chatbot, queue=False)
271
+ files.change(update_info, files, uploaded_info)
272
+
273
+ return demo
274
+
275
+ if __name__ == "__main__":
276
+ demo = create_demo()
277
+ demo.queue(default_concurrency_limit=1, max_size=4)
278
+ demo.launch(
279
+ server_name="0.0.0.0",
280
+ server_port=7860,
281
+ ssr_mode=False,
282
+ )