State Management with AG-UI #2966
-
|
Dear agent-framework community, my goal is to deterministically edit the state of an agent via a tool. However, all I found in the demos was, how the agent state is edited by the predict_state via the predict_state_config. However, the predict_state consists of non-deterministically generated tool arguments. Motivation:
I found this pydantic_ai demo, where the tool emits a StateDeltaEvent, but the agent_framework tools would not let me return events. @agent.tool_plain
async def update_steps(steps: list[str]) -> StateSnapshotEvent:
"""Update the steps of the agent."""
return StateSnapshotEvent(
type=EventType.STATE_SNAPSHOT,
snapshot={
"observed_steps": steps
}
)How would this be done using agent_framework? Thanks for your help! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
|
Hi @tschokokuki, thanks for bringing this up. It is a current gap. As a potential workaround, one could intercept the agent stream and inject from ag_ui.core import StateSnapshotEvent
async def intercept_stream():
async for update in agent.run_stream(messages):
yield update
for content in update.contents:
if isinstance(content, FunctionResultContent) and content.name == "get_weather":
# Emit state based on actual tool result
yield StateSnapshotEvent(snapshot={"weather": content.result})Otherwise we will need to add handling for tools to directly emit state events. Would you be interested in contributing to this? The relevant code is in |
Beta Was this translation helpful? Give feedback.
-
|
Hi @moonbox3, Thank you very much for pointing me in the right direction. I got the workaround running as you suggested. From my side, I added one more requirement: I want to pass a custom message for the LLM rather than relying on a default one. I wonder, what would be the best return format for the Tools in context of the agent_framework project. Option 1: StateDeltaEventPro:
Cons:
@ai_function()
def get_weather() -> StateDeltaEvent:
weather = "sunny"
return StateDeltaEvent(
delta=[{"op": "replace", "path": "/weather", "value": weather}],
result_message=f"Weather updated to: {weather}")Option 2: Dictionary with a markerPro:
Cons:
@ai_function()
def get_weather() -> Dict:
weather = "sunny"
return {
"__agui_event__": True,
"event_type": "StateDeltaEvent",
"event_data": {
"delta": [{"op": "replace", "path": "/weather", "value": weather}]
},
"result_message": f"Weather updated to: {weather}"
}In my opinion 1 would be preferable. Before dedicating myself to the final solution, what would be the prefered option in your opinion? Thanks a lot! |
Beta Was this translation helpful? Give feedback.
@tschokokuki Those are both valid options; however, I believe a third approach may be work better.
A few comments about the options you listed:
Option 1 (returning
StateDeltaEventdirectly) has a fundamental issue:FunctionResultContent.resultexpects JSON-serializable data, not event objects. This is why you're seeing serialization failures and result: null in thread state. Theresult_messagefield is a workaround that introduces coupling between the event schema and the result schema.Option 2 (Dict with marker) avoids the serialization issue but introduces sentinel detection (
__agui_event__), pollutes the result schema, and creates artifacts in thread state that downstream consumers …