Skip to main content

onelastleaf Python plugin SDK

This repository contains the asyncio/gRPC runtime used by Python plugins for onelastleaf. The package name is onelastleaf-plugin-sdk; Python code imports it as onelastleaf_plugin_sdk.

If you are here to try the SDK, there are two useful paths:

  • work on and test the SDK itself;
  • generate a small Python plugin and let oll install and run it.

Both are covered below. Python 3.11 or newer is required. The SDK is tested on Linux, macOS, and Windows with Python 3.11 through 3.14.

Install the SDK

Install the published release inside your project's virtual environment:

python -m pip install onelastleaf-plugin-sdk==0.1.0

Most plugin authors do not need to install the SDK by hand: the project generated by oll plugin new already declares this exact dependency.

Work on the SDK

Create a development environment

Keep the SDK's dependencies in a virtual environment inside this checkout:

python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e '.[dev]'

-e . installs the checkout in editable mode, so source changes are visible without reinstalling it. The dev extra adds the test, lint, type-check, package-build, and pinned protobuf-generation tools; plugin users do not install those tools.

If python3 -m venv cannot create an environment, install your operating system's Python venv/ensurepip package first. You should not need sudo pip or an installation into the system Python.

Run the tests

With the environment still active:

python -m pytest
ruff format --check src tests tools
ruff check src tests tools
mypy
python tools/generate_protocol.py --check

The tests cover handshake validation, full trace propagation, concurrent job completion/cancellation races, late host responses, stdin liveness, and bounded-memory artifact streaming. The other commands check formatting and typing and prove that the checked-in protocol bindings are reproducible.

Build a wheel and source archive

Python does not have a separate compile step for this SDK. The closest release build is:

python -m build

This writes a wheel and source archive to dist/. Building is not required while developing with an editable install.

Sync and regenerate the protocol

The main onelastleaf repository is the protocol authority. Pull the current files from GitHub rather than relying on another checkout that happens to be on your machine:

protocol_source="$(mktemp -d)"
git clone --depth 1 https://github.com/onelastleaf/onelastleaf.git \
  "$protocol_source/onelastleaf"
for name in common config document plugin; do
  cp "$protocol_source/onelastleaf/proto/oll/$name.proto" proto/oll/
done
python tools/generate_protocol.py
python tools/generate_protocol.py --check

grpcio-tools==1.75.1 is pinned in the development extra. Its generated gRPC guard therefore agrees with the runtime dependency floor grpcio>=1.75.1, and its protobuf generator agrees with protobuf>=6.31.1. Only src/onelastleaf_plugin_sdk/protocol/ is generated; there is no second output tree to keep in sync. CI runs Python 3.11 through 3.14 against those exact runtime floors, so the declared minimums are exercised rather than merely accepted by the resolver.

Try the SDK with oll

Install the matching released oll binary and put it on PATH, then follow the main project's quick start to initialize and start the local daemon. You will also need a remote Git repository that the daemon can clone.

A plugin cannot be run by pointing it at a public oll port: oll creates a private loopback gRPC endpoint for each plugin process.

1. Generate a Python plugin

Choose a new destination and stable plugin ID:

oll plugin new hello-python \
  --language python \
  --id dev.example.hello \
  --name hello-python
cd hello-python

Generation only writes files. It does not create a venv, install packages, initialize Git, or contact the daemon. The generated project contains an echo action, a test, pyproject.toml, and the publisher manifest oll.toml.

2. Install the published SDK dependency

The generated pyproject.toml pins the published SDK release:

dependencies = ["onelastleaf-plugin-sdk==0.1.0"]

Leave this dependency as published. During local plugin development, pip downloads it from the configured Python package index. During plugin installation, oll creates a fresh managed venv and the generated source recipe downloads the same pinned release there.

3. Test the plugin as a normal Python project

The plugin gets its own development environment. It is separate from the SDK checkout's .venv:

python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e . pytest
python -m pytest
deactivate

Do not start the generated console script directly as an end-to-end test. The script expects OLL_PLUGIN_ENDPOINT and a parent-liveness stdin pipe, both of which are supplied by oll when it supervises the process.

4. Commit, install, and call the plugin

oll installs source plugins from Git, so commit the project and push it to a remote the daemon can access:

git init -b main
git add .
git commit -m "feat: add hello Python plugin"
git remote add origin https://github.com/you/hello-python.git
git push -u origin main

Then install and start it:

oll plugin install https://github.com/you/hello-python.git --source
oll plugin start dev.example.hello
oll plugin info dev.example.hello

Call the generated action:

oll plugin call dev.example.hello echo -- hello from python
oll job info <job-id-printed-by-the-previous-command>

plugin call returns after the job is admitted and prints its job ID. Use job info to inspect the result. Runtime output is available through:

oll plugin log dev.example.hello

After changing the plugin, commit and push again, then publish the new generation and restart the running process:

oll plugin update dev.example.hello
oll plugin restart dev.example.hello

A successful update deliberately does not restart a running plugin for you.

Why there are several virtual environments

Python venvs can feel confusing here because they solve three different problems:

Environment Owner Purpose
SDK checkout .venv you Run this repository's tests and build packages.
Plugin checkout .venv you Run the plugin's own tests while developing it.
Installed generation .venv oll Run the exact plugin generation managed by the daemon.

The first two are disposable local development environments. oll neither reads nor reuses them. The generated Python oll.toml instead declares this recipe:

