Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/release-python-sdk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# @wbx-modified copilot-a3f7·MTN | 2026-04-24 | tag-triggered PyPI publish
# Trigger: push a tag matching `python-sdk-v*` (e.g. `python-sdk-v0.1.0`).
# Requires repo secret `PYPI_API_TOKEN` (a PyPI project-scoped token).
# Optional: switch to OIDC trusted-publishing by removing the `password` line and
# configuring the project on PyPI as a trusted publisher.
name: release-python-sdk

on:
push:
tags:
- "python-sdk-v*"

permissions:
contents: read
id-token: write # for future OIDC trusted publishing

jobs:
build-and-publish:
runs-on: ubuntu-latest
defaults:
run:
working-directory: clients/python
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install build deps
run: python -m pip install --upgrade pip build

- name: Verify version matches tag
run: |
TAG="${GITHUB_REF_NAME#python-sdk-v}"
PKG=$(python -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
echo "tag=$TAG pkg=$PKG"
test "$TAG" = "$PKG"

- name: Build sdist + wheel
run: python -m build

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: clients/python/dist
password: ${{ secrets.PYPI_API_TOKEN }}
53 changes: 53 additions & 0 deletions .github/workflows/release-ts-sdk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# @wbx-modified copilot-a3f7·MTN | 2026-04-24 | tag-triggered npm publish
# Trigger: push a tag matching `ts-sdk-v*` (e.g. `ts-sdk-v0.1.0`).
# Requires repo secret `NPM_TOKEN` (npm automation token with publish scope on
# the `@recallworks` org).
name: release-ts-sdk

on:
push:
tags:
- "ts-sdk-v*"

permissions:
contents: read
id-token: write # for npm provenance

jobs:
build-and-publish:
runs-on: ubuntu-latest
defaults:
run:
working-directory: clients/typescript
steps:
- uses: actions/checkout@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: "20"
registry-url: "https://registry.npmjs.org"

- name: Install
run: npm install --no-audit --no-fund

- name: Verify version matches tag
run: |
TAG="${GITHUB_REF_NAME#ts-sdk-v}"
PKG=$(node -p "require('./package.json').version")
echo "tag=$TAG pkg=$PKG"
test "$TAG" = "$PKG"

- name: Typecheck
run: npx tsc --noEmit

- name: Test
run: npx vitest run --reporter=basic

- name: Build
run: npx tsup src/index.ts --format esm,cjs --dts --clean

- name: Publish to npm (with provenance)
run: npm publish --access public --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ __pycache__/
.env
*.pem
*.key

# Local secrets — NEVER commit
.tokens.local

25 changes: 15 additions & 10 deletions clients/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ pip install recall-client
from recall_client import RecallClient

with RecallClient("http://localhost:8787", api_key="changeme") as client:
client.remember("the user prefers dark mode", tags=["pref", "ui"])
hits = client.recall("user preferences", limit=5)
for h in hits:
print(f"{h.score:.3f} {h.content}")
print(client.health()) # {"status": "ok", "chunks": ..., "ready": True}

client.remember("the user prefers dark mode", tags="pref,ui")
res = client.recall("user preferences", n=5)
print(res.result) # markdown listing of hits
```

Every tool returns a `ToolResponse` envelope with three string fields: `result`, `tool`, `by`. The server formats results as markdown — parse `result` if you need structured fields.

## Async

```python
Expand All @@ -29,9 +32,9 @@ from recall_client import AsyncRecallClient

async def main():
async with AsyncRecallClient("http://localhost:8787", api_key="changeme") as c:
await c.remember("async memory works", tags=["async"])
hits = await c.recall("memory")
print(hits)
await c.remember("async memory works", tags="async")
res = await c.recall("memory")
print(res.result)

asyncio.run(main())
```
Expand All @@ -51,13 +54,15 @@ The client exposes typed wrappers for the high-traffic tools:
| `forget()` | `forget` |
| `health()` | `GET /health` |

For any tool without a typed wrapper (e.g. `index_file`, `reindex`, `snapshot_index`, `anti_pattern`, `session_close`, `maintenance`), use the generic dispatch:
All 13 server tools have typed wrappers (`remember`, `recall`, `reflect`, `anti_pattern`, `session_close`, `checkpoint`, `pulse`, `memory_stats`, `forget`, `reindex`, `index_file`, `maintenance`, `snapshot_index`). For any custom or future tool, use the generic dispatch:

```python
client.call_tool("index_file", path="/data/notes.md")
client.call_tool("anti_pattern", pattern="don't auto-merge without CI")
client.call_tool("index_file", filepath="/data/notes.md")
client.call_tool("forget", source="agent-observation")
```

> Note: `forget()` takes a `source` label and **soft-archives** every chunk with that source. It does not delete a single chunk by id.

## Errors

All exceptions derive from `RecallError`:
Expand Down
2 changes: 1 addition & 1 deletion clients/python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "hatchling.build"

[project]
name = "recall-client"
version = "0.1.0"
version = "0.2.0"
description = "Official Python client for Recall — open-source memory for AI agents"
readme = "README.md"
license = "MIT"
Expand Down
32 changes: 13 additions & 19 deletions clients/python/recall_client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
# @wbx-modified copilot-a3f7·MTN | 2026-04-24 | Python SDK package init | prev: NEW
# @wbx-modified copilot-a3f7·MTN | 2026-04-24 | rewrite to match real server contract | prev: 0.1.0
"""recall_client — official Python SDK for Recall memory server.

Quick start:
All Recall tools return ``{"result": str, "tool": str, "by": str}`` over HTTP.
This SDK exposes:

from recall_client import RecallClient

client = RecallClient("http://localhost:8787", api_key="changeme")
client.remember("first memory", tags=["hello"])
hits = client.recall("hello", limit=5)
for h in hits:
print(h.content, h.score)
- ``RecallClient`` / ``AsyncRecallClient`` with one typed wrapper per registered tool.
- Generic ``call_tool(name, **kwargs) -> ToolResponse`` for anything new.

Async usage:
Quick start::

from recall_client import AsyncRecallClient
from recall_client import RecallClient

async with AsyncRecallClient("http://localhost:8787", api_key="changeme") as c:
await c.remember("async memory")
hits = await c.recall("memory")
with RecallClient("http://localhost:8787", api_key="changeme") as c:
c.remember("first memory", tags="hello,greeting")
print(c.recall("hello", n=3).result)
"""

from .async_client import AsyncRecallClient
Expand All @@ -29,18 +25,16 @@
RecallServerError,
RecallToolError,
)
from .models import Hit, RememberResult, ToolResult
from .models import ToolResponse

__version__ = "0.1.0"
__version__ = "0.2.0"
__all__ = [
"AsyncRecallClient",
"Hit",
"RecallAuthError",
"RecallClient",
"RecallConnectionError",
"RecallError",
"RecallServerError",
"RecallToolError",
"RememberResult",
"ToolResult",
"ToolResponse",
]
Loading
Loading