mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
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]>
This commit is contained in:
@@ -66,7 +66,7 @@ Each reads `HANZO_API_KEY` from the environment and talks to
|
||||
`https://api.hanzo.ai` unless `HANZO_BASE_URL` says otherwise:
|
||||
|
||||
```bash
|
||||
export HANZO_API_KEY=hk-...
|
||||
export HANZO_API_KEY=sk-...
|
||||
uv run python -m examples.hello
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ def api_key() -> str:
|
||||
"""
|
||||
key = os.environ.get("HANZO_API_KEY")
|
||||
if not key:
|
||||
raise SystemExit("HANZO_API_KEY is not set — export an IAM JWT or an hk- cloud key")
|
||||
raise SystemExit("HANZO_API_KEY is not set — export an IAM JWT or a pk-/sk- cloud key")
|
||||
return key
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ pip install hanzo-flags # or: uv add hanzo-flags
|
||||
```python
|
||||
from hanzo_flags import HanzoFlags
|
||||
|
||||
flags = HanzoFlags("https://api.hanzo.ai", token="hk-...")
|
||||
flags = HanzoFlags("https://api.hanzo.ai", token="sk-...")
|
||||
flags.load("user-123", person_properties={"plan": "pro"})
|
||||
|
||||
if flags.is_enabled("checkout-exp"):
|
||||
@@ -46,7 +46,7 @@ Async (asyncio services):
|
||||
```python
|
||||
from hanzo_flags import AsyncHanzoFlags
|
||||
|
||||
flags = AsyncHanzoFlags("https://api.hanzo.ai", token="hk-...")
|
||||
flags = AsyncHanzoFlags("https://api.hanzo.ai", token="sk-...")
|
||||
await flags.load("user-123")
|
||||
if flags.is_enabled("checkout-exp"):
|
||||
...
|
||||
@@ -56,7 +56,7 @@ One-shot:
|
||||
|
||||
```python
|
||||
from hanzo_flags import evaluate
|
||||
res = evaluate("https://api.hanzo.ai", "user-123", token="hk-...")
|
||||
res = evaluate("https://api.hanzo.ai", "user-123", token="sk-...")
|
||||
res.is_enabled("checkout-exp")
|
||||
```
|
||||
|
||||
|
||||
@@ -25,7 +25,13 @@ from pathlib import Path
|
||||
from dataclasses import field, dataclass
|
||||
from urllib.parse import unquote, urlparse
|
||||
|
||||
from hanzo_tools.core import BaseTool, CloudError, HanzoCloud, MCPResourceDocument
|
||||
from hanzo_tools.core import (
|
||||
NO_KEY,
|
||||
BaseTool,
|
||||
CloudError,
|
||||
HanzoCloud,
|
||||
MCPResourceDocument,
|
||||
)
|
||||
|
||||
# LSP server configurations
|
||||
LSP_SERVERS = {
|
||||
@@ -633,7 +639,7 @@ class LSPTool(BaseTool):
|
||||
return {
|
||||
"action": action,
|
||||
"repo": repo,
|
||||
"error": "no hk- key: set HANZO_API_KEY or ~/.hanzo/config.json .apiKey",
|
||||
"error": NO_KEY,
|
||||
}
|
||||
|
||||
# The wire speaks LSP's own frame: 0-based line, 0-based UTF-16
|
||||
|
||||
@@ -11,7 +11,7 @@ import json
|
||||
import asyncio
|
||||
|
||||
from hanzo_tools.lsp import TOOLS, LSPTool
|
||||
from hanzo_tools.core import HanzoCloud
|
||||
from hanzo_tools.core import NO_KEY, HanzoCloud
|
||||
from hanzo_tools.lsp.lsp_tool import CLOUD_OPS, LOCAL_ACTIONS
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class FakeClient:
|
||||
return FakeResponse(self.reply, self.status)
|
||||
|
||||
|
||||
def tool_over(reply=None, key="hk-test", status=200):
|
||||
def tool_over(reply=None, key="sk-test", status=200):
|
||||
"""An LSPTool whose cloud client answers from a canned transport."""
|
||||
tool = LSPTool()
|
||||
cloud = HanzoCloud(key=key)
|
||||
@@ -153,7 +153,7 @@ def test_upstream_failure_is_reported_not_raised():
|
||||
def test_without_a_key_nothing_is_sent():
|
||||
tool, client = tool_over(key="")
|
||||
out = _run(tool.run(action="hover", repo="hanzoai/mcp", file="a.py", line=1))
|
||||
assert "hk-" in out.data["error"]
|
||||
assert out.data["error"] == NO_KEY
|
||||
assert client.calls == []
|
||||
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ class FakeStreamClient:
|
||||
|
||||
def cloud_over(lines, status=200, body=""):
|
||||
"""A real HanzoCloud whose transport replays `lines`."""
|
||||
cloud = HanzoCloud(key="hk-test")
|
||||
cloud = HanzoCloud(key="sk-test")
|
||||
client = FakeStreamClient(FakeSSEResponse(lines, status=status, body=body))
|
||||
|
||||
async def fake_get_client():
|
||||
|
||||
@@ -5,7 +5,7 @@ Uses the Hanzo Cloud RAG pipeline:
|
||||
- POST /v1/chat-docs — RAG chat with component context
|
||||
- POST /v1/index-docs — index components into the search backend
|
||||
|
||||
Authentication: publishable key (pk-*) for read, API key (hk-*) for write.
|
||||
Authentication: publishable key (pk-) for read, secret key (sk-) for write.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
@@ -30,6 +30,7 @@ from hanzo_tools.core.unified import (
|
||||
)
|
||||
from hanzo_tools.core.id_tool import IdTool, id_tool
|
||||
from hanzo_tools.core.cloud import (
|
||||
NO_KEY,
|
||||
HanzoCloud,
|
||||
CloudError,
|
||||
cloud_api_key,
|
||||
@@ -61,6 +62,7 @@ __all__ = [
|
||||
"IdTool",
|
||||
"id_tool",
|
||||
# Cloud client (api.hanzo.ai)
|
||||
"NO_KEY",
|
||||
"HanzoCloud",
|
||||
"CloudError",
|
||||
"cloud_api_key",
|
||||
|
||||
@@ -5,9 +5,9 @@ search, web search, research, vision — talks to it through this one client.
|
||||
There is exactly one place that knows the base URL, the auth header, and how to
|
||||
turn a non-2xx into a typed error; tools compose it, never re-implement it.
|
||||
|
||||
Auth resolves in order: ``HANZO_API_KEY`` env, the ``apiKey`` (hk- key) in
|
||||
``~/.hanzo/config.json``, then the ``hanzo`` CLI's live IAM session. Base URL is
|
||||
``https://api.hanzo.ai``, overridable via ``HANZO_API_BASE``.
|
||||
Auth resolves in order: ``HANZO_API_KEY`` env, the ``apiKey`` (a ``pk-``/``sk-``
|
||||
key) in ``~/.hanzo/config.json``, then the ``hanzo`` CLI's live IAM session.
|
||||
Base URL is ``https://api.hanzo.ai``, overridable via ``HANZO_API_BASE``.
|
||||
|
||||
Reference: HIP-0300 unified tools; api.hanzo.ai /v1 surface.
|
||||
"""
|
||||
@@ -21,6 +21,13 @@ from collections.abc import AsyncIterator
|
||||
|
||||
DEFAULT_BASE = "https://api.hanzo.ai"
|
||||
|
||||
#: What a cloud-backed tool says when no key resolved — one sentence in one
|
||||
#: place, so the shapes cannot drift apart tool by tool. Cloud admits two:
|
||||
#: ``pk-`` is the publishable key you may ship in a browser bundle, ``sk-`` is
|
||||
#: the one you may not. ``APIKeyPrefixes`` in cloud's ``auth_identity.go`` is
|
||||
#: the authority; anything else resolves to no principal.
|
||||
NO_KEY = "no API key: run `hanzo login` or set HANZO_API_KEY (pk-/sk-)"
|
||||
|
||||
|
||||
# Memo for cli_session_token: shelling out costs ~100ms and the token is stable
|
||||
# for the session, so resolve it at most once per process. `False` means "asked
|
||||
@@ -58,8 +65,8 @@ def cli_session_token() -> str | None:
|
||||
def cloud_api_key() -> str | None:
|
||||
"""Resolve the bearer credential.
|
||||
|
||||
Order: ``HANZO_API_KEY`` env, then the hk- key in ~/.hanzo/config.json,
|
||||
then the CLI's live IAM session.
|
||||
Order: ``HANZO_API_KEY`` env, then the key in ~/.hanzo/config.json, then
|
||||
the CLI's live IAM session.
|
||||
"""
|
||||
env = os.environ.get("HANZO_API_KEY") or os.environ.get("HANZO_KEY")
|
||||
if env and env.strip():
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ For the typed Hanzo Cloud API client, install [`hanzoai`](https://pypi.org/proje
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
HANZO_API_KEY=hk-...
|
||||
HANZO_API_KEY=sk-...
|
||||
HANZO_BASE_URL=https://api.hanzo.ai
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import hanzoai.cloud
|
||||
|
||||
def test_client_constructs():
|
||||
cfg = hanzoai.cloud.Configuration(host="https://api.hanzo.ai")
|
||||
cfg.access_token = "hk-test"
|
||||
cfg.access_token = "sk-test"
|
||||
client = hanzoai.cloud.ApiClient(cfg)
|
||||
assert client is not None
|
||||
assert cfg.host == "https://api.hanzo.ai"
|
||||
|
||||
Reference in New Issue
Block a user