MegaTronX commited on
Commit
b7082b3
·
verified ·
1 Parent(s): 0dafea0

Upload 4 files

Browse files
Files changed (3) hide show
  1. app.py +163 -32
  2. requirements.txt +4 -1
  3. space.yaml +1 -2
app.py CHANGED
@@ -1,48 +1,179 @@
 
 
 
 
 
 
1
  import gradio as gr
2
- import re, os, zipfile, tempfile
3
 
4
- def parse_ai_response(text):
5
- files = []
6
- pattern = r"```([a-zA-Z0-9]+)?\n([\s\S]*?)```"
7
- matches = re.findall(pattern, text)
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  lines = text.splitlines()
10
- inferred_names = []
11
  for i, line in enumerate(lines):
12
- if re.search(r"(`.*?`|\.js|\.html|\.css|\.py)", line):
13
- m = re.search(r"([A-Za-z0-9_\-]+\.[A-Za-z0-9]+)", line)
14
- if m:
15
- inferred_names.append(m.group(1))
16
 
17
- for i, (lang, code) in enumerate(matches):
18
- filename = inferred_names[i] if i < len(inferred_names) else f"file_{i+1}.{lang or 'txt'}"
19
- files.append((filename, code))
20
- return files
 
 
21
 
22
- def create_zip(files):
23
  temp_dir = tempfile.mkdtemp()
24
  zip_path = os.path.join(temp_dir, "project.zip")
25
- with zipfile.ZipFile(zip_path, "w") as zipf:
26
- for name, content in files:
27
- file_path = os.path.join(temp_dir, name)
28
- os.makedirs(os.path.dirname(file_path), exist_ok=True)
29
- with open(file_path, "w", encoding="utf-8") as f:
 
 
30
  f.write(content)
31
- zipf.write(file_path, arcname=name)
32
  return zip_path
33
 
34
- def process_response(text):
35
- files = parse_ai_response(text)
36
- zip_path = create_zip(files)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  return zip_path
38
 
39
- interface = gr.Interface(
40
- fn=process_response,
41
- inputs=gr.Textbox(lines=30, label="Paste AI project response here"),
42
- outputs=gr.File(label="Download generated ZIP"),
43
- title="AI Project Packager",
44
- description="Paste an AI-generated project (like a Node.js or Python app) and this tool will parse, assemble, and zip the files for you."
45
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  if __name__ == "__main__":
48
- interface.launch()
 
1
+ \
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 ----
11
+ from llama_cpp import Llama
12
+ from llama_cpp_agent import LlamaCppAgent, MessagesFormatterType
13
+ from llama_cpp_agent.providers import LlamaCppPythonProvider
14
 
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:
26
+ local_path = os.path.join(MODELS_DIR, filename)
27
+ if not os.path.exists(local_path):
28
+ hf_hub_download(repo_id=repo_id, filename=filename, local_dir=MODELS_DIR)
29
+ return local_path
30
+
31
+ def build_agent(model_path: str, n_ctx=8192, n_gpu_layers=81, n_batch=1024, flash_attn=True):
32
+ llm = Llama(
33
+ model_path=model_path,
34
+ n_ctx=n_ctx,
35
+ n_gpu_layers=n_gpu_layers,
36
+ n_batch=n_batch,
37
+ flash_attn=flash_attn,
38
+ )
39
+ provider = LlamaCppPythonProvider(llm)
40
+ agent = LlamaCppAgent(
41
+ provider,
42
+ system_prompt=(
43
+ "You are an expert code-packager and software project compiler. "
44
+ "Given an AI-generated project description containing code blocks and hints "
45
+ "about filenames or structure, extract each file with its most likely filename "
46
+ "and exact code content. Return ONLY a strict JSON array named manifest, "
47
+ "where each element is an object with keys 'filename' and 'content'. "
48
+ "Do not add commentary outside JSON. "
49
+ "Ensure filenames include directories if implied (e.g., 'src/main.py'). "
50
+ "Preserve code exactly as provided inside code fences."
51
+ ),
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
+ def call_llm_manifest(agent, provider, text, temperature=0.2, top_p=0.9, top_k=40, repeat_penalty=1.1, max_tokens=2048):
60
+ # Instruction prompt. Model must respond with STRICT JSON.
61
+ prompt = f"""
62
+ Read the following AI project description and return ONLY JSON.
63
+ Output schema (strict):
64
+ [{{"filename": "server.js", "content": "// code..."}}]
65
+
66
+ AI project description:
67
+ {text}
68
+ """
69
+ settings = provider.get_provider_default_settings()
70
+ settings.temperature = float(temperature)
71
+ settings.top_p = float(top_p)
72
+ settings.top_k = int(top_k)
73
+ settings.repeat_penalty = float(repeat_penalty)
74
+ settings.max_tokens = int(max_tokens)
75
+ settings.stream = False
76
+
77
+ out = agent.get_chat_response(prompt, llm_sampling_settings=settings, print_output=False)
78
+ # Try to extract a JSON array from the output robustly
79
+ json_text = None
80
+ try:
81
+ # Prefer the largest bracketed array slice
82
+ start = out.find('[')
83
+ end = out.rfind(']')
84
+ if start != -1 and end != -1 and end > start:
85
+ json_text = out[start:end+1]
86
+ manifest = json.loads(json_text)
87
+ else:
88
+ raise ValueError("No JSON array found")
89
+ except Exception:
90
+ # Fallback: single-file package of raw output for transparency
91
+ manifest = [ {"filename": JSON_FALLBACK_NAME, "content": out} ]
92
+ return manifest
93
+
94
+ def naive_regex_merge(text):
95
+ """
96
+ Heuristic backup that maps code fences to probable filenames by scanning nearby lines.
97
+ This runs only when the model output is a single fallback file OR user ticks 'Force Heuristic Merge'.
98
+ """
99
+ blocks = []
100
+ # Find all triple-backtick code blocks
101
+ code_pattern = re.compile(r"```([a-zA-Z0-9]*)\n(.*?)```", re.DOTALL)
102
+ # Find filename candidates in preceding lines such as '### STEP: server.js' or '`server.js`'
103
  lines = text.splitlines()
104
+ candidates = []
105
  for i, line in enumerate(lines):
106
+ m = re.search(r"([A-Za-z0-9_\\-./]+?\\.[A-Za-z0-9]+)", line)
107
+ if m:
108
+ candidates.append(m.group(1))
 
109
 
110
+ for idx, m in enumerate(code_pattern.finditer(text)):
111
+ lang = m.group(1) or "txt"
112
+ code = m.group(2)
113
+ filename = candidates[idx] if idx < len(candidates) else f"file_{idx+1}.{lang}"
114
+ blocks.append({"filename": filename, "content": code})
115
+ return blocks
116
 
117
+ def create_zip_from_manifest(manifest):
118
  temp_dir = tempfile.mkdtemp()
119
  zip_path = os.path.join(temp_dir, "project.zip")
120
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
121
+ for item in manifest:
122
+ fname = item.get("filename", JSON_FALLBACK_NAME).lstrip("/")
123
+ content = item.get("content", "")
124
+ fpath = os.path.join(temp_dir, fname)
125
+ os.makedirs(os.path.dirname(fpath), exist_ok=True)
126
+ with open(fpath, "w", encoding="utf-8") as f:
127
  f.write(content)
128
+ z.write(fpath, arcname=fname)
129
  return zip_path
130
 
131
+ def package_with_llm(ai_text, repo_id, filename, temperature, top_p, top_k, repeat_penalty, max_tokens, force_heuristic):
132
+ model_path = ensure_model(repo_id, filename)
133
+ agent, provider = build_agent(model_path=model_path)
134
+
135
+ manifest = call_llm_manifest(
136
+ agent, provider, ai_text,
137
+ temperature=temperature, top_p=top_p, top_k=top_k,
138
+ repeat_penalty=repeat_penalty, max_tokens=max_tokens
139
+ )
140
+
141
+ # If model failed to JSON-ify properly (single fallback) or user forces merge, try heuristic merge
142
+ if force_heuristic or (len(manifest) == 1 and manifest[0]["filename"] == JSON_FALLBACK_NAME):
143
+ heuristic = naive_regex_merge(ai_text)
144
+ if heuristic:
145
+ manifest = heuristic
146
+
147
+ zip_path = create_zip_from_manifest(manifest)
148
  return zip_path
149
 
150
+ with gr.Blocks(title="AI Project Packager (GGUF, llama.cpp)") as demo:
151
+ gr.Markdown("# AI Project Packager (GGUF, llama.cpp)")
152
+ gr.Markdown("Paste an AI-generated multi-file project description. A local GGUF model will infer filenames and contents, then return a downloadable ZIP.")
153
+
154
+ with gr.Row():
155
+ ai_text = gr.Textbox(lines=24, label="Paste AI response here")
156
+
157
+ with gr.Accordion("LLM Settings", open=False):
158
+ repo_id = gr.Textbox(value=DEFAULT_REPO_ID, label="Model repo_id")
159
+ filename = gr.Textbox(value=DEFAULT_FILENAME, label="Model filename (*.gguf)")
160
+ with gr.Row():
161
+ temperature = gr.Slider(0.0, 2.0, value=0.2, step=0.05, label="Temperature")
162
+ top_p = gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Top-p")
163
+ top_k = gr.Slider(0, 100, value=40, step=1, label="Top-k")
164
+ with gr.Row():
165
+ repeat_penalty = gr.Slider(0.8, 2.0, value=1.1, step=0.05, label="Repeat penalty")
166
+ max_tokens = gr.Slider(256, 4096, value=2048, step=32, label="Max tokens")
167
+ force_heuristic = gr.Checkbox(value=False, label="Force heuristic filename/code merge if JSON parse fails")
168
+
169
+ out_zip = gr.File(label="Download packaged ZIP")
170
+
171
+ run_btn = gr.Button("Package Project", variant="primary")
172
+ run_btn.click(
173
+ fn=package_with_llm,
174
+ inputs=[ai_text, repo_id, filename, temperature, top_p, top_k, repeat_penalty, max_tokens, force_heuristic],
175
+ outputs=[out_zip]
176
+ )
177
 
178
  if __name__ == "__main__":
179
+ demo.launch()
requirements.txt CHANGED
@@ -1 +1,4 @@
1
- gradio>=4.0
 
 
 
 
1
+ gradio==5.49.1
2
+ huggingface_hub>=0.24.0
3
+ llama-cpp-python>=0.2.90
4
+ llama-cpp-agent>=0.2.43
space.yaml CHANGED
@@ -1,10 +1,9 @@
1
- # space.yaml
2
  title: AI Project Packager
3
  emoji: 📦
4
  colorFrom: blue
5
  colorTo: purple
6
  sdk: gradio
7
- sdk_version: 4.0
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
 
 
1
  title: AI Project Packager
2
  emoji: 📦
3
  colorFrom: blue
4
  colorTo: purple
5
  sdk: gradio
6
+ sdk_version: 5.49.1
7
  app_file: app.py
8
  pinned: false
9
  license: apache-2.0