polyrt
Unified Python interface for LLMs across local and cloud backends.
polyrt is a typed adapter library that exposes a single small surface — generate,
stream, and their async variants — backed by Anthropic's Claude, OpenAI's GPT
models, and local Apple-Silicon models via MLX. It does not route, it does not
orchestrate, it does not run a server: it gives you one consistent function call
shape and one consistent return type, regardless of which backend you point it at.
Install
| Backend | Install |
|---|---|
| Claude (Anthropic) | pip install polyrt[anthropic] |
| OpenAI | pip install polyrt[openai] |
| MLX (Apple Silicon) | pip install polyrt[mlx] |
| All of the above | pip install polyrt[all] |
Python 3.11+ required.
Quickstart
Sync
"""Synchronous one-shot generation."""
import polyrt
result = polyrt.generate(
"claude",
model="claude-haiku-4-5",
messages=[{"role": "user", "content": "Say hello in one word."}],
max_tokens=20,
)
print(result.text)
print(f"Tokens: {result.usage.input_tokens} in, {result.usage.output_tokens} out")
Async streaming
"""Async streaming generation."""
import asyncio
import polyrt
async def main() -> None:
async for event in polyrt.astream(
"openai",
model="gpt-4.1-mini",
messages=[{"role": "user", "content": "Count to five."}],
max_tokens=40,
):
if event.type == "token":
print(event.delta, end="", flush=True)
elif event.type == "done" and event.usage is not None:
print(f"\n\n[{event.usage.total_tokens} tokens]")
asyncio.run(main())
Structured output
"""Schema-constrained structured output."""
from pydantic import BaseModel
import polyrt
class City(BaseModel):
name: str
country: str
population: int
result = polyrt.generate(
"claude",
model="claude-haiku-4-5",
messages=[{"role": "user", "content": "Pick a notable city and return its data."}],
schema=City,
)
assert isinstance(result.parsed, City)
print(f"{result.parsed.name}, {result.parsed.country} — pop {result.parsed.population:,}")
Backends
| Backend | Capabilities | Extra |
|---|---|---|
claude (alias: anthropic) |
schema, tools, vision, streaming | polyrt[anthropic] |
openai |
schema, tools, vision, streaming | polyrt[openai] |
mlx |
streaming (schema/tools/vision come in v0.2) | polyrt[mlx] |
polyrt.generate("anthropic", ...) and polyrt.generate("claude", ...) are
equivalent — pick whichever name reads better in your call site.
Schema enforcement
When you pass schema=SomeModel, polyrt asks the backend to constrain its output
to that Pydantic model and parses the result for you. The strategy differs by
backend:
- Claude — passes the schema as a single tool with
tool_choiceforced to that tool, then parses the tool'sinputpayload. - OpenAI — uses Structured Outputs (
response_formatwithtype=json_schema,strict=true) so the model is constrained at decode time. - MLX — not yet supported in v0.1; raises
NotSupportedError. v0.2 will add support viaoutlinesorlm-format-enforcer.
Response.parsed is your validated Pydantic instance when it works, None otherwise.
What polyrt is not
- Not a router that picks a backend for you
- Not an agent framework
- Not a model server or proxy
- Not a fine-tuning toolkit
- Not a prompt template engine
It is a typed adapter. If you want one of the things above, this is the layer you would build it on top of.
Adding a backend
- Subclass
Backend(or duck-type the protocol) and implement the seven methods. - Call
polyrt.registry.register("<name>", YourFactory)at module import time. - Either ship inside
polyrt/backends/<name>.py, or expose your factory as apolyrt.backendsentry point so users get it viapip install.
See docs/backends.md for the full contract.
License
Apache 2.0. See LICENSE.
Acknowledgments
anthropic-sdk-pythonopenai-pythonmlx-lmlitellm— prior art for the unified-interface idea.
Metadata
Release files for polyrt 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| polyrt-0.1.1.tar.gz | 26.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| polyrt-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.3 kB
Release files / polyrt-0.1.1.tar.gz
| Download URL | polyrt-0.1.1.tar.gz |
|---|---|
| Size | 26.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4b21ce885ed207b9faacccd294e76fd37f4b36a8acf09dd7a16727d03558f923
|
|
BLAKE2b-256 checksum How to use checksums |
1186335523b91ff2ea9fb950f5327783faa6409cf80e520621851aa31de621eb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.9 {"installer":{"name":"uv","version":"0.11.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / polyrt-0.1.1-py3-none-any.whl
| Download URL | polyrt-0.1.1-py3-none-any.whl |
|---|---|
| Size | 24.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e6cb059f02c961ddccce947b45657444b7ab35339ee232bf504b84fa160619ad
|
|
BLAKE2b-256 checksum How to use checksums |
ffdcbaeebf04acc8c79daeaaaa18bfc533d667c952010f40ef7401ff7bd12052
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.9 {"installer":{"name":"uv","version":"0.11.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|