Skip to content

Add vanilla agent dashboard - #78

Merged
DidierRLopes merged 29 commits into
mainfrom
feat/add-agent-dashboard-widgets-example
Sep 18, 2025
Merged

Add vanilla agent dashboard#78
DidierRLopes merged 29 commits into
mainfrom
feat/add-agent-dashboard-widgets-example

Conversation

@DidierRLopes

Copy link
Copy Markdown
Contributor
  • Add vanilla agent dashboard
  • Clean up financial prompt optimizer
  • Improve README

@DidierRLopes
DidierRLopes requested a review from piiq September 15, 2025 20:21

@piiq piiq left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've asked GPT-5 for strategies to improve the 40 example to make it easier to comprehend. Here's the response (edited)


Here are concrete ways to simplify this example so it clearly demonstrates “getting the active tab context and its widgets” without extra noise.

Key pain points:

  • Heavy formatting logic: The format_widget function builds tables, truncates text, and handles many fields, which distracts from the core idea.
  • Nested loops and O(n²) search: Matching tab widgets to full details by scanning all_widgets repeatedly makes the code longer and harder to follow.
  • Repeated branching: Handling primary/secondary/dashboard/fallback separately leads to verbose, duplicated logic.
  • Conditional streaming by role: Special-casing “human” adds branching that isn’t necessary for a simple demo.

Simplification strategies

  • Focus on the active tab:
    • Show only the active tab and a short list of its widgets. Optionally add “show all tabs” later.
  • Use simple text output:
    • Replace tables with short bullet lines: “- Name (id: X) — short description”.
  • Index widgets by UUID:
    • Build a dict once: by_uuid = {str(w.uuid): w for w in widgets} to map dashboard references quickly and clearly.
  • Single path for gathering widgets:
    • Combine primary and secondary once into a list; avoid repeated conditionals later.
  • Single streaming path:
    • Always stream one compact message; skip separate generators/role checks.
  • Small helpers:
    • short(text, width=120) to tidy and truncate text.
    • gather_widgets(request) to combine widget sources.
    • active_tab(info) to pick the active or first tab.

Lean /v1/query sketch

from textwrap import shorten
from fastapi import FastAPI
from sse_starlette.sse import EventSourceResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from openbb_ai.models import QueryRequest
from openbb_ai import message_chunk

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"], allow_credentials=True,
    allow_methods=["*"], allow_headers=["*"],
)

@app.get("/agents.json")
def get_copilot_description():
    return JSONResponse(content={
        "vanilla_agent_dashboard_widgets": {
            "name": "Vanilla Agent Dashboard Widgets",
            "description": "Show active tab and its widgets.",
            "endpoints": {"query": "http://localhost:7777/v1/query"},
            "features": {"streaming": True},
        }
    })

def short(text: str, width: int = 120) -> str:
    text = (text or "").replace("\n", " ").strip()
    return shorten(text, width=width, placeholder="...") if text else "no description"

def gather_widgets(req: QueryRequest):
    items = []
    if req.widgets:
        if req.widgets.primary: items += req.widgets.primary
        if req.widgets.secondary: items += req.widgets.secondary
    return items

def active_tab(info):
    if not info or not info.tabs: return None
    if info.current_tab_id:
        for t in info.tabs:
            if t.tab_id == info.current_tab_id:
                return t
    return info.tabs[0]

@app.post("/v1/query")
async def query(request: QueryRequest) -> EventSourceResponse:
    widgets = gather_widgets(request)
    by_uuid = {str(getattr(w, "uuid", "")): w for w in widgets}

    info = request.workspace_state.current_dashboard_info if request.workspace_state else None
    tab = active_tab(info)

    lines = []
    if tab:
        lines.append(f"Active tab: {tab.tab_id}")
        for tw in (tab.widgets or []):
            w = by_uuid.get(str(tw.widget_uuid))
            name = (getattr(w, "name", None) or getattr(w, "widget_id", None)
                    or getattr(tw, "name", None) or "Widget")
            wid = getattr(w, "widget_id", None) or "N/A"
            desc = short(getattr(w, "description", None))
            lines.append(f"- {name} (id: {wid}) — {desc}")
    elif widgets:
        lines.append("Widgets in context:")
        for w in widgets:
            name = w.name or w.widget_id or "Widget"
            lines.append(f"- {name}")
    else:
        lines.append("No widgets found on your dashboard.")

    async def stream():
        yield message_chunk("\n".join(lines)).model_dump()

    return EventSourceResponse(stream(), media_type="text/event-stream")
  • Keeps SSE and shows only the essentials: active tab and its widgets.
  • Gather widgets once from primary + secondary.
  • Build by_uuid for O(1) lookups.
  • Select active tab; fall back to the first tab or to a simple “widgets in context” list if no tab info.
  • Produce a short, readable list of lines and stream them as one message.

Why this is easier to grasp

  • The example demonstrates a single concept—active tab context—without formatting complexity.
  • The data path is linear and obvious: gather → index → pick tab → list widgets.
  • Helpers isolate small, reusable bits and reduce mental load.

@DidierRLopes
DidierRLopes merged commit bdb9ba3 into main Sep 18, 2025
1 check passed
@DidierRLopes
DidierRLopes deleted the feat/add-agent-dashboard-widgets-example branch September 18, 2025 13:42
@DidierRLopes

Copy link
Copy Markdown
Contributor Author

@piiq I'm not a fan of doing just the active tab because then it doesn't show how you get widgets from other tabs, which I think defeats the purpose of the example :/

I created this ticket #79

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants