danielrosehill's picture
commit
7391b54
raw
history blame
9.93 kB
import gradio as gr
import openai
import os
from typing import Optional, Tuple
def transform_text(input_text: str, system_prompt: str, api_key: str) -> Tuple[str, str]:
"""
Transform input text using OpenAI API with the provided system prompt.
Args:
input_text: The text to transform
system_prompt: User-defined system prompt
api_key: OpenAI API key
Returns:
Tuple of (output_text, cleared_input_text)
"""
if not api_key.strip():
return "Error: Please provide your OpenAI API key.", input_text
if not input_text.strip():
return "Error: Please provide input text to transform.", input_text
if not system_prompt.strip():
return "Error: Please provide a system prompt.", input_text
try:
# Initialize OpenAI client with user's API key
client = openai.OpenAI(api_key=api_key.strip())
# Append workflow instruction to user's system prompt
enhanced_system_prompt = f"""{system_prompt.strip()}
IMPORTANT: This is a single-turn text transformation workflow interface. You must respond with the complete transformed text only, without any additional commentary, explanations, or formatting before or after the content."""
# Make API call
response = client.chat.completions.create(
model="gpt-4o-mini", # Using cost-effective model
messages=[
{"role": "system", "content": enhanced_system_prompt},
{"role": "user", "content": input_text.strip()}
],
temperature=0.7,
max_tokens=4000
)
output_text = response.choices[0].message.content
# Return output and clear input for next use
return output_text, ""
except openai.AuthenticationError:
return "Error: Invalid OpenAI API key. Please check your key and try again.", input_text
except openai.RateLimitError:
return "Error: Rate limit exceeded. Please try again later.", input_text
except openai.APIError as e:
return f"Error: OpenAI API error - {str(e)}", input_text
except Exception as e:
return f"Error: {str(e)}", input_text
def copy_to_clipboard(text: str) -> str:
"""Return the text for copying (Gradio handles the clipboard functionality)"""
return text
# Custom CSS for better styling
custom_css = """
.main-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.input-section, .output-section, .system-prompt-section {
margin-bottom: 20px;
}
.api-key-section {
background-color: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
border-left: 4px solid #007bff;
}
.transform-button {
background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
border: none;
color: white;
font-weight: bold;
font-size: 16px;
padding: 12px 24px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.copy-button {
background: linear-gradient(45deg, #56ab2f 0%, #a8e6cf 100%);
border: none;
color: white;
font-weight: bold;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
}
.warning-text {
color: #856404;
background-color: #fff3cd;
border: 1px solid #ffeaa7;
padding: 10px;
border-radius: 6px;
margin-bottom: 15px;
}
"""
# Create the Gradio interface
with gr.Blocks(css=custom_css, title="Text Transformation Model", theme=gr.themes.Soft()) as app:
gr.HTML("""
<div style="text-align: center; margin-bottom: 30px;">
<h1 style="background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-size: 2.5em; margin-bottom: 10px;">
πŸš€ Text Transformation Model
</h1>
<p style="font-size: 1.2em; color: #666; margin-bottom: 20px;">
Transform your text using AI with custom system prompts and your own OpenAI API key
</p>
</div>
""")
# API Key Section
with gr.Row():
with gr.Column():
gr.HTML('<div class="warning-text">⚠️ <strong>Privacy Notice:</strong> Your API key is stored locally in your browser and never sent to our servers.</div>')
api_key_input = gr.Textbox(
label="πŸ”‘ OpenAI API Key",
placeholder="Enter your OpenAI API key (sk-...)",
type="password",
info="Your API key is stored locally in your browser for this session."
)
# System Prompt Section
with gr.Row():
with gr.Column():
system_prompt_input = gr.Textbox(
label="🎯 System Prompt",
placeholder="Enter your system prompt here (e.g., 'Translate the following text to French', 'Summarize this text in bullet points', etc.)",
lines=4,
info="Define how the AI should transform your input text. This prompt will be enhanced automatically for single-turn processing."
)
# Input/Output Section
with gr.Row():
with gr.Column(scale=1):
input_text = gr.Textbox(
label="πŸ“ Input Text",
placeholder="Paste or type your text here...",
lines=10,
info="Enter the text you want to transform"
)
transform_btn = gr.Button(
"✨ Transform Text",
variant="primary",
size="lg",
elem_classes=["transform-button"]
)
with gr.Column(scale=1):
output_text = gr.Textbox(
label="πŸ“€ Output Text",
lines=10,
info="Transformed text will appear here",
interactive=False
)
copy_btn = gr.Button(
"πŸ“‹ Copy Output",
variant="secondary",
elem_classes=["copy-button"]
)
# Event handlers
transform_btn.click(
fn=transform_text,
inputs=[input_text, system_prompt_input, api_key_input],
outputs=[output_text, input_text] # Clear input after transformation
)
copy_btn.click(
fn=copy_to_clipboard,
inputs=[output_text],
outputs=[]
)
# Add JavaScript for clipboard functionality and local storage
gr.HTML("""
<script>
// Local storage for API key and system prompt
document.addEventListener('DOMContentLoaded', function() {
// Load saved API key and system prompt
const savedApiKey = localStorage.getItem('openai_api_key');
const savedSystemPrompt = localStorage.getItem('system_prompt');
if (savedApiKey) {
const apiKeyInput = document.querySelector('input[type="password"]');
if (apiKeyInput) apiKeyInput.value = savedApiKey;
}
if (savedSystemPrompt) {
const systemPromptInputs = document.querySelectorAll('textarea');
if (systemPromptInputs.length > 0) {
systemPromptInputs[0].value = savedSystemPrompt;
}
}
// Save API key and system prompt on change
document.addEventListener('input', function(e) {
if (e.target.type === 'password') {
localStorage.setItem('openai_api_key', e.target.value);
}
if (e.target.tagName === 'TEXTAREA' && e.target.placeholder.includes('system prompt')) {
localStorage.setItem('system_prompt', e.target.value);
}
});
// Enhanced copy functionality
document.addEventListener('click', function(e) {
if (e.target.textContent.includes('Copy Output')) {
const outputTextarea = document.querySelectorAll('textarea');
const outputText = outputTextarea[outputTextarea.length - 1].value;
if (outputText && !outputText.startsWith('Error:')) {
navigator.clipboard.writeText(outputText).then(function() {
// Visual feedback
const originalText = e.target.textContent;
e.target.textContent = 'βœ… Copied!';
e.target.style.background = 'linear-gradient(45deg, #28a745 0%, #20c997 100%)';
setTimeout(function() {
e.target.textContent = originalText;
e.target.style.background = 'linear-gradient(45deg, #56ab2f 0%, #a8e6cf 100%)';
}, 2000);
}).catch(function(err) {
console.error('Could not copy text: ', err);
alert('Copy failed. Please select and copy the text manually.');
});
} else {
alert('No valid output text to copy.');
}
}
});
});
</script>
""")
# Footer
gr.HTML("""
<div style="text-align: center; margin-top: 40px; padding: 20px; border-top: 1px solid #eee; color: #666;">
<p>πŸ’‘ <strong>Tips:</strong></p>
<ul style="text-align: left; max-width: 600px; margin: 0 auto;">
<li>Your API key and system prompt are saved locally in your browser</li>
<li>Each transformation is a single API call - the input form clears automatically</li>
<li>Use specific system prompts for better results (e.g., "Translate to Spanish", "Summarize in 3 bullet points")</li>
<li>The AI will respond with only the transformed text, no additional commentary</li>
</ul>
</div>
""")
if __name__ == "__main__":
app.launch()