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.


Built by the makers of Costumary, the craft build journal for serious makers.

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.1.tar.gz (24.7 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.1-py3-none-any.whl (21.2 kB view details)

Uploaded Python 3

File details

Details for the file pali_client-0.2.1.tar.gz.

File metadata

  • Download URL: pali_client-0.2.1.tar.gz
  • Upload date:
  • Size: 24.7 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.1.tar.gz
Algorithm Hash digest
SHA256 1ce77b9c3febcf6124d67d5a886cc96b24363668ffbabb13c2dfdd1b5a23a6cc
MD5 341beb21d7810096acff23dfa3512a20
BLAKE2b-256 5d18782803f69a04488191795638979f125caef0d67dd8b2851e88d0102bfc4b

See more details on using hashes here.

File details

Details for the file pali_client-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: pali_client-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 21.2 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 54eb187bdc17ef8a53f3c93effc7c7a2471bbdfc55032cb703c1b6b1e96dcb54
MD5 eac4f27ff578a28ecc79e5fa2bb65b37
BLAKE2b-256 f0fb85153036b44132e581a1f4a5d1a8363dcef617b1f8675e66f91581943d33

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