Spaces:
Runtime error
Runtime error
| # chatbot_demo.py | |
| import gradio as gr | |
| import logging | |
| from datetime import datetime | |
| from typing import List, Tuple, Optional | |
| import os | |
| import socket | |
| from kallam.app.chatbot_manager import ChatbotManager | |
| mgr = ChatbotManager(log_level="INFO") | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # INLINE SVG for icons | |
| CABBAGE_SVG = """ | |
| <svg width="128" height="128" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" | |
| role="img" aria-label="cabbage"> | |
| <defs> | |
| <linearGradient id="leaf" x1="0" x2="0" y1="0" y2="1"> | |
| <stop offset="0%" stop-color="#9be58b"/> | |
| <stop offset="100%" stop-color="#5cc46a"/> | |
| </linearGradient> | |
| </defs> | |
| <g fill="none"> | |
| <circle cx="32" cy="32" r="26" fill="url(#leaf)"/> | |
| <path d="M12,34 C18,28 22,26 28,27 C34,28 38,32 44,30 C48,29 52,26 56,22" | |
| stroke="#2e7d32" stroke-width="3" stroke-linecap="round"/> | |
| <path d="M10,40 C18,36 22,42 28,42 C34,42 38,38 44,40 C50,42 54,40 58,36" | |
| stroke="#2e7d32" stroke-width="3" stroke-linecap="round"/> | |
| <path d="M24 24 C28 20 36 20 40 24 C44 28 44 36 32 38 C20 36 20 28 24 24 Z" | |
| fill="#bff5b6"/> | |
| </g> | |
| </svg> | |
| """ | |
| # ----------------------- | |
| # Core handlers | |
| # ----------------------- | |
| def _session_status(session_id: str) -> str: | |
| """Get current session status using mgr.get_session()""" | |
| if not session_id: | |
| return "🔴 **No Active Session** - Click **New Session** to start" | |
| try: | |
| # Use same method as simple app | |
| now = datetime.now() | |
| s = mgr.get_session(session_id) or {} | |
| ts = s.get("timestamp", now.strftime("%d %b %Y | %I:%M %p")) | |
| model = s.get("model_used", "Orchestrated SEA-Lion") | |
| total = s.get("total_messages", 0) | |
| saved_memories = s.get("saved_memories") or "General consultation" | |
| return f""" | |
| 🟢 **Session:** `{session_id[:8]}...` | |
| 🏥 **Profile:** {saved_memories[:50]}{"..." if len(saved_memories) > 50 else ""} | |
| 📅 **Created:** {ts} | |
| 💬 **Messages:** {total} | |
| 🤖 **Model:** {model} | |
| """.strip() | |
| except Exception as e: | |
| logger.error(f"Error getting session status: {e}") | |
| return f"❌ **Error loading session:** {session_id[:8]}..." | |
| def start_new_session(health_profile: str = ""): | |
| """Create new session using mgr - same as simple app""" | |
| try: | |
| sid = mgr.start_session(saved_memories=health_profile.strip() or None) | |
| status = _session_status(sid) | |
| # Initial welcome message | |
| welcome_msg = { | |
| "role": "assistant", | |
| "content": """Hello! I'm KaLLaM 🌿, your caring AI health advisor 💖 | |
| I can communicate in both **Thai** and **English**. I'm here to support your health and well-being with personalized advice. How are you feeling today? 😊 | |
| สวัสดีค่ะ! ฉันชื่อกะหล่ำ 🌿 เป็นที่ปรึกษาด้านสุขภาพ AI ที่จะคอยดูแลคุณ 💖 ฉันสามารถสื่อสารได้ทั้งภาษาไทยและภาษาอังกฤษ วันนี้รู้สึกยังไงบ้างคะ? 😊""" | |
| } | |
| history = [welcome_msg] | |
| result_msg = f"✅ **New Session Created Successfully!**\n\n🆔 Session ID: `{sid}`" | |
| if health_profile.strip(): | |
| result_msg += f"\n🏥 **Health Profile:** Applied successfully" | |
| return sid, history, "", status, result_msg | |
| except Exception as e: | |
| logger.error(f"Error creating new session: {e}") | |
| return "", [], "", "❌ **Failed to create session**", f"❌ **Error:** {e}" | |
| def send_message(user_msg: str, history: list, session_id: str): | |
| """Send message using mgr - same as simple app""" | |
| # Defensive: auto-create session if missing (same as simple app) | |
| if not session_id: | |
| logger.warning("No session found, auto-creating...") | |
| sid, history, _, status, _ = start_new_session("") | |
| history.append({"role": "assistant", "content": "🔄 **New session created automatically.** You can now continue chatting!"}) | |
| return history, "", sid, status | |
| if not user_msg.strip(): | |
| return history, "", session_id, _session_status(session_id) | |
| try: | |
| # Add user message | |
| history = history + [{"role": "user", "content": user_msg}] | |
| # Get bot response using mgr (same as simple app) | |
| bot_response = mgr.handle_message( | |
| session_id=session_id, | |
| user_message=user_msg | |
| ) | |
| # Add bot response | |
| history = history + [{"role": "assistant", "content": bot_response}] | |
| return history, "", session_id, _session_status(session_id) | |
| except Exception as e: | |
| logger.error(f"Error processing message: {e}") | |
| error_msg = {"role": "assistant", "content": f"❌ **Error:** Unable to process your message. Please try again.\n\nDetails: {e}"} | |
| history = history + [error_msg] | |
| return history, "", session_id, _session_status(session_id) | |
| def update_health_profile(session_id: str, health_profile: str): | |
| """Update health profile for current session using mgr's database access""" | |
| if not session_id: | |
| return "❌ **No active session**", _session_status(session_id) | |
| if not health_profile.strip(): | |
| return "❌ **Please provide health information**", _session_status(session_id) | |
| try: | |
| # Use mgr's database path (same pattern as simple app would use) | |
| from kallam.infra.db import sqlite_conn | |
| with sqlite_conn(str(mgr.db_path)) as conn: | |
| conn.execute( | |
| "UPDATE sessions SET saved_memories = ?, last_activity = ? WHERE session_id = ?", | |
| (health_profile.strip(), datetime.now().isoformat(), session_id), | |
| ) | |
| result = f"✅ **Health Profile Updated Successfully!**\n\n📝 **Updated Information:** {health_profile.strip()[:100]}{'...' if len(health_profile.strip()) > 100 else ''}" | |
| return result, _session_status(session_id) | |
| except Exception as e: | |
| logger.error(f"Error updating health profile: {e}") | |
| return f"❌ **Error updating profile:** {e}", _session_status(session_id) | |
| def clear_session(session_id: str): | |
| """Clear current session using mgr""" | |
| if not session_id: | |
| return "", [], "", "🔴 **No active session to clear**", "❌ **No active session**" | |
| try: | |
| # Check if mgr has delete_session method, otherwise handle gracefully | |
| if hasattr(mgr, 'delete_session'): | |
| mgr.delete_session(session_id) | |
| else: | |
| # Fallback: just clear the session data if method doesn't exist | |
| logger.warning("delete_session method not available, clearing session state only") | |
| return "", [], "", "🔴 **Session cleared - Create new session to continue**", f"✅ **Session `{session_id[:8]}...` cleared successfully**" | |
| except Exception as e: | |
| logger.error(f"Error clearing session: {e}") | |
| return session_id, [], "", _session_status(session_id), f"❌ **Error clearing session:** {e}" | |
| def force_summary(session_id: str): | |
| """Force summary using mgr (same as simple app)""" | |
| if not session_id: | |
| return "❌ No active session." | |
| try: | |
| if hasattr(mgr, 'summarize_session'): | |
| s = mgr.summarize_session(session_id) | |
| return f"📋 Summary updated:\n\n{s}" | |
| else: | |
| return "❌ Summarize function not available." | |
| except Exception as e: | |
| return f"❌ Failed to summarize: {e}" | |
| def lock_inputs(): | |
| """Lock inputs during processing (same as simple app)""" | |
| return gr.update(interactive=False), gr.update(interactive=False) | |
| def unlock_inputs(): | |
| """Unlock inputs after processing (same as simple app)""" | |
| return gr.update(interactive=True), gr.update(interactive=True) | |
| # ----------------------- | |
| # UI with improved architecture and greenish cream styling - LIGHT MODE DEFAULT | |
| # ----------------------- | |
| def create_app() -> gr.Blocks: | |
| # Enhanced CSS with greenish cream color scheme, fixed positioning, and light mode defaults | |
| custom_css = """ | |
| :root { | |
| --kallam-primary: #659435; | |
| --kallam-secondary: #5ea0bd; | |
| --kallam-accent: #b8aa54; | |
| --kallam-light: #f8fdf5; | |
| --kallam-dark: #2d3748; | |
| --kallam-cream: #f5f7f0; | |
| --kallam-green-cream: #e8f4e0; | |
| --kallam-border-cream: #d4e8c7; | |
| --shadow-soft: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
| --shadow-medium: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| --border-radius: 12px; | |
| --transition: all 0.3s ease; | |
| } | |
| /* Force light mode styles - Override any dark mode defaults */ | |
| body, .gradio-container, .app { | |
| background-color: #ffffff !important; | |
| color: #2d3748 !important; | |
| } | |
| /* Ensure light backgrounds for all major containers */ | |
| .block, .form, .gap { | |
| background-color: #ffffff !important; | |
| color: #2d3748 !important; | |
| } | |
| /* Light mode for input elements */ | |
| input, textarea, select { | |
| background-color: #ffffff !important; | |
| border: 1px solid #d1d5db !important; | |
| color: #2d3748 !important; | |
| } | |
| input:focus, textarea:focus, select:focus { | |
| border-color: var(--kallam-primary) !important; | |
| box-shadow: 0 0 0 3px rgba(101, 148, 53, 0.1) !important; | |
| } | |
| /* Ensure dark mode styles don't override in light mode */ | |
| html:not(.dark) .dark { | |
| display: none !important; | |
| } | |
| .gradio-container { | |
| max-width: 100% !important; | |
| width: 100% !important; | |
| margin: 0 auto !important; | |
| min-height: 100vh; | |
| background-color: #ffffff !important; | |
| } | |
| .main-layout { | |
| display: flex !important; | |
| min-height: calc(100vh - 2rem) !important; | |
| gap: 1.5rem !important; | |
| } | |
| .fixed-sidebar { | |
| width: 320px !important; | |
| min-width: 320px !important; | |
| max-width: 320px !important; | |
| background: #ffffff !important; | |
| backdrop-filter: blur(10px) !important; | |
| border-radius: var(--border-radius) !important; | |
| border: 3px solid var(--kallam-primary) !important; | |
| box-shadow: var(--shadow-soft) !important; | |
| padding: 1.5rem !important; | |
| height: fit-content !important; | |
| position: sticky !important; | |
| top: 1rem !important; | |
| overflow: visible !important; | |
| } | |
| .main-content { | |
| flex: 1 !important; | |
| min-width: 0 !important; | |
| } | |
| .kallam-header { | |
| background: linear-gradient(135deg, var(--kallam-secondary) 0%, var(--kallam-primary) 50%, var(--kallam-accent) 100%); | |
| border-radius: var(--border-radius); | |
| padding: 2rem; | |
| margin-bottom: 1.5rem; | |
| text-align: center; | |
| box-shadow: var(--shadow-medium); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .kallam-header h1 { | |
| color: white !important; | |
| font-size: 2.5rem !important; | |
| font-weight: 700 !important; | |
| margin: 0 !important; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.2); | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .kallam-subtitle { | |
| color: rgba(255,255,255,0.9) !important; | |
| font-size: 1.1rem !important; | |
| margin-top: 0.5rem !important; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .btn { | |
| border-radius: 8px !important; | |
| font-weight: 600 !important; | |
| padding: 0.75rem 1.5rem !important; | |
| transition: var(--transition) !important; | |
| border: none !important; | |
| box-shadow: var(--shadow-soft) !important; | |
| cursor: pointer !important; | |
| } | |
| .btn:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: var(--shadow-medium) !important; | |
| } | |
| .btn.btn-primary { | |
| background: linear-gradient(135deg, var(--kallam-primary) 0%, var(--kallam-secondary) 100%) !important; | |
| color: white !important; | |
| } | |
| .btn.btn-secondary { | |
| background: #f8f9fa !important; | |
| color: #2d3748 !important; | |
| border: 1px solid #d1d5db !important; | |
| } | |
| .chat-container { | |
| background: var(--kallam-green-cream) !important; | |
| border-radius: var(--border-radius) !important; | |
| border: 2px solid var(--kallam-border-cream) !important; | |
| box-shadow: var(--shadow-medium) !important; | |
| overflow: hidden !important; | |
| } | |
| .session-status-container .markdown { | |
| margin: 0 !important; | |
| padding: 0 !important; | |
| font-size: 0.85rem !important; | |
| line-height: 1.4 !important; | |
| overflow-wrap: break-word !important; | |
| word-break: break-word !important; | |
| } | |
| @media (max-width: 1200px) { | |
| .main-layout { | |
| flex-direction: column !important; | |
| } | |
| .fixed-sidebar { | |
| width: 100% !important; | |
| min-width: 100% !important; | |
| max-width: 100% !important; | |
| position: static !important; | |
| } | |
| } | |
| """ | |
| # Create a light theme with explicit light mode settings | |
| light_theme = gr.themes.Soft( # type: ignore | |
| primary_hue="green", | |
| secondary_hue="blue", | |
| neutral_hue="slate" | |
| ).set( | |
| # Force light mode colors | |
| body_background_fill="white", | |
| body_text_color="#2d3748", | |
| background_fill_primary="white", | |
| background_fill_secondary="#f8f9fa", | |
| border_color_primary="#d1d5db", | |
| border_color_accent="#659435", | |
| button_primary_background_fill="#659435", | |
| button_primary_text_color="white", | |
| button_secondary_background_fill="#f8f9fa", | |
| button_secondary_text_color="#2d3748" | |
| ) | |
| with gr.Blocks( | |
| title="🥬 KaLLaM - Thai Motivational Therapeutic Advisor", | |
| theme=light_theme, | |
| css=custom_css, | |
| js=""" | |
| function() { | |
| // Force light mode on load by removing any dark classes and setting light preferences | |
| document.documentElement.classList.remove('dark'); | |
| document.body.classList.remove('dark'); | |
| // Set data attributes for light mode | |
| document.documentElement.setAttribute('data-theme', 'light'); | |
| // Override any system preferences for dark mode | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| @media (prefers-color-scheme: dark) { | |
| :root { | |
| color-scheme: light !important; | |
| } | |
| body, .gradio-container { | |
| background-color: white !important; | |
| color: #2d3748 !important; | |
| } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| } | |
| """ | |
| ) as app: | |
| # State management - same as simple app | |
| session_id = gr.State(value="") | |
| # Header | |
| gr.HTML(f""" | |
| <div class="kallam-header"> | |
| <div style="display: flex; align-items: center; justify-content: flex-start; gap: 2rem; padding: 0 2rem;"> | |
| {CABBAGE_SVG} | |
| <div style="text-align: left;"> | |
| <h1 style="text-align: left; margin: 0;">KaLLaM</h1> | |
| <p class="kallam-subtitle" style="text-align: left; margin: 0.5rem 0 0 0;">Thai Motivational Therapeutic Advisor</p> | |
| </div> | |
| </div> | |
| </div> | |
| """) | |
| # Main layout | |
| with gr.Row(elem_classes=["main-layout"]): | |
| # Sidebar with enhanced styling | |
| with gr.Column(scale=1, elem_classes=["fixed-sidebar"]): | |
| gr.HTML(""" | |
| <div style="text-align: center; padding: 0.5rem 0 1rem 0;"> | |
| <h3 style="color: #659435; margin: 0; font-size: 1.2rem;">Controls</h3> | |
| <p style="color: #666; margin: 0.25rem 0 0 0; font-size: 0.9rem;">Manage session and health profile</p> | |
| </div> | |
| """) | |
| with gr.Group(): | |
| new_session_btn = gr.Button("➕ New Session", variant="primary", size="lg", elem_classes=["btn", "btn-primary"]) | |
| health_profile_btn = gr.Button("👤 Custom Health Profile", variant="secondary", elem_classes=["btn", "btn-secondary"]) | |
| clear_session_btn = gr.Button("🗑️ Clear Session", variant="secondary", elem_classes=["btn", "btn-secondary"]) | |
| # Hidden health profile section | |
| with gr.Column(visible=False) as health_profile_section: | |
| gr.HTML('<div style="margin: 1rem 0;"><hr style="border: none; border-top: 1px solid #d1d5db;"></div>') | |
| health_context = gr.Textbox( | |
| label="🏥 Patient's Health Information", | |
| placeholder="e.g., Patient's name, age, medical conditions (high blood pressure, diabetes), current symptoms, medications, lifestyle factors, mental health status...", | |
| lines=5, | |
| max_lines=8, | |
| info="This information helps KaLLaM provide more personalized and relevant health advice. All data is kept confidential within your session." | |
| ) | |
| with gr.Row(): | |
| update_profile_btn = gr.Button("💾 Update Health Profile", variant="primary", elem_classes=["btn", "btn-primary"]) | |
| back_btn = gr.Button("⏪ Back", variant="secondary", elem_classes=["btn", "btn-secondary"]) | |
| gr.HTML('<div style="margin: 1rem 0;"><hr style="border: none; border-top: 1px solid #d1d5db;"></div>') | |
| # Session status | |
| session_status = gr.Markdown(value="🔄 **Initializing...**") | |
| # Main chat area | |
| with gr.Column(scale=3, elem_classes=["main-content"]): | |
| gr.HTML(""" | |
| <div style="text-align: center; padding: 1rem 0;"> | |
| <h2 style="color: #659435; margin: 0; font-size: 1.5rem;">💬 Health Consultation Chat</h2> | |
| <p style="color: #666; margin: 0.5rem 0 0 0;">Chat with your AI health advisor in Thai or English</p> | |
| </div> | |
| """) | |
| chatbot = gr.Chatbot( | |
| label="Chat with KaLLaM", | |
| height=500, | |
| show_label=False, | |
| type="messages", | |
| elem_classes=["chat-container"] | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=5): | |
| msg = gr.Textbox( | |
| label="Message", | |
| placeholder="Ask about your health in Thai or English...", | |
| lines=1, | |
| max_lines=4, | |
| show_label=False, | |
| elem_classes=["chat-container"] | |
| ) | |
| with gr.Column(scale=1, min_width=120): | |
| send_btn = gr.Button("➤", variant="primary", size="lg", elem_classes=["btn", "btn-primary"]) | |
| # Result display | |
| result_display = gr.Markdown(visible=False) | |
| # Footer | |
| gr.HTML(""" | |
| <div style=" | |
| position: fixed; bottom: 0; left: 0; right: 0; | |
| background: linear-gradient(135deg, var(--kallam-secondary) 0%, var(--kallam-primary) 100%); | |
| color: white; padding: 0.75rem 1rem; text-align: center; font-size: 0.8rem; | |
| box-shadow: 0 -4px 6px -1px rgba(0, 0, 0, 0.1); z-index: 1000; | |
| border-top: 1px solid rgba(255,255,255,0.2); | |
| "> | |
| <div style="max-width: 1400px; margin: 0 auto; display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: 1.5rem;"> | |
| <span style="font-weight: 600;">Built with ❤️ by:</span> | |
| <div style="display: flex; flex-direction: column; align-items: center; gap: 0.2rem;"> | |
| <span style="font-weight: 500;">👨💻 Nopnatee Trivoravong</span> | |
| <div style="display: flex; gap: 0.5rem; font-size: 0.75rem;"> | |
| <span>📧 [email protected]</span> | |
| <span>•</span> | |
| <a href="https://github.com/Nopnatee" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: none;">GitHub</a> | |
| </div> | |
| </div> | |
| <span style="color: rgba(255,255,255,0.7);">|</span> | |
| <div style="display: flex; flex-direction: column; align-items: center; gap: 0.2rem;"> | |
| <span style="font-weight: 500;">👨💻 Khamic Srisutrapon</span> | |
| <div style="display: flex; gap: 0.5rem; font-size: 0.75rem;"> | |
| <span>📧 [email protected]</span> | |
| <span>•</span> | |
| <a href="https://github.com/Khamic672" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: none;">GitHub</a> | |
| </div> | |
| </div> | |
| <span style="color: rgba(255,255,255,0.7);">|</span> | |
| <div style="display: flex; flex-direction: column; align-items: center; gap: 0.2rem;"> | |
| <span style="font-weight: 500;">👩💻 Napas Siripala</span> | |
| <div style="display: flex; gap: 0.5rem; font-size: 0.75rem;"> | |
| <span>📧 [email protected]</span> | |
| <span>•</span> | |
| <a href="https://github.com/kaoqueri" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: none;">GitHub</a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| """) | |
| # ====== EVENT HANDLERS - Same pattern as simple app ====== | |
| # Auto-initialize on page load (same as simple app) | |
| def _init(): | |
| sid, history, _, status, note = start_new_session("") | |
| return sid, history, status, note | |
| app.load( | |
| fn=_init, | |
| inputs=None, | |
| outputs=[session_id, chatbot, session_status, result_display] | |
| ) | |
| # New session | |
| new_session_btn.click( | |
| fn=lambda: start_new_session(""), | |
| inputs=None, | |
| outputs=[session_id, chatbot, msg, session_status, result_display] | |
| ) | |
| # Show/hide health profile section | |
| def show_health_profile(): | |
| return gr.update(visible=True) | |
| def hide_health_profile(): | |
| return gr.update(visible=False) | |
| health_profile_btn.click( | |
| fn=show_health_profile, | |
| outputs=[health_profile_section] | |
| ) | |
| back_btn.click( | |
| fn=hide_health_profile, | |
| outputs=[health_profile_section] | |
| ) | |
| # Update health profile | |
| update_profile_btn.click( | |
| fn=update_health_profile, | |
| inputs=[session_id, health_context], | |
| outputs=[result_display, session_status] | |
| ).then( | |
| fn=hide_health_profile, | |
| outputs=[health_profile_section] | |
| ) | |
| # Send message with lock/unlock pattern (inspired by simple app) | |
| send_btn.click( | |
| fn=lock_inputs, | |
| inputs=None, | |
| outputs=[send_btn, msg], | |
| queue=False, # lock applies instantly | |
| ).then( | |
| fn=send_message, | |
| inputs=[msg, chatbot, session_id], | |
| outputs=[chatbot, msg, session_id, session_status], | |
| ).then( | |
| fn=unlock_inputs, | |
| inputs=None, | |
| outputs=[send_btn, msg], | |
| queue=False, | |
| ) | |
| # Enter/submit flow: same treatment | |
| msg.submit( | |
| fn=lock_inputs, | |
| inputs=None, | |
| outputs=[send_btn, msg], | |
| queue=False, | |
| ).then( | |
| fn=send_message, | |
| inputs=[msg, chatbot, session_id], | |
| outputs=[chatbot, msg, session_id, session_status], | |
| ).then( | |
| fn=unlock_inputs, | |
| inputs=None, | |
| outputs=[send_btn, msg], | |
| queue=False, | |
| ) | |
| # Clear session | |
| clear_session_btn.click( | |
| fn=clear_session, | |
| inputs=[session_id], | |
| outputs=[session_id, chatbot, msg, session_status, result_display] | |
| ) | |
| return app | |
| def main(): | |
| app = create_app() | |
| # Resolve bind address and port | |
| server_name = os.getenv("GRADIO_SERVER_NAME", "0.0.0.0") | |
| server_port = int(os.getenv("PORT", os.getenv("GRADIO_SERVER_PORT", 8080))) | |
| # Basic health log to confirm listening address | |
| try: | |
| hostname = socket.gethostname() | |
| ip_addr = socket.gethostbyname(hostname) | |
| except Exception: | |
| hostname = "unknown" | |
| ip_addr = "unknown" | |
| logger.info( | |
| "Starting Gradio app | bind=%s:%s | host=%s ip=%s", | |
| server_name, | |
| server_port, | |
| hostname, | |
| ip_addr, | |
| ) | |
| logger.info( | |
| "Env: PORT=%s GRADIO_SERVER_NAME=%s GRADIO_SERVER_PORT=%s", | |
| os.getenv("PORT"), | |
| os.getenv("GRADIO_SERVER_NAME"), | |
| os.getenv("GRADIO_SERVER_PORT"), | |
| ) | |
| # Secrets presence check (mask values) | |
| def _mask(v: str | None) -> str: | |
| if not v: | |
| return "<missing>" | |
| return f"set(len={len(v)})" | |
| logger.info( | |
| "Secrets: SEA_LION_API_KEY=%s GEMINI_API_KEY=%s", | |
| _mask(os.getenv("SEA_LION_API_KEY")), | |
| _mask(os.getenv("GEMINI_API_KEY")), | |
| ) | |
| app.launch( | |
| share=False, | |
| server_name=server_name, # cloud: 0.0.0.0, local: 127.0.0.1 | |
| server_port=server_port, # cloud: $PORT, local: 7860/8080 | |
| debug=False, | |
| show_error=True, | |
| inbrowser=True | |
| ) | |
| if __name__ == "__main__": | |
| main() |