Skip to content

Commit 3450322

Browse files
MikeRyanDevDouweM
andauthored
fix: Change handling of empty state objects for AG-UI (#3462)
Co-authored-by: Douwe Maan <[email protected]>
1 parent 757d409 commit 3450322

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

pydantic_ai_slim/pydantic_ai/ui/ag_ui/_adapter.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
from __future__ import annotations
44

5-
from collections.abc import Sequence
5+
from collections.abc import Mapping, Sequence
66
from functools import cached_property
77
from typing import (
88
TYPE_CHECKING,
99
Any,
10+
cast,
1011
)
1112

1213
from ... import ExternalToolset, ToolDefinition
@@ -107,7 +108,14 @@ def toolset(self) -> AbstractToolset[AgentDepsT] | None:
107108
@cached_property
108109
def state(self) -> dict[str, Any] | None:
109110
"""Frontend state from the AG-UI run input."""
110-
return self.run_input.state
111+
state = self.run_input.state
112+
if state is None:
113+
return None
114+
115+
if isinstance(state, Mapping) and not state:
116+
return None
117+
118+
return cast('dict[str, Any]', state)
111119

112120
@classmethod
113121
def load_messages(cls, messages: Sequence[Message]) -> list[ModelMessage]:

tests/test_ag_ui.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,27 @@ async def simple_stream(messages: list[ModelMessage], agent_info: AgentInfo) ->
226226
yield '(no tool calls)'
227227

228228

229+
async def test_agui_adapter_state_none() -> None:
230+
"""Ensure adapter exposes `None` state when no frontend state provided."""
231+
agent = Agent(
232+
model=FunctionModel(stream_function=simple_stream),
233+
)
234+
235+
run_input = RunAgentInput(
236+
thread_id=uuid_str(),
237+
run_id=uuid_str(),
238+
messages=[],
239+
state=None,
240+
context=[],
241+
tools=[],
242+
forwarded_props=None,
243+
)
244+
245+
adapter = AGUIAdapter(agent=agent, run_input=run_input, accept=None)
246+
247+
assert adapter.state is None
248+
249+
229250
async def test_basic_user_message() -> None:
230251
"""Test basic user message with text response."""
231252
agent = Agent(
@@ -1193,6 +1214,24 @@ async def test_request_with_state_without_handler() -> None:
11931214
pass
11941215

11951216

1217+
async def test_request_with_empty_state_without_handler() -> None:
1218+
agent = Agent(model=FunctionModel(stream_function=simple_stream))
1219+
1220+
run_input = create_input(
1221+
UserMessage(
1222+
id='msg_1',
1223+
content='Hello, how are you?',
1224+
),
1225+
state={},
1226+
)
1227+
1228+
events = list[dict[str, Any]]()
1229+
async for event in run_ag_ui(agent, run_input):
1230+
events.append(json.loads(event.removeprefix('data: ')))
1231+
1232+
assert events == simple_result()
1233+
1234+
11961235
async def test_request_with_state_with_custom_handler() -> None:
11971236
@dataclass
11981237
class CustomStateDeps:

0 commit comments

Comments
 (0)