Wire NotificationBus into main and Gradio UI
Create NotificationBus in __main__.py and inject into scheduler and UI. Gradio subscribes as the "gradio" listener with a 10-second polling timer that displays notifications in a banner above the chatbot. ClickUp status shown in the header bar. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>cora-start
parent
a67e714045
commit
7864ca2461
|
|
@ -58,17 +58,26 @@ def main():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning("Tool system not available: %s", e)
|
log.warning("Tool system not available: %s", e)
|
||||||
|
|
||||||
|
# Notification bus (UI-agnostic)
|
||||||
|
notification_bus = None
|
||||||
|
try:
|
||||||
|
from .notifications import NotificationBus
|
||||||
|
log.info("Initializing notification bus...")
|
||||||
|
notification_bus = NotificationBus(db)
|
||||||
|
except Exception as e:
|
||||||
|
log.warning("Notification bus not available: %s", e)
|
||||||
|
|
||||||
# Phase 3+: Scheduler
|
# Phase 3+: Scheduler
|
||||||
try:
|
try:
|
||||||
from .scheduler import Scheduler
|
from .scheduler import Scheduler
|
||||||
log.info("Starting scheduler...")
|
log.info("Starting scheduler...")
|
||||||
scheduler = Scheduler(config, db, agent)
|
scheduler = Scheduler(config, db, agent, notification_bus=notification_bus)
|
||||||
scheduler.start()
|
scheduler.start()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning("Scheduler not available: %s", e)
|
log.warning("Scheduler not available: %s", e)
|
||||||
|
|
||||||
log.info("Launching Gradio UI on %s:%s...", config.host, config.port)
|
log.info("Launching Gradio UI on %s:%s...", config.host, config.port)
|
||||||
app, css = create_ui(agent, config, llm)
|
app, css = create_ui(agent, config, llm, notification_bus=notification_bus)
|
||||||
app.launch(
|
app.launch(
|
||||||
server_name=config.host,
|
server_name=config.host,
|
||||||
server_port=config.port,
|
server_port=config.port,
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,26 @@ if TYPE_CHECKING:
|
||||||
from .agent import Agent
|
from .agent import Agent
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .llm import LLMAdapter
|
from .llm import LLMAdapter
|
||||||
|
from .notifications import NotificationBus
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
_CSS = """
|
_CSS = """
|
||||||
.contain { max-width: 900px; margin: auto; }
|
.contain { max-width: 900px; margin: auto; }
|
||||||
footer { display: none !important; }
|
footer { display: none !important; }
|
||||||
|
.notification-banner {
|
||||||
|
background: #1a1a2e;
|
||||||
|
border: 1px solid #16213e;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def create_ui(agent: Agent, config: Config, llm: LLMAdapter) -> gr.Blocks:
|
def create_ui(agent: Agent, config: Config, llm: LLMAdapter,
|
||||||
|
notification_bus: NotificationBus | None = None) -> gr.Blocks:
|
||||||
"""Build and return the Gradio app."""
|
"""Build and return the Gradio app."""
|
||||||
|
|
||||||
available_models = llm.list_chat_models()
|
available_models = llm.list_chat_models()
|
||||||
|
|
@ -30,15 +40,24 @@ def create_ui(agent: Agent, config: Config, llm: LLMAdapter) -> gr.Blocks:
|
||||||
current_model = llm.current_model
|
current_model = llm.current_model
|
||||||
|
|
||||||
exec_status = "available" if llm.is_execution_brain_available() else "unavailable"
|
exec_status = "available" if llm.is_execution_brain_available() else "unavailable"
|
||||||
|
clickup_status = "enabled" if config.clickup.enabled else "disabled"
|
||||||
|
|
||||||
with gr.Blocks(title="CheddahBot") as app:
|
with gr.Blocks(title="CheddahBot") as app:
|
||||||
gr.Markdown("# CheddahBot", elem_classes=["contain"])
|
gr.Markdown("# CheddahBot", elem_classes=["contain"])
|
||||||
gr.Markdown(
|
gr.Markdown(
|
||||||
f"*Chat Brain:* `{current_model}` | "
|
f"*Chat Brain:* `{current_model}` | "
|
||||||
f"*Execution Brain (Claude Code CLI):* `{exec_status}`",
|
f"*Execution Brain (Claude Code CLI):* `{exec_status}` | "
|
||||||
|
f"*ClickUp:* `{clickup_status}`",
|
||||||
elem_classes=["contain"],
|
elem_classes=["contain"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# -- Notification banner --
|
||||||
|
notification_display = gr.Markdown(
|
||||||
|
value="",
|
||||||
|
visible=False,
|
||||||
|
elem_classes=["contain", "notification-banner"],
|
||||||
|
)
|
||||||
|
|
||||||
with gr.Row(elem_classes=["contain"]):
|
with gr.Row(elem_classes=["contain"]):
|
||||||
model_dropdown = gr.Dropdown(
|
model_dropdown = gr.Dropdown(
|
||||||
choices=model_choices,
|
choices=model_choices,
|
||||||
|
|
@ -191,6 +210,22 @@ def create_ui(agent: Agent, config: Config, llm: LLMAdapter) -> gr.Blocks:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return None, f"Voice chat error: {e}"
|
return None, f"Voice chat error: {e}"
|
||||||
|
|
||||||
|
def poll_notifications():
|
||||||
|
"""Poll the notification bus for pending messages."""
|
||||||
|
if not notification_bus:
|
||||||
|
return gr.update(value="", visible=False)
|
||||||
|
|
||||||
|
messages = notification_bus.get_pending("gradio")
|
||||||
|
if not messages:
|
||||||
|
return gr.update() # No change
|
||||||
|
|
||||||
|
# Format notifications as markdown
|
||||||
|
lines = []
|
||||||
|
for msg in messages[-5:]: # Show last 5 notifications max
|
||||||
|
lines.append(f"**Notification:** {msg}")
|
||||||
|
banner = "\n\n".join(lines)
|
||||||
|
return gr.update(value=banner, visible=True)
|
||||||
|
|
||||||
# -- Wire events --
|
# -- Wire events --
|
||||||
|
|
||||||
model_dropdown.change(on_model_change, [model_dropdown], None)
|
model_dropdown.change(on_model_change, [model_dropdown], None)
|
||||||
|
|
@ -209,6 +244,12 @@ def create_ui(agent: Agent, config: Config, llm: LLMAdapter) -> gr.Blocks:
|
||||||
[voice_output, voice_status],
|
[voice_output, voice_status],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Notification polling timer (every 10 seconds)
|
||||||
|
if notification_bus:
|
||||||
|
notification_bus.subscribe("gradio", lambda msg, cat: None) # Register listener
|
||||||
|
timer = gr.Timer(10)
|
||||||
|
timer.tick(poll_notifications, None, [notification_display])
|
||||||
|
|
||||||
# Load conversation list on app start
|
# Load conversation list on app start
|
||||||
app.load(_load_conversations, None, [conv_list])
|
app.load(_load_conversations, None, [conv_list])
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue