Official Python client for the Hawk daemon API
The Hawk SDK for Python is the official client for interacting with the Hawk daemon API. It provides an idiomatic Python interface for chat, streaming, sessions, stats, and agent orchestration. Both synchronous and async clients are provided, with the same API surface on each.
- Dual client pattern -
HawkClient(sync) andAsyncHawkClientwith identical method signatures - Streaming - SSE-based real-time chat streaming via
StreamReader/AsyncStreamReader - Type safety - Pydantic v2 models with full type hints (Python 3.9+)
- Context managers -
with HawkClient() as client:for automatic resource cleanup - Typed errors - Status-code-based exception hierarchy (
AuthenticationError,NotFoundError,RateLimitError, etc.) - Retry with backoff - Configurable exponential backoff with jitter via
RetryConfig - Minimal dependencies -
httpx(HTTP),pydanticv2, andeval-type-backport(Python < 3.10)
pip install hawk-sdkfrom hawk import HawkClient
with HawkClient() as client:
# Health check
health = client.health()
print(f"Status: {health.status}, Version: {health.version}")
print(f"Active sessions: {health.active_sessions}")
# Chat
response = client.chat("Explain decorators in Python")
print(response.response)
print(f"Tokens: in={response.tokens_in}, out={response.tokens_out}")import asyncio
from hawk import AsyncHawkClient
async def main():
async with AsyncHawkClient() as client:
health = await client.health()
print(f"Status: {health.status}, Version: {health.version}")
response = await client.chat("Hello!")
print(response.response)
asyncio.run(main())from hawk import HawkClient
with HawkClient() as client:
with client.chat_stream("Write a haiku about coding") as stream:
for event in stream.events():
if event.event is None or event.event == "content":
print(event.data, end="", flush=True)
print()
# Or collect all text at once
with client.chat_stream("Write a haiku about coding") as stream:
text = stream.collect_text()
print(f"Full response: {text}")
# Or collect tool calls
with client.chat_stream("What tools do I have?", tools=[{"type": "list"}]) as stream:
calls = stream.collect_tool_calls()
for call in calls:
print(f"Tool: {call.name}({call.arguments})")Both clients share the same constructor and method signatures.
HawkClient(
base_url: str = "http://127.0.0.1:4590",
api_key: str | None = None,
retry_config: RetryConfig | None = None,
timeout: float = 30.0,
pool_connections: int = 10,
pool_maxsize: int = 100,
)| Parameter | Default | Description |
|---|---|---|
base_url |
http://127.0.0.1:4590 |
Base URL of the Hawk daemon |
api_key |
None |
Bearer token for authenticated requests |
retry_config |
DEFAULT_RETRY_CONFIG |
Retry configuration (429, 5xx, etc.) |
timeout |
30.0 |
HTTP request timeout in seconds |
pool_connections |
10 |
Max keep-alive connections |
pool_maxsize |
100 |
Max total connections |
# Health
health() -> HealthResponse
# Chat
chat(
prompt: str,
session_id: str | None = None,
model: str | None = None,
max_turns: int | None = None,
autonomy: str | None = None,
cwd: str | None = None,
agent: str | None = None,
tools: list[dict[str, Any]] | None = None,
tool_results: list[ToolResult] | None = None,
) -> ChatResponse
# Streaming chat
chat_stream(
prompt: str,
session_id: str | None = None,
model: str | None = None,
max_turns: int | None = None,
autonomy: str | None = None,
cwd: str | None = None,
agent: str | None = None,
tools: list[dict[str, Any]] | None = None,
tool_results: list[ToolResult] | None = None,
) -> StreamReader # or AsyncStreamReader
# Session management
get_session(session_id: str) -> SessionDetail
list_sessions() -> list[SessionSummary]
delete_session(session_id: str) -> None
# Messages with pagination
list_messages(
session_id: str,
limit: int = 50,
offset: int = 0,
) -> PaginatedResponse[Message]
# Stats
stats() -> StatsResponseAll API errors inherit from HawkAPIError. Catch specific subclasses:
from hawk import HawkClient, HawkAPIError, RateLimitError, AuthenticationError
with HawkClient() as client:
try:
response = client.chat("Hello")
except AuthenticationError:
print("Invalid API key")
except RateLimitError as e:
print(f"Rate limited, retry after {e.retry_after}s")
except HawkAPIError as e:
print(f"API error {e.status_code}: {e.message}")Error classes: BadRequestError (400), AuthenticationError (401), ForbiddenError (403),
NotFoundError (404), RateLimitError (429), InternalServerError (500),
ServiceUnavailableError (503).
Every public method wraps its logic in a retry closure that automatically:
- Respects
Retry-Afterheaders on 429 responses - Uses exponential backoff with jitter for retryable statuses (429, 500, 502, 503, 504)
- Raises typed exceptions matching the HTTP status code
hawk-sdk-python is a consumer of Hawk public APIs and contracts. It does not
import from engine repos such as eyrie, yaad, tok, trace, sight, or
inspect. If a capability is needed across repos, it should be exposed through
Hawk or hawk-core-contracts, not through engine internals.
Contributions are welcome — please read CONTRIBUTING.md before opening a pull request.
MIT - see LICENSE for details.