jonmabe commited on
Commit
3045603
·
verified ·
1 Parent(s): 441abb3

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +259 -0
app.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tiny-LLM CLI SFT Demo - Generate Shell Commands from Natural Language
3
+
4
+ This model was fine-tuned to translate natural language instructions to CLI commands.
5
+ """
6
+
7
+ import gradio as gr
8
+ import torch
9
+ from huggingface_hub import hf_hub_download
10
+ from model import TinyLLM, MODEL_CONFIG
11
+
12
+ # Model configuration
13
+ MODEL_ID = "jonmabe/tiny-llm-cli-sft"
14
+ MODEL_FILENAME = "best_model.pt"
15
+
16
+ # Load tokenizer
17
+ try:
18
+ from tokenizers import Tokenizer
19
+ tokenizer_path = hf_hub_download(repo_id=MODEL_ID, filename="tokenizer.json")
20
+ tokenizer = Tokenizer.from_file(tokenizer_path)
21
+ print("Loaded tokenizer from model repo")
22
+ except Exception as e:
23
+ print(f"Could not load tokenizer: {e}")
24
+ tokenizer = None
25
+
26
+ # Load model
27
+ print("Downloading model...")
28
+ model_path = hf_hub_download(repo_id=MODEL_ID, filename=MODEL_FILENAME)
29
+ print(f"Model downloaded to {model_path}")
30
+
31
+ print("Loading model...")
32
+ checkpoint = torch.load(model_path, map_location="cpu", weights_only=False)
33
+
34
+ # Get config from checkpoint if available
35
+ if "config" in checkpoint and isinstance(checkpoint["config"], dict):
36
+ config = checkpoint["config"]
37
+ if "model" in config:
38
+ config = config["model"]
39
+ else:
40
+ config = MODEL_CONFIG
41
+
42
+ # Initialize model
43
+ model = TinyLLM(config)
44
+
45
+ # Load weights
46
+ if "model_state_dict" in checkpoint:
47
+ state_dict = checkpoint["model_state_dict"]
48
+ else:
49
+ state_dict = checkpoint
50
+
51
+ missing, unexpected = model.load_state_dict(state_dict, strict=False)
52
+ if missing:
53
+ print(f"Warning: Missing keys: {missing[:5]}...")
54
+ if unexpected:
55
+ print(f"Warning: Unexpected keys: {unexpected[:5]}...")
56
+
57
+ # Move to device
58
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
59
+ model = model.to(device)
60
+ model.eval()
61
+
62
+ total_params = sum(p.numel() for p in model.parameters())
63
+ print(f"Model loaded on {device} with {total_params:,} parameters")
64
+
65
+
66
+ def clean_bpe_output(text: str) -> str:
67
+ """Clean BPE artifacts from tokenizer output."""
68
+ # Replace BPE space marker with actual space
69
+ text = text.replace("Ġ", " ")
70
+ # Replace BPE newline marker with actual newline
71
+ text = text.replace("Ċ", "\n")
72
+ # Clean up extra spaces
73
+ text = " ".join(text.split())
74
+ return text.strip()
75
+
76
+
77
+ def generate_command(
78
+ instruction: str,
79
+ max_tokens: int = 50,
80
+ temperature: float = 0.7,
81
+ top_p: float = 0.9,
82
+ top_k: int = 50,
83
+ ) -> str:
84
+ """Generate a CLI command from an instruction."""
85
+
86
+ if not instruction.strip():
87
+ return "Please enter an instruction."
88
+
89
+ if tokenizer is None:
90
+ return "Tokenizer not available."
91
+
92
+ # Format prompt
93
+ prompt = f"Instruction: {instruction}\nCommand:"
94
+
95
+ # Tokenize
96
+ encoded = tokenizer.encode(prompt)
97
+ input_ids = torch.tensor([encoded.ids], dtype=torch.long).to(device)
98
+ input_len = input_ids.shape[1]
99
+
100
+ # Generate
101
+ with torch.no_grad():
102
+ output_ids = model.generate(
103
+ input_ids,
104
+ max_new_tokens=max_tokens,
105
+ temperature=temperature,
106
+ top_p=top_p,
107
+ top_k=top_k,
108
+ eos_token_id=tokenizer.token_to_id("</s>"),
109
+ )
110
+
111
+ # Decode only the generated tokens
112
+ generated_ids = output_ids[0, input_len:].tolist()
113
+ raw_output = tokenizer.decode(generated_ids)
114
+
115
+ # Clean BPE artifacts
116
+ command = clean_bpe_output(raw_output)
117
+
118
+ # Extract just the command (first line, stop at newline)
119
+ command = command.split("\n")[0].strip()
120
+
121
+ return command
122
+
123
+
124
+ # Example instructions
125
+ EXAMPLES = [
126
+ ["List all files in the current directory"],
127
+ ["Find all Python files"],
128
+ ["Show disk usage"],
129
+ ["Create a new folder called test"],
130
+ ["Search for 'error' in log files"],
131
+ ["Show the last 10 lines of a file"],
132
+ ["Count lines in a file"],
133
+ ["Copy files to another directory"],
134
+ ["Show running processes"],
135
+ ["Check available disk space"],
136
+ ]
137
+
138
+
139
+ # Create Gradio interface
140
+ with gr.Blocks(title="CLI Command Generator") as demo:
141
+ gr.Markdown("""
142
+ # 🖥️ CLI Command Generator
143
+
144
+ Translate natural language instructions to shell commands using a **54M parameter** language model.
145
+
146
+ ⚠️ **Note**: This is an early-stage SFT model. Outputs may be incomplete or incorrect.
147
+
148
+ ### How to Use
149
+ 1. Enter a natural language instruction
150
+ 2. Click "Generate" or press Enter
151
+ 3. The model will suggest a shell command
152
+ """)
153
+
154
+ with gr.Row():
155
+ with gr.Column(scale=2):
156
+ instruction_input = gr.Textbox(
157
+ label="Instruction",
158
+ placeholder="Describe what you want to do...",
159
+ lines=2,
160
+ value="List all files in the current directory"
161
+ )
162
+
163
+ with gr.Row():
164
+ with gr.Column():
165
+ max_tokens = gr.Slider(
166
+ minimum=10,
167
+ maximum=100,
168
+ value=50,
169
+ step=5,
170
+ label="Max Tokens",
171
+ )
172
+ temperature = gr.Slider(
173
+ minimum=0.1,
174
+ maximum=1.5,
175
+ value=0.7,
176
+ step=0.1,
177
+ label="Temperature",
178
+ info="Higher = more creative"
179
+ )
180
+
181
+ with gr.Column():
182
+ top_p = gr.Slider(
183
+ minimum=0.1,
184
+ maximum=1.0,
185
+ value=0.9,
186
+ step=0.05,
187
+ label="Top-p",
188
+ )
189
+ top_k = gr.Slider(
190
+ minimum=1,
191
+ maximum=100,
192
+ value=50,
193
+ step=5,
194
+ label="Top-k",
195
+ )
196
+
197
+ generate_btn = gr.Button("⚡ Generate Command", variant="primary", size="lg")
198
+
199
+ with gr.Column(scale=2):
200
+ output_command = gr.Textbox(
201
+ label="Generated Command",
202
+ lines=3,
203
+ interactive=False,
204
+ )
205
+
206
+ gr.Markdown("""
207
+ ### Common Commands Reference
208
+ - `ls` - list files
209
+ - `find` - search for files
210
+ - `grep` - search in files
211
+ - `df` - disk usage
212
+ - `du` - directory size
213
+ - `tar` - archive files
214
+ - `scp` - copy over SSH
215
+ """)
216
+
217
+ gr.Markdown("### 📝 Example Instructions")
218
+ gr.Examples(
219
+ examples=EXAMPLES,
220
+ inputs=instruction_input,
221
+ )
222
+
223
+ # Event handlers
224
+ generate_btn.click(
225
+ fn=generate_command,
226
+ inputs=[instruction_input, max_tokens, temperature, top_p, top_k],
227
+ outputs=output_command,
228
+ )
229
+
230
+ instruction_input.submit(
231
+ fn=generate_command,
232
+ inputs=[instruction_input, max_tokens, temperature, top_p, top_k],
233
+ outputs=output_command,
234
+ )
235
+
236
+ gr.Markdown("""
237
+ ---
238
+ ### About This Model
239
+
240
+ **Model**: [jonmabe/tiny-llm-cli-sft](https://huggingface.co/jonmabe/tiny-llm-cli-sft)
241
+
242
+ This is a Supervised Fine-Tuned (SFT) version of [tiny-llm-54m](https://huggingface.co/jonmabe/tiny-llm-54m),
243
+ trained on ~13,000 natural language → CLI command pairs.
244
+
245
+ #### Known Limitations
246
+ - 🔬 **Experimental**: Outputs may be incomplete or incorrect
247
+ - 📊 **Small model**: 54M parameters limits capability
248
+ - 🔧 **Needs improvement**: More training data and steps needed
249
+
250
+ #### Training Details
251
+ - **Steps**: 2,000
252
+ - **Best Val Loss**: 1.2456
253
+ - **Data**: Geddy's NL2Bash + NL2Bash benchmark + synthetic
254
+ - **Hardware**: RTX 5090, ~9 minutes
255
+ """)
256
+
257
+
258
+ if __name__ == "__main__":
259
+ demo.launch()