Spaces:
Sleeping
Sleeping
| """ | |
| Gradio UI layout and event wiring. | |
| Two-column layout: | |
| - Left (70%): ``gr.Chatbot`` with streaming responses. | |
| - Right (30%): Agent reasoning panel (collapsible accordions showing the | |
| Thought / Action / Observation chain, tool execution log, and cache | |
| status). | |
| """ | |
| from __future__ import annotations | |
| import asyncio | |
| import json | |
| import logging | |
| from typing import Any, AsyncIterator | |
| import gradio as gr | |
| from agent.events import AgentEvent, EventType | |
| from agent.orchestrator import AgentOrchestrator | |
| from model.engine import ModelEngine | |
| from tools import create_default_registry | |
| from ui.formatters import format_event_for_chatbot, format_event_for_log | |
| logger = logging.getLogger(__name__) | |
| # Pre-populated example prompts. | |
| _EXAMPLES = [ | |
| "Analyze AAPL's momentum and tell me if it's overbought", | |
| "Compare the correlation between NVDA and AMD over the last 6 months", | |
| "Run a Monte Carlo simulation for TSLA -- what's the 30-day risk?", | |
| "How might the current fed funds rate affect tech stocks?", | |
| ] | |
| def build_interface(demo_mode: bool = True) -> gr.Blocks: | |
| """Construct and return the Gradio Blocks interface. | |
| Parameters | |
| ---------- | |
| demo_mode: | |
| When True, the agent uses mock model responses (no GPU needed). | |
| """ | |
| engine = ModelEngine(demo_mode=demo_mode) | |
| registry = create_default_registry() | |
| with gr.Blocks( | |
| title="Agentic Market Analyzer", | |
| ) as app: | |
| gr.Markdown( | |
| "# Agentic Market Analyzer\n" | |
| "A custom ReAct agent for market microstructure analysis. " | |
| "Ask about any stock, ETF, or economic indicator." | |
| ) | |
| with gr.Row(): | |
| # ---------------------------------------------------------- # | |
| # Left panel: Chat | |
| # ---------------------------------------------------------- # | |
| with gr.Column(scale=7): | |
| chatbot = gr.Chatbot( | |
| label="Chat", | |
| height=550, | |
| ) | |
| with gr.Row(): | |
| msg_input = gr.Textbox( | |
| placeholder="Ask about any ticker, indicator, or market question...", | |
| show_label=False, | |
| scale=8, | |
| container=False, | |
| ) | |
| send_btn = gr.Button("Send", variant="primary", scale=1) | |
| clear_btn = gr.Button("Clear", scale=1) | |
| gr.Examples( | |
| examples=_EXAMPLES, | |
| inputs=msg_input, | |
| label="Example queries", | |
| ) | |
| # ---------------------------------------------------------- # | |
| # Right panel: Reasoning log | |
| # ---------------------------------------------------------- # | |
| with gr.Column(scale=3): | |
| with gr.Accordion("Agent Reasoning", open=True): | |
| reasoning_log = gr.Markdown( | |
| value="*Waiting for a query...*", | |
| label="Reasoning Chain", | |
| ) | |
| with gr.Accordion("Tool Execution Log", open=False): | |
| tool_log = gr.JSON(label="Tool Results", value={}) | |
| with gr.Accordion("Session Info", open=False): | |
| session_info = gr.Markdown( | |
| value=( | |
| f"- Mode: {'Demo (mock model)' if demo_mode else 'Live (GPU)'}\n" | |
| f"- Max iterations: 8\n" | |
| f"- Tools: {', '.join(registry.list_names())}" | |
| ) | |
| ) | |
| # -------------------------------------------------------------- # | |
| # State | |
| # -------------------------------------------------------------- # | |
| chat_history = gr.State([]) | |
| # -------------------------------------------------------------- # | |
| # Event handlers | |
| # -------------------------------------------------------------- # | |
| async def handle_message( | |
| user_message: str, | |
| history: list, | |
| ) -> tuple[list, list, str, str, dict]: | |
| """Process a user message through the agent loop.""" | |
| if not user_message.strip(): | |
| return history, history, "", "*No query provided.*", {} | |
| # Append user message to chat. | |
| history = history + [{"role": "user", "content": user_message}] | |
| orchestrator = AgentOrchestrator( | |
| model=engine, | |
| tool_registry=registry, | |
| max_iterations=8, | |
| tool_timeout=30.0, | |
| ) | |
| log_entries: list[str] = [] | |
| tool_results: dict[str, Any] = {} | |
| final_answer = "" | |
| async for event in orchestrator.run(user_message): | |
| log_entry = format_event_for_log(event) | |
| log_entries.append(log_entry) | |
| if event.type == EventType.TOOL_RESULT: | |
| tool_name = event.metadata.get("tool", "unknown") | |
| tool_results[tool_name] = { | |
| "success": event.metadata.get("success", False), | |
| "time_ms": event.metadata.get("execution_time_ms", 0), | |
| "cached": event.metadata.get("cached", False), | |
| "preview": event.content[:200], | |
| } | |
| chat_msg = format_event_for_chatbot(event) | |
| if chat_msg: | |
| final_answer = chat_msg | |
| if final_answer: | |
| history = history + [{"role": "assistant", "content": final_answer}] | |
| reasoning_md = "\n\n---\n\n".join(log_entries) if log_entries else "*No events.*" | |
| return ( | |
| history, # chatbot | |
| history, # chat_history state | |
| "", # clear input | |
| reasoning_md, # reasoning_log | |
| tool_results, # tool_log | |
| ) | |
| def clear_chat() -> tuple[list, list, str, str, dict]: | |
| return [], [], "", "*Waiting for a query...*", {} | |
| send_btn.click( | |
| fn=handle_message, | |
| inputs=[msg_input, chat_history], | |
| outputs=[chatbot, chat_history, msg_input, reasoning_log, tool_log], | |
| ) | |
| msg_input.submit( | |
| fn=handle_message, | |
| inputs=[msg_input, chat_history], | |
| outputs=[chatbot, chat_history, msg_input, reasoning_log, tool_log], | |
| ) | |
| clear_btn.click( | |
| fn=clear_chat, | |
| outputs=[chatbot, chat_history, msg_input, reasoning_log, tool_log], | |
| ) | |
| return app | |
| _CUSTOM_CSS = """ | |
| .gradio-container { | |
| max-width: 1400px !important; | |
| } | |
| """ | |