Typed sync and async Python client plus middleware for the Pali memory API.
Project description
Pali Python Client
Typed sync and async Python SDK plus middleware for the Pali memory API.
Pali is very early in development and should not be treated as a complete memory solution yet. Right now the product focus is infrastructure correctness and reliability first.
Middleware is available as an early-stage autopilot helper, not a guaranteed memory optimization system.
Installation
pip install pali-client
Optional extras:
pip install "pali-client[openai]"
pip install "pali-client[dev]"
Environment Variables
The client supports low-priority environment variable fallbacks:
PALI_BASE_URLPALI_TOKENPALI_TIMEOUT
Constructor arguments always win over environment values.
Quickstart
from pali import PaliClient
client = PaliClient("http://127.0.0.1:8080")
client.create_tenant("user:42", name="User 42")
client.store("user:42", "Likes jazz", tags=["music"], kind="observation")
results = client.search("user:42", "music preferences", top_k=3)
for item in results.items:
print(item.content)
Common Patterns
Store a memory:
from pali import PaliClient
client = PaliClient("http://127.0.0.1:8080", token="jwt-token")
stored = client.store(
"user:42",
"Moved to Austin in 2024.",
tier="episodic",
kind="event",
tags=["profile"],
source="chat_message",
created_by="user",
)
print(stored.id)
Search memory:
results = client.search(
"user:42",
"where does the user live?",
top_k=5,
min_score=0.25,
tiers=["episodic", "semantic"],
kinds=["event", "observation"],
)
Delete a memory:
client.delete("user:42", "mem_abc123")
Async client:
import asyncio
from pali import PaliAsyncClient
async def main() -> None:
async with PaliAsyncClient("http://127.0.0.1:8080") as client:
health = await client.health()
print(health.status)
asyncio.run(main())
Middleware wrap (experimental autopilot):
from pali import PaliClient, PaliMiddleware
client = PaliClient("http://127.0.0.1:8080")
middleware = PaliMiddleware(client, "user:42")
def llm(messages):
return "The user likes jazz."
wrapped = middleware.wrap(llm)
reply = wrapped([{"role": "user", "content": "What music do I like?"}])
print(reply)
Auto-mutate memory with explicit opt-in (experimental):
from pali import PaliMiddleware, ReplaceMemoryAction, StoreMemoryRequest
def planner(messages, recalled_memories, result, response_text):
if recalled_memories and "moved to austin" in response_text.lower():
return [
ReplaceMemoryAction(
memory_id=recalled_memories[0].id,
request=StoreMemoryRequest(
tenant_id="user:42",
content="User lives in Austin.",
kind="observation",
created_by="system",
),
)
]
return []
middleware = PaliMiddleware(
client,
"user:42",
allow_destructive_actions=True,
action_planner=planner,
)
allow_destructive_actions=False by default. That means auto-add is on, but delete/replace actions are skipped unless the caller opts in.
Middleware hooks:
from pali import PaliClient, PaliMiddleware
def hook(phase: str, payload: dict[str, object]) -> None:
print(phase, sorted(payload))
client = PaliClient("http://127.0.0.1:8080")
middleware = PaliMiddleware(
client,
"user:42",
hooks={
"SEARCH": [hook],
"INJECT": [hook],
"CALL": [hook],
"STORE": [hook],
},
)
Anthropic wrap:
wrapped = middleware.wrap_anthropic(anthropic_client)
response = wrapped.messages.create(
model="claude-3-7-sonnet-latest",
max_tokens=256,
system="Be concise.",
messages=[{"role": "user", "content": [{"type": "text", "text": "What do I like?"}]}],
)
Configuration Reference
| Argument | Type | Default | Env var | Notes |
|---|---|---|---|---|
base_url |
str |
required | PALI_BASE_URL |
Constructor argument wins over env. |
token |
`str | None` | None |
PALI_TOKEN |
timeout |
float |
15.0 |
PALI_TIMEOUT |
Applied per request. |
max_retries |
int |
3 |
none | 1 disables retries. |
http_client |
`httpx.Client | httpx.AsyncClient | None` | None |
Current API Coverage
Implemented now:
health()create_tenant()tenant_stats()store()store_batch()ingest()ingest_batch()search()list_postprocess_jobs()get_postprocess_job()delete()
Not implemented because the current Pali server does not expose them as of March 12, 2026:
GET /v1/memory/:idPATCH /v1/memory/:idDELETE /v1/tenants/:id- cursor-based pagination for memory search
- streaming search/event feeds
Middleware mutation semantics:
- Default writeback is add-only.
- Middleware can now execute
store,delete, andreplaceactions when a customaction_planneris provided. replaceis currently executed as delete-plus-store because the server still has noPATCH /v1/memory/:id.- Destructive actions require
allow_destructive_actions=True.
Error Handling
from pali import NotFoundError, PaliError
try:
client.delete("user:42", "missing")
except NotFoundError as err:
print(err.request_id)
except PaliError as err:
print(err)
Common API errors are raised as typed subclasses:
UnauthorizedErrorForbiddenErrorNotFoundErrorConflictErrorRateLimitErrorAPIErrorValidationErrorTransportError
Contributing
See CONTRIBUTING.md.
Examples
See examples/basic.py, examples/async_client.py, and examples/middleware_hooks.py.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pali_client-0.2.0.tar.gz.
File metadata
- Download URL: pali_client-0.2.0.tar.gz
- Upload date:
- Size: 24.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3f4079883bc35de29dd58dd11bd57b95f96c4fe1d08d8205871a671e17281b1
|
|
| MD5 |
c1d4e2d17771ca185e6098a20c85baf1
|
|
| BLAKE2b-256 |
21db1660d5aa53370261d3d1aa76c05796ec3c74622ad98ce8cab320c6e02c09
|
File details
Details for the file pali_client-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pali_client-0.2.0-py3-none-any.whl
- Upload date:
- Size: 21.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63cb51509043e71a3640b4f41f4a5da161cd2000a171ff9e1d6d5bd3b0eb5958
|
|
| MD5 |
d38a33631c0411ee9baef7655818126c
|
|
| BLAKE2b-256 |
99691bde41defbe0f1cb3b142dead58c5ecdd4e903b284f2194298fef4333225
|