Skip to content

Commit 7c5c49d

Browse files
committed
chore(ag-ui): address PR feedback
* Remove logging * Remove load_dotenv * Make Agent private * Remove unnecessary type hints * Use 422 instead of 400 for validation errors
1 parent 62d975a commit 7c5c49d

File tree

14 files changed

+145
-244
lines changed

14 files changed

+145
-244
lines changed

docs/ag-ui.md

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,15 @@ an open protocol. Think of it as a universal translator for AI-driven systems
88
no matter what language an agent speaks: AG-UI ensures fluent communication.
99

1010
The team at [Rocket Science](https://www.rocketscience.gg/), contributed the
11-
[AG-UI integration](#ag-ui-adapter) to make it easy to implement the AG-UI
11+
initial version of AG-UI integration to make it easy to implement the AG-UI
1212
protocol with PydanticAI agents.
1313

1414
This also includes an [`Agent.to_ag_ui`][pydantic_ai.Agent.to_ag_ui] convenience
1515
method which simplifies the creation of [`AGUIApp`][pydantic_ai.ag_ui.AGUIApp]
1616
for PydanticAI agents, which is built on top of [Starlette](https://www.starlette.io/),
1717
meaning it's fully compatible with any ASGI server.
1818

19-
## AG-UI Adapter
20-
21-
The [Adapter][pydantic_ai.ag_ui.Adapter] class is an adapter between
22-
PydanticAI agents and the AG-UI protocol written in Python. It provides support
23-
for all aspects of spec including:
24-
25-
- [Events](https://docs.ag-ui.com/concepts/events)
26-
- [Messages](https://docs.ag-ui.com/concepts/messages)
27-
- [State Management](https://docs.ag-ui.com/concepts/state)
28-
- [Tools](https://docs.ag-ui.com/concepts/tools)
29-
30-
### Installation
19+
## Installation
3120

3221
The only dependencies are:
3322

@@ -51,7 +40,7 @@ required AG-UI dependencies:
5140
pip/uv-add 'pydantic-ai-slim[ag-ui]'
5241
```
5342

54-
### Quick start
43+
## Quick start
5544

5645
```py {title="agent_to_ag_ui.py" py="3.10" hl_lines="17-28"}
5746
"""Basic example for AG-UI with FastAPI and Pydantic AI."""
@@ -73,7 +62,16 @@ uvicorn agent_to_ag_ui:app --host 0.0.0.0 --port 8000
7362
This will expose the agent as an AG-UI server, and you can start sending
7463
requests to it.
7564

76-
### Design
65+
## Design
66+
67+
The solution provides and adapter layer between the AG-UI protocol and
68+
PydanticAI agents written in Python, including support for all aspects of spec
69+
including:
70+
71+
- [Events](https://docs.ag-ui.com/concepts/events)
72+
- [Messages](https://docs.ag-ui.com/concepts/messages)
73+
- [State Management](https://docs.ag-ui.com/concepts/state)
74+
- [Tools](https://docs.ag-ui.com/concepts/tools)
7775

7876
The adapter receives messages in the form of a
7977
[`RunAgentInput`](https://docs.ag-ui.com/sdk/js/core/types#runagentinput)
@@ -87,14 +85,13 @@ streamed back to the caller as Server-Sent Events (SSE).
8785
A user request may require multiple round trips between client UI and PydanticAI
8886
server, depending on the tools and events needed.
8987

90-
In addition to the [Adapter][pydantic_ai.ag_ui.Adapter] there is also
91-
[AGUIApp][pydantic_ai.ag_ui.AGUIApp] which is slim wrapper around
92-
[Starlette](https://www.starlette.io/) providing easy access to run a PydanticAI
93-
server with AG-UI support with any ASGI server.
88+
This is exposed via the [AGUIApp][pydantic_ai.ag_ui.AGUIApp] which is slim
89+
wrapper around [Starlette](https://www.starlette.io/) providing easy access to
90+
run a PydanticAI server with AG-UI support with any ASGI server.
9491

95-
### Features
92+
## Features
9693

97-
To expose a PydanticAI agent as an AG-UI server including state support, you can
94+
To expose a PydanticAI agent as an AG-UI server including state support, you
9895
use the [`to_ag_ui`][pydantic_ai.agent.Agent.to_ag_ui] method create an ASGI
9996
compatible server.
10097

@@ -104,7 +101,7 @@ server using the [`StateDeps`][pydantic_ai.ag_ui.StateDeps] which implements the
104101
decode state contained in [`RunAgentInput.state`](https://docs.ag-ui.com/sdk/js/core/types#runagentinput)
105102
when processing requests.
106103

107-
#### State management
104+
### State management
108105

109106
The adapter provides full support for
110107
[AG-UI state management](https://docs.ag-ui.com/concepts/state), which enables
@@ -145,21 +142,15 @@ Since the goal of [`to_ag_ui`][pydantic_ai.agent.Agent.to_ag_ui] is to be a
145142
convenience method, it accepts the same a combination of the arguments require
146143
for:
147144

148-
- [`Adapter`][pydantic_ai.ag_ui.Adapter] constructor
145+
- [`AGUIApp`][pydantic_ai.ag_ui.AGUIApp] constructor
149146
- [`Agent.iter`][pydantic_ai.agent.Agent.iter] method
150147

151-
If you want more control you can either use
152-
[`agent_to_ag_ui`][pydantic_ai.ag_ui.agent_to_ag_ui] helper method or create
153-
and [`Agent`][pydantic_ai.ag_ui.Agent] directly which also provide
154-
the ability to customise [`Starlette`](https://www.starlette.io/applications/#starlette.applications.Starlette)
155-
options.
156-
157-
#### Tools
148+
### Tools
158149

159150
AG-UI tools are seamlessly provided to the PydanticAI agent, enabling rich
160151
use experiences with frontend user interfaces.
161152

162-
#### Events
153+
### Events
163154

164155
The adapter provides the ability for PydanticAI tools to send
165156
[AG-UI events](https://docs.ag-ui.com/concepts/events) simply by defining a tool
@@ -172,17 +163,12 @@ for custom events and state updates.
172163

173164
from __future__ import annotations
174165

175-
from typing import TYPE_CHECKING
176-
177166
from ag_ui.core import CustomEvent, EventType, StateSnapshotEvent
178167
from pydantic import BaseModel
179168

180169
from pydantic_ai import Agent, RunContext
181170
from pydantic_ai.ag_ui import StateDeps
182171

183-
if TYPE_CHECKING:
184-
pass
185-
186172

187173
class DocumentState(BaseModel):
188174
"""State for the document being written."""
@@ -222,9 +208,9 @@ def custom_events() -> list[CustomEvent]:
222208
]
223209
```
224210

225-
### Examples
211+
## Examples
226212

227-
For more examples of how to use [`Adapter`][pydantic_ai.ag_ui.Adapter] see
213+
For more examples of how to use [`to_ag_ui`][pydantic_ai.Agent.to_ag_ui] see
228214
[`pydantic_ai_ag_ui_examples`](https://github.com/pydantic/pydantic-ai/tree/main/examples/pydantic_ai_ag_ui_examples),
229215
which includes working server for the with the
230216
[AG-UI Dojo](https://docs.ag-ui.com/tutorials/debugging#the-ag-ui-dojo) which
@@ -252,7 +238,7 @@ options:
252238
Agent log level (default: info)
253239
```
254240

255-
Run with adapter debug logging:
241+
Run with dojo server with debug logging:
256242

257243
```shell
258244
python -m pydantic_ai_ag_ui_examples.dojo_server --log-level debug
@@ -263,3 +249,9 @@ Using uvicorn:
263249
```shell
264250
uvicorn pydantic_ai_ag_ui_examples.dojo_server:app --port 9000
265251
```
252+
253+
There is also a simplified basic example:
254+
255+
```shell
256+
python -m pydantic_ai_ag_ui_examples.basic
257+
```

examples/pydantic_ai_ag_ui_examples/api/agentic_chat.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,14 @@
55
from datetime import datetime
66
from zoneinfo import ZoneInfo
77

8-
from dotenv import load_dotenv
9-
108
from pydantic_ai import Agent
11-
from pydantic_ai.ag_ui import AGUIApp
12-
13-
# Ensure environment variables are loaded.
14-
load_dotenv()
159

16-
agent: Agent = Agent(
10+
agent = Agent(
1711
'openai:gpt-4o-mini',
1812
output_type=str,
1913
)
2014

21-
app: AGUIApp = agent.to_ag_ui()
15+
app = agent.to_ag_ui()
2216

2317

2418
@agent.tool_plain

examples/pydantic_ai_ag_ui_examples/api/agentic_generative_ui.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,11 @@
77
from typing import Any, Literal
88

99
from ag_ui.core import EventType, StateDeltaEvent, StateSnapshotEvent
10-
from dotenv import load_dotenv
1110
from pydantic import BaseModel, Field
1211

1312
from pydantic_ai import Agent
14-
from pydantic_ai.ag_ui import AGUIApp
1513

16-
# Ensure environment variables are loaded.
17-
load_dotenv()
18-
19-
agent: Agent = Agent(
14+
agent = Agent(
2015
'openai:gpt-4o-mini',
2116
output_type=str,
2217
instructions=dedent(
@@ -35,7 +30,7 @@
3530
),
3631
)
3732

38-
app: AGUIApp = agent.to_ag_ui()
33+
app = agent.to_ag_ui()
3934

4035

4136
class StepStatus(StrEnum):

examples/pydantic_ai_ag_ui_examples/api/human_in_the_loop.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,9 @@
77

88
from textwrap import dedent
99

10-
from dotenv import load_dotenv
11-
1210
from pydantic_ai import Agent
13-
from pydantic_ai.ag_ui import AGUIApp
14-
15-
# Ensure environment variables are loaded.
16-
load_dotenv()
1711

18-
agent: Agent = Agent(
12+
agent = Agent(
1913
'openai:gpt-4o-mini',
2014
output_type=str,
2115
instructions=dedent(
@@ -30,4 +24,4 @@
3024
),
3125
)
3226

33-
app: AGUIApp = agent.to_ag_ui()
27+
app = agent.to_ag_ui()

examples/pydantic_ai_ag_ui_examples/api/predictive_state_updates.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
from typing import TYPE_CHECKING
66

77
from ag_ui.core import CustomEvent, EventType
8-
from dotenv import load_dotenv
98
from pydantic import BaseModel
109

1110
from pydantic_ai import Agent
12-
from pydantic_ai.ag_ui import AGUIApp, StateDeps
11+
from pydantic_ai.ag_ui import StateDeps
1312

1413
if TYPE_CHECKING: # pragma: no cover
1514
from pydantic_ai import RunContext
@@ -21,16 +20,13 @@ class DocumentState(BaseModel):
2120
document: str = ''
2221

2322

24-
# Ensure environment variables are loaded.
25-
load_dotenv()
26-
27-
agent: Agent = Agent(
23+
agent = Agent(
2824
'openai:gpt-4o-mini',
2925
output_type=str,
3026
deps_type=StateDeps[DocumentState],
3127
)
3228

33-
app: AGUIApp = agent.to_ag_ui(deps=StateDeps(DocumentState()))
29+
app = agent.to_ag_ui(deps=StateDeps(DocumentState()))
3430

3531

3632
# Tools which return AG-UI events will be sent to the client as part of the

examples/pydantic_ai_ag_ui_examples/api/shared_state.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
from __future__ import annotations
44

55
from enum import StrEnum
6+
from textwrap import dedent
67
from typing import TYPE_CHECKING
78

89
from ag_ui.core import EventType, StateSnapshotEvent
9-
from dotenv import load_dotenv
1010
from pydantic import BaseModel, Field
1111

1212
from pydantic_ai import Agent
13-
from pydantic_ai.ag_ui import AGUIApp, StateDeps
13+
from pydantic_ai.ag_ui import StateDeps
1414

1515
if TYPE_CHECKING: # pragma: no cover
1616
from pydantic_ai import RunContext
@@ -88,16 +88,13 @@ class RecipeSnapshot(BaseModel):
8888
)
8989

9090

91-
# Ensure environment variables are loaded.
92-
load_dotenv()
93-
94-
agent: Agent = Agent(
91+
agent = Agent(
9592
'openai:gpt-4o-mini',
9693
output_type=str,
9794
deps_type=StateDeps[RecipeSnapshot],
9895
)
9996

100-
app: AGUIApp = agent.to_ag_ui(deps=StateDeps(RecipeSnapshot()))
97+
app = agent.to_ag_ui(deps=StateDeps(RecipeSnapshot()))
10198

10299

103100
@agent.tool_plain
@@ -126,19 +123,22 @@ def recipe_instructions(ctx: RunContext[StateDeps[RecipeSnapshot]]) -> str:
126123
Returns:
127124
Instructions string for the recipe generation agent.
128125
"""
129-
return f"""You are a helpful assistant for creating recipes.
126+
return dedent(
127+
f"""
128+
You are a helpful assistant for creating recipes.
130129
131-
IMPORTANT:
132-
- Create a complete recipe using the existing ingredients
133-
- Append new ingredients to the existing ones
134-
- Use the `display_recipe` tool to present the recipe to the user
135-
- Do NOT repeat the recipe in the message, use the tool instead
130+
IMPORTANT:
131+
- Create a complete recipe using the existing ingredients
132+
- Append new ingredients to the existing ones
133+
- Use the `display_recipe` tool to present the recipe to the user
134+
- Do NOT repeat the recipe in the message, use the tool instead
136135
137-
Once you have created the updated recipe and displayed it to the user,
138-
summarise the changes in one sentence, don't describe the recipe in
139-
detail or send it as a message to the user.
136+
Once you have created the updated recipe and displayed it to the user,
137+
summarise the changes in one sentence, don't describe the recipe in
138+
detail or send it as a message to the user.
140139
141-
The current state of the recipe is:
140+
The current state of the recipe is:
142141
143-
{ctx.deps.state.recipe.model_dump_json(indent=2)}
144-
"""
142+
{ctx.deps.state.recipe.model_dump_json(indent=2)}
143+
""",
144+
)

examples/pydantic_ai_ag_ui_examples/api/tool_based_generative_ui.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,11 @@
55

66
from __future__ import annotations
77

8-
from dotenv import load_dotenv
9-
108
from pydantic_ai import Agent
11-
from pydantic_ai.ag_ui import AGUIApp
12-
13-
# Ensure environment variables are loaded.
14-
load_dotenv()
159

16-
agent: Agent = Agent(
10+
agent = Agent(
1711
'openai:gpt-4o-mini',
1812
output_type=str,
1913
)
2014

21-
app: AGUIApp = agent.to_ag_ui()
15+
app = agent.to_ag_ui()

examples/pydantic_ai_ag_ui_examples/basic.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from pydantic_ai import Agent
66

7-
agent: Agent[None, str] = Agent(
7+
agent = Agent(
88
'openai:gpt-4o-mini',
99
instructions='You are a helpful assistant.',
1010
)
@@ -13,12 +13,12 @@
1313
if __name__ == '__main__':
1414
import uvicorn
1515

16-
from .cli import Args, parse_args
16+
from .cli.args import parse_args
1717

18-
args: Args = parse_args()
18+
args = parse_args()
1919

2020
uvicorn.run(
21-
'pydantic_ai_ag_ui_examples.dojo_server:app',
21+
'pydantic_ai_ag_ui_examples.basic:app',
2222
port=args.port,
2323
reload=args.reload,
2424
log_level=args.log_level,
Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1 @@
11
"""Command line interface for the PydanticAI AG-UI servers."""
2-
3-
from __future__ import annotations
4-
5-
from .args import Args, parse_args
6-
7-
__all__ = [
8-
'Args',
9-
'parse_args',
10-
]

0 commit comments

Comments
 (0)