|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +Guidance for agentic coding tools working in this repository. |
| 4 | +Scope: entire repo. |
| 5 | + |
| 6 | +`CLAUDE.md` is a symlink to this file. Always edit `AGENTS.md` directly; never modify `CLAUDE.md`. |
| 7 | + |
| 8 | +## Project Snapshot |
| 9 | +- Project: `otari` (PyPI package name `otari`), the **Python client SDK** for the otari |
| 10 | + gateway / platform. `OtariClient` (sync) and `AsyncOtariClient` (async) talk to a running |
| 11 | + gateway over HTTP. |
| 12 | +- Language/runtime: Python 3.11+ (CI matrix: 3.11, 3.12, 3.13). |
| 13 | +- Package manager + task runner: `uv`. |
| 14 | +- Source root: `src/otari` (imported as `otari`). |
| 15 | +- Tests: `tests/unit` (mocked, offline) and `tests/integration` (real gateway). |
| 16 | + |
| 17 | +## Architecture (Big Picture) |
| 18 | +This SDK is a **thin, hand-written shell over an OpenAPI-generated typed core**. Read these |
| 19 | +together before changing request behavior. |
| 20 | + |
| 21 | +- **Generated core** (`src/otari/_client/`): produced by OpenAPI Generator from the gateway's |
| 22 | + OpenAPI spec. It is **generated, not hand-edited.** Regeneration happens upstream in the |
| 23 | + gateway repo (`.github/workflows/gateway-sdk-codegen.yml`), which opens a |
| 24 | + `sdk-codegen/client-core` PR here. The core is **excluded from ruff and mypy** |
| 25 | + (`pyproject.toml`: `extend-exclude` / mypy `exclude`). Do not edit it to fix a lint error; |
| 26 | + fix the shell or the upstream spec/generator instead. |
| 27 | +- **Hand-written shell** (everything else under `src/otari/`): |
| 28 | + - `client.py` / `async_client.py`: ergonomic `OtariClient` / `AsyncOtariClient` with |
| 29 | + `completion`, `response`, `message`, `embedding`, `moderation`, `rerank`, `list_models`, |
| 30 | + batch operations, and a `control_plane` accessor. |
| 31 | + - `_base.py`: shared logic: auth-mode resolution, default headers, URL normalization, and |
| 32 | + the single seam where generated `ApiException` is caught and mapped to typed errors. |
| 33 | + - `_streaming.py`: hand-written SSE shim. The generated core buffers and **cannot stream**, |
| 34 | + so streaming endpoints use raw `httpx` + a line/event parser. Chat streaming yields typed |
| 35 | + `ChatCompletionChunk`; responses/messages streaming yields raw event `dict`s (no chunk |
| 36 | + model exists for those). |
| 37 | + - `errors.py`: typed error hierarchy (`OtariError` base + subclasses). |
| 38 | + - `types.py`: re-exports of generated models plus hand-written TypedDicts (batch/options). |
| 39 | + - `control_plane.py`: wrapper over the management endpoints (keys/users/budgets/pricing/usage). |
| 40 | + |
| 41 | +### Two auth modes (must both keep working) |
| 42 | +Resolved in `_base.py` from constructor args, then environment: |
| 43 | +- **Platform** (`OTARI_AI_TOKEN` / `platform_token`): `Authorization: Bearer <token>`, base URL |
| 44 | + defaults to `https://api.otari.ai`. |
| 45 | +- **Self-hosted** (`api_key` + `api_base`, env `GATEWAY_API_KEY` / `GATEWAY_API_BASE`): |
| 46 | + `Otari-Key` header; `api_base` is **required** in this mode. |
| 47 | +Error mapping applies in both modes; do not regress one when changing the other. |
| 48 | + |
| 49 | +### Endpoint-coverage drift gate |
| 50 | +`tests/unit/test_endpoint_coverage.py` fetches the canonical gateway spec |
| 51 | +(`https://raw.githubusercontent.com/mozilla-ai/otari/main/docs/public/openapi.json`) and |
| 52 | +asserts every gateway endpoint is accounted for in `sdk-endpoints.txt` (`[covered]` or |
| 53 | +`[excluded]` with a reason). A new gateway endpoint with no wrapper and no explicit exclusion |
| 54 | +fails CI. When you add or intentionally skip an endpoint, update `sdk-endpoints.txt`. |
| 55 | + |
| 56 | +## Setup Commands |
| 57 | +- Install (dev): `uv sync --extra dev` |
| 58 | + |
| 59 | +## Test Commands |
| 60 | +- Full suite: `uv run pytest` |
| 61 | +- Unit only: `uv run pytest tests/unit` |
| 62 | +- Single test: `uv run pytest tests/unit/test_client.py::TestOtariClient::test_completion -v` |
| 63 | +- Drift gate (needs network): `uv run pytest tests/unit/test_endpoint_coverage.py -v` |
| 64 | +- Integration tests under `tests/integration/` spawn / require a real gateway and are skipped |
| 65 | + when one is not available. |
| 66 | + |
| 67 | +## Lint / Typecheck / Build Commands |
| 68 | +- Lint: `uv run ruff check .` |
| 69 | +- Typecheck (mypy strict): `uv run mypy src/` |
| 70 | +- Build: `uv build` |
| 71 | + |
| 72 | +## Repository Conventions |
| 73 | +- `from __future__ import annotations` at the top of modules; `TYPE_CHECKING` for type-only |
| 74 | + imports; import `Callable`/`Iterator` from `collections.abc`. |
| 75 | +- mypy is `strict`; the generated `otari._client` is excluded. New/changed shell code must be |
| 76 | + fully typed. Use `@overload` for streaming polymorphism (`stream=True` vs not), as `client.py` |
| 77 | + already does. |
| 78 | +- Public API is exported from `src/otari/__init__.py` (clients, errors, types); don't remove or |
| 79 | + rename exports without auditing callers. |
| 80 | +- Unit tests mock at the transport seam (the generated core's REST client) and use `respx` for |
| 81 | + the raw-`httpx` streaming path. Test classes are `Test<Feature>`. |
| 82 | + |
| 83 | +## Change Validation Checklist |
| 84 | +- Touched request handling, auth, or errors → run `tests/unit` and confirm both auth modes still |
| 85 | + map errors correctly. |
| 86 | +- Touched streaming → run the streaming tests; verify chat yields `ChatCompletionChunk` and |
| 87 | + responses/messages yield raw dicts. |
| 88 | +- Added/removed an endpoint wrapper → update `sdk-endpoints.txt` and run the drift gate. |
| 89 | +- Always run `uv run ruff check .` and `uv run mypy src/` before opening a PR. |
| 90 | + |
| 91 | +## Writing style |
| 92 | + |
| 93 | +- Avoid em dashes and double hyphens (`--`) used as separators in prose |
| 94 | + (README, docs, doc comments, commit messages, PR descriptions). Use commas, |
| 95 | + semicolons, colons, parentheses, or periods, or rephrase. This does not apply |
| 96 | + to code (for example CLI flags like `--all`) or en-dash numeric ranges like `3–4`. |
| 97 | + |
| 98 | +## Notes for Agents |
| 99 | +- Never hand-edit `src/otari/_client/`; it is regenerated from the gateway spec. |
| 100 | +- Prefer minimal, targeted edits; match existing typing and import style in touched files. |
| 101 | +- Preserve security-relevant behavior (header/auth handling, error-detail boundaries). |
0 commit comments