feat(sdk-python): accept scope kwargs on memory set/get/delete and reconcile memory.search#707
Draft
santoshkumarradha wants to merge 3 commits into
Draft
feat(sdk-python): accept scope kwargs on memory set/get/delete and reconcile memory.search#707santoshkumarradha wants to merge 3 commits into
santoshkumarradha wants to merge 3 commits into
Conversation
…rrect README search API Add optional scope/scope_id kwargs to app.memory.set/get/delete so the developer-facing MemoryInterface matches what humans and LLMs naturally guess (app.memory.set(key, data, scope="global")) and mirrors the TypeScript SDK, which already accepts scope positionally. scope=None keeps today's hierarchical behavior unchanged; explicit scopes route to the existing accessor clients. Invalid scopes raise a ValueError listing valid scopes; non-global scopes without a scope_id raise a clear ValueError. No general memory search endpoint exists on the control plane (only /api/v1/memory/vector/search, already exposed as similarity_search), so the README's advertised app.memory.search(...) is corrected to similarity_search and the scope names are corrected from 'agent/run' to 'actor/workflow'.
Contributor
Performance
✓ No regressions detected |
Contributor
📊 Coverage gateThresholds from
✅ Gate passedNo surface regressed past the allowed threshold and the aggregate stayed above the floor. |
Contributor
📐 Patch coverage gateThreshold: 80% on lines this PR touches vs
✅ Patch gate passedEvery surface whose lines were touched by this PR has patch coverage at or above the threshold. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #712
Motivation
During tutorial writing, four independent LLM writers all guessed the same non-existent API:
That signature does not exist. The Python
MemoryInterfaceonly exposed scoped access through accessors (app.memory.global_scope.set(...),app.memory.session(id).set(...), etc.), while the TypeScript SDK already acceptsscope(andscopeId) positionally onset/get/delete. When four writers and four models independently reach for the same shape, the API should match the guess. This PR makes the Python API match both the natural guess and the TS SDK.What changed
Two changes, nothing else.
1. Scope kwargs on
MemoryInterface.set/get/deleteset,get, anddeletenow accept optionalscopeandscope_idkeyword arguments.scope=None(default) keeps today's behavior exactly:setuses automatic context scoping,getperforms hierarchical lookup (workflow -> session -> actor -> global),deleteuses the current scope. Zero behavior change for existing code.scope="global"delegates toglobal_scope;scope="session"|"actor"|"workflow"delegates to the corresponding accessor and requiresscope_id.ValueErrorlisting the valid scopes. A non-global scope without ascope_idraises aValueErrorthat points the caller at the accessor form.GlobalMemoryClient/ScopedMemoryClient) via a single private dispatch helper, so there is one code path.2. Reconcile
memory.searchThe README advertised
app.memory.search(...), which does not exist. Corrected to the real method and fixed the scope names.What was verified against the control plane
control-plane/internal/server/routes_memory.go:31-41registers/memory/set,/memory/get,/memory/delete,/memory/list, and vector routes. The only search endpoint isPOST /memory/vector/search(routes_memory.go:40). There is no general/memory/search.control-plane/internal/server/apicatalog/catalog_entries.go:96-107lists set/get/delete/list plusvector/search("Similarity search over vectors"), and no plain memory search.workflow,session,actor,global—control-plane/internal/handlers/memory.go:273(scopes := []string{"workflow", "session", "actor", "global"}) and thegetScopeIDswitch atcontrol-plane/internal/handlers/memory.go:422-433. There is noagentorrunscope. The server derivesscope_idfrom theX-Workflow-ID/X-Session-ID/X-Actor-IDheaders (resolveScope,memory.go:402-418), which the PythonMemoryClient._build_headersalready sets fromscope_id.sdk/typescript/src/memory/MemoryInterface.ts:41-117—set/get/delete/exists/listKeysall takescopeandscopeIdpositionally, andMemoryScopeis'workflow' | 'session' | 'actor' | 'global'(sdk/typescript/src/types/agent.ts:72).Change 2 outcome: the server endpoint does NOT exist. Per scope, I did not invent a
searchmethod. Instead I fixed the README:app.memory.set() / .get() / .search()->.similarity_search()(the real method onMemoryInterface).app.memory.search(embedding, top_k=5)->app.memory.similarity_search(embedding, top_k=5).Global, agent, session, run->Global, actor, session, workflow(the actual scope names).The low-level Python
MemoryClientalready implementssimilarity_searchagainst/memory/vector/search, so no new SDK method was needed.Test plan
New file
sdk/python/tests/test_memory_scope_kwargs.py(all@pytest.mark.unit, mocking the low-level client the same way neighboring memory tests do):scope=Nonedefault behavior unchanged forset/get/deletescope="global"delegation forset/get/delete(andscope_idignored for global)scope="session"|"actor"|"workflow"delegation with explicitscope_idforset/get/deletescope_iddelegate through the current execution contextValueErrorlisting valid scopes (onset/get/delete)scope_idraisesValueErrorpointing at the accessor formResults (run in
sdk/python):ruff checkandruff formatpass on the touched files.Follow-ups filed separately
Out of scope for this PR, noted for later:
Resultcost field parityon_changeparityExecutionFilterversion fieldCross-SDK memory check
set/get/deletealready derived scoped headers from metadata whenscopeIdwas omitted.ExecutionContext, and the control-plane backend intentionally omits empty scope headers without client-side failure.MemoryEventClient.history: event history filtering now derivesscope_idfrom metadata whenscopeIdis omitted, matching the scalar memory client behavior and the control-plane history query contract.Additional verification: