Skip to main content

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_URL
  • PALI_TOKEN
  • PALI_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/:id
  • PATCH /v1/memory/:id
  • DELETE /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, and replace actions when a custom action_planner is provided.
  • replace is currently executed as delete-plus-store because the server still has no PATCH /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:

  • UnauthorizedError
  • ForbiddenError
  • NotFoundError
  • ConflictError
  • RateLimitError
  • APIError
  • ValidationError
  • TransportError

Contributing

See CONTRIBUTING.md.

Examples

See examples/basic.py, examples/async_client.py, and examples/middleware_hooks.py.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pali_client-0.2.0.tar.gz (24.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pali_client-0.2.0-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

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

Hashes for pali_client-0.2.0.tar.gz
Algorithm Hash digest
SHA256 a3f4079883bc35de29dd58dd11bd57b95f96c4fe1d08d8205871a671e17281b1
MD5 c1d4e2d17771ca185e6098a20c85baf1
BLAKE2b-256 21db1660d5aa53370261d3d1aa76c05796ec3c74622ad98ce8cab320c6e02c09

See more details on using hashes here.

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

Hashes for pali_client-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 63cb51509043e71a3640b4f41f4a5da161cd2000a171ff9e1d6d5bd3b0eb5958
MD5 d38a33631c0411ee9baef7655818126c
BLAKE2b-256 99691bde41defbe0f1cb3b142dead58c5ecdd4e903b284f2194298fef4333225

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page