Skip to content

feat(sdk): Python REST client (agentmemory-py)#360

Closed
rohitg00 wants to merge 1 commit into
mainfrom
feat/python-sdk-342
Closed

feat(sdk): Python REST client (agentmemory-py)#360
rohitg00 wants to merge 1 commit into
mainfrom
feat/python-sdk-342

Conversation

@rohitg00
Copy link
Copy Markdown
Owner

@rohitg00 rohitg00 commented May 13, 2026

Closes #342.

Adds agentmemory-py — a thin Python REST client for the daemon at :3111. Native ecosystem reach for notebooks, Django backends, and any Python code that wants memory without writing its own retry/auth/JSON glue. No business logic — the daemon owns embedding, BM25, vector indexing, and lifecycle; this is a transport wrapper with typed responses.

API surface

Sync Client and async AsyncClient, identical method sets:

Method HTTP
health() GET /agentmemory/livez
remember(content=..., ...) POST /agentmemory/remember
smart_search(query=..., limit=...) POST /agentmemory/smart-search
memories(project=..., latest=False) GET /agentmemory/memories
memory(memory_id) GET /agentmemory/memories/{id}
forget(memory_id=...) POST /agentmemory/forget
semantic() GET /agentmemory/semantic
procedural() GET /agentmemory/procedural
relations() GET /agentmemory/relations
request(method, path, json=..., …) escape hatch for any other endpoint

Responses are TypedDict-shaped (Memory, SmartSearchResult, RememberResult, …) — no pydantic dep. The only runtime dependency is httpx. Build backend is hatchling, Python >=3.10.

Plaintext-bearer guard (parity with v0.9.12 / #315)

Ported byte-for-byte from the Node/Python integrations landed in #315:

  • Warn once on stderr when a bearer token would cross plaintext HTTP to a non-loopback host.
  • Loopback hosts (localhost / 127.0.0.1 / ::1) and https:// targets stay silent.
  • AGENTMEMORY_REQUIRE_HTTPS=1 upgrades the warning to a hard AuthError raised at Client construction.
  • AGENTMEMORY_URL and AGENTMEMORY_SECRET resolved as fallbacks when the constructor args are None.

PyPI publishing

New .github/workflows/publish-python.yml mirrors the existing Node publish.yml:

  • Triggered by pushing a tag of the form python-v0.1.0 (prefix keeps it separate from the Node release tag stream).
  • Trusted-publisher OIDC via pypa/gh-action-pypi-publish@release/v1 — no API token in secrets.
  • Runs pytest then python -m build then publishes wheel + sdist.

Verification

  • pytest packages/python/tests -q31 passed, 1 skipped (the skipped one is the live-daemon check gated by AGENTMEMORY_LIVE=1).
  • python -m build packages/python/ — produces agentmemory-0.1.0-py3-none-any.whl (15 KB) and agentmemory-0.1.0.tar.gz.
  • pip install dist/*.whl && python -c "from agentmemory import Client; c = Client(); print(c.base_url)" — prints http://localhost:3111, no error.

Test breakdown

  • tests/test_client.py (18 tests): sync + async surface, header construction, env-var resolution, query-string + path params, escape hatch, error mapping (AuthError on 401, ResponseError on other non-2xx, NetworkError on connection refused).
  • tests/test_bearer_guard.py (13 tests): loopback exemptions (v4, v6, hostname), HTTPS exemption, no-token short-circuit, warn-once semantics, AGENTMEMORY_REQUIRE_HTTPS=1 refusal, AsyncClient parity, post-warning bearer-still-sent assertion.

Files

  • packages/python/agentmemory/{__init__.py,_http.py,_types.py,_exceptions.py} — package source.
  • packages/python/tests/{conftest.py,test_client.py,test_bearer_guard.py} — pytest suite.
  • packages/python/{pyproject.toml,README.md,LICENSE} — hatchling config, quickstart, Apache-2.0 (matches root repo).
  • .github/workflows/publish-python.yml — trusted-publisher release.
  • .gitignore — adds Python build/cache patterns.
  • CHANGELOG.md — entry under Unreleased.

Summary by CodeRabbit

  • New Features

    • Added a Python REST client package for agentmemory daemon with both synchronous and asynchronous support.
    • Typed API methods for memory operations (remember, search, forget, etc.) and health checks.
    • Bearer token authentication with HTTPS enforcement guard.
    • Generic escape-hatch request method for unmapped endpoints.
  • Documentation

    • Added comprehensive README with installation, quickstart, configuration, and examples.
    • Added CHANGELOG entry documenting release details.
  • Chores

    • Added PyPI publishing workflow triggered on Python version tags.
    • Updated .gitignore for Python build artifacts.
    • Added Apache 2.0 license and complete test suite with mocked and live integration tests.

Review Change Stack

Python users currently call the daemon's REST surface at :3111 by hand with
requests or curl. Ship a thin first-party client so notebooks, data-science
glue, and Python backends get the same shape as the TypeScript SDK without
hand-rolling auth, retries, or the plaintext-bearer guard.

New package at packages/python/:
  - sync Client + async AsyncClient with identical surfaces
  - methods: remember, smart_search, memories, memory(id), forget,
    semantic, procedural, relations, health, plus request() escape hatch
  - TypedDict response envelopes (no pydantic dep)
  - only runtime dep is httpx
  - plaintext-bearer guard ported from v0.9.12 byte-for-byte: warn once
    on stderr when bearer would cross plaintext HTTP to a non-loopback
    host; raise AuthError instead if AGENTMEMORY_REQUIRE_HTTPS=1
  - hatchling build backend, Python >=3.10
  - 31 tests via respx-mocked httpx + 1 opt-in live test gated by
    AGENTMEMORY_LIVE=1

Plus .github/workflows/publish-python.yml: PyPI trusted-publisher release
on tag python-v* (mirrors the Node workflow). Apache-2.0 licensed to match
the root repo.

Closes #342
@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agentmemory Ready Ready Preview, Comment May 13, 2026 7:42pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

📝 Walkthrough

Walkthrough

This PR introduces agentmemory-py, a thin REST client package for the agentmemory daemon. It includes sync and async clients with typed methods mapping to all daemon endpoints, an HTTP transport layer with plaintext-bearer token guard, comprehensive tests using respx, package configuration, and a PyPI publishing workflow via OIDC trusted publisher.

Changes

agentmemory-py Package

Layer / File(s) Summary
Exception hierarchy and response types
packages/python/agentmemory/_exceptions.py, packages/python/agentmemory/_types.py
Defines AgentMemoryError base class, AuthError (401/403, bearer/HTTPS), NetworkError (request-level failures), ResponseError (non-2xx with status/body); TypedDict response envelopes (Memory, SmartSearchResult, RememberResult, etc.) using NotRequired and total=False for optional fields.
HTTP transport and bearer guard
packages/python/agentmemory/_http.py
HttpCore class manages sync/async httpx clients; resolves base URL and API key from parameters/environment; enforces plaintext-bearer guard (warns once to stderr or raises AuthError per AGENTMEMORY_REQUIRE_HTTPS=1); decodes responses as JSON/text; translates errors to client exceptions; provides request_sync/request_async interface returning (status, decoded_body).
Sync and async client API
packages/python/agentmemory/__init__.py
Client and AsyncClient expose typed methods mapping to daemon endpoints (health, remember, smart_search, memories, memory, forget, semantic, procedural, relations); _drop_none helper omits None payload fields; context-manager support; request() escape hatch for custom endpoints; version and all exports.
Package config and test suite
packages/python/pyproject.toml, packages/python/tests/conftest.py, packages/python/tests/test_bearer_guard.py, packages/python/tests/test_client.py
Hatchling build, project metadata, httpx>=0.27 runtime dependency, pytest/pytest-asyncio/respx dev dependencies; conftest isolates environment variables per test; bearer_guard tests validate stderr warnings for plaintext HTTP non-loopback and AGENTMEMORY_REQUIRE_HTTPS enforcement for both Client/AsyncClient; client tests use respx to mock traffic, validate request construction, authorization headers, error mapping, environment variable configuration, and async equivalents including live daemon option.
Release workflow and repository config
.github/workflows/publish-python.yml, .gitignore, CHANGELOG.md, packages/python/LICENSE, packages/python/README.md
GitHub Actions publishes from python-v* tags to PyPI via OIDC trusted publisher; .gitignore excludes __pycache__, *.egg-info/, .pytest_cache/, dist/, build/; CHANGELOG documents client surfaces and guard behavior; Apache 2.0 LICENSE; README covers installation, quickstart (sync/async), API endpoint mapping, environment configuration, error types, testing, and license.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A thin rabbit hops to Python's door,
With typed clients, async and more,
Bearer tokens guarded with grace,
RESTful endpoints in their place,
To PyPI the daemon's voice shall pour! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(sdk): Python REST client (agentmemory-py)' clearly and concisely summarizes the main change—adding a Python REST client SDK for the agentmemory daemon.
Linked Issues check ✅ Passed All primary objectives from issue #342 are met: 100% REST endpoints exposed, sync/async clients with identical surfaces, TypedDict responses, bearer-token auth with plaintext-bearer guard, respx tests plus optional live test, and PyPI publishing workflow implemented.
Out of Scope Changes check ✅ Passed All changes align with PR objectives: Python client package, tests, workflows, and documentation. No server-side, LangChain/LlamaIndex adapters, or unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/python-sdk-342

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/python/tests/test_client.py (1)

28-38: 💤 Low value

Unused parameter in helper function.

The _mock parameter is not used within the function body. The docstring mentions it's kept for call-site readability, but this can be confusing.

♻️ Optional: Remove unused parameter
-def _route(_mock, method: str, path: str, status: int, body, *, base: str = BASE):
+def _route(method: str, path: str, status: int, body, *, base: str = BASE):
     """Register a respx route on the global mock router.
 
-    The ``@respx.mock`` decorator patches the module-level transport, so we
-    use ``respx.route`` (not the ``respx.router`` module) to register
-    expectations. The first arg is kept for call-site readability.
+    The ``@respx.mock`` decorator patches the module-level transport, so
+    we use ``respx.route`` to register expectations.
     """
 
     return respx.route(method=method, url=f"{base}{path}").mock(

Then update call sites (e.g., line 46, 53, etc.) to remove the respx.router argument.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/python/tests/test_client.py` around lines 28 - 38, The helper
function _route has an unused first parameter `_mock`; remove that parameter
from the signature and docstring and update all call sites that pass the
respx.router/mock argument (e.g., tests around the places noted) to call
_route(method, path, status, body, base=...) instead; ensure function name
`_route` remains and no other references expect the removed parameter.
packages/python/README.md (1)

90-94: 💤 Low value

Clarify testing instructions are for repository contributors.

The test path packages/python/tests assumes a repository checkout. Users who install via pip install agentmemory won't have this directory.

📝 Proposed clarification
 ## Testing

+(For contributors working from the repository checkout:)
+
 ```bash
 pip install -e ".[dev]"
-pytest packages/python/tests
+pytest tests

Alternatively, add a note that testing is for development and users should refer to the test suite on GitHub.
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @packages/python/README.md around lines 90 - 94, Update the Testing section
in the README to clarify these commands are for repository contributors and
adjust the pytest invocation so it runs from the package test directory when
using a repo checkout; specifically, change the example pytest path from
"packages/python/tests" to "tests" (or explain running from the repo root with
pytest packages/python/tests) and add a short note that users who installed via
pip should consult the repository's test suite on GitHub rather than expecting
tests to be present in the installed package.


</details>

</blockquote></details>
<details>
<summary>.github/workflows/publish-python.yml (1)</summary><blockquote>

`33-36`: _⚡ Quick win_

**Prefer installing dev dependencies from the package specification.**

The workflow manually lists test dependencies that duplicate `pyproject.toml`'s `[project.optional-dependencies.dev]` specification. If dev dependencies change, both locations must be updated.


<details>
<summary>♻️ Proposed refactor</summary>

```diff
       - name: Install build tooling
         run: |
           python -m pip install --upgrade pip
-          python -m pip install build hatchling pytest pytest-asyncio respx httpx
+          python -m pip install build
+          python -m pip install -e ".[dev]"

This ensures the workflow uses the same dependency versions as pyproject.toml.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/publish-python.yml around lines 33 - 36, The workflow step
named "Install build tooling" currently hardcodes dev/test packages; change it
to install the project's dev extras from pyproject.toml instead so CI stays in
sync with [project.optional-dependencies.dev]. Replace the explicit pip install
line (the one installing build hatchling pytest pytest-asyncio respx httpx) with
a single installation of the package extras (e.g., install the project with the
"dev" extras like ".[dev]" or the equivalent editable form) so the workflow
derives dev dependencies from pyproject.toml rather than duplicating them.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/python/agentmemory/__init__.py`:
- Line 118: The parameter named type in the sync and async remember() methods
shadows Python's builtin; rename that parameter to memory_type in both function
signatures (sync remember and async remember) and update all internal references
and usages inside those functions (e.g., any code that reads the parameter,
builds dicts, or passes it onward) as well as any places within the same module
that call or reference the parameter name (including default handling and
docstrings) so all occurrences of type are replaced with memory_type to resolve
the Ruff A002 warning.

---

Nitpick comments:
In @.github/workflows/publish-python.yml:
- Around line 33-36: The workflow step named "Install build tooling" currently
hardcodes dev/test packages; change it to install the project's dev extras from
pyproject.toml instead so CI stays in sync with
[project.optional-dependencies.dev]. Replace the explicit pip install line (the
one installing build hatchling pytest pytest-asyncio respx httpx) with a single
installation of the package extras (e.g., install the project with the "dev"
extras like ".[dev]" or the equivalent editable form) so the workflow derives
dev dependencies from pyproject.toml rather than duplicating them.

In `@packages/python/README.md`:
- Around line 90-94: Update the Testing section in the README to clarify these
commands are for repository contributors and adjust the pytest invocation so it
runs from the package test directory when using a repo checkout; specifically,
change the example pytest path from "packages/python/tests" to "tests" (or
explain running from the repo root with pytest packages/python/tests) and add a
short note that users who installed via pip should consult the repository's test
suite on GitHub rather than expecting tests to be present in the installed
package.

In `@packages/python/tests/test_client.py`:
- Around line 28-38: The helper function _route has an unused first parameter
`_mock`; remove that parameter from the signature and docstring and update all
call sites that pass the respx.router/mock argument (e.g., tests around the
places noted) to call _route(method, path, status, body, base=...) instead;
ensure function name `_route` remains and no other references expect the removed
parameter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 196e23ca-0c42-4cdb-9fe9-aea23c1f3ca5

📥 Commits

Reviewing files that changed from the base of the PR and between 96c0ed0 and 93aaf04.

📒 Files selected for processing (14)
  • .github/workflows/publish-python.yml
  • .gitignore
  • CHANGELOG.md
  • packages/python/LICENSE
  • packages/python/README.md
  • packages/python/agentmemory/__init__.py
  • packages/python/agentmemory/_exceptions.py
  • packages/python/agentmemory/_http.py
  • packages/python/agentmemory/_types.py
  • packages/python/pyproject.toml
  • packages/python/tests/__init__.py
  • packages/python/tests/conftest.py
  • packages/python/tests/test_bearer_guard.py
  • packages/python/tests/test_client.py

project: Optional[str] = None,
title: Optional[str] = None,
concepts: Optional[List[str]] = None,
type: Optional[str] = None,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cd packages/python && find . -name "__init__.py" | grep agentmemory

Repository: rohitg00/agentmemory

Length of output: 90


🏁 Script executed:

wc -l packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 108


🏁 Script executed:

sed -n '110,130p' packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 773


🏁 Script executed:

sed -n '314,330p' packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 571


🏁 Script executed:

sed -n '100,150p' packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 1680


🏁 Script executed:

sed -n '300,360p' packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 1925


🏁 Script executed:

grep -n "type: Optional\[str\]" packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 144


🏁 Script executed:

grep -n '"type": type' packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 132


🏁 Script executed:

sed -n '116,125p' packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 486


🏁 Script executed:

sed -n '320,329p' packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 424


🏁 Script executed:

sed -n '134,138p' packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 237


🏁 Script executed:

sed -n '331,335p' packages/python/agentmemory/__init__.py

Repository: rohitg00/agentmemory

Length of output: 237


Rename type parameter to avoid builtin shadowing.

Lines 118 and 322 use type as a parameter name in the sync and async remember() methods respectively. This shadows Python's builtin and is flagged by Ruff (A002). Renaming to memory_type requires updates at lines 118, 136 (sync method) and 322, 333 (async method).

Proposed fix
-        type: Optional[str] = None,
+        memory_type: Optional[str] = None,
@@
-                "type": type,
+                "type": memory_type,
@@
-        type: Optional[str] = None,
+        memory_type: Optional[str] = None,
@@
-                "type": type,
+                "type": memory_type,
🧰 Tools
🪛 Ruff (0.15.12)

[error] 118-118: Function argument type is shadowing a Python builtin

(A002)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/python/agentmemory/__init__.py` at line 118, The parameter named
type in the sync and async remember() methods shadows Python's builtin; rename
that parameter to memory_type in both function signatures (sync remember and
async remember) and update all internal references and usages inside those
functions (e.g., any code that reads the parameter, builds dicts, or passes it
onward) as well as any places within the same module that call or reference the
parameter name (including default handling and docstrings) so all occurrences of
type are replaced with memory_type to resolve the Ruff A002 warning.

@rohitg00
Copy link
Copy Markdown
Owner Author

Closing — wrong abstraction.

agentmemory functions register as api::observe, api::search, api::context, api::health etc. via sdk.registerFunction(...). iii-sdk already ships in Python (pip install iii-sdk), Node, and Rust — any of them can call those functions directly via the iii WebSocket transport. Shipping a separate Python REST client duplicates transport, fragments the story, and ignores the cross-language primitive that already exists.

Will replace with examples/python/ showing iii-sdk-python calling agentmemory's registered functions directly — single SDK, every language.

Issue #342 stays open and gets re-scoped to "Python usage via iii-sdk".

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.

Python thin REST client (agentmemory-py)

1 participant