Repository navigation
Python / AG-UI: Recommended way to JSON-serialize AGUIThreadSnapshot for a durable store?
#9189
Replies: 3 comments 1 reply
|
From reading the code I'd treat it as the application's job. make_json_safe is a last-resort "json.dumps must not raise" pass, its docstring says as much, and it's lossy by design (set -> str, dataclass -> dict): agent-framework/python/packages/core/agent_framework/_serialization.py Lines 733 to 746 in 40763be The core durable path is stricter. AgentSession.to_dict() warns on unregistered values with "durable session stores will reject it. Call register_state_type()", and the SessionStore codec runs _validate_durable_state_value, which rejects non-finite floats and anything that isn't a JSON scalar/list/mapping: agent-framework/python/packages/core/agent_framework/_sessions.py Lines 449 to 466 in 40763be The AG-UI snapshot path just doesn't apply that validation. It stringifies instead and logs/swallows failures so a finished run still gets RUN_FINISHED. What I'd do: register every custom session_state type with register_state_type(cls, type_id="yourapp.x", encoder=..., decoder=...). With that, to_dict emits {"...": ..., "type": "yourapp.x"} and from_dict rebuilds the instance. I checked this round-trips through json.dumps/json.loads on 1.20.0. Convert Enums, sets and NaN/inf yourself before they go into state or Shared State. Then in your store's save(), serialize strictly with json.dumps(dataclasses.asdict(snapshot), allow_nan=False), and reject NUL if your backend can't take it (Postgres jsonb rejects \u0000). The framework will log the failure and keep the previous snapshot, which beats silently storing "{1, 2}". The Enum RecursionError looks like a bug in make_json_safe to me (it walks vars() into the enum class), probably worth a separate issue. I don't know whether the maintainers consider the str() fallback part of the contract, but nothing in the snapshot code depends on it round-tripping. |
|
Short answer: keep the values JSON-native on the application side. The core package holds its own durable session snapshots to a stricter contract, and that contract is a good one to copy. What I would do in a database-backed
|
|
Kurt Geminic (@kgeminicdev) Rodion Kazennov (@nefayran) Thank you both, this was really helpful. I'll use the approach you described as the basis for my implementation. The key point for me is to guarantee the round trip with tests: what the store's One note on my design: since As you suggested, I'll also open a separate issue for the |
Uh oh!
There was an error while loading. Please reload this page.
I'd like to implement a durable
agent_framework_ag_ui.AGUIThreadSnapshotStorebacked by a database, usingagent-framework-core1.20.0 andagent-framework-ag-ui1.5.0. My goal is that snapshots round-trip without errors and without silent data loss.What I observed
session_stateandstatepass throughagent_framework._serialization.make_json_safe()before reaching the store. In most cases it does not raise; unknown types fall back tostr():set({1, 2})→"{1, 2}"AgentSession.state→ restored as a plaindictSome values still are not valid strict JSON:
NaN/Infinity\u0000A plain
Enummakesmake_json_safe()raiseRecursionError.Failures are logged and swallowed (as designed in Python: bridge AG-UI request state and session continuity #7084), with different outcomes:
_safe_serialize_session_continuation_state(): if serializingsession_statefails (including theRecursionErrorabove), the snapshot is still saved withsession_state=None.ThreadSnapshotSession.save(): if the store rejects the snapshot (e.g.NaNor\u0000), the new snapshot is not saved and the previous one remains.In both cases the run completes normally.
Question
Is it the application's responsibility to keep snapshot values strictly JSON-native, converting them before they reach the runner (with custom
session_statetypes registered viaagent_framework.register_state_type())? Or is the stringification bymake_json_safe()meant to be relied on?All reactions