mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
The document grew: cloud's woven spec was merged and won, taking hanzo.yaml from
1132 paths / 1519 operations / 779 schemas to 1737 / 2452 / 1798.
pkg/hanzoai/cloud/ is now 263 api + 2031 model modules, all importing.
`generate.py python --check` reports [python] clean.
The resync also RENAMED nearly every operationId to cloud_<method>_<path>, which
renames every generated method: billing_billingBalance became
cloud_get_v1_billing_balance, cloud_AgentsController.Create became
cloud_post_v1_agents. Examples written against the old names stop resolving —
exactly what the examples gate exists to catch. It went red; this is the fix.
Every flow was re-probed against api.hanzo.ai unauthenticated and with a bogus
key, because a spec says what SHOULD be served and only a request says what IS.
All eleven operations answer 401/403 — routed and identity-gated. Two moved:
store to the PROVISIONING plane (POST /v1/kv, GET|DELETE /v1/kv/{name}). The
per-key data plane the spec also describes is mounted nowhere: GET
/v1/kv/keys/{key} 404s, PUT and DELETE 405, kv.hanzo.ai 404s the whole
prefix. A round-trip on keys could not run.
tools to GET /v1/tools, the catalog. A live JSON-RPC door at POST /v1/mcp
answers tools/list with 730 tools but is absent from hanzo.yaml, so the
generator emits no method and an example would have to bypass the SDK
to reach it. Of the declared MCP routes /v1/automations/mcp returns 405.
hello stays on bot_authMe: /v1/ai/account answers 200 with
type="anonymous-user" to a request with NO Authorization header, so a hello
built on it certifies a key that would 401 everywhere else.
All six run and report the server's own refusal for a bogus key:
hello 403 no validated principal store 403 X-Org-Id required
chat 401 API key validation failed agent 403 X-Org-Id required
money 401 sign in to view billing tools 403 a validated principal required
money goes through the generated *_without_preload_content variant, because its
two operations are declared with a `default` response and no content so the
typed methods return None though the server sends JSON — a spec gap, and not a
small one: 696 of 2425 operations model no response body. That raw variant does
NOT raise on 4xx (the check lives in the typed deserialization these lack), so
the example checks status itself; without it a 401 body printed as the balance.
3.1.5 -> 3.1.6. 3.1.5 is live on PyPI, so this is a real patch above it.
Co-authored-by: Hanzo Dev <[email protected]>
564 lines
14 KiB
TOML
564 lines
14 KiB
TOML
[project]
|
|
name = "hanzoai"
|
|
version = "3.1.6"
|
|
description = "The official Python library for the Hanzo API"
|
|
dynamic = ["readme"]
|
|
license = "BSD-3-Clause"
|
|
authors = [
|
|
{ name = "Hanzo", email = "[email protected]" },
|
|
]
|
|
dependencies = [
|
|
# Runtime deps for the openapi-generator urllib3 client. All pure-Python.
|
|
"urllib3>=2.1.0, <3",
|
|
"python-dateutil>=2.8.2",
|
|
# Bump pydantic floor so we land on a pydantic-core (≥2.27) that ships cp313t wheels.
|
|
"pydantic>=2.10, <3",
|
|
"typing-extensions>=4.10, <5",
|
|
]
|
|
requires-python = ">=3.12"
|
|
classifiers = [
|
|
"Typing :: Typed",
|
|
"Intended Audience :: Developers",
|
|
"Programming Language :: Python :: 3.12",
|
|
"Programming Language :: Python :: 3.13",
|
|
"Operating System :: OS Independent",
|
|
"Operating System :: POSIX",
|
|
"Operating System :: MacOS",
|
|
"Operating System :: POSIX :: Linux",
|
|
"Operating System :: Microsoft :: Windows",
|
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
"License :: OSI Approved :: BSD License"
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
llm = ["hanzo-llm>=1.0.0"]
|
|
# Optional ZAP-native binary transport (hanzoai.zap). Off by default; opt in via
|
|
# `hanzoai.zap_http_client(...)`. hanzo-zap pulls in zap-proto for the wire codec.
|
|
zap = ["hanzo-zap>=0.7.0", "httpx>=0.27.0"]
|
|
|
|
[project.urls]
|
|
Homepage = "https://github.com/hanzoai/python-sdk"
|
|
Repository = "https://github.com/hanzoai/python-sdk"
|
|
|
|
[tool.rye]
|
|
managed = true
|
|
# version pins are in requirements-dev.lock
|
|
dev-dependencies = [
|
|
"pyright>=1.1.359",
|
|
"mypy",
|
|
"respx",
|
|
"pytest",
|
|
"pytest-asyncio>=0.26.0,<1.0.0",
|
|
"ruff",
|
|
"time-machine",
|
|
"nox",
|
|
"dirty-equals>=0.6.0",
|
|
"importlib-metadata>=6.7.0",
|
|
"rich>=13.7.1",
|
|
"nest_asyncio==1.6.0",
|
|
# hanzo-memory test dependencies
|
|
"polars>=1.0.0",
|
|
"httpx>=0.27.0",
|
|
"fastapi>=0.115.0",
|
|
"fastembed>=0.4.0",
|
|
]
|
|
|
|
[tool.rye.workspace]
|
|
members = [
|
|
"pkg/hanzo",
|
|
"pkg/hanzo-aci",
|
|
"pkg/hanzo-mcp",
|
|
"pkg/hanzo-memory",
|
|
"pkg/hanzo-network",
|
|
"pkg/hanzo-dev-py",
|
|
"pkg/hanzo-flags",
|
|
"pkg/hanzo-s3",
|
|
"pkg/hanzo-train",
|
|
"pkg/hanzo-tools-ui",
|
|
"pkg/hanzo-zap",
|
|
"pkg/hanzoai",
|
|
]
|
|
|
|
[tool.rye.scripts]
|
|
format = { chain = [
|
|
"format:ruff",
|
|
"format:docs",
|
|
"fix:ruff",
|
|
# run formatting again to fix any inconsistencies when imports are stripped
|
|
"format:ruff",
|
|
]}
|
|
"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md"
|
|
"format:ruff" = "ruff format"
|
|
|
|
"lint" = { chain = [
|
|
"check:ruff",
|
|
"typecheck",
|
|
"check:importable",
|
|
]}
|
|
"check:ruff" = "ruff check ."
|
|
"fix:ruff" = "ruff check --fix ."
|
|
|
|
"check:importable" = "python -c 'import hanzoai'"
|
|
|
|
typecheck = { chain = [
|
|
"typecheck:pyright",
|
|
"typecheck:mypy"
|
|
]}
|
|
"typecheck:pyright" = "pyright"
|
|
"typecheck:verify-types" = "pyright --verifytypes hanzoai --ignoreexternal"
|
|
"typecheck:mypy" = "mypy pkg/hanzoai"
|
|
|
|
[build-system]
|
|
requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"build>=1.2.2.post1",
|
|
"dirty-equals>=0.6.0",
|
|
"fastapi>=0.128.0",
|
|
"fastembed>=0.7.4",
|
|
"hanzo-memory",
|
|
"httpx>=0.28.1",
|
|
"importlib-metadata>=6.7.0",
|
|
"mypy",
|
|
"nest-asyncio==1.6.0",
|
|
"nox",
|
|
"polars>=1.36.1",
|
|
"pyright>=1.1.359",
|
|
"pytest>=8.4.2",
|
|
"pytest-asyncio>=0.26.0,<1.0.0",
|
|
"respx",
|
|
"rich>=13.7.1",
|
|
"ruff",
|
|
"time-machine",
|
|
]
|
|
|
|
[tool.hatch.build]
|
|
include = [
|
|
"pkg/hanzoai"
|
|
]
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["pkg/hanzoai"]
|
|
|
|
[tool.hatch.build.targets.sdist]
|
|
# Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc)
|
|
include = [
|
|
"/*.toml",
|
|
"/*.json",
|
|
"/*.lock",
|
|
"/*.md",
|
|
"/mypy.ini",
|
|
"/noxfile.py",
|
|
"bin/*",
|
|
"examples/*",
|
|
"pkg/hanzoai/*",
|
|
"tests/*",
|
|
]
|
|
|
|
[tool.hatch.metadata.hooks.fancy-pypi-readme]
|
|
content-type = "text/markdown"
|
|
|
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
|
|
path = "README.md"
|
|
|
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
|
|
# replace relative links with absolute links
|
|
pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)'
|
|
replacement = '[\1](https://github.com/hanzoai/python-sdk/tree/main/\g<2>)'
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests"]
|
|
addopts = "--tb=short"
|
|
xfail_strict = true
|
|
asyncio_mode = "auto"
|
|
asyncio_default_fixture_loop_scope = "function"
|
|
markers = [
|
|
"integration: marks tests as integration tests (deselect with '-m \"not integration\"')"
|
|
]
|
|
filterwarnings = [
|
|
"error",
|
|
"ignore:OpenTelemetry configuration.*is not supported by Datadog:UserWarning",
|
|
"ignore::DeprecationWarning:ddtrace.*",
|
|
"ignore:Support for class-based `config` is deprecated:pydantic.warnings.PydanticDeprecatedSince20",
|
|
"ignore:hanzo-tools-core is deprecated:DeprecationWarning"
|
|
]
|
|
|
|
[tool.pyright]
|
|
# Use basic mode — strict mode with 22+ disabled checks is contradictory.
|
|
# SDK has many optional dependencies and generated code that cannot be
|
|
# fully type-checked in strict mode.
|
|
typeCheckingMode = "basic"
|
|
pythonVersion = "3.12"
|
|
|
|
exclude = [
|
|
"_dev",
|
|
".venv",
|
|
".nox",
|
|
# All subpackages have their own linting/type checking
|
|
"pkg/hanzo",
|
|
"pkg/hanzo-aci",
|
|
"pkg/hanzo-agent",
|
|
"pkg/hanzo-agents",
|
|
"pkg/hanzo-async",
|
|
"pkg/hanzo-cli",
|
|
"pkg/hanzo-consensus",
|
|
"pkg/hanzo-iam",
|
|
"pkg/hanzo-kms",
|
|
"pkg/hanzo-mcp",
|
|
"pkg/hanzo-memory",
|
|
"pkg/hanzo-network",
|
|
"pkg/hanzo-node",
|
|
"pkg/hanzo-dev-py",
|
|
"pkg/hanzo-train",
|
|
"pkg/hanzo-tools",
|
|
"pkg/hanzo-tools-agent",
|
|
"pkg/hanzo-tools-api",
|
|
"pkg/hanzo-tools-browser",
|
|
"pkg/hanzo-tools-code",
|
|
"pkg/hanzo-tools-computer",
|
|
"pkg/hanzo-tools-config",
|
|
"pkg/hanzo-tools-core",
|
|
"pkg/hanzo-tools-database",
|
|
"pkg/hanzo-tools-editor",
|
|
"pkg/hanzo-tools-fs",
|
|
"pkg/hanzo-tools-ide",
|
|
"pkg/hanzo-tools-jupyter",
|
|
"pkg/hanzo-tools-llm",
|
|
"pkg/hanzo-tools-lsp",
|
|
"pkg/hanzo-tools-mcp",
|
|
"pkg/hanzo-tools-memory",
|
|
"pkg/hanzo-tools-net",
|
|
"pkg/hanzo-tools-plan",
|
|
"pkg/hanzo-tools-reasoning",
|
|
"pkg/hanzo-tools-refactor",
|
|
"pkg/hanzo-tools-repl",
|
|
"pkg/hanzo-tools-shell",
|
|
"pkg/hanzo-tools-test",
|
|
"pkg/hanzo-tools-todo",
|
|
"pkg/hanzo-tools-ui",
|
|
"pkg/hanzo-tools-vcs",
|
|
"pkg/hanzo-tools-vector",
|
|
"pkg/hanzo-web3",
|
|
# Non-production code
|
|
"bin",
|
|
"examples",
|
|
"tests",
|
|
"scripts",
|
|
]
|
|
|
|
reportImportCycles = false
|
|
reportPrivateUsage = false
|
|
|
|
# Relax strict checks for this SDK with many optional dependencies
|
|
reportMissingTypeStubs = false
|
|
reportUnknownVariableType = false
|
|
reportUnknownMemberType = false
|
|
reportUnknownParameterType = false
|
|
reportUnknownArgumentType = false
|
|
reportMissingParameterType = false
|
|
reportUnusedImport = "warning"
|
|
reportIncompatibleMethodOverride = false
|
|
reportMissingImports = false
|
|
reportConstantRedefinition = false
|
|
reportMissingTypeArgument = false
|
|
reportArgumentType = false
|
|
reportReturnType = false
|
|
reportIndexIssue = false
|
|
reportUnnecessaryContains = false
|
|
reportAttributeAccessIssue = false
|
|
reportCallIssue = false
|
|
reportOptionalMemberAccess = false
|
|
reportGeneralTypeIssues = false
|
|
reportImplicitOverride = false
|
|
reportAssignmentType = false
|
|
reportOptionalIterable = false
|
|
|
|
[tool.ruff]
|
|
line-length = 120
|
|
output-format = "grouped"
|
|
target-version = "py312"
|
|
exclude = [
|
|
"pkg/hanzo-agent", # submodule - linted separately
|
|
"pkg/hanzo-memory", # needs careful review - has complex redefinitions
|
|
]
|
|
|
|
[tool.ruff.format]
|
|
docstring-code-format = true
|
|
|
|
[tool.ruff.lint]
|
|
select = [
|
|
# isort
|
|
"I",
|
|
# bugbear rules
|
|
"B",
|
|
# remove unused imports
|
|
"F401",
|
|
# bare except statements
|
|
"E722",
|
|
# unused arguments
|
|
"ARG",
|
|
# misuse of typing.TYPE_CHECKING
|
|
# import rules
|
|
"TID251",
|
|
# docstring issues
|
|
"D",
|
|
# logging format
|
|
"G",
|
|
# security
|
|
"S",
|
|
# module imports
|
|
"E402",
|
|
# star imports
|
|
"F403",
|
|
# undefined names
|
|
"F821",
|
|
]
|
|
ignore = [
|
|
# mutable defaults
|
|
"B006",
|
|
# Unused function/method arguments - OK for protocol methods
|
|
"ARG001",
|
|
"ARG002",
|
|
"ARG003",
|
|
"ARG004",
|
|
"ARG005",
|
|
# Docstring formatting
|
|
"D205",
|
|
"D415",
|
|
# Line too long - handled by formatter
|
|
"E501",
|
|
# Ambiguous variable name
|
|
"E741",
|
|
# subprocess call - OK for CLI tools
|
|
"S603",
|
|
# Empty method in abstract base class
|
|
"B027",
|
|
# print statements - OK in examples and CLI tools
|
|
"T201",
|
|
"T203",
|
|
# Unused imports - we'll fix these manually
|
|
"F401",
|
|
# Undefined name - we'll fix these manually
|
|
"F821",
|
|
# raise without from inside except
|
|
"B904",
|
|
# logging format issues - OK for now
|
|
"G001",
|
|
"G002",
|
|
"G003",
|
|
# Unused loop control variable
|
|
"B007",
|
|
# subprocess without shell validation - OK for our use
|
|
"S605",
|
|
# try-except-pass - OK for optional imports
|
|
"S110",
|
|
# Missing docstrings - too many to fix now
|
|
"D100",
|
|
"D101",
|
|
"D102",
|
|
"D103",
|
|
"D104",
|
|
"D105",
|
|
"D106",
|
|
"D107",
|
|
# Other docstring issues
|
|
"D200",
|
|
"D201",
|
|
"D202",
|
|
"D203",
|
|
"D204",
|
|
"D206",
|
|
"D207",
|
|
"D208",
|
|
"D209",
|
|
"D210",
|
|
"D211",
|
|
"D212",
|
|
"D213",
|
|
"D214",
|
|
"D215",
|
|
"D300",
|
|
"D301",
|
|
"D400",
|
|
"D401",
|
|
"D402",
|
|
"D403",
|
|
"D404",
|
|
"D405",
|
|
"D406",
|
|
"D407",
|
|
"D408",
|
|
"D409",
|
|
"D410",
|
|
"D411",
|
|
"D412",
|
|
"D413",
|
|
"D414",
|
|
"D416",
|
|
"D417",
|
|
# Module level import not at top
|
|
"E402",
|
|
# Star imports
|
|
"F403",
|
|
# Loop variable binding
|
|
"B023",
|
|
# Use single if instead of nested - sometimes nested is clearer
|
|
"SIM102",
|
|
# Use of assert - OK in tests
|
|
"S101",
|
|
# Logging with f-string - performance is not critical
|
|
"G004",
|
|
# Starting process with partial path - OK for our use
|
|
"S607",
|
|
# Hardcoded temp directory - OK for tests
|
|
"S108",
|
|
# Popen without shell - OK
|
|
"S602",
|
|
# Starting process with shell - already handled
|
|
"S608",
|
|
# Use of random - OK for non-crypto use
|
|
"S311",
|
|
# Try-except-continue - OK
|
|
"S112",
|
|
# Insecure hash function - OK for non-crypto use
|
|
"S324",
|
|
# Request without timeout - OK for some cases
|
|
"S113",
|
|
# Hardcoded password - only in tests
|
|
"S106",
|
|
# os.system - OK for some CLI tools
|
|
"S606",
|
|
# eval - used safely in specific contexts
|
|
"S307",
|
|
# Hardcoded bind address - OK for examples
|
|
"S105",
|
|
# Use of exec - OK for specific use cases
|
|
"S102",
|
|
# Exception info in logging - OK
|
|
"G201",
|
|
]
|
|
unfixable = [
|
|
# disable auto fix for print statements
|
|
"T201",
|
|
"T203",
|
|
]
|
|
|
|
[tool.ruff.lint.flake8-tidy-imports.banned-api]
|
|
"functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead"
|
|
|
|
[tool.ruff.lint.isort]
|
|
length-sort = true
|
|
length-sort-straight = true
|
|
combine-as-imports = true
|
|
extra-standard-library = ["typing_extensions"]
|
|
known-first-party = ["hanzoai", "tests"]
|
|
|
|
[tool.mypy]
|
|
python_version = "3.12"
|
|
ignore_missing_imports = true
|
|
# Relax type checking - SDK has many optional dependencies and dynamic imports
|
|
check_untyped_defs = false
|
|
disallow_untyped_defs = false
|
|
disallow_untyped_calls = false
|
|
disallow_incomplete_defs = false
|
|
warn_return_any = false
|
|
warn_unused_ignores = false
|
|
exclude = [
|
|
# Subpackages - have their own linting/type checking
|
|
"pkg/hanzo-agent/",
|
|
"pkg/hanzo-aci/",
|
|
"pkg/hanzo-mcp/",
|
|
"pkg/hanzo-memory/",
|
|
"pkg/hanzo-network/",
|
|
"pkg/hanzo-dev-py/",
|
|
"pkg/hanzo/",
|
|
# Non-production code
|
|
"bin/",
|
|
"examples/",
|
|
"scripts/",
|
|
"tests/",
|
|
]
|
|
|
|
[tool.uv.workspace]
|
|
members = [
|
|
"pkg/hanzo-memory",
|
|
"pkg/hanzo-s3",
|
|
"pkg/hanzo-train",
|
|
"pkg/hanzo-tools",
|
|
"pkg/hanzo-tools-core",
|
|
# Required tool packages — the hanzo-tools-core integration suite asserts each
|
|
# imports with an exact TOOLS count; they must be workspace members so
|
|
# `uv sync` installs the real toolset (never a stale/absent wheel).
|
|
"pkg/hanzo-tools-fs",
|
|
"pkg/hanzo-tools-shell",
|
|
"pkg/hanzo-tools-agent",
|
|
"pkg/hanzo-tools-reasoning",
|
|
"pkg/hanzo-tools-lsp",
|
|
"pkg/hanzo-tools-refactor",
|
|
"pkg/hanzo-tools-todo",
|
|
"pkg/hanzo-tools-jupyter",
|
|
"pkg/hanzo-tools-memory",
|
|
"pkg/hanzo-tools-ui",
|
|
"pkg/hanzo-tools-browser",
|
|
"pkg/hanzo-cli",
|
|
"pkg/hanzo-iam",
|
|
"pkg/hanzo-kms",
|
|
"pkg/hanzo-tools-s3",
|
|
]
|
|
|
|
[tool.uv.sources]
|
|
hanzo-memory = { workspace = true }
|
|
# Canonical hanzo_tools.core (BaseTool, ToolImage, _result_to_mcp) lives in
|
|
# hanzo-tools; hanzo-tools-core is the EMPTY shim (0.3.1+). Pin BOTH to the on-disk
|
|
# source, never PyPI — the published hanzo-tools-core 0.3.0 still ships the duplicate
|
|
# hanzo_tools/core/ that shadows ToolImage and breaks the browser tool.
|
|
hanzo-tools = { workspace = true }
|
|
hanzo-tools-core = { workspace = true }
|
|
# Required tool packages — on-disk source so `uv sync` installs the real toolset
|
|
# and the hanzo-tools-core integration suite validates against live code.
|
|
hanzo-tools-fs = { workspace = true }
|
|
hanzo-tools-shell = { workspace = true }
|
|
hanzo-tools-agent = { workspace = true }
|
|
hanzo-tools-reasoning = { workspace = true }
|
|
hanzo-tools-lsp = { workspace = true }
|
|
hanzo-tools-refactor = { workspace = true }
|
|
hanzo-tools-todo = { workspace = true }
|
|
hanzo-tools-jupyter = { workspace = true }
|
|
hanzo-tools-memory = { workspace = true }
|
|
hanzo-tools-ui = { workspace = true }
|
|
# Browser tool routes through the local zapd router; the on-disk source is the
|
|
# single truth (not a published wheel) so the MCP and the repo never diverge.
|
|
hanzo-tools-browser = { workspace = true }
|
|
# Unified CLI + its service clients resolve to the on-disk source, never a
|
|
# stale published wheel — one truth for the control surface.
|
|
hanzo-cli = { workspace = true }
|
|
hanzo-iam = { workspace = true }
|
|
hanzo-kms = { workspace = true }
|
|
hanzo-s3 = { workspace = true }
|
|
hanzo-train = { workspace = true }
|
|
hanzo-tools-s3 = { workspace = true }
|
|
|
|
[tool.ruff.lint.extend-per-file-ignores]
|
|
# Allow print statements in scripts and examples
|
|
"bin/**.py" = ["T201", "T203"]
|
|
"scripts/**.py" = ["T201", "T203", "E722"]
|
|
"tests/**.py" = ["T201", "T203", "F401", "F841"]
|
|
"examples/**.py" = ["T201", "T203", "F841"]
|
|
"**/examples/**.py" = ["T201", "T203", "F841"]
|
|
"pkg/**/examples/**.py" = ["T201", "T203", "F841"]
|
|
# CLI tools can use print
|
|
"**/cli*.py" = ["T201", "T203"]
|
|
"**/__main__.py" = ["T201", "T203"]
|
|
# Specific file ignores
|
|
"pkg/hanzo-memory/src/hanzo_memory/db/base.py" = ["B027"]
|
|
"pkg/hanzo-dev-py/src/hanzo_dev/backends.py" = ["S603"]
|
|
"pkg/hanzo-dev-py/src/hanzo_dev/repl.py" = ["S605"]
|
|
"pkg/hanzo-dev-py/src/hanzo_dev/tests.py" = ["ARG001"]
|
|
"pkg/hanzo-dev-py/src/hanzo_dev/ipython_repl.py" = ["ARG002"]
|
|
"**/cli.py" = ["T201", "T203"]
|
|
# Test files
|
|
"**/test_*.py" = ["F401", "F841"]
|
|
"**/tests/**.py" = ["F401", "F841"]
|