Skip to content
Open
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
1cfa48e
add Agent.to_web() method and web chat UI module #3295
dsfaccini Nov 17, 2025
d83caa5
add "web" group
dsfaccini Nov 17, 2025
bfffd4b
try import create_chat_app
dsfaccini Nov 17, 2025
055e120
fix tests to run on CI
dsfaccini Nov 17, 2025
6bc8b16
fix example to use tool_plain
dsfaccini Nov 17, 2025
32f7e1d
add clai web - tested with uv run clai web
dsfaccini Nov 18, 2025
cf0e177
wip: remove agent discovery and fix tests
dsfaccini Nov 20, 2025
e7f44eb
rename command
dsfaccini Nov 20, 2025
c4ffde3
rename function
dsfaccini Nov 20, 2025
0595c27
- define builtin tool ids
dsfaccini Nov 21, 2025
0d24941
fix tests
dsfaccini Nov 21, 2025
e5b30c2
- update CLI commands and improve agent loading mechanism
dsfaccini Nov 21, 2025
f2dd19a
Merge branch 'main' into clai-chat
dsfaccini Nov 21, 2025
8ca7a27
import sorting
dsfaccini Nov 21, 2025
fa3bb5f
more import sorting
dsfaccini Nov 21, 2025
e45c93f
covergae?
dsfaccini Nov 22, 2025
f90b570
Merge upstream/main into clai-chat
dsfaccini Nov 22, 2025
da8032c
Merge upstream/main into clai-chat
dsfaccini Nov 26, 2025
0ab07b6
- remove agent_options - add supported_builtin_tools - swap fastapi f…
dsfaccini Nov 26, 2025
191897f
refactor from --web to web, adjust flags, add tests, update docs
dsfaccini Nov 26, 2025
1faae3f
remove clai import on test
dsfaccini Nov 26, 2025
8423e89
coverage
dsfaccini Nov 26, 2025
a32c15a
- consolidate web docs in own doc
dsfaccini Nov 27, 2025
91f9533
remove memory cache and swap prints for consoles
dsfaccini Nov 27, 2025
441d6a0
use snapshots where it makes sense
dsfaccini Nov 27, 2025
558985f
Merge branch 'main' into clai-chat
dsfaccini Nov 27, 2025
85f63bc
make _web a private module
dsfaccini Nov 28, 2025
cd91e81
move supported_builtin_tools to abstract tool set type
dsfaccini Nov 28, 2025
a132f26
warning about memory tool
dsfaccini Nov 29, 2025
943c7a8
move name formatting to model method
dsfaccini Nov 29, 2025
831bdf3
move to toolsets
dsfaccini Nov 29, 2025
125c059
update docstring
dsfaccini Nov 29, 2025
025f1b5
take agent's own model into account
dsfaccini Nov 29, 2025
0701b22
bit of docs
dsfaccini Nov 29, 2025
40b5c78
move builtin tool support to profile and move check to base model class
dsfaccini Nov 29, 2025
0673277
fix test profile classes to avoid ci api key error
dsfaccini Nov 29, 2025
974bac2
Merge branch 'main' into clai-chat
dsfaccini Nov 29, 2025
4b58307
coverage
dsfaccini Nov 29, 2025
74f09e9
add anthropic prefix
dsfaccini Dec 1, 2025
897e4e4
fix naming
dsfaccini Dec 1, 2025
10d32fe
remove get agent utility and make supported builtin tools a classmethod
dsfaccini Dec 1, 2025
1c93cf0
remove test
dsfaccini Dec 1, 2025
b60f2ff
Add screenshot link for web chat UI
dsfaccini Dec 3, 2025
ee21cfb
- remove builtin_tools suport check for specific models, base model t…
dsfaccini Dec 3, 2025
cf1da90
support double instructions
dsfaccini Dec 3, 2025
372db8b
remove program check, tie tool arg options to constant and set up aut…
dsfaccini Dec 4, 2025
32cc5c5
merge
dsfaccini Dec 4, 2025
48094d2
add support for model instances, deps and settings
dsfaccini Dec 4, 2025
cd1e683
fix tests after cli adjustments
dsfaccini Dec 4, 2025
c1ba780
separate chat command
dsfaccini Dec 4, 2025
6984dfd
Merge branch 'main' into clai-chat
dsfaccini Dec 4, 2025
cf54985
Merge branch 'main' into clai-chat
dsfaccini Dec 4, 2025
106c8b9
- remove mcp support for now
dsfaccini Dec 4, 2025
f491e57
Merge branch 'main' into clai-chat
dsfaccini Dec 4, 2025
16e36dc
narrow types
dsfaccini Dec 4, 2025
48bd838
Merge branch 'main' into clai-chat
dsfaccini Dec 5, 2025
b45156c
Update clai/README.md
dsfaccini Dec 9, 2025
3733a56
Merge branch 'main' into clai-chat
dsfaccini Dec 9, 2025
b57fb38
Update clai/README.md
dsfaccini Dec 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 68 additions & 22 deletions clai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,57 +25,103 @@ export OPENAI_API_KEY='your-api-key-here'
Then with [`uvx`](https://docs.astral.sh/uv/guides/tools/), run:

```bash
uvx clai
uvx clai chat
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm can we keep clai working as it did, and not require this new argument?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

we can, I though this would be cleaner/less confusing

```

Or to install `clai` globally [with `uv`](https://docs.astral.sh/uv/guides/tools/#installing-tools), run:

```bash
uv tool install clai
...
clai
clai chat
```

Or with `pip`, run:

```bash
pip install clai
...
clai
clai chat
```

Either way, running `clai` will start an interactive session where you can chat with the AI model. Special commands available in interactive mode:
Either way, running `clai chat` will start an interactive session where you can chat with the AI model. Special commands available in interactive mode:

- `/exit`: Exit the session
- `/markdown`: Show the last response in markdown format
- `/multiline`: Toggle multiline input mode (use Ctrl+D to submit)
- `/cp`: Copy the last response to clipboard

## Web Chat UI

Launch a web-based chat interface for your agent:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's first have an example without an agent, to show that it will work that way as well


```bash
clai web --agent module:agent_variable
```

![Web Chat UI](https://github.com/user-attachments/assets/8a1c90dc-f62b-4e35-9d66-59459b45790d)

For example, if you have an agent defined in `my_agent.py`:

```python
from pydantic_ai import Agent

my_agent = Agent('openai:gpt-5', system_prompt='You are a helpful assistant.')
```

Launch the web UI with:

```bash
clai web --agent my_agent:my_agent
```

This will start a web server (default: http://127.0.0.1:7932) with a chat interface for your agent.

### Web Command Options

- `--agent`, `-a`: Agent to serve in `module:variable` format
- `--model`, `-m`: Model to make available (repeatable, agent's model is default if present)
- `--tool`, `-t`: [Builtin tool](https://ai.pydantic.dev/builtin-tools/) to enable (repeatable). See [available tools](https://ai.pydantic.dev/ui/web/#builtin-tool-support).
- `--instructions`, `-i`: System instructions. In generic mode (no `--agent`), these are the agent instructions. With `--agent`, these are passed as extra instructions to each run.
- `--host`: Host to bind the server to (default: 127.0.0.1)
- `--port`: Port to bind the server to (default: 7932)

### Using with Models and Tools

You can specify which models and builtin tools are available in the UI via CLI flags:

```bash
# Generic agent with specific models and tools
clai web -m openai:gpt-5 -m anthropic:claude-sonnet-4-5 -t web_search -t code_execution

# Custom agent with additional models
clai web --agent my_agent:my_agent -m openai:gpt-5 -m google:gemini-2.5-pro

# Generic agent with system instructions
clai web -m openai:gpt-5 -i 'You are a helpful coding assistant'

# Custom agent with extra instructions for each run
clai web --agent my_agent:my_agent -i 'Always respond in Spanish'
```

When using `--agent`, the agent's configured model becomes the default. CLI models (`-m`) are additional options. Without `--agent`, the first `-m` model is the default.

For full documentation, see [Web Chat UI](https://ai.pydantic.dev/ui/web/).

## Help

```
usage: clai [-h] [-m [MODEL]] [-a AGENT] [-l] [-t [CODE_THEME]] [--no-stream] [--version] [prompt]
usage: clai [-h] [-l] [--version] {chat,web} ...

Pydantic AI CLI v...

Special prompts:
* `/exit` - exit the interactive mode (ctrl-c and ctrl-d also work)
* `/markdown` - show the last markdown output of the last question
* `/multiline` - toggle multiline mode
* `/cp` - copy the last response to clipboard

positional arguments:
prompt AI Prompt, if omitted fall into interactive mode
{chat,web} Available commands
chat Interactive chat with an AI model
Copy link
Collaborator

Choose a reason for hiding this comment

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

Related to the above, can we make this the default somehow instead of requiring it?

web Launch web chat UI for an agent

options:
-h, --help show this help message and exit
-m [MODEL], --model [MODEL]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Removing all of this makes this Help section pretty useless, so we should either have a new section listing the options (as we did for clai web), or we should see if there's a way to get this to list the default options for the default chat mode

Model to use, in format "<provider>:<model>" e.g. "openai:gpt-5" or "anthropic:claude-sonnet-4-5". Defaults to "openai:gpt-5".
-a AGENT, --agent AGENT
Custom Agent to use, in format "module:variable", e.g. "mymodule.submodule:my_agent"
-l, --list-models List all available models and exit
-t [CODE_THEME], --code-theme [CODE_THEME]
Which colors to use for code, can be "dark", "light" or any theme from pygments.org/styles/. Defaults to "dark" which works well on dark terminals.
--no-stream Disable streaming from the model
--version Show version and exit
-h, --help show this help message and exit
-l, --list-models List all available models and exit
--version Show version and exit
```
71 changes: 65 additions & 6 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,46 +22,105 @@ export OPENAI_API_KEY='your-api-key-here'
Then with [`uvx`](https://docs.astral.sh/uv/guides/tools/), run:

```bash
uvx clai
uvx clai chat
```

Or to install `clai` globally [with `uv`](https://docs.astral.sh/uv/guides/tools/#installing-tools), run:

```bash
uv tool install clai
...
clai
clai chat
```

Or with `pip`, run:

```bash
pip install clai
...
clai
clai chat
```

Either way, running `clai` will start an interactive session where you can chat with the AI model. Special commands available in interactive mode:
Either way, running `clai chat` will start an interactive session where you can chat with the AI model. Special commands available in interactive mode:

- `/exit`: Exit the session
- `/markdown`: Show the last response in markdown format
- `/multiline`: Toggle multiline input mode (use Ctrl+D to submit)
- `/cp`: Copy the last response to clipboard

### Web Chat UI
Copy link
Collaborator

Choose a reason for hiding this comment

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

There's a lot of duplication here with clai/README.md. Maybe that readme should just link to this page?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah I wasn't sure about it as well, I thought there maybe be people who look more at the repo than the docs, I know I sometimes do, so I left it in both places, but I can just link to the docs from the readme.


Launch a web-based chat interface for your agent:

```bash
clai web --agent module:agent_variable
```

For example, if you have an agent defined in `my_agent.py`:

```python
from pydantic_ai import Agent

my_agent = Agent('openai:gpt-5', instructions='You are a helpful assistant.')
```

Launch the web UI with:

```bash
clai web --agent my_agent:my_agent
```

This will start a web server (default: http://127.0.0.1:7932) with a chat interface for your agent.

#### CLI Options

```bash
# With a custom agent
clai web --agent my_module:my_agent

# With specific models (first is default when no --agent)
clai web -m openai:gpt-5 -m anthropic:claude-sonnet-4-5

# With builtin tools
clai web -m openai:gpt-5 -t web_search -t code_execution

# Generic agent with system instructions
clai web -m openai:gpt-5 -i 'You are a helpful coding assistant'

# Custom agent with extra instructions for each run
clai web --agent my_module:my_agent -i 'Always respond in Spanish'
```

| Option | Description |
|--------|-------------|
| `--agent`, `-a` | Agent to serve in `module:variable` format |
| `--model`, `-m` | Model to make available (repeatable, agent's model is default if present) |
| `--tool`, `-t` | [Builtin tool](builtin-tools.md) to enable (repeatable). See [available tools](ui/web.md#builtin-tool-support). |
Copy link
Collaborator

Choose a reason for hiding this comment

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

Some of the notes from above also apply here

| `--instructions`, `-i` | System instructions. In generic mode (no `--agent`), these are the agent instructions. With `--agent`, these are passed as extra instructions to each run. |
| `--host` | Host to bind server (default: 127.0.0.1) |
| `--port` | Port to bind server (default: 7932) |

!!! note "Memory Tool"
The `memory` tool requires the agent to have memory configured and cannot be enabled via `-t memory` alone. An agent with memory must be provided via `--agent`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's link to the docs that explain this


For programmatic usage with `Agent.to_web()`, see the [Web UI documentation](ui/web.md).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
For programmatic usage with `Agent.to_web()`, see the [Web UI documentation](ui/web.md).
The web chat UI can also be launched programmatically using `Agent.to_web()`, see the [Web UI documentation](ui/web.md).


### Help

To get help on the CLI, use the `--help` flag:

```bash
uvx clai --help
uvx clai chat --help
uvx clai web --help
```

### Choose a model

You can specify which model to use with the `--model` flag:

```bash
uvx clai --model anthropic:claude-sonnet-4-0
uvx clai chat --model anthropic:claude-sonnet-4-0
```

(a full list of models available can be printed with `uvx clai --list-models`)
Expand All @@ -79,7 +138,7 @@ agent = Agent('openai:gpt-5', instructions='You always respond in Italian.')
Then run:

```bash
uvx clai --agent custom_agent:agent "What's the weather today?"
uvx clai chat --agent custom_agent:agent "What's the weather today?"
```

The format must be `module:variable` where:
Expand Down
116 changes: 116 additions & 0 deletions docs/ui/web.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Web Chat UI

Pydantic AI includes a built-in web chat interface that you can use to interact with your agents through a browser.

<video src="https://github.com/user-attachments/assets/8a1c90dc-f62b-4e35-9d66-59459b45790d" autoplay loop muted playsinline></video>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's just have a screenshot; I like videos but this one takes too many seconds to get to point, shows an error first, and has a typo in the initial prompt 😄


## Installation

Install the `web` extra (installs Starlette):

```bash
pip/uv-add 'pydantic-ai-slim[web]'
```

For CLI usage with `clai web`, see the [CLI documentation](../cli.md#web-chat-ui).

## Usage

Create a web app from an agent instance using [`Agent.to_web()`][pydantic_ai.agent.Agent.to_web]:

=== "Using Model Names"
Copy link
Collaborator

Choose a reason for hiding this comment

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

We haven't explained yet what these model names do, so we should start with a simple example that just does to_web and then shows how to run it using uvicorn, and then we can have a next section about configuring available models and builtin tools, where we can explain (in text, not code comments) that models can either be a list of strings (followed by an example) or a dict of labels to instances (followed by an example). And then the built-in tools example should be in its own section


```python
from pydantic_ai import Agent
from pydantic_ai.builtin_tools import WebSearchTool

model = 'openai:gpt-5'
agent = Agent(model)

@agent.tool_plain
def get_weather(city: str) -> str:
return f'The weather in {city} is sunny'

# Create app with model names (their display names are auto-generated)
app = agent.to_web(
models=['openai:gpt-5', 'anthropic:claude-sonnet-4-5'],
builtin_tools=[WebSearchTool()],
)

# Or with custom display labels
app = agent.to_web(
models={'GPT 5': 'openai:gpt-5', 'Claude': 'anthropic:claude-sonnet-4-5'},
builtin_tools=[WebSearchTool()],
)
```

=== "Using Model Instances"

```python
from pydantic_ai import Agent
from pydantic_ai.builtin_tools import WebSearchTool
from pydantic_ai.models.anthropic import AnthropicModel
from pydantic_ai.models.openai import OpenAIChatModel

# Create separate models with their own custom configuration
anthropic_model = AnthropicModel('claude-sonnet-4-5')
openai_model = OpenAIChatModel('gpt-5', provider='openai')

agent = Agent(openai_model)

@agent.tool_plain
def get_weather(city: str) -> str:
return f'The weather in {city} is sunny'

# Use the instances directly
app = agent.to_web(
models=[openai_model, anthropic_model],
builtin_tools=[WebSearchTool()],
)

# Or mix instances and strings with custom labels
app = agent.to_web(
models={'Custom GPT': openai_model, 'Claude': 'anthropic:claude-sonnet-4-5'},
builtin_tools=[WebSearchTool()],
)

# With extra instructions passed to each run
app = agent.to_web(
models=[openai_model],
instructions='Always respond in a friendly tone.',
)
```

The returned Starlette app can be run with any ASGI server:

```bash
uvicorn my_module:app --host 0.0.0.0 --port 8080
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's use the same port the CLI uses

```

## Builtin Tool Support
Copy link
Collaborator

Choose a reason for hiding this comment

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

Related to what I wrote above, a separate section makes sense, but we need to explain the feature first instead of diving straight into implementation details like "Builtin tool support is automatically determined from each model's profile".

So something like, "you can can specify a list of built-in tools that will be shown as options to the user, if the selected model supports it"


Builtin tool support is automatically determined from each model's profile. The UI will only show tools that the selected model supports.

Available [builtin tools](../builtin-tools.md):

- `web_search` - Web search capability ([`WebSearchTool`][pydantic_ai.builtin_tools.WebSearchTool])
- `code_execution` - Code execution in a sandbox ([`CodeExecutionTool`][pydantic_ai.builtin_tools.CodeExecutionTool])
- `image_generation` - Image generation ([`ImageGenerationTool`][pydantic_ai.builtin_tools.ImageGenerationTool])
- `web_fetch` - Fetch content from URLs ([`WebFetchTool`][pydantic_ai.builtin_tools.WebFetchTool])
- `memory` - Persistent memory across conversations ([`MemoryTool`][pydantic_ai.builtin_tools.MemoryTool])
Copy link
Collaborator

Choose a reason for hiding this comment

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

This will get outdated, so I'd much prefer to only link to the other docs and then only explicitly mention the tools that aren't supported, like memory.

Those other docs don't currently mention the string names of the tools, but we don't support strings on agent.run(builtin_tools=...) either, so I'd be fine with requiring instances here as well. (Or I guess at some point supporting strings there as well, and then documenting the strings in the main builtin tools docs)


!!! note "Memory Tool Requirements"
The `memory` tool requires the agent to have memory configured via the
`memory` parameter when creating the agent.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is confusing/incorrect: there's no memory parameter on the agent.

The MemoryTool requires the agent to have a memory tool, but if that exists, I think the agent should always have the memory builtin tool enabled at agent construction time. So here, we can just say that the memory tool is not supported via this route (same for the CLI).



## Reserved Routes

The web UI app uses the following routes which should not be overwritten:

- `/` and `/{id}` - Serves the chat UI
- `/api/chat` - Chat endpoint (POST, OPTIONS)
- `/api/configure` - Frontend configuration (GET)
- `/api/health` - Health check (GET)

The app cannot currently be mounted at a subpath (e.g., `/chat`) because the UI expects these routes at the root. You can add additional routes to the app, but avoid conflicts with these reserved paths.
4 changes: 3 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,12 @@ nav:
- Overview: ui/overview.md
- AG-UI: ui/ag-ui.md
- Vercel AI: ui/vercel-ai.md
- Web Chat UI: ui/web.md
Copy link
Collaborator

Choose a reason for hiding this comment

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

To the user, this is a Pydantic AI feature, not a UI event stream integration (even if it technically uses that functionality), so it shouldn't be under this section. Instead, let's make it its own dedicated page under Documentation above. We can just move it to web.md as well, no ui/ needed.

- Agent2Agent (A2A): a2a.md

- Related Packages:
- Clai: cli.md
- Clai:
- CLI: cli.md
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please revert this change, it can be its own item


- Examples:
- Setup: examples/setup.md
Expand Down
Loading