lyzr-memory-client
Typed sync/async Python client for the Lyzr Memory /v1 API. Use it from PepGenX (JWT-only: an Okta bearer token and nothing else) and from Lyzr-hosted deployments (x-api-key). Built on httpx and pydantic.
Install
pip install lyzr-memory-client
# or
uv add lyzr-memory-client
Requires Python 3.10+. Runtime dependencies: httpx and pydantic only.
PepGenX quick start
JWT-only posture: the SDK mints an Okta client-credentials token and sends Authorization: Bearer <jwt> and nothing else — no API key, no team_id / project_id / user_id headers. The server derives tenancy from the token, so pass instance defaults such as project_id= / owner_id= yourself if your calls should inherit them.
import os
from lyzr_memory_client import MemoryClient
client = MemoryClient.for_pepgenx(
os.environ["LYZR_MEMORY_BASE_URL"], # APIM base + memory route from onboarding
client_id=os.environ["OIDC_CLIENT_ID"],
client_secret=os.environ["OIDC_CLIENT_SECRET"],
issuer=os.environ["OIDC_ISSUER"], # Okta: token URL is {issuer}/v1/token
scope="pepgenx2.0",
project_id=os.environ.get("PEPGENX_PROJECT_ID"), # optional instance default
)
for_pepgenx takes exactly one credential source: client_id + client_secret (with issuer or token_url), a caller-owned token_provider, or a static token=. A provider it mints is closed by client.close(). Anything else raises ConfigurationError.
me = client.whoami()
print(me.subject, me.org_id, me.roles)
client.add(
[
{"role": "user", "content": "I prefer short answers."},
{"role": "assistant", "content": "Understood."},
],
session_id="ses_demo",
sync_extraction=True,
)
hits = client.search("preferred answer length")
ctx = client.get_context([{"role": "user", "content": "Remind me of my preference."}])
client.clear_session(session_id="ses_demo")
print(hits.count, ctx.context_string)
Admin onboarding
Import a PepGenX onboarding key and register the service principal before memory calls succeed under MEMORY_AGENT_REGISTRY_ENFORCE:
# Use a bootstrap/admin client that can mint keys and registrations.
created = client.admin.api_keys.create(
"my-agent-key",
org_id=os.environ["PEPGENX_TEAM_ID"],
project_id=os.environ["PEPGENX_PROJECT_ID"],
secret=os.environ["PEPGENX_ONBOARDING_SECRET"], # their onboarding key value
)
client.admin.agent_registrations.upsert(
client_id=os.environ["PEPGENX_OIDC_CLIENT_ID"],
asset_id="ast_my_agent",
team_id=os.environ["PEPGENX_TEAM_ID"],
project_id=os.environ["PEPGENX_PROJECT_ID"],
)
Without a registration (or with enabled=false), memory reads and writes return 403 memory_not_enabled. See docs/AUTH.md and docs/PEPGENX.md.
The legacy composite header contract (API key plus team_id / project_id / user_id) is compatibility only, reached by passing auth=PepGenXAuth(...) to the constructor; it is documented in docs/AUTH.md and docs/PEPGENX.md.
Lyzr-hosted quick start
Documented hosted base URL: https://memory.studio.lyzr.ai. Always pass base_url= or set LYZR_MEMORY_BASE_URL; there is deliberately no compiled default hostname.
import os
from lyzr_memory_client import MemoryClient
client = MemoryClient.for_lyzr(
base_url=os.environ.get("LYZR_MEMORY_BASE_URL", "https://memory.studio.lyzr.ai"),
api_key=os.environ["LYZR_API_KEY"],
)
Environment variables
| Variable | Purpose |
|---|---|
LYZR_MEMORY_BASE_URL |
Service base URL (required unless base_url= is passed) |
LYZR_MEMORY_TOKEN |
Bearer token for plain BearerAuth resolution |
LYZR_API_KEY |
Lyzr-hosted x-api-key |
OIDC_TOKEN_URL / OIDC_ISSUER |
Token endpoint, or Okta issuer (issuer → {issuer}/v1/token) |
OIDC_CLIENT_ID |
OAuth client id |
OIDC_CLIENT_SECRET |
OAuth client secret |
OIDC_SCOPE |
Optional space-delimited scopes |
OIDC_AUDIENCE |
Optional audience claim for the token request |
ClientCredentials.from_env() reads the OIDC_* variables above; pass prefix= to use a different naming scheme.
With auth=None, MemoryClient(...) resolves auth from the environment in this order: OIDC_CLIENT_ID + OIDC_CLIENT_SECRET (with OIDC_ISSUER or OIDC_TOKEN_URL) → a minting BearerAuth, then PEPGENX_API_KEY, then LYZR_API_KEY, then LYZR_MEMORY_TOKEN. A provider built from OIDC_* is owned by the client and closed with it.
Async
from lyzr_memory_client import AsyncMemoryClient
async with AsyncMemoryClient.for_lyzr(base_url=..., api_key=...) as client:
me = await client.whoami()
AsyncMemoryClient mirrors MemoryClient; call await client.aclose() if you are not using the context manager.
Resources and top-level helpers
Namespaces: memories, summaries, conversations, providers, auth, admin.api_keys, admin.agent_registrations, health.
Convenience methods on the client apply instance defaults (owner_id, agent_id, session_id, project_id, provider_type): add, search, get_context, list, list_all, messages, clear_session, get, update, delete, graph_query, whoami, health, ready. Use with_defaults(...) to get a new client sharing the same transport.
Retries and timeouts
Defaults: 30 s total timeout, 5 s connect; 3 attempts with exponential backoff 1 s → 30 s (jitter); Retry-After is honoured when present.
POST /v1/memories is not idempotent server-side. The SDK retries it only on connection-phase failures and HTTP 429 / 503, never after a read timeout. Full retry rules: docs/ERRORS.md.
Eventual consistency
Fact extraction runs asynchronously unless you pass sync_extraction=True on add. Search and list may lag briefly after a fire-and-forget add.
Tenancy rules
- Body/query
project_idmust match the authenticated project when the principal carries one; mismatch → 400tenant_key_conflict. - Service principals (client-credentials tokens) are bound to their Okta subject as
agent_id; they cannot choose a differentagent_idor widen viaagent_ids.
Error handling
from lyzr_memory_client import PermissionDeniedError
try:
client.update(memory_id, content="…")
except PermissionDeniedError as e:
print(e.code, e.request_id, e.detail)
See docs/ERRORS.md for the full hierarchy and .code vocabulary.
Transaction ids
Pass transaction_id= on any resource call. The SDK sends it as the transaction_id header; the service echoes the correlation id in X-Request-Id (available as APIError.request_id).
Security
- Credentials are wrapped in
Secretand never appear in logs orrepr. - TLS verification is on by default (
verify=True). - Corporate CA:
MemoryClient(..., verify="/path/to/ca.pem"). - HTTP proxies follow httpx
trust_env(standardHTTP_PROXY/HTTPS_PROXY/NO_PROXY).
Compatibility
SDK 0.1.x targets Lyzr Memory API v1 as frozen in spec/SOURCE (OpenAPI pin: spec/openapi.json).
Development
| Target | Purpose |
|---|---|
make sync |
uv sync --locked |
make lint / make fmt |
Ruff check / format |
make type |
mypy --strict |
make test |
pytest with ≥90% coverage |
make test-e2e |
Live e2e (-m e2e) |
make security |
bandit + pip-audit |
make build |
Wheel/sdist + twine check |
make gen-models |
Regenerate models (scripts/gen_models.sh) |
make openapi-check |
Diff against live export (scripts/export_openapi_memory.py) when MEMORY_REPO is set |
make sync-core |
Vendor _core from/to the sibling RAG client |
make all |
lint + type + test + security + build |
License
MIT. See LICENSE.
Metadata
Release files for lyzr-memory-client 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lyzr_memory_client-0.2.0.tar.gz | 118.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lyzr_memory_client-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 180.9 kB
Release files / lyzr_memory_client-0.2.0.tar.gz
| Download URL | lyzr_memory_client-0.2.0.tar.gz |
|---|---|
| Size | 118.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6970c1ef899d80cdae09ca6e335cc0e95c3129598321553e1645269ffdaeacce
|
|
BLAKE2b-256 checksum How to use checksums |
f44d677c52ed13558f47a69a1a548987fb716b90138b4dd754959c6e7dfee981
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.
Transparency logRelease files / lyzr_memory_client-0.2.0-py3-none-any.whl
| Download URL | lyzr_memory_client-0.2.0-py3-none-any.whl |
|---|---|
| Size | 62.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a86af9241588a57d427373c4f563830cacc3bfcb4adce7beb9bac206c9221426
|
|
BLAKE2b-256 checksum How to use checksums |
95726df0cf0a44f687eb23bd04b83423dea7dd4cb61036656f56196940cc952d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.
Transparency log