Skip to main content

Python SDK for Pacerelle encrypted local agent relays.

Project description

Pacerelle Python SDK

Python agent client for Pacerelle encrypted local agent relays.

Use this SDK to connect a local Python process to Pacerelle, receive messages, reply to conversations, drive widgets, and return encrypted files or media.

pip install pacerelle

Alpha release: APIs may change before the first stable release. Production wheels bundle the native Signal runtime for the target platform.

Before You Run

Create an agent in Pacerelle. The confirmation panel shows both Identifiant de l'agent and Jeton d'authentification. Use Copier la configuration .env to copy the required variables.

export PACERELLE_AGENT_ID="agent-id"
export PACERELLE_AGENT_TOKEN="agent-token"

On Windows PowerShell:

$env:PACERELLE_AGENT_ID = "agent-id"
$env:PACERELLE_AGENT_TOKEN = "agent-token"

Published packages connect to the Pacerelle API by default. Local source builds default to http://localhost:8080 for development.

Minimal Echo Agent

import asyncio
import os

from pacerelle import AgentGatewayClient

client = AgentGatewayClient(
    token=os.environ["PACERELLE_AGENT_TOKEN"],
    agent_id=os.environ["PACERELLE_AGENT_ID"],
    e2ee=True,
)


async def handle(message, agent):
    await agent.send_message(
        conversation_id=message.conversation_id,
        to=message.from_id,
        reply_to_message_id=message.id,
        text=f"Received: {message.text}",
    )


client.on_message(handle)
asyncio.run(client.connect())

Incoming Messages

The handler receives an AgentMessage:

async def handle(message, agent):
    print(message.id)
    print(message.conversation_id)
    print(message.from_id)
    print(message.text)
    print(message.attachments)
    print(message.widget_response)

Use message.from_id as the to value when replying to the user.

Sending Messages And Replies

Send a normal message:

await agent.send_message(
    conversation_id=message.conversation_id,
    to=message.from_id,
    text="I can help with that.",
)

Reply to a specific user message:

await agent.send_message(
    conversation_id=message.conversation_id,
    to=message.from_id,
    reply_to_message_id=message.id,
    text="Replying to your last message.",
)

Running Your Own Agent Logic

async def handle(message, agent):
    result = await run_my_agent(message.text)

    await agent.send_message(
        conversation_id=message.conversation_id,
        to=message.from_id,
        reply_to_message_id=message.id,
        text=result,
    )

Widgets

Widgets are sent as encrypted conversation messages. Each method returns the widget id. User answers arrive later as message.widget_response.

Confirm

await agent.send_confirm_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="confirm-delete",
    title="Delete file?",
    body="This cannot be undone.",
    danger=True,
    labels={"yes": "Delete", "no": "Cancel"},
)

Handle the answer:

if message.widget_response and message.widget_response.ref == "confirm-delete":
    if message.widget_response.cancelled:
        return
    if message.widget_response.value is True:
        await agent.send_message(
            conversation_id=message.conversation_id,
            to=message.from_id,
            text="Confirmed.",
        )

Choice

await agent.send_choice_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="choose-format",
    title="Choose a format",
    options=[
        {"id": "pdf", "label": "PDF"},
        {"id": "csv", "label": "CSV"},
    ],
    multi=False,
)

Permission

await agent.send_permission_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="permission-files",
    title="Allow file access?",
    body="The agent needs access to selected files.",
    scopes=["once", "session"],
)

Form

await agent.send_form_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="profile-form",
    title="Complete profile",
    submitLabel="Save",
    fields=[
        {"name": "email", "label": "Email", "type": "email", "required": True},
        {"name": "notes", "label": "Notes", "type": "textarea"},
    ],
)

Progress

progress_id = await agent.send_progress_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="import-progress",
    title="Importing files",
    value=10,
    max=100,
    cancellable=True,
)

Update it:

await agent.send_widget_update(
    conversation_id=message.conversation_id,
    to=message.from_id,
    ref=progress_id,
    spec={"value": 65, "body": "Almost done"},
)

File Picker

await agent.send_file_picker_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="pick-files",
    title="Choose files",
    multiple=True,
    accept=[".pdf", "image/*"],
    max_files=5,
)

Date And Time

await agent.send_datetime_widget(
    conversation_id=message.conversation_id,
    to=message.from_id,
    widget_id="schedule",
    title="Pick a meeting time",
    mode="datetime",
    min="2026-05-21T09:00:00",
)

Files And Media

send_file and send_media encrypt bytes locally with AES-GCM, upload only ciphertext to /agent/blobs, then send the attachment key and IV inside the E2EE message payload.

await agent.send_file(
    conversation_id=message.conversation_id,
    to=message.from_id,
    reply_to_message_id=message.id,
    text="Here is the report.",
    name="report.txt",
    mime="text/plain",
    data=b"private report",
)

Media adds optional dimensions or duration:

await agent.send_media(
    conversation_id=message.conversation_id,
    to=message.from_id,
    text="Preview attached.",
    name="chart.png",
    mime="image/png",
    data=png_bytes,
    width=1200,
    height=800,
)

Encryption

When e2ee=True, the SDK encrypts and decrypts messages locally before they leave your machine. On connect, the client publishes the agent pre-key bundle, establishes encrypted sessions for conversations, and keeps message contents opaque to the relay.

Use e2ee=False only for local debugging or non-encrypted transports.

MCP

This package is the Python SDK for building agents. The MCP server is distributed separately:

npx -y @pacerelle/mcp-server

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

pacerelle-0.1.0a1.tar.gz (52.9 kB view details)

Uploaded Source

Built Distributions

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

pacerelle-0.1.0a1-cp312-cp312-win_amd64.whl (781.1 kB view details)

Uploaded CPython 3.12Windows x86-64

pacerelle-0.1.0a1-cp312-cp312-manylinux_2_34_x86_64.whl (928.5 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.34+ x86-64

pacerelle-0.1.0a1-cp312-cp312-manylinux_2_34_aarch64.whl (738.7 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.34+ ARM64

pacerelle-0.1.0a1-cp312-cp312-macosx_14_0_arm64.whl (625.3 kB view details)

Uploaded CPython 3.12macOS 14.0+ ARM64

File details

Details for the file pacerelle-0.1.0a1.tar.gz.

File metadata

  • Download URL: pacerelle-0.1.0a1.tar.gz
  • Upload date:
  • Size: 52.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pacerelle-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 d83c15c91624fbd3133ed57250463785177a3ccf0654ab1a6cb23de61e08b661
MD5 f982338ba82d6c805b595c77dcfb1364
BLAKE2b-256 e8c705459b135f1ea85de7626a005bad0e47dc87012a523eefc5e325611fed8c

See more details on using hashes here.

File details

Details for the file pacerelle-0.1.0a1-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for pacerelle-0.1.0a1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 06bba4a2ced65d31f547b8b3efd05b97631c66ce653e1aa3a851d6d8497b3ceb
MD5 367237dee0077a68cf03a3a2ec5b17eb
BLAKE2b-256 11ea1a725e847f48f6a56cc2334fc0fcd69f430f844ff606300e3d2358a6ee5a

See more details on using hashes here.

File details

Details for the file pacerelle-0.1.0a1-cp312-cp312-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for pacerelle-0.1.0a1-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 4bbe1ea2a0709c3c9fde25142f4e365940d1cb314e64f974b4d80e50fed28367
MD5 a5e99f02358603366d763433c3dd0b5a
BLAKE2b-256 22ceca5b26a5277ea88aa93ba651ef39e9a804fd197fd899a238057af50dbac6

See more details on using hashes here.

File details

Details for the file pacerelle-0.1.0a1-cp312-cp312-manylinux_2_34_aarch64.whl.

File metadata

File hashes

Hashes for pacerelle-0.1.0a1-cp312-cp312-manylinux_2_34_aarch64.whl
Algorithm Hash digest
SHA256 cfa93bcd31ef9574b98a92219a142a9a762c2df8d2c639e09e2f89c3e33e80b1
MD5 a5d6efb7094f43abfd4209a442e52d78
BLAKE2b-256 b976f12ebf23073301e39c9256fca303ef5111f16e077f497399aa4ee9e83031

See more details on using hashes here.

File details

Details for the file pacerelle-0.1.0a1-cp312-cp312-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for pacerelle-0.1.0a1-cp312-cp312-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 b1d5ceb04933362ea1257b6b08f29e39d1c3ac7fc9057080767ed29bd2d59c06
MD5 7b733cce9a1a04434e3ff2f9bede354e
BLAKE2b-256 cb9c1909b0aa3dd0d142fec132fe92b03ae1ea506a86a5850ea6c5d4c4ddfa59

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