-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add Agent.to_web() method and web chat UI #3456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| @@ -0,0 +1,186 @@ | |||
| """Agent discovery using AST parsing to find pydantic_ai.Agent objects.""" | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is cool but I think it's too much magic, at least for the first version of this feature (I wouldn't throw the code away; we could consider it as a separate PR later). I think the existing -a AGENT flag is sufficient for now.
| args = parser.parse_args(args_list) | ||
|
|
||
| # Handle web subcommand | ||
| if args.command == 'web': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it should be --web so it doesn't conflict with the prompt arg?
|
|
||
| self._get_toolset().apply(_set_sampling_model) | ||
|
|
||
| def to_web(self) -> Any: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're gonna need some args here -- have a look at the to_a2a and to_ag_ui methods. Not saying we need all of those args, but some may be useful
| This method returns a pre-configured FastAPI application that provides a web-based | ||
| chat interface for interacting with the agent. The UI is served from a CDN and | ||
| includes support for model selection and builtin tool configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The is that the UI would be downloaded and cached on first use, so it's not meant to always be "served from a CDN"
| @@ -0,0 +1,75 @@ | |||
| """Model and builtin tool configurations for the web chat UI.""" | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The goal is for the developer to be able to pass a list of models and builtin tools to to_web, instead of anything hard-coded they may not have keys for. We'd need a new field like supported_builtin_tools on ModelProfile to store for each model/provider which builtin tools it supports. Then we can automatically generate the structures below based on the user-provided data and which tools work with which models.
| name: str | ||
|
|
||
|
|
||
| BUILTIN_TOOL_DEFS: list[BuiltinTool] = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Friendly names for builtin tools can go in builtin_tools.py
| from .agent_options import AIModel, BuiltinTool | ||
| from .api import create_api_router | ||
|
|
||
| CDN_URL = 'https://cdn.jsdelivr.net/npm/@pydantic/ai-chat-ui/dist/index.html' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should pin a specific version, in case we make backward-incompatible changes at some point
| async def index(request: Request): # pyright: ignore[reportUnusedFunction] | ||
| """Serve the chat UI from CDN.""" | ||
| async with httpx.AsyncClient() as client: | ||
| response = await client.get(CDN_URL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should cache this somewhere
| response = client.get('/api/configure') | ||
| assert response.status_code == 200 | ||
| data = response.json() | ||
| assert 'models' in data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a snapshot please :)
web-based chat interface for Pydantic AI agents
pydantic_ai.ui.webAgent.to_web()fastapi
app = create_chat_app(agent)the following endpoints come preconfigured:
GET /and/:id- serve the chat UIPOST /api/chat- Main chat endpoint using VercelAIAdapterGET /api/configure- Returns available models and builtin toolsGET /api/health- Health checkoptions and example
NOTE: the module for options is currently
pydantic_ai.ui.web.pre-configured model options:
anthropic:claude-sonnet-4-5openai-responses:gpt-5google-gla:gemini-2.5-prosupported builtin tools:
web_searchcode_executionimage_generationtesting
tests/test_ui_web.pynotes
@pydantic/[email protected]clai webcommand to launch from the CLI (as inuvx pydantic-workwithout the whole URL magic)docs/ui/to_web.md? I'd also reference this indocs/ui/overview.mdanddocs/agents.mdEDIT: if you try it out it's worth noting that the current hosted UI doesn't handle
ErrorChunks, so you will get no spinner and no response when there's a model-level error and fastapi will return a 200 any way.This will happen for instance when you use a model for which you don't have a valid API key in your environment
I opened a PR for the error chunks here pydantic/ai-chat-ui#4.
Closes #3295