Files
python-sdk/examples/hello/__main__.py
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

51 lines
2.1 KiB
Python

"""hello — prove the key works.
``GET /v1/keys`` (operationId ``get_v1_keys``): the caller's own API keys.
This flow's whole job is to FAIL when the key is bad, so the route has to be one
that actually checks, and that is decided by probing api.hanzo.ai rather than by
reading the document. Three identity-shaped routes are disqualified for
answering 200 to a caller carrying no credential at all — ``/v1/ai/account``
(``type="anonymous-user"``), ``/v1/iam/whoami`` and ``/v1/iam/account`` (both
200 ``{"status":"error"}``) — and a ``hello`` built on any of them prints a
cheerful identity for a key that would be refused everywhere else, which is
worse than no check because it reads as proof.
``/v1/keys`` answers 403 ``{"code":"forbidden","error":"sign in to manage API
keys"}`` with no key and with a bogus one, while the nonsense sibling
``/v1/keys-zzq9`` answers 404 — so the 403 is this route refusing, not a
wildcard door swallowing the address.
This replaces ``bot_authMe`` (``GET /v1/bot/auth/me``), which no longer
resolves: cloud relays all of ``/v1/bot`` through one ``app.All("/v1/bot/*")``,
so the document carries ``/v1/bot/{wildcard1}`` and no operation at that
address. The old id existed only in a hand-authored spec and vanished with it —
which is the reason an example's operationId has to come from the served
document.
python -m examples.hello
"""
from hanzoai.cloud import KeysApi
from examples.client import BASE_URL, client, run
def main() -> None:
with client() as api:
listing = KeysApi(api).get_v1_keys()
keys = listing.keys or []
print(f"hello from {BASE_URL}")
print(f" the key is accepted; this org has {len(keys)} API key(s)")
for key in keys[:5]:
# `key.key` is the secret itself and is never printed. The prefix is the
# part that names one without disclosing it.
print(f" {key.prefix or '(no prefix)'} · {key.type or 'untyped'} · created {key.created_at or 'unknown'}")
if len(keys) > 5:
print(f" … and {len(keys) - 5} more")
if __name__ == "__main__":
run(main)