mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
v8.5.500
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0da7a4185c |
fix(six): one word, one body, one shape across the three SDKs
Thirteen places where this SDK and the other two answered the same question
differently. Every one is now what the served handler reads, or what HIP-0044
says where the handler is silent.
Where this SDK was already right, the other two moved: search normalizes the
knowledge kind in both directions, `Page.total` stays the server's own count,
and `put` picks its method from the name. Where it was not:
- kb.Import carries what the handler answers. `/v1/knowledge/import` writes
{format, imported, pages} (knowledge/import.go); this modelled `imported`
alone, so a caller could not see which pages landed.
- Fact carries the server's three. `GET /v1/graph` serves twelve members
(graph/typed.go, wireFact) and no other route hands back an assertion's
content address, so projecting the nine an asserter states made a read lossy
with nothing to recover it from. `id`, `by` and `knowable` arrive read-only
and `write()` never sends them.
- graph.extract and graph.ingest take a Source. Both routes bind the same
{source, subject, text, at} (graph/semantic.go, graphSourceIn), so it is one
value — and the first positional argument is now the same thing here as it is
in the other two.
- import_(format, data, project) — the order the contract writes and the other
two take. It was (export, *, format).
- A body carries what somebody set. `send` dropped None from the query and left
it in the body, so `search.find` posted {"index": null, "offset": null,
"project": null} where the other two omit the key: three requests for one
call, and cloud asked to read a value where there is none. Audit's pageSize
and p go the same way — the route's own defaults are the same 100 and 1, so
an unset term is one the route decides identically, and `all` pins the size it
walks at because it needs to know the page length.
- A write body is field data and nothing else. `url` belongs to a source, and an
empty title is left out so the doctype names the required field that is
missing rather than being handed a blank one.
- kb.list takes order and kb.links takes a project. `/v1/framework/{doctype}`
serves order_by and `/v1/knowledge/graph` serves ?project=.
- policy_denied and entitlement_required do not exist. Neither string appears
anywhere in hanzoai/cloud, so REFUSED is the two codes it does emit. A 403
read as denied on an invented code would hand an unauthenticated caller a cure
for a refusal nobody made.
- One refusal reader, by named fallback: code from `code` else `error`, reason
from `detail` else `message`, cures from `cure`. The gate's own `reason` —
"unpaid", "unresolved" — names the admit leg that failed rather than
explaining anything, so it is dropped, and `title` is not a refusal member.
- Money counts minor units, not cents: hundredths for USD, whole yen for JPY,
thousandths for KWD.
- Balance.reserved, not held. The wire says `holds`; `held` is an arm.
- Vocabulary.rules, because it is a list. The wire member stays `rule`.
- The fourth outcome is Fault, not the generated client's ApiException. It is
the one shared concept whose name a reader could not carry between languages,
and an exception from the generated layer is not this layer's vocabulary.
- A client with no credential builds. It raised at construction, which is a
process that cannot start where the other two are a process whose first call
fails — and the first call is where the credential is actually needed.
HIP-0044 is amended where the code was right and the document was not.
ruff check, ruff format --check and mypy clean on every changed file; 109
passed. The two test_smoke failures are pre-existing (ToolsApi is absent from
the generated tree) and reproduce on a stashed working copy.
Co-authored-by: Hanzo Dev <[email protected]>
|
||
|
|
7824191215 |
feat: budget, policy, audit, search, kb and graph on one credential
Six capabilities hang off the client as first-class methods rather than as generated operations a caller assembles for itself: budget (allowance, wallet, tier, charged ledger), policy (one authorization question), audit (the org's trail, read-only by construction), search (one ranked set over every corpus), kb (the corpus you write and the connectors that fill it), and graph (assertions with provenance and time). The same six words, the same method names and the same answer type as the Go and TypeScript clients. A refused budget and a refused policy are answers. Answer is a union of three frozen types with a discriminant — Ok, Denied, Held — so a caller reads "denied, and here is why" without catching, and reading .value off a refusal raises it rather than handing back a half-answer. One rule maps HTTP to an arm: a 202 whose body says held is a hold, 402 and the four refusal codes on 403 are refusals, and a status that decided nothing raises. Every arm carries the x-request-id, which is the word audit.Event.request uses too — the join that lets a caller find the row the server wrote for the call it just made. Auth is IAM and nothing else. The client exchanges its own clientId and clientSecret for a short-lived access token (client_credentials, client_secret_basic, RFC 8707 resource), holds it until shortly before expiry, and re-mints once on a 401. It no longer accepts a bearer, so HANZO_API_KEY, Configuration(access_token=) construction and result() are gone rather than deprecated: two ways to hold one credential is the defect this removes. as_ still scopes to a subject and now mints from the client's own token. hanzoai resolves its public names on first use, and the capability layer is written over the wire rather than over the generated client, so importing the six costs milliseconds instead of 2,700 generated modules. ruff and ruff format are clean, mypy is clean over the thirteen new and changed modules, and 106 tests pass. The two failures in test_smoke are the ToolsApi name the current document no longer carries, and they fail at HEAD too. The mint was exercised against the live hanzo.id, which answers invalid_client for a credential it does not know; the six were exercised against a stub asserting the exact request and decoding the shapes cloud's own schemas declare. examples/six runs the whole thing as a caller would. Co-authored-by: Hanzo Dev <[email protected]> |