Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
-
import spaces
|
| 2 |
import os
|
| 3 |
import re
|
| 4 |
import json
|
| 5 |
import tempfile
|
| 6 |
import zipfile
|
| 7 |
import gradio as gr
|
|
|
|
| 8 |
from huggingface_hub import hf_hub_download
|
| 9 |
|
| 10 |
# ---- LLM: llama.cpp via llama_cpp_agent ----
|
|
@@ -15,11 +15,9 @@ from llama_cpp_agent.providers import LlamaCppPythonProvider
|
|
| 15 |
# ----------------------
|
| 16 |
# Model configuration
|
| 17 |
# ----------------------
|
| 18 |
-
# You can change these defaults in the UI
|
| 19 |
DEFAULT_REPO_ID = "tHottie/NeuralDaredevil-8B-abliterated-Q4_K_M-GGUF"
|
| 20 |
DEFAULT_FILENAME = "neuraldaredevil-8b-abliterated-q4_k_m-imat.gguf"
|
| 21 |
MODELS_DIR = "models"
|
| 22 |
-
|
| 23 |
os.makedirs(MODELS_DIR, exist_ok=True)
|
| 24 |
|
| 25 |
def ensure_model(repo_id: str, filename: str) -> str:
|
|
@@ -28,13 +26,16 @@ def ensure_model(repo_id: str, filename: str) -> str:
|
|
| 28 |
hf_hub_download(repo_id=repo_id, filename=filename, local_dir=MODELS_DIR)
|
| 29 |
return local_path
|
| 30 |
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
| 32 |
llm = Llama(
|
| 33 |
model_path=model_path,
|
| 34 |
-
n_ctx=
|
| 35 |
-
n_gpu_layers=
|
| 36 |
-
n_batch=
|
| 37 |
-
flash_attn=
|
| 38 |
)
|
| 39 |
provider = LlamaCppPythonProvider(llm)
|
| 40 |
agent = LlamaCppAgent(
|
|
@@ -52,21 +53,16 @@ def build_agent(model_path: str, n_ctx=8192, n_gpu_layers=81, n_batch=1024, flas
|
|
| 52 |
predefined_messages_formatter_type=MessagesFormatterType.GEMMA_2,
|
| 53 |
debug_output=False
|
| 54 |
)
|
| 55 |
-
return agent, provider
|
| 56 |
|
| 57 |
-
JSON_FALLBACK_NAME = "project.txt"
|
| 58 |
-
|
| 59 |
-
@spaces.GPU(duration=120)
|
| 60 |
-
def call_llm_manifest(agent, provider, text, temperature=0.2, top_p=0.9, top_k=40, repeat_penalty=1.1, max_tokens=2048):
|
| 61 |
-
# Instruction prompt. Model must respond with STRICT JSON.
|
| 62 |
prompt = f"""
|
| 63 |
-
Read the following AI project description and return ONLY JSON.
|
| 64 |
Output schema (strict):
|
| 65 |
[{{"filename": "server.js", "content": "// code..."}}]
|
| 66 |
|
| 67 |
AI project description:
|
| 68 |
-
{
|
| 69 |
"""
|
|
|
|
| 70 |
settings = provider.get_provider_default_settings()
|
| 71 |
settings.temperature = float(temperature)
|
| 72 |
settings.top_p = float(top_p)
|
|
@@ -76,38 +72,32 @@ AI project description:
|
|
| 76 |
settings.stream = False
|
| 77 |
|
| 78 |
out = agent.get_chat_response(prompt, llm_sampling_settings=settings, print_output=False)
|
| 79 |
-
|
| 80 |
-
|
| 81 |
try:
|
| 82 |
-
|
| 83 |
-
start
|
| 84 |
-
|
| 85 |
-
if start != -1 and end != -1 and end > start:
|
| 86 |
-
json_text = out[start:end+1]
|
| 87 |
-
manifest = json.loads(json_text)
|
| 88 |
else:
|
| 89 |
-
raise ValueError("No JSON
|
| 90 |
except Exception:
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
def naive_regex_merge(text):
|
| 96 |
-
"""
|
| 97 |
-
Heuristic backup that maps code fences to probable filenames by scanning nearby lines.
|
| 98 |
-
This runs only when the model output is a single fallback file OR user ticks 'Force Heuristic Merge'.
|
| 99 |
-
"""
|
| 100 |
blocks = []
|
| 101 |
-
# Find all triple-backtick code blocks
|
| 102 |
code_pattern = re.compile(r"```([a-zA-Z0-9]*)\n(.*?)```", re.DOTALL)
|
| 103 |
-
# Find filename candidates in preceding lines such as '### STEP: server.js' or '`server.js`'
|
| 104 |
lines = text.splitlines()
|
| 105 |
candidates = []
|
| 106 |
-
for
|
| 107 |
-
m = re.search(r"([A-Za-z0-9_
|
| 108 |
if m:
|
| 109 |
candidates.append(m.group(1))
|
| 110 |
-
|
| 111 |
for idx, m in enumerate(code_pattern.finditer(text)):
|
| 112 |
lang = m.group(1) or "txt"
|
| 113 |
code = m.group(2)
|
|
@@ -120,7 +110,7 @@ def create_zip_from_manifest(manifest):
|
|
| 120 |
zip_path = os.path.join(temp_dir, "project.zip")
|
| 121 |
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
|
| 122 |
for item in manifest:
|
| 123 |
-
fname = item.get("filename",
|
| 124 |
content = item.get("content", "")
|
| 125 |
fpath = os.path.join(temp_dir, fname)
|
| 126 |
os.makedirs(os.path.dirname(fpath), exist_ok=True)
|
|
@@ -129,28 +119,12 @@ def create_zip_from_manifest(manifest):
|
|
| 129 |
z.write(fpath, arcname=fname)
|
| 130 |
return zip_path
|
| 131 |
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
temperature=temperature, top_p=top_p, top_k=top_k,
|
| 139 |
-
repeat_penalty=repeat_penalty, max_tokens=max_tokens
|
| 140 |
-
)
|
| 141 |
-
|
| 142 |
-
# If model failed to JSON-ify properly (single fallback) or user forces merge, try heuristic merge
|
| 143 |
-
if force_heuristic or (len(manifest) == 1 and manifest[0]["filename"] == JSON_FALLBACK_NAME):
|
| 144 |
-
heuristic = naive_regex_merge(ai_text)
|
| 145 |
-
if heuristic:
|
| 146 |
-
manifest = heuristic
|
| 147 |
-
|
| 148 |
-
zip_path = create_zip_from_manifest(manifest)
|
| 149 |
-
return zip_path
|
| 150 |
-
|
| 151 |
-
with gr.Blocks(title="AI Project Packager (GGUF, llama.cpp)") as demo:
|
| 152 |
-
gr.Markdown("# AI Project Packager (GGUF, llama.cpp)")
|
| 153 |
-
gr.Markdown("Paste an AI-generated multi-file project description. A local GGUF model will infer filenames and contents, then return a downloadable ZIP.")
|
| 154 |
|
| 155 |
with gr.Row():
|
| 156 |
ai_text = gr.Textbox(lines=24, label="Paste AI response here")
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
import re
|
| 3 |
import json
|
| 4 |
import tempfile
|
| 5 |
import zipfile
|
| 6 |
import gradio as gr
|
| 7 |
+
import spaces
|
| 8 |
from huggingface_hub import hf_hub_download
|
| 9 |
|
| 10 |
# ---- LLM: llama.cpp via llama_cpp_agent ----
|
|
|
|
| 15 |
# ----------------------
|
| 16 |
# Model configuration
|
| 17 |
# ----------------------
|
|
|
|
| 18 |
DEFAULT_REPO_ID = "tHottie/NeuralDaredevil-8B-abliterated-Q4_K_M-GGUF"
|
| 19 |
DEFAULT_FILENAME = "neuraldaredevil-8b-abliterated-q4_k_m-imat.gguf"
|
| 20 |
MODELS_DIR = "models"
|
|
|
|
| 21 |
os.makedirs(MODELS_DIR, exist_ok=True)
|
| 22 |
|
| 23 |
def ensure_model(repo_id: str, filename: str) -> str:
|
|
|
|
| 26 |
hf_hub_download(repo_id=repo_id, filename=filename, local_dir=MODELS_DIR)
|
| 27 |
return local_path
|
| 28 |
|
| 29 |
+
# GPU context ensures model loads only when a GPU session is active
|
| 30 |
+
@spaces.GPU(duration=120)
|
| 31 |
+
def package_with_llm(ai_text, repo_id, filename, temperature, top_p, top_k, repeat_penalty, max_tokens, force_heuristic):
|
| 32 |
+
model_path = ensure_model(repo_id, filename)
|
| 33 |
llm = Llama(
|
| 34 |
model_path=model_path,
|
| 35 |
+
n_ctx=8192,
|
| 36 |
+
n_gpu_layers=81,
|
| 37 |
+
n_batch=1024,
|
| 38 |
+
flash_attn=True,
|
| 39 |
)
|
| 40 |
provider = LlamaCppPythonProvider(llm)
|
| 41 |
agent = LlamaCppAgent(
|
|
|
|
| 53 |
predefined_messages_formatter_type=MessagesFormatterType.GEMMA_2,
|
| 54 |
debug_output=False
|
| 55 |
)
|
|
|
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
prompt = f"""
|
| 58 |
+
Read the following AI project description and return ONLY JSON.
|
| 59 |
Output schema (strict):
|
| 60 |
[{{"filename": "server.js", "content": "// code..."}}]
|
| 61 |
|
| 62 |
AI project description:
|
| 63 |
+
{ai_text}
|
| 64 |
"""
|
| 65 |
+
|
| 66 |
settings = provider.get_provider_default_settings()
|
| 67 |
settings.temperature = float(temperature)
|
| 68 |
settings.top_p = float(top_p)
|
|
|
|
| 72 |
settings.stream = False
|
| 73 |
|
| 74 |
out = agent.get_chat_response(prompt, llm_sampling_settings=settings, print_output=False)
|
| 75 |
+
|
| 76 |
+
# Parse JSON output robustly
|
| 77 |
try:
|
| 78 |
+
start, end = out.find('['), out.rfind(']')
|
| 79 |
+
if start != -1 and end > start:
|
| 80 |
+
manifest = json.loads(out[start:end+1])
|
|
|
|
|
|
|
|
|
|
| 81 |
else:
|
| 82 |
+
raise ValueError("No JSON found")
|
| 83 |
except Exception:
|
| 84 |
+
manifest = [{"filename": "project.txt", "content": out}]
|
| 85 |
+
|
| 86 |
+
# Heuristic fallback if model fails
|
| 87 |
+
if force_heuristic or (len(manifest) == 1 and manifest[0]["filename"] == "project.txt"):
|
| 88 |
+
manifest = naive_regex_merge(ai_text)
|
| 89 |
+
|
| 90 |
+
return create_zip_from_manifest(manifest)
|
| 91 |
|
| 92 |
def naive_regex_merge(text):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
blocks = []
|
|
|
|
| 94 |
code_pattern = re.compile(r"```([a-zA-Z0-9]*)\n(.*?)```", re.DOTALL)
|
|
|
|
| 95 |
lines = text.splitlines()
|
| 96 |
candidates = []
|
| 97 |
+
for line in lines:
|
| 98 |
+
m = re.search(r"([A-Za-z0-9_\-./]+?\.[A-Za-z0-9]+)", line)
|
| 99 |
if m:
|
| 100 |
candidates.append(m.group(1))
|
|
|
|
| 101 |
for idx, m in enumerate(code_pattern.finditer(text)):
|
| 102 |
lang = m.group(1) or "txt"
|
| 103 |
code = m.group(2)
|
|
|
|
| 110 |
zip_path = os.path.join(temp_dir, "project.zip")
|
| 111 |
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
|
| 112 |
for item in manifest:
|
| 113 |
+
fname = item.get("filename", "project.txt").lstrip("/")
|
| 114 |
content = item.get("content", "")
|
| 115 |
fpath = os.path.join(temp_dir, fname)
|
| 116 |
os.makedirs(os.path.dirname(fpath), exist_ok=True)
|
|
|
|
| 119 |
z.write(fpath, arcname=fname)
|
| 120 |
return zip_path
|
| 121 |
|
| 122 |
+
# --------------------------
|
| 123 |
+
# Gradio Interface
|
| 124 |
+
# --------------------------
|
| 125 |
+
with gr.Blocks(title="AI Project Packager (GGUF, ZeroGPU)") as demo:
|
| 126 |
+
gr.Markdown("# AI Project Packager (GGUF, ZeroGPU)")
|
| 127 |
+
gr.Markdown("Uses ephemeral GPU power to infer filenames & package AI-generated code projects.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
with gr.Row():
|
| 130 |
ai_text = gr.Textbox(lines=24, label="Paste AI response here")
|