diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..9a3d21a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,142 @@ +# AGENTS.md + +Notes for coding agents working in this repo, and for agents writing code +against the Sonilo Python packages. + +## What this repo is + +Three PyPI packages in one repo: + +| Package | Path | What it is | +|---|---|---| +| `sonilo` | `.` (`src/sonilo`) | Python client for the Sonilo API. Sync (`Sonilo`) and async (`AsyncSonilo`) clients, Python 3.9+, only depends on `httpx` | +| `sonilo-cli` | `sonilo-cli/` | `sonilo` command line tool, built on `sonilo` | +| `sonilo-video-kit` | `sonilo-video-kit/` | Helpers that generate a soundtrack, duck it under speech and mix it into a video with local ffmpeg | + +The TypeScript counterparts live in +[sonilo-js](https://github.com/sonilo-ai/sonilo-js). + +## Layout + +``` +src/sonilo/_client.py Sonilo: auth, base URL, timeout, transport +src/sonilo/_async_client.py AsyncSonilo, same surface with async methods +src/sonilo/resources/*.py one module per endpoint (text_to_music, video_to_sfx, dubbing, ...) +src/sonilo/resources/tasks.py tasks.get / tasks.wait and the parse_*_result parsers +src/sonilo/errors.py error classes, mapped from HTTP status and the API `code` +src/sonilo/types.py result dataclasses and TypedDicts +src/sonilo/_version.py __version__, sent in X-Sonilo-Client-Version +sonilo-cli/src/sonilo_cli/ __main__.py (every command), login.py, credentials.py +sonilo-video-kit/src/sonilo_video_kit/ generate, duck, mix, loudness, ffmpeg wrappers +context7.json rules Context7 serves to coding assistants (checked by sonilo-cli/tests/test_context7.py) +examples/ runnable scripts +``` + +## Commands + +CI (`.github/workflows/ci.yml`) runs on Python 3.9 and 3.12 with ffmpeg +installed: + +```bash +pip install -e ".[dev]" -e "./sonilo-video-kit[dev]" -e "./sonilo-cli[dev]" +pytest # sonilo +pytest sonilo-video-kit # needs ffmpeg and ffprobe on PATH +pytest sonilo-cli +``` + +Tests mock HTTP with `respx`; none of them call the real API. + +## Making changes + +- Keep `sonilo` on Python 3.9 syntax (`Optional[...]`, `from __future__ import annotations`) + and on `httpx` as its only dependency. +- Every resource has a sync class and an async class in the same module. Change + both, and cover both in tests. +- New endpoint: add a module under `src/sonilo/resources/`, attach it in both + `_client.py` and `_async_client.py`, export new result types from + `src/sonilo/__init__.py`, add tests, document it in `README.md`, then add the + CLI command in `sonilo-cli/src/sonilo_cli/__main__.py` if a user would run it + from a terminal. +- Use the API's field names (`video_url`, `variants_num`, `output_format`) as + the Python keyword arguments. +- Tri-state options (`ducking`, `preserve_speech`) are sent only when not + `None`, so the server default applies when they are left unset. +- Branch on the API's error `code`, never on message text. +- Wrappers pass `client_name` / `client_version` (`cli-python`, + `videokit-python`) so their traffic is reported in the `X-Sonilo-Client` + header; direct SDK use leaves them unset. +- `context7.json` rules are limited to 255 characters each and 50 in total; + `sonilo-cli/tests/test_context7.py` enforces it. + +## Releases + +Each package publishes to PyPI through OIDC Trusted Publishing when its tag is +pushed. The workflow fails if the tag does not match the version in the +package's `pyproject.toml`. + +| Package | Bump | Tag | Workflow | +|---|---|---|---| +| `sonilo` | `pyproject.toml` and `src/sonilo/_version.py` | `vX.Y.Z` | `publish.yml` | +| `sonilo-cli` | `sonilo-cli/pyproject.toml` and `sonilo_cli/__init__.py` | `sonilo-cli-vX.Y.Z` | `publish-cli.yml` | +| `sonilo-video-kit` | `sonilo-video-kit/pyproject.toml` and `sonilo_video_kit/_version.py` | `sonilo-video-kit-vX.Y.Z` | `publish-video-kit.yml` | + +`sonilo-cli` pins `sonilo>=X.Y,