Last Updated: 3/9/2026
Python Client
A Python client for interacting with the Pie server.
Installation
pip install pie-clientpip install pie-clientQuick Start
import asyncio import asynciofrom pie import PieClient, ParsedPrivateKey from pie import PieClient, ParsedPrivateKey async def main(): async def main(): async with PieClient("ws://127.0.0.1:8080") as client: async with PieClient("ws://127.0.0.1:8080") as client: # Authentication is always required
# Authentication is always required # If server auth is enabled, provide a valid private key:
# If server auth is enabled, provide a valid private key: key = ParsedPrivateKey.from_file("~/.ssh/id_ed25519") key = ParsedPrivateKey. from_file("~/.ssh/id_ed25519") await client.authenticate("username", key) await client. authenticate("username", key) # If server auth is disabled, any username works without a key:
# If server auth is disabled, any username works without a key: # await client.authenticate("any_username")
# await client.authenticate("any_username") # Upload and launch a program
# Upload and launch a program with open("my_program.wasm", "rb") as f: with open("my_program.wasm", "rb") as f: await client.upload_program(f.read()) await client. upload_program(f. read()) program_hash = "..." # blake3 hash of the wasm binary program_hash = "..."
# blake3 hash of the wasm binary instance = await client.launch_instance(program_hash) instance = await client. launch_instance(program_hash) # Interact with the instance
# Interact with the instance await instance.send("hello") await instance. send("hello") event, message = await instance.recv() event, message = await instance. recv() print(f"Received: {event.name} - {message}") print(f"Received: {event. name} - {message} ") asyncio.run(main()) asyncio. run(main())PieClient
The main client class for connecting to a Pie server.
Constructor
PieClient(server_uri: str) PieClient(server_uri: str)| Parameter | Type | Description |
|---|---|---|
server_uri | str | WebSocket URI (e.g., "ws://127.0.0.1:8080") |
Methods
| Method | Description |
|---|---|
authenticate(username, private_key=None) | Public key authentication (challenge-response) |
internal_authenticate(token) | Token-based internal authentication |
upload_program(bytes) | Upload a WASM program |
program_exists(hash) | Check if program is uploaded |
launch_instance(hash, args=[], detached=False) | Launch a program instance by hash |
launch_instance_from_registry(name, args=[], detached=False) | Launch from registry |
attach_instance(instance_id) | Attach to a detached instance |
list_instances() | List running instances |
terminate_instance(instance_id) | Terminate an instance |
ping() | Check server connectivity |
query(subject, record) | Generic server query |
Context Manager
Use as an async context manager for automatic cleanup:
async with PieClient("ws://127.0.0.1:8080") as client: async with PieClient("ws://127.0.0.1:8080") as client: # client is connected
# client is connected await client.authenticate("username") await client. authenticate("username") # ...
# ... # client is automatically closed
# client is automatically closedInstance
Represents a running program instance on the server.
Methods
| Method | Description |
|---|---|
send(message) | Send a string message to the instance |
upload_blob(bytes) | Upload binary data to the instance |
recv() | Receive next event (returns (Event, data)) |
terminate() | Request termination |
Example
instance = await client.launch_instance_from_registry(instance = await client. launch_instance_from_registry( "text-completion", "text-completion", ["--prompt", "Hello"] ["--prompt", "Hello"] ) ) # Send additional input
# Send additional input await instance.send("Continue the story...") await instance. send("Continue the story...") # Receive streaming output
# Receive streaming output while True: while True: event, data = await instance.recv() event, data = await instance. recv() if event.name == "Stdout": if event. name == "Stdout": print(data, end="") print(data, end = "") elif event.name == "Completed": elif event. name == "Completed": break breakEvent Types
Events returned by instance.recv():
| Event | Description |
|---|---|
Message | Text message from instance |
Stdout | Streaming stdout output |
Stderr | Streaming stderr output |
Blob | Binary data received |
Completed | Instance finished successfully |
Aborted | Instance was aborted |
Exception | Instance raised an exception |
ServerError | Server-side error |
OutOfResources | Resource limit reached |
ParsedPrivateKey
Handles SSH private keys for authentication.
Supported Key Types
- RSA (≥2048 bits)
- ED25519
- ECDSA (P-256, P-384)
Methods
# From file
# From file key = ParsedPrivateKey.from_file("~/.ssh/id_ed25519") key = ParsedPrivateKey. from_file("~/.ssh/id_ed25519") # From string
# From string key = ParsedPrivateKey.parse(key_content) key = ParsedPrivateKey. parse(key_content)Example
from pie import PieClient, ParsedPrivateKey from pie import PieClient, ParsedPrivateKey key = ParsedPrivateKey.from_file("~/.ssh/id_ed25519") key = ParsedPrivateKey. from_file("~/.ssh/id_ed25519") async with PieClient("ws://127.0.0.1:8080") as client: async with PieClient("ws://127.0.0.1:8080") as client: await client.authenticate("username", key) await client. authenticate("username", key)Detached Instances
Launch instances that persist after disconnection:
# Launch detached
# Launch detached instance = await client.launch_instance_from_registry(instance = await client. launch_instance_from_registry( "long-running-task", "long-running-task", detached=True detached = True ) ) instance_id = instance.id instance_id = instance. id # Later, reattach
# Later, reattach instance = await client.attach_instance(instance_id) instance = await client. attach_instance(instance_id) event, data = await instance.recv() event, data = await instance. recv()Error Handling
from pie import PieClient, PieError from pie import PieClient, PieError try: try: async with PieClient("ws://127.0.0.1:8080") as client: async with PieClient("ws://127.0.0.1:8080") as client: await client.authenticate("username") await client. authenticate("username") instance = await client.launch_instance("invalid_hash") instance = await client. launch_instance("invalid_hash") except PieError as e: except PieError as e: print(f"Pie error: {e}") print(f"Pie error: {e} ") except ConnectionError as e: except ConnectionError as e: print(f"Connection failed: {e}") print(f"Connection failed: {e} ")