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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -271
app.py CHANGED
@@ -1,282 +1,53 @@
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
- from pathlib import Path
8
- from typing import Optional
9
-
10
- MODEL_REPO = os.environ.get("MODEL_REPO", "DavidAU/Qwen3.6-27B-Heretic-Uncensored-FINETUNE-NEO-CODE-Di-IMatrix-MAX-GGUF")
11
- MODEL_FILE = os.environ.get("MODEL_FILE", "Qwen3.6-27B-NEO-CODE-HERE-2T-OT-Q4_K_M.gguf")
12
- N_CTX = int(os.environ.get("N_CTX", "16384")) # FIX: was 4096
13
- MAX_TOKENS = int(os.environ.get("MAX_TOKENS", "4096")) # FIX: env-tunable
14
- N_GPU = int(os.environ.get("N_GPU_LAYERS", os.environ.get("DAVIDAU_N_GPU_LAYERS", "-1")))
15
-
16
- SYSTEM_PROMPT = """You are codeMax, an uncensored 27B coding assistant. You excel at:
17
- - Writing production code in Python, TypeScript, JavaScript, Rust, Go, C/C++, SQL, shell
18
- - Debugging, refactoring, reviewing complex codebases
19
- - Explaining algorithms, architecture, system design
20
- - Reading shared files and answering about them
21
- Rules: put code in ```language blocks. Be precise and thorough."""
22
-
23
- CSS = """footer { display: none !important; }""" # FIX: css lives here now
24
-
25
- _llm = None
26
- _model_path: Optional[str] = None
27
- _boot_error: Optional[str] = None # FIX: surface startup failures in chat instead of a dead space
28
-
29
- def _download():
30
- global _model_path
31
- if _model_path is not None:
32
- return _model_path
33
- from huggingface_hub import hf_hub_download
34
- print(f"[MODEL] Downloading {MODEL_FILE}...", file=sys.stderr)
35
- _model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
36
- print(f"[MODEL] Cached -> {_model_path}", file=sys.stderr)
37
- return _model_path
38
 
39
- def _load():
40
- global _llm
41
- if _llm is not None:
42
- return _llm
43
- from llama_cpp import Llama
44
- path = _download()
45
- print(f"[MODEL] Loading (n_gpu_layers={N_GPU}, n_ctx={N_CTX})...", file=sys.stderr)
46
- _llm = Llama(
47
- model_path=path,
48
- n_ctx=N_CTX,
49
- n_gpu_layers=N_GPU,
50
- chat_format="chatml",
51
- verbose=False,
52
- seed=-1,
53
- )
54
- print("[MODEL] Ready.", file=sys.stderr)
55
- return _llm
56
 
57
- # FIX: don't let a startup OOM kill the whole space silently — serve the UI, report the error
58
- try:
59
- print("[BOOT] Loading model at startup...", file=sys.stderr)
60
- _load()
61
- except Exception as e:
62
- _boot_error = f"{type(e).__name__}: {e}"
63
- print(f"[BOOT] Model load failed (will retry on first message): {_boot_error}", file=sys.stderr)
64
-
65
- def _read_text(path, enc="utf-8"):
66
- for e in [enc, "utf-8-sig", "latin-1", "cp1252", "utf-16"]:
67
- try:
68
- with open(path, "r", encoding=e) as f:
69
- return f.read()
70
- except UnicodeError:
71
- continue
72
- with open(path, "r", encoding="utf-8", errors="replace") as f:
73
- return f.read()
74
-
75
- def parse_file(file_path, file_name):
76
- suffix = Path(file_name).suffix.lower()
77
- name = Path(file_name).name
78
-
79
- if suffix in (
80
- ".txt", ".md", ".py", ".ts", ".tsx", ".js", ".jsx", ".mjs",
81
- ".css", ".scss", ".less", ".html", ".htm", ".xml", ".svg",
82
- ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf",
83
- ".sh", ".bash", ".zsh", ".fish", ".ps1", ".bat",
84
- ".sql", ".prisma",
85
- ".c", ".cpp", ".cc", ".cxx", ".h", ".hpp", ".hh",
86
- ".java", ".kt", ".kts", ".scala", ".groovy",
87
- ".go", ".rs", ".rb", ".php", ".pl", ".pm",
88
- ".swift", ".r", ".lua", ".zig", ".nim", ".dart",
89
- ".env", ".gitignore", ".editorconfig",
90
- ".tf", ".tfvars", ".hcl", ".vue", ".svelte", ".astro",
91
- ):
92
- content = _read_text(file_path)
93
- lang = suffix.lstrip(".")
94
- if suffix == ".md": lang = "markdown"
95
- elif suffix in (".yml",): lang = "yaml"
96
- elif suffix in (".tf", ".tfvars"): lang = "hcl"
97
- elif suffix in (".htm",): lang = "html"
98
- return f"### `{name}`\n```{lang}\n{content}\n```\n\n---\n"
99
 
100
- if suffix == ".json":
101
- content = _read_text(file_path)
102
- try:
103
- parsed = json.loads(content)
104
- content = json.dumps(parsed, indent=2, ensure_ascii=False)
105
- except Exception:
106
- pass
107
- return f"### `{name}`\n```json\n{content}\n```\n\n---\n"
108
 
109
- if suffix == ".csv":
110
- import csv, io
111
- content = _read_text(file_path)
112
- rows = list(csv.reader(io.StringIO(content)))
113
- if len(rows) > 51:
114
- rows = rows[:50] + [[f"... {len(rows) - 50} rows truncated"]]
115
- widths = [max(len(str(c)) for c in col) for col in zip(*rows)]
116
- sep = "|-" + "-|-".join("-" * x for x in widths) + "-|"
117
- table = "\n".join(
118
- "| " + " | ".join(str(c).ljust(widths[i]) for i, c in enumerate(row)) + " |"
119
- for row in rows
120
- )
121
- table = table.split("\n", 1)
122
- table.insert(1, sep)
123
- return f"### `{name}`\n" + "\n".join(table) + "\n\n---\n"
124
 
125
- if suffix == ".docx":
126
- try:
127
- import docx
128
- doc = docx.Document(file_path)
129
- text = "\n\n".join(p.text for p in doc.paragraphs if p.text.strip())
130
- return f"### `{name}`\n{text}\n\n---\n"
131
- except Exception as e:
132
- return f"### `{name}` (DOCX error: {e})\n\n---\n"
133
 
134
- if suffix == ".pdf":
135
- try:
136
- import pdfplumber
137
- with pdfplumber.open(file_path) as pdf:
138
- text = "\n\n".join(page.extract_text() or "" for page in pdf.pages)
139
- return f"### `{name}`\n{text}\n\n---\n"
140
- except Exception as e:
141
- return f"### `{name}` (PDF error: {e})\n\n---\n"
142
 
143
- try:
144
- content = _read_text(file_path)
145
- return f"### `{name}`\n```\n{content[:10000]}\n```\n\n---\n"
146
- except Exception as e:
147
- return f"### `{name}` (could not read: {e})\n\n---\n"
148
 
149
- def parse_all_files(files):
150
- if not files:
151
- return ""
152
- parts = []
153
- for f in files:
154
- if isinstance(f, dict):
155
- path = f.get("path") or f.get("name")
156
- name = f.get("orig_name") or f.get("name", "unknown")
157
- elif hasattr(f, "name"):
158
- path = f.name
159
- name = getattr(f, "orig_name", Path(path).name)
160
- else:
161
- path = str(f)
162
- name = Path(path).name
163
- try:
164
- parts.append(parse_file(path, name))
165
- except Exception as e:
166
- parts.append(f"### `{name}`\nParse error: {e}\n\n---\n")
167
- return "\n".join(parts)
168
 
169
- def respond(message, history, uploaded_files):
170
- # FIX: if boot failed, say so in chat and retry once
171
  try:
172
- llm = _load()
173
- except Exception as e:
174
- err = _boot_error or f"{type(e).__name__}: {e}"
175
- yield history + [
176
- {"role": "user", "content": message},
177
- {"role": "assistant", "content": f"⚠️ Model failed to load: `{err}`\n\nMost likely: hardware too small (needs A10G or 32GB CPU space) or still downloading — try again in a minute."},
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: # FIX: css here, not in launch()
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", # FIX: explicit, matches respond() format
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
- ) # FIX: css removed from launch()
 
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()