Metadata-Version: 2.4
Name: pillar-ai-server
Version: 0.2.4
Summary: Pillar backend SDK — register server-side tools and handle tool calls from Pillar Cloud
Author-email: Pillar Team <eng@trypillar.com>
License: MIT
Keywords: agent,ai,pillar,sdk,tools
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: typing-extensions>=4.0; python_version < '3.11'
Provides-Extra: pydantic
Requires-Dist: pydantic>=2.0; extra == 'pydantic'
Description-Content-Type: text/markdown

# pillar-ai-server

Python backend SDK for [Pillar](https://trypillar.com) — register server-side tools and handle tool calls from Pillar Cloud.

## Install

```bash
pip install pillar-ai-server
```

## Quick start

```python
from pillar import Pillar, ToolContext

pillar = Pillar(secret="plr_...")

@pillar.tool(description="Look up a customer by email")
async def lookup_customer(email: str, ctx: ToolContext) -> dict:
    customer = await db.get_customer(email=email)
    return {"name": customer.name, "plan": customer.plan}
```

## Framework integration

### Django

```python
# urls.py
from myapp.pillar_tools import pillar

urlpatterns = [
    path("pillar/", pillar.django_view()),
]
```

### Flask

```python
from myapp.pillar_tools import pillar

app.route("/pillar", methods=["POST"])(pillar.flask_handler())
```

### Standalone

```python
pillar.serve(port=8787)
```

## Confirmation responses

When a tool needs user confirmation before performing an action, return a `ConfirmationResponse` from your handler. Pillar renders a channel-appropriate confirmation UI and re-calls your handler with `ctx.confirmed = True` after the user confirms.

```python
from pillar import Pillar, ToolContext, ConfirmationResponse

pillar = Pillar(secret="plr_...")

@pillar.tool(description="Create a new plan")
async def create_plan(name: str, price: float, ctx: ToolContext) -> dict:
    if not ctx.confirmed:
        return ConfirmationResponse(
            confirmation_required=True,
            title="Create plan",
            message=f'Create "{name}" at ${price}/mo?',
            details={"Plan name": name, "Price": f"${price}/mo"},
            confirm_payload={"name": name, "price": price},
        )

    await db.create_plan(name=name, price=price)
    return {"created": True}
```

### ConfirmationResponse fields

| Field | Required | Description |
|---|---|---|
| `confirmation_required` | yes | Must be `True`. |
| `title` | no | Short headline. Defaults to the tool name. |
| `message` | no | One-line description shown before the buttons. |
| `details` | no | Key-value pairs rendered as a summary card. |
| `confirm_payload` | no | Opaque data passed back to your handler on confirm. Defaults to the original arguments. |

### ToolContext

| Field | Type | Description |
|---|---|---|
| `caller` | `CallerInfo` | Identity and channel metadata for the user. |
| `conversation_id` | `str` | Current conversation ID. |
| `call_id` | `str` | Unique ID for this tool call. |
| `product_id` | `str \| None` | Product ID, if applicable. |
| `confirmed` | `bool` | `True` when re-executing after user confirmation. |
| `is_identified` | `bool` | `True` when the caller has a resolved external user ID. |
