Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Publish Python release

on:
push:
tags:
- "v*"
workflow_dispatch:

concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: read

jobs:
build:
name: Build release
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.12"

- name: Verify tag matches package version
if: github.event_name == 'push'
shell: bash
run: |
package_version=$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
test "v${package_version}" = "${GITHUB_REF_NAME}"

- name: Install build frontend
run: python -m pip install --disable-pip-version-check build

- name: Build wheel and source release
run: python -m build

- name: Store release packages
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: python-release-packages
path: dist/
if-no-files-found: error
retention-days: 1

publish-to-pypi:
name: Publish release to PyPI
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/livepeer-gateway
permissions:
id-token: write

steps:
- name: Download release packages
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: python-release-packages
path: dist/

- name: Publish release to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1

publish-to-testpypi:
name: Publish release to TestPyPI
if: github.event_name == 'workflow_dispatch'
needs: build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/livepeer-gateway
permissions:
id-token: write

steps:
- name: Download release packages
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: python-release-packages
path: dist/

- name: Publish release to TestPyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
with:
repository-url: https://test.pypi.org/legacy/
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
# Python cache
__pycache__/
*.pyc
.coverage
.pytest_cache/
dist/
htmlcov/

# Generated files
lp_rpc.proto
Expand Down
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Changelog

All notable changes to this project will be documented in this file. The
format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/).

## [1.0.0] - 2026-08-11

The first stable release of the Livepeer Python SDK.

### Added

- Live Runner registration, discovery, session reservation, raw calls, proxy
calls, and session lifecycle events.
- Scope startup for application and serverless runners.
- BYOC inference and training jobs, including signed payments, payment refresh,
status polling, and completion waits.
- Live video-to-video jobs with capability-aware orchestrator discovery,
ordered fallback selection, token-based configuration, and remote-signer
payments.
- Multi-track media publishing and media output APIs for bytes, decoded frames,
and demuxed packets.
- Trickle channels for control messages, events, JSON Lines, keepalives, and
observable publisher/subscriber statistics.
- Orchestrator information, capability discovery, TLS trust-on-first-use, and
typed SDK errors.

### Changed

- Declared the generated gRPC client's actual minimum runtime versions:
`grpcio>=1.76.0` and `protobuf>=6.31.1`.
- Completed the package metadata and documented installation from PyPI.

[1.0.0]: https://github.com/livepeer/livepeer-python-gateway/releases/tag/v1.0.0
62 changes: 62 additions & 0 deletions PUBLISHING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Publishing releases

Releases are built and published by
[`publish-to-pypi.yml`](.github/workflows/publish-to-pypi.yml). The workflow
uses PyPI Trusted Publishing, so no PyPI API token is stored in GitHub.

## Production release

1. Update the version in `pyproject.toml` and `uv.lock`.
2. Add the dated release notes to `CHANGELOG.md`.
3. Merge the release commit into `main` and ensure the test workflow passes.
4. Create and push an annotated tag matching the package version:

```bash
git switch main
git pull --ff-only github main
git tag -a v1.0.0 -m "Livepeer Python SDK 1.0.0"
git push github v1.0.0
```

5. Approve the deployment in the protected `pypi` GitHub Environment.
6. Verify the release at <https://pypi.org/project/livepeer-gateway/>.

The workflow rejects tags that do not exactly match `v` followed by the
version in `pyproject.toml`. The publishing action generates and uploads PyPI
digital attestations for both release files.

## TestPyPI

Run the `Publish Python release` workflow manually from the Actions tab.
Manual runs build and upload only to TestPyPI. Each package version can be
uploaded only once, so bump to a new pre-release version before repeating a
TestPyPI upload.

<details>
<summary>Initial repository setup (maintainers only)</summary>

Create these GitHub Environments in the repository settings:

- `pypi`: add a required reviewer and restrict deployment to version tags.
- `testpypi`: manual approval is optional because this target is only available
through a manually dispatched workflow.

Register a pending GitHub Trusted Publisher at
<https://pypi.org/manage/account/publishing/> with:

| Field | Value |
|---|---|
| PyPI project name | `livepeer-gateway` |
| Owner | `livepeer` |
| Repository | `livepeer-python-gateway` |
| Workflow | `publish-to-pypi.yml` |
| Environment | `pypi` |

Optionally register the same pending publisher at
<https://test.pypi.org/manage/account/publishing/>, using the `testpypi`
environment. PyPI and TestPyPI require separate accounts and publisher setup.

The workflow file must be present on the repository's default branch before a
Trusted Publisher can use it.

</details>
61 changes: 47 additions & 14 deletions README
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
Generate protobufs:
# Livepeer Python SDK

The official Python SDK for the Livepeer network. Submit AI and video compute
jobs directly to orchestrators, use a remote signer for payment, and stream
media and control data over Livepeer's trickle protocol.

## Requirements

- Python 3.12 or newer

## Installation

Install the stable release from PyPI:

```bash
python -m pip install livepeer-gateway
```

The import package is named `livepeer_gateway`:

```python
from livepeer_gateway import StartJobRequest, start_lv2v
```

## Development

Install the locked development dependencies with
[uv](https://docs.astral.sh/uv/):

```bash
uv sync --locked --group test
```

Generate protobufs after installing the code-generation extra:

```bash
uv sync --extra dev
uv run generate-lp-rpc
```
Expand All @@ -9,39 +42,39 @@ uv run generate-lp-rpc

Install the locked test dependencies and run the complete pytest suite:

```
```bash
uv sync --locked --group test
uv run --group test pytest
```

Pass a test file or node ID to pytest for a focused run:

```
```bash
uv run --group test pytest tests/test_live_runner.py
uv run --group test pytest tests/test_live_runner.py::TestLiveRunnerHelpers::test_parse_go_duration
```

Run the suite with the configured line and branch coverage:

```
```bash
uv run --group test pytest --cov=livepeer_gateway --cov-branch --cov-report=term-missing
```

## Usage Examples

First install dependencies for example code
```
```bash
uv sync --extra examples
```

Get orchestrator info, offchain mode
```
```bash
uv run examples/get_orchestrator_info.py localhost:8935
```

On-chain mode with a remote signer

```
```bash
uv run examples/get_orchestrator_info.py --signer "<signer-host:port>"

# Use a custom discovery endpoint to filter orchestrators
Expand All @@ -50,22 +83,22 @@ uv run examples/get_orchestrator_info.py --signer "<signer-host:port>" '<discove
```

Get orchestrator info using a token encoding signer / discovery parameters
```
```bash
uv run examples/get_orchestrator_info.py --token "<base64-token>"
```

Write raw frames to a LiveVideoToVideo job
```
```bash
uv run examples/write_frames.py localhost:8935
```

Capture MacOS camera frames and publish via write_frame
```
```bash
uv run examples/camera_capture.py localhost:8935
```

Capture MacOS camera frames and subscribe to media output (stdout or file)
```
```bash
uv run examples/camera_capture.py localhost:8935 --output - | ffplay -fflags nobuffer -flags low_delay -probesize 32 -i -
uv run examples/camera_capture.py localhost:8935 --output out.ts
```
Expand All @@ -78,13 +111,13 @@ async with job.media_output() as output:
```

Composite camera input and decoded output side-by-side with PTS delta
```
uv sync --group examples
```bash
uv sync --extra examples
uv run examples/in_out_composite.py localhost:8935
```

Subscribe to a LiveVideoToVideo trickle events channel
```
```bash
uv run examples/subscribe_events.py localhost:8935
```

Expand Down
Loading