Skip to content

Commit db4a462

Browse files
authored
fix: bump RunState schema to 1.1 with explicit backward-read policy (#2502)
1 parent 335b7e6 commit db4a462

File tree

2 files changed

+40
-5
lines changed

2 files changed

+40
-5
lines changed

src/agents/run_state.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,13 @@
9494
ContextSerializer = Callable[[Any], Mapping[str, Any]]
9595
ContextDeserializer = Callable[[Mapping[str, Any]], Any]
9696

97-
# Schema version for serialization compatibility
98-
CURRENT_SCHEMA_VERSION = "1.0"
97+
# RunState schema policy.
98+
# 1. Bump CURRENT_SCHEMA_VERSION when serialized shape/semantics change.
99+
# 2. Keep older readable versions in SUPPORTED_SCHEMA_VERSIONS for backward reads.
100+
# 3. to_json() always emits CURRENT_SCHEMA_VERSION.
101+
# 4. Forward compatibility is intentionally fail-fast (older SDKs reject newer versions).
102+
CURRENT_SCHEMA_VERSION = "1.1"
103+
SUPPORTED_SCHEMA_VERSIONS = frozenset({"1.0", CURRENT_SCHEMA_VERSION})
99104

100105
_FUNCTION_OUTPUT_ADAPTER: TypeAdapter[FunctionCallOutput] = TypeAdapter(FunctionCallOutput)
101106
_COMPUTER_OUTPUT_ADAPTER: TypeAdapter[ComputerCallOutput] = TypeAdapter(ComputerCallOutput)
@@ -1894,10 +1899,12 @@ async def _build_run_state_from_json(
18941899
schema_version = state_json.get("$schemaVersion")
18951900
if not schema_version:
18961901
raise UserError("Run state is missing schema version")
1897-
if schema_version != CURRENT_SCHEMA_VERSION:
1902+
if schema_version not in SUPPORTED_SCHEMA_VERSIONS:
1903+
supported_versions = ", ".join(sorted(SUPPORTED_SCHEMA_VERSIONS))
18981904
raise UserError(
18991905
f"Run state schema version {schema_version} is not supported. "
1900-
f"Please use version {CURRENT_SCHEMA_VERSION}"
1906+
f"Supported versions are: {supported_versions}. "
1907+
f"New snapshots are written as version {CURRENT_SCHEMA_VERSION}."
19011908
)
19021909

19031910
agent_map = _build_agent_map(initial_agent)

tests/test_run_state.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
)
6262
from agents.run_state import (
6363
CURRENT_SCHEMA_VERSION,
64+
SUPPORTED_SCHEMA_VERSIONS,
6465
RunState,
6566
_build_agent_map,
6667
_deserialize_items,
@@ -286,11 +287,13 @@ async def test_throws_error_if_schema_version_is_missing_or_invalid(self):
286287
await RunState.from_string(agent, str_data)
287288

288289
json_data["$schemaVersion"] = "0.1"
290+
supported_versions = ", ".join(sorted(SUPPORTED_SCHEMA_VERSIONS))
289291
with pytest.raises(
290292
Exception,
291293
match=(
292294
f"Run state schema version 0.1 is not supported. "
293-
f"Please use version {CURRENT_SCHEMA_VERSION}"
295+
f"Supported versions are: {supported_versions}. "
296+
f"New snapshots are written as version {CURRENT_SCHEMA_VERSION}."
294297
),
295298
):
296299
await RunState.from_string(agent, json.dumps(json_data))
@@ -3330,6 +3333,31 @@ async def test_from_json_unsupported_schema_version(self):
33303333
with pytest.raises(UserError, match="Run state schema version 2.0 is not supported"):
33313334
await RunState.from_json(agent, state_json)
33323335

3336+
@pytest.mark.asyncio
3337+
async def test_from_json_accepts_previous_schema_version(self):
3338+
"""Test that from_json accepts a previous, explicitly supported schema version."""
3339+
agent = Agent(name="TestAgent")
3340+
state_json = {
3341+
"$schemaVersion": "1.0",
3342+
"original_input": "test",
3343+
"current_agent": {"name": "TestAgent"},
3344+
"context": {
3345+
"context": {"foo": "bar"},
3346+
"usage": {"requests": 0, "input_tokens": 0, "output_tokens": 0, "total_tokens": 0},
3347+
"approvals": {},
3348+
},
3349+
"max_turns": 3,
3350+
"current_turn": 0,
3351+
"model_responses": [],
3352+
"generated_items": [],
3353+
}
3354+
3355+
restored = await RunState.from_json(agent, state_json)
3356+
assert restored._current_agent is not None
3357+
assert restored._current_agent.name == "TestAgent"
3358+
assert restored._context is not None
3359+
assert restored._context.context == {"foo": "bar"}
3360+
33333361
@pytest.mark.asyncio
33343362
async def test_from_json_agent_not_found(self):
33353363
"""Test that from_json raises error when agent is not found in agent map."""

0 commit comments

Comments
 (0)