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
133 changes: 133 additions & 0 deletions .github/workflows/on-pr-sdk-python-e2e-full.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# QVAC sdk-python full cross-platform e2e - PR (test-e2e-full-gated)
#
# The broader real-worker leg: runs the FULL pytest suite (including the `heavy`
# parakeet/BCI streaming tests) on the self-hosted GPU runners for all three
# desktop platforms, proving the Python client cross-platform. Models are cached
# (keyed on the shared model registry) so repeat runs skip the cold download.
#
# The fast per-PR leg is on-pr-sdk-python-e2e.yml (hosted, `verified` label,
# minimal models, -m "not heavy"). This heavier leg is gated on `test-e2e-full`.
#
# Plain `pull_request` (not pull_request_target): the worker build uses only the
# public-npm `@qvac/*` packages and pytest (no `@tetherto` GPR harness / MQTT
# secrets), so this needs no secrets. The `test-e2e-full` label is the
# maintainer approval that gates running PR code on the self-hosted runners.
name: QVAC Tests (sdk-python) full - PR

on:
pull_request:
types: [labeled, synchronize]
paths:
- "packages/sdk-python/**"
- "packages/sdk/**"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
test:
name: full e2e (${{ matrix.os }})
# Only once `test-e2e-full` is applied (or already present, on later pushes),
# or on manual dispatch.
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event.action == 'labeled' && github.event.label.name == 'test-e2e-full') ||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'test-e2e-full'))
strategy:
fail-fast: false
matrix:
os: ["qvac-win25-x64-gpu", "qvac-ubuntu2204-x64-gpu", "qvac-macos26-arm64-gpu"]
runs-on: ${{ matrix.os }}
timeout-minutes: 45
defaults:
run:
shell: bash
working-directory: packages/sdk-python
env:
# Point the worker's model storage (SDK `cacheDirectory` config, applied
# via BareRpcTransport's __init_config) at a cacheable, absolute, per-OS
# path. Same idea as the SDK e2e's cacheDirectory.
QVAC_CACHE_DIR: ${{ github.workspace }}/.qvac-cache
steps:
# Self-hosted runners persist the workspace between runs; wipe it.
- name: Manual Workspace Cleanup
if: runner.environment != 'github-hosted'
working-directory: .
run: rm -rf "$GITHUB_WORKSPACE" && mkdir -p "$GITHUB_WORKSPACE"

- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2

# Use the runner's own Python. actions/setup-python doesn't work on these
# self-hosted GPU runners: its python-versions tarball ships a setup.sh
# that hardcodes `mkdir /Users/runner/hostedtoolcache`, which isn't
# writable here (the runner user's home is /Users/actions-runner-*). The
# runners already have Python; make a venv from it so installs don't touch
# the system interpreter. Fails loudly if the system Python is < 3.10.
# Per infra, don't run actions/setup-python on these runners: Python is
# already provisioned, and setup-python hard-fails anyway (its
# python-versions tarball hardcodes an unwritable /Users/runner toolcache
# path). Use the present Python via a venv (keeps installs out of the
# system interpreter). Fails loudly if the system Python is < 3.10.
- name: Set up Python (runner's system Python)
run: |
set -e
if command -v python3 >/dev/null 2>&1; then PY=python3; else PY=python; fi
echo "System Python: $("$PY" --version) at $(command -v "$PY")"
"$PY" -c 'import sys; assert sys.version_info[:2] >= (3, 10), "need Python >=3.10, got " + sys.version'
"$PY" -m venv .venv
if [ -x ".venv/bin/python" ]; then PYBIN="$(pwd)/.venv/bin/python"; else PYBIN="$(pwd)/.venv/Scripts/python"; fi
echo "PYBIN=$PYBIN" >> "$GITHUB_ENV"
"$PYBIN" -m pip install --upgrade pip
# node/bun are needed to build the worker. Unlike setup-python these
# actions work on the runners (they find the pre-cached tool, no download),
# and bun in particular is not otherwise on PATH. Same actions the SDK's
# own desktop e2e (test-node-sdk.yml) uses on these runners.
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # 6.3.0
with:
node-version: 22
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # 2.2.0
with:
bun-version: latest

# Shared model cache, keyed on the same registry the SDK e2e keys on, so
# it invalidates in lockstep. Cross-OS so the three platforms share it.
- name: Restore models cache
id: models-cache
uses: actions/cache/restore@v4
with:
path: ${{ github.workspace }}/.qvac-cache
key: qvac-py-models-${{ hashFiles('packages/sdk/models/registry/models.ts') }}
# Fall back to the SDK e2e's own cache for the same registry, so the
# first Python run reuses already-downloaded models instead of
# re-fetching them.
restore-keys: |
qvac-models-${{ hashFiles('packages/sdk/models/registry/models.ts') }}
enableCrossOsArchive: true

- name: Install (with transport + optional extras)
run: |
"$PYBIN" -m pip install -e ".[gen,dev,bare-rpc,vla,notebook]"

- name: Build worker
run: |
"$PYBIN" scripts/build_worker.py

- name: Test (full suite, real worker)
run: |
"$PYBIN" -m pytest tests/ -v

# Save even on test failure so the (multi-GB) models downloaded this run
# are cached and the next run doesn't re-fetch them. Skipped only when the
# cache already hit.
- name: Save models cache
if: always() && steps.models-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ github.workspace }}/.qvac-cache
key: qvac-py-models-${{ hashFiles('packages/sdk/models/registry/models.ts') }}
enableCrossOsArchive: true
64 changes: 64 additions & 0 deletions .github/workflows/on-pr-sdk-python-e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# QVAC sdk-python real-worker e2e - PR (verified-gated)
#
# The full real-worker suite builds the @qvac/sdk worker and downloads models,
# so it runs long. Like the other SDK e2es it's gated on the `verified` label
# rather than running on every PR β€” the fast leg (pr-checks-sdk-python.yml) runs
# gen/lint/type + the mocked unit tests on every PR.
#
# Plain `pull_request` (not pull_request_target): the worker build uses only the
# public-npm `@qvac/*` packages, so it needs no secrets and is fork-safe as-is.
name: QVAC Tests (sdk-python) - PR

on:
# Runs only when the `verified` label is applied (or on manual dispatch) β€”
# the real-worker suite downloads models and runs long, so it's opt-in per PR
# rather than firing on every push.
pull_request:
types: [labeled]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
e2e:
name: real-worker e2e (linux-x64)
# Fire only when the label just applied is `verified` (or on manual dispatch).
if: github.event.label.name == 'verified' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 45
defaults:
run:
working-directory: packages/sdk-python
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2

- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # 6.2.0
with:
python-version: '3.10'

- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # 6.3.0
with:
node-version: 22
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # 2.2.0
with:
bun-version: latest

- name: Install (with transport + optional extras)
run: |
python3 -m venv .venv
.venv/bin/pip install --quiet -e ".[gen,dev,bare-rpc,vla,notebook]"

# All-public @qvac deps, no auth β€” same as the CLI's plain build.
- name: Build worker
run: .venv/bin/python3 scripts/build_worker.py

# Fast leg: the CLI-set minimal models (Qwen 600M, EmbeddingGemma,
# Whisper EN-tiny, Supertonic). The `heavy` tests (parakeet/BCI streaming)
# run in the broader cross-platform leg that reuses the shared model cache.
- name: Test (real worker, fast set)
run: .venv/bin/python3 -m pytest -m "not heavy" tests/ -v
19 changes: 10 additions & 9 deletions .github/workflows/pr-checks-sdk-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,27 @@ jobs:
.venv/bin/pip install --quiet -e ".[gen,dev]"

# Regenerates from packages/sdk/contract/{schema,manifest}.json and
# fails if the committed src/qvac/_generated tree doesn't match β€”
# fails if the committed src/tetherto/qvac_sdk/_generated tree doesn't match β€”
# mirrors the SDK pod's own `contract:check` step.
- name: Contract check (generated client up to date)
run: .venv/bin/python3 scripts/generate.py --check

- name: Format check
run: .venv/bin/python3 -m black --check src/qvac scripts/ tests/
run: .venv/bin/python3 -m black --check src/tetherto/qvac_sdk scripts/ tests/

- name: Lint (ruff)
run: .venv/bin/python3 -m ruff check src/qvac scripts/ tests/
run: .venv/bin/python3 -m ruff check src/tetherto/qvac_sdk scripts/ tests/

- name: Typecheck (mypy)
run: |
.venv/bin/python3 -m mypy -p qvac
.venv/bin/python3 -m mypy -p tetherto.qvac_sdk
.venv/bin/python3 -m mypy scripts
.venv/bin/python3 -m mypy tests

# test_poc_smoke.py/test_poc_progress.py need a real Bare worker + a
# downloaded model; they skip themselves unless a built worker exists
# at WORKER (QVAC_POC_SDK_DIR override or its monorepo-relative
# default), which none does here.
- name: Test
# Fast leg: mocked unit tests only. The real-worker tests (transport, poc,
# conformance corpus, orchestrate tool loop, disconnect) skip themselves
# here β€” no worker is built and bare-rpc isn't installed β€” and run in the
# verified-gated on-pr-sdk-python-e2e.yml instead, which is long (it
# downloads models).
- name: Test (unit)
run: .venv/bin/python3 -m pytest tests/ -v
2 changes: 1 addition & 1 deletion docs/website/content/docs/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { TrackCopy } from '@/components/track-copy'

## Supported environments

QVAC SDK is distributed as the npm package `@qvac/sdk` for JavaScript/TypeScript projects.
QVAC SDK is distributed as the npm package `@qvac/sdk` for JavaScript/TypeScript projects. For Python, install `tetherto-qvac-sdk` from PyPI β€” see the [Python SDK](/python-sdk) guide.

### JS environments

Expand Down
Loading
Loading