FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

VertexAiSessionService.get_session replaces Event.id with the Vertex resource name, discarding the persisted raw_event.id · Issue #6530 · google/adk-python · GitHub

VertexAiSessionService.get_session replaces Event.id with the Vertex resource name, discarding the persisted raw_event.id #6530

Description

Description

When a session is loaded via VertexAiSessionService.get_session, each returned Event.id is set to the Vertex event resource name (api_event_obj.name.split('/')[-1], a server-assigned numeric id) instead of the original ADK Event.id that was generated during the run and streamed to the caller.

The original id is still persisted — it's inside the raw_event blob — but _from_api_event deep-copies raw_event and then overwrites the id before validating. So the same logical event ends up with two different ids depending on how you observe it:

Observed via Event.id
live run / streaming (run_async, :query/streamQuery) the ADK Event.id (e.g. a UUID like c452feb0-b9ad-48b1-b863-c7dd2f085378)
get_session (reload) the Vertex resource name (e.g. 658731057016733696)

Where

src/google/adk/sessions/vertex_ai_session_service.py, _from_api_event — the raw_event branch (and the same in the fallback branch):

raw_event_dict = _get_raw_event(api_event_obj)   # contains the original ADK id
if raw_event_dict:
    event_dict = copy.deepcopy(raw_event_dict)
    event_dict.update({
        'id': api_event_obj.name.split('/')[-1],  # <-- overwrites the original id
        'invocation_id': getattr(api_event_obj, 'invocation_id', None),
        'author': getattr(api_event_obj, 'author', None),
    })
    return Event.model_validate(event_dict)

raw_event_dict['id'] holds the original id and is discarded. Confirmed on main and on the released 1.31.1.

Impact

Any feature that correlates a streamed event with its persisted form by Event.id breaks, because the two id-spaces don't reconcile.

Our concrete case: a resumable SSE layer keys a per-event resume cursor on Event.id. After a page reload we render history from get_session and reconnect using the last rendered event's id as the resume cursor. Because the reloaded id (numeric) is in a different id-space than the live stream (UUID), the cursor can't be resolved against the in-flight buffer and dedupe-by-id fails — a mid-turn reload re-renders the already-shown prefix of the in-flight turn as duplicates.

More generally this makes Event.id unusable as a stable cross-observation key, which is surprising given invocation_id, author, and the full content are faithfully round-tripped from raw_event.

Minimal repro (no live backend needed)

The overwrite is visible by driving the conversion function directly:

import datetime
from types import SimpleNamespace
from google.adk.events.event import Event
from google.adk.sessions.vertex_ai_session_service import _from_api_event

original = Event(id="c452feb0-b9ad-48b1-b863-c7dd2f085378",
                 invocation_id="e-1", author="model")
raw_event = original.model_dump(exclude_none=True, mode="json", by_alias=True)
# raw_event["id"] == "c452feb0-..."  (the original id is persisted)

api_event = SimpleNamespace(
    raw_event=raw_event,
    name="projects/p/locations/l/reasoningEngines/r/sessions/s/events/658731057016733696",
    invocation_id="e-1", author="model",
    timestamp=datetime.datetime.now(datetime.timezone.utc),
)
loaded = _from_api_event(api_event)
print(loaded.id)  # -> "658731057016733696"  (original UUID discarded, though it's in raw_event)

Expected

get_session should return each event with its original Event.id (the value already present in raw_event), so ids are stable across streaming and reload. The Vertex resource name is still available separately on api_event_obj.name for callers who need it.

Proposed fix

When raw_event is present, preserve its id rather than overwriting it (fall back to the resource name only when raw_event is absent, for older data):

event_dict = copy.deepcopy(raw_event_dict)
event_dict.update({
    'invocation_id': getattr(api_event_obj, 'invocation_id', None),
    'author': getattr(api_event_obj, 'author', None),
})
event_dict.setdefault('id', api_event_obj.name.split('/')[-1])  # only if raw_event lacked one
return Event.model_validate(event_dict)

Workaround

Stash the original id in custom_metadata on append and restore it on read — custom_metadata is a declared field that round-trips through raw_event and (unlike the top-level id) is not overwritten by _from_api_event. (Related: #6055, where an undeclared custom_metadata attribute was dropped on persistence — a declared field avoids that.)

Environment

  • google-adk 1.31.1 (behavior identical on main)
  • VertexAiSessionService (Vertex AI Agent Engine sessions)

Metadata

Metadata

Labels

request clarification[Status] The maintainer need clarification or more information from the authorservices[Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions


Back | FazBrowse Home | New Git URL