Spaces:
Sleeping
Sleeping
jens.luecke commited on
Commit Β·
098726a
1
Parent(s): 9a92021
Refactor iframe preview creation and enhance API key handling. Simplified iframe HTML structure and improved error messaging. Added model retrieval for non-Hugging Face providers in API key management.
Browse files
app.py
CHANGED
|
@@ -141,126 +141,20 @@ def start_preview_app():
|
|
| 141 |
|
| 142 |
|
| 143 |
def create_iframe_preview():
|
| 144 |
-
"""Create
|
| 145 |
print("π Creating iframe preview...")
|
| 146 |
# Start the preview app
|
| 147 |
success, message = start_preview_app()
|
| 148 |
|
| 149 |
if not success:
|
| 150 |
print(f"β Failed to create preview iframe: {message}")
|
| 151 |
-
return f""
|
| 152 |
-
|
| 153 |
-
<
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
# Add timestamp to force iframe refresh
|
| 160 |
-
timestamp = int(time.time() * 1000)
|
| 161 |
-
print("β
Iframe preview created successfully")
|
| 162 |
-
|
| 163 |
-
return f"""
|
| 164 |
-
<div style='width: 100%; height: 70vh; border: 1px solid #ddd; border-radius: \
|
| 165 |
-
8px; overflow: hidden; position: relative; background: #f9f9f9;'>
|
| 166 |
-
<iframe
|
| 167 |
-
id="preview-iframe-{timestamp}"
|
| 168 |
-
src="{PREVIEW_URL}"
|
| 169 |
-
width="100%"
|
| 170 |
-
height="100%"
|
| 171 |
-
frameborder="0"
|
| 172 |
-
style="border: none; background: white;"
|
| 173 |
-
sandbox="allow-same-origin allow-scripts allow-forms allow-popups \
|
| 174 |
-
allow-top-navigation allow-modals"
|
| 175 |
-
loading="eager"
|
| 176 |
-
onload="document.getElementById('loading-{timestamp}').style.display='none'"
|
| 177 |
-
onerror="document.getElementById('iframe-{timestamp}').style.display='none';\
|
| 178 |
-
document.getElementById('fallback-{timestamp}').style.display='block'"
|
| 179 |
-
></iframe>
|
| 180 |
-
|
| 181 |
-
<!-- Loading indicator -->
|
| 182 |
-
<div id="loading-{timestamp}" style="position: absolute; top: 0; left: 0; \
|
| 183 |
-
width: 100%; height: 100%; background: rgba(255,255,255,0.9); \
|
| 184 |
-
display: flex; align-items: center; justify-content: center; z-index: 10;">
|
| 185 |
-
<div style="text-align: center;">
|
| 186 |
-
<div style="border: 4px solid #f3f3f3; border-top: 4px solid #007bff; \
|
| 187 |
-
border-radius: 50%; width: 40px; height: 40px; animation: spin 1s \
|
| 188 |
-
linear infinite; margin: 0 auto 15px;"></div>
|
| 189 |
-
<p style="color: #666; margin: 0;">Loading preview...</p>
|
| 190 |
-
</div>
|
| 191 |
-
</div>
|
| 192 |
-
|
| 193 |
-
<!-- Fallback content -->
|
| 194 |
-
<div id="fallback-{timestamp}" style="display: none; padding: 20px; \
|
| 195 |
-
text-align: center; height: 100%; display: flex; flex-direction: column; \
|
| 196 |
-
justify-content: center;">
|
| 197 |
-
<div style='background: white; border-radius: 8px; padding: 30px; \
|
| 198 |
-
margin: 20px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);'>
|
| 199 |
-
<h3 style='color: #28a745; margin-bottom: 20px;'>
|
| 200 |
-
π Preview App Running!
|
| 201 |
-
</h3>
|
| 202 |
-
<p style='color: #666; margin-bottom: 25px; font-size: 16px;'>
|
| 203 |
-
Your Gradio app is running successfully.
|
| 204 |
-
</p>
|
| 205 |
-
<div style='margin: 20px 0;'>
|
| 206 |
-
<a href="{PREVIEW_URL}" target="_blank"
|
| 207 |
-
style='display: inline-block; padding: 12px 25px; background: \
|
| 208 |
-
#007bff; color: white;
|
| 209 |
-
text-decoration: none; border-radius: 6px; \
|
| 210 |
-
font-weight: bold; font-size: 16px;
|
| 211 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.2);'>
|
| 212 |
-
π Open Preview in New Tab
|
| 213 |
-
</a>
|
| 214 |
-
</div>
|
| 215 |
-
<div style='margin-top: 20px; padding: 15px; background: #f8f9fa; \
|
| 216 |
-
border-radius: 4px; border-left: 4px solid #007bff;'>
|
| 217 |
-
<p style='margin: 0; color: #495057; font-size: 14px;'>
|
| 218 |
-
<strong>URL:</strong> <code style='background: #e9ecef; \
|
| 219 |
-
padding: 2px 6px; border-radius: 3px;'>{PREVIEW_URL}</code>
|
| 220 |
-
</p>
|
| 221 |
-
</div>
|
| 222 |
-
</div>
|
| 223 |
-
</div>
|
| 224 |
-
|
| 225 |
-
<!-- Open in new tab button -->
|
| 226 |
-
<div style='position: absolute; top: 10px; right: 10px; z-index: 20;'>
|
| 227 |
-
<a href="{PREVIEW_URL}" target="_blank"
|
| 228 |
-
style='display: inline-block; padding: 8px 12px; background: \
|
| 229 |
-
rgba(0,0,0,0.8); color: white;
|
| 230 |
-
text-decoration: none; border-radius: 4px; font-size: 12px;'>
|
| 231 |
-
β Open in new tab
|
| 232 |
-
</a>
|
| 233 |
-
</div>
|
| 234 |
-
</div>
|
| 235 |
-
|
| 236 |
-
<div style='padding: 10px; text-align: center; color: #666; font-size: 12px;'>
|
| 237 |
-
Preview running on <a href="{PREVIEW_URL}" target="_blank">{PREVIEW_URL}</a>
|
| 238 |
-
<span style='margin-left: 10px; color: #999;'>
|
| 239 |
-
Last updated: {time.strftime('%H:%M:%S')}
|
| 240 |
-
</span>
|
| 241 |
-
</div>
|
| 242 |
-
|
| 243 |
-
<style>
|
| 244 |
-
@keyframes spin {{
|
| 245 |
-
0% {{ transform: rotate(0deg); }}
|
| 246 |
-
100% {{ transform: rotate(360deg); }}
|
| 247 |
-
}}
|
| 248 |
-
</style>
|
| 249 |
-
|
| 250 |
-
<script>
|
| 251 |
-
// Auto-fallback after 10 seconds if iframe doesn't load
|
| 252 |
-
setTimeout(function() {{
|
| 253 |
-
const loading = document.getElementById('loading-{timestamp}');
|
| 254 |
-
const fallback = document.getElementById('fallback-{timestamp}');
|
| 255 |
-
if (loading && loading.style.display !== 'none') {{
|
| 256 |
-
loading.style.display = 'none';
|
| 257 |
-
if (fallback) {{
|
| 258 |
-
fallback.style.display = 'flex';
|
| 259 |
-
}}
|
| 260 |
-
}}
|
| 261 |
-
}}, 10000);
|
| 262 |
-
</script>
|
| 263 |
-
"""
|
| 264 |
|
| 265 |
|
| 266 |
def is_preview_running():
|
|
@@ -283,8 +177,22 @@ def ensure_preview_running():
|
|
| 283 |
start_preview_app()
|
| 284 |
|
| 285 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
def save_api_key(provider, api_key):
|
| 287 |
-
"""Save API key to environment variable."""
|
| 288 |
if not api_key.strip():
|
| 289 |
return f"β οΈ Please enter a valid API key for {provider}"
|
| 290 |
|
|
@@ -299,8 +207,20 @@ def save_api_key(provider, api_key):
|
|
| 299 |
|
| 300 |
env_var_name = env_var_map.get(provider)
|
| 301 |
if env_var_name:
|
|
|
|
| 302 |
os.environ[env_var_name] = api_key.strip()
|
| 303 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
else:
|
| 305 |
return f"β Unknown provider: {provider}"
|
| 306 |
|
|
@@ -331,9 +251,15 @@ def get_api_key_status(selected_llm_provider="Anthropic"):
|
|
| 331 |
if llm_env_var and os.getenv(llm_env_var):
|
| 332 |
key = os.getenv(llm_env_var)
|
| 333 |
masked_key = f"{key[:8]}...{key[-4:]}" if len(key) > 12 else "***"
|
| 334 |
-
|
|
|
|
| 335 |
else:
|
| 336 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
|
| 338 |
return "\n".join(status)
|
| 339 |
|
|
@@ -506,8 +432,8 @@ class GradioUI:
|
|
| 506 |
label="Current API Key Status",
|
| 507 |
value=get_api_key_status(),
|
| 508 |
interactive=False,
|
| 509 |
-
lines=
|
| 510 |
-
max_lines=
|
| 511 |
)
|
| 512 |
|
| 513 |
# with gr.Row():
|
|
@@ -557,22 +483,33 @@ class GradioUI:
|
|
| 557 |
|
| 558 |
# Set up event handlers for API key saving
|
| 559 |
def save_and_update_status(
|
| 560 |
-
provider, api_key, selected_llm_provider="Anthropic"
|
| 561 |
):
|
| 562 |
message = save_api_key(provider, api_key)
|
| 563 |
status = get_api_key_status(selected_llm_provider)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
return message, status, "" # Clear the input field
|
| 565 |
|
| 566 |
hf_save_btn.click(
|
| 567 |
lambda key, llm_prov: save_and_update_status(
|
| 568 |
-
"Hugging Face", key, llm_prov
|
| 569 |
),
|
| 570 |
inputs=[hf_token, llm_provider],
|
| 571 |
outputs=[api_message, api_status, hf_token],
|
| 572 |
).then(lambda: gr.Textbox(visible=True), outputs=[api_message])
|
| 573 |
|
| 574 |
llm_save_btn.click(
|
| 575 |
-
lambda provider, key: save_and_update_status(
|
|
|
|
|
|
|
| 576 |
inputs=[llm_provider, llm_token],
|
| 577 |
outputs=[api_message, api_status, llm_token],
|
| 578 |
).then(lambda: gr.Textbox(visible=True), outputs=[api_message])
|
|
@@ -689,6 +626,35 @@ class GradioUI:
|
|
| 689 |
|
| 690 |
return demo
|
| 691 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 692 |
|
| 693 |
if __name__ == "__main__":
|
| 694 |
from kiss_agent import KISSAgent
|
|
|
|
| 141 |
|
| 142 |
|
| 143 |
def create_iframe_preview():
|
| 144 |
+
"""Create a simple iframe for the preview."""
|
| 145 |
print("π Creating iframe preview...")
|
| 146 |
# Start the preview app
|
| 147 |
success, message = start_preview_app()
|
| 148 |
|
| 149 |
if not success:
|
| 150 |
print(f"β Failed to create preview iframe: {message}")
|
| 151 |
+
return f'<div style="padding: 20px; color: red;">\
|
| 152 |
+
β Failed to start preview: {message}\
|
| 153 |
+
</div>'
|
| 154 |
+
|
| 155 |
+
print("β
Simple iframe preview created")
|
| 156 |
+
return f'<iframe src="{PREVIEW_URL}" width="100%" height="600px" frameborder="0">\
|
| 157 |
+
</iframe>'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
|
| 159 |
|
| 160 |
def is_preview_running():
|
|
|
|
| 177 |
start_preview_app()
|
| 178 |
|
| 179 |
|
| 180 |
+
def get_default_model_for_provider(provider: str) -> str:
|
| 181 |
+
"""Get the default model ID for a given provider."""
|
| 182 |
+
provider_model_map = {
|
| 183 |
+
"Anthropic": "anthropic/claude-sonnet-4-20250514",
|
| 184 |
+
"OpenAI": "openai/gpt-4o",
|
| 185 |
+
"Mistral": "mistral/codestral-latest",
|
| 186 |
+
"SambaNova": "sambanova/Meta-Llama-3.1-70B-Instruct",
|
| 187 |
+
"Hugging Face": "huggingface/together/Qwen/Qwen2.5-Coder-32B-Instruct",
|
| 188 |
+
}
|
| 189 |
+
return provider_model_map.get(
|
| 190 |
+
provider, "huggingface/together/Qwen/Qwen2.5-Coder-32B-Instruct"
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
def save_api_key(provider, api_key):
|
| 195 |
+
"""Save API key to environment variable and update model accordingly."""
|
| 196 |
if not api_key.strip():
|
| 197 |
return f"β οΈ Please enter a valid API key for {provider}"
|
| 198 |
|
|
|
|
| 207 |
|
| 208 |
env_var_name = env_var_map.get(provider)
|
| 209 |
if env_var_name:
|
| 210 |
+
# Always set the provider-specific API key
|
| 211 |
os.environ[env_var_name] = api_key.strip()
|
| 212 |
+
|
| 213 |
+
# For non-Hugging Face providers, also set the generic API_KEY and MODEL_ID
|
| 214 |
+
# This ensures the main agent uses the correct model and API key
|
| 215 |
+
if provider != "Hugging Face":
|
| 216 |
+
os.environ["API_KEY"] = api_key.strip()
|
| 217 |
+
os.environ["MODEL_ID"] = get_default_model_for_provider(provider)
|
| 218 |
+
return (
|
| 219 |
+
f"β
{provider} API key saved successfully \n"
|
| 220 |
+
f"Model: {get_default_model_for_provider(provider)}"
|
| 221 |
+
)
|
| 222 |
+
else:
|
| 223 |
+
return f"β
{provider} API key saved successfully"
|
| 224 |
else:
|
| 225 |
return f"β Unknown provider: {provider}"
|
| 226 |
|
|
|
|
| 251 |
if llm_env_var and os.getenv(llm_env_var):
|
| 252 |
key = os.getenv(llm_env_var)
|
| 253 |
masked_key = f"{key[:8]}...{key[-4:]}" if len(key) > 12 else "***"
|
| 254 |
+
model = get_default_model_for_provider(selected_llm_provider)
|
| 255 |
+
status.append(f"β
{selected_llm_provider}: {masked_key} (Model: {model})")
|
| 256 |
else:
|
| 257 |
+
model = get_default_model_for_provider(selected_llm_provider)
|
| 258 |
+
status.append(f"β {selected_llm_provider}: Not set (Would use: {model})")
|
| 259 |
+
|
| 260 |
+
# Show current active model
|
| 261 |
+
current_model = os.getenv("MODEL_ID", "Qwen/Qwen2.5-Coder-32B-Instruct")
|
| 262 |
+
status.append(f"π€ Current Active Model: {current_model}")
|
| 263 |
|
| 264 |
return "\n".join(status)
|
| 265 |
|
|
|
|
| 432 |
label="Current API Key Status",
|
| 433 |
value=get_api_key_status(),
|
| 434 |
interactive=False,
|
| 435 |
+
lines=4,
|
| 436 |
+
max_lines=4,
|
| 437 |
)
|
| 438 |
|
| 439 |
# with gr.Row():
|
|
|
|
| 483 |
|
| 484 |
# Set up event handlers for API key saving
|
| 485 |
def save_and_update_status(
|
| 486 |
+
provider, api_key, selected_llm_provider="Anthropic", session_state=None
|
| 487 |
):
|
| 488 |
message = save_api_key(provider, api_key)
|
| 489 |
status = get_api_key_status(selected_llm_provider)
|
| 490 |
+
|
| 491 |
+
# If this is an LLM provider (not Hugging Face), recreate the agent
|
| 492 |
+
if provider != "Hugging Face" and session_state is not None:
|
| 493 |
+
agent_message = self.recreate_agent_with_new_model(
|
| 494 |
+
session_state, provider
|
| 495 |
+
)
|
| 496 |
+
if agent_message:
|
| 497 |
+
message += f"\n{agent_message}"
|
| 498 |
+
|
| 499 |
return message, status, "" # Clear the input field
|
| 500 |
|
| 501 |
hf_save_btn.click(
|
| 502 |
lambda key, llm_prov: save_and_update_status(
|
| 503 |
+
"Hugging Face", key, llm_prov, session_state
|
| 504 |
),
|
| 505 |
inputs=[hf_token, llm_provider],
|
| 506 |
outputs=[api_message, api_status, hf_token],
|
| 507 |
).then(lambda: gr.Textbox(visible=True), outputs=[api_message])
|
| 508 |
|
| 509 |
llm_save_btn.click(
|
| 510 |
+
lambda provider, key: save_and_update_status(
|
| 511 |
+
provider, key, provider, session_state
|
| 512 |
+
),
|
| 513 |
inputs=[llm_provider, llm_token],
|
| 514 |
outputs=[api_message, api_status, llm_token],
|
| 515 |
).then(lambda: gr.Textbox(visible=True), outputs=[api_message])
|
|
|
|
| 626 |
|
| 627 |
return demo
|
| 628 |
|
| 629 |
+
def recreate_agent_with_new_model(self, session_state, provider=None):
|
| 630 |
+
"""Recreate the agent with updated model configuration."""
|
| 631 |
+
from kiss_agent import KISSAgent
|
| 632 |
+
|
| 633 |
+
# Get the new model ID if provider is specified
|
| 634 |
+
if provider and provider != "Hugging Face":
|
| 635 |
+
model_id = get_default_model_for_provider(provider)
|
| 636 |
+
|
| 637 |
+
# Get API key from provider-specific environment variable
|
| 638 |
+
env_var_map = {
|
| 639 |
+
"Anthropic": "ANTHROPIC_API_KEY",
|
| 640 |
+
"OpenAI": "OPENAI_API_KEY",
|
| 641 |
+
"SambaNova": "SAMBANOVA_API_KEY",
|
| 642 |
+
"Mistral": "MISTRAL_API_KEY",
|
| 643 |
+
}
|
| 644 |
+
|
| 645 |
+
env_var_name = env_var_map.get(provider)
|
| 646 |
+
api_key = os.getenv(env_var_name) if env_var_name else None
|
| 647 |
+
|
| 648 |
+
if not api_key:
|
| 649 |
+
return f"β No API key found for {provider}"
|
| 650 |
+
|
| 651 |
+
# Create new agent with updated model
|
| 652 |
+
new_agent = KISSAgent(model_id=model_id, api_key=api_key)
|
| 653 |
+
session_state["agent"] = new_agent
|
| 654 |
+
|
| 655 |
+
return f"π Agent updated to use {provider} model: {model_id}"
|
| 656 |
+
return ""
|
| 657 |
+
|
| 658 |
|
| 659 |
if __name__ == "__main__":
|
| 660 |
from kiss_agent import KISSAgent
|