|
|
""" |
|
|
Python to JavaScript Translator using Fireworks AI (Kimi K2-0905) |
|
|
Single-file Gradio app for code translation with editable output and notes. |
|
|
""" |
|
|
|
|
|
import gradio as gr |
|
|
import json |
|
|
import requests |
|
|
from typing import Optional, Tuple, Union |
|
|
from pydantic import BaseModel, ValidationError |
|
|
import tempfile |
|
|
import os |
|
|
|
|
|
|
|
|
BASE_URL = "https://api.fireworks.ai/inference/v1" |
|
|
MODEL_ID = "accounts/fireworks/models/kimi-k2-instruct-0905" |
|
|
MAX_INPUT_CHARS = 20000 |
|
|
|
|
|
class TranslationResponse(BaseModel): |
|
|
"""Validated response schema from the model""" |
|
|
js_code: str |
|
|
notes: str |
|
|
|
|
|
def create_system_prompt() -> str: |
|
|
"""Create the system prompt for strict JSON output""" |
|
|
return """You are a precise code translator. Task: translate Python β modern JavaScript. |
|
|
Requirements: |
|
|
- Output STRICT JSON with keys: "js_code" (string), "notes" (string). |
|
|
- No prose outside JSON. Do not wrap in markdown. Do not include triple backticks. |
|
|
- Be faithful to logic. Avoid hallucinated imports. Use idiomatic JS. |
|
|
- Prefer ES modules, async/await where appropriate, and clear error handling. |
|
|
- If Python uses libraries without direct JS equivalent, stub or suggest closest widely used package and explain in notes. |
|
|
- If behavior is ambiguous, call it out in notes. |
|
|
- 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.""" |
|
|
|
|
|
def create_user_prompt(python_code: str) -> str: |
|
|
"""Create the user prompt with the Python code to translate""" |
|
|
return f"""Translate this Python code to JavaScript. |
|
|
|
|
|
Source Python: |
|
|
{python_code} |
|
|
|
|
|
Context for translator: |
|
|
- Target runtime: Node.js LTS or browser-friendly (choose best; specify in notes). |
|
|
- Preserve functionality and public API shape where possible. |
|
|
- Replace Pythonic constructs (list comps, generators, context managers) with idiomatic JS. |
|
|
- For file I/O, network calls, async behavior, propose suitable JS equivalents. |
|
|
- For type clarity, add minimal JSDoc comments where helpful (no TypeScript). |
|
|
- If the input is a script with a __main__ guard, structure JS with a main() and an IIFE or top-level await. |
|
|
- Return ONLY JSON with "js_code" and "notes".""" |
|
|
|
|
|
def call_fireworks_api(api_key: str, python_code: str, retry_on_invalid_json: bool = True) -> Union[TranslationResponse, str]: |
|
|
""" |
|
|
Call Fireworks AI API for translation |
|
|
Returns either TranslationResponse or error string |
|
|
""" |
|
|
headers = { |
|
|
"Authorization": f"Bearer {api_key}", |
|
|
"Content-Type": "application/json" |
|
|
} |
|
|
|
|
|
payload = { |
|
|
"model": MODEL_ID, |
|
|
"messages": [ |
|
|
{"role": "system", "content": create_system_prompt()}, |
|
|
{"role": "user", "content": create_user_prompt(python_code)} |
|
|
], |
|
|
"temperature": 0.2, |
|
|
"top_p": 0.9, |
|
|
"max_tokens": 4000 |
|
|
} |
|
|
|
|
|
try: |
|
|
response = requests.post( |
|
|
f"{BASE_URL}/chat/completions", |
|
|
headers=headers, |
|
|
json=payload, |
|
|
timeout=30 |
|
|
) |
|
|
|
|
|
if response.status_code != 200: |
|
|
return f"API Error: {response.status_code} - {response.text[:200]}" |
|
|
|
|
|
result = response.json() |
|
|
content = result.get("choices", [{}])[0].get("message", {}).get("content", "") |
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
content = content.strip() |
|
|
if content.startswith("```json"): |
|
|
content = content[7:] |
|
|
if content.startswith("```"): |
|
|
content = content[3:] |
|
|
if content.endswith("```"): |
|
|
content = content[:-3] |
|
|
content = content.strip() |
|
|
|
|
|
parsed = json.loads(content) |
|
|
return TranslationResponse(**parsed) |
|
|
|
|
|
except (json.JSONDecodeError, ValidationError) as e: |
|
|
if retry_on_invalid_json: |
|
|
|
|
|
payload["messages"].append({ |
|
|
"role": "assistant", |
|
|
"content": content |
|
|
}) |
|
|
payload["messages"].append({ |
|
|
"role": "user", |
|
|
"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." |
|
|
}) |
|
|
return call_fireworks_api(api_key, python_code, retry_on_invalid_json=False) |
|
|
else: |
|
|
return f"Failed to parse model response as JSON. Raw response:\n\n{content}" |
|
|
|
|
|
except requests.exceptions.Timeout: |
|
|
return "Request timed out. Please try again." |
|
|
except requests.exceptions.RequestException as e: |
|
|
return f"Network error: {str(e)}" |
|
|
except Exception as e: |
|
|
return f"Unexpected error: {str(e)}" |
|
|
|
|
|
def translate( |
|
|
api_key: str, |
|
|
python_text: str, |
|
|
file_upload, |
|
|
want_notes: bool |
|
|
) -> Tuple[str, str, gr.update]: |
|
|
""" |
|
|
Main translation function |
|
|
Returns: (js_code, notes_markdown, status_message) |
|
|
""" |
|
|
|
|
|
if not api_key or not api_key.strip(): |
|
|
return "", "β **Error:** Please provide your Fireworks API key", gr.update(value="Missing API key", visible=True) |
|
|
|
|
|
|
|
|
python_code = "" |
|
|
status_msg = "" |
|
|
|
|
|
if file_upload is not None: |
|
|
try: |
|
|
with open(file_upload.name, 'r', encoding='utf-8') as f: |
|
|
python_code = f.read() |
|
|
status_msg = "Using uploaded file" |
|
|
except Exception as e: |
|
|
return "", f"β **Error reading file:** {str(e)}", gr.update(value="File read error", visible=True) |
|
|
elif python_text and python_text.strip(): |
|
|
python_code = python_text |
|
|
status_msg = "Using pasted code" |
|
|
else: |
|
|
return "", "β **Error:** Please provide Python code either by pasting or uploading a file", gr.update(value="No input provided", visible=True) |
|
|
|
|
|
|
|
|
if len(python_code) > MAX_INPUT_CHARS: |
|
|
truncated = python_code[:MAX_INPUT_CHARS] |
|
|
warning = f"β οΈ **Warning:** Input truncated from {len(python_code)} to {MAX_INPUT_CHARS} characters\n\n" |
|
|
python_code = truncated |
|
|
else: |
|
|
warning = "" |
|
|
|
|
|
|
|
|
char_count = f"Input: {len(python_code)} characters" |
|
|
|
|
|
|
|
|
result = call_fireworks_api(api_key.strip(), python_code) |
|
|
|
|
|
if isinstance(result, str): |
|
|
|
|
|
return "", f"{warning}β **Translation Error:**\n\n{result}", gr.update(value=f"Error: {result[:50]}...", visible=True) |
|
|
|
|
|
|
|
|
js_code = result.js_code |
|
|
notes = result.notes if want_notes else "" |
|
|
|
|
|
notes_display = f"{warning}## π Translation Notes\n\n{notes}" if notes else f"{warning}*Notes disabled*" |
|
|
|
|
|
return js_code, notes_display, gr.update(value=f"β
Translation successful | {char_count}", visible=True) |
|
|
|
|
|
def download_js(js_code: str) -> Tuple[str, bytes]: |
|
|
""" |
|
|
Create downloadable JS file |
|
|
Returns: (filename, content_bytes) |
|
|
""" |
|
|
if not js_code: |
|
|
return None |
|
|
|
|
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False, encoding='utf-8') as f: |
|
|
f.write(js_code) |
|
|
temp_path = f.name |
|
|
|
|
|
return temp_path |
|
|
|
|
|
|
|
|
EXAMPLE_SNIPPETS = { |
|
|
"Simple Function": '''def greet(name): |
|
|
"""Greet a person with their name.""" |
|
|
return f"Hello, {name}!" |
|
|
|
|
|
if __name__ == "__main__": |
|
|
print(greet("World"))''', |
|
|
|
|
|
"File I/O": '''import json |
|
|
|
|
|
def read_config(filepath): |
|
|
"""Read configuration from JSON file.""" |
|
|
with open(filepath, 'r') as f: |
|
|
config = json.load(f) |
|
|
return config |
|
|
|
|
|
def save_results(data, output_file): |
|
|
"""Save results to JSON file.""" |
|
|
with open(output_file, 'w') as f: |
|
|
json.dump(data, f, indent=2) |
|
|
print(f"Results saved to {output_file}")''', |
|
|
|
|
|
"Class with Inheritance": '''class Animal: |
|
|
def __init__(self, name, species): |
|
|
self.name = name |
|
|
self.species = species |
|
|
|
|
|
def make_sound(self): |
|
|
return "Some generic sound" |
|
|
|
|
|
def info(self): |
|
|
return f"{self.name} is a {self.species}" |
|
|
|
|
|
class Dog(Animal): |
|
|
def __init__(self, name, breed): |
|
|
super().__init__(name, "Canine") |
|
|
self.breed = breed |
|
|
|
|
|
def make_sound(self): |
|
|
return "Woof!" |
|
|
|
|
|
def fetch(self, item): |
|
|
return f"{self.name} fetched the {item}!"''' |
|
|
} |
|
|
|
|
|
def load_example(example_name: str) -> str: |
|
|
"""Load an example snippet""" |
|
|
return EXAMPLE_SNIPPETS.get(example_name, "") |
|
|
|
|
|
|
|
|
with gr.Blocks(title="Python β JavaScript Translator", theme=gr.themes.Soft()) as app: |
|
|
gr.Markdown( |
|
|
""" |
|
|
# πβπ Python β JavaScript Translator |
|
|
### Powered by Kimi K2-0905 on Fireworks AI |
|
|
|
|
|
Translate Python code to modern, idiomatic JavaScript with detailed migration notes. |
|
|
""" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Row(): |
|
|
api_key_box = gr.Textbox( |
|
|
label="π Fireworks API Key", |
|
|
placeholder="fw-...", |
|
|
type="password", |
|
|
scale=3, |
|
|
info="Your key is used only for this session" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Row(): |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### π₯ Input") |
|
|
|
|
|
|
|
|
example_dropdown = gr.Dropdown( |
|
|
choices=list(EXAMPLE_SNIPPETS.keys()), |
|
|
label="Load Example", |
|
|
value=None, |
|
|
interactive=True |
|
|
) |
|
|
|
|
|
python_text = gr.Code( |
|
|
label="Python Code", |
|
|
language="python", |
|
|
lines=15, |
|
|
interactive=True |
|
|
) |
|
|
|
|
|
file_upload = gr.File( |
|
|
label="Or upload a .py file", |
|
|
file_types=[".py"], |
|
|
type="filepath" |
|
|
) |
|
|
|
|
|
want_notes = gr.Checkbox( |
|
|
label="Return step-by-step notes & caveats", |
|
|
value=True |
|
|
) |
|
|
|
|
|
translate_btn = gr.Button( |
|
|
"π Translate to JavaScript", |
|
|
variant="primary", |
|
|
size="lg" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### π€ Output") |
|
|
|
|
|
with gr.Tabs(): |
|
|
with gr.Tab("JavaScript (editable)"): |
|
|
js_output = gr.Code( |
|
|
label="Generated JavaScript", |
|
|
language="javascript", |
|
|
lines=15, |
|
|
interactive=True |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("Considerations & Notes"): |
|
|
notes_output = gr.Markdown( |
|
|
value="*Translation notes will appear here*" |
|
|
) |
|
|
|
|
|
|
|
|
status_msg = gr.Textbox( |
|
|
label="Status", |
|
|
visible=False, |
|
|
interactive=False |
|
|
) |
|
|
|
|
|
|
|
|
gr.Markdown( |
|
|
""" |
|
|
--- |
|
|
β οΈ **Important:** Always review and test generated code. This tool provides a starting point for migration but may require manual adjustments for complex scenarios. |
|
|
|
|
|
π **Tips:** |
|
|
- The model excels at translating algorithmic logic and data structures |
|
|
- For library-specific code, check the notes for recommended JS equivalents |
|
|
- Generated code targets modern ES6+ JavaScript |
|
|
""" |
|
|
) |
|
|
|
|
|
|
|
|
example_dropdown.change( |
|
|
fn=load_example, |
|
|
inputs=[example_dropdown], |
|
|
outputs=[python_text] |
|
|
) |
|
|
|
|
|
translate_btn.click( |
|
|
fn=translate, |
|
|
inputs=[api_key_box, python_text, file_upload, want_notes], |
|
|
outputs=[js_output, notes_output, status_msg] |
|
|
) |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
app.launch( |
|
|
share=False, |
|
|
server_name="0.0.0.0", |
|
|
server_port=7860, |
|
|
show_error=True |
|
|
) |