Skip to content

Migrate to the supermemory v5 SDK (namespace API) - #5

Closed
bbamnesia wants to merge 1 commit into
supermemoryai:mainfrom
bbamnesia:feat/sdk5-native-api
Closed

bbamnesia wants to merge 1 commit into
supermemoryai:mainfrom
bbamnesia:feat/sdk5-native-api

Conversation

@bbamnesia

Copy link
Copy Markdown

What

Moves the provider from the 3.x SDK to the official supermemory Python SDK 5 (supermemory>=5.0.0,<6) and the namespace-first v5 API. Tools, config keys, environment variables, and the tool result shapes stay the same. Plugin version goes to 2.0.0 because the SDK major changes and a self-hosted server now has to serve the v5 routes.

Why

SDK 5.0.0 is generated from the v5 API (Fern instead of Stainless), and the calls this provider made are gone or have changed. With SDK 5 installed, the current main fails on every operation, for example:

  • store / capture: 'DocumentsClient' object has no attribute 'add'
  • forget: MemoriesClient.forget() got an unexpected keyword argument 'container_tag'
  • search: 'function' object has no attribute 'memories' (search is now a method)
  • profile / recall / status probe: Supermemory.profile() got an unexpected keyword argument 'container_tag'

Capture failures are logged at DEBUG and retried in the background, so on SDK 5 auto-capture would quietly stop working.

How each call maps

Provider operation 3.x call v5 call (SDK 5)
store, turn capture, memory mirror documents.add(container_tags=[tag], custom_id=…, entity_context=…) add(namespace, id=…, supporting_context=…, task_type="memory"), i.e. POST /ns/{ns}/document
search search.memories(q=…, container_tag=…, search_mode=…) search(namespace, query=…, search_mode=…, threshold=0.6, rerank="none", rewrite_query=False)
recall / profile profile(container_tag=…, q=…) profile(namespace), plus a separate search(...) when there is a query
forget by id memories.forget(container_tag=…, id=…) memories.forget(namespace, ids=[id]) with per-id confirmation

What stays the same:

  • Namespace = the existing container tag. It goes through the same _sanitize_tag as before, including {identity} templates, the SUPERMEMORY_CONTAINER_TAG override, and the multi-container whitelist. Per the v5 migration guide, existing tag values are valid namespaces, so there is no data migration and no partition gets renamed.
  • Capture appends. Turns still go to one document per session per 4-hour window, keyed by the same stable id. v5 POST with an existing id appends or diffs into that document (PATCH would replace it, so the provider never uses it). sm_source: hermes metadata and the x-sm-source header are unchanged. dreaming is not set, so the server default applies.
  • Search precision. v5 lowered the default similarity threshold from 0.6 to 0.3. The provider sends the old 0.6, plus rerank: "none" and rewriteQuery: false, explicitly, so recall does not silently get broader. The pre-v5 search_mode: "documents" maps to v5 chunks, and chunks is also accepted directly.
  • Endpoint and auth. base_url precedence is unchanged (supermemory.json → SUPERMEMORY_BASE_URL → hosted). The resolved URL is always passed explicitly. An unreachable or failing self-hosted endpoint fails closed and is never retried against the hosted API.
  • Isolation. auto_capture / auto_recall, and the cron / flush / subagent write isolation, are untouched.

New behavior that the migration requires:

  • Chunk-level hits keep their text. v5 hybrid results put source passages in chunk with memory null. Hit text now falls back memory → chunk → chunks[].content, so these hits no longer render as empty content or empty prefetch bullets.
  • Profile + query. The v5 profile endpoint has no query parameter. When a query is present (automatic recall, or supermemory-profile with query), the provider fetches the profile and runs one search in parallel on a short-lived helper thread. It waits for the search at most until api_timeout has passed since recall started. If one leg fails, the other is still used. Prefetch logs the failure at DEBUG, and the profile tool returns it under errors. Only a double failure is an error. supermemory-profile with query now also returns those search hits as results (same shape as supermemory-search), since v5 has nowhere else for the query to go. Profile facts are rendered from their memory text, never from the model repr.
  • Forget is confirmed. A forget-by-id is reported as successful only if the API's matches is exactly [id] and errors is empty. Otherwise the tool returns an error rather than forgotten: true. Forget-by-query now searches in memories mode, because a hybrid top hit can be a chunk and a chunk id is not a memory id.
  • SDK version gate. If something else in the shared Hermes venv pins a different SDK major, _SupermemoryClient refuses to start. The provider stays inactive, and hermes memory status shows e.g. supermemory 3.62.0 is installed; this provider needs supermemory>=5,<6 instead of every request failing later. The check is on the major version, not an exact pin, so 5.x patch and minor releases are accepted.

Delivery semantics

These are documented in the README, without promising exactly-once.

  • SDK retries stay off (max_retries=0; the SDK 5 default is 2 and covers timeouts, connection errors, 408/409/429 and 5xx). Each provider hook sends a capture batch exactly once. A failed batch is retried by the provider on the next turn, session end, session switch, or shutdown.
  • That retry is at-least-once. If the server accepted an append but the response was lost, the retry appends the same turns again. The v5 guide says an unchanged duplicate document returns done, but it doesn't promise dedup for a re-sent delta, so I don't rely on it.
  • The retry buffer is in memory only, capped at 50 turns / 256 KiB (oldest dropped first), and lost when the process exits. This is unchanged from before.

Overlap with open PRs

Tests

tests/ (new) runs the real provider module against the real installed SDK 5: its request serializers, response models, and error mapping. Only the HTTP layer is replaced, by an httpx.MockTransport. Sockets are blocked, and all data is synthetic. The few Hermes host modules the provider imports are stubbed, using the signatures from hermes-agent's public agent/memory_provider.py and tools/registry.py. tests/pytest.ini exists because the repo root is the plugin package itself.

Coverage includes:

  • append-by-stable-id wire body (id, taskType, supportingContext, metadata.sm_source, no containerTags/customId/dreaming)
  • injected-context stripping
  • single-send on 503/409/429/timeout/connection failure, with exactly one provider retry and no duplicate on clean shutdown
  • pending retries keep their original session; the buffer cap holds
  • cron/flush/subagent and auto_capture/auto_recall disabled send nothing
  • the default namespace, {identity} template and env override, and the custom-container whitelist (including disabled mode)
  • all four search modes and the explicit pre-v5 search parameters
  • chunk / chunks text fallback
  • profile with and without a query; partial and double recall failures (401/503/timeout)
  • the recall wait stays within api_timeout when search is slow
  • exact forget plus partial/mismatched responses; forget-by-query uses memories
  • base_url / env / hosted precedence, the auth header and x-sm-source on every request, and fail-closed local endpoint
  • status probe; the SDK gate (3.x / 4.x / 6.x rejected, 5.x accepted); dependency declarations agree
$ uv run --isolated --no-project --python 3.11 --with 'supermemory>=5.0.0,<6' --with pytest python -m pytest tests -q
51 passed
  • Run on Python 3.11.16 and 3.13.15 with supermemory 5.0.0, httpx 0.28.1, pydantic 2.14.1, pytest 9.1.1. 5.0.0 is currently the only release in the declared range.
  • Red check: the same suite against unmodified main with SDK 5 gives 44 failed, 7 passed. The 7 that pass cover behavior the migration doesn't touch: the capture-disabled and cron/subagent no-write paths, the buffer cap, and the bucket id format.
  • ruff check --select E,F,W is clean on __init__.py and tests/. py_compile is clean.

Not covered: no maintainer CI ran, and these tests don't hit the hosted API or a self-hosted server end to end. Please treat the wire mapping as verified against the SDK's own serializers, not against a live server.

Known limitations

  • Profile + query timing: the profile request runs on the calling thread and is bounded only by the SDK's per-phase httpx timeouts, as before, not by a hard wall clock. A search that runs late keeps one daemon thread alive until its own timeout ends it.
  • A provider retry after a 409 happens on the next hook. The v5 guide advises waiting for the conflicting operation to reach a terminal state.
  • The v5 profile buckets are not surfaced; the tool output is unchanged except for results / errors when a query is given.
  • A pending turn retried after its 4-hour window ends is written into the new window's document. This is unchanged pre-existing behavior.

🤖 Generated with Claude Code

Move every call to the namespace-first v5 API via supermemory>=5,<6
(client.add / search / profile / memories.forget). Tools, config keys
and env vars are unchanged; the existing sanitized container tag is the
v5 namespace, so stored memories stay addressable.

- capture still appends by stable document id (POST, not PATCH) with
  sm_source metadata; SDK retries stay off so one hook sends a batch once
- search sends the pre-v5 threshold/rerank/rewrite explicitly (v5 lowered
  the default threshold to 0.3); "documents" maps to "chunks"
- hits whose text is in chunk/chunks are rendered instead of blank
- v5 profile has no query: profile + search run concurrently within
  api_timeout, keeping whichever leg succeeds
- forget by id requires the API to confirm that exact id; forget by query
  searches memories only
- refuse other SDK majors with a clear status message
- add offline tests against the real SDK over a mock HTTP transport

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedpypi/​supermemory@​3.62.0 ⏵ 5.0.0100 +1100100100100

View full report

@capy-ai

capy-ai Bot commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Thank you @bbamnesia for this migration and the thorough call-by-call mapping. It was really useful when we compared approaches.

We're closing this in favor of #6. We decided to store one document per session and rewrite it in full on every capture, rather than appending turn deltas, because appended resends were what caused duplicate memories in our other plugins. #6 already keys capture by session, so we're finishing V5 there on top of #8's tag fix. Your PR helped shape that work.

@capy-ai capy-ai Bot closed this Oct 19, 2026
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.

1 participant