A tiny observable runtime for Python agents.
Project description
Runlet
Runlet is a tiny observable runtime for Python agents.
The project is a library, not an application framework. Its core direction is a provider-neutral, async-first agent runtime with strict context budgeting, structured observability, and flexible hooks around model and tool execution.
Current Status
This repository now contains an MVP runtime skeleton with core contracts, events, tools, hooks, context budgeting, streaming, provider adapters, and in-memory state. The API is not stable yet.
Design Goals
- Keep the core runtime small and embeddable.
- Treat context budgeting and compression as mandatory runtime safety checks.
- Expose hooks before and after model calls, tool calls, state operations, and context compression.
- Emit structured events for runs, steps, model calls, tool calls, context changes, state changes, and failures.
- Stay provider-neutral: model SDKs integrate through adapters, not core dependencies.
Non-Goals
Runlet core does not aim to provide:
- A web application framework.
- A hosted agent platform.
- A task queue or worker system.
- A UI or trace viewer.
- A multi-tenant control plane.
- A graph workflow engine in the first release.
Project Documents
Minimal Shape
from runlet import Agent, Runtime, tool
@tool
async def lookup(order_id: str) -> str:
return f"order {order_id}"
agent = Agent(
name="support",
instructions="Help users with orders.",
model=my_model_provider,
tools=(lookup,),
)
result = await Runtime().run(agent, "Where is order 123?")
OpenAI Provider
Install the optional OpenAI dependency:
pip install "runlet[openai]"
Minimal example:
from runlet import Agent, Runtime
from runlet.providers import OpenAIResponsesProvider
provider = OpenAIResponsesProvider(model="gpt-5.5")
agent = Agent(
name="assistant",
instructions="Be helpful.",
model=provider,
)
result = await Runtime().run(agent, "Say hello in one sentence.")
Custom base URL:
from runlet.providers import OpenAIResponsesProvider
provider = OpenAIResponsesProvider(
model="gpt-5.5",
base_url="https://your-endpoint.example/v1",
)
Provider-specific request options:
from runlet.core import Message
from runlet.core.models import ModelRequest
from runlet.providers import OpenAIResponsesProvider
provider = OpenAIResponsesProvider(model="gpt-5.5")
request = ModelRequest(
messages=[Message.user("Summarize this briefly.")],
options={
"openai": {
"extra_body": {
"reasoning": {"effort": "medium"},
},
},
},
)
response = await provider.complete(request)
Streaming text deltas:
from runlet import Agent, Runtime
from runlet.providers import OpenAIResponsesProvider
provider = OpenAIResponsesProvider(model="gpt-5.5")
agent = Agent(
name="assistant",
instructions="Be helpful.",
model=provider,
)
async for event in Runtime().stream(agent, "Explain recursion in one sentence."):
if event.type == "model.stream.delta":
print(event.payload["delta"], end="")
Streaming with tool execution:
from runlet import Agent, Runtime, tool
from runlet.providers import OpenAIResponsesProvider
@tool
async def lookup_order(order_id: str) -> str:
return f"order {order_id} shipped"
provider = OpenAIResponsesProvider(model="gpt-5.5")
agent = Agent(
name="assistant",
instructions="Use tools when needed.",
model=provider,
tools=(lookup_order,),
)
async for event in Runtime().stream(agent, "Check order 123 and tell me the result."):
if event.type == "model.stream.delta":
print(event.payload["delta"], end="")
When the provider emits a tool call during streaming, Runtime.stream() now
executes the tool, appends the tool result to the conversation, and continues
the next model round until the run completes.
Current scope of the provider:
complete()supportedcapabilities()supportedstream()supported- text deltas supported
- streaming tool execution through
Runtime.stream()supported base_urlsupportedoptions["openai"]["extra_body"]supported- provider-specific request options stay under
ModelRequest.options["openai"]
Current streaming contract:
- providers can emit provider-neutral streaming step events internally
Runtime.stream()handles multi-round tool execution loops- OpenAI is the first provider implementation of this contract
Development
Run the current test suite:
PYTHONPATH=src python3 -m unittest discover tests
Releasing
Runlet publishes to PyPI from Git tags through GitHub Actions.
Release flow:
- Update
[project].versioninpyproject.toml - Merge the release commit to
main - Create a version tag such as
v0.1.0 - Push the tag to GitHub
The publish workflow will:
- verify the Git tag matches
pyproject.toml - run the test suite
- build
sdistandwheel - validate package metadata
- publish to PyPI through Trusted Publishing
Example:
git tag v0.1.0
git push origin v0.1.0
Repository setup requirement:
- configure PyPI Trusted Publishing for this GitHub repository and the
.github/workflows/publish.ymlworkflow
Project details
Release history Release notifications | RSS feed
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 runlet-0.1.0.tar.gz.
File metadata
- Download URL: runlet-0.1.0.tar.gz
- Upload date:
- Size: 21.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5cadf3b6ea6d4c5f827eaca0a6132fe8f70f840ee6b338a66fc3c78b50f858da
|
|
| MD5 |
5c98b75c8be23e4212d519db0ccf8ad0
|
|
| BLAKE2b-256 |
425854c0bf25ab4257b08b73815b6a6f62317c80e396f99d97b7bab10b0ca310
|
Provenance
The following attestation bundles were made for runlet-0.1.0.tar.gz:
Publisher:
publish.yml on DMIAOCHEN/runlet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
runlet-0.1.0.tar.gz -
Subject digest:
5cadf3b6ea6d4c5f827eaca0a6132fe8f70f840ee6b338a66fc3c78b50f858da - Sigstore transparency entry: 1978615100
- Sigstore integration time:
-
Permalink:
DMIAOCHEN/runlet@fb1eb0be1b67f423946f9eedebfa35f903b7b13d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/DMIAOCHEN
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fb1eb0be1b67f423946f9eedebfa35f903b7b13d -
Trigger Event:
push
-
Statement type:
File details
Details for the file runlet-0.1.0-py3-none-any.whl.
File metadata
- Download URL: runlet-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
28b040105fb5eeaa9781e24218e4ac0cdda1039b538c3f26c0b49623cc814e85
|
|
| MD5 |
8e3c06ef5d696e75695aa80563ead266
|
|
| BLAKE2b-256 |
b25b4bb53f4834a9b7ea919ad3fb92270992119db3e4eb84371623abebd7bb2d
|
Provenance
The following attestation bundles were made for runlet-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on DMIAOCHEN/runlet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
runlet-0.1.0-py3-none-any.whl -
Subject digest:
28b040105fb5eeaa9781e24218e4ac0cdda1039b538c3f26c0b49623cc814e85 - Sigstore transparency entry: 1978615317
- Sigstore integration time:
-
Permalink:
DMIAOCHEN/runlet@fb1eb0be1b67f423946f9eedebfa35f903b7b13d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/DMIAOCHEN
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fb1eb0be1b67f423946f9eedebfa35f903b7b13d -
Trigger Event:
push
-
Statement type: