Skip to content

Commit 56c3848

Browse files
njbrakeclaude
andauthored
docs: add AGENTS.md/CLAUDE.md agent guide, refresh README (#6)
Add an AGENTS.md agent guide (CLAUDE.md symlinked to it, matching the otari repo) covering the SDK architecture, both auth modes, the endpoint-coverage drift gate, and the exact uv build/test/lint commands. README: add the 409 BatchNotCompleteError row, correct the stale 'built on the OpenAI Python SDK' claim, retitle the heading to 'Otari Python Client SDK', and rebrand otari-gateway wording to otari. Drop the internal 'Option C' codename from source docstrings/comments. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent c57cb5e commit 56c3848

10 files changed

Lines changed: 243 additions & 112 deletions

File tree

AGENTS.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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).

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AGENTS.md

0 commit comments

Comments
 (0)