Add a Pydantic AI example Bot - #56
Conversation
|
Update: pushed a fix and verified the example end-to-end locally. The first version imported Verified against
README note corrected and the dependency pinned to the tested release ( One open question for maintainers: happy to keep this on Pydantic AI's native AG-UI adapter, or switch to hand-emitted |
|
Keeping this open — it's the only Python example and the direct answer to an enterprise asking whether it can bring its own Python agent, and the earlier import bug is fixed ( |
A third framework example beside LangGraph and Mastra, and the first in another language. A real Pydantic AI agent served over AG-UI: the surface's tools arrive per run and their calls stream back to OpenBot to run through the gateway, so the process drives a governed browser it has no direct access to, the same contract as the Bot in the box. Self-contained (its own pyproject.toml, outside the Bun workspaces), imports the AG-UI helper from whichever module path the installed pydantic-ai exposes, and answers /health like the other examples. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The first version imported handle_ag_ui_request from pydantic_ai.ui.ag_ui with a fallback to pydantic_ai.ag_ui. Neither exists in current pydantic-ai: the module exposes AGUIAdapter, and there is no top-level ag_ui module, so the Bot failed at import before serving a single request. Serve with AGUIAdapter.dispatch_request(request, agent=agent) instead, verified against pydantic-ai 2.33.0: the server boots, /health answers, and a RunAgentInput POST with a granted tool streams RUN_STARTED then the model call (frontend tools accepted, AG-UI events emitted as SSE). Pin the dependency to the tested release and correct the README note. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…efault The endpoint read no header, which is the one thing an example must not teach now that the server sends a Bot's key on every run: it takes a run from anyone who can reach the port. Guarded by REQUIRE_KEY, the same optional guard the LangGraph example carries. 4202 sits in the band the in-box agents use, 4200 and 4201. The examples step by a hundred, 4300 and 4400, and 4500 is the supervisor, so 4600. gpt-4.1 is not what this deployment ships; gpt-5.5 is the default everywhere else. Also record that Pydantic AI calls /responses rather than /v1/chat/completions, which decides whether a given gateway can serve this at all, and ignore the egg-info that the README's own install step leaves behind.
4595dd1 to
a4be71d
Compare
davidmckayv
left a comment
There was a problem hiding this comment.
Merging. The example is right about the contract and I have now driven it, which the PR notes it had not been.
Started LLMock as the model, pointed the example at it, and posted a real RunAgentInput carrying one browser tool the file never names:
- the surface's tool reached the model as an external tool:
modelWasOffered: ["computer_navigate"]; - it came back as
RUN_STARTED→TOOL_CALL_START→TOOL_CALL_ARGS({"url":"https://example.com"}) →TOOL_CALL_END→RUN_FINISHED, and the Python process executed nothing; GET /healthanswers{"status":"ok","framework":"pydantic-ai"}.
Verified against pydantic-ai 2.33.0. pydantic_ai.ui.ag_ui.AGUIAdapter.dispatch_request exists with the signature this calls, so the single import is correct. The PR body describes a fallback import for older module paths that the code does not have, and should not: an import that silently accepts an older API is worse than one that fails.
Four things fixed in a4be71d:
- No key check. The endpoint read no header, so it took a run from anyone who could reach the port. That is the one thing an example must not teach now that the server sends a Bot's key on every run. Added the same optional
REQUIRE_KEYguard the LangGraph example carries. Driven: 401 with no header, 401 with a wrong one, 200 with the right one. - Port. 4202 sits in the band the in-box agents use (4200, 4201). The examples step by a hundred, 4300 and 4400, and 4500 is the supervisor, so 4600.
gpt-4.1. The shipped default everywhere else isgpt-5.5.egg-info. The README's ownpip install -e .leaves a directory the.gitignoremissed.
One thing worth knowing beyond this PR, now recorded in the README: Pydantic AI calls /responses, not /v1/chat/completions. So a gateway serving only chat completions cannot answer this example, and the inverse of the limit the two TypeScript Bots carry, which is that they speak chat completions and so cannot use the models that require Responses.
Rebased onto main, which it was 112 commits behind. format:check and lint clean.
What this changes
A third framework example beside
examples/langgraph-botandexamples/mastra-bot, and the first in another language. A real Pydantic AI agent served over AG-UI: the surface's tools arrive per run inRunAgentInput.tools, Pydantic AI exposes them to the model as external tools, and their calls stream back to OpenBot to run through the governed gateway — so the process drives a browser it has no direct access to, the same contract as the Bot in the box and the two existing examples.Worth doing because the README names Pydantic AI among the frameworks a Bot can be written in, and the strongest evidence that OpenBot is framework- and language-agnostic is a working example in a language that is not TypeScript.
Self-contained: its own
pyproject.tomloutside the Bun workspaces, so it does not touch the JS build. It imports the AG-UI helper from whichever module path the installedpydantic-aiexposes (pydantic_ai.ui.ag_uiin current releases,pydantic_ai.ag_uiin earlier ones) and answers/healthlike the other examples.Where it runs
A customer's own agent process, exactly like
langgraph-botandmastra-bot— reached by OpenBot only as an AG-UI endpoint URL. It holds no OpenBot state; the tool loop stays on the OpenBot client.RunAgentInput.PORT(default 4202), run by the developer the same way the other examples are; not part of the deployed ingress.Boundary and audit
Proof