Protocol-first orchestration for AI agents.
Declarative multiparty session types for AI agents
What it is
You describe one typed global choreography between named roles. par-ai
projects it onto every role, rejects alts a role cannot observe, and runs
the session. A language model fills a typed payload or picks a declared
branch — it does not route the workflow, invent roles, or call arbitrary
code. The protocol owns control; the model owns content.
What it catches
A role that must behave differently on two branches of a alt it never saw fails before any model call:
>>> from par_ai.kernel.errors import ProjectionError
>>> from par_ai import Nothing, Text
>>> from par_ai.protocol import alt, msg, project
>>> from par_ai.kernel.role import roles
>>> A, B, C = roles("A", "B", "C")
>>> broken = alt[A, B] (
... Nothing("Yes") >> msg[A, C] ("Y", Text),
... Nothing("No") >> msg[C, A] ("N", Text),
... ).close()
>>> try:
... project(broken, C)
... except ProjectionError as err:
... print("role" in str(err).lower() and "Yes" in str(err))
True
Install
pip install par-ai
pip install "par-ai[openai]" # optional OpenAI Responses backend
Quickstart
Minimal agent session with the OpenAI gpt-5.6-luna model:
import asyncio
from openai import AsyncOpenAI
from par_ai.agent import Agent
from par_ai.human import Human, ScriptedHumanIo
from par_ai import OpenAIModel
from par_ai import Text
from par_ai.protocol import msg, render
from par_ai.kernel.role import roles
from par_ai.runtime import AgentRuntime
A, B = roles('Writer', 'Reader')
proto = msg[A, B]('Note', Text).close()
print(render(proto))
model = OpenAIModel('gpt-5.6-luna', AsyncOpenAI(max_retries=0, timeout=30.0))
writer = Agent(model, A, 'send a short note', proto)
reader = Human(B, proto, ScriptedHumanIo([]))
async def main() -> None:
trace = await AgentRuntime(proto, [writer, reader]).run()
print(trace[0].payload)
asyncio.run(main())
Examples — live in 10 seconds
export OPENAI_API_KEY=...
pip install 'par-ai[openai]'
uv run python examples/online/hello.py
Sample output:
=== protocol ===
Writer -> Reviewer : Note(str)
Reviewer -> Writer : Ack(str)
end
=== conversation ===
Writer:Note -> Reviewer 'Ship the typed choreography first.'
Reviewer:Ack -> Writer 'Received.'
Catch a projection error without a key: uv run python examples/offline/projection_error.py.
Full ladder: examples/README.md.
When to use — and when not to
Use when
- the allowed interaction shape is known up front
- every role must only act on messages it actually receives
- you need projection to fail closed before the first model call
- tools are roles with a protocol surface, not free function-calling
- sessions must resume from recorded decisions without re-asking
Do not use when
- one agent with a free tool set is enough (use a simpler agent SDK)
- the route must be discovered at runtime by the model
- you need a large catalogue of vendor integrations out of the box
- you require a stable API before 1.0 (this project is research / 0.x)
- you need multi-process or multi-machine transport (runtime is in-process)
Links
Status
Research framework at 0.1.x. What par-ai proves, checks at run time, and
deliberately leaves to the application — including the non-guarantees (no
deadlock-freedom, no liveness, no exactly-once) — is the
guarantee table. Public
surface is the union of tier-1/2 __all__ exports; see
tests/public_api.txt.
Exception types are stable; message text and journal formats are not.
Security
Untrusted payloads and web content are data, not instructions. User-written
tools run with the host process privileges — sandbox them and validate paths
or commands before any effect. Give AsyncOpenAI a finite transport timeout
(the model wrapper does not add one). Deadline, Allowance, and Metered
bound branch windows, protocol steps, and token spend. Journals and tracers
persist payloads and model output in plaintext.
Private reports: SECURITY.md. Guidance: security.
Development
uv sync --all-groups
just all # or: uv run nox -t ci
Agent conventions for contributors live in AGENTS.md.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentsparty-0.1.0.tar.gz.
File metadata
- Download URL: agentsparty-0.1.0.tar.gz
- Upload date:
- Size: 110.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"25.11","id":"xantusia","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbaf3cef315f5f494ef0427c5f2009ec1369590eec69e6597deb48049047824d
|
|
| MD5 |
7169ca082b4dce118fc31cf697ddbe45
|
|
| BLAKE2b-256 |
67ded3a8fd5c796e18a7337f1c5f2ea7c4b512a7ce8ed6b01053f0b0fceb8736
|
File details
Details for the file agentsparty-0.1.0-py3-none-any.whl.
File metadata
- Download URL: agentsparty-0.1.0-py3-none-any.whl
- Upload date:
- Size: 141.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"25.11","id":"xantusia","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7691ad03ecac638bc85f694e28b45180a09dc8d9b8e19aa8ec7833f9e9cb387c
|
|
| MD5 |
dd7db09f61d1bcb0f8b846f4c02cd618
|
|
| BLAKE2b-256 |
ae719aebb068bc6b372320bb9c019c100299d3337d34e7791dd3ef08f574a301
|