Skip to content

fix: Port session id validation, thread id scoping and Vertex RAG cleanup fixes to v1 - #6809

Merged
GWeale merged 4 commits into
v1from
backport-v1-pr21
Aug 19, 2026
Merged

fix: Port session id validation, thread id scoping and Vertex RAG cleanup fixes to v1#6809
GWeale merged 4 commits into
v1from
backport-v1-pr21

Conversation

@GWeale

@GWeale GWeale commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

This PR ports four fixes to the v1 branch:

  1. fix: validate session_id and enforce ownership in delete_session (upstream b2916c71)

    • VertexAiSessionService.delete_session fetches the session and raises ValueError when the stored user_id differs from the caller's.
    • session_id must match ^[A-Za-z0-9_-]+$ on create, get, delete and append.
    • Deleting a missing session returns quietly.
  2. fix: scope LangGraph checkpointer thread id to app and user (upstream 19e2a728)

    • The thread id is a SHA-256 digest over app name, user id and session id.
    • Checkpoints written by earlier releases are orphaned, with no fallback read; let in-flight conversations finish before upgrading.
  3. fix(memory): make Vertex RAG uploads async-safe (re-implemented from upstream 80a05b7f)

    • The upload temp file is removed in a finally block.
    • VertexAiRagMemoryService raises ValueError at construction when a RAG resource has no rag_corpus.
    • Both Vertex RAG SDK calls run through asyncio.to_thread.
  4. fix: key in-memory memory store by app_name and user_id tuple (upstream fd8f7eb2)

    • InMemoryMemoryService keys its event store on the (app_name, user_id) tuple.

Item 3 is a re-implementation rather than a port.

GWeale added 4 commits August 17, 2026 23:06
…ssion (v1)

Port of upstream commit b2916c7 (PR #5580) to the v1 branch.

VertexAiSessionService.delete_session accepted a user_id argument and never
read it, so the caller's identity had no bearing on which session was deleted;
get_session has checked ownership since it was written. delete_session now
fetches the session first, returns silently if it is already gone, and raises
ValueError when the session belongs to a different user.

Session ids were also interpolated straight into the resource path. A new
_validate_session_id rejects anything outside ^[A-Za-z0-9_-]+$ in
create_session, get_session, delete_session and append_event, so an id can no
longer contribute extra path segments.

Behaviour changes for existing users: a caller-chosen session_id containing a
dot, a colon or any non-ASCII character is now rejected with ValueError, where
before it was passed through. Server-generated Vertex session ids are numeric
and unaffected. delete_session now costs one extra get round trip, and a
missing session returns without raising instead of surfacing the delete's 404.
Port of upstream commit 19e2a72 to the v1 branch.

LangGraphAgent passed the bare session id to the LangGraph checkpointer as its
thread id. Session ids are caller-chosen and only unique within an
(app name, user id) pair, so two users who picked the same session id read and
wrote each other's graph state, and so did two apps. The thread id is now the
SHA-256 digest of the app name, user id and session id together. Each
component is length-prefixed before hashing, so a component containing the
separator cannot stand in for a different triple, and hashing keeps the user
id out of checkpointer storage.

Behaviour change for existing users: checkpoints written by earlier releases
are keyed on the old thread id and are not reused. With a persistent
checkpointer, the first turn after upgrading resumes from empty graph state,
and because the adapter sends only the last human message when a checkpointer
is configured, the history that lived in the checkpointer is gone rather than
re-sent. There is no migration path. Reading the old thread id as a fallback
would mean reading the ambiguous key this change exists to remove, so it would
reintroduce the cross-user collision. Affected users should finish in-flight
conversations before upgrading.
…p (v1)

Adapted from upstream commit 80a05b7. That commit rewrote this service onto a
client SDK the v1 branch does not have, so the code here is written against
v1's module-level vertexai RAG surface rather than cherry-picked.

VertexAiRagMemoryService.add_session_to_memory writes the full session
transcript to a plaintext file in the system temp directory before uploading
it. The removal was the last statement of the happy path, so any upload that
raised, or a cancelled task, left the transcript on disk indefinitely. The
write and the uploads now sit in a try whose finally removes the file,
tolerating FileNotFoundError, and the path is recorded before the write so a
failed write is cleaned up too. The file is opened with an explicit utf-8
encoding instead of the locale default.

Corpus names are also validated before the file is created rather than after,
so a misconfigured service writes nothing at all.

Both methods are declared async but called the synchronous RAG SDK, so every
upload and retrieval blocked the event loop for the whole HTTP round trip.
Both calls now run through asyncio.to_thread. This is the same two functions
the transcript cleanup rewrites, which is why it is one change. to_thread
copies the current contextvars context, so tracing spans still propagate into
the worker thread, and both SDK entry points read the module globals set by
vertexai.init and build a fresh client per call, so neither is thread-affine.

Behaviour change for existing users: VertexAiRagMemoryService() constructed
with no rag_corpus now raises ValueError("rag_corpus must be set on every RAG
resource.") before doing any work. Previously the guard it replaces could
never fire, because __init__ always builds a one-element rag_resources list
even when rag_corpus is None, so that configuration wrote the transcript to
disk and then failed inside the SDK with corpus_name=None. The CLI factory
always passes a corpus, so it is unaffected. Separately, the SDK call now runs
on a worker thread, which anyone substituting a thread-hostile fake for
rag.upload_file or rag.retrieval_query would notice.
… (v1)

Port of upstream commit fd8f7eb to the v1 branch.

InMemoryMemoryService kept its per-user event store under the string
f'{app_name}/{user_id}'. That flattening is not injective: an app named
'acme/alice' with user 'bob' produced the same key as app 'acme' with user
'alice/bob', so the two shared a store and each could read the other's
sessions. The key is now the (app_name, user_id) tuple, which cannot be
aliased.

The store is private and in-memory, so nothing persists across a restart and
there is no stored format to migrate. The only observable change is for a
caller or test reaching into service._session_events with a string key.
@GWeale GWeale changed the title fix: Port session ownership, scoping and Vertex RAG cleanup fixes to v1 fix: Port session id validation, thread id scoping and Vertex RAG cleanup fixes to v1 Aug 19, 2026
@GWeale
GWeale merged commit 3fec8c5 into v1 Aug 19, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants