Skip to main content

Parse widget data

Retrieve data for user‑selected widgets and pass it to your model. Enable widget-dashboard-select and call get_widget_data when the latest user message arrives.

Reference implementation in this GitHub repository.

Raw reply without context

Architecture​

This pattern uses a minimal FastAPI backend with two endpoints and OpenBB AI SDK helpers to retrieve widget data and stream results.

agents.json configuration with widget-dashboard-select feature enabled:

return JSONResponse(content={
"vanilla_agent_raw_context": {
"endpoints": {"query": "http://localhost:7777/v1/query"},
"features": {
"widget-dashboard-select": True,
"widget-dashboard-search": False,
},
}
})

Query flow​

  • Check if latest message is human with widgets.primary populated
  • Build WidgetRequest objects with current parameter values
  • Early exit: yield get_widget_data() SSE immediately for UI to execute
  • On subsequent request with tool results:
    • Parse DataContent items from tool message
    • Extract and format widget data into context string
    • Append context to user messages for LLM processing
    • Stream LLM response with message_chunk()

OpenBB AI SDK​

  • get_widget_data(widget_requests): Creates FunctionCallSSE for widget data retrieval
  • WidgetRequest(widget, input_arguments): Specifies widget and parameter values
  • Widget: Contains widget metadata (uuid, name, type, params)
  • WidgetParam: Individual parameter with name, type, current_value
  • DataContent: Container for widget response data
  • message_chunk(text): Creates MessageChunkSSE for streaming text

Core logic​

from openbb_ai import get_widget_data, WidgetRequest, message_chunk

@app.post("/v1/query")
async def query(request: QueryRequest) -> EventSourceResponse:
if (
request.messages[-1].role == "human"
and request.widgets
and request.widgets.primary
):
widget_requests = [
WidgetRequest(
widget=w,
input_arguments={p.name: p.current_value for p in w.params},
)
for w in request.widgets.primary
]

async def retrieve_widget_data():
# Function-call SSE that Workspace interprets and executes
yield get_widget_data(widget_requests).model_dump()

return EventSourceResponse(retrieve_widget_data(), media_type="text/event-stream")

# Process tool message with widget data
openai_messages = [
ChatCompletionSystemMessageParam(
role="system",
content="You are a helpful financial assistant."
)
]

context_str = ""
for message in request.messages:
if message.role == "human":
openai_messages.append(
ChatCompletionUserMessageParam(role="user", content=message.content)
)
elif message.role == "tool":
# Extract widget data from latest tool result
for data_content in message.data:
for item in data_content.items:
context_str += str(item.content) + "\n"

# Append context to last user message
if context_str and openai_messages:
openai_messages[-1]["content"] += "\n\nContext:\n" + context_str

async def execution_loop():
async for event in await client.chat.completions.create(
model="gpt-4o",
messages=openai_messages,
stream=True
):
if chunk := event.choices[0].delta.content:
yield message_chunk(chunk).model_dump()

return EventSourceResponse(execution_loop(), media_type="text/event-stream")

Dashboard widgets vs explicit context​

The example above uses request.widgets.primary which contains widgets explicitly selected by the user. If you want to access all widgets available on the current dashboard instead, you can use request.widgets.secondary:

# Access dashboard widgets instead of explicit context
if (
request.messages[-1].role == "human"
and request.widgets
and request.widgets.secondary # Dashboard widgets
):
widget_requests = [
WidgetRequest(
widget=w,
input_arguments={p.name: p.current_value for p in w.params},
)
for w in request.widgets.secondary # Use secondary instead of primary
]

Important: To access dashboard widgets, you must enable the widget-dashboard-search feature in your agents.json:

"features": {
...
"widget-dashboard-search": True, # Dashboard widgets
}

This gives your agent broader context about the user's dashboard setup and available data sources, rather than just the widgets they've explicitly selected.