Skip to main content

Pydantic AI adapter for OpenBB Workspace. Connect any pydantic-ai agent to OpenBB via SSE streaming, widget tools, and PDF context.

Project description

uv ty Ask DeepWiki

OpenBB Pydantic AI Adapter

openbb-pydantic-ai lets any Pydantic AI agent run behind OpenBB Workspace by translating QueryRequest payloads into a Pydantic AI run, exposing Workspace widgets as deferred tools, and streaming native OpenBB SSE events back to the UI.

  • Stateless by design: each QueryRequest carries the full conversation history, widgets, context, and URLs so requests are processed independently.
  • First-class widget tools: every widget becomes a deferred Pydantic AI tool; when the model calls one, the adapter emits copilotFunctionCall events and waits for the Workspace to return data before resuming.
  • Rich event stream: reasoning steps, thinking traces, tables, charts, HTML artifacts, and citations are streamed as native OpenBB SSE payloads.
  • PDF context: install the [pdf] extra and any PDF widget in the Workspace is automatically extracted and passed as context to the agent.
  • Output helpers included: structured outputs (dicts/lists) are auto-detected and converted to tables or charts; chart parameters are normalized for consistent rendering.

See the OpenBB Custom Agent SDK and Pydantic AI UI adapter docs for the underlying types.

Installation

pip install openbb-pydantic-ai
# or with uv
uv add openbb-pydantic-ai

For PDF context support (requires docling):

uv add "openbb-pydantic-ai[pdf]"
# GPU variant (CUDA 12.8)
uv add "openbb-pydantic-ai[pdf-cu128]"

Quick Start (FastAPI)

from anyio import BrokenResourceError
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from pydantic_ai import Agent

from openbb_pydantic_ai import OpenBBAIAdapter, OpenBBDeps

agent = Agent(
    "openrouter:minimax/minimax-m2.5",
    instructions="Be concise and helpful. Only use widget tools for data lookups.",
    deps_type=OpenBBDeps,
)

app = FastAPI()
AGENT_BASE_URL = "http://localhost:8003"


@app.get("/agents.json")
async def agents_json():
    return JSONResponse(
        content={
            "<agent-id>": {
                "name": "My Custom Agent",
                "description": "This is my custom agent",
                "image": f"{AGENT_BASE_URL}/my-custom-agent/logo.png",
                "endpoints": {"query": f"{AGENT_BASE_URL}/query"},
                "features": {
                    "streaming": True,
                    "widget-dashboard-select": True,  # primary & secondary widgets
                    "widget-dashboard-search": True,  # extra widgets
                    "mcp-tools": True,
                },
            }
        }
    )


@app.post("/query")
async def query(request: Request):
    try:
        return await OpenBBAIAdapter.dispatch_request(request, agent=agent)
    except BrokenResourceError:
        pass  # client disconnected


app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://pro.openbb.co"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

How It Works

1. Request Handling

  • OpenBB Workspace POST's a QueryRequest to /query
  • OpenBBAIAdapter validates it, builds the Pydantic AI message stack, and injects workspace context and URLs as system prompts

2. Widget Tool Conversion

  • Widgets in the request become deferred Pydantic AI tools
  • Each call emits a copilotFunctionCall event (via get_widget_data)
  • The adapter pauses until Workspace responds with data, then resumes the run

3. Event Streaming

Pydantic AI event OpenBB SSE event
Text chunk copilotMessageChunk
Reasoning / thinking block Collapsed under "Step-by-step reasoning" dropdown
Table / chart / HTML artifact copilotMessageArtifact
Widget citations copilotCitationCollection (batched at end of run)

Features

Widget Toolsets

Widgets are grouped by priority (primary, secondary, extra) and exposed through dedicated toolsets. Tool names follow the openbb_widget_<identifier> convention with any redundant openbb_ prefix trimmed (e.g. openbb_widget_financial_statements).

Control access via the agents.json feature flags:

"features": {
    "widget-dashboard-select": true,
    "widget-dashboard-search": true
}

Visualization: Charts, Tables & HTML

Three built-in tools handle structured output. The model can call any of them directly; the adapter handles serialization and streaming.

openbb_create_chart

Creates chart artifacts inline in the response. Supported types: line, bar, scatter, pie, donut.

Insert {{place_chart_here}} in the model's text where the chart should appear — the adapter swaps the placeholder with the rendered artifact while streaming:

Here is the revenue breakdown: {{place_chart_here}}

Required axes:

  • line / bar / scatter: x_key + y_keys
  • pie / donut: angle_key + callout_label_key

Different field spellings (y_keys, yKeys, etc.) are accepted and normalized before emitting.

openbb_create_table

Creates a table artifact from structured data with explicit column ordering and metadata. Use this when you want predictable output over auto-detection.

openbb_create_html

Renders a self-contained HTML artifact, useful for custom layouts, formatted reports, or SVG-based plots when Markdown isn't enough.

Constraint: limited to HTML + CSS + inline SVG. No JavaScript. This is an OpenBB Workspace restriction on non-Enterprise plans.

Auto-detection: dict/list outputs shaped like {"type": "table", "data": [...]} or a plain list of dicts are automatically converted to table artifacts without calling any tool explicitly.

Markdown tables are also supported: stream tabular data as Markdown and Workspace renders it as an interactive table users can promote to a widget.

MCP Tools

Tools listed in QueryRequest.tools are exposed as an external MCP toolset. The model sees the same tool names the Workspace UI presents. Deferred execute_agent_tool results replay on the next request just like widget results.

Enable in agents.json:

"features": { "mcp-tools": true }

PDF Context

Install the [pdf] extra and any PDF widget on the active dashboard is automatically extracted and passed as context before the run starts, no code changes needed.

uv add "openbb-pydantic-ai[pdf]"

Text is extracted and linked back to citation bounding boxes so the agent can cite specific pages (currently you get a citation to the page, and not a displayed bounding box).

Performance: GPU extraction is significantly faster. CPU works, but expect slowdowns on documents over ~50 pages.

Deferred Results & Citations

  • Pending widget responses in the request are replayed before the run starts, keeping multi-turn workflows seamless.
  • Every widget call records a citation via openbb_ai.helpers.cite, emitted as a copilotCitationCollection at the end of the run.

Progressive Tool Discovery (Default)

Instead of dumping every tool schema into the context upfront, the adapter wraps toolsets with four meta-tools:

Meta-tool Purpose
list_tools List available tools by group
search_tools Keyword search across tool descriptions
get_tool_schema Fetch the full schema for a specific tool
call_tools Invoke a tool by name

The model fetches schemas only when it needs them, keeping the initial context window small. Deferred flows (widget data, MCP) continue to emit get_widget_data and execute_agent_tool events as before.

To disable and expose all schemas upfront:

adapter = OpenBBAIAdapter(
    agent=agent,
    run_input=run_input,
    enable_progressive_tool_discovery=False,
)

Adding Custom Toolsets

Pass custom or third-party toolsets to the adapter at request time rather than mounting them on Agent. They are merged into the progressive discovery wrapper automatically.

Important: do not also pass these toolsets to Agent(toolsets=[...]) when using the OpenBB adapter — they would appear as both direct and progressive tools.

Tag a toolset with add_to_progressive(...):

from pydantic_ai.toolsets import FunctionToolset
from pydantic_ai.tools import RunContext

from openbb_pydantic_ai import OpenBBDeps
from openbb_pydantic_ai.tool_discovery import add_to_progressive

custom_tools = FunctionToolset[OpenBBDeps](id="custom_agent_tools")


@custom_tools.tool
def earnings_note(ctx: RunContext[OpenBBDeps], symbol: str) -> str:
    _ = ctx
    return f"Custom note for {symbol}"


add_to_progressive(
    custom_tools,
    group="custom_agent_tools",
    description="Custom user tools",
)

# Pass at request time
return await OpenBBAIAdapter.dispatch_request(request, agent=agent, toolsets=[custom_tools])

Or use the @progressive(...) decorator directly on the tool function:

from openbb_pydantic_ai.tool_discovery import progressive


@progressive(toolset=custom_tools, group="custom_agent_tools", description="Custom user tools")
@custom_tools.tool
def earnings_note(ctx: RunContext[OpenBBDeps], symbol: str) -> str:
    _ = ctx
    return f"Custom note for {symbol}"

Untagged toolsets passed at request time are forwarded as standalone toolsets without being merged into the progressive wrapper.

Advanced Usage

Instantiate the adapter manually for full control:

from openbb_pydantic_ai import OpenBBAIAdapter

run_input = OpenBBAIAdapter.build_run_input(body_bytes)
adapter = OpenBBAIAdapter(agent=agent, run_input=run_input)

async for event in adapter.run_stream():
    yield event  # already encoded as OpenBB SSE payloads

message_history, deferred_tool_results, and on_complete callbacks are forwarded directly to Agent.run_stream_events().

Runtime deps & prompts: OpenBBDeps bundles widgets (by priority group), context rows, relevant URLs, workspace state, timezone, and a state dict you can pass to toolsets or output validators. The adapter merges dashboard context and current widget parameter values into the runtime instructions automatically — append your own instructions without re-supplying that context.

Local Development

uv sync --dev
uv run pytest
uv run pre-commit run --all-files  # lint + format

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openbb_pydantic_ai-0.1.8.tar.gz (61.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openbb_pydantic_ai-0.1.8-py3-none-any.whl (75.8 kB view details)

Uploaded Python 3

File details

Details for the file openbb_pydantic_ai-0.1.8.tar.gz.

File metadata

  • Download URL: openbb_pydantic_ai-0.1.8.tar.gz
  • Upload date:
  • Size: 61.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for openbb_pydantic_ai-0.1.8.tar.gz
Algorithm Hash digest
SHA256 9cd8b0f6032fcee30fb925538f3a2585ad41bc6c518d62415927e6bf887867a0
MD5 2b4ba381b5ad8306a35a659e9b4c46f3
BLAKE2b-256 f3c3f9332aa496e0a959d09837b591610340cd8fe7c936f0682826e47c322e21

See more details on using hashes here.

File details

Details for the file openbb_pydantic_ai-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: openbb_pydantic_ai-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 75.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for openbb_pydantic_ai-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 a6056075cfe12a49f86ba7e2c6d88dc6434ba5040bba229a26388c7a9144e457
MD5 275519da814fdc71259d41312429df4b
BLAKE2b-256 9f2c44f3d32bc761f64d8d2d1f6a73c2912df65e688163c6a227c7ba666afbfb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page