Skip to content
Draft
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
26 changes: 23 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,21 @@ 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. Its PyPI package name is
submit/poll). A separate candidate `client.grid.video_jobs` exposes Core's
default-off `/v1/videos/jobs` credit-backed submission/recovery contract; it
does not change those existing synchronous methods. 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

- **`src/grid_sdk/`** — the package (clients + raw-Grid helpers). Owned in its own AGENTS.md.
- **`tests/`** — pytest suite (`test_client.py`, `test_grid.py`); uses `respx` to mock HTTP.
- **`tests/`** — pytest suite (client, legacy media and durable video jobs); uses `respx` to mock HTTP.
Owned in its own AGENTS.md.
- **`pyproject.toml`** — setuptools build, `src/` layout, deps, pytest config.
- **`qualification/`** - optional Core SQL/auth plus built-JS wire parity checks;
additional actual JS localhost HTTP submission/recovery, synthetic
storage/workers only, explicitly selected dependency environment.
- **`.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
Expand All @@ -69,9 +74,23 @@ submit/poll). The retired horde `/api/v2` async queue is NOT used. Its PyPI pack
recent OpenAI SDK releases may install `httpx2` instead of the `httpx` import used here.
Do not add dependencies casually — thinness is the product.
- **Public API is `__init__.__all__`:** `Grid`, `AsyncGrid`, `AIPG`, `AsyncAIPG`,
`DEFAULT_BASE_URL`. `AIPG`/`AsyncAIPG` are aliases of `Grid`/`AsyncGrid` and must stay so.
`DEFAULT_BASE_URL`, `VideoJobs`, `AsyncVideoJobs`, `VideoJob`, `VideoJobError`,
`VideoMedia`, `VideoResult`. `AIPG`/`AsyncAIPG` are aliases of `Grid`/`AsyncGrid` and must stay so.
Candidate payment exports add `VideoAuthorizationRequest`,
`SignedVideoAuthorization`, `VideoPaymentError`, `prepare_video_authorization`,
`sign_video_authorization` and `async_sign_video_authorization`.
- **Keep `__version__` (in `__init__.py`) in sync with `version` in `pyproject.toml`.**
- Package metadata, source SPDX headers, and `LICENSE` all use MIT.
- Candidate video helpers are independently implemented MIT clients for the
wire protocol, not copies of AGPL Core/Director implementation. They never
retry POST, download media, hold funds, or claim a closed job was refunded.
Payment helpers independently implement the public quote/EIP-712 wire contract
without adding dependencies. They perform no HTTP and never take keys or
send; actual preview quotes refuse signer callbacks. A returned signature
is not funding, account/contract verification or a payment rail.
Fresh signing requires binding-v3 upfront/EIP-3009 terms plus explicit
`accept_upfront_payment=True`; default/false refuses callbacks even when
enabled. Earlier quotes are historical-only. Keep this policy aligned with JS.
- **Release identity:** publish only a non-prerelease GitHub Release whose
`v<version>` tag exactly matches `pyproject.toml` and `__version__`. The
release workflow imports the wheel with runtime dependencies only, then runs
Expand All @@ -93,3 +112,4 @@ submit/poll). The retired horde `/api/v2` async queue is NOT used. Its PyPI pack

- [src/grid_sdk/AGENTS.md](src/grid_sdk/AGENTS.md) — the SDK package: clients + raw-Grid access.
- [tests/AGENTS.md](tests/AGENTS.md) — mocked Python SDK contract tests.
- [qualification/AGENTS.md](qualification/AGENTS.md) - cross-repo video jobs qualification.
140 changes: 138 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,121 @@ result = client.grid.generate({"prompt": "...", "models": [...], "params": {...}
```

`client.grid` waits for the synchronous Grid response and returns the finished
OpenAI-shaped result. Use `timeout` for long media jobs; there is no SDK
submit/poll mode on this `/v1` client.
OpenAI-shaped result. Use `timeout` for long media jobs. These existing calls
do not use the separate video-jobs lifecycle below.

## Candidate Durable Video Jobs

This branch's `0.2.0a3` adds `client.grid.video_jobs` (and its `AsyncGrid`
twin). It requires the candidate Core `/v1/videos/jobs` contract, which is
default-off and **not a public production feature yet**. Installing this SDK
does not enable admission, qualify a worker or authorize spending.

```python
from uuid import uuid4

jobs = client.grid.video_jobs
key = str(uuid4())
body = {
"model": "LTX-2.5 Distilled BF16",
"prompt": "A person waves from inside a parked car",
"frames": 33,
"fps": 24,
"max_cost_micro_usd": 50_000, # $0.05 ceiling, not a quote
# Audio-to-video: reference_audio="data:audio/wav;base64,..."
# Video-to-video: also reference_video="data:video/mp4;base64,..."
# Video reference requires an explicit size, e.g. size="768x512".
}
# Durably save key + immutable body in your application's private journal FIRST.
# Do not generate a fresh key when recovering this operation.
receipt = jobs.submit(body, idempotency_key=key)
# Save the accepted receipt before waiting or downloading any result.
terminal = jobs.wait(receipt, timeout=600)
```

`submit` checks `/health` contract features without credentials, then sends
**one POST**. Core still owns worker availability, recipe/input validation,
live pricing and the atomic credit hold. There is no model fallback, automatic
POST retry, asset download or client-side billing counter. Costs are integer
micro-USD, limited to `2**53 - 1` for exact parity with JavaScript.

If the reply is lost, malformed, cancelled or fails after POST begins, preserve
the saved key and use `jobs.recover(idempotency_key=key)`. Once you have a job
ID, use `jobs.recover(job_id=receipt["job_id"])` or `jobs.wait(receipt)`.
Recovery is **GET-only**, does not re-upload sources, and bypasses capability
preflight so accepted jobs remain recoverable after new admission is disabled.
404/409/5xx are surfaced; none triggers another submission. A `VideoJobError`
reports sanitized `kind`, `http_status`, and `submission_uncertain`; HTTP errors
after POST are conservatively uncertain, even if Core probably rejected them.
Cancellation also requires recovery; it does not prove the job was cancelled.

`wait` returns `completed` or `closed_without_result`, validating stable job,
model, request hash, held amount and ceiling on every poll. The latter means
no recoverable output, **not proof of a refund**. Timeout does not release a
hold or start another render. The returned hashes are metadata commitments;
the SDK does not download, hash or quality-check the output.

For delegated service calls, pass `user_token=...` to submit/recover/wait;
it travels only in `X-Grid-User-Token`, never a URL. Keep service credentials
server-side. The SDK refuses redirects on the new authenticated paths.

Source inputs must be inline, server-validated WAV/MP4, not arbitrary remote
URLs. Audio uses stereo PCM16 WAV at 48 kHz, exactly `frames * 2000` samples
at 24 fps. Video uses frame-matched 24-fps H.264 MP4 plus a separate WAV and
explicit output size. Current LTX frame/recipe gates remain authoritative.
An `image` or timed `keyframes` pin constrains actual frames: **it is not an
appearance-only identity reference**.

The separate, unpriced **`LTX-2.5 Dev BF16 Ingredients`** candidate accepts
`reference_image` as a 768x448 PNG, exact-duration `reference_audio`, and optional
frame-matched `reference_video`, at 121 frames/24 fps, 40 steps and CFG 2.
Use explicit `size="768x448"` and no `image`/`keyframes`. `reference_strength`
defaults to 1; with video, `guide_strength` defaults to 0.5 and
`motion_attention_strength` to 0.25. Optional `reference_attention_strength`
is independently bounded to (0, 1]; omission preserves the original appearance
guide, rather than silently choosing a new default. Core validates these controls and decodes
every source. The helper forwards them; it never executes an adapter or turns
the PNG into frame zero. Source-health features do not mean this recipe is
admitted, priced or visually qualified. Private native test-credit lifecycle
evidence exists; public/purchased-credit acceptance, measured quality and
model-use clearance remain gates. x402 video payments are a proposal, not an
implemented SDK payment rail.

## Candidate Video Wallet Preparation

`prepare_video_authorization` checks a trusted, authenticated Core quote against
your independently retained model, normalized intent hash, reviewed recipient,
network and exact spending ceiling. It returns unsigned EIP-712 data without
keys, HTTP or dependencies.
New binding-v3 quotes must disclose upfront settlement and EIP-3009. Earlier
quotes are historical-only, not fresh wallet requests. Upfront means payment
before rendering; explicitly accept it only after reviewing that policy.

```python
from grid_sdk import prepare_video_authorization

request = prepare_video_authorization(
quote,
payer=wallet_address,
expected_model=model,
expected_request_hash=saved_intent_hash,
expected_recipient=reviewed_recipient,
network="eip155:84532",
max_cost_micro_usd=100_000,
accept_upfront_payment=True, # Explicitly accept payment before rendering.
)
```

Quotes are private/default-off and return `payment_enabled: false`.
`sign_video_authorization` and its async twin block those before invoking a
wallet callback. Do not flip that field yourself or prompt a real wallet for
this candidate. Enabled quotes still refuse signing unless upfront acceptance
was explicitly true; omitted/false policies never invoke the wallet. No payment
endpoint or sender exists. Future enabled callbacks
sign only, with expiry checked afterwards; Core must verify signatures, live
accounts/tokens, nonce claims and actual funds. Never log or persist
`SignedVideoAuthorization.payload` as ordinary evidence. A signed authorization
is not a completed payment or generation.

## It's just OpenAI underneath

Expand All @@ -114,6 +227,29 @@ tool calling, structured output, the full `.chat`/`.images`/`.models` surface
works here too. You can also point existing OpenAI code at the Grid by setting
`base_url="https://api.aipowergrid.io/v1"` if you'd rather not switch packages.

## Candidate Qualification

Unit tests mock all HTTP (`pytest tests`). Optional cross-repo tests exercise
Core's real scoped authentication and SQL holds, but synthetic storage/workers,
including accepted-reply loss and recovery after admission rollback. Build the
JS peer first, then run from this SDK checkout with Core's locked test Python:

```bash
PYTHONPATH=/absolute/path/to/candidate-core \
AIPG_JS_VIDEO_QUALIFICATION=/absolute/path/to/js-peer/qualification/core-wire.mjs \
/absolute/path/to/core-test-python -m pytest qualification/test_core_video_jobs.py
```

Use an isolated scratch `CREDITS_TEST_DB_URL` for PostgreSQL; otherwise its
cases skip and only SQLite is proved. Source decoder skips are not successful
qualification. This harness does not run GPUs, use R2 or spend live credits.
The JavaScript transport cases use unmocked fetch against a temporary IPv4
loopback Core server, with fixture credentials supplied on stdin and no Core
lifespan/background tasks. They require one actual POST, GET-only request and
terminal recovery, plus real 401/403/404 ownership/scope checks. Local socket
permission is required. Reply loss is a post-commit 503, not a network-outage
test; the synthetic completion does not qualify actual video output.

## Config

| | |
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "grid-sdk"
version = "0.1.2"
version = "0.2.0a3"
description = "Python SDK for AI Power Grid open-model, OpenAI-compatible inference."
readme = "README.md"
requires-python = ">=3.9"
Expand All @@ -30,3 +30,4 @@ where = ["src"]

[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
57 changes: 57 additions & 0 deletions qualification/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# SDK/Core Qualification

## Purpose

Optional cross-repo checks against an explicitly selected Core checkout and
built JavaScript SDK. Not part of runtime packages or ordinary SDK unit tests.

## Contracts

- Run with Core's locked Python dependencies and `PYTHONPATH` pointing to the
candidate Core root. The normal SDK test environment cannot run these checks.
- Import Core's scoped-auth/SQL fixtures, not production credentials or URLs.
Storage, workers, quotes and output bytes are synthetic; SQL/auth are real.
- `AIPG_JS_VIDEO_QUALIFICATION` names the JS peer's `qualification/core-wire.mjs`.
Build that peer first. Missing peer configuration fails instead of silently
skipping its parity check.
- Exercise generated/audio/video modes with/without lost accepted replies;
require one job/hold/payout and one submission per SDK. Recovery after
admission rollback must be GET-only.
- Also exercise separate Dev Ingredients audio/appearance and motion/appearance
profiles, with reference attention omitted or explicit, using decoded
synthetic PNG/WAV/MP4 and an explicit isolated fixture tariff. Require the
appearance worker feature, no opening pin, distinct private bindings and
100,834-micro hold/charge. This does not price/admit the real candidate or
qualify native GPU/R2/identity, even when the SQL proof runs on Postgres.
- Real JavaScript transport cases additionally serve the isolated router on an
ephemeral IPv4 loopback socket and run the peer's `core-http.mjs`, without
mocking fetch. Supply fixture credentials over stdin only and clear the child
environment except PATH. These include both appearance modes/attention
choices, not just the ordinary source protocol. Require unauthenticated
health, one actual POST, the committed guide/control, owner/key GET recovery,
and real 401/403/404 isolation after settlement and
admission rollback. Stop the child and local server in every terminal path.
Lost replies here are a post-commit 503, not a physical network outage.
- Use real source decoders for synthetic MP4/WAV. Missing FFmpeg skips are not
successful media qualification. PostgreSQL runs require an isolated scratch
`CREDITS_TEST_DB_URL`; never use a live Grid DB.

## Verification

Run `pytest qualification/test_core_video_jobs.py` with those dependencies and
paths. These checks do not qualify GPU output, funded R2 lifecycle or production.

`test_core_video_payments.py` uses real scoped quote HTTP/SQL, decoded
generated/audio/video fixtures and the standalone MIT payment module. Set
`AIPG_JS_VIDEO_PAYMENTS_QUALIFICATION` to the built peer's
`qualification/video-payments.mjs`; optionally choose its Node executable with
`AIPG_VIDEO_QUALIFICATION_NODE`. Missing peer configuration fails. Both clients
must block signing the actual preview quote; only a local future-enabled copy
exercises new unfunded signatures and Core's actual verifier. Private two-phase
IPC never carries keys/credentials; synthetic signatures are transient private
data, not logged evidence. Terminate children on every path and require
unchanged balance plus zero holds/jobs/ledgers.
Both SDKs must refuse callbacks on an enabled test copy when upfront consent is
omitted or false, then sign only with explicit acceptance of binding-v3 terms.
This is protocol/crypto proof, not wallet UI, live contract/facilitator, receipts,
replay exclusion or funding.
Loading
Loading