Skip to content

feat!: generate the SDK from the v5 API with Fern (5.0.0rc1), replacing Stainless - #87

Merged
MaheshtheDev merged 4 commits into
mainfrom
fern-v5
Oct 5, 2026
Merged

MaheshtheDev merged 4 commits into
mainfrom
fern-v5

Conversation

@Dhravya

@Dhravya Dhravya commented Oct 5, 2026

Copy link
Copy Markdown
Member

What

Replaces the Stainless-generated SDK with one generated from the v5 API (https://api.supermemory.ai/v5/openapi) by Fern's open-source Python generator. It runs locally in Docker, the same way cloudflare/forge generates its SDKs, so there's no hosted codegen service or per-language plan. It's versioned 5.0.0rc1, a prerelease, so pip install supermemory stays on 3.62.0 until 5.0.0.

The SDK

Method names match the TypeScript SDK's v5 surface. Everything around the methods keeps the 3.x conventions.

from supermemory import Supermemory, NotFoundError

client = Supermemory()  # SUPERMEMORY_API_KEY
client.add("user_alex", content="Alex prefers morning meetings.", id="pref-1")
results = client.search("user_alex", query="when does alex meet?", limit=5).results
  • Surface:
    • root: client.{add, search, profile, list}
    • documents.{get, update, delete, batch_add, upload_file, replace_with_file, update_file}
    • memories.{forget, forget_matching}
    • profiles.{get_buckets, set_buckets, delete_buckets}
    • connectors.{list_providers, list, create, get, update, delete, sync}
    • namespaces.* and organization.*
  • Kept from 3.x:
    • client options: api_key, base_url, timeout, max_retries, default_headers, default_query, http_client
    • the SUPERMEMORY_* env vars
    • per-call options: extra_headers, extra_query, extra_body, timeout, plus with_options
    • the exception hierarchy, e.g. NotFoundError and RateLimitError, with .response and .request
    • with_raw_response.parse(), .to_dict() / .to_json(), and plain dicts for request params, typed as TypedDicts
  • Named types: Document, Memory, SearchResult, Connector, Profile, FilterAnd, … 104 types instead of 204, set in fern/schemas.yaml.
  • Breaking: container tags become namespaces, and Python 3.10+ is required. See MIGRATION.md.

How it's built

Path Role
fern/overlay.yaml method names and grouping, i.e. the SDK's syntax
fern/schemas.yaml type names (structural matching, so new endpoints pick them up)
fern/generators.yml, fern/openapi.json generator config and the committed spec snapshot
custom/ hand-written 3.x-compatible layer, spec-agnostic, plus 2 patches for bugs in Fern's generated core: nested filters were sent unconverted (e.g. case_sensitive instead of caseSensitive), and a crash on Python 3.9 with current pydantic
scripts/generate fetch spec → name schemas → fern generate --local → layer custom/ → format

src/supermemory is never edited by hand. CI fails if it doesn't match a fresh generation.

Automation

  • Generate SDK (generate.yml) runs on repository_dispatch: openapi-updated from the API deploy, a daily cron, or a manual run with an optional version. If the generated code changed, it bumps the version (rc1 → rc2, 5.0.0 → 5.0.1), runs the tests and opens a PR.
  • Publish to PyPI runs on merge to main when the version is new: it builds, publishes with SUPERMEMORY_PYPI_TOKEN, then creates a tag and GitHub release.
  • CI runs:
    • tests on Python 3.10–3.14 and on pydantic v1
    • ruff, the format check, mypy and the build
    • the generated-code drift check
    • optional live-API tests

Testing

  • 36 offline tests pass on Python 3.10, 3.11, 3.12, 3.13 and 3.14, and on pydantic v1. They cover:
    • the wire format of every route group: method, path, auth header and body
    • nested filter and connector dicts sent with the API's camelCase field names
    • the 3.x conventions: options, env vars, the error class for each status code, timeouts, connection errors, raw responses and model helpers
    • every Python snippet in README.md and MIGRATION.md, run against a mock API
  • ruff, mypy and pyright are clean, and pyright accepts valid filter dicts and rejects invalid ones.
  • Regenerating twice gives identical output, and actionlint passes.
  • ⚠️ The live-API tests (tests/live/) haven't passed yet. The key used locally got 401 Unauthorized on every v5 route, including GET /organization. Needs a valid key to confirm.

Before merging

  • Add repo secret CODEGEN_PR_TOKEN (a fine-grained PAT or app token with contents and pull-requests write). PRs opened with the default token don't trigger CI.
  • Optionally add SUPERMEMORY_LIVE_API_KEY to turn on the live tests, and run them once.
  • Add the dispatch to mono's API deploy: gh api repos/supermemoryai/python-sdk/dispatches -f event_type=openapi-updated.
  • Merging publishes supermemory==5.0.0rc1 to PyPI.

🤖 Generated with Claude Code

Generate src/supermemory from https://api.supermemory.ai/v5/openapi using
Fern's open-source Python generator run locally in Docker (the approach
cloudflare/forge uses), so no hosted codegen service or plan is needed.

- fern/: spec snapshot, overlay (method surface matching sdk-ts v5),
  schemas.yaml (named types: Document, Memory, SearchResult, Connector, ...)
- custom/: hand-written 3.x-compatible client conventions (options, env vars,
  extra_headers/extra_query/extra_body/timeout, with_options, exception
  hierarchy, with_raw_response.parse, to_dict/to_json) plus two patches for
  bugs in Fern's generated core (recursive filter serialization, py3.9)
- scripts/generate: fetch spec, name schemas, run Fern, layer custom/
- CI: py3.10-3.14 + pydantic v1 tests, lint, mypy, generated-code drift
  check, optional live-API tests; Generate SDK workflow opens a version-bumped
  PR when the spec changes; merging publishes to PyPI
- tests: wire-level route/auth/body tests, compat tests, every README and
  MIGRATION snippet executed against a mock API
- version 5.0.0rc1; requires Python 3.10+

BREAKING CHANGE: namespace-first v5 methods (client.add(namespace, ...),
client.search(namespace, query=...)); container tags become namespaces.
See MIGRATION.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@socket-security

socket-security Bot commented Oct 5, 2026 •

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
Addedpypi/​mypy@​2.4.075100100100100
Addedpypi/​pytest@​9.1.187100100100100
Addedpypi/​ruff@​0.16.10100100100100100
Addedpypi/​pydantic-core@​2.46.5100100100100100
Addedpypi/​pytest-asyncio@​1.4.0100100100100100

View full report

list_providers was wrong: the route lists connectors across the org, not providers. Matches the TypeScript SDK (listAll) and the docs.
@MaheshtheDev
MaheshtheDev enabled auto-merge October 5, 2026 17:30
The compat layer handed Fern only timeout.read, so a per-request float replaced the client's httpx.Timeout and dropped connect/write/pool. It now forwards the whole Timeout object, which Fern passes straight to httpx. Fern's HTTP client only retried ConnectError and RemoteProtocolError; a new patch adds httpx.TimeoutException so read timeouts honor max_retries as before.
Brings back the NOT_GIVEN sentinel so "not supplied" and an explicit None stay distinct. Omitted keeps the default (60 s read, 5 s connect); None at construction, per call, or via with_options turns all timeout phases off. NOT_GIVEN and NotGiven are exported again.
@MaheshtheDev
MaheshtheDev merged commit b6f9d12 into main Oct 5, 2026
11 checks passed
MaheshtheDev added a commit that referenced this pull request Oct 5, 2026
…88)

Picks up the two regenerations that landed on `fern-v5` after #87 merged, so they never shipped in rc1:

- `connectors.delete(..., delete_documents=False)` is a bool (mono#3411 made the query flag a boolean).
- `namespaces.delete` returns `NamespaceDeleted_Deleted | NamespaceDeleted_Queued` with a `status` discriminator (mono#3412).

Version bumped to 5.0.0rc2 so `publish-pypi.yml` publishes on merge. Tests, ruff, mypy pass; both changes were exercised against production.
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.

3 participants