aisrini commited on
Commit
f1ac8c1
Β·
verified Β·
1 Parent(s): e57a3e7

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +364 -0
app.py ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Python to JavaScript Translator using Fireworks AI (Kimi K2-0905)
3
+ Single-file Gradio app for code translation with editable output and notes.
4
+ """
5
+
6
+ import gradio as gr
7
+ import json
8
+ import requests
9
+ from typing import Optional, Tuple, Union
10
+ from pydantic import BaseModel, ValidationError
11
+ import tempfile
12
+ import os
13
+
14
+ # Configurable constants - adjust these if needed
15
+ BASE_URL = "https://api.fireworks.ai/inference/v1" # Fireworks OpenAI-compatible endpoint
16
+ MODEL_ID = "accounts/fireworks/models/kimi-k2-instruct-0905" # Kimi K2 model on Fireworks
17
+ MAX_INPUT_CHARS = 20000 # Character limit for input code
18
+
19
+ class TranslationResponse(BaseModel):
20
+ """Validated response schema from the model"""
21
+ js_code: str
22
+ notes: str
23
+
24
+ def create_system_prompt() -> str:
25
+ """Create the system prompt for strict JSON output"""
26
+ return """You are a precise code translator. Task: translate Python β†’ modern JavaScript.
27
+ Requirements:
28
+ - Output STRICT JSON with keys: "js_code" (string), "notes" (string).
29
+ - No prose outside JSON. Do not wrap in markdown. Do not include triple backticks.
30
+ - Be faithful to logic. Avoid hallucinated imports. Use idiomatic JS.
31
+ - Prefer ES modules, async/await where appropriate, and clear error handling.
32
+ - If Python uses libraries without direct JS equivalent, stub or suggest closest widely used package and explain in notes.
33
+ - If behavior is ambiguous, call it out in notes.
34
+ - Include in "notes": any assumptions, required packages, runtime constraints (Node/browser), performance/security concerns, differences in exceptions, I/O, concurrency, numeric precision, date/time, types, and mutability."""
35
+
36
+ def create_user_prompt(python_code: str) -> str:
37
+ """Create the user prompt with the Python code to translate"""
38
+ return f"""Translate this Python code to JavaScript.
39
+
40
+ Source Python:
41
+ {python_code}
42
+
43
+ Context for translator:
44
+ - Target runtime: Node.js LTS or browser-friendly (choose best; specify in notes).
45
+ - Preserve functionality and public API shape where possible.
46
+ - Replace Pythonic constructs (list comps, generators, context managers) with idiomatic JS.
47
+ - For file I/O, network calls, async behavior, propose suitable JS equivalents.
48
+ - For type clarity, add minimal JSDoc comments where helpful (no TypeScript).
49
+ - If the input is a script with a __main__ guard, structure JS with a main() and an IIFE or top-level await.
50
+ - Return ONLY JSON with "js_code" and "notes"."""
51
+
52
+ def call_fireworks_api(api_key: str, python_code: str, retry_on_invalid_json: bool = True) -> Union[TranslationResponse, str]:
53
+ """
54
+ Call Fireworks AI API for translation
55
+ Returns either TranslationResponse or error string
56
+ """
57
+ headers = {
58
+ "Authorization": f"Bearer {api_key}",
59
+ "Content-Type": "application/json"
60
+ }
61
+
62
+ payload = {
63
+ "model": MODEL_ID,
64
+ "messages": [
65
+ {"role": "system", "content": create_system_prompt()},
66
+ {"role": "user", "content": create_user_prompt(python_code)}
67
+ ],
68
+ "temperature": 0.2,
69
+ "top_p": 0.9,
70
+ "max_tokens": 4000
71
+ }
72
+
73
+ try:
74
+ response = requests.post(
75
+ f"{BASE_URL}/chat/completions",
76
+ headers=headers,
77
+ json=payload,
78
+ timeout=30
79
+ )
80
+
81
+ if response.status_code != 200:
82
+ return f"API Error: {response.status_code} - {response.text[:200]}"
83
+
84
+ result = response.json()
85
+ content = result.get("choices", [{}])[0].get("message", {}).get("content", "")
86
+
87
+ # Try to parse as JSON
88
+ try:
89
+ # Clean up common issues
90
+ content = content.strip()
91
+ if content.startswith("```json"):
92
+ content = content[7:]
93
+ if content.startswith("```"):
94
+ content = content[3:]
95
+ if content.endswith("```"):
96
+ content = content[:-3]
97
+ content = content.strip()
98
+
99
+ parsed = json.loads(content)
100
+ return TranslationResponse(**parsed)
101
+
102
+ except (json.JSONDecodeError, ValidationError) as e:
103
+ if retry_on_invalid_json:
104
+ # Retry with stricter prompt
105
+ payload["messages"].append({
106
+ "role": "assistant",
107
+ "content": content
108
+ })
109
+ payload["messages"].append({
110
+ "role": "user",
111
+ "content": "Your response was not valid JSON. Please respond with ONLY valid JSON containing 'js_code' and 'notes' fields. No markdown, no backticks, just raw JSON."
112
+ })
113
+ return call_fireworks_api(api_key, python_code, retry_on_invalid_json=False)
114
+ else:
115
+ return f"Failed to parse model response as JSON. Raw response:\n\n{content}"
116
+
117
+ except requests.exceptions.Timeout:
118
+ return "Request timed out. Please try again."
119
+ except requests.exceptions.RequestException as e:
120
+ return f"Network error: {str(e)}"
121
+ except Exception as e:
122
+ return f"Unexpected error: {str(e)}"
123
+
124
+ def translate(
125
+ api_key: str,
126
+ python_text: str,
127
+ file_upload,
128
+ want_notes: bool
129
+ ) -> Tuple[str, str, gr.update]:
130
+ """
131
+ Main translation function
132
+ Returns: (js_code, notes_markdown, status_message)
133
+ """
134
+ # Validate API key
135
+ if not api_key or not api_key.strip():
136
+ return "", "❌ **Error:** Please provide your Fireworks API key", gr.update(value="Missing API key", visible=True)
137
+
138
+ # Determine input source
139
+ python_code = ""
140
+ status_msg = ""
141
+
142
+ if file_upload is not None:
143
+ try:
144
+ with open(file_upload.name, 'r', encoding='utf-8') as f:
145
+ python_code = f.read()
146
+ status_msg = "Using uploaded file"
147
+ except Exception as e:
148
+ return "", f"❌ **Error reading file:** {str(e)}", gr.update(value="File read error", visible=True)
149
+ elif python_text and python_text.strip():
150
+ python_code = python_text
151
+ status_msg = "Using pasted code"
152
+ else:
153
+ return "", "❌ **Error:** Please provide Python code either by pasting or uploading a file", gr.update(value="No input provided", visible=True)
154
+
155
+ # Check input size
156
+ if len(python_code) > MAX_INPUT_CHARS:
157
+ truncated = python_code[:MAX_INPUT_CHARS]
158
+ warning = f"⚠️ **Warning:** Input truncated from {len(python_code)} to {MAX_INPUT_CHARS} characters\n\n"
159
+ python_code = truncated
160
+ else:
161
+ warning = ""
162
+
163
+ # Show character count
164
+ char_count = f"Input: {len(python_code)} characters"
165
+
166
+ # Call API
167
+ result = call_fireworks_api(api_key.strip(), python_code)
168
+
169
+ if isinstance(result, str):
170
+ # Error occurred
171
+ return "", f"{warning}❌ **Translation Error:**\n\n{result}", gr.update(value=f"Error: {result[:50]}...", visible=True)
172
+
173
+ # Success
174
+ js_code = result.js_code
175
+ notes = result.notes if want_notes else ""
176
+
177
+ notes_display = f"{warning}## πŸ“‹ Translation Notes\n\n{notes}" if notes else f"{warning}*Notes disabled*"
178
+
179
+ return js_code, notes_display, gr.update(value=f"βœ… Translation successful | {char_count}", visible=True)
180
+
181
+ def download_js(js_code: str) -> Tuple[str, bytes]:
182
+ """
183
+ Create downloadable JS file
184
+ Returns: (filename, content_bytes)
185
+ """
186
+ if not js_code:
187
+ return None
188
+
189
+ # Create temporary file
190
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False, encoding='utf-8') as f:
191
+ f.write(js_code)
192
+ temp_path = f.name
193
+
194
+ return temp_path
195
+
196
+ # Example snippets
197
+ EXAMPLE_SNIPPETS = {
198
+ "Simple Function": '''def greet(name):
199
+ """Greet a person with their name."""
200
+ return f"Hello, {name}!"
201
+
202
+ if __name__ == "__main__":
203
+ print(greet("World"))''',
204
+
205
+ "File I/O": '''import json
206
+
207
+ def read_config(filepath):
208
+ """Read configuration from JSON file."""
209
+ with open(filepath, 'r') as f:
210
+ config = json.load(f)
211
+ return config
212
+
213
+ def save_results(data, output_file):
214
+ """Save results to JSON file."""
215
+ with open(output_file, 'w') as f:
216
+ json.dump(data, f, indent=2)
217
+ print(f"Results saved to {output_file}")''',
218
+
219
+ "Class with Inheritance": '''class Animal:
220
+ def __init__(self, name, species):
221
+ self.name = name
222
+ self.species = species
223
+
224
+ def make_sound(self):
225
+ return "Some generic sound"
226
+
227
+ def info(self):
228
+ return f"{self.name} is a {self.species}"
229
+
230
+ class Dog(Animal):
231
+ def __init__(self, name, breed):
232
+ super().__init__(name, "Canine")
233
+ self.breed = breed
234
+
235
+ def make_sound(self):
236
+ return "Woof!"
237
+
238
+ def fetch(self, item):
239
+ return f"{self.name} fetched the {item}!"'''
240
+ }
241
+
242
+ def load_example(example_name: str) -> str:
243
+ """Load an example snippet"""
244
+ return EXAMPLE_SNIPPETS.get(example_name, "")
245
+
246
+ # Build Gradio interface
247
+ with gr.Blocks(title="Python β†’ JavaScript Translator", theme=gr.themes.Soft()) as app:
248
+ gr.Markdown(
249
+ """
250
+ # πŸβ†’πŸ“œ Python β†’ JavaScript Translator
251
+ ### Powered by Kimi K2-0905 on Fireworks AI
252
+
253
+ Translate Python code to modern, idiomatic JavaScript with detailed migration notes.
254
+ """
255
+ )
256
+
257
+ # Row 1: API Key
258
+ with gr.Row():
259
+ api_key_box = gr.Textbox(
260
+ label="πŸ”‘ Fireworks API Key",
261
+ placeholder="fw-...",
262
+ type="password",
263
+ scale=3,
264
+ info="Your key is used only for this session"
265
+ )
266
+
267
+ # Row 2: Main content
268
+ with gr.Row():
269
+ # Left column: Input
270
+ with gr.Column(scale=1):
271
+ gr.Markdown("### πŸ“₯ Input")
272
+
273
+ # Example dropdown
274
+ example_dropdown = gr.Dropdown(
275
+ choices=list(EXAMPLE_SNIPPETS.keys()),
276
+ label="Load Example",
277
+ value=None,
278
+ interactive=True
279
+ )
280
+
281
+ python_text = gr.Code(
282
+ label="Python Code",
283
+ language="python",
284
+ lines=15,
285
+ interactive=True
286
+ )
287
+
288
+ file_upload = gr.File(
289
+ label="Or upload a .py file",
290
+ file_types=[".py"],
291
+ type="filepath"
292
+ )
293
+
294
+ want_notes = gr.Checkbox(
295
+ label="Return step-by-step notes & caveats",
296
+ value=True
297
+ )
298
+
299
+ translate_btn = gr.Button(
300
+ "πŸš€ Translate to JavaScript",
301
+ variant="primary",
302
+ size="lg"
303
+ )
304
+
305
+ # Right column: Output
306
+ with gr.Column(scale=1):
307
+ gr.Markdown("### πŸ“€ Output")
308
+
309
+ with gr.Tabs():
310
+ with gr.Tab("JavaScript (editable)"):
311
+ js_output = gr.Code(
312
+ label="Generated JavaScript",
313
+ language="javascript",
314
+ lines=15,
315
+ interactive=True
316
+ )
317
+
318
+
319
+ with gr.Tab("Considerations & Notes"):
320
+ notes_output = gr.Markdown(
321
+ value="*Translation notes will appear here*"
322
+ )
323
+
324
+ # Status message (initially hidden)
325
+ status_msg = gr.Textbox(
326
+ label="Status",
327
+ visible=False,
328
+ interactive=False
329
+ )
330
+
331
+ # Footer
332
+ gr.Markdown(
333
+ """
334
+ ---
335
+ ⚠️ **Important:** Always review and test generated code. This tool provides a starting point for migration but may require manual adjustments for complex scenarios.
336
+
337
+ πŸ“ **Tips:**
338
+ - The model excels at translating algorithmic logic and data structures
339
+ - For library-specific code, check the notes for recommended JS equivalents
340
+ - Generated code targets modern ES6+ JavaScript
341
+ """
342
+ )
343
+
344
+ # Wire up interactions
345
+ example_dropdown.change(
346
+ fn=load_example,
347
+ inputs=[example_dropdown],
348
+ outputs=[python_text]
349
+ )
350
+
351
+ translate_btn.click(
352
+ fn=translate,
353
+ inputs=[api_key_box, python_text, file_upload, want_notes],
354
+ outputs=[js_output, notes_output, status_msg]
355
+ )
356
+
357
+
358
+ if __name__ == "__main__":
359
+ app.launch(
360
+ share=False,
361
+ server_name="0.0.0.0",
362
+ server_port=7860,
363
+ show_error=True
364
+ )