slenk commited on
Commit
d74777b
·
verified ·
1 Parent(s): 5b9ce50

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +18 -5
  2. app.py +254 -0
  3. requirements.txt +8 -0
README.md CHANGED
@@ -1,12 +1,25 @@
1
  ---
2
- title: Codewraith
3
- emoji: 🏃
4
  colorFrom: purple
5
- colorTo: purple
6
  sdk: gradio
7
- sdk_version: 6.11.0
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: CodeWraith
3
+ emoji: 🔮
4
  colorFrom: purple
5
+ colorTo: indigo
6
  sdk: gradio
7
+ sdk_version: 4.44.1
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
  ---
12
 
13
+ # CodeWraith - Module-to-Spec Transformer
14
+
15
+ Generate technical specifications from Python source code using a fine-tuned LLM.
16
+
17
+ ## How it works
18
+
19
+ 1. Paste Python source code in the left panel
20
+ 2. Adjust sampling parameters (temperature, top_p, max tokens)
21
+ 3. Toggle RAG to include similar examples as context
22
+ 4. Click **Generate Specification**
23
+
24
+ The model is a LoRA-fine-tuned Llama that was trained on 200+ Python module / specification pairs
25
+ generated by a teacher model and verified with AST-based structural validation.
app.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """CodeWraith HuggingFace Spaces entry point.
2
+
3
+ Downloads the LoRA adapter from HF Hub and serves the Gradio interface.
4
+ Set HF_REPO_ID environment variable to point to your uploaded adapter.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import os
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ import gradio as gr
15
+
16
+ # --- Config ---
17
+
18
+ HF_REPO_ID = os.environ.get("HF_REPO_ID", "slenk/codewraith-lora-3b")
19
+ MODEL_KEY = os.environ.get("MODEL_KEY", "3b")
20
+ ADAPTER_DIR = "./adapter"
21
+
22
+ MODELS = {
23
+ "3b": "unsloth/Llama-3.2-3B-Instruct",
24
+ "8b": "unsloth/Llama-3.1-8B-Instruct",
25
+ }
26
+
27
+ SYSTEM_MESSAGE = (
28
+ "You are CodeWraith, a technical specification generator. "
29
+ "Given Python source code, produce a structured Markdown specification "
30
+ "that accurately captures all functions, classes, parameters, return types, "
31
+ "dependencies, and error handling patterns."
32
+ )
33
+
34
+ EXAMPLE_CODE = '''\
35
+ def fibonacci(n: int) -> list[int]:
36
+ """Generate the first n Fibonacci numbers."""
37
+ if n <= 0:
38
+ return []
39
+ sequence = [0, 1]
40
+ while len(sequence) < n:
41
+ sequence.append(sequence[-1] + sequence[-2])
42
+ return sequence[:n]
43
+ '''
44
+
45
+ # --- Global state ---
46
+
47
+ _model = None
48
+ _tokenizer = None
49
+ _retriever = None
50
+
51
+
52
+ # --- Model loading ---
53
+
54
+
55
+ def download_adapter():
56
+ """Download the LoRA adapter from HF Hub if not already cached."""
57
+ if Path(ADAPTER_DIR).exists() and any(Path(ADAPTER_DIR).iterdir()):
58
+ print(f"Adapter already cached at {ADAPTER_DIR}")
59
+ return
60
+
61
+ from huggingface_hub import snapshot_download
62
+
63
+ print(f"Downloading adapter from {HF_REPO_ID}...")
64
+ snapshot_download(repo_id=HF_REPO_ID, local_dir=ADAPTER_DIR)
65
+ print("Download complete.")
66
+
67
+
68
+ def load_model() -> tuple[Any, Any]:
69
+ """Load the base model with LoRA adapter."""
70
+ global _model, _tokenizer # noqa: PLW0603
71
+
72
+ if _model is not None:
73
+ return _model, _tokenizer
74
+
75
+ download_adapter()
76
+
77
+ from peft import PeftModel
78
+ from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
79
+
80
+ model_name = MODELS[MODEL_KEY]
81
+ print(f"Loading {model_name}...")
82
+
83
+ bnb_config = BitsAndBytesConfig(load_in_4bit=True)
84
+
85
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
86
+ model = AutoModelForCausalLM.from_pretrained(
87
+ model_name,
88
+ quantization_config=bnb_config,
89
+ device_map="auto",
90
+ )
91
+ model = PeftModel.from_pretrained(model, ADAPTER_DIR)
92
+ model.eval()
93
+
94
+ _model, _tokenizer = model, tokenizer
95
+ return model, tokenizer
96
+
97
+
98
+ # --- RAG ---
99
+
100
+
101
+ def init_retriever():
102
+ """Initialize retriever if training data is bundled."""
103
+ global _retriever # noqa: PLW0603
104
+
105
+ if _retriever is not None:
106
+ return _retriever
107
+
108
+ index_path = Path("chromadb")
109
+ data_path = Path("training_pairs_clean.jsonl")
110
+
111
+ if not index_path.exists() and data_path.exists():
112
+ # Build index from bundled data
113
+ try:
114
+ import chromadb
115
+ from chromadb.utils import embedding_functions
116
+
117
+ client = chromadb.PersistentClient(path=str(index_path))
118
+ ef = embedding_functions.SentenceTransformerEmbeddingFunction(
119
+ model_name="all-MiniLM-L6-v2"
120
+ )
121
+ collection = client.get_or_create_collection(
122
+ name="codewraith_specs", embedding_function=ef
123
+ )
124
+
125
+ if collection.count() == 0:
126
+ pairs = []
127
+ with data_path.open() as f:
128
+ for line in f:
129
+ if line.strip():
130
+ pairs.append(json.loads(line))
131
+
132
+ for i in range(0, len(pairs), 50):
133
+ batch = pairs[i : i + 50]
134
+ collection.add(
135
+ ids=[f"pair_{i + j}" for j in range(len(batch))],
136
+ documents=[p["input"] for p in batch],
137
+ metadatas=[{"spec": p["output"]} for p in batch],
138
+ )
139
+
140
+ _retriever = (client, collection, ef)
141
+ except Exception as e:
142
+ print(f"RAG init failed: {e}")
143
+
144
+ return _retriever
145
+
146
+
147
+ def retrieve_context(source_code: str, n_results: int = 3) -> str:
148
+ """Retrieve similar examples as context."""
149
+ ret = init_retriever()
150
+ if ret is None:
151
+ return ""
152
+
153
+ _, collection, _ = ret
154
+ results = collection.query(query_texts=[source_code], n_results=n_results)
155
+
156
+ parts = ["Here are examples of Python code and their specifications:\n"]
157
+ for i, (doc, meta) in enumerate(zip(results["documents"][0], results["metadatas"][0]), 1):
158
+ parts.append(
159
+ f"\n--- Example {i} ---\n"
160
+ f"Code:\n```python\n{doc[:1500]}\n```\n"
161
+ f"Specification:\n{meta['spec'][:1500]}\n"
162
+ )
163
+ parts.append("\nNow generate a specification for the following code:\n")
164
+ return "".join(parts)
165
+
166
+
167
+ # --- Inference ---
168
+
169
+
170
+ def generate_spec(
171
+ source_code: str,
172
+ temperature: float = 0.7,
173
+ top_p: float = 0.9,
174
+ max_tokens: int = 2048,
175
+ use_rag: bool = True,
176
+ ) -> str:
177
+ """Generate a technical specification."""
178
+ if not source_code.strip():
179
+ return "*Please paste some Python source code.*"
180
+
181
+ model, tokenizer = load_model()
182
+
183
+ user_content = source_code
184
+ if use_rag:
185
+ context = retrieve_context(source_code)
186
+ if context:
187
+ user_content = context + source_code
188
+
189
+ messages = [
190
+ {"role": "system", "content": SYSTEM_MESSAGE},
191
+ {"role": "user", "content": user_content},
192
+ ]
193
+
194
+ inputs = tokenizer.apply_chat_template(
195
+ messages, tokenize=True, add_generation_prompt=True, return_tensors="pt"
196
+ ).to(model.device)
197
+
198
+ outputs = model.generate(
199
+ input_ids=inputs,
200
+ max_new_tokens=max_tokens,
201
+ temperature=temperature,
202
+ top_p=top_p,
203
+ do_sample=True,
204
+ )
205
+
206
+ generated = outputs[0][inputs.shape[-1] :]
207
+ return tokenizer.decode(generated, skip_special_tokens=True)
208
+
209
+
210
+ # --- Gradio UI ---
211
+
212
+
213
+ def create_app():
214
+ with gr.Blocks(
215
+ title="CodeWraith - Module-to-Spec Transformer",
216
+ theme=gr.themes.Soft(),
217
+ ) as app:
218
+ gr.Markdown(
219
+ "# CodeWraith\n"
220
+ "Generate technical specifications from Python source code.\n\n"
221
+ "Paste your Python code on the left, adjust sampling parameters, "
222
+ "and click **Generate Specification**."
223
+ )
224
+
225
+ with gr.Row():
226
+ with gr.Column(scale=1):
227
+ code_input = gr.Code(
228
+ language="python",
229
+ label="Python Source Code",
230
+ value=EXAMPLE_CODE,
231
+ lines=20,
232
+ )
233
+ with gr.Row():
234
+ temperature = gr.Slider(0.0, 2.0, value=0.7, step=0.1, label="Temperature")
235
+ top_p = gr.Slider(0.0, 1.0, value=0.9, step=0.05, label="Top-p")
236
+ max_tokens = gr.Slider(256, 4096, value=2048, step=256, label="Max Tokens")
237
+ use_rag = gr.Checkbox(value=True, label="Use RAG (retrieve similar examples)")
238
+ generate_btn = gr.Button("Generate Specification", variant="primary")
239
+
240
+ with gr.Column(scale=1):
241
+ spec_output = gr.Markdown(label="Generated Specification")
242
+
243
+ generate_btn.click(
244
+ fn=generate_spec,
245
+ inputs=[code_input, temperature, top_p, max_tokens, use_rag],
246
+ outputs=spec_output,
247
+ )
248
+
249
+ return app
250
+
251
+
252
+ if __name__ == "__main__":
253
+ app = create_app()
254
+ app.launch()
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ transformers>=4.40.0
2
+ torch>=2.1.0
3
+ accelerate>=0.27.0
4
+ peft>=0.10.0
5
+ gradio>=4.0.0
6
+ chromadb>=0.5.0
7
+ sentence-transformers>=3.0.0
8
+ bitsandbytes>=0.43.0