pi-py
Python port of pi's core libraries, published on
PyPI as one package, pi-ai-python, with two import packages:
| Import | Upstream | What it does |
|---|---|---|
pi_ai |
@earendil-works/pi-ai |
Unified LLM API: provider collections, auth resolution (API keys, OAuth), streaming events, token and cost tracking, tool calling, cross-provider context hand-off, image generation, classification |
pi_agent |
@earendil-works/pi-agent-core |
Stateful agent: tool execution (parallel or sequential), steering and follow-up queues, lifecycle events, hooks |
pip install pi-ai-python # httpx + official SDKs: anthropic, openai, google-genai
pip install "pi-ai-python[bedrock]" # + boto3 for Amazon Bedrock
pip install "pi-ai-python[all]" # + orjson, websockets, pydantic, boto3
中文说明见 README.zh-CN.md。
import asyncio
from pi_ai import Context, UserMessage
from pi_ai.providers.all import builtin_models
async def main() -> None:
models = builtin_models() # every built-in provider; auth comes from env vars such as ANTHROPIC_API_KEY
model = models.get_model("anthropic", "claude-sonnet-4-5")
stream = models.stream(model, Context(system_prompt="Be brief.", messages=[UserMessage("Hello!")]))
async for event in stream:
if event.type == "text_delta":
print(event.delta, end="", flush=True)
message = await stream.result()
print(f"\n{message.usage.input} in / {message.usage.output} out, ${message.usage.cost.total:.4f}")
asyncio.run(main())
from pi_agent import Agent, AgentInitialState, agent_tool
@agent_tool
async def get_time(timezone: str = "UTC") -> str:
"""Get the current time.
Args:
timezone: IANA timezone name.
"""
...
agent = Agent(
initial_state=AgentInitialState(system_prompt="You are helpful.", model=model, tools=[get_time]),
stream_fn=models.stream_simple,
)
async for event in agent.events("What time is it in Tokyo?"):
print(event.type)
Same API, Python conventions
The port keeps pi's architecture and semantics: the same Models/Provider
runtime, the same event protocol (start → text_delta… → done/error), the
same transcript model (system prompt and tools carried by system messages), the
same auth precedence rules, the same model catalog, and the same agent loop.
Names follow Python conventions:
| TypeScript | Python |
|---|---|
createModels(), models.streamSimple(...) |
create_models(), models.stream_simple(...) |
{ apiKey, sessionId, maxTokens } options |
api_key=..., session_id=..., max_tokens=... keyword arguments (or a dict) |
message.stopReason, usage.cacheRead |
message.stop_reason, usage.cache_read |
{ role: "user", content: "hi", timestamp } |
UserMessage("hi") (timestamp defaults to now) |
Type.Object({...}) (TypeBox) |
Type.Object({...}) (built in) or a Pydantic model |
AbortController / signal |
pi_ai.AbortController / signal= |
for await (const e of stream) / await stream.result() |
async for e in stream / await stream.result() |
agent.continue() |
agent.continue_() (alias agent.resume()) |
String values that appear on the wire or in persisted transcripts are unchanged
("toolUse", "toolResult", "text_delta", API ids, provider ids), and
pi_ai.serialization reads and writes pi's camelCase JSON, so sessions, model
definitions and auth.json credential files are interchangeable with TypeScript pi.
Design notes
- Official vendor SDKs. As in pi, the Anthropic, OpenAI (Chat Completions,
Responses, Azure, OpenRouter images), Google (Gemini, Vertex) and Bedrock adapters use
the vendors' official SDKs:
anthropic,openai,google-genaiandboto3(pi-ai-python[bedrock]). The SDK clients share one pooledhttpx.AsyncClientper event loop. APIs that pi calls without an SDK (Mistral, OpenAI Codex, pi-messages, classifiers) usehttpxdirectly. - Fast streaming path. Slotted data classes, an allocation-light event queue,
zero-copy forwarding between wrapped streams, an incremental partial-JSON parser
for tool-call arguments (about 60x faster than re-parsing each delta on large
arguments), linear-time accumulation of streamed text, optional
orjsondecoding, and compiled, cached tool-argument validators. On one core the anthropic-messages adapter processes about 110,000 stream events per second and openai-completions about 70,000, where the openai SDK's SSE decoder is the limit (seebenchmarks/). - Lazy everything. Model catalogs load per provider on first use; API implementations and OAuth flows import on first request.
- Async-native cancellation.
AbortSignalaborts interrupt pending network reads immediately (task cancellation scoped byabort_scope), and aborted requests end their stream withstop_reason == "aborted"instead of raising. - Python-friendly additions. Pydantic models as tool parameter schemas,
@agent_toolto build tools from typed functions,Agent.events()as an async iterator,FileCredentialStore(pi'sauth.jsonformat with cross-process locking), keyword-argument options, streams that can be created outside an event loop and closed withasync with.
Development
uv sync # create .venv with the package (editable) and dev tools
uv run pytest # run all tests (offline; HTTP is mocked)
uv run ruff check . && uv run ruff format --check .
uv run pyright
uv run python benchmarks/bench_partial_json.py
uv run python benchmarks/bench_stream.py
Model catalog data (packages/ai/src/pi_ai/providers/data/*.json) is the generated
catalog shipped with @earendil-works/pi-ai 1.3.0.
License
MIT. Based on pi by Mario Zechner and contributors.
Metadata
Release files for pi-ai-python 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pi_ai_python-1.3.0.tar.gz | 548.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pi_ai_python-1.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 996.4 kB
Release files / pi_ai_python-1.3.0.tar.gz
| Download URL | pi_ai_python-1.3.0.tar.gz |
|---|---|
| Size | 548.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
092b270ad64a8ee02467d99323644a6ed4864e8e7143a53f8bf425fb6db683c2
|
|
BLAKE2b-256 checksum How to use checksums |
0f1e336dcfa949d37087f34c145afc79f2c41c325f055347811d45d042d26c1d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logRelease files / pi_ai_python-1.3.0-py3-none-any.whl
| Download URL | pi_ai_python-1.3.0-py3-none-any.whl |
|---|---|
| Size | 447.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ba6e6297fec7ceac3a3fa03a4022469e078534cc8abb67196a85e9f70b1e42e8
|
|
BLAKE2b-256 checksum How to use checksums |
48b7144758e3a59996e906a3b5f4abd4eef8233ff4b1fa3523df84b56e945912
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency log