Skip to main content

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]"

Install the latest pre-release explicitly:

pip install --pre "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() supported
  • capabilities() supported
  • stream() supported
  • text deltas supported
  • streaming tool execution through Runtime.stream() supported
  • base_url supported
  • options["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:

  1. Update [project].version in pyproject.toml
  2. Merge the release commit to main
  3. Create a version tag such as v0.2.0a1
  4. Push the tag to GitHub

The publish workflow will:

  • verify the Git tag matches pyproject.toml
  • run the test suite
  • build sdist and wheel
  • validate package metadata
  • publish to PyPI through Trusted Publishing

Example:

git tag v0.2.0a1
git push origin v0.2.0a1

Repository setup requirement:

  • configure PyPI Trusted Publishing for this GitHub repository and the .github/workflows/publish.yml workflow

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

runlet-0.2.0a1.tar.gz (21.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

runlet-0.2.0a1-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

Details for the file runlet-0.2.0a1.tar.gz.

File metadata

  • Download URL: runlet-0.2.0a1.tar.gz
  • Upload date:
  • Size: 21.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for runlet-0.2.0a1.tar.gz
Algorithm Hash digest
SHA256 b4f7de3f7c228d7d10718f1aa44c27c32a44e4933351de868bb99d9001292bf8
MD5 a4a3ed67d394fa5a7d3c9f9ade719de5
BLAKE2b-256 7a40f83dac877e361b141d6aef91582e84c23bf70071687c30bb7655275127d2

See more details on using hashes here.

Provenance

The following attestation bundles were made for runlet-0.2.0a1.tar.gz:

Publisher: publish.yml on DMIAOCHEN/runlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file runlet-0.2.0a1-py3-none-any.whl.

File metadata

  • Download URL: runlet-0.2.0a1-py3-none-any.whl
  • Upload date:
  • Size: 17.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for runlet-0.2.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 5d426120bda5e0aa4ded4c30e3aec4d39b7769fff8b603980e14c28a34468764
MD5 05e83c549f195554c518c762ae4f2e6d
BLAKE2b-256 c692c77c9e1a6123784d0213d790d4fe4f306a60b61412c7a75fb83d272c1f6d

See more details on using hashes here.

Provenance

The following attestation bundles were made for runlet-0.2.0a1-py3-none-any.whl:

Publisher: publish.yml on DMIAOCHEN/runlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page