[source]
checkout = "generation"
steps = [
  ["python3", "-m", "venv", "{generation}/.venv"],
  ["{generation}/.venv/bin/pip", "install", "{generation}"],
]

[runtime]
argv = ["{generation}/.venv/bin/hello-python"]

oll places the complete Git checkout in its final UUID-named generation before creating the venv. That ordering is important because venv scripts contain absolute paths and may break if their directory is renamed afterward. Only after the build is validated does oll atomically switch current to the new generation; a failed update leaves the old one current. On startup, oll also cleans up unreferenced generations left behind by a crash.

The installed generation keeps the source checkout, its .git directory, the venv, and other build output. Do not commit secrets, and expect an installed Python plugin to use more disk space than its source tree alone.

This checkout mode is selected by the publisher manifest, not guessed from the language, and a user mask cannot change it. Generation-mode recipes may refer to {generation} and {mask_dir}; {source} and {install} are not available in this mode. Release installation ignores the source checkout recipe and still requires a relocatable published artifact.

Runtime model in plain English

The plugin does not host a server. oll hosts a loopback TCP gRPC server on an ephemeral port, starts the plugin as a trusted child process, and passes the address in OLL_PLUGIN_ENDPOINT. The SDK connects to that address, completes the protocol handshake, and dispatches actions registered with Plugin.action.

The child's stdin is reserved for parent liveness. EOF means the oll parent is gone, so the SDK exits; stdin is not available for plugin commands or user input. stdout and stderr are captured in per-plugin logs.

A minimal action looks like this:

import asyncio

from onelastleaf_plugin_sdk import ActionResult, Plugin

plugin = Plugin("dev.example.hello", "0.1.0")


@plugin.action("echo", "Return the supplied arguments")
async def echo(_context, arguments: list[str]) -> ActionResult:
    return ActionResult.string(" ".join(arguments))


asyncio.run(plugin.run())

The generated project wraps this in a console entry point so oll can start it from the managed venv.

The action context owns a host API already bound to that job and its trace, so normal plugin code does not pass job_id or trace messages around:

from onelastleaf_plugin_sdk.protocol import common_pb2

configured = await context.host.get_config()
document = await context.host.read_document(request)
await context.host.log(
    common_pb2.LOG_LEVEL_INFO,
    "my-plugin",
    "work completed",
)

Artifacts use a seekable binary source. The SDK hashes it outside the event loop, negotiates the transfer, and sends one bounded chunk at a time:

from io import BytesIO

from onelastleaf_plugin_sdk import ArtifactInput

descriptor = await context.host.store_artifact(
    ArtifactInput(
        artifact_id="aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
        file_name="result.txt",
        media_type="text/plain",
        source=BytesIO(b"result"),
    )
)

context.deadline is informational. oll owns deadline enforcement and sends a job-scoped cancellation request when it expires. The SDK sets context.cancellation, cancels only that action task, keeps serving heartbeats and other jobs, and acknowledges cancellation only after the action has stopped.

Protocol evolution

This SDK follows the canonical protobuf wire contract. It never computes, embeds, publishes, or compares a schema hash or fingerprint. Descriptor-wide hashes change for compatible additions and unrelated services, so they reject valid peers. Protocol changes instead preserve field numbers and wire types, give additions safe absent semantics, and tolerate unknown fields. Exact SDK pins provide reproducible builds; they are not protobuf API versioning.

The plugin envelope has no SDK-level encoded-size cap. Both gRPC directions are configured as unlimited so Python's usual 4 MiB receive default is not retained. Artifact data still follows the smaller chunk size negotiated in HostHello.

Common problems

  • No module named onelastleaf_plugin_sdk: activate the intended venv and run python -m pip install -e . in the SDK or plugin checkout as appropriate.
  • OLL_PLUGIN_ENDPOINT is required: the plugin was started by hand. Install and start it through oll for an end-to-end run.
  • Installation fails while running pip: inspect the diagnostic's retained build-log path. The daemon user needs python3 with venv support and access to every package source named by pyproject.toml.
  • New code is installed but old behavior is still running: plugin update does not restart the process; run oll plugin restart <id-or-name>.

Metadata

Release files for onelastleaf-plugin-sdk 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for onelastleaf-plugin-sdk 0.1.0
File Size Uploaded
onelastleaf_plugin_sdk-0.1.0.tar.gz 69.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for onelastleaf-plugin-sdk 0.1.0
File Interpreter ABI Platform
onelastleaf_plugin_sdk-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 134.4 kB

Release files / onelastleaf_plugin_sdk-0.1.0.tar.gz

Download URL onelastleaf_plugin_sdk-0.1.0.tar.gz
Size 69.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ec83febdc332273775f696e63232cc8e0dc46270a66021bff3ee1da8ed883bea
BLAKE2b-256 checksum
How to use checksums
83f560a7af09f00b39d147f68f737ae485f0d440858963dd71cd2ec8f9b64fe3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Gentoo","version":"2.18","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / onelastleaf_plugin_sdk-0.1.0-py3-none-any.whl

Download URL onelastleaf_plugin_sdk-0.1.0-py3-none-any.whl
Size 65.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e33b7a06b48f2a79f3fd6d93d7657419dd1590407d87de4d4b276a4ebbc724ef
BLAKE2b-256 checksum
How to use checksums
8422e04cd1e8e7ee6bc949e915af007b652b98e1ad0369fd7e050f065754bd9e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Gentoo","version":"2.18","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page