Reasoning models served over OpenAI-compatible APIs can open a reply
with <think>...</think>. It stayed in final_output, in the history sent
back on the next turn, and in front of structured output, where it
failed JSON parsing. process_model_response, where every model's output
becomes run items, now drops a leading block from the message text.
Text that only mentions the tag, or an unclosed block, is unchanged;
tool call arguments and result.raw_responses keep what the model sent.
Co-authored-by: Hanzo Dev <[email protected]>
An OpenAI-compatible server can answer HTTP 200 with {"error": {...}}.
The openai client parses that as a result with no choices or output, and
the run died in a TypeError. Both model classes now raise
openai.APIError with the server's message and body, as the client does
for an error object inside a stream.
The test drives both routes through a local server. conftest imports
the Responses resource before swapping two of its classes for
factories; imported after, it subclasses a function and fails.
Co-authored-by: Hanzo Dev <[email protected]>
Importing agents registered an exporter that posted every trace and
span to api.openai.com with the default client's key, so a run against
any other server sent its traces there and logged a 401. Nothing is
registered now. BackendSpanExporter takes the endpoint it posts to, with
no default and no OPENAI_* fallback. set_tracing_export_api_key and
use_for_tracing only fed that upload, so they are gone.
Co-authored-by: Hanzo Dev <[email protected]>
The old README showed Web3Agent, a marketplace and a cli extension that
do not import from hanzo-agent 0.1.1, a HANZO_ROUTER_URL nothing reads,
and examples on gpt-4. It now shows a first agent and a function tool
that ran on Python 3.9 and 3.12 against a local OpenAI-compatible
server, how to point the client at api.hanzo.ai, and the extras whose
names import.
It also says what the defaults do: the Responses route unless
chat_completions is set, traces posted to api.openai.com with the
client's key unless tracing is off, the openai client's retry rules,
and a <think> block kept in final_output.
pytest picked up the stray scripts at the repo root, and
test_simple_anthropic.py exits on import, so make tests stopped at
collection. testpaths keeps it to tests/: 369 passed.
Co-authored-by: Hanzo Dev <[email protected]>
hanzoai on PyPI is the API client from hanzoai/python-sdk (3.1.1; all 12
releases carry that provenance). This repo declared the same dist name and
had a release-triggered PyPI publisher, so a release here would land a
different package under the name consumers expect to be the API client.
- remove .github/workflows/publish.yml; python-sdk is the one Python
publisher and it already ships this SDK as hanzo-agent
- name the dist hanzo-agent, matching the PyPI project whose homepage is
this repo, and carry the rename through the self-referential full extra
so hatchling stops resolving it against hanzoai
- README/docs: install hanzo-agent, import agents, the top-level module
the wheel ships
- docker-publish: build on hanzo-build-linux-amd64, not ubuntu-latest
Co-authored-by: Hanzo Dev <[email protected]>
The two README examples following "Hosted via Hanzo Network" reference
symbols that don't exist on the import paths shown. Verified against
`src/agents/__init__.py` and `src/agents/orchestration/__init__.py` at
HEAD.
**Multi-Agent Network**
- `from agents import create_network` → `create_network` lives at
`agents.network.create_network` and isn't re-exported from the top
level. Import fails today.
- `from agents.routers import SemanticRouter` → there is no
`agents.routers` module. `SemanticRouter` is defined in
`agents/network/router.py` and re-exported from `agents.network`.
Both fixed by `from agents.network import create_network, SemanticRouter`.
Verified: `agents/network/__init__.py` exports both in `__all__`.
**Orchestrated Workflow**
The example was a richer mockup than the real API supports today:
- `from agents import create_workflow, Step` → `create_workflow`
doesn't exist anywhere in the codebase; `Step` is at
`agents.orchestration.Step`, not the top level.
- `create_workflow(name=..., steps=[...])` → `Workflow.__init__`
takes `(name, description, version)` only; steps are added via
`Workflow.add_*_step()` methods or via
`Orchestrator.create_workflow_from_agents()`.
- `Step.conditional(condition=..., true_step=..., false_step=...)` →
actual signature is `Step.conditional(condition, if_true, if_false)`
(workflow.py:443).
- `Step.transform(...)` → no such static method. Transform steps are
added via `Workflow.add_transform_step()`.
- `workflow.run({...})` → `Workflow` has no `run` method. Execution
is via `Orchestrator.execute_workflow(workflow_id, input)`
(orchestrator.py:108).
Rewritten to mirror the working pattern from
`examples/network_example.py::orchestrated_workflow_example()` —
`Orchestrator(config=OrchestrationConfig(...))`, `register_agent`,
`create_workflow_from_agents`, `register_workflow`, `execute_workflow`.
The lower-level `Workflow` + `Step.{agent,parallel,conditional,loop}`
helpers are mentioned as a pointer for readers who want direct
control.
Also tightened trailing-comma formatting in the Multi-Agent example
(black-style trailing commas on multi-line args) — pure cosmetic, no
behavior change.
No source code changes. README-only.
Co-authored-by: Abhishek Krishna <[email protected]>