feat: Add OpenAI API key input option in UI
Browse files- Add API key configuration section in Settings accordion
- Implement password-protected OpenAI API key input field
- Add real-time API status display and connection testing
- Enable users to use personal OpenAI quota instead of shared limits
- Add dynamic API key validation and error handling
- Implement custom autocomplete instance creation for user keys
- Update AutoCompleteApp class with API key management methods
- Modify main function to allow startup without pre-configured keys
- Add comprehensive user documentation for API key feature
- Enhance user experience with real-time status updates
This allows users to enter their own OpenAI API key directly in the interface,
avoiding rate limits and using their personal quota while maintaining security
by not storing keys permanently.
@@ -24,6 +24,8 @@ class AutoCompleteApp:
|
|
24 |
def __init__(self):
|
25 |
self.last_request_time = 0
|
26 |
self.current_suggestions = []
|
|
|
|
|
27 |
|
28 |
def get_suggestions(
|
29 |
self, text: str, context: str, output_tokens: int = 150, user_context: str = ""
|
@@ -87,8 +89,13 @@ class AutoCompleteApp:
|
|
87 |
f"โ ๏ธ Text too long (max {settings.MAX_INPUT_LENGTH} characters)",
|
88 |
)
|
89 |
|
|
|
|
|
|
|
90 |
# Create a temporary autocomplete instance with custom prompts
|
91 |
-
temp_autocomplete = SmartAutoComplete(
|
|
|
|
|
92 |
if custom_prompts:
|
93 |
temp_autocomplete.CONTEXT_PROMPTS = custom_prompts
|
94 |
|
@@ -134,6 +141,68 @@ class AutoCompleteApp:
|
|
134 |
logger.error(f"Error inserting suggestion: {str(e)}")
|
135 |
return current_text
|
136 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
|
138 |
def create_interface():
|
139 |
"""Create and configure the Gradio interface"""
|
@@ -175,6 +244,8 @@ def create_interface():
|
|
175 |
**Intelligent text completion powered by AI**
|
176 |
|
177 |
Choose your context, enter your text, and click submit to get AI-powered completions! โจ
|
|
|
|
|
178 |
""")
|
179 |
|
180 |
with gr.Row():
|
@@ -214,6 +285,31 @@ def create_interface():
|
|
214 |
|
215 |
# Settings
|
216 |
with gr.Accordion("โ๏ธ Settings", open=False):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
output_length = gr.Slider(
|
218 |
minimum=50,
|
219 |
maximum=500,
|
@@ -341,6 +437,16 @@ def create_interface():
|
|
341 |
)
|
342 |
|
343 |
# Event handlers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
344 |
def update_suggestions(
|
345 |
text,
|
346 |
context,
|
@@ -394,6 +500,15 @@ def create_interface():
|
|
394 |
copy_update = gr.update(visible=copy_visible, value=copy_text)
|
395 |
return status, copy_update
|
396 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
397 |
# Submit button handler
|
398 |
submit_btn.click(
|
399 |
fn=update_suggestions,
|
@@ -417,15 +532,17 @@ def create_interface():
|
|
417 |
---
|
418 |
|
419 |
### ๐ฎ How to Use:
|
420 |
-
1. **
|
421 |
-
2. **
|
422 |
-
3. **
|
423 |
-
4. **
|
424 |
-
5. **
|
425 |
-
6. **
|
426 |
-
7. **
|
|
|
427 |
|
428 |
### ๐ Pro Tips:
|
|
|
429 |
- **Context Window**: Add background info, previous conversations, or references to improve suggestions
|
430 |
- **Email**: Try starting with "Dear..." or "I hope..." + add meeting context
|
431 |
- **Creative**: Start with "Once upon a time..." + add story background
|
@@ -450,12 +567,15 @@ def create_interface():
|
|
450 |
def main():
|
451 |
"""Main function to run the application"""
|
452 |
try:
|
453 |
-
# Check API configuration
|
454 |
if not settings.validate_api_keys():
|
455 |
-
logger.
|
456 |
-
|
457 |
-
|
458 |
-
|
|
|
|
|
|
|
459 |
|
460 |
logger.info("Starting Smart Auto-Complete application...")
|
461 |
|
|
|
24 |
def __init__(self):
|
25 |
self.last_request_time = 0
|
26 |
self.current_suggestions = []
|
27 |
+
self.user_api_key = None
|
28 |
+
self.custom_autocomplete = None
|
29 |
|
30 |
def get_suggestions(
|
31 |
self, text: str, context: str, output_tokens: int = 150, user_context: str = ""
|
|
|
89 |
f"โ ๏ธ Text too long (max {settings.MAX_INPUT_LENGTH} characters)",
|
90 |
)
|
91 |
|
92 |
+
# Use the active autocomplete instance (user's custom or default)
|
93 |
+
active_autocomplete = self.get_active_autocomplete()
|
94 |
+
|
95 |
# Create a temporary autocomplete instance with custom prompts
|
96 |
+
temp_autocomplete = SmartAutoComplete(
|
97 |
+
active_autocomplete.settings if active_autocomplete else settings
|
98 |
+
)
|
99 |
if custom_prompts:
|
100 |
temp_autocomplete.CONTEXT_PROMPTS = custom_prompts
|
101 |
|
|
|
141 |
logger.error(f"Error inserting suggestion: {str(e)}")
|
142 |
return current_text
|
143 |
|
144 |
+
def update_api_key(self, api_key: str) -> str:
|
145 |
+
"""Update the OpenAI API key and reinitialize the autocomplete engine"""
|
146 |
+
try:
|
147 |
+
if not api_key or not api_key.strip():
|
148 |
+
self.user_api_key = None
|
149 |
+
self.custom_autocomplete = None
|
150 |
+
return "๐ Reverted to default configuration"
|
151 |
+
|
152 |
+
# Validate the API key format
|
153 |
+
if not api_key.startswith("sk-"):
|
154 |
+
return "โ Invalid API key format. OpenAI keys start with 'sk-'"
|
155 |
+
|
156 |
+
# Create a custom settings object with the user's API key
|
157 |
+
from config.settings import AppSettings
|
158 |
+
|
159 |
+
custom_settings = AppSettings()
|
160 |
+
custom_settings.OPENAI_API_KEY = api_key.strip()
|
161 |
+
|
162 |
+
# Create a new autocomplete instance with the custom settings
|
163 |
+
self.custom_autocomplete = SmartAutoComplete(custom_settings)
|
164 |
+
self.user_api_key = api_key.strip()
|
165 |
+
|
166 |
+
return "โ
API key updated successfully! Using your personal quota."
|
167 |
+
|
168 |
+
except Exception as e:
|
169 |
+
logger.error(f"Error updating API key: {str(e)}")
|
170 |
+
return f"โ Error updating API key: {str(e)}"
|
171 |
+
|
172 |
+
def test_api_connection(self, api_key: str = None) -> str:
|
173 |
+
"""Test the API connection with the current or provided key"""
|
174 |
+
try:
|
175 |
+
# Use custom autocomplete if user has provided a key, otherwise use default
|
176 |
+
test_autocomplete = (
|
177 |
+
self.custom_autocomplete if self.user_api_key else autocomplete
|
178 |
+
)
|
179 |
+
|
180 |
+
if api_key and api_key.strip():
|
181 |
+
# Test with the provided key
|
182 |
+
from config.settings import AppSettings
|
183 |
+
|
184 |
+
test_settings = AppSettings()
|
185 |
+
test_settings.OPENAI_API_KEY = api_key.strip()
|
186 |
+
test_autocomplete = SmartAutoComplete(test_settings)
|
187 |
+
|
188 |
+
# Test with a simple completion
|
189 |
+
test_result = test_autocomplete.get_suggestions(
|
190 |
+
text="Hello, this is a test", context="linkedin", max_tokens=10
|
191 |
+
)
|
192 |
+
|
193 |
+
if test_result and len(test_result) > 0:
|
194 |
+
return "โ
API connection successful!"
|
195 |
+
else:
|
196 |
+
return "โ API connection failed - no response received"
|
197 |
+
|
198 |
+
except Exception as e:
|
199 |
+
logger.error(f"API connection test failed: {str(e)}")
|
200 |
+
return f"โ API connection test failed: {str(e)}"
|
201 |
+
|
202 |
+
def get_active_autocomplete(self):
|
203 |
+
"""Get the currently active autocomplete instance"""
|
204 |
+
return self.custom_autocomplete if self.user_api_key else autocomplete
|
205 |
+
|
206 |
|
207 |
def create_interface():
|
208 |
"""Create and configure the Gradio interface"""
|
|
|
244 |
**Intelligent text completion powered by AI**
|
245 |
|
246 |
Choose your context, enter your text, and click submit to get AI-powered completions! โจ
|
247 |
+
|
248 |
+
๐ก **Tip**: Add your own OpenAI API key in Settings to use your personal quota and avoid rate limits.
|
249 |
""")
|
250 |
|
251 |
with gr.Row():
|
|
|
285 |
|
286 |
# Settings
|
287 |
with gr.Accordion("โ๏ธ Settings", open=False):
|
288 |
+
# API Key Configuration
|
289 |
+
with gr.Group():
|
290 |
+
gr.Markdown("### ๐ API Configuration")
|
291 |
+
openai_key_input = gr.Textbox(
|
292 |
+
label="OpenAI API Key (Optional)",
|
293 |
+
placeholder="sk-... (Enter your OpenAI API key to use your own quota)",
|
294 |
+
type="password",
|
295 |
+
value="",
|
296 |
+
info="Your API key is only used for this session and not stored permanently.",
|
297 |
+
)
|
298 |
+
|
299 |
+
api_status = gr.Textbox(
|
300 |
+
label="API Status",
|
301 |
+
value="Using default configuration"
|
302 |
+
if settings.OPENAI_API_KEY
|
303 |
+
else "No API key configured",
|
304 |
+
interactive=False,
|
305 |
+
lines=1,
|
306 |
+
)
|
307 |
+
|
308 |
+
test_api_btn = gr.Button("๐งช Test API Connection", size="sm")
|
309 |
+
|
310 |
+
gr.Markdown("---")
|
311 |
+
|
312 |
+
# Output Settings
|
313 |
output_length = gr.Slider(
|
314 |
minimum=50,
|
315 |
maximum=500,
|
|
|
437 |
)
|
438 |
|
439 |
# Event handlers
|
440 |
+
def update_api_key(api_key):
|
441 |
+
"""Handle API key updates"""
|
442 |
+
status = app_instance.update_api_key(api_key)
|
443 |
+
return status
|
444 |
+
|
445 |
+
def test_api_connection(api_key):
|
446 |
+
"""Handle API connection testing"""
|
447 |
+
status = app_instance.test_api_connection(api_key)
|
448 |
+
return status
|
449 |
+
|
450 |
def update_suggestions(
|
451 |
text,
|
452 |
context,
|
|
|
500 |
copy_update = gr.update(visible=copy_visible, value=copy_text)
|
501 |
return status, copy_update
|
502 |
|
503 |
+
# API Key event handlers
|
504 |
+
openai_key_input.change(
|
505 |
+
fn=update_api_key, inputs=[openai_key_input], outputs=[api_status]
|
506 |
+
)
|
507 |
+
|
508 |
+
test_api_btn.click(
|
509 |
+
fn=test_api_connection, inputs=[openai_key_input], outputs=[api_status]
|
510 |
+
)
|
511 |
+
|
512 |
# Submit button handler
|
513 |
submit_btn.click(
|
514 |
fn=update_suggestions,
|
|
|
532 |
---
|
533 |
|
534 |
### ๐ฎ How to Use:
|
535 |
+
1. **Add your API key** (optional) - Enter your OpenAI API key in Settings to use your own quota
|
536 |
+
2. **Select your context** (Email, Creative, or LinkedIn)
|
537 |
+
3. **Add context information** (optional) - background info, references, or previous context
|
538 |
+
4. **Enter your text** in the main text area
|
539 |
+
5. **Adjust output length** (50-500 tokens) in settings
|
540 |
+
6. **Customize prompts** (optional) - edit AI prompts in "Edit Context Prompts" section
|
541 |
+
7. **Click "Get Suggestions"** to generate completions
|
542 |
+
8. **Copy from the generated text box** (Select All + Ctrl+C/Cmd+C)
|
543 |
|
544 |
### ๐ Pro Tips:
|
545 |
+
- **API Key**: Add your own OpenAI API key to use your personal quota and avoid rate limits
|
546 |
- **Context Window**: Add background info, previous conversations, or references to improve suggestions
|
547 |
- **Email**: Try starting with "Dear..." or "I hope..." + add meeting context
|
548 |
- **Creative**: Start with "Once upon a time..." + add story background
|
|
|
567 |
def main():
|
568 |
"""Main function to run the application"""
|
569 |
try:
|
570 |
+
# Check API configuration - now optional since users can provide their own keys
|
571 |
if not settings.validate_api_keys():
|
572 |
+
logger.warning(
|
573 |
+
"No default API keys found. Users can provide their own keys in the UI."
|
574 |
+
)
|
575 |
+
print("โ ๏ธ No default API keys configured.")
|
576 |
+
print("Users can enter their own OpenAI API key in the Settings section.")
|
577 |
+
else:
|
578 |
+
logger.info("Default API keys found and validated.")
|
579 |
|
580 |
logger.info("Starting Smart Auto-Complete application...")
|
581 |
|