Skip to content

FirestoreSessionService ignores app: / user: state prefixes, so app and user state are never shared across sessions聽#1645

Description

@innoprej

馃敶 Required Information

Describe the Bug:

FirestoreSessionService does not implement ADK's scoped state. ADK's State class defines app:, user: and temp: prefixes (State.APP_PREFIX, USER_PREFIX, TEMP_PREFIX), and InMemorySessionService stores app:/user: keys in shared app/user stores and merges them back into every session. FirestoreSessionService behaves differently:

  1. appendEvent only routes keys starting with _app_ / _user_ to the app-state / user-state collections. Keys written through the normal ADK API (app:theme, user:name) fall through to the per-session state field, so they are only visible in that one session.
  2. The app-state and user-state collections are write-only. getSession, createSession and listSessions never read them, so even a value stored under _app_ / _user_ never reaches another session.
  3. temp: keys are persisted in the session document. BaseSessionService.appendEvent skips them.
  4. Removal checks value == null, but ADK marks removals with the State.REMOVED sentinel, so a removed key is kept and the sentinel object is written to the session's state map instead of the key being deleted.

Code references (main @ 189d463):

  • contrib/firestore-session-service/.../FirestoreSessionService.java appendEvent (prefix split around L657-L689, removal check L666)
  • getSession builds the session only from the session document's state field (around L262-L272)
  • Compare core/.../InMemorySessionService.java appendEvent (L316-L338) and mergeWithGlobalState (L398)

Steps to Reproduce:

  1. Configure a runner with FirestoreSessionService.
  2. In session A, a tool or callback sets context.state().put("user:name", "Jae").
  3. Create session B for the same app and user, and read user:name.

The same can be shown with the module's existing mocked tests (no Firestore needed): appending an event with state delta {"app:theme": "dark", "user:name": "Jae"} never calls set on the app-state / user-state documents, and the session document is updated with state = {app:theme=dark, user:name=Jae}. Appending {"temp:scratch": "x", "gone": State.REMOVED} writes both temp:scratch and the REMOVED sentinel into the session's state.

Expected Behavior:

Same semantics as InMemorySessionService (and the Python ADK database session services): app: keys shared by all sessions of the app, user: keys shared by all sessions of the app and user, merged back into Session.state() on getSession / createSession / listSessions, temp: keys not persisted, and State.REMOVED deleting the key.

Observed Behavior:

In session B, user:name is missing. App/user state behaves like session state. temp: values persist, and removed keys are not deleted.

Environment Details:

  • ADK Library Version: main @ 189d463 (1.11.1-SNAPSHOT), google-adk-firestore-session-service
  • OS: Linux
  • TS Version: N/A

Model Information:

  • N/A (session service bug, model independent)

馃煛 Optional Information

Regression: No. The _app_ / _user_ handling has been there since the Firestore service was added.

Additional Context:

A fix would (a) split on State.APP_PREFIX / State.USER_PREFIX, (b) merge the app-state / user-state documents back into the session state with the prefixes on read, (c) skip State.TEMP_PREFIX, and (d) treat State.REMOVED as a delete (FieldValue.delete() in the shared stores). Existing data stored under _app_ / _user_ keys (if anyone relied on that convention) may need a note in the changelog. I'm happy to send a PR if this direction looks right.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions