File size: 17,179 Bytes
66dbebd ae20ff2 66dbebd c5e8f57 ae20ff2 c5e8f57 ae20ff2 c5e8f57 66dbebd c5e8f57 66dbebd 7c65f3f 66dbebd c5e8f57 66dbebd c5e8f57 66dbebd c5e8f57 66dbebd f96d28a c5e8f57 66dbebd ae20ff2 7c65f3f ae20ff2 7c65f3f ae20ff2 7c65f3f ae20ff2 7c65f3f ae20ff2 7c65f3f ae20ff2 7c65f3f ae20ff2 7c65f3f ae20ff2 7c65f3f ae20ff2 7c65f3f c5e8f57 ae20ff2 c5e8f57 ae20ff2 66dbebd ae20ff2 c5e8f57 66dbebd ae20ff2 c5e8f57 ae20ff2 c5e8f57 66dbebd |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 |
# app.py - Mobile-First Implementation
import gradio as gr
import uuid
import logging
import traceback
from typing import Optional, Tuple, List, Dict, Any
import os
# Configure comprehensive logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(),
logging.FileHandler('app.log')
]
)
logger = logging.getLogger(__name__)
# Try to import orchestration components
orchestrator = None
orchestrator_available = False
try:
logger.info("Attempting to import orchestration components...")
import sys
sys.path.insert(0, '.')
sys.path.insert(0, 'src')
from src.agents.intent_agent import create_intent_agent
from src.agents.synthesis_agent import create_synthesis_agent
from src.agents.safety_agent import create_safety_agent
from llm_router import LLMRouter
from orchestrator_engine import MVPOrchestrator
from context_manager import EfficientContextManager
from config import settings
logger.info("β Successfully imported orchestration components")
orchestrator_available = True
except ImportError as e:
logger.warning(f"Could not import orchestration components: {e}")
logger.info("Will use placeholder mode")
try:
from spaces import GPU
SPACES_GPU_AVAILABLE = True
logger.info("HF Spaces GPU available")
except ImportError:
# Not running on HF Spaces or spaces module not available
SPACES_GPU_AVAILABLE = False
GPU = None
logger.info("Running without HF Spaces GPU")
def create_mobile_optimized_interface():
"""Create the mobile-optimized Gradio interface and return demo with components"""
# Store components for wiring
interface_components = {}
with gr.Blocks(
title="AI Research Assistant MVP",
theme=gr.themes.Soft(
primary_hue="blue",
secondary_hue="gray",
font=("Inter", "system-ui", "sans-serif")
),
css="""
/* Mobile-first responsive CSS */
.mobile-container {
max-width: 100vw;
margin: 0 auto;
padding: 0 12px;
}
/* Touch-friendly button sizing */
.gradio-button {
min-height: 44px !important;
min-width: 44px !important;
font-size: 16px !important; /* Prevents zoom on iOS */
}
/* Mobile-optimized chat interface */
.chatbot-container {
height: 60vh !important;
max-height: 60vh !important;
overflow-y: auto !important;
-webkit-overflow-scrolling: touch !important;
}
/* Mobile input enhancements */
.textbox-input {
font-size: 16px !important; /* Prevents zoom */
min-height: 44px !important;
padding: 12px !important;
}
/* Responsive grid adjustments */
@media (max-width: 768px) {
.gradio-row {
flex-direction: column !important;
gap: 8px !important;
}
.gradio-column {
width: 100% !important;
}
.chatbot-container {
height: 50vh !important;
}
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
body {
background: #1a1a1a;
color: #ffffff;
}
}
/* Hide scrollbars but maintain functionality */
.chatbot-container::-webkit-scrollbar {
width: 4px;
}
/* Loading states */
.loading-indicator {
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
/* Mobile menu enhancements */
.accordion-content {
max-height: 200px !important;
overflow-y: auto !important;
}
"""
) as demo:
# Session Management (Mobile-Optimized)
with gr.Column(elem_classes="mobile-container"):
gr.Markdown("""
# π§ Research Assistant
*Academic AI with transparent reasoning*
""")
# Session Header Bar (Mobile-Friendly)
with gr.Row():
session_info = gr.Textbox(
label="Session ID",
value=str(uuid.uuid4())[:8], # Shortened for mobile
max_lines=1,
show_label=False,
container=False,
scale=3
)
new_session_btn = gr.Button(
"π New",
size="sm",
variant="secondary",
scale=1,
min_width=60
)
menu_toggle = gr.Button(
"βοΈ",
size="sm",
variant="secondary",
scale=1,
min_width=60
)
# Main Chat Area (Mobile-Optimized)
with gr.Tabs() as main_tabs:
with gr.TabItem("π¬ Chat", id="chat_tab"):
chatbot = gr.Chatbot(
label="",
show_label=False,
height="60vh",
elem_classes="chatbot-container",
type="messages"
)
interface_components['chatbot'] = chatbot
# Mobile Input Area
with gr.Row():
message_input = gr.Textbox(
placeholder="Ask me anything...",
show_label=False,
max_lines=3,
container=False,
scale=4,
autofocus=True
)
interface_components['message_input'] = message_input
send_btn = gr.Button(
"β Send",
variant="primary",
scale=1,
min_width=80
)
interface_components['send_btn'] = send_btn
# Technical Details Tab (Collapsible for Mobile)
with gr.TabItem("π Details", id="details_tab"):
with gr.Accordion("Reasoning Chain", open=False):
reasoning_display = gr.JSON(
label="",
show_label=False
)
with gr.Accordion("Agent Performance", open=False):
performance_display = gr.JSON(
label="",
show_label=False
)
with gr.Accordion("Session Context", open=False):
context_display = gr.JSON(
label="",
show_label=False
)
# Mobile Bottom Navigation
with gr.Row(visible=False, elem_id="mobile_nav") as mobile_navigation:
chat_nav_btn = gr.Button("π¬ Chat", variant="secondary", size="sm", min_width=0)
details_nav_btn = gr.Button("π Details", variant="secondary", size="sm", min_width=0)
settings_nav_btn = gr.Button("βοΈ Settings", variant="secondary", size="sm", min_width=0)
# Settings Panel (Modal for Mobile)
with gr.Column(visible=False, elem_id="settings_panel") as settings:
with gr.Accordion("Display Options", open=True):
show_reasoning = gr.Checkbox(
label="Show reasoning chain",
value=True,
info="Display step-by-step reasoning"
)
show_agent_trace = gr.Checkbox(
label="Show agent execution trace",
value=False,
info="Display which agents processed your request"
)
compact_mode = gr.Checkbox(
label="Compact mode",
value=False,
info="Optimize for smaller screens"
)
with gr.Accordion("Performance Options", open=False):
response_speed = gr.Radio(
choices=["Fast", "Balanced", "Thorough"],
value="Balanced",
label="Response Speed Preference"
)
cache_enabled = gr.Checkbox(
label="Enable context caching",
value=True,
info="Faster responses using session memory"
)
gr.Button("Save Preferences", variant="primary")
# Wire up the submit handler INSIDE the gr.Blocks context
if 'send_btn' in interface_components and 'message_input' in interface_components and 'chatbot' in interface_components:
# Connect the submit handler with the GPU-decorated function
interface_components['send_btn'].click(
fn=chat_handler_fn,
inputs=[interface_components['message_input'], interface_components['chatbot']],
outputs=[interface_components['chatbot'], interface_components['message_input']]
)
return demo, interface_components
def setup_event_handlers(demo, event_handlers):
"""Setup event handlers for the interface"""
# Find components by their labels or types
components = {}
for block in demo.blocks:
if hasattr(block, 'label'):
if block.label == 'Session ID':
components['session_info'] = block
elif hasattr(block, 'value') and 'session' in str(block.value).lower():
components['session_id'] = block
# Setup message submission handler
try:
# This is a simplified version - you'll need to adapt based on your actual component structure
if hasattr(demo, 'submit'):
demo.submit(
fn=event_handlers.handle_message_submit,
inputs=[components.get('message_input'), components.get('chatbot')],
outputs=[components.get('message_input'), components.get('chatbot')]
)
except Exception as e:
print(f"Could not setup event handlers: {e}")
# Fallback to basic functionality
return demo
async def process_message_async(message: str, history: Optional[List], session_id: str) -> Tuple[List, str]:
"""
Process message with full orchestration system
Returns (updated_history, empty_string)
"""
global orchestrator
try:
logger.info(f"Processing message: {message[:100]}")
logger.info(f"Session ID: {session_id}")
if not message or not message.strip():
logger.debug("Empty message received")
return history if history else [], ""
if history is None:
history = []
new_history = list(history) if isinstance(history, list) else []
# Add user message
new_history.append({"role": "user", "content": message.strip()})
# Try to use orchestrator if available
if orchestrator is not None:
try:
logger.info("Attempting full orchestration...")
# Use orchestrator to process
result = await orchestrator.process_request(
session_id=session_id,
user_input=message.strip()
)
# Extract response from result
response = result.get('response', result.get('final_response', str(result)))
logger.info(f"Orchestrator returned response: {response[:100]}")
except Exception as orch_error:
logger.error(f"Orchestrator error: {orch_error}", exc_info=True)
response = f"[Orchestrator Error] {str(orch_error)}"
else:
# Fallback placeholder
logger.info("Using placeholder response")
response = f"I received your message: {message}\n\nThis is a placeholder response. The orchestrator system is {'' if orchestrator_available else 'not'} available."
# Add assistant response
new_history.append({"role": "assistant", "content": response})
logger.info("Message processing complete")
return new_history, ""
except Exception as e:
logger.error(f"Error in process_message_async: {e}", exc_info=True)
error_history = list(history) if history else []
error_history.append({"role": "user", "content": message})
error_history.append({"role": "assistant", "content": f"I encountered an error: {str(e)}"})
return error_history, ""
def process_message(message: str, history: Optional[List]) -> Tuple[List, str]:
"""
Synchronous wrapper for async processing
"""
import asyncio
# Generate a session ID
session_id = str(uuid.uuid4())[:8]
try:
# Run async processing
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
result = loop.run_until_complete(process_message_async(message, history, session_id))
return result
except Exception as e:
logger.error(f"Error in process_message: {e}", exc_info=True)
error_history = list(history) if history else []
error_history.append({"role": "user", "content": message})
error_history.append({"role": "assistant", "content": f"Error: {str(e)}"})
return error_history, ""
# Decorate the chat handler with GPU if available
if SPACES_GPU_AVAILABLE and GPU is not None:
@GPU # This decorator is detected by HF Spaces for ZeroGPU allocation
def gpu_chat_handler(message, history):
"""Handle chat messages with GPU support"""
return process_message(message, history)
chat_handler_fn = gpu_chat_handler
else:
chat_handler_fn = process_message
# Initialize orchestrator on module load
def initialize_orchestrator():
"""Initialize the orchestration system with logging"""
global orchestrator
if not orchestrator_available:
logger.info("Orchestrator components not available, skipping initialization")
return
try:
logger.info("=" * 60)
logger.info("INITIALIZING ORCHESTRATION SYSTEM")
logger.info("=" * 60)
# Get HF token
hf_token = os.getenv('HF_TOKEN', '')
if not hf_token:
logger.warning("HF_TOKEN not found in environment")
# Initialize LLM Router
logger.info("Step 1/6: Initializing LLM Router...")
llm_router = LLMRouter(hf_token)
logger.info("β LLM Router initialized")
# Initialize Agents
logger.info("Step 2/6: Initializing Agents...")
agents = {
'intent_recognition': create_intent_agent(llm_router),
'response_synthesis': create_synthesis_agent(llm_router),
'safety_check': create_safety_agent(llm_router)
}
logger.info(f"β Initialized {len(agents)} agents")
# Initialize Context Manager
logger.info("Step 3/6: Initializing Context Manager...")
context_manager = EfficientContextManager()
logger.info("β Context Manager initialized")
# Initialize Orchestrator
logger.info("Step 4/6: Initializing Orchestrator...")
orchestrator = MVPOrchestrator(llm_router, context_manager, agents)
logger.info("β Orchestrator initialized")
logger.info("=" * 60)
logger.info("ORCHESTRATION SYSTEM READY")
logger.info("=" * 60)
except Exception as e:
logger.error(f"Failed to initialize orchestrator: {e}", exc_info=True)
orchestrator = None
# Try to initialize orchestrator
initialize_orchestrator()
if __name__ == "__main__":
logger.info("=" * 60)
logger.info("STARTING APP")
logger.info("=" * 60)
demo, components = create_mobile_optimized_interface()
logger.info("β Interface created")
logger.info(f"Orchestrator available: {orchestrator is not None}")
# Launch the app
logger.info("=" * 60)
logger.info("LAUNCHING GRADIO APP")
logger.info("=" * 60)
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False
)
|