Files
python-sdk/LLM.md
T
zeekayandHanzo Dev 3a09eb4319 regenerate from cloud@5126939 — the client leaves the hand-merged lineage
pkg/hanzoai/cloud goes +2186 -2126 ~175: 1700 paths / 2354 operations / 2186
schemas, 182 api modules and 2172 model modules, against the 239/1116 it carried
from hanzo.yaml. The document is hanzoai/cloud's own emission now, pinned in a
new .spec-lock by commit and sha256, and generate.py reaches it by value —
--skip-validate-spec means the 1012 missing-`responses` errors no longer write
zero files, so a client can be cut from the authority instead of a projection of
it.

Two renamings come with that, and both are the fix rather than the damage. IAM's
types are namespace-qualified (iam.Role, iam.Application, 95 of them) because a
bare `Role` was two unrelated shapes wearing one name. And the <svc>_ prefix is
gone from every operationId, so every generated method lost it.

The prefix is what broke the examples, and the gate saw only two thirds of it.
Four flows failed on their imports; `money` and `tools` PASSED while every call
in them named a method that no longer existed — an import resolves the names in
the `from … import` line and a method is looked up at call time. All five flows
now name operations that exist, checked by resolving each one as an attribute,
and hanzo.yml records that ceiling so the next reader does not trust the gate
for more than it says.

`chat` is removed, which is a measurement and not a preference: cloud declares
POST /v1/chat/completions with no requestBody and no responses at all, so the
generated method takes no body and returns None — the one call a chat example
exists to make cannot be expressed. Inventing the type, or hand-rolling the HTTP
inside a generated client, is the drift these SDKs exist to prevent; js-sdk
dropped its own chat flow at 2.0.7 for exactly this. It returns the release
cloud gives that route a body.

Two smoke assertions were already red before this regeneration and are now true
again: they pinned AIApi/APIKeysApi/MCPApi and AdminApi.plugin_admin_*, names
from the retired lineage. The surface is all still there under AiApi/KeysApi/
McpApi and AdminApi.admin_plugins.

tests/test_zap_transport.py still errors, exactly as it did before: it imports
`Hanzo` from hanzoai, which the hand-written package does not define. That is
not this document's business and is untouched.

Minor rather than patch: every generated method changed name.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-05 11:00:03 -07:00

157 lines
8.9 KiB
Markdown

# LLM.md — hanzoai/python-sdk
**What this is:** the flagship, most-complete Hanzo SDK — a `uv` workspace of 60+
packages: the typed cloud client (`hanzoai`), agents, MCP server + tools, memory,
distributed compute, and the `hanzo` CLI. `pip install hanzo`.
**Canonical role (one-way SDK model):** Hanzo ships two SDK lines per language —
(1) the full cloud SDK generated from OpenAPI, (2) the AI/agents library. This repo
is the Python flagship of line 2, the reference for every other language.
Completeness: Python → Rust → C++ → Go. One impl, one place; discovery repos link
OUT, never duplicate. Full spec: `~/work/hanzo/SDK-ARCHITECTURE.md`.
## Install / run
```bash
pip install hanzoai # typed cloud client
pip install hanzo # agents + MCP + orchestration helpers
uv sync --all-packages # dev: whole workspace
uv run pytest tests/ -v # tests
```
## Console-script law — only the native binary is called `hanzo`
The `hanzo` command is the Rust CLI (`curl -fsSL https://hanzo.sh | sh`). No
package in this workspace may declare a `hanzo` console script.
Both `hanzo` and `hanzo-cli` used to, and `hanzo` depends on `hanzo-cli`, so
`pip install hanzo` installed two distributions fighting over one name — whichever
landed last won. Measured on a clean venv at 0.4.3: `hanzo --help` printed
*hanzo-cli's* program (bot/deploy/iam/k8s/kms/login/logout/paas/s3/whoami), not
the one `pkg/hanzo/README.md` documented. And if the native CLI was already on
PATH, `hanzo login` meant one of three different things depending on install
order — the real CLI reads a bare `hanzo login` as an AI task, since the verb
there is `hanzo auth login`.
Now: `hanzo` ships `hanzo-py`, `hanzo-cli` ships `hanzo-cli`. Script named after
its distribution, one canonical `hanzo`.
`hanzo-node` on PyPI is also **not** the `hanzo-node` command. That command is a
symlink to the Hanzo CLI, installed by hanzo.sh. The PyPI package fetches a
different Rust binary from `hanzoai/node` — a private repo, so its release assets
404 for anyone outside the org. Both READMEs say so rather than implying one
product.
## Brand rules (hard — enforce in all docs)
- Never "LLM gateway"; never position against LiteLLM. Hanzo is a full AI SDK / AI
cloud, not a proxy.
- Zen models are our own family — never name upstream models.
- Paths are `/v1/…`, never `/api/…`. Base host: `https://api.hanzo.ai`.
- Voice: "Hanzo — the Open AI Cloud." Crisp, developer-first, no emoji-spam.
## Codegen — this repo PULLS, it never pushes
```
hanzoai/cloud emits its own router spec -> cloud/openapi.yaml [the ONE SDK input]
hanzoai/openapi generate.py + sdks.yaml -> the invocation, as data
this repo owns its test + bump + release, and pins what it projected
```
The client is a projection of **cloud's document directly**, and `.spec-lock`
names the commit and sha256 it was cut from. `generate.py` passes
`--skip-validate-spec`, so the 1012 missing-`responses` errors that once made
cloud's emission write zero files no longer stop it; `hanzo.yaml` is out of this
SDK's path (it still feeds the doc site and the skills plane). Regenerate with
the document by value:
```bash
cd ~/work/hanzo/openapi && uv run --with pyyaml python3 generate.py python \
--repo ~/work/hanzo/python-sdk --spec ~/work/hanzo/cloud/openapi.yaml
```
Current `pkg/hanzoai/cloud/` is **1700 paths / 2354 operations / 2186 schemas** →
182 api modules + 2172 model modules.
**Two renamings arrived with the lineage, and neither is a defect to undo.**
IAM's types are namespace-qualified — `iam.Role`, `iam.Application`, 95 of them
— because a bare `Role` had been two unrelated shapes under one name (IAM's
14-property role, and a 2-property `{role, user}` row from another service).
Both exist now and each says which it is. And the `<svc>_` operationId prefix is
gone, so every method lost it: `cloud_get_v1_tools` → `get_v1_tools`,
`AIApi`/`APIKeysApi`/`MCPApi` → `AiApi`/`KeysApi`/`McpApi`,
`AdminApi.plugin_admin_plugins` → `admin_plugins`.
`pkg/hanzoai/cloud/` is **generated — never hand-edit it.** `generate.py` does
`rmtree(dst) + copytree(src)`, so anything written there dies on the next run. Regenerate
only from hanzoai/openapi (`python3 generate.py python`) — never from here. The old
`scripts/generate.sh` was a second, destructive driver (`rm -rf pkg/hanzoai`, which would
have eaten the hand-written `config/mcp/protocols/session/zap` modules); it is deleted.
Consumed at 3.1.3: cloud `8143fc0e`, openapi `2861089`. Regenerate whenever either moves —
openapi `3300cda` dropped `{org}` from the KMS secrets routes (`/v1/kms/orgs/{org}/secrets`
-> `/v1/kms/secrets`; the org is read from the token), which silently stranded 3.1.2 on a
path the server no longer serves.
**The case-variant tag defect is CLOSED.** `hanzo.yaml` used to carry 23 tag groups
differing only by case (`AI`/`ai`, `Users`/`users`, …); openapi-generator mapped both
spellings onto one module and 127 of the 411 operations in those groups never reached the
client. Fixed upstream in the per-service specs, as that note predicted. Verified on the
current spec: **239 distinct tags → 239 api modules, 1:1**, so nothing collapses, and
`generate.py python --check` reports `[python] clean` with no local strip of any kind.
**Two spec defects were found and fixed upstream while regenerating at 3.1.5.** Neither was
patched here; both are in `hanzoai/openapi` main:
- `fc0c17a` — 35 `/v1/platform` operations carried no `responses`. OAS 3.x requires it and
openapi-generator aborts the entire document, so `hanzo.yaml` was producing no client in
*any* language, not just Python.
- `07783f5` — `ChatCompletionResponse.choices` was `items: {type: object}`, so
`choices[0].message.content` was `List[object]` and unusable without a cast. Now an
`ai_ChatChoice` schema.
The rule holds in both directions: a generated tree is never hand-repaired, and a defect
found by generating is fixed in the spec, where every other language gets the fix too.
## Examples — the six canonical flows
`examples/{hello,chat,money,store,agent,tools}`, one directory each, plus
`examples/client.py` as the single place a base URL or an env var is resolved. The same six
exist in every Hanzo SDK. Run one with `python -m examples.hello` from the repo root.
Each flow's call sits behind `if __name__ == "__main__":` on purpose. That is what lets CI
*import* all six to prove every `from hanzoai.cloud import X` still resolves, without an API
key and without opening a socket — so a spec change that renames or drops an operation goes
red in the gate instead of in a user's app.
They are a gate, not decoration. The TypeScript twin of the `chat` flow is what surfaced the
`choices` defect above: the generated tree imported and built perfectly, because building
generated code only proves it is internally consistent. Only calling it proves the surface
is usable.
## CI
Fleet convention, added at 3.1.5: root `hanzo.yml` (the `test:` gate) plus a 7-line
`.github/workflows/cicd.yml` importing `hanzoai/ci`. The gate is two blocks — import every
generated module (for generated code that IS the build step; there is no compiler to catch a
bad `$ref`), then import the six flows. Both provision an interpreter with `uv` when the arc
runner lacks one.
Scope is deliberate: the cloud client and its flows, not all 65 packages. A red gate should
mean "the client the spec just produced is broken", not "something, somewhere".
**Publishing is not here.** `.hanzo/workflows/publish-pypi.yml` on our own runners stays the
canonical path because it reads the PyPI token from KMS like every other publish credential
in the fleet. `hanzo.yml` gates only; a second publish path would be one too many.
## Key entry points
- `pkg/hanzoai/` — typed OpenAPI client (`ApiClient`, `Configuration`, `Ai*Api`). Two
surfaces live here: `pkg/hanzoai/{api,models}` (older, frozen — nothing regenerates it
now) and `pkg/hanzoai/cloud/` (current, spec-driven). New work targets `cloud/`.
- `pkg/hanzo/src/hanzo/cli.py` — the `hanzo` CLI command tree.
- `pkg/hanzo-mcp/` — MCP server; tools via `[project.entry-points."hanzo.tools"]`.
- `pkg/hanzo-tools-*/` — one concern each, exports a `TOOLS` list.
- `pkg/hanzo-{agents,agent,network,memory}/` — agent/compute/memory libraries.
- `pkg/hanzo-kms/` — KMS client. The server is **luxfi/kms** (`kms.hanzo.ai`,
`kms.lux.cloud`) and its whole surface is `/v1/kms/auth/login` plus
`/v1/kms/orgs/{org}/secrets[/{path}/{name}]`. `/api/*` is Infisical's and was
never served — it looked like a decode error rather than a 404 only because old
builds answered every unmatched path with the console SPA (200 text/html).
A secret is (org, path, name, env), one value each — no versions. The server
splits the trailing URL at its LAST slash into (path, name), so escape each
segment individually. `pkg/hanzo-kms/tests/` pins all of it.
**Rules for agents:** update THIS file with significant discoveries; never write
random summary files; keep the README cross-link block intact.