aisrini's picture
Create app.py
f1ac8c1 verified
raw
history blame
12.5 kB
"""
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
# Configurable constants - adjust these if needed
BASE_URL = "https://api.fireworks.ai/inference/v1" # Fireworks OpenAI-compatible endpoint
MODEL_ID = "accounts/fireworks/models/kimi-k2-instruct-0905" # Kimi K2 model on Fireworks
MAX_INPUT_CHARS = 20000 # Character limit for input code
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 to parse as JSON
try:
# Clean up common issues
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:
# Retry with stricter prompt
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)
"""
# Validate API key
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)
# Determine input source
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)
# Check input size
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 = ""
# Show character count
char_count = f"Input: {len(python_code)} characters"
# Call API
result = call_fireworks_api(api_key.strip(), python_code)
if isinstance(result, str):
# Error occurred
return "", f"{warning}❌ **Translation Error:**\n\n{result}", gr.update(value=f"Error: {result[:50]}...", visible=True)
# Success
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
# Create temporary file
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
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, "")
# Build Gradio interface
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.
"""
)
# Row 1: API Key
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"
)
# Row 2: Main content
with gr.Row():
# Left column: Input
with gr.Column(scale=1):
gr.Markdown("### πŸ“₯ Input")
# Example dropdown
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"
)
# Right column: Output
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 message (initially hidden)
status_msg = gr.Textbox(
label="Status",
visible=False,
interactive=False
)
# Footer
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
"""
)
# Wire up interactions
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
)