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
77 changes: 77 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Publish PyPI package

on:
release:
types: [published]

permissions:
contents: read

jobs:
publish:
if: ${{ !github.event.release.prerelease }}
runs-on: ubuntu-22.04
timeout-minutes: 15
environment:
name: pypi
url: https://pypi.org/p/grid-sdk
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ github.event.release.tag_name }}
fetch-depth: 0
fetch-tags: true
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
cache: pip
- name: Verify release identity
shell: bash
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
set -euo pipefail
version="$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
module_version="$(python - <<'PY'
import ast
from pathlib import Path

tree = ast.parse(Path("src/grid_sdk/__init__.py").read_text())
for node in tree.body:
if not isinstance(node, ast.Assign):
continue
if any(isinstance(target, ast.Name) and target.id == "__version__" for target in node.targets):
print(ast.literal_eval(node.value))
break
else:
raise SystemExit("__version__ assignment not found")
PY
)"
test "$module_version" = "$version" || {
echo "module version $module_version does not match project version $version" >&2
exit 1
}
test "$RELEASE_TAG" = "v$version" || {
echo "release tag $RELEASE_TAG does not match package version $version" >&2
exit 1
}
test "$(git rev-parse HEAD)" = "$(git rev-list -n 1 "$RELEASE_TAG")"
- name: Build and inspect distributions
run: |
python -m pip install build==1.6.0 twine==7.0.0
python -m build
python -m twine check dist/*
- name: Test built wheel
shell: bash
run: |
wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
test -n "$wheel"
python -m pip install "$wheel" pytest pytest-asyncio respx httpx
pytest tests/ -v
python -c 'import grid_sdk; print(grid_sdk.__version__)'
- name: Publish with PyPI trusted publishing
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
10 changes: 7 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

jobs:
pytest:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
timeout-minutes: 10
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
Expand Down
10 changes: 8 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ subclasses the official `openai` client, pre-points it at the Grid, reads the ke
environment, and adds Grid-specific conveniences (`online_models()`, and `client.grid` for the
native SYNCHRONOUS media endpoints: `/v1/images/generations` (video / img2img / ControlNet /
LoRAs / styles) and `/v1/videos/generations` — the call returns the finished result, no
submit/poll). The retired horde `/api/v2` async queue is NOT used. Published to PyPI as
`grid-sdk`; importable as `grid_sdk`. Co-canonical peer of the JS SDK `../grid-sdk-js`
submit/poll). The retired horde `/api/v2` async queue is NOT used. Its PyPI package name is
`grid-sdk`; it is importable as `grid_sdk`. Co-canonical peer of the JS SDK `../grid-sdk-js`
(npm `grid-ai`) — keep the two SDKs' surfaces aligned.

## Ownership
Expand All @@ -53,6 +53,9 @@ submit/poll). The retired horde `/api/v2` async queue is NOT used. Published to
- **`pyproject.toml`** — setuptools build, `src/` layout, deps, pytest config.
- **`.github/workflows/secret-scan.yml`, `.gitleaks.toml`, and `.gitleaksignore`** —
checksum-verified complete-history secret scanning with exact historical fingerprints only.
- **`.github/workflows/release.yml`** — release-published, tag-bound PyPI
trusted publishing. It uses GitHub OIDC and the protected `pypi`
environment; registry tokens do not belong in GitHub secrets.
- `dist/`, `.venv/`, `*.egg-info/` — build/vendored artifacts; do not edit or document.

## Local Contracts
Expand All @@ -67,6 +70,9 @@ submit/poll). The retired horde `/api/v2` async queue is NOT used. Published to
`DEFAULT_BASE_URL`. `AIPG`/`AsyncAIPG` are aliases of `Grid`/`AsyncGrid` and must stay so.
- **Keep `__version__` (in `__init__.py`) in sync with `version` in `pyproject.toml`.**
- Package metadata, source SPDX headers, and `LICENSE` all use MIT.
- **Release identity:** publish only a non-prerelease GitHub Release whose
`v<version>` tag exactly matches `pyproject.toml` and `__version__`. The
release workflow tests the built wheel before OIDC publication.

## Work Guidance

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Open-model, OpenAI-compatible inference across community-operated GPUs.

> **Release status:** `grid-sdk` is staged for its initial PyPI publication.
> The install command below becomes available when the first verified release
> is published; until then, use the OpenAI SDK with the Grid base URL shown below.

The Grid API speaks the OpenAI protocol, so this SDK is a thin layer over the
official `openai` package: it points at the Grid, reads your key from the
environment, and adds Grid-specific conveniences. Everything you know from the
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ["setuptools>=68.0", "wheel"]
requires = ["setuptools==82.0.1", "wheel==0.48.0"]
build-backend = "setuptools.build_meta"

[project]
Expand All @@ -8,7 +8,8 @@ version = "0.1.1"
description = "Python SDK for AI Power Grid open-model, OpenAI-compatible inference."
readme = "README.md"
requires-python = ">=3.9"
license = { text = "MIT" }
license = "MIT"
license-files = ["LICENSE"]
authors = [{ name = "AI Power Grid" }]
keywords = ["ai", "llm", "inference", "openai", "decentralized", "aipg"]
dependencies = [
Expand Down
Loading