Skip to content

Inconsistent Session Serialization Between ADK Web and ADK Run #3558

@j0monty

Description

@j0monty

When using adk run --save_session, session files are serialized with snake_case field names (app_name, user_id, usage_metadata). However, adk web serializes the same session files with camelCase field names (appName, userId, usageMetadata). This inconsistency breaks compatibility between session files generated by different ADK CLI commands, despite both using the same Pydantic models with alias_generator=alias_generators.to_camel configuration.

To Reproduce

Minimal reproduction:

  1. Create a simple agent in src/my_agent/agent.py:
from google.adk import Agent

root_agent = Agent(
    name="test_agent",
    model="gemini-2.5-flash",
    instruction="You are a test agent. Say hello.",
)
  1. Create input file input.json:
{
  "state": {},
  "queries": ["Hello!"]
}
  1. Run agent with session saving:
adk run src/my_agent --save_session --session_id test --replay input.json
  1. Inspect the saved session file:
cat src/my_agent/test.session.json | jq 'keys'

Observed output (snake_case):

["app_name", "events", "id", "last_update_time", "state", "user_id"]
  1. Compare with a session file from adk web (run adk web src/my_agent and check saved session)

Expected output from adk web (camelCase):

["appName", "events", "id", "lastUpdateTime", "state", "userId"]

Field comparison:

adk run output (incorrect):

{
  "app_name": "test_agent",
  "user_id": "user",
  "events": [{
    "model_version": "gemini-2.5-flash",
    "usage_metadata": {
      "prompt_token_count": 123,
      "candidates_token_count": 456
    }
  }]
}

adk web output (correct):

{
  "appName": "test_agent",
  "userId": "user",
  "events": [{
    "modelVersion": "gemini-2.5-flash",
    "usageMetadata": {
      "promptTokenCount": 123,
      "candidatesTokenCount": 456
    }
  }]
}

Expected behavior

Both adk run and adk web should serialize session files with camelCase field names, consistent with the Pydantic model configuration in Session and Event classes:

model_config = ConfigDict(
    alias_generator=alias_generators.to_camel,
    populate_by_name=True,
)

All session files should use: appName, userId, modelVersion, usageMetadata, promptTokenCount, candidatesTokenCount, etc.

Desktop

  • OS: macOS (darwin 24.6.0)
  • Python version: 3.12.11 (python -V)
  • ADK version: 1.18.0+ (pip show google-adk)

Model Information

  • Are you using LiteLLM: No
  • Which model is being used: gemini-2.5-flash

Additional context

Root cause identified:

In google/adk/cli/cli.py at line 218, the session serialization is missing by_alias=True:

217:    with open(session_path, 'w', encoding='utf-8') as f:
218:      f.write(session.model_dump_json(indent=2, exclude_none=True))

For comparison, adk_web_server.py correctly includes by_alias=True at lines 1424 and 1523:

1423:              sse_event = event.model_dump_json(
1424-                  exclude_none=True, by_alias=True
1425-              )

Proposed fix:

Change line 218 in google/adk/cli/cli.py to:

f.write(session.model_dump_json(indent=2, exclude_none=True, by_alias=True))

Affected fields:

All Pydantic model fields in Session, Event, and nested objects:

  • Session: app_nameappName, user_iduserId, last_update_timelastUpdateTime
  • Event: model_versionmodelVersion, usage_metadatausageMetadata, invocation_idinvocationId
  • UsageMetadata: prompt_token_countpromptTokenCount, candidates_token_countcandidatesTokenCount

Impact:

Session files from adk run cannot be easily consumed by tools expecting the documented camelCase format. This requires workarounds in consuming code to parse both formats, and breaks consistency expectations for the same data structure.

Metadata

Metadata

Assignees

Labels

core[Component] This issue is related to the core interface and implementationplanned[Status] This issue is planned to be work on by ADK eng team

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions