kirikir13 commited on
Commit
016896d
Β·
verified Β·
1 Parent(s): 7b27dd1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +289 -124
app.py CHANGED
@@ -1,135 +1,300 @@
 
 
 
 
1
 
2
- """codeMax β€” DavidAU coder/chat with file drop-in."""
3
-
4
  import os
5
- import sys
6
- import subprocess
7
-
8
- # ── Runtime install: llama-cpp-python (CPU) ──
9
- try:
10
- import llama_cpp
11
- except ImportError:
12
- print("[RUNTIME] Installing llama-cpp-python (CPU)...", flush=True)
13
- subprocess.check_call([
14
- sys.executable, "-m", "pip", "install", "--no-cache-dir",
15
- "--extra-index-url", "https://abetlen.github.io/llama-cpp-python/whl/cpu",
16
- "llama-cpp-python"
17
- ])
18
- import llama_cpp
19
- print("[RUNTIME] llama-cpp-python installed", flush=True)
20
 
21
- import gradio as gr
22
- from huggingface_hub import hf_hub_download
23
- from llama_cpp import Llama
24
-
25
- # ── Load DavidAU ──
26
- REPO_ID = "DavidAU/Qwen3.6-27B-Heretic-Uncensored-FINETUNE-NEO-CODE-Di-IMatrix-MAX-GGUF"
27
- FILENAME = "Qwen3.6-27B-NEO-CODE-HERE-2T-OT-Q4_K_M.gguf"
28
-
29
- print(f"[MODEL] Downloading {FILENAME}...", flush=True)
30
- model_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME)
31
- print(f"[MODEL] Loading...", flush=True)
32
-
33
- llm = Llama(
34
- model_path=model_path,
35
- n_ctx=8192,
36
- n_threads=8,
37
- verbose=False,
38
- )
39
- print("[MODEL] Ready.", flush=True)
40
-
41
-
42
- def build_prompt(message, history, system_prompt, file_content=None):
43
- """Build ChatML prompt with optional file content."""
44
- parts = []
45
- if system_prompt:
46
- parts.append(f"<|system|>\n{system_prompt}</s>")
47
- if history:
48
- for msg in history:
49
- role = msg.get("role", "user")
50
- content = msg.get("content", "")
51
- parts.append(f"<|{role}|>\n{content}</s>")
52
- user_msg = message
53
- if file_content:
54
- user_msg = f"File contents:\n```\n{file_content}\n```\n\nUser: {message}"
55
- parts.append(f"<|user|>\n{user_msg}</s>")
56
- parts.append("<|assistant|>\n")
57
- return "\n".join(parts)
58
-
59
-
60
- def respond(message, history, system_prompt, file_upload):
61
- """Generate response from DavidAU."""
62
- if not message.strip():
63
- return ""
64
-
65
- file_content = None
66
- if file_upload is not None:
67
  try:
68
- if isinstance(file_upload, dict):
69
- path = file_upload.get("name", file_upload.get("path", ""))
70
- else:
71
- path = str(file_upload)
72
- with open(path, "r", encoding="utf-8", errors="replace") as f:
73
- file_content = f.read()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  except Exception:
75
  pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
- # Convert history to dict format
78
- history_dicts = []
79
- if history:
80
- for h in history:
81
- if isinstance(h, (list, tuple)) and len(h) == 2:
82
- history_dicts.append({"role": "user", "content": h[0]})
83
- history_dicts.append({"role": "assistant", "content": h[1]})
 
 
 
 
 
 
 
 
 
84
 
85
- prompt = build_prompt(message, history_dicts, system_prompt, file_content)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
- try:
88
- stream = llm(
89
- prompt,
90
- max_tokens=2048,
91
- stop=["</s>", "<|user|>"],
92
- stream=True,
93
- )
94
- response = ""
95
- for output in stream:
96
- token = output["choices"][0].get("text", "")
97
- response += token
98
- yield response
99
- except Exception as e:
100
- yield f"[Error: {e}]"
101
-
102
-
103
- # ── UI ──
104
- with gr.Blocks(title="codeMax β€” DavidAU Coder", css="""
105
- :root { --bg: #0d1117; --fg: #c9d1d9; --accent: #58a6ff; }
106
- body, .gradio-container { background: var(--bg) !important; color: var(--fg) !important; }
107
- .gr-button-primary { background: var(--accent) !important; }
108
- """) as demo:
109
- gr.Markdown("# codeMax\n### DavidAU Qwen3.6-27B β€” Coder + File Drop")
110
- with gr.Row():
111
- with gr.Column(scale=1):
112
- system_box = gr.Textbox(
113
- label="System Prompt",
114
- value="You are an expert software engineer. Be concise, write clean code.",
115
- lines=3,
116
- )
117
- file_input = gr.File(label="Drop code file here", file_types=[".py", ".js", ".ts", ".tsx", ".json", ".txt", ".md", ".html", ".css"])
118
- with gr.Column(scale=3):
119
- chatbot = gr.Chatbot(label="Chat", height=480, type="messages")
120
- msg_box = gr.Textbox(label="Message", placeholder="Ask me to code something...", lines=2)
121
- send_btn = gr.Button("Send", variant="primary")
122
-
123
- send_btn.click(
124
- respond,
125
- inputs=[msg_box, chatbot, system_box, file_input],
126
- outputs=[chatbot],
127
- )
128
- msg_box.submit(
129
- respond,
130
- inputs=[msg_box, chatbot, system_box, file_input],
131
- outputs=[chatbot],
132
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
  if __name__ == "__main__":
135
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
1
+ # =============================================================================
2
+ # codeMax β€” Qwen 3.6 27B CoderBot on ZeroGPU (Gradio 6)
3
+ # Model: DavidAU/Qwen3.6-27B-Heretic-Uncensored-FINETUNE-NEO-CODE-Di-IMatrix-MAX-GGUF
4
+ # =============================================================================
5
 
6
+ import spaces
7
+ import gradio as gr
8
  import os
9
+ import json
10
+ from pathlib import Path
11
+ from typing import List, Optional, Generator
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
+ # --- Config ----------------------------------------------------------------
14
+ MODEL_REPO = os.environ.get("MODEL_REPO", "DavidAU/Qwen3.6-27B-Heretic-Uncensored-FINETUNE-NEO-CODE-Di-IMatrix-MAX-GGUF")
15
+ MODEL_FILE = os.environ.get("MODEL_FILE", "Qwen3.6-27B-NEO-CODE-HERE-2T-OT-Q4_K_M.gguf")
16
+ N_CTX = int(os.environ.get("N_CTX", "8192"))
17
+ N_GPU = int(os.environ.get("N_GPU_LAYERS", "-1")) # -1 = all layers on GPU
18
+
19
+ SYSTEM_PROMPT = """You are codeMax, an uncensored 27B coding assistant. You excel at:
20
+ β€’ Writing production code in Python, TypeScript, JavaScript, Rust, Go, C/C++, SQL, shell
21
+ β€’ Debugging, refactoring, reviewing complex codebases
22
+ β€’ Explaining algorithms, architecture, system design
23
+ β€’ Reading shared files and answering about them
24
+
25
+ Rules: put code in ```language blocks. Be precise and thorough."""
26
+
27
+ # --- Model singleton -------------------------------------------------------
28
+ _llm = None
29
+ _model_path: Optional[str] = None
30
+
31
+ def _download():
32
+ global _model_path
33
+ if _model_path is not None:
34
+ return _model_path
35
+ from huggingface_hub import hf_hub_download
36
+ print(f"[MODEL] Downloading {MODEL_FILE}...")
37
+ _model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
38
+ print(f"[MODEL] Cached β†’ {_model_path}")
39
+ return _model_path
40
+
41
+ def _load():
42
+ global _llm
43
+ if _llm is not None:
44
+ return _llm
45
+ from llama_cpp import Llama
46
+ path = _download()
47
+ print(f"[MODEL] Loading (n_gpu_layers={N_GPU})...")
48
+ _llm = Llama(model_path=path, n_ctx=N_CTX, n_gpu_layers=N_GPU,
49
+ chat_format="chatml", verbose=False, seed=-1)
50
+ print("[MODEL] Ready.")
51
+ return _llm
52
+
53
+ # --- File parsers ----------------------------------------------------------
54
+ def _read_text(path, enc="utf-8"):
55
+ for e in [enc, "utf-8-sig", "latin-1", "cp1252", "utf-16"]:
 
 
 
56
  try:
57
+ with open(path, "r", encoding=e) as f:
58
+ return f.read()
59
+ except UnicodeError:
60
+ continue
61
+ with open(path, "r", encoding="utf-8", errors="replace") as f:
62
+ return f.read()
63
+
64
+ CODE_EXTS = {".txt",".md",".py",".ts",".tsx",".js",".jsx",".mjs",".cjs",
65
+ ".css",".scss",".less",".html",".htm",".xml",".svg",
66
+ ".yaml",".yml",".toml",".ini",".cfg",".conf",
67
+ ".sh",".bash",".zsh",".fish",".ps1",".bat",
68
+ ".sql",".graphql",".prisma",".cs",
69
+ ".c",".cpp",".cc",".cxx",".h",".hpp",".hh",
70
+ ".java",".kt",".kts",".scala",".groovy",
71
+ ".go",".rs",".rb",".php",".pl",".pm",
72
+ ".swift",".r",".lua",".zig",".nim",".dart",
73
+ ".env",".gitignore",".editorconfig",
74
+ ".tf",".tfvars",".hcl",".vue",".svelte",".astro"}
75
+
76
+ def parse_file(file_path, file_name):
77
+ suffix = Path(file_name).suffix.lower()
78
+ name = Path(file_name).name
79
+ if suffix in CODE_EXTS:
80
+ content = _read_text(file_path)
81
+ lang = suffix.lstrip(".")
82
+ if suffix == ".md": lang = "markdown"
83
+ elif suffix in (".yml",): lang = "yaml"
84
+ elif suffix in (".tf",".tfvars"): lang = "hcl"
85
+ elif suffix in (".htm",): lang = "html"
86
+ return f"### πŸ“„ `{name}`\n```{lang}\n{content}\n```\n\n---\n"
87
+ if suffix == ".json":
88
+ content = _read_text(file_path)
89
+ try:
90
+ parsed = json.loads(content)
91
+ content = json.dumps(parsed, indent=2, ensure_ascii=False)
92
  except Exception:
93
  pass
94
+ return f"### πŸ“„ `{name}`\n```json\n{content}\n```\n\n---\n"
95
+ if suffix == ".csv":
96
+ import csv, io
97
+ content = _read_text(file_path)
98
+ rows = list(csv.reader(io.StringIO(content)))
99
+ if len(rows) > 51:
100
+ rows = rows[:50] + [[f"… {len(rows)-50} rows truncated"]]
101
+ w = [max(len(str(c)) for c in col) for col in zip(*rows)]
102
+ sep = "|-" + "-|-".join("-"*x for x in w) + "-|"
103
+ t = "\n".join("| " + " | ".join(str(c).ljust(w[i]) for i,c in enumerate(r)) + " |" for r in rows)
104
+ t = t.split("\n",1); t.insert(1,sep)
105
+ return f"### πŸ“Š `{name}`\n" + "\n".join(t) + "\n\n---\n"
106
+ if suffix == ".docx":
107
+ try:
108
+ import docx; d=docx.Document(file_path)
109
+ return f"### πŸ“ `{name}`\n"+"\n\n".join(p.text for p in d.paragraphs if p.text.strip())+"\n\n---\n"
110
+ except Exception as e:
111
+ return f"### ⚠️ `{name}` (DOCX error: {e})\n\n---\n"
112
+ if suffix == ".pdf":
113
+ try:
114
+ import pdfplumber
115
+ with pdfplumber.open(file_path) as pdf:
116
+ t = "\n\n".join(p.extract_text() or "" for p in pdf.pages)
117
+ return f"### πŸ“‘ `{name}`\n{t}\n\n---\n"
118
+ except Exception as e:
119
+ return f"### ⚠️ `{name}` (PDF error: {e})\n\n---\n"
120
+ try:
121
+ return f"### πŸ“„ `{name}`\n```\n{_read_text(file_path)[:10000]}\n```\n\n---\n"
122
+ except Exception as e:
123
+ return f"### ❌ `{name}` ({e})\n\n---\n"
124
 
125
+ def parse_all_files(files):
126
+ if not files: return ""
127
+ out = []
128
+ for f in files:
129
+ if isinstance(f, dict):
130
+ path = f.get("path") or f.get("name")
131
+ name = f.get("orig_name") or f.get("name","unknown")
132
+ elif hasattr(f,"name"):
133
+ path = f.name; name = getattr(f,"orig_name",Path(path).name)
134
+ else:
135
+ path=str(f); name=Path(path).name
136
+ try:
137
+ out.append(parse_file(path,name))
138
+ except Exception as e:
139
+ out.append(f"### ❌ `{name}`\n{e}\n\n---\n")
140
+ return "\n".join(out)
141
 
142
+ # --- Generation ------------------------------------------------------------
143
+ @spaces.GPU
144
+ def respond(message: str, history: List, uploaded_files) -> Generator[List, None, None]:
145
+ fc = parse_all_files(uploaded_files)
146
+ msgs = [{"role":"system","content":SYSTEM_PROMPT}]
147
+ if fc:
148
+ msgs.append({"role":"user","content":"[Uploaded files β€” read carefully]\n\n"+fc})
149
+ msgs.append({"role":"assistant","content":"Got it! I've read all the files. Ask me anything."})
150
+ for entry in history:
151
+ r = entry.get("role","user"); c = entry.get("content","")
152
+ if c: msgs.append({"role":r,"content":c})
153
+ msgs.append({"role":"user","content":message})
154
+ llm = _load()
155
+ stream = llm.create_chat_completion(messages=msgs, temperature=0.6, top_p=0.8,
156
+ top_k=20, max_tokens=4096, stream=True)
157
+ partial = ""
158
+ for chunk in stream:
159
+ if chunk.get("choices"):
160
+ d = chunk["choices"][0].get("delta",{}).get("content","")
161
+ if d: partial += d
162
+ yield history + [{"role":"user","content":message},
163
+ {"role":"assistant","content":partial}]
164
+ yield history + [{"role":"user","content":message},
165
+ {"role":"assistant","content":partial}]
166
 
167
+ # --- UI --------------------------------------------------------------------
168
+ def create_demo():
169
+ with gr.Blocks(title="codeMax β€” Qwen 3.6 27B Coder") as demo:
170
+ gr.Markdown("# 🧠 codeMax\n**Qwen 3.6 27B · Uncensored · Code-optimized · ZeroGPU**")
171
+ with gr.Row(equal_height=True):
172
+ with gr.Column(scale=3):
173
+ chatbot = gr.Chatbot(label="Chat", height=580,
174
+ avatar_images=(None,"https://huggingface.co/front/assets/huggingface_logo-noborder.svg"))
175
+ with gr.Row():
176
+ msg = gr.Textbox(placeholder="Ask about code, share files, or chat...",
177
+ scale=8, show_label=False, container=False)
178
+ send = gr.Button("β–Ά", scale=1, variant="primary", min_width=48)
179
+ clear_btn = gr.Button("πŸ—‘ Clear", size="sm")
180
+ with gr.Column(scale=1, elem_classes="file-upload-col"):
181
+ gr.Markdown("### πŸ“Ž Drop Files")
182
+ files = gr.File(file_count="multiple", label="Code, docs, data...",
183
+ file_types=list(CODE_EXTS)+[".json",".csv",".docx",".pdf"])
184
+ uploaded_info = gr.Markdown("_No files uploaded._")
185
+
186
+ def update_info(uploaded):
187
+ if not uploaded: return "_No files uploaded._"
188
+ names = [f.get("orig_name","?") if isinstance(f,dict) else getattr(f,"orig_name",Path(str(f)).name) for f in uploaded]
189
+ return "**Uploaded:**\n"+"\n".join(f"β€’ `{n}`" for n in names)
190
+
191
+ def stream_response(message, history, current_files):
192
+ for h in respond(message, history, current_files):
193
+ yield h
194
+
195
+ msg.submit(stream_response, [msg, chatbot, files], [chatbot]).then(lambda:"", None, [msg])
196
+ send.click(stream_response, [msg, chatbot, files], [chatbot]).then(lambda:"", None, [msg])
197
+ clear_btn.click(lambda: [], None, chatbot, queue=False)
198
+ files.change(update_info, files, uploaded_info)
199
+ return demo
200
+
201
+ if __name__ == "__main__":
202
+ demo = create_demo()
203
+ demo.queue(default_concurrency_limit=1, max_size=4)
204
+ demo.launch(css=""".file-upload-col{background:var(--background-fill-secondary);border-radius:12px;padding:16px}footer{display:none!important}""")
205
+ with gr.Row():
206
+ msg = gr.Textbox(
207
+ placeholder="Ask about your code, or just start chatting …",
208
+ scale=8,
209
+ show_label=False,
210
+ container=False,
211
+ )
212
+ send = gr.Button("β–Ά", scale=1, variant="primary", min_width=48)
213
+ with gr.Row():
214
+ clear_btn = gr.Button("πŸ—‘ Clear chat", size="sm", scale=1)
215
+
216
+ # ---- RIGHT: File upload + info ----
217
+ with gr.Column(scale=1, elem_classes="file-upload-col"):
218
+ gr.Markdown("### πŸ“Ž Drop Files")
219
+ files = gr.File(
220
+ file_count="multiple",
221
+ label="Upload code, docs, data …",
222
+ file_types=[
223
+ ".py", ".ts", ".tsx", ".js", ".jsx", ".mjs",
224
+ ".json", ".yaml", ".yml", ".toml",
225
+ ".md", ".txt", ".csv",
226
+ ".html", ".css", ".scss", ".xml", ".svg",
227
+ ".c", ".cpp", ".h", ".hpp", ".java", ".kt",
228
+ ".go", ".rs", ".rb", ".php", ".swift",
229
+ ".sql", ".sh", ".bash", ".ps1",
230
+ ".docx", ".pdf",
231
+ ".env", ".gitignore", ".cfg", ".ini", ".conf",
232
+ ".vue", ".svelte", ".dockerfile",
233
+ ],
234
+ )
235
+ gr.Markdown(
236
+ """
237
+ **Supported:** `.py` `.ts` `.js` `.json` `.md` `.docx` `.pdf` `.csv`
238
+ `.yaml` `.toml` `.html` `.css` `.sql` `.go` `.rs` `.java` + more
239
+
240
+ Files are parsed and sent as context β€” the model reads them
241
+ before answering.
242
+ """
243
+ )
244
+ uploaded_info = gr.Markdown("_No files uploaded yet._")
245
+ file_content_state = gr.State("")
246
+
247
+ # ---- Event wiring ----
248
+
249
+ def update_file_info(uploaded):
250
+ if not uploaded:
251
+ return "_No files uploaded._", ""
252
+ names = []
253
+ for f in uploaded:
254
+ if isinstance(f, dict):
255
+ names.append(f.get("orig_name", "?"))
256
+ else:
257
+ names.append(getattr(f, "orig_name", Path(str(f)).name))
258
+ label = "**Uploaded:**\n" + "\n".join(f"β€’ `{n}`" for n in names)
259
+ return label, parse_all_files(uploaded)
260
+
261
+ def respond(message, history, current_files):
262
+ """Generator wrapper that streams into the chatbot."""
263
+ for updated_history in chatbot_respond(message, history, current_files):
264
+ yield updated_history
265
+
266
+ # Trigger response on text submit or send button
267
+ msg.submit(
268
+ respond,
269
+ inputs=[msg, chatbot, files],
270
+ outputs=[chatbot],
271
+ ).then(lambda: "", None, [msg])
272
+
273
+ send.click(
274
+ respond,
275
+ inputs=[msg, chatbot, files],
276
+ outputs=[chatbot],
277
+ ).then(lambda: "", None, [msg])
278
+
279
+ # Clear chat β€” return empty list (Gradio 6 "messages" format)
280
+ clear_btn.click(lambda: [], None, chatbot, queue=False)
281
+
282
+ # File upload feedback
283
+ files.change(update_file_info, files, [uploaded_info, file_content_state])
284
+
285
+ return demo
286
+
287
+
288
+ # ===========================================================================
289
+ # LAUNCH
290
+ # ===========================================================================
291
 
292
  if __name__ == "__main__":
293
+ demo = create_demo()
294
+ demo.queue(default_concurrency_limit=1, max_size=4)
295
+ demo.launch(
296
+ css="""
297
+ .file-upload-col { background: var(--background-fill-secondary); border-radius: 12px; padding: 16px; }
298
+ footer { display: none !important; }
299
+ """,
300
+ )