mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
the front door tells the truth about the client behind it
README: PyPI serves 3.2.1 and this tree is 3.2.11, so the install line is the git one. Between the two the default version left the operation ids — 3.2.1 carries get_v1_keys/get_v1_tools (182 api modules, 2172 models) and this carries get_keys/get_tools — so `pip install hanzoai` above a `get_keys()` quickstart hands the reader a client that cannot run it. Counts are measured off the pinned document: 1814 paths, 2479 operations, 191 tags + default_api = 192 API classes over 2460 models, 2659 importable modules. Auth was wrong everywhere and it was the same root cause: the document declares no securityScheme, so every operation got an empty auth_settings and Configuration(access_token=…) is read by nothing. Serializing get_keys from such a Configuration produces a request with no Authorization header at all — the five flows have been calling anonymously and reading the refusal as a bad key. examples/client.py now passes the bearer through ApiClient's header_name/value pair, verified against a local server that echoes what it received, and a smoke test pins both halves so the day cloud emits the scheme, the test fails and the header moves back into Configuration. The examples gate resolved class names and stopped there, which is how `money` and `tools` once passed it while every call in them named a method that no longer existed. It now walks the syntax tree for attribute accesses on anything bound to a *Api and asks the class: ten names resolve, and a stale get_v1_tools fails in the same second the import passes. Deleted five example scripts that imported hanzoai.Hanzo, hanzoai.cluster, hanzoai.agents and hanzoai.completion — none of which exist — or were dict literals captioned "in practice this would be called through the MCP interface"; compileall passed all five, because syntax is not a claim. Deleted three docs that were fabrication rather than drift and that nothing rendered: FEATURES.md, QUICKSTART.md, GPT5_ORCHESTRATION.md. pkg/hanzo-agent said `pip install hanzoai` and `from hanzoai import Agent, Runner`; the client has no Agent. Its own `full` extra resolved to hanzoai[web3,tee,marketplace,cli], extras the client does not declare, so it installed the client and none of the extensions. pyproject said BSD-3-Clause while LICENSE is the Apache 2.0 text. test_smoke imported tracker_api and no /v1/tracker path is emitted any more, so the suite carried a failing test naming a module the client does not have; it reads analytics_api now, off the client.
This commit is contained in:
+7
-4
@@ -138,10 +138,13 @@ docs(network): update API documentation
|
||||
|
||||
## Package-Specific Guidelines
|
||||
|
||||
### Core SDK (`pkg/hanzoai`)
|
||||
- Maintain OpenAI compatibility
|
||||
- Preserve backward compatibility
|
||||
- Document breaking changes
|
||||
### The client (`pkg/hanzoai/cloud`)
|
||||
- Generated. Never edit it by hand — a regeneration is `rmtree` + `copytree`, so
|
||||
the edit is gone and CI's `generate.py --check` fails the run before that.
|
||||
- A defect you find in generated code is fixed in the API document, where every
|
||||
other language SDK gets the same fix.
|
||||
- Method and class names are the document's operation ids, so they move when it
|
||||
does. Note the rename in the commit; do not add an alias for the old name.
|
||||
|
||||
### CLI (`pkg/hanzo`)
|
||||
- Keep commands intuitive
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# LLM.md — hanzoai/python-sdk
|
||||
|
||||
**What this is:** the flagship, most-complete Hanzo SDK — a `uv` workspace of 60+
|
||||
**What this is:** the flagship, most-complete Hanzo SDK — a `uv` workspace of 65
|
||||
packages: the typed cloud client (`hanzoai`), agents, MCP server + tools, memory,
|
||||
distributed compute, and the `hanzo` CLI. `pip install hanzo`.
|
||||
|
||||
@@ -12,12 +12,22 @@ OUT, never duplicate. Full spec: `~/work/hanzo/SDK-ARCHITECTURE.md`.
|
||||
|
||||
## Install / run
|
||||
```bash
|
||||
pip install hanzoai # typed cloud client
|
||||
uv pip install "hanzoai @ git+https://github.com/hanzoai/python-sdk" # the current client
|
||||
pip install hanzo # agents + MCP + orchestration helpers
|
||||
uv sync --all-packages # dev: whole workspace
|
||||
uv run pytest tests/ -v # tests
|
||||
```
|
||||
|
||||
**PyPI is behind, and the README says so instead of pretending.** `hanzoai` on
|
||||
PyPI is **3.2.1**; this tree is 3.2.11, and v3.2.3 … v3.2.10 are tagged on the
|
||||
forge with nothing published for any of them. The gap is not cosmetic:
|
||||
3.2.1 was cut before the default version left the operation ids, so it carries
|
||||
`get_v1_keys`/`get_v1_tools` (182 api modules, 2172 models) plus the retired flat
|
||||
`hanzoai/api` tree, while this one carries `get_keys`/`get_tools`. A README that
|
||||
printed `pip install hanzoai` above a `get_keys()` quickstart would be handing
|
||||
the reader a client that cannot run it, so the install line is the git one until
|
||||
a publish lands.
|
||||
|
||||
## Console-script law — only the native binary is called `hanzo`
|
||||
|
||||
The `hanzo` command is the Rust CLI (`curl -fsSL https://hanzo.sh | sh`). No
|
||||
@@ -64,8 +74,18 @@ the document by value:
|
||||
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 **1813 paths / 2479 operations** → 192 api
|
||||
modules + 2460 model modules.
|
||||
Current `pkg/hanzoai/cloud/` is **1814 paths / 2479 operations** → 192 api
|
||||
modules + 2460 model modules, 2659 importable modules in total. The document
|
||||
carries 191 tags; the 192nd module is `default_api`, which holds the 50
|
||||
operations that carry no tag (`/`, `/.well-known/agent-skills/*`, …).
|
||||
|
||||
**834 of the 2479 operations model no response body** — 716 declare no
|
||||
`responses` at all and 118 declare responses with no `content` — so those methods
|
||||
are typed `-> None` and the payload is reachable only through the generated
|
||||
`*_without_preload_content` variant. `examples/money` is the worked example of
|
||||
reading one honestly, including the part that is easy to miss: the raw variant
|
||||
does not raise on a 4xx, because raising is part of the typed deserialization
|
||||
those operations do not have.
|
||||
|
||||
**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
|
||||
@@ -90,8 +110,29 @@ path the server no longer serves.
|
||||
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.
|
||||
document this tree is pinned to: **191 tags → 191 api modules, 1:1**, plus `default_api`
|
||||
for the untagged 50, so nothing collapses, and `generate.py python --check` reports
|
||||
`[python] clean` with no local strip of any kind.
|
||||
|
||||
## The client does not authenticate itself, and that is a document defect
|
||||
|
||||
`Configuration(access_token=…)` is inert here. cloud's document declares no
|
||||
`securitySchemes` and no `security`, so openapi-generator wrote
|
||||
`_auth_settings: List[str] = []` into all 2479 operations,
|
||||
`Configuration.auth_settings()` returns `{}`, and `update_params_for_auth`
|
||||
returns on its first line. Measured: serializing `get_keys` from a Configuration
|
||||
built with `access_token="sk-test"` yields a request with **no `Authorization`
|
||||
header at all**. The examples had been built that way since they were written,
|
||||
so every flow was calling anonymously and reporting the server's refusal as
|
||||
though the key were bad.
|
||||
|
||||
`examples/client.py` now passes the header through `ApiClient`'s own
|
||||
`header_name`/`header_value` pair — the generated client's supported way to send
|
||||
a header the document did not describe — and a flow run against a local echo
|
||||
server shows `Authorization: Bearer <key>` on the wire. That is the correct
|
||||
short-term shape, but the real fix belongs upstream: when cloud's emission
|
||||
declares a bearer scheme, `access_token` starts working, and the header should
|
||||
move back into `Configuration` in one edit. Until then, one place sets it.
|
||||
|
||||
**Two spec defects were found and fixed upstream while regenerating at 3.1.5.** Neither was
|
||||
patched here; both are in `hanzoai/openapi` main:
|
||||
@@ -105,14 +146,26 @@ patched here; both are in `hanzoai/openapi` main:
|
||||
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 — five flows, and nothing loose beside them
|
||||
|
||||
`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.
|
||||
`examples/{hello,money,store,agent,tools}`, one directory each, plus
|
||||
`examples/client.py` as the single place a base URL or an env var is resolved. Run one with
|
||||
`python -m examples.hello` from the repo root.
|
||||
|
||||
`chat` is absent by measurement, not by choice: `POST /v1/chat/completions` is declared with
|
||||
no `requestBody` and no `responses`, so the generated method takes no arguments and returns
|
||||
`None` — the one call the flow exists to make cannot be expressed. It returns the day the
|
||||
document describes the body; `hanzo.yml` carries the one-line test for that.
|
||||
|
||||
Five loose scripts used to sit beside the flows (`using_grok.py`, `unified_ai_example.py`,
|
||||
`self_learning_agent.py`, `parallel_ai_doc_editing.py`, `worktree_orchestration.py`) and all
|
||||
five were dead: they imported `hanzoai.Hanzo`, `hanzoai.cluster`, `hanzoai.agents`,
|
||||
`hanzoai.completion` — none of which exist — or were dict-literal sketches captioned "in
|
||||
practice this would be called through the MCP interface". `compileall` passed them all,
|
||||
because syntax is not a claim. They are deleted. `examples/` is the flows.
|
||||
|
||||
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
|
||||
*import* all five 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.
|
||||
|
||||
@@ -124,10 +177,12 @@ 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
|
||||
`.hanzo/workflows/cicd.yml` importing `hanzoai/ci`. The gate is three 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.
|
||||
bad `$ref`), read the syntax tree for duplicate fields the import cannot see, then resolve
|
||||
and import the five flows. Each provisions an interpreter with `uv` when the runner lacks
|
||||
one. There is no `.github/workflows/` here: the label these callers ask for is served by the
|
||||
git-runner fleet on git.hanzo.ai and by nothing on github.com.
|
||||
|
||||
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".
|
||||
@@ -136,13 +191,43 @@ mean "the client the spec just produced is broken", not "something, somewhere".
|
||||
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.
|
||||
|
||||
The `examples` step used to import the flows and stop there, and the comment beside it said
|
||||
so honestly — a method name is looked up at call time, so a renamed operation passed the
|
||||
import and failed in a user's app. It now resolves them: the syntax tree gives every
|
||||
attribute access on a `*Api`-bound name, and the class is asked whether it has it. Ten names
|
||||
resolve today, and a stale `get_v1_tools` fails the step in the same second the import
|
||||
passes.
|
||||
|
||||
## Prose is checked the same way as code
|
||||
|
||||
Three docs under `docs/` were fabrication, not drift: `FEATURES.md`
|
||||
(`from hanzoai import cluster/agents/mcp` — none exist), `QUICKSTART.md` and
|
||||
`GPT5_ORCHESTRATION.md` (invented console output for CLI flags and models that were never
|
||||
ours). None were in the mkdocs nav, so nothing rendered them and nothing checked them.
|
||||
Deleted. `pkg/hanzo-agent` told the reader to `pip install hanzoai` and
|
||||
`from hanzoai import Agent, Runner` — the cloud client has no `Agent` — and its own `full`
|
||||
extra resolved to `hanzoai[web3,tee,marketplace,cli]`, extras the cloud client does not
|
||||
declare, so it installed the client and none of the extensions. Both corrected to
|
||||
`hanzo-agent` / `from agents import`.
|
||||
|
||||
`pyproject.toml` said `BSD-3-Clause` while `LICENSE` is the Apache 2.0 text, so the wheel's
|
||||
metadata contradicted the file inside it. Metadata now matches the file: `Apache-2.0`.
|
||||
|
||||
`tests/test_smoke.py` imported `hanzoai.cloud.api.tracker_api`; no `/v1/tracker` path is
|
||||
emitted any more, so the suite had a failing test that named a module the client does not
|
||||
have. It reads `analytics_api` now, off the client.
|
||||
|
||||
## 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/hanzoai/` — the client (`ApiClient`, `Configuration`, `*Api`) under `cloud/`, plus
|
||||
seven hand-written modules beside it (`config`, `mcp`, `protocols`, `session`, `zap`, …).
|
||||
`pkg/hanzoai/{api,models}` is GONE from git; if a checkout still shows `resources/` or
|
||||
`types/` on disk they are untracked leftovers of that surface, and the wheel excludes them
|
||||
— a built 3.2.11 wheel is 2660 files of `hanzoai/cloud/` and those seven modules, nothing
|
||||
else.
|
||||
- `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-tools-*/` — one concern each; 37 of the 38 register a `TOOLS` list under the
|
||||
`hanzo.tools` entry point (`hanzo-tools-core` is the shared base, so it registers none).
|
||||
- `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
|
||||
|
||||
@@ -1,242 +1,155 @@
|
||||
<p align="center"><img src=".github/hero.svg" alt="Hanzo Python SDK" width="880"></p>
|
||||
|
||||
# Hanzo Python SDK
|
||||
|
||||
**The flagship Python SDK for the Open AI Cloud — models, agents, tools, memory, and MCP in one install.**
|
||||
`hanzoai` is the Python client for the Hanzo API, generated from the API's own
|
||||
OpenAPI document — 1814 paths, 2479 operations, 192 API classes over 2460 models.
|
||||
Every `/v1` route is in it, and the names it exposes are the document's operation
|
||||
ids rather than a hand-picked subset. [`.spec-lock`](.spec-lock) names the commit
|
||||
and sha256 of the document this tree was cut from.
|
||||
|
||||
[](https://github.com/hanzoai/python-sdk/actions/workflows/ci.yml)
|
||||
[](https://pypi.org/project/hanzoai/)
|
||||
[](https://pypi.org/project/hanzoai/)
|
||||
[](LICENSE)
|
||||
|
||||
This is the most complete Hanzo SDK — a `uv` workspace of 60+ composable packages
|
||||
covering the full AI surface: the typed cloud client, an agent framework, the
|
||||
Model Context Protocol server and tools, persistent memory + RAG, distributed
|
||||
compute, and a batteries-included CLI. If you build AI in Python, start here.
|
||||
[](https://pypi.org/project/hanzoai/)
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
pip install hanzoai # the typed cloud API client
|
||||
pip install hanzo # orchestration helpers, agents, MCP
|
||||
pip install "hanzo[all]" # everything, including optional extras
|
||||
uv pip install "hanzoai @ git+https://github.com/hanzoai/python-sdk"
|
||||
```
|
||||
|
||||
The `hanzo` **command** is not a Python package — it is a native binary:
|
||||
Install from source, not from PyPI, until this version reaches it. PyPI serves
|
||||
**3.2.1**; this tree is **3.2.11**, and in between the API document dropped the
|
||||
default version from its operation ids. 3.2.1's methods are `get_v1_keys` and
|
||||
`get_v1_tools`; the current ones are `get_keys` and `get_tools`, so
|
||||
`pip install hanzoai` gets you a client that works and a set of names that
|
||||
matches nothing below.
|
||||
|
||||
```bash
|
||||
curl -fsSL https://hanzo.sh | sh
|
||||
hanzo auth login
|
||||
Check the install without a key — `GET /v1/models` is public:
|
||||
|
||||
```python
|
||||
import json
|
||||
from hanzoai.cloud import ApiClient, Configuration, ModelsApi
|
||||
|
||||
body = ModelsApi(ApiClient(Configuration())).get_models_without_preload_content().read()
|
||||
print(len(json.loads(body)["data"]), "models") # e.g. 112 models
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
```python
|
||||
from hanzoai.cloud import ApiClient, Configuration, ChatApi
|
||||
import os
|
||||
from hanzoai.cloud import ApiClient, Configuration, KeysApi
|
||||
|
||||
config = Configuration(host="https://api.hanzo.ai", access_token="sk-...")
|
||||
client = ApiClient(
|
||||
Configuration(host="https://api.hanzo.ai"),
|
||||
"Authorization", f"Bearer {os.environ['HANZO_API_KEY']}",
|
||||
)
|
||||
|
||||
with ApiClient(config) as client:
|
||||
print(ChatApi(client).post_v1_chat_completions())
|
||||
with client as api:
|
||||
for key in KeysApi(api).get_keys().keys or []:
|
||||
print(key.prefix, key.type, key.created_at)
|
||||
```
|
||||
|
||||
`hanzoai.cloud` is the client, and it is the only one. Method and class names are
|
||||
the document's operation ids, so they move when the document does — that is what
|
||||
makes the client checkable against a release instead of against memory.
|
||||
Every route follows that shape: one `*Api` class per tag, one method per
|
||||
operation, typed models in and out. Keys come from
|
||||
[cloud.hanzo.ai](https://cloud.hanzo.ai) or `hanzo login`, in two shapes —
|
||||
`pk-` to read, `sk-` to write.
|
||||
|
||||
Every route is `https://api.hanzo.ai/v1/<service>/*`. Models come from the **Zen**
|
||||
family (our own models) plus any provider you connect — one typed client, no proxy
|
||||
in the middle.
|
||||
## Auth
|
||||
|
||||
## Examples — the six canonical flows
|
||||
The header is `Authorization: Bearer <key>`, and you set it on the `ApiClient` as
|
||||
above — **not** through `Configuration(access_token=…)`. The document declares no
|
||||
security scheme, so the generator wrote an empty `auth_settings` into every
|
||||
operation and nothing ever reads `access_token`. A client built that way sends no
|
||||
`Authorization` header at all: every call goes out anonymous, and the refusal
|
||||
that comes back reads like a bad key rather than an unsent header.
|
||||
|
||||
`examples/` carries one directory per flow. These are the same six in every
|
||||
Hanzo SDK, so a reader who knows one language's set can navigate another's.
|
||||
Nothing in the client reads the environment. [`examples/client.py`](examples/client.py)
|
||||
is where `HANZO_API_KEY` and `HANZO_BASE_URL` get resolved — one place, for all
|
||||
five flows.
|
||||
|
||||
## Examples
|
||||
|
||||
`examples/` carries one directory per flow. Each is a whole path through one part
|
||||
of the API. On every push CI imports all five and resolves every method name they
|
||||
call against the client, which is what keeps them from rotting into pseudocode.
|
||||
|
||||
| flow | what it does | routes |
|
||||
|---|---|---|
|
||||
| [`hello`](examples/hello) | identity — prove the key works | `GET /v1/bot/auth/me` |
|
||||
| [`chat`](examples/chat) | one completion | `POST /v1/chat/completions` |
|
||||
| [`hello`](examples/hello) | prove the key works | `GET /v1/keys` |
|
||||
| [`money`](examples/money) | balance + usage | `GET /v1/billing/balance`, `GET /v1/billing/usage` |
|
||||
| [`store`](examples/store) | KV round-trip | `POST /v1/kv`, `GET`/`DELETE /v1/kv/{name}` |
|
||||
| [`agent`](examples/agent) | create + run + read | `POST /v1/agents`, `POST /v1/agents/{ref}/run`, `GET /v1/agents/{ref}/runs` |
|
||||
| [`tools`](examples/tools) | tool catalog | `GET /v1/tools` |
|
||||
|
||||
Each reads `HANZO_API_KEY` from the environment and talks to
|
||||
`https://api.hanzo.ai` unless `HANZO_BASE_URL` says otherwise:
|
||||
| [`agent`](examples/agent) | create, run, read the runs | `POST /v1/agents`, `POST /v1/agents/{ref}/run`, `GET /v1/agents/{ref}/runs` |
|
||||
| [`tools`](examples/tools) | the tool catalog | `GET /v1/tools` |
|
||||
|
||||
```bash
|
||||
export HANZO_API_KEY=sk-...
|
||||
uv run python -m examples.hello
|
||||
python -m examples.hello
|
||||
```
|
||||
|
||||
They import from **`hanzoai.cloud`** — the client generated from
|
||||
`https://api.hanzo.ai/v1/openapi.json`, which is where new work goes.
|
||||
`examples/client.py` is the single place a base URL or an env var is resolved.
|
||||
CI imports all six on every push, which is what keeps them from rotting into
|
||||
pseudocode.
|
||||
There is no `chat` flow because there is nothing to generate one from:
|
||||
`POST /v1/chat/completions` is declared with no `requestBody` and no `responses`,
|
||||
so the method takes no arguments and returns `None`. Hand-rolling the request
|
||||
inside a generated client is the drift these SDKs exist to prevent. It comes back
|
||||
the day the document describes the body.
|
||||
|
||||
## Packages
|
||||
The same gap shows up in `money`, which reads its two payloads through the
|
||||
generated `*_without_preload_content` variant: of 2479 operations, 716 declare no
|
||||
`responses` and another 118 declare no response content, so 834 of them model no
|
||||
body to deserialize. Those become ordinary typed calls when the schemas land, and
|
||||
nothing else about them changes.
|
||||
|
||||
The workspace splits cleanly by concern. The headline packages:
|
||||
Reference for the routes themselves: [api.hanzo.ai/docs](https://api.hanzo.ai/docs),
|
||||
served from the same document — [api.hanzo.ai/v1/openapi.json](https://api.hanzo.ai/v1/openapi.json).
|
||||
|
||||
| Package | Purpose |
|
||||
|---------|---------|
|
||||
| `hanzoai` | Typed cloud API client (generated from the Hanzo OpenAPI surface). |
|
||||
| `hanzo` | Orchestration helpers and the older Python CLI (console script `hanzo-py`). |
|
||||
| `hanzo-mcp` | Model Context Protocol server — discovers tools via entry points. |
|
||||
| `hanzo-agents` / `hanzo-agent` | Agent framework — build and orchestrate agents and swarms. |
|
||||
| `hanzo-network` | Distributed AI compute and node orchestration. |
|
||||
| `hanzo-memory` | Persistent memory + RAG (SQLite, optional vector backends). |
|
||||
| `hanzo-tools-*` | 60+ single-concern tool packages (`shell`, `browser`, `fs`, `code`, `vector`, `iam`, …), each exposing a `TOOLS` list. |
|
||||
## The rest of the repo
|
||||
|
||||
```
|
||||
python-sdk/
|
||||
└── pkg/
|
||||
├── hanzoai/ # typed cloud client (OpenAPI-generated)
|
||||
├── hanzo/ # orchestration helpers + legacy Python CLI
|
||||
├── hanzo-mcp/ # MCP server (entry-point tool discovery)
|
||||
├── hanzo-agents/ # agent framework
|
||||
├── hanzo-network/ # distributed compute
|
||||
├── hanzo-memory/ # memory + RAG
|
||||
└── hanzo-tools-*/ # composable tool packages
|
||||
```
|
||||
This is a `uv` workspace. `pkg/hanzoai` is the client above; the other 64
|
||||
packages are hand-written, ship separately, and mostly carry their own README:
|
||||
|
||||
## CLI
|
||||
| Package | Install | Purpose |
|
||||
|---|---|---|
|
||||
| `pkg/hanzoai` | `hanzoai` | the client above |
|
||||
| `pkg/hanzo-mcp` | `hanzo-mcp` | Model Context Protocol server |
|
||||
| `pkg/hanzo-agent` | `hanzo-agent` | agent framework (import path `agents`) |
|
||||
| `pkg/hanzo-agents` | `hanzo-agents` | agent networks and swarms |
|
||||
| `pkg/hanzo-memory` | `hanzo-memory` | persistent memory + RAG over SQLite |
|
||||
| `pkg/hanzo-network` | `hanzo-network` | distributed compute nodes |
|
||||
| `pkg/hanzo-tools-*` | one each | 37 single-concern tool packages, each registering a `TOOLS` list under the `hanzo.tools` entry point, which is how `hanzo-mcp` finds them |
|
||||
|
||||
The Hanzo CLI is a native binary, not a Python package:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://hanzo.sh | sh
|
||||
hanzo auth login
|
||||
hanzo models list
|
||||
hanzo "fix the failing test"
|
||||
```
|
||||
|
||||
It carries one command group per Hanzo Cloud product, generated from the same
|
||||
contract this SDK is generated from. `hanzo --help` prints the tree.
|
||||
|
||||
`pip install hanzo` still ships the older Python CLI as **`hanzo-py`**. It is
|
||||
named that way on purpose: two programs called `hanzo` on one PATH is how
|
||||
`hanzo login` came to mean different things to different people.
|
||||
|
||||
## Model Context Protocol (`hanzo-mcp`)
|
||||
|
||||
`hanzo-mcp` hosts the MCP server and discovers tools through
|
||||
`[project.entry-points."hanzo.tools"]`, so any installed `hanzo-tools-*` package
|
||||
lights up automatically.
|
||||
|
||||
```python
|
||||
from hanzo_mcp import create_mcp_server
|
||||
|
||||
server = create_mcp_server()
|
||||
server.register_tool(my_tool)
|
||||
server.start()
|
||||
```
|
||||
|
||||
## Agents (`hanzo-agents`)
|
||||
|
||||
```python
|
||||
from hanzo_agents import Agent, Swarm
|
||||
|
||||
agent = Agent(
|
||||
name="researcher",
|
||||
model="zen5-coder",
|
||||
instructions="You are a research assistant.",
|
||||
)
|
||||
|
||||
swarm = Swarm([agent])
|
||||
result = await swarm.run("Research quantum computing.")
|
||||
```
|
||||
|
||||
## Network (`hanzo-network`)
|
||||
|
||||
```python
|
||||
from hanzo_network import LocalComputeNode, DistributedNetwork
|
||||
|
||||
node = LocalComputeNode(node_id="node-001")
|
||||
network = DistributedNetwork()
|
||||
network.register_node(node)
|
||||
```
|
||||
|
||||
## Memory (`hanzo-memory`)
|
||||
|
||||
Persistent memory and RAG backed by SQLite, with optional vector search
|
||||
(`sqlite-vec`, `lancedb`, `kuzu`). Global state lives in `~/.hanzo/`; per-project
|
||||
state in `.hanzo/`.
|
||||
|
||||
```python
|
||||
from hanzo_memory import MemoryService
|
||||
|
||||
memory = MemoryService()
|
||||
await memory.store("key", "value")
|
||||
result = await memory.retrieve("key")
|
||||
```
|
||||
The `hanzo` **command** is a native binary, not a Python package:
|
||||
`curl -fsSL https://hanzo.sh | sh`. `pip install hanzo` ships the older Python CLI
|
||||
under the name `hanzo-py`, so the two never fight over one name on a PATH.
|
||||
|
||||
## Development
|
||||
|
||||
This is a `uv` workspace.
|
||||
```bash
|
||||
git clone https://github.com/hanzoai/python-sdk && cd python-sdk
|
||||
uv sync --all-packages
|
||||
uv run pytest tests/ -v
|
||||
```
|
||||
|
||||
`pkg/hanzoai/cloud/` is generated and is never edited by hand — a regeneration
|
||||
does `rmtree` then `copytree`, so an edit there is gone on the next run. It comes
|
||||
from [hanzoai/openapi](https://github.com/hanzoai/openapi):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/hanzoai/python-sdk.git
|
||||
cd python-sdk
|
||||
uv sync --all-packages # install the whole workspace
|
||||
|
||||
uv run pytest tests/ -v # run tests
|
||||
make lint # ruff lint
|
||||
make format # ruff format
|
||||
make type-check # mypy / pyright
|
||||
cd ../openapi && uv run --with pyyaml python3 generate.py python \
|
||||
--repo ../python-sdk --spec ../cloud/openapi.yaml
|
||||
```
|
||||
|
||||
Per-package work:
|
||||
|
||||
```bash
|
||||
uv run pytest pkg/hanzo-mcp -v
|
||||
cd pkg/hanzo && uv build
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
HANZO_API_KEY=your-api-key
|
||||
HANZO_BASE_URL=https://api.hanzo.ai
|
||||
HANZO_LOG_LEVEL=INFO
|
||||
```
|
||||
|
||||
Or `~/.hanzo/config.yaml`:
|
||||
|
||||
```yaml
|
||||
api:
|
||||
key: your-api-key
|
||||
base_url: https://api.hanzo.ai
|
||||
logging:
|
||||
level: INFO
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
- Transport is TLS 1.3+. Secrets belong in a KMS, never in source or plaintext.
|
||||
- SOC 2 audit in progress; HIPAA BAA available.
|
||||
|
||||
Report vulnerabilities to **[email protected]**. See [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). Use type hints,
|
||||
add tests for new behavior, and run `make lint` before opening a PR.
|
||||
A defect found in generated code is fixed in the document, where every other
|
||||
language gets the fix too. See [LLM.md](LLM.md) for how the lane works.
|
||||
|
||||
## License
|
||||
|
||||
Apache License 2.0 — see [LICENSE](LICENSE).
|
||||
|
||||
## Support
|
||||
|
||||
- Docs: [docs.hanzo.ai](https://docs.hanzo.ai)
|
||||
- Issues: [github.com/hanzoai/python-sdk/issues](https://github.com/hanzoai/python-sdk/issues)
|
||||
- Email: support@hanzo.ai
|
||||
Apache 2.0 — see [LICENSE](LICENSE). Report vulnerabilities to security@hanzo.ai
|
||||
([SECURITY.md](SECURITY.md)).
|
||||
|
||||
## Hanzo — the Open AI Cloud
|
||||
|
||||
Open source · every language · on-chain settlement. [hanzo.ai](https://hanzo.ai) · [docs.hanzo.ai](https://docs.hanzo.ai)
|
||||
|
||||
**SDKs in every language** — [Python](https://github.com/hanzoai/python-sdk) (flagship) · [TypeScript](https://github.com/hanzo-js/sdk) · [Go](https://github.com/hanzo-go/sdk) · [Rust](https://github.com/hanzo-rs/sdk) · [C++](https://github.com/hanzo-cpp/sdk) · [Swift](https://github.com/hanzo-swift/sdk) · [Kotlin](https://github.com/hanzo-kt/sdk) · [umbrella](https://github.com/hanzoai/sdk)
|
||||
[hanzo.ai](https://hanzo.ai) · [docs.hanzo.ai](https://docs.hanzo.ai) ·
|
||||
same client in other languages:
|
||||
[TypeScript](https://github.com/hanzoai/js-sdk) ·
|
||||
[Go](https://github.com/hanzoai/go-sdk) ·
|
||||
[Java](https://github.com/hanzoai/java-sdk) ·
|
||||
[Kotlin](https://github.com/hanzoai/kotlin-sdk) ·
|
||||
[umbrella](https://github.com/hanzoai/sdk)
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
# Hanzo AI SDK - Unified AI Features
|
||||
|
||||
The Hanzo AI SDK now includes integrated support for agents, MCP (Model Context Protocol), and local AI clusters, providing a complete solution for AI development that is **local, private, and free**.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install hanzoai
|
||||
|
||||
# Optional: Install additional components
|
||||
pip install hanzo-agents # For agent networks
|
||||
pip install hanzo-mcp # For MCP tools
|
||||
pip install exo-explore # For local AI clusters
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### 1. Local AI Clusters
|
||||
|
||||
Run AI models locally on your own hardware or across your network of devices:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from hanzoai import cluster
|
||||
|
||||
async def run_local_ai():
|
||||
# Start a local cluster
|
||||
my_cluster = await cluster.start_local_cluster(
|
||||
name="my-ai-cluster",
|
||||
model_path="~/.cache/huggingface/hub"
|
||||
)
|
||||
|
||||
# Run inference locally
|
||||
result = await my_cluster.inference(
|
||||
prompt="Hello, local AI!",
|
||||
model="llama-3.2-3b"
|
||||
)
|
||||
|
||||
print(result)
|
||||
await my_cluster.stop()
|
||||
|
||||
asyncio.run(run_local_ai())
|
||||
```
|
||||
|
||||
### 2. Agent Networks
|
||||
|
||||
Create sophisticated AI agent systems with local and distributed execution:
|
||||
|
||||
```python
|
||||
from hanzoai import agents
|
||||
|
||||
# Create agents
|
||||
local_agent = agents.create_agent(
|
||||
name="local-assistant",
|
||||
model="llama-3.2-3b",
|
||||
base_url="http://localhost:8000" # Local cluster
|
||||
)
|
||||
|
||||
cloud_agent = agents.create_agent(
|
||||
name="cloud-assistant",
|
||||
model="anthropic/claude-3-5-sonnet-20241022"
|
||||
)
|
||||
|
||||
# Create a network
|
||||
network = agents.create_network(
|
||||
agents=[local_agent, cloud_agent],
|
||||
router=agents.state_based_router() # Smart routing
|
||||
)
|
||||
|
||||
# The network automatically routes tasks to the best agent
|
||||
result = await network.run("Complex task requiring analysis")
|
||||
```
|
||||
|
||||
### 3. MCP Tools
|
||||
|
||||
Access 70+ tools through the Model Context Protocol:
|
||||
|
||||
```python
|
||||
from hanzoai import mcp
|
||||
|
||||
# Create an MCP server
|
||||
server = mcp.create_mcp_server(
|
||||
name="my-tools",
|
||||
allowed_paths=[".", "/workspace"],
|
||||
enable_agent_tool=True
|
||||
)
|
||||
|
||||
# Or connect to an existing MCP server
|
||||
client = mcp.MCPClient()
|
||||
await client.connect()
|
||||
|
||||
# Use tools
|
||||
result = await client.call_tool(
|
||||
"search",
|
||||
pattern="def main",
|
||||
path="."
|
||||
)
|
||||
```
|
||||
|
||||
### 4. Mining Network
|
||||
|
||||
Contribute compute to the network and earn rewards:
|
||||
|
||||
```python
|
||||
from hanzoai import cluster
|
||||
|
||||
# Join the mining network
|
||||
miner = await cluster.join_mining_network(
|
||||
wallet_address="0x1234...",
|
||||
max_ram=8, # GB
|
||||
max_vram=4 # GB
|
||||
)
|
||||
|
||||
# Check mining stats
|
||||
stats = miner.get_stats()
|
||||
print(f"Compute contributed: {stats['compute_contributed']}")
|
||||
print(f"Rewards earned: {stats['rewards_earned']}")
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Local AI │ │ Agent Networks │ │ MCP Tools │
|
||||
│ Cluster │────▶│ │────▶│ │
|
||||
│ (exo-based) │ │ (hanzo-agents) │ │ (hanzo-mcp) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
└───────────────────────┴────────────────────────┘
|
||||
│
|
||||
┌──────────▼──────────┐
|
||||
│ Hanzo AI SDK │
|
||||
│ (unified API) │
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### 1. **Cost-Effective Development**
|
||||
Use local models for development and testing, only using cloud APIs when necessary:
|
||||
|
||||
```python
|
||||
# Development: Use local cluster
|
||||
dev_result = await local_cluster.inference("Test prompt")
|
||||
|
||||
# Production: Use cloud with same interface
|
||||
prod_result = completion(model="gpt-4", messages=[...])
|
||||
```
|
||||
|
||||
### 2. **Privacy-First Applications**
|
||||
Keep sensitive data local while still leveraging AI:
|
||||
|
||||
```python
|
||||
# Process sensitive documents locally
|
||||
local_agent = agents.create_agent(
|
||||
name="privacy-agent",
|
||||
model="llama-3.2-3b",
|
||||
base_url="http://localhost:8000"
|
||||
)
|
||||
|
||||
result = await local_agent.process_documents(
|
||||
documents=sensitive_files,
|
||||
keep_local=True
|
||||
)
|
||||
```
|
||||
|
||||
### 3. **Distributed AI Workloads**
|
||||
Distribute work across multiple devices:
|
||||
|
||||
```python
|
||||
# Create a cluster across your devices
|
||||
cluster_config = cluster.ClusterConfig(
|
||||
broadcast_addresses=[
|
||||
"192.168.1.100", # Desktop
|
||||
"192.168.1.101", # Laptop
|
||||
"192.168.1.102", # Server
|
||||
]
|
||||
)
|
||||
|
||||
distributed_cluster = cluster.HanzoCluster(cluster_config)
|
||||
await distributed_cluster.start()
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Start Local**: Always try local models first for cost savings
|
||||
2. **Smart Routing**: Use agent networks to automatically route between local and cloud
|
||||
3. **Cache Models**: Download and cache models locally for offline use
|
||||
4. **Join Mining**: Contribute unused compute to earn rewards
|
||||
5. **Use MCP**: Leverage the extensive tool ecosystem
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
# Local cluster configuration
|
||||
HANZO_CLUSTER_NAME=my-cluster
|
||||
HANZO_MODEL_PATH=~/.cache/huggingface/hub
|
||||
|
||||
# Mining configuration
|
||||
HANZO_WALLET_ADDRESS=0x1234...
|
||||
HANZO_MAX_RAM=16
|
||||
HANZO_MAX_VRAM=8
|
||||
|
||||
# Agent configuration
|
||||
HANZO_DEFAULT_MODEL=llama-3.2-3b
|
||||
HANZO_FALLBACK_MODEL=anthropic/claude-3-5-sonnet-20241022
|
||||
```
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [ ] Automatic model downloading and management
|
||||
- [ ] Peer-to-peer model sharing
|
||||
- [ ] Federated learning support
|
||||
- [ ] Mobile device support
|
||||
- [ ] Enhanced mining rewards system
|
||||
- [ ] Native GUI for cluster management
|
||||
|
||||
## Support
|
||||
|
||||
For help and support:
|
||||
- Documentation: https://docs.hanzo.ai
|
||||
- Discord: https://discord.gg/CJCyAsm9Vr
|
||||
- GitHub: https://github.com/hanzoai
|
||||
@@ -1,290 +0,0 @@
|
||||
# GPT-5/Codex Orchestration Guide
|
||||
|
||||
## Overview
|
||||
|
||||
Hanzo Dev supports using GPT-5, GPT-4, Codex, and other advanced models as orchestrators for AI-powered code review, development, and system improvements.
|
||||
|
||||
## Available Orchestrator Models
|
||||
|
||||
| Model | Best For | Context Window | Cost |
|
||||
|-------|----------|----------------|------|
|
||||
| `gpt-5` | Complex reasoning, architecture | 128K | High |
|
||||
| `gpt-4o` | Code review, optimization | 128K | Medium |
|
||||
| `gpt-4-turbo` | Fast iterations | 128K | Medium |
|
||||
| `gpt-4` | General development | 32K | Medium |
|
||||
| `codex` | Code generation, completion | 8K | Low |
|
||||
| `o3` | Advanced reasoning | 200K | High |
|
||||
| `claude-3-5-sonnet` | Long context, analysis | 200K | Medium |
|
||||
| `local:llama3.2` | Simple tasks (free!) | 8K | Free |
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Basic GPT-5 Orchestration
|
||||
|
||||
```bash
|
||||
# Use GPT-5 as the main orchestrator
|
||||
hanzo dev --orchestrator gpt-5
|
||||
```
|
||||
|
||||
### 2. GPT-4o for Code Review
|
||||
|
||||
```bash
|
||||
# Optimized GPT-4 for code review
|
||||
hanzo dev --orchestrator gpt-4o --instances 3 --critic-instances 2
|
||||
```
|
||||
|
||||
### 3. Cost-Optimized Hybrid Mode
|
||||
|
||||
```bash
|
||||
# GPT-5 orchestrator + local workers (90% cost reduction)
|
||||
hanzo dev --orchestrator gpt-5 --use-hanzo-net
|
||||
```
|
||||
|
||||
This configuration:
|
||||
- Uses GPT-5 only for high-level orchestration
|
||||
- Deploys local models for simple tasks
|
||||
- Routes complex tasks to API models when needed
|
||||
- Reduces costs by 90%
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Comprehensive Code Review
|
||||
|
||||
```bash
|
||||
hanzo dev --orchestrator gpt-4o \
|
||||
--instances 3 \
|
||||
--critic-instances 2 \
|
||||
--enable-guardrails \
|
||||
--workspace /path/to/project
|
||||
```
|
||||
|
||||
Then provide review instructions:
|
||||
```
|
||||
Please review the codebase for:
|
||||
1. Security vulnerabilities
|
||||
2. Performance bottlenecks
|
||||
3. Architecture issues
|
||||
4. Code quality problems
|
||||
5. Best practice violations
|
||||
|
||||
Generate a detailed report with fixes.
|
||||
```
|
||||
|
||||
### Multi-Model Orchestration
|
||||
|
||||
You can combine different models for specialized tasks:
|
||||
|
||||
```python
|
||||
# In your Python code
|
||||
from hanzo.dev import HanzoDevOrchestrator
|
||||
|
||||
orchestrator = HanzoDevOrchestrator(
|
||||
orchestrator_model="gpt-5", # High-level planning
|
||||
worker_models=[
|
||||
"gpt-4o", # Code generation
|
||||
"codex", # Code completion
|
||||
"local:llama3.2" # Simple tasks
|
||||
],
|
||||
critic_models=[
|
||||
"claude-3-5-sonnet", # Deep analysis
|
||||
"gpt-4-turbo" # Fast validation
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
## Cost Optimization Strategies
|
||||
|
||||
### 1. Tiered Routing
|
||||
|
||||
The system automatically routes tasks based on complexity:
|
||||
|
||||
| Task Type | Routed To | Examples |
|
||||
|-----------|-----------|----------|
|
||||
| Simple | Local models | Formatting, linting, simple checks |
|
||||
| Medium | GPT-4-turbo | Refactoring, documentation |
|
||||
| Complex | GPT-4o/GPT-5 | Architecture, security analysis |
|
||||
| Critical | GPT-5/O3 | System design, complex debugging |
|
||||
|
||||
### 2. Batch Processing
|
||||
|
||||
Group related tasks for efficient processing:
|
||||
|
||||
```bash
|
||||
# Process multiple files in parallel
|
||||
hanzo dev --orchestrator gpt-4o \
|
||||
--batch-mode \
|
||||
--files "src/**/*.py"
|
||||
```
|
||||
|
||||
### 3. Caching and Reuse
|
||||
|
||||
The system caches:
|
||||
- Common code patterns
|
||||
- Previous review results
|
||||
- Frequent queries
|
||||
|
||||
## Model-Specific Features
|
||||
|
||||
### GPT-5 Features
|
||||
- Advanced reasoning chains
|
||||
- Multi-step planning
|
||||
- Cross-codebase analysis
|
||||
- Architectural recommendations
|
||||
|
||||
### GPT-4o Features
|
||||
- Optimized for code tasks
|
||||
- Fast response times
|
||||
- Excellent at refactoring
|
||||
- Strong type inference
|
||||
|
||||
### Codex Features
|
||||
- Code completion
|
||||
- Docstring generation
|
||||
- Test generation
|
||||
- Code translation
|
||||
|
||||
### O3 Features
|
||||
- Complex problem solving
|
||||
- Mathematical proofs
|
||||
- Algorithm optimization
|
||||
- Security analysis
|
||||
|
||||
## Integration with MCP Tools
|
||||
|
||||
All orchestrators can use MCP tools:
|
||||
|
||||
```bash
|
||||
# Enable all MCP tools for the orchestrator
|
||||
hanzo dev --orchestrator gpt-5 --mcp-tools
|
||||
```
|
||||
|
||||
Available tools:
|
||||
- File operations
|
||||
- Code search
|
||||
- Git operations
|
||||
- Browser automation
|
||||
- Database queries
|
||||
- API calls
|
||||
|
||||
## Monitoring and Debugging
|
||||
|
||||
### View Orchestrator Decisions
|
||||
|
||||
```bash
|
||||
# Enable verbose logging
|
||||
hanzo dev --orchestrator gpt-4o --verbose
|
||||
|
||||
# Monitor in real-time
|
||||
hanzo dev --orchestrator gpt-5 --monitor
|
||||
```
|
||||
|
||||
### Performance Metrics
|
||||
|
||||
The system tracks:
|
||||
- Token usage per model
|
||||
- Response times
|
||||
- Task success rates
|
||||
- Cost per operation
|
||||
|
||||
View metrics:
|
||||
```bash
|
||||
hanzo metrics --orchestrator gpt-5
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Start with GPT-4o**: Good balance of performance and cost
|
||||
2. **Use local models for simple tasks**: Free and fast
|
||||
3. **Reserve GPT-5 for complex problems**: Maximum capability when needed
|
||||
4. **Enable critic agents**: Catch issues early
|
||||
5. **Use guardrails**: Prevent code degradation
|
||||
6. **Monitor costs**: Track usage with `hanzo metrics`
|
||||
|
||||
## Example Workflows
|
||||
|
||||
### Security Audit
|
||||
|
||||
```bash
|
||||
hanzo dev --orchestrator gpt-5 \
|
||||
--focus security \
|
||||
--workspace . \
|
||||
--output security-report.md
|
||||
```
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
```bash
|
||||
hanzo dev --orchestrator gpt-4o \
|
||||
--focus performance \
|
||||
--profile \
|
||||
--suggest-optimizations
|
||||
```
|
||||
|
||||
### Architecture Review
|
||||
|
||||
```bash
|
||||
hanzo dev --orchestrator o3 \
|
||||
--focus architecture \
|
||||
--generate-diagrams \
|
||||
--suggest-patterns
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
# Required for OpenAI models
|
||||
export OPENAI_API_KEY=sk-...
|
||||
|
||||
# Optional: Custom endpoints
|
||||
export OPENAI_API_BASE=https://api.openai.com/v1
|
||||
export GPT5_ENDPOINT=https://api.openai.com/v1 # When available
|
||||
|
||||
# Cost limits
|
||||
export HANZO_DAILY_LIMIT=100 # USD
|
||||
export HANZO_HOURLY_LIMIT=10 # USD
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Model Not Available
|
||||
|
||||
If GPT-5 is not yet available:
|
||||
```bash
|
||||
# Fallback to GPT-4o
|
||||
hanzo dev --orchestrator gpt-4o
|
||||
```
|
||||
|
||||
### Rate Limits
|
||||
|
||||
Handle rate limits with:
|
||||
```bash
|
||||
# Add retry logic and backoff
|
||||
hanzo dev --orchestrator gpt-4o \
|
||||
--retry-on-rate-limit \
|
||||
--max-retries 3
|
||||
```
|
||||
|
||||
### High Costs
|
||||
|
||||
Reduce costs with:
|
||||
```bash
|
||||
# Use local models for most tasks
|
||||
hanzo dev --orchestrator gpt-4o \
|
||||
--use-hanzo-net \
|
||||
--prefer-local
|
||||
```
|
||||
|
||||
## Future Features
|
||||
|
||||
- **GPT-5 Vision**: Code review from screenshots
|
||||
- **Voice Control**: Natural language commands
|
||||
- **Auto-fix**: Automatic issue resolution
|
||||
- **Continuous Monitoring**: 24/7 code quality checks
|
||||
- **Team Collaboration**: Multiple orchestrators working together
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- GitHub: https://github.com/hanzoai/python-sdk
|
||||
- Discord: https://discord.gg/CJCyAsm9Vr
|
||||
- Email: support@hanzo.ai
|
||||
@@ -1,220 +0,0 @@
|
||||
# 🚀 Quick Start: Run GPT-5 Pro + Codex Orchestration
|
||||
|
||||
## Step 1: Check Installation
|
||||
|
||||
```bash
|
||||
# Check if hanzo is installed
|
||||
hanzo --version
|
||||
|
||||
# If not installed, install it:
|
||||
pip install hanzo
|
||||
|
||||
# Or install from this directory:
|
||||
pip install -e pkg/hanzo/
|
||||
```
|
||||
|
||||
## Step 2: Set API Keys
|
||||
|
||||
```bash
|
||||
# Set your OpenAI API key (required for GPT-5/Codex)
|
||||
export OPENAI_API_KEY="sk-..."
|
||||
|
||||
# Optional: Set Anthropic key for Claude models
|
||||
export ANTHROPIC_API_KEY="sk-ant-..."
|
||||
|
||||
# Optional: Set hanzo router endpoint if using router mode
|
||||
export HANZO_ROUTER_URL="http://localhost:4000"
|
||||
```
|
||||
|
||||
## Step 3: Run Different Configurations
|
||||
|
||||
### Option A: GPT-5 Pro + Codex (Best for Code)
|
||||
```bash
|
||||
hanzo dev --orchestrator gpt-5-pro-codex
|
||||
```
|
||||
|
||||
### Option B: Via Hanzo Router
|
||||
```bash
|
||||
# First, start the router (in another terminal)
|
||||
hanzo router start
|
||||
|
||||
# Then run dev with router
|
||||
hanzo dev --orchestrator router:gpt-5
|
||||
```
|
||||
|
||||
### Option C: Direct Codex Mode
|
||||
```bash
|
||||
hanzo dev --orchestrator codex
|
||||
```
|
||||
|
||||
### Option D: Cost-Optimized (90% Savings)
|
||||
```bash
|
||||
# Start local AI first
|
||||
hanzo net --models llama-3.2-3b --port 52415
|
||||
|
||||
# Then run with hybrid mode
|
||||
hanzo dev --orchestrator cost-optimized --use-hanzo-net
|
||||
```
|
||||
|
||||
## Step 4: Interactive Commands
|
||||
|
||||
Once running, you can interact with the orchestrator:
|
||||
|
||||
```bash
|
||||
# In the hanzo dev REPL:
|
||||
> review my code for security issues
|
||||
> generate a REST API for user management
|
||||
> refactor this function for better performance
|
||||
> add comprehensive tests to this module
|
||||
> explain this architecture decision
|
||||
```
|
||||
|
||||
## Complete Example Session
|
||||
|
||||
```bash
|
||||
# Terminal 1: Start local AI (optional, for cost savings)
|
||||
$ hanzo net --models llama-3.2-3b
|
||||
Starting Hanzo Net Compute Node
|
||||
✓ Model loaded: llama-3.2-3b
|
||||
Serving at http://localhost:52415
|
||||
|
||||
# Terminal 2: Start hanzo router (optional, for router mode)
|
||||
$ hanzo router start
|
||||
Hanzo Router v1.74.3
|
||||
Serving at http://localhost:4000
|
||||
Connected providers: OpenAI, Anthropic, Google, Mistral
|
||||
|
||||
# Terminal 3: Run the orchestrator
|
||||
$ hanzo dev --orchestrator gpt-5-pro-codex --instances 3
|
||||
Orchestrator Configuration
|
||||
Mode: hybrid
|
||||
Primary Model: gpt-5-pro
|
||||
Codex Model: code-davinci-002
|
||||
Cost Optimization: Enabled
|
||||
|
||||
Hanzo Dev - AI Coding OS
|
||||
✓ GPT-5 Pro orchestrator initialized
|
||||
✓ Codex connected for code generation
|
||||
✓ 3 worker agents ready
|
||||
✓ MCP tools enabled
|
||||
✓ Cost-optimized routing active
|
||||
|
||||
Ready for commands...
|
||||
>
|
||||
```
|
||||
|
||||
## Available Commands in REPL
|
||||
|
||||
```bash
|
||||
> help # Show available commands
|
||||
> status # Show agent status
|
||||
> review <file> # Review code file
|
||||
> generate <description> # Generate code
|
||||
> refactor <code> # Refactor existing code
|
||||
> test <module> # Generate tests
|
||||
> debug <error> # Debug an issue
|
||||
> explain <concept> # Explain code/architecture
|
||||
> optimize <code> # Optimize performance
|
||||
> secure <code> # Security audit
|
||||
> document <code> # Generate documentation
|
||||
```
|
||||
|
||||
## Monitor Performance
|
||||
|
||||
```bash
|
||||
# In another terminal, monitor the orchestrator
|
||||
$ hanzo dev --monitor
|
||||
┌─────────────────────────────────┐
|
||||
│ Orchestrator Status │
|
||||
├─────────────────────────────────┤
|
||||
│ Model: GPT-5 Pro │
|
||||
│ Workers: 3/3 active │
|
||||
│ Tasks: 12 completed, 2 pending │
|
||||
│ Cost: $0.45 (this session) │
|
||||
│ Tokens: 45,231 / 128,000 │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
```bash
|
||||
# If you get API key errors:
|
||||
echo $OPENAI_API_KEY # Check if set
|
||||
export OPENAI_API_KEY="sk-..." # Set it
|
||||
|
||||
# If hanzo command not found:
|
||||
pip install hanzo # Install globally
|
||||
# OR
|
||||
python -m hanzo.cli dev --orchestrator gpt-5-pro-codex # Run as module
|
||||
|
||||
# If port already in use:
|
||||
hanzo dev --orchestrator gpt-5-pro-codex --hanzo-net-port 52416
|
||||
|
||||
# Check logs:
|
||||
tail -f ~/.hanzo/dev/logs/orchestrator.log
|
||||
```
|
||||
|
||||
## Cost Tracking
|
||||
|
||||
```bash
|
||||
# Check your usage
|
||||
$ hanzo metrics
|
||||
Today's Usage:
|
||||
GPT-5 Pro: $2.45 (16,300 tokens)
|
||||
Codex: $0.80 (40,000 tokens)
|
||||
GPT-4o: $1.20 (24,000 tokens)
|
||||
Local Models: $0.00 (120,000 tokens)
|
||||
Total: $4.45
|
||||
Savings: $12.55 (74% saved via optimization)
|
||||
```
|
||||
|
||||
## Pro Tips
|
||||
|
||||
1. **Start with cost-optimized mode** to save money while testing
|
||||
2. **Use router mode** for automatic failover between providers
|
||||
3. **Enable monitoring** to track performance and costs
|
||||
4. **Use local models** for simple tasks (formatting, linting)
|
||||
5. **Reserve GPT-5 Pro** for complex architectural decisions
|
||||
|
||||
## Full Production Setup
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# save as: start-hanzo-dev.sh
|
||||
|
||||
# Start local AI
|
||||
echo "Starting local AI..."
|
||||
hanzo net --models llama-3.2-3b --port 52415 &
|
||||
LOCAL_PID=$!
|
||||
|
||||
# Wait for local AI to be ready
|
||||
sleep 5
|
||||
|
||||
# Start router (optional)
|
||||
echo "Starting hanzo router..."
|
||||
hanzo router start --port 4000 &
|
||||
ROUTER_PID=$!
|
||||
|
||||
# Wait for router
|
||||
sleep 3
|
||||
|
||||
# Start orchestrator with GPT-5 Pro + Codex
|
||||
echo "Starting GPT-5 Pro + Codex orchestrator..."
|
||||
hanzo dev \
|
||||
--orchestrator gpt-5-pro-codex \
|
||||
--instances 3 \
|
||||
--critic-instances 2 \
|
||||
--enable-guardrails \
|
||||
--use-hanzo-net \
|
||||
--workspace . \
|
||||
--monitor
|
||||
|
||||
# Cleanup on exit
|
||||
trap "kill $LOCAL_PID $ROUTER_PID" EXIT
|
||||
```
|
||||
|
||||
Make it executable and run:
|
||||
```bash
|
||||
chmod +x start-hanzo-dev.sh
|
||||
./start-hanzo-dev.sh
|
||||
```
|
||||
+26
-9
@@ -1,23 +1,40 @@
|
||||
# Hanzo Python SDK
|
||||
|
||||
The complete Python SDK for building AI applications with Hanzo AI infrastructure.
|
||||
A workspace of 65 packages. Two things live here that people usually arrive for:
|
||||
the API client, and the MCP server.
|
||||
|
||||
## Overview
|
||||
## The API client (`hanzoai`)
|
||||
|
||||
The Hanzo Python SDK provides:
|
||||
Generated from the Hanzo API's own OpenAPI document — 1814 paths, 2479
|
||||
operations, 192 API classes over 2460 models. It is documented in the
|
||||
[repository README](https://github.com/hanzoai/python-sdk#readme), which carries
|
||||
the install line, the quickstart and the five runnable flows in `examples/`.
|
||||
|
||||
- **Agent SDK** (`hanzo-agent`) - Build agentic AI applications with a lightweight, production-ready framework
|
||||
- **MCP Server** (`hanzo-mcp`) - Model Context Protocol server with 30+ tools for AI code assistants
|
||||
- **Tool Packages** (`hanzo-tools-*`) - Modular tool packages for file operations, shell commands, browser automation, and more
|
||||
```python
|
||||
import os
|
||||
from hanzoai.cloud import ApiClient, Configuration, KeysApi
|
||||
|
||||
client = ApiClient(
|
||||
Configuration(host="https://api.hanzo.ai"),
|
||||
"Authorization", f"Bearer {os.environ['HANZO_API_KEY']}",
|
||||
)
|
||||
|
||||
with client as api:
|
||||
for key in KeysApi(api).get_keys().keys or []:
|
||||
print(key.prefix, key.type, key.created_at)
|
||||
```
|
||||
|
||||
The rest of this site is the MCP server, the agent framework and the tool
|
||||
packages.
|
||||
|
||||
## Installation
|
||||
|
||||
=== "Full Install"
|
||||
=== "MCP server"
|
||||
```bash
|
||||
pip install hanzo-mcp[tools-all]
|
||||
pip install hanzo-mcp
|
||||
```
|
||||
|
||||
=== "Agent SDK Only"
|
||||
=== "Agent SDK"
|
||||
```bash
|
||||
pip install hanzo-agent
|
||||
```
|
||||
|
||||
+15
-5
@@ -1,7 +1,7 @@
|
||||
"""The one place an example learns where the API is and who it is.
|
||||
|
||||
Every flow imports this and nothing else builds a client, so there is a single
|
||||
answer to "which base URL?" and "which env var?" across all six.
|
||||
answer to "which base URL?" and "which env var?" across all five.
|
||||
|
||||
Run any flow from the repo root::
|
||||
|
||||
@@ -37,12 +37,22 @@ def api_key() -> str:
|
||||
|
||||
|
||||
def client() -> ApiClient:
|
||||
"""An ApiClient bound to the configured host and bearer key.
|
||||
"""An ApiClient bound to the configured host, carrying the key as a bearer.
|
||||
|
||||
``access_token`` becomes ``Authorization: Bearer <key>``, which is the only
|
||||
scheme the spec declares.
|
||||
The header is set here rather than through ``Configuration(access_token=…)``,
|
||||
and that is a correction, not a preference. The document declares no security
|
||||
scheme, so the generator wrote an empty ``auth_settings`` into every
|
||||
operation, ``Configuration.auth_settings()`` returns ``{}``, and
|
||||
``access_token`` is read by nothing. Measured on this tree: serializing
|
||||
``get_keys`` from a Configuration built with ``access_token="sk-test"``
|
||||
produces a request with no ``Authorization`` header at all. Every flow built
|
||||
that way calls anonymously and collects a 403 that reads like a key problem.
|
||||
|
||||
``header_name``/``header_value`` is the generated client's own way to send a
|
||||
header the document did not describe, so it stays the one way here until the
|
||||
document carries the scheme.
|
||||
"""
|
||||
return ApiClient(Configuration(host=BASE_URL, access_token=api_key()))
|
||||
return ApiClient(Configuration(host=BASE_URL), "Authorization", f"Bearer {api_key()}")
|
||||
|
||||
|
||||
def run(main) -> None:
|
||||
|
||||
@@ -8,11 +8,13 @@ Neither call takes an org: both derive the tenant SERVER-side from the token's
|
||||
argument to pass.
|
||||
|
||||
Both are declared with a ``default`` response and no ``content``, so the typed
|
||||
methods return None even though the server sends JSON. That is a spec gap, not
|
||||
an SDK one — 696 of 2425 operations currently model no response body — so this
|
||||
example reads the raw payload through the generated ``*_without_preload_content``
|
||||
variant rather than pretending a type it was not given. When the schemas land,
|
||||
these become ordinary typed calls and nothing else about them changes.
|
||||
methods return None even though the server sends JSON. That is a document gap,
|
||||
not an SDK one — 834 of the document's 2479 operations model no response body
|
||||
(716 declare no ``responses`` at all, 118 declare responses with no ``content``)
|
||||
— so this example reads the raw payload through the generated
|
||||
``*_without_preload_content`` variant rather than pretending a type it was not
|
||||
given. When the schemas land, these become ordinary typed calls and nothing else
|
||||
about them changes.
|
||||
|
||||
python -m examples.money
|
||||
"""
|
||||
|
||||
@@ -1,424 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Parallel AI Documentation Editing using Hanzo-MCP Batch Tool
|
||||
|
||||
This demonstrates how to edit multiple AI documentation files in parallel
|
||||
using the batch tool to delegate to different CLI agents (claude, codex, gemini, grok).
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Any, Dict, List
|
||||
|
||||
# Example of how to use hanzo-mcp batch tool for parallel edits
|
||||
# In practice, this would be called through the MCP interface
|
||||
|
||||
|
||||
def create_parallel_edit_batch():
|
||||
"""
|
||||
Create batch invocations for parallel document editing.
|
||||
Each agent works on a different file simultaneously.
|
||||
"""
|
||||
|
||||
batch_invocations = [
|
||||
{
|
||||
"tool_name": "claude",
|
||||
"input": {
|
||||
"prompt": """
|
||||
Read and enhance CLAUDE.md with:
|
||||
1. Add section on Claude's computer use capabilities
|
||||
2. Include Claude's vision capabilities for code screenshots
|
||||
3. Add patterns for using Claude's analysis tools
|
||||
4. Include Claude 3.5 Sonnet's latest improvements
|
||||
5. Add section on prompt caching for cost optimization
|
||||
|
||||
Return the complete enhanced content maintaining all existing sections.
|
||||
Focus on Claude-specific strengths and unique features.
|
||||
"""
|
||||
},
|
||||
},
|
||||
{
|
||||
"tool_name": "codex",
|
||||
"input": {
|
||||
"prompt": """
|
||||
Read and enhance LLM.md with:
|
||||
1. Add section on OpenAI's latest GPT-4 Turbo capabilities
|
||||
2. Include advanced function calling patterns
|
||||
3. Add OpenAI's Assistants API integration
|
||||
4. Include batch API usage for cost optimization
|
||||
5. Add fine-tuning patterns for custom models
|
||||
|
||||
Return the complete enhanced content maintaining all existing sections.
|
||||
Focus on OpenAI-specific optimizations and features.
|
||||
"""
|
||||
},
|
||||
},
|
||||
{
|
||||
"tool_name": "gemini",
|
||||
"input": {
|
||||
"prompt": """
|
||||
Read and enhance GEMINI.md with:
|
||||
1. Add section on Gemini's latest 1.5 Flash improvements
|
||||
2. Include Gemini Code capabilities
|
||||
3. Add patterns for Gemini's grounding with Google Search
|
||||
4. Include Gemini's extensions and plugins
|
||||
5. Add section on Gemini Advanced features
|
||||
|
||||
Return the complete enhanced content maintaining all existing sections.
|
||||
Focus on Google-specific integrations and multimodal strengths.
|
||||
"""
|
||||
},
|
||||
},
|
||||
{
|
||||
"tool_name": "grok",
|
||||
"input": {
|
||||
"prompt": """
|
||||
Create a new GROK.md file with:
|
||||
1. Grok model family overview (Grok-1, Grok-2)
|
||||
2. Real-time information access patterns
|
||||
3. X (Twitter) integration capabilities
|
||||
4. Code generation optimizations
|
||||
5. Humor and personality in responses
|
||||
6. Integration with xAI ecosystem
|
||||
7. Best practices for Grok usage
|
||||
|
||||
Follow the same structure as other AI doc files.
|
||||
Include code examples and practical patterns.
|
||||
"""
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return batch_invocations
|
||||
|
||||
|
||||
def create_agent_editing_batch():
|
||||
"""
|
||||
Create batch for editing AGENTS.md using multiple agents.
|
||||
Each agent enhances a different section.
|
||||
"""
|
||||
|
||||
batch_invocations = [
|
||||
{
|
||||
"tool_name": "claude",
|
||||
"input": {
|
||||
"prompt": """
|
||||
Enhance the 'Agent Communication Protocols' section in AGENTS.md:
|
||||
- Add WebSocket-based real-time communication
|
||||
- Include event-driven messaging patterns
|
||||
- Add distributed consensus protocols
|
||||
- Include agent negotiation strategies
|
||||
Write only the enhanced section content.
|
||||
"""
|
||||
},
|
||||
},
|
||||
{
|
||||
"tool_name": "codex",
|
||||
"input": {
|
||||
"prompt": """
|
||||
Enhance the 'Git Worktree Management' section in AGENTS.md:
|
||||
- Add automated merge conflict resolution
|
||||
- Include CI/CD integration patterns
|
||||
- Add branch protection strategies
|
||||
- Include automated PR generation
|
||||
Write only the enhanced section content.
|
||||
"""
|
||||
},
|
||||
},
|
||||
{
|
||||
"tool_name": "gemini",
|
||||
"input": {
|
||||
"prompt": """
|
||||
Enhance the 'Swarm Coordination Patterns' section in AGENTS.md:
|
||||
- Add emergent behavior patterns
|
||||
- Include load balancing strategies
|
||||
- Add fault tolerance mechanisms
|
||||
- Include performance optimization
|
||||
Write only the enhanced section content.
|
||||
"""
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return batch_invocations
|
||||
|
||||
|
||||
def create_review_and_merge_batch(edited_content: Dict[str, str]):
|
||||
"""
|
||||
Create batch for reviewing and merging edits using consensus.
|
||||
Multiple agents review each other's work.
|
||||
"""
|
||||
|
||||
batch_invocations = [
|
||||
{
|
||||
"tool_name": "claude",
|
||||
"input": {
|
||||
"prompt": f"""
|
||||
Review the Codex edits to LLM.md:
|
||||
{edited_content.get("llm_edits", "")}
|
||||
|
||||
Check for:
|
||||
1. Technical accuracy
|
||||
2. Consistency with existing content
|
||||
3. Code example correctness
|
||||
4. Best practices alignment
|
||||
|
||||
Provide feedback and improved version if needed.
|
||||
"""
|
||||
},
|
||||
},
|
||||
{
|
||||
"tool_name": "codex",
|
||||
"input": {
|
||||
"prompt": f"""
|
||||
Review the Claude edits to CLAUDE.md:
|
||||
{edited_content.get("claude_edits", "")}
|
||||
|
||||
Check for:
|
||||
1. Implementation feasibility
|
||||
2. Performance implications
|
||||
3. Security considerations
|
||||
4. Integration complexity
|
||||
|
||||
Provide feedback and improved version if needed.
|
||||
"""
|
||||
},
|
||||
},
|
||||
{
|
||||
"tool_name": "gemini",
|
||||
"input": {
|
||||
"prompt": f"""
|
||||
Review all edits and create integration tests:
|
||||
|
||||
Files edited:
|
||||
- LLM.md (by Codex)
|
||||
- CLAUDE.md (by Claude)
|
||||
- GEMINI.md (by Gemini)
|
||||
- AGENTS.md (by multiple)
|
||||
|
||||
Generate:
|
||||
1. Cross-reference validation
|
||||
2. Consistency checks
|
||||
3. Integration test cases
|
||||
4. Documentation validation script
|
||||
"""
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return batch_invocations
|
||||
|
||||
|
||||
class ParallelDocumentEditor:
|
||||
"""
|
||||
Orchestrates parallel editing of documentation using multiple AI agents.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.agents = ["claude", "codex", "gemini", "grok"]
|
||||
self.files = ["LLM.md", "AGENTS.md", "GEMINI.md", "CLAUDE.md"]
|
||||
|
||||
async def execute_parallel_edits(self):
|
||||
"""
|
||||
Execute parallel edits using batch tool.
|
||||
"""
|
||||
|
||||
# Phase 1: Initial parallel edits
|
||||
print("🚀 Phase 1: Initiating parallel edits...")
|
||||
initial_batch = create_parallel_edit_batch()
|
||||
|
||||
# This would be executed as:
|
||||
# results = await batch(
|
||||
# description="Edit AI documentation files",
|
||||
# invocations=initial_batch
|
||||
# )
|
||||
|
||||
# Simulated results for demonstration
|
||||
results_phase1 = {
|
||||
"claude": "Enhanced CLAUDE.md content...",
|
||||
"codex": "Enhanced LLM.md content...",
|
||||
"gemini": "Enhanced GEMINI.md content...",
|
||||
"grok": "New GROK.md content...",
|
||||
}
|
||||
|
||||
print("✅ Phase 1 complete: All files edited in parallel")
|
||||
|
||||
# Phase 2: Parallel section enhancements
|
||||
print("\n🚀 Phase 2: Enhancing specific sections...")
|
||||
section_batch = create_agent_editing_batch()
|
||||
|
||||
# results = await batch(
|
||||
# description="Enhance AGENTS.md sections",
|
||||
# invocations=section_batch
|
||||
# )
|
||||
|
||||
results_phase2 = {
|
||||
"communication": "Enhanced communication section...",
|
||||
"worktree": "Enhanced worktree section...",
|
||||
"swarm": "Enhanced swarm section...",
|
||||
}
|
||||
|
||||
print("✅ Phase 2 complete: Sections enhanced")
|
||||
|
||||
# Phase 3: Cross-review and validation
|
||||
print("\n🚀 Phase 3: Cross-reviewing edits...")
|
||||
review_batch = create_review_and_merge_batch(results_phase1)
|
||||
|
||||
# results = await batch(
|
||||
# description="Review and validate edits",
|
||||
# invocations=review_batch
|
||||
# )
|
||||
|
||||
print("✅ Phase 3 complete: All edits reviewed and validated")
|
||||
|
||||
return {"phase1": results_phase1, "phase2": results_phase2, "status": "completed"}
|
||||
|
||||
async def apply_edits_in_parallel(self, edits: Dict[str, str]):
|
||||
"""
|
||||
Apply validated edits to files in parallel.
|
||||
"""
|
||||
|
||||
write_batch = [
|
||||
{"tool_name": "write", "input": {"file_path": f"/Users/z/work/hanzo/python-sdk/{file}", "content": content}}
|
||||
for file, content in edits.items()
|
||||
]
|
||||
|
||||
# Execute all writes in parallel
|
||||
# results = await batch(
|
||||
# description="Write updated documentation",
|
||||
# invocations=write_batch
|
||||
# )
|
||||
|
||||
print("✅ All files updated in parallel")
|
||||
|
||||
|
||||
# Practical example of batch execution in MCP context
|
||||
BATCH_EXAMPLE = """
|
||||
# In Claude Desktop or hanzo-mcp CLI, you would execute:
|
||||
|
||||
batch --description "Edit AI docs in parallel" --invocations '[
|
||||
{
|
||||
"tool_name": "claude",
|
||||
"input": {
|
||||
"prompt": "Enhance CLAUDE.md with latest features"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_name": "codex",
|
||||
"input": {
|
||||
"prompt": "Enhance LLM.md with OpenAI patterns"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_name": "gemini",
|
||||
"input": {
|
||||
"prompt": "Enhance GEMINI.md with multimodal examples"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_name": "grok",
|
||||
"input": {
|
||||
"prompt": "Create GROK.md documentation"
|
||||
}
|
||||
}
|
||||
]'
|
||||
"""
|
||||
|
||||
|
||||
def demonstrate_batch_patterns():
|
||||
"""
|
||||
Demonstrate various batch execution patterns.
|
||||
"""
|
||||
|
||||
print("=" * 60)
|
||||
print("PARALLEL AI DOCUMENTATION EDITING PATTERNS")
|
||||
print("=" * 60)
|
||||
|
||||
# Pattern 1: Parallel file editing
|
||||
print("\n📝 Pattern 1: Parallel File Editing")
|
||||
print("-" * 40)
|
||||
|
||||
parallel_edit = {
|
||||
"description": "Edit 4 files simultaneously",
|
||||
"invocations": [
|
||||
{"tool": "claude", "target": "CLAUDE.md"},
|
||||
{"tool": "codex", "target": "LLM.md"},
|
||||
{"tool": "gemini", "target": "GEMINI.md"},
|
||||
{"tool": "grok", "target": "AGENTS.md"},
|
||||
],
|
||||
}
|
||||
|
||||
print(json.dumps(parallel_edit, indent=2))
|
||||
|
||||
# Pattern 2: Sequential with parallel stages
|
||||
print("\n📝 Pattern 2: Sequential Stages with Parallel Tasks")
|
||||
print("-" * 40)
|
||||
|
||||
staged_execution = {
|
||||
"stage1": {
|
||||
"description": "Analyze all files in parallel",
|
||||
"parallel": True,
|
||||
"tasks": ["analyze_claude.md", "analyze_llm.md", "analyze_agents.md"],
|
||||
},
|
||||
"stage2": {
|
||||
"description": "Edit based on analysis",
|
||||
"parallel": True,
|
||||
"tasks": ["edit_claude.md", "edit_llm.md", "edit_agents.md"],
|
||||
},
|
||||
"stage3": {"description": "Review all edits", "parallel": False, "tasks": ["consensus_review"]},
|
||||
}
|
||||
|
||||
print(json.dumps(staged_execution, indent=2))
|
||||
|
||||
# Pattern 3: Divide and conquer
|
||||
print("\n📝 Pattern 3: Divide and Conquer")
|
||||
print("-" * 40)
|
||||
|
||||
divide_conquer = {
|
||||
"description": "Each agent handles specific sections",
|
||||
"file": "AGENTS.md",
|
||||
"parallel_sections": [
|
||||
{"agent": "claude", "section": "Communication Protocols", "expertise": "System design"},
|
||||
{"agent": "codex", "section": "Code Generation", "expertise": "Implementation"},
|
||||
{"agent": "gemini", "section": "Testing Strategies", "expertise": "Quality assurance"},
|
||||
],
|
||||
}
|
||||
|
||||
print(json.dumps(divide_conquer, indent=2))
|
||||
|
||||
# Pattern 4: Consensus editing
|
||||
print("\n📝 Pattern 4: Consensus-Based Editing")
|
||||
print("-" * 40)
|
||||
|
||||
consensus_edit = {
|
||||
"description": "Multiple agents edit same content",
|
||||
"target": "architecture.md",
|
||||
"agents": ["claude", "codex", "gemini"],
|
||||
"strategy": "Each agent provides version, then consensus",
|
||||
"final_review": "grok",
|
||||
}
|
||||
|
||||
print(json.dumps(consensus_edit, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
|
||||
# Demonstrate patterns
|
||||
demonstrate_batch_patterns()
|
||||
|
||||
# Example execution (would be async in practice)
|
||||
print("\n" + "=" * 60)
|
||||
print("EXAMPLE PARALLEL EXECUTION")
|
||||
print("=" * 60)
|
||||
|
||||
editor = ParallelDocumentEditor()
|
||||
|
||||
# In practice, this would be:
|
||||
# asyncio.run(editor.execute_parallel_edits())
|
||||
|
||||
print("\n✨ Parallel editing demonstration complete!")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("BATCH TOOL USAGE IN MCP")
|
||||
print("=" * 60)
|
||||
print(BATCH_EXAMPLE)
|
||||
@@ -1,38 +0,0 @@
|
||||
from agents import Agent, Runner, ReflexionEngine
|
||||
from agents.reflexion import Rule
|
||||
from hanzo_memory.client import SQLiteMemoryClient
|
||||
|
||||
|
||||
async def main():
|
||||
# 1. Initialize Memory Client (SQLite)
|
||||
# Ensure the DB exists or is created
|
||||
db_client = SQLiteMemoryClient(user_id="test_user")
|
||||
|
||||
# 2. Initialize Reflexion Engine
|
||||
reflexion = ReflexionEngine(memory_client=db_client)
|
||||
|
||||
# 3. Add some initial rules (simulating past learning)
|
||||
await reflexion.add_rule("Always double check file paths before writing.", context="coding")
|
||||
|
||||
# 4. Create an Agent with Reflexion
|
||||
agent = Agent(
|
||||
name="SelfLearningBot",
|
||||
instructions="You are a helpful assistant that learns from mistakes.",
|
||||
reflexion=reflexion,
|
||||
)
|
||||
|
||||
# 5. Simulate a run (in a real scenario, the runner would use these)
|
||||
print(f"Agent {agent.name} initialized.")
|
||||
rules = await agent.reflexion.load_rules(context="coding")
|
||||
print(f"Loaded {len(rules)} rules for context 'coding':")
|
||||
for r in rules:
|
||||
print(f" - {r.content}")
|
||||
|
||||
# 6. Simulate a learning event
|
||||
print("\nSimulating reflection...")
|
||||
new_rule = await agent.reflexion.add_rule("Use pathlib for path manipulations.", context="coding")
|
||||
print(f"Added new rule: {new_rule.content}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Example of using the unified Hanzo AI SDK.
|
||||
|
||||
This demonstrates:
|
||||
1. Local AI cluster with exo
|
||||
2. Agent networks
|
||||
3. MCP tools
|
||||
4. Cloud AI fallback
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from hanzoai import mcp, agents, cluster, completion
|
||||
|
||||
|
||||
async def main():
|
||||
"""Run the example."""
|
||||
|
||||
# 1. Start a local AI cluster
|
||||
print("Starting local AI cluster...")
|
||||
local_cluster = await cluster.start_local_cluster(
|
||||
name="my-cluster",
|
||||
model_path="~/.cache/huggingface/hub", # Use local models
|
||||
)
|
||||
|
||||
# 2. Create agents
|
||||
print("\nCreating AI agents...")
|
||||
|
||||
# Local agent using the cluster
|
||||
local_agent = agents.create_agent(
|
||||
name="local-helper",
|
||||
model="llama-3.2-3b", # Uses local cluster
|
||||
base_url=local_cluster.get_api_endpoint(),
|
||||
)
|
||||
|
||||
# Cloud agent as fallback
|
||||
cloud_agent = agents.create_agent(name="cloud-helper", model="anthropic/claude-3-5-sonnet-20241022")
|
||||
|
||||
# 3. Create an agent network
|
||||
print("\nCreating agent network...")
|
||||
network = agents.create_network(
|
||||
agents=[local_agent, cloud_agent],
|
||||
router=agents.state_based_router(), # Smart routing
|
||||
)
|
||||
|
||||
# 4. Create MCP server with tools
|
||||
print("\nStarting MCP server...")
|
||||
mcp_server = mcp.create_mcp_server(name="hanzo-unified", allowed_paths=[".", "/tmp"], enable_agent_tool=True)
|
||||
|
||||
# 5. Example: Use local AI for simple tasks
|
||||
print("\n--- Local AI Example ---")
|
||||
local_result = await local_cluster.inference(prompt="Write a haiku about distributed AI", max_tokens=50)
|
||||
print(f"Local AI: {local_result}")
|
||||
|
||||
# 6. Example: Use cloud AI for complex tasks
|
||||
print("\n--- Cloud AI Example ---")
|
||||
cloud_result = completion(
|
||||
model="anthropic/claude-3-5-sonnet-20241022",
|
||||
messages=[{"role": "user", "content": "Explain the benefits of local AI clusters"}],
|
||||
)
|
||||
print(f"Cloud AI: {cloud_result}")
|
||||
|
||||
# 7. Join mining network (optional)
|
||||
if input("\nJoin mining network? (y/n): ").lower() == "y":
|
||||
wallet = input("Enter wallet address: ")
|
||||
miner = await cluster.join_mining_network(wallet_address=wallet)
|
||||
print(f"Mining stats: {miner.get_stats()}")
|
||||
|
||||
# Cleanup
|
||||
print("\nShutting down...")
|
||||
await local_cluster.stop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=== Hanzo AI Unified SDK Example ===")
|
||||
print("Local, Private, Free AI Infrastructure")
|
||||
print("=====================================\n")
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -1,190 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Example of using xAI Grok with Hanzo Python SDK.
|
||||
|
||||
This example demonstrates:
|
||||
1. Direct API usage with Grok
|
||||
2. Streaming responses
|
||||
3. Batch operations with Grok
|
||||
4. Consensus with multiple models including Grok
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import asyncio
|
||||
|
||||
# Add paths for development (remove in production)
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "pkg", "hanzoai"))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "pkg", "hanzo", "src"))
|
||||
|
||||
try:
|
||||
from hanzoai import Hanzo, AsyncHanzo
|
||||
except ImportError:
|
||||
print("Error: hanzoai not installed")
|
||||
print("Install with: pip install hanzoai")
|
||||
sys.exit(1)
|
||||
|
||||
# Note: Requires XAI_API_KEY environment variable to be set
|
||||
# Get your API key from https://x.ai/api
|
||||
|
||||
|
||||
def basic_grok_usage():
|
||||
"""Basic usage of Grok through Hanzo SDK."""
|
||||
client = Hanzo() # Uses HANZO_API_KEY env var
|
||||
|
||||
# Simple completion
|
||||
response = client.chat.completions.create(
|
||||
model="grok-4", # or "grok" or "xai-grok"
|
||||
messages=[
|
||||
{"role": "system", "content": "You have real-time knowledge."},
|
||||
{"role": "user", "content": "What are the latest AI developments this week?"},
|
||||
],
|
||||
temperature=0.7,
|
||||
max_tokens=500,
|
||||
)
|
||||
|
||||
print("Grok Response:")
|
||||
print(response.choices[0].message.content)
|
||||
print("\n" + "=" * 50 + "\n")
|
||||
|
||||
|
||||
def streaming_grok():
|
||||
"""Stream responses from Grok for real-time output."""
|
||||
client = Hanzo()
|
||||
|
||||
print("Streaming from Grok:")
|
||||
stream = client.chat.completions.create(
|
||||
model="grok-4", messages=[{"role": "user", "content": "Explain quantum computing in simple terms"}], stream=True
|
||||
)
|
||||
|
||||
for chunk in stream:
|
||||
if chunk.choices[0].delta.content:
|
||||
print(chunk.choices[0].delta.content, end="", flush=True)
|
||||
|
||||
print("\n\n" + "=" * 50 + "\n")
|
||||
|
||||
|
||||
async def async_grok_usage():
|
||||
"""Async usage of Grok for concurrent operations."""
|
||||
client = AsyncHanzo()
|
||||
|
||||
# Concurrent requests to different models
|
||||
tasks = [
|
||||
client.chat.completions.create(
|
||||
model="grok-4", messages=[{"role": "user", "content": "What's happening in tech today?"}]
|
||||
),
|
||||
client.chat.completions.create(
|
||||
model="claude-3-5-sonnet", messages=[{"role": "user", "content": "Explain machine learning"}]
|
||||
),
|
||||
client.chat.completions.create(model="gpt-4", messages=[{"role": "user", "content": "Write a haiku about AI"}]),
|
||||
]
|
||||
|
||||
print("Concurrent requests to Grok, Claude, and GPT-4:")
|
||||
responses = await asyncio.gather(*tasks)
|
||||
|
||||
for i, (model, response) in enumerate(zip(["Grok", "Claude", "GPT-4"], responses, strict=False)):
|
||||
print(f"\n{model} Response:")
|
||||
print(response.choices[0].message.content[:200] + "...")
|
||||
|
||||
print("\n" + "=" * 50 + "\n")
|
||||
|
||||
|
||||
def batch_operations_with_grok():
|
||||
"""Demonstrate batch operations using Grok."""
|
||||
from hanzo.batch_orchestrator import BatchConfig, BatchOrchestrator
|
||||
|
||||
print("Batch Operations with Grok:")
|
||||
|
||||
# Parse different batch configurations
|
||||
configs = [
|
||||
"batch:5 agent:grok analyze code quality",
|
||||
"consensus:3 agent:grok,claude,gemini review architecture",
|
||||
"critic:2 agent:grok,gpt-4 chain:true security audit",
|
||||
]
|
||||
|
||||
for cmd in configs:
|
||||
config = BatchConfig.from_command(cmd)
|
||||
print(f"\nCommand: {cmd}")
|
||||
print(f" - Batch size: {config.batch_size}")
|
||||
print(f" - Agent model: {config.agent_model}")
|
||||
if config.consensus_mode:
|
||||
print(f" - Consensus models: {config.consensus_models}")
|
||||
if config.critic_mode:
|
||||
print(f" - Critic chain: {config.critic_chain}")
|
||||
|
||||
print("\n" + "=" * 50 + "\n")
|
||||
|
||||
|
||||
def grok_with_tools():
|
||||
"""Use Grok with MCP tools."""
|
||||
try:
|
||||
from hanzo_mcp.tools.agent.cli_tools import GrokCLITool
|
||||
|
||||
print("Grok CLI Tool Configuration:")
|
||||
grok_tool = GrokCLITool()
|
||||
|
||||
print(f"Tool name: {grok_tool.name}")
|
||||
print(f"Description: {grok_tool.description}")
|
||||
print(f"Default model: {grok_tool.default_model}")
|
||||
print(f"API key env: {grok_tool.api_key_env}")
|
||||
|
||||
# Get auth environment
|
||||
env = grok_tool.get_auth_env()
|
||||
if "XAI_API_KEY" in env:
|
||||
print("✅ XAI_API_KEY is configured")
|
||||
else:
|
||||
print("⚠️ XAI_API_KEY not found in environment")
|
||||
|
||||
except ImportError:
|
||||
print("hanzo-mcp not installed. Install with: pip install hanzo-mcp")
|
||||
|
||||
print("\n" + "=" * 50 + "\n")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all Grok examples."""
|
||||
print("=" * 50)
|
||||
print("xAI Grok Examples with Hanzo SDK")
|
||||
print("=" * 50 + "\n")
|
||||
|
||||
# Check for API key
|
||||
if not os.environ.get("XAI_API_KEY"):
|
||||
print("⚠️ Warning: XAI_API_KEY not set")
|
||||
print("Get your API key from https://x.ai/api")
|
||||
print("Set it with: export XAI_API_KEY='your-key-here'")
|
||||
print("\nRunning examples that don't require actual API calls...\n")
|
||||
|
||||
# Run non-API examples
|
||||
batch_operations_with_grok()
|
||||
grok_with_tools()
|
||||
return
|
||||
|
||||
# Run all examples
|
||||
try:
|
||||
# Basic usage
|
||||
basic_grok_usage()
|
||||
|
||||
# Streaming
|
||||
streaming_grok()
|
||||
|
||||
# Async operations
|
||||
asyncio.run(async_grok_usage())
|
||||
|
||||
# Batch operations
|
||||
batch_operations_with_grok()
|
||||
|
||||
# MCP tools
|
||||
grok_with_tools()
|
||||
|
||||
print("✅ All Grok examples completed successfully!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
print("\nMake sure you have:")
|
||||
print("1. Set XAI_API_KEY environment variable")
|
||||
print("2. Set HANZO_API_KEY environment variable (or pass api_key to client)")
|
||||
print("3. Installed hanzoai: pip install hanzoai")
|
||||
print("4. (Optional) Installed hanzo-mcp: pip install hanzo-mcp")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,227 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example: Orchestrating Git Worktrees with Hanzo-MCP Agents
|
||||
|
||||
This demonstrates how to use hanzo-mcp agents to manage parallel development
|
||||
across multiple git worktrees, with each agent handling a specific task.
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from typing import Any, Dict, List
|
||||
|
||||
# This would normally use the hanzo-mcp Python client
|
||||
# For demonstration, showing the conceptual flow
|
||||
|
||||
|
||||
class WorktreeOrchestrator:
|
||||
"""Orchestrates multiple agents working on git worktrees."""
|
||||
|
||||
def __init__(self, base_branch: str = "main"):
|
||||
self.base_branch = base_branch
|
||||
self.worktrees: Dict[str, str] = {}
|
||||
|
||||
def create_worktree_agent_prompt(self, task_id: str, task_content: str) -> str:
|
||||
"""Generate agent prompt for worktree task."""
|
||||
return f"""
|
||||
You are an autonomous development agent assigned to task {task_id}.
|
||||
|
||||
TASK: {task_content}
|
||||
|
||||
INSTRUCTIONS:
|
||||
1. Create a git worktree for this task:
|
||||
```bash
|
||||
git worktree add -b feature/{task_id} ../worktree-{task_id}
|
||||
cd ../worktree-{task_id}
|
||||
```
|
||||
|
||||
2. Read the architecture.md file to understand the system design
|
||||
|
||||
3. Implement the required functionality:
|
||||
- Follow existing code patterns
|
||||
- Use appropriate error handling
|
||||
- Add necessary imports
|
||||
- Create clean, modular code
|
||||
|
||||
4. Write tests for your implementation:
|
||||
- Unit tests for new functions
|
||||
- Integration tests if needed
|
||||
- Ensure tests pass
|
||||
|
||||
5. Commit your changes:
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "feat({task_id}): {task_content}"
|
||||
```
|
||||
|
||||
6. Return a summary including:
|
||||
- Files created/modified
|
||||
- Test results
|
||||
- Any issues encountered
|
||||
- Ready for review: YES/NO
|
||||
|
||||
Use available tools:
|
||||
- read: to read existing files
|
||||
- run_command: for git and test operations
|
||||
- search: to find patterns in codebase
|
||||
- grep_ast: to understand code structure
|
||||
"""
|
||||
|
||||
def create_critic_prompt(self, task_id: str, agent_summary: str) -> str:
|
||||
"""Generate critic prompt for reviewing agent work."""
|
||||
return f"""
|
||||
You are a senior code reviewer. Review the implementation in worktree-{task_id}.
|
||||
|
||||
AGENT SUMMARY:
|
||||
{agent_summary}
|
||||
|
||||
REVIEW CHECKLIST:
|
||||
1. Code Quality
|
||||
- [ ] Follows architecture.md patterns
|
||||
- [ ] Clean, readable code
|
||||
- [ ] Proper error handling
|
||||
- [ ] No code duplication
|
||||
|
||||
2. Security
|
||||
- [ ] Input validation
|
||||
- [ ] No hardcoded secrets
|
||||
- [ ] Safe data handling
|
||||
- [ ] SQL injection prevention
|
||||
|
||||
3. Performance
|
||||
- [ ] Efficient algorithms
|
||||
- [ ] No unnecessary loops
|
||||
- [ ] Proper caching
|
||||
- [ ] Resource cleanup
|
||||
|
||||
4. Testing
|
||||
- [ ] Adequate test coverage
|
||||
- [ ] Edge cases handled
|
||||
- [ ] Tests actually pass
|
||||
- [ ] Mocks used appropriately
|
||||
|
||||
5. Documentation
|
||||
- [ ] Functions have docstrings
|
||||
- [ ] Complex logic explained
|
||||
- [ ] API changes documented
|
||||
|
||||
Read the implementation files and provide:
|
||||
1. List of issues found (if any)
|
||||
2. Severity of each issue (critical/major/minor)
|
||||
3. Specific fix recommendations
|
||||
4. Final verdict: APPROVED or NEEDS_FIXES
|
||||
|
||||
Be strict but fair. This code will go to production.
|
||||
"""
|
||||
|
||||
def execute_workflow(self, tasks: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||
"""Execute the complete worktree workflow."""
|
||||
|
||||
results = {"total_tasks": len(tasks), "completed": [], "failed": [], "worktrees": []}
|
||||
|
||||
for task in tasks:
|
||||
task_id = task["id"]
|
||||
task_content = task["content"]
|
||||
|
||||
print(f"\n🚀 Starting task {task_id}: {task_content}")
|
||||
|
||||
# Step 1: Agent implements the task
|
||||
agent_prompt = self.create_worktree_agent_prompt(task_id, task_content)
|
||||
|
||||
# This would call: agent(prompt=agent_prompt)
|
||||
# For demo, showing the structure
|
||||
agent_result = {
|
||||
"task_id": task_id,
|
||||
"worktree": f"worktree-{task_id}",
|
||||
"files_modified": ["src/auth.py", "tests/test_auth.py"],
|
||||
"tests_passed": True,
|
||||
"ready_for_review": True,
|
||||
}
|
||||
|
||||
print(f"✅ Agent completed implementation")
|
||||
|
||||
# Step 2: Critic reviews the implementation
|
||||
if agent_result["ready_for_review"]:
|
||||
critic_prompt = self.create_critic_prompt(task_id, json.dumps(agent_result, indent=2))
|
||||
|
||||
# This would call: critic(analysis=critic_prompt)
|
||||
critic_result = {"verdict": "APPROVED", "issues": [], "score": 95}
|
||||
|
||||
if critic_result["verdict"] == "APPROVED":
|
||||
print(f"✅ Critic approved implementation (score: {critic_result['score']})")
|
||||
results["completed"].append(task_id)
|
||||
|
||||
# Step 3: Prepare for merge
|
||||
self.worktrees[task_id] = f"feature/{task_id}"
|
||||
results["worktrees"].append(
|
||||
{
|
||||
"task_id": task_id,
|
||||
"branch": f"feature/{task_id}",
|
||||
"path": f"../worktree-{task_id}",
|
||||
"ready_to_merge": True,
|
||||
}
|
||||
)
|
||||
else:
|
||||
print(f"⚠️ Critic requested fixes: {critic_result['issues']}")
|
||||
|
||||
# Step 4: Agent fixes issues
|
||||
fix_prompt = f"""
|
||||
Fix these issues in worktree-{task_id}:
|
||||
{json.dumps(critic_result["issues"], indent=2)}
|
||||
"""
|
||||
|
||||
# Second attempt would go here
|
||||
results["failed"].append(
|
||||
{"task_id": task_id, "reason": "Needs fixes", "issues": critic_result["issues"]}
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
def merge_completed_worktrees(self, results: Dict[str, Any]) -> None:
|
||||
"""Merge all completed worktrees back to base branch."""
|
||||
|
||||
print(f"\n🔀 Merging completed worktrees to {self.base_branch}")
|
||||
|
||||
for worktree in results["worktrees"]:
|
||||
if worktree["ready_to_merge"]:
|
||||
branch = worktree["branch"]
|
||||
|
||||
# This would execute:
|
||||
# git checkout main
|
||||
# git merge --no-ff feature/{task_id}
|
||||
# git push origin main
|
||||
|
||||
print(f"✅ Merged {branch} to {self.base_branch}")
|
||||
|
||||
print(f"\n🎉 Workflow complete! {len(results['completed'])}/{results['total_tasks']} tasks merged")
|
||||
|
||||
|
||||
# Example usage
|
||||
if __name__ == "__main__":
|
||||
# Sample tasks from todo list
|
||||
tasks = [
|
||||
{"id": "auth-1", "content": "Implement JWT token generation", "priority": "high"},
|
||||
{"id": "auth-2", "content": "Create login endpoint", "priority": "high"},
|
||||
{"id": "auth-3", "content": "Add password reset flow", "priority": "medium"},
|
||||
{"id": "auth-4", "content": "Implement rate limiting", "priority": "medium"},
|
||||
]
|
||||
|
||||
# Initialize orchestrator
|
||||
orchestrator = WorktreeOrchestrator(base_branch="main")
|
||||
|
||||
# Execute parallel development workflow
|
||||
results = orchestrator.execute_workflow(tasks)
|
||||
|
||||
# Merge successful implementations
|
||||
orchestrator.merge_completed_worktrees(results)
|
||||
|
||||
# Output summary
|
||||
print("\n📊 Workflow Summary:")
|
||||
print(f" Total tasks: {results['total_tasks']}")
|
||||
print(f" Completed: {len(results['completed'])}")
|
||||
print(f" Failed: {len(results['failed'])}")
|
||||
print(f" Worktrees created: {len(results['worktrees'])}")
|
||||
|
||||
# This summary could be sent to Linear or saved to a report
|
||||
with open("workflow_report.json", "w") as f:
|
||||
json.dump(results, f, indent=2)
|
||||
@@ -117,17 +117,22 @@ test:
|
||||
# exists, so a spec change that renames or drops a MODEL or an API class
|
||||
# goes red here rather than in a user's app.
|
||||
#
|
||||
# Importing is enough BECAUSE each flow guards its call behind
|
||||
# `if __name__ == "__main__"` — the module body resolves every name without
|
||||
# opening a socket, so the gate needs no API key and no network.
|
||||
# Importing is enough for the CLASS names BECAUSE each flow guards its call
|
||||
# behind `if __name__ == "__main__"` — the module body resolves every name
|
||||
# without opening a socket, so the gate needs no API key and no network.
|
||||
#
|
||||
# KNOWN CEILING, so nobody reads this gate for more than it says: an import
|
||||
# resolves the names in the `from … import` line and NOTHING ELSE. A method
|
||||
# name is looked up at call time, so a renamed OPERATION passes here and
|
||||
# fails in a user's app. Measured: when the `<svc>_` prefix left every
|
||||
# operationId, `money` and `tools` went on passing this gate while every
|
||||
# call in them named a method that no longer existed. Four flows failed on
|
||||
# their imports and two were silently broken — the gate saw four.
|
||||
# It is NOT enough for the METHOD names, and that gap has shipped twice. An
|
||||
# import resolves the `from … import` line and nothing else; a method is
|
||||
# looked up at call time, so a renamed OPERATION passes an import and fails
|
||||
# in a user's app. Measured: when the `<svc>_` prefix left every operationId,
|
||||
# four flows failed on their imports and `money` and `tools` went on passing
|
||||
# while every call in them named a method that no longer existed — the gate
|
||||
# saw four of six.
|
||||
#
|
||||
# So the check below reads the syntax tree for attribute accesses on anything
|
||||
# bound to a `*Api` and asks the class whether it has them. Ten names resolve
|
||||
# today. The receiver is resolved through its assignment, so it catches
|
||||
# `KeysApi.get_tools` as readily as a stale `get_v1_tools`.
|
||||
#
|
||||
# `chat` is NOT in this list, and its absence is a measurement rather than a
|
||||
# decision. cloud declares POST /v1/chat/completions with no `requestBody`
|
||||
@@ -148,15 +153,36 @@ test:
|
||||
PYTHONPATH=pkg "$UV" run --no-project --python 3.12 \
|
||||
--with pydantic --with python-dateutil --with urllib3 --with typing-extensions \
|
||||
python -c '
|
||||
import compileall, importlib, sys
|
||||
import ast, compileall, importlib, pathlib, sys
|
||||
import hanzoai.cloud as C
|
||||
ok = compileall.compile_dir("examples", quiet=1, maxlevels=3)
|
||||
bad = []
|
||||
for flow in ("hello", "money", "store", "agent", "tools"):
|
||||
try:
|
||||
importlib.import_module(f"examples.{flow}.__main__")
|
||||
print(f"examples.{flow}: OK")
|
||||
except Exception as e:
|
||||
bad.append(f"examples.{flow}: {e!r}")
|
||||
continue
|
||||
tree = ast.parse(pathlib.Path(f"examples/{flow}/__main__.py").read_text())
|
||||
bound = {}
|
||||
for n in ast.walk(tree):
|
||||
if (isinstance(n, ast.Assign) and isinstance(n.value, ast.Call)
|
||||
and isinstance(n.value.func, ast.Name) and n.value.func.id.endswith("Api")):
|
||||
bound.update({t.id: n.value.func.id for t in n.targets if isinstance(t, ast.Name)})
|
||||
names = 0
|
||||
for n in ast.walk(tree):
|
||||
if not isinstance(n, ast.Attribute):
|
||||
continue
|
||||
v = n.value
|
||||
cls = bound.get(v.id) if isinstance(v, ast.Name) else (
|
||||
v.func.id if isinstance(v, ast.Call) and isinstance(v.func, ast.Name)
|
||||
and v.func.id.endswith("Api") else None)
|
||||
if not cls:
|
||||
continue
|
||||
names += 1
|
||||
if not hasattr(getattr(C, cls), n.attr):
|
||||
bad.append(f"examples.{flow}: {cls} has no {n.attr}")
|
||||
print(f"examples.{flow}: OK, {names} method name(s) resolved")
|
||||
for b in bad: print("FAIL", b, file=sys.stderr)
|
||||
sys.exit(1 if (bad or not ok) else 0)
|
||||
'
|
||||
@@ -171,9 +197,10 @@ test:
|
||||
#
|
||||
# The document moved from hanzoai/openapi `hanzo.yaml` (hand-merged, 1742 paths,
|
||||
# fed by nothing, and NOTHING HAS EVER SENT the spec-update this repo listens
|
||||
# for) to hanzoai/cloud `openapi.yaml` — smaller (1058 paths), emitted from the
|
||||
# code by each app's own router, and gated on every cloud release. A smaller true
|
||||
# document beats a larger unverified one.
|
||||
# for) to hanzoai/cloud `openapi.yaml` — emitted from the code by each app's own
|
||||
# router, and gated on every cloud release. It was 1058 paths when it took over
|
||||
# and is 1814 now, which is the point: it grows from the routers rather than from
|
||||
# an editor, so a true document overtook the unverified one on its own.
|
||||
client:
|
||||
generate: ./scripts/generate.sh
|
||||
version: "pyproject.toml:grep -m1 '^version' pyproject.toml | sed -E 's/.*\"(.*)\".*/\\1/'"
|
||||
|
||||
@@ -49,22 +49,22 @@ source env/bin/activate
|
||||
|
||||
```bash
|
||||
# Basic installation
|
||||
pip install hanzoai
|
||||
pip install hanzo-agent
|
||||
|
||||
# With Web3 support
|
||||
pip install "hanzoai[web3]"
|
||||
pip install "hanzo-agent[web3]"
|
||||
|
||||
# With TEE support
|
||||
pip install "hanzoai[tee]"
|
||||
pip install "hanzo-agent[tee]"
|
||||
|
||||
# With Marketplace support
|
||||
pip install "hanzoai[marketplace]"
|
||||
pip install "hanzo-agent[marketplace]"
|
||||
|
||||
# With CLI support
|
||||
pip install "hanzoai[cli]"
|
||||
pip install "hanzo-agent[cli]"
|
||||
|
||||
# Full installation (all extensions)
|
||||
pip install "hanzoai[full]"
|
||||
pip install "hanzo-agent[full]"
|
||||
```
|
||||
|
||||
## Quick Examples
|
||||
@@ -72,7 +72,7 @@ pip install "hanzoai[full]"
|
||||
### Simple Agent
|
||||
|
||||
```python
|
||||
from hanzoai import Agent, Runner
|
||||
from agents import Agent, Runner
|
||||
|
||||
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
|
||||
|
||||
@@ -144,7 +144,7 @@ result = await workflow.run({"topic": "AI Safety"})
|
||||
## Handoffs example
|
||||
|
||||
```python
|
||||
from hanzoai import Agent, Runner
|
||||
from agents import Agent, Runner
|
||||
import asyncio
|
||||
|
||||
spanish_agent = Agent(
|
||||
@@ -179,7 +179,7 @@ if __name__ == "__main__":
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from hanzoai import Agent, Runner, function_tool
|
||||
from agents import Agent, Runner, function_tool
|
||||
|
||||
|
||||
@function_tool
|
||||
|
||||
@@ -17,9 +17,9 @@ The Hanzo Agent SDK can be configured to route all LLM requests through the Hanz
|
||||
### 1. Install the SDK
|
||||
|
||||
```bash
|
||||
pip install hanzoai
|
||||
pip install hanzo-agent
|
||||
# or
|
||||
uv pip install hanzoai
|
||||
uv pip install hanzo-agent
|
||||
```
|
||||
|
||||
### 2. Configure Environment
|
||||
|
||||
@@ -61,7 +61,7 @@ cli = [
|
||||
|
||||
# All extensions
|
||||
full = [
|
||||
"hanzoai[web3,tee,marketplace,cli]",
|
||||
"hanzo-agent[web3,tee,marketplace,cli]",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ name = "hanzoai"
|
||||
version = "3.2.11"
|
||||
description = "The official Python library for the Hanzo API"
|
||||
dynamic = ["readme"]
|
||||
license = "BSD-3-Clause"
|
||||
license = "Apache-2.0"
|
||||
authors = [
|
||||
{ name = "Hanzo", email = "[email protected]" },
|
||||
]
|
||||
@@ -27,7 +27,7 @@ classifiers = [
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Operating System :: Microsoft :: Windows",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"License :: OSI Approved :: BSD License"
|
||||
"License :: OSI Approved :: Apache Software License"
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
|
||||
+31
-2
@@ -16,13 +16,42 @@ def test_client_constructs():
|
||||
assert cfg.host == "https://api.hanzo.ai"
|
||||
|
||||
|
||||
def test_the_bearer_comes_from_the_client_not_the_configuration():
|
||||
"""`Configuration(access_token=…)` sends nothing, and callers must know it.
|
||||
|
||||
The document declares no security scheme, so every generated operation got
|
||||
an empty `auth_settings` and `update_params_for_auth` returns before it can
|
||||
read `access_token`. A caller who sets it — the obvious thing to do — calls
|
||||
anonymously and reads the refusal as a bad key. The examples set the header
|
||||
on `ApiClient`, which merges `default_headers` into every request.
|
||||
|
||||
When cloud's emission grows the scheme, the first assertion fails. That is
|
||||
the point: `access_token` starts working that day and the header moves back
|
||||
into `Configuration`, in one place, for good.
|
||||
"""
|
||||
cfg = hanzoai.cloud.Configuration(host="https://api.hanzo.ai", access_token="sk-test")
|
||||
assert cfg.auth_settings() == {}
|
||||
assert "Authorization" not in hanzoai.cloud.ApiClient(cfg).default_headers
|
||||
|
||||
keyed = hanzoai.cloud.ApiClient(cfg, "Authorization", "Bearer sk-test")
|
||||
assert keyed.default_headers["Authorization"] == "Bearer sk-test"
|
||||
|
||||
|
||||
def test_product_apis_present():
|
||||
from hanzoai.cloud.api.tracker_api import TrackerApi
|
||||
"""Whole products, not just the AI routes, reach the SDK.
|
||||
|
||||
This asked for `TrackerApi` until the document stopped carrying a `tracker`
|
||||
tag — no `/v1/tracker` path is emitted at all now — so it named a module the
|
||||
client does not have and failed on the import. `analytics` is the surviving
|
||||
surface next door (`/v1/analytics/{health,overview,timeseries,top}`), read
|
||||
off the client rather than guessed.
|
||||
"""
|
||||
from hanzoai.cloud.api.analytics_api import AnalyticsApi
|
||||
from hanzoai.cloud.api.chat_api import ChatApi
|
||||
from hanzoai.cloud.api.crm_api import CrmApi
|
||||
|
||||
client = hanzoai.cloud.ApiClient(hanzoai.cloud.Configuration(host="https://api.hanzo.ai"))
|
||||
for cls in (TrackerApi, ChatApi, CrmApi):
|
||||
for cls in (AnalyticsApi, ChatApi, CrmApi):
|
||||
assert cls(client) is not None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user