Files
python-sdk/pkg/hanzo-flags
zooqueenandHanzo Dev 20a2178e61 auth: the two key shapes are pk- and sk-, and one sentence says so
Cloud's APIKeyPrefixes admits pk- and sk- only (auth_identity.go, 4b030b77).
The lsp tool still told the user to set an hk- key, which cloud resolves to no
principal — following the message produced a key that could never authenticate.

The message is now a single NO_KEY beside the resolver that mints it, exported
from hanzo_tools.core, so a second tool reporting it cannot spell it differently.
Resolution order is untouched: HANZO_API_KEY, then ~/.hanzo/config.json .apiKey,
then the CLI's IAM session.

vector_index names the split cloud actually draws: pk- publishable for read,
sk- secret for write.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-08 15:04:19 -07:00
..
2026-07-19 20:02:48 -07:00

hanzo-flags

Feature flags + A/B testing for Python, on the Hanzo native flags engine.

The Python client for cloud /v1/flags — the same PostHog-compatible evaluation endpoint the Rust core, Go binding, and @hanzo/flags (TypeScript) all speak. One flag definition, evaluated identically in every language.

Zero runtime dependencies — stdlib urllib only. A flag check must never drag a dependency tree into a service.

Install

pip install hanzo-flags     # or: uv add hanzo-flags

Use

from hanzo_flags import HanzoFlags

flags = HanzoFlags("https://api.hanzo.ai", token="sk-...")
flags.load("user-123", person_properties={"plan": "pro"})

if flags.is_enabled("checkout-exp"):
    ...

variant = flags.variant("pricing-test")   # "control" | "b" | None
payload = flags.payload("pricing-test")   # the flag's JSON payload, or None

Group targeting (orgs/teams):

from hanzo_flags import Group

flags.load(
    "user-123",
    groups={"0": Group(key="acme", properties={"tier": "gold"})},
)

Async (asyncio services):

from hanzo_flags import AsyncHanzoFlags

flags = AsyncHanzoFlags("https://api.hanzo.ai", token="sk-...")
await flags.load("user-123")
if flags.is_enabled("checkout-exp"):
    ...

One-shot:

from hanzo_flags import evaluate
res = evaluate("https://api.hanzo.ai", "user-123", token="sk-...")
res.is_enabled("checkout-exp")

Guarantees

  • Fail-open. A transport or decode error returns the last good (or empty) result with errors_while_computing set — load() never raises on the hot path.
  • Cached by context + TTL (default 15s). Re-evaluating the same context inside the TTL is free, so a hot path may call load() freely.

The family

Language Package Mode
Rust hanzo-flags crate native (the evaluation core)
Go cloud/clients/flags in-process via FFI to the Rust core
TypeScript @hanzo/flags HTTP to /v1/flags (browser + node)
Python hanzo-flags HTTP to /v1/flags (this package)

All four resolve the same flag to the same value: the definitions live once in the cloud flags cockpit (/v1/flags/defs), and every client evaluates against them.