Skip to content

Commit 8cfbba2

Browse files
authored
feat(hawk-sdk-python): production hardening (#1)
* feat(hawk-sdk-python): production hardening — ruff, mypy, version bump - Added ruff linter config (E, F, W, I, N, UP, B, A, SIM, TCH, RUF rules) - Added mypy strict type checking config - Added Makefile with standard targets (test, lint, format, typecheck) - Bumped version to 0.2.0 - Added pytest strict markers and short traceback config * feat(hawk-sdk-python): align __version__ with pyproject, add User-Agent, full OSS bootstrap The prior hardening commit bumped pyproject.toml to 0.2.0 but missed `src/hawk/_version.py`, which still reported 0.1.0. This commit fixes that inconsistency and lands the OSS standard files that were missing. Version surface (fixed + added): - src/hawk/_version.py — `__version__ = "0.2.0"` (was 0.1.0). pyproject.toml was already 0.2.0. - src/hawk/client.py — both `HawkClient._build_headers` and `AsyncHawkClient._build_headers` now set `User-Agent: hawk-sdk-python/<__version__>`. httpx merges client-default headers with per-request overrides, so this also covers the streaming endpoint without changing the per-request `headers={"Accept": "text/event-stream"}` override. New OSS files (this is the first PR to add them): - CHANGELOG.md — Keep-a-Changelog format with [Unreleased] capturing this PR + a backfilled [0.1.0] entry for the initial SDK and prior hardening pass. - CONTRIBUTING.md — quick start with venv + editable install, branch flow (this repo branches from main), conventional commits, code standards (mypy --strict, ruff, async-first, Pydantic v2, User-Agent rule), testing with respx, and the bump-both-version-files procedure. - SECURITY.md — vulnerability reporting via GitHub Security Advisories, in-scope examples (token leakage, TLS misuse, Pydantic deserialization issues, redirect host escape), out-of-scope pointers (daemon issues to hawk repo, third-party-package issues upstream). - CODE_OF_CONDUCT.md — Contributor Covenant 2.1. - .gitattributes — LF normalization, binary detection, linguist hints to collapse lock files. - .editorconfig — UTF-8, LF, 4-space indent for Python (PEP 8), 2-space for YAML/JSON/TOML, no-trim for Markdown. - .github/workflows/ci.yml — pytest matrix on Python 3.9 / 3.10 / 3.11 / 3.12 / 3.13, ruff (lint + format check), mypy --strict, build sdist + wheel + twine check. - .github/dependabot.yml — weekly pip + github-actions, pip grouped by pydantic and pytest to reduce PR noise. - .github/PULL_REQUEST_TEMPLATE.md — Summary / Changes / API impact (with bump-both-files reminder) / Daemon compatibility / Async compatibility (sync + async kept in lock-step) / Testing / Checklist (incl. User-Agent rule). - .github/ISSUE_TEMPLATE/bug_report.yml — surface dropdown (HawkClient / AsyncHawkClient / streaming / retry / tools / agent / workflow / typed errors / build), required SDK + daemon + Python versions, package-versions textarea. - .github/ISSUE_TEMPLATE/feature_request.yml — kind selector covering 9 areas (client method / streaming / retry / errors / tools / agent-workflow / pydantic / config / tooling) and solo-dev fit checks (incl. 'sync and async kept in lock-step', 'does not break wire-compatibility with existing daemon versions'). - .github/ISSUE_TEMPLATE/config.yml — routes security to advisories, questions to discussions, blocks blank issues. - .gitignore — expanded from 6 lines to cover the broader Python toolchain (venv dirs, .mypy_cache, .ruff_cache, .pytest_cache, .tox, .nox, htmlcov, coverage.xml, .env). Verification: - `pytest` — 65/65 pass (1 pre-existing cosmetic warning in test_workflow.py about an unawaited coroutine in a mock; not introduced by this PR) - `hawk.__version__` returns "0.2.0" - `HawkClient._build_headers()` returns {'Accept': 'application/json', 'User-Agent': 'hawk-sdk-python/0.2.0'} * chore: standardize eco-wide infra (versioning, CI, hooks, templates) - VERSION file as single source of truth - CODEOWNERS for auto-review routing - Canonical Makefile with standard targets - release-please config + workflow - lefthook/pre-commit hooks (conventional commits, fmt, lint, secrets) - Canonical CI + release GitHub Actions workflows - Standardized .editorconfig, .gitattributes, CODE_OF_CONDUCT, SECURITY, CONTRIBUTING - goreleaser config (where applicable) Part of hawk-eco standardization sweep. * feat(hawk-sdk-python): adopt agentscope patterns — toolkit, plan, tracing, eval, discovery, memory Add 6 new modules inspired by agentscope-ai/agentscope: - toolkit: tool groups, middleware chain, async background execution - plan: plan-as-tools with contextual hints for autonomous steering - tracing: OTel decorator-based tracing (zero-cost when disabled) - evaluate: agent benchmarking framework with N-run aggregation - discovery: A2A agent discovery (file, HTTP well-known, composite) - memory_tools: voluntary record/retrieve/forget as agent tools
1 parent c76a472 commit 8cfbba2

31 files changed

Lines changed: 2912 additions & 8 deletions

.editorconfig

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# EditorConfig — https://editorconfig.org
2+
# Canonical eco-wide template (.shared-templates/editorconfig.tmpl).
3+
4+
root = true
5+
6+
# Default for everything.
7+
[*]
8+
charset = utf-8
9+
end_of_line = lf
10+
insert_final_newline = true
11+
trim_trailing_whitespace = true
12+
indent_style = space
13+
indent_size = 4
14+
15+
# Go uses tabs by convention.
16+
[*.go]
17+
indent_style = tab
18+
indent_size = 4
19+
20+
# Python — PEP 8.
21+
[*.py]
22+
indent_size = 4
23+
24+
# TypeScript / JavaScript — 2 spaces, ecosystem default.
25+
[*.{ts,tsx,js,jsx,mjs,cjs}]
26+
indent_size = 2
27+
28+
# Web assets.
29+
[*.{html,css,scss}]
30+
indent_size = 2
31+
32+
# YAML — 2 spaces (ecosystem standard, GitHub Actions, k8s, etc.).
33+
[*.{yml,yaml}]
34+
indent_size = 2
35+
36+
# JSON / JSONC.
37+
[*.{json,jsonc}]
38+
indent_size = 2
39+
40+
# TOML.
41+
[*.toml]
42+
indent_size = 2
43+
44+
# Markdown — 2 spaces, preserve trailing whitespace (used for line breaks).
45+
[*.md]
46+
trim_trailing_whitespace = false
47+
indent_size = 2
48+
49+
# Shell scripts.
50+
[*.{sh,bash,zsh,fish}]
51+
indent_size = 4
52+
53+
# Makefiles must use tabs.
54+
[{Makefile,*.mk}]
55+
indent_style = tab
56+
57+
# Dockerfiles.
58+
[Dockerfile*]
59+
indent_size = 4
60+
61+
# GitHub Actions workflows — 2 spaces.
62+
[.github/**/*.{yml,yaml}]
63+
indent_size = 2
64+
65+
# Config files.
66+
[*.{cfg,ini,conf}]
67+
indent_size = 4

.gitattributes

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Canonical eco-wide .gitattributes template (.shared-templates/gitattributes.tmpl).
2+
# Auto-detect text files and normalise line endings to LF.
3+
4+
* text=auto eol=lf
5+
6+
# --- Source code -----------------------------------------------------------
7+
*.go text eol=lf diff=golang
8+
*.py text eol=lf diff=python
9+
*.ts text eol=lf
10+
*.tsx text eol=lf
11+
*.js text eol=lf
12+
*.jsx text eol=lf
13+
*.mjs text eol=lf
14+
*.cjs text eol=lf
15+
*.rs text eol=lf diff=rust
16+
17+
# --- Shell + config --------------------------------------------------------
18+
*.sh text eol=lf
19+
*.bash text eol=lf
20+
*.toml text eol=lf
21+
*.yaml text eol=lf
22+
*.yml text eol=lf
23+
*.json text eol=lf linguist-language=JSON
24+
*.jsonc text eol=lf linguist-language=JSON
25+
*.cff text eol=lf
26+
27+
# --- Documentation ---------------------------------------------------------
28+
*.md text eol=lf diff=markdown
29+
*.txt text eol=lf
30+
31+
# --- Build / packaging ----------------------------------------------------
32+
Makefile text eol=lf
33+
*.mk text eol=lf
34+
Dockerfile* text eol=lf
35+
docker-compose*.yml text eol=lf
36+
.github/**/*.yml text eol=lf
37+
.github/**/*.yaml text eol=lf
38+
39+
# --- Generated artefacts (mark as such for diffs and language stats) ------
40+
go.mod text eol=lf linguist-generated
41+
go.sum text eol=lf linguist-generated
42+
*.pb.go linguist-generated
43+
*_generated.go linguist-generated
44+
package-lock.json linguist-generated
45+
pnpm-lock.yaml linguist-generated
46+
yarn.lock linguist-generated
47+
48+
# --- Vendored / external sources ------------------------------------------
49+
vendor/** linguist-vendored
50+
node_modules/** linguist-vendored
51+
testdata/** linguist-vendored
52+
benchmarks/data/** linguist-vendored
53+
54+
# --- Binary files (do not text-normalise) ---------------------------------
55+
*.exe binary
56+
*.dll binary
57+
*.so binary
58+
*.dylib binary
59+
*.a binary
60+
*.o binary
61+
*.db binary
62+
*.sqlite binary
63+
*.png binary
64+
*.jpg binary
65+
*.jpeg binary
66+
*.gif binary
67+
*.ico binary
68+
*.svg text eol=lf
69+
*.pdf binary
70+
*.zip binary
71+
*.tar.gz binary
72+
*.tgz binary
73+
*.whl binary
74+
75+
# --- Source archive hygiene (excluded from `git archive`) -----------------
76+
.github export-ignore
77+
.shared-templates export-ignore
78+
.gitattributes export-ignore
79+
.gitignore export-ignore
80+
.editorconfig export-ignore
81+
.golangci.yml export-ignore
82+
.goreleaser.yml export-ignore
83+
.goreleaser.yaml export-ignore
84+
testdata/ export-ignore
85+
benchmarks/ export-ignore
86+
e2e/ export-ignore
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
name: Bug report
2+
description: Something is broken or behaving unexpectedly.
3+
title: "bug: <one-line summary>"
4+
labels: ["bug", "triage"]
5+
6+
body:
7+
- type: markdown
8+
attributes:
9+
value: |
10+
Thanks for taking the time to file a bug report. Please fill in as much
11+
of the form as you can — the more we know, the faster we can fix it.
12+
13+
Before submitting:
14+
- Search [existing issues](https://github.com/GrayCodeAI/hawk-sdk-python/issues) to avoid duplicates.
15+
- If this is a security issue, please **do not** file a public issue. See `SECURITY.md`.
16+
17+
- type: textarea
18+
id: what-happened
19+
attributes:
20+
label: What happened?
21+
description: A clear, concise description of the bug.
22+
placeholder: When I call HawkClient.<method>, I expected X but got Y.
23+
validations:
24+
required: true
25+
26+
- type: dropdown
27+
id: surface
28+
attributes:
29+
label: Surface
30+
description: Which SDK surface is affected?
31+
options:
32+
- "HawkClient (sync)"
33+
- "AsyncHawkClient (async)"
34+
- "Streaming (chat_stream / StreamReader)"
35+
- "Retry / backoff"
36+
- "Tools (chat_with_tools, @tool decorator)"
37+
- "Agent / AsyncAgent"
38+
- "Workflow / AsyncWorkflow"
39+
- "Typed errors (HawkAPIError hierarchy)"
40+
- "Build / packaging"
41+
validations:
42+
required: true
43+
44+
- type: textarea
45+
id: reproduce
46+
attributes:
47+
label: Steps to reproduce
48+
description: Minimal Python snippet that reliably reproduces the problem.
49+
render: python
50+
placeholder: |
51+
from hawk import HawkClient
52+
with HawkClient() as c:
53+
resp = c.chat("hello")
54+
# ^ wrong shape / panic / hang / etc.
55+
validations:
56+
required: true
57+
58+
- type: textarea
59+
id: expected
60+
attributes:
61+
label: Expected behavior
62+
description: What did you expect to happen instead?
63+
validations:
64+
required: true
65+
66+
- type: input
67+
id: sdk-version
68+
attributes:
69+
label: hawk-sdk version
70+
description: Output of `python -c "import hawk; print(hawk.__version__)"`.
71+
placeholder: "0.2.0"
72+
validations:
73+
required: true
74+
75+
- type: input
76+
id: daemon-version
77+
attributes:
78+
label: hawk daemon version
79+
description: Output of `hawk version` (the daemon you're hitting).
80+
placeholder: "0.2.0"
81+
validations:
82+
required: true
83+
84+
- type: input
85+
id: python-version
86+
attributes:
87+
label: Python version
88+
description: Output of `python --version`.
89+
placeholder: "Python 3.11.9"
90+
validations:
91+
required: true
92+
93+
- type: input
94+
id: os
95+
attributes:
96+
label: Operating system
97+
description: e.g. macOS 14.5 (arm64), Ubuntu 24.04 (amd64), Windows 11 (amd64).
98+
placeholder: "macOS 14.5 (arm64)"
99+
validations:
100+
required: true
101+
102+
- type: textarea
103+
id: deps
104+
attributes:
105+
label: Relevant package versions
106+
description: |
107+
Paste the output of `pip freeze | grep -E "^(httpx|pydantic|hawk-sdk)"` (or `uv pip list` equivalent).
108+
render: shell
109+
110+
- type: textarea
111+
id: logs
112+
attributes:
113+
label: Logs / traceback
114+
description: |
115+
Paste any relevant output, including the full traceback.
116+
**Redact API tokens, session IDs, and any private data first.**
117+
render: shell
118+
119+
- type: checkboxes
120+
id: confirm
121+
attributes:
122+
label: Confirmation
123+
options:
124+
- label: I searched existing issues and did not find a duplicate.
125+
required: true
126+
- label: I redacted any secrets, tokens, or private data from logs.
127+
required: true

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
blank_issues_enabled: false
2+
contact_links:
3+
- name: Security vulnerability
4+
url: https://github.com/GrayCodeAI/hawk-sdk-python/security/advisories/new
5+
about: Please report security issues privately via a GitHub Security Advisory. See SECURITY.md.
6+
- name: Question / discussion
7+
url: https://github.com/GrayCodeAI/hawk-sdk-python/discussions
8+
about: Have a question or want to discuss an idea? Open a discussion instead of an issue.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: Feature request
2+
description: Suggest an improvement or a new SDK capability.
3+
title: "feat: <one-line summary>"
4+
labels: ["enhancement", "triage"]
5+
6+
body:
7+
- type: markdown
8+
attributes:
9+
value: |
10+
Thanks for proposing a feature. hawk-sdk is a thin Python client for
11+
the local hawk daemon. Every feature is evaluated against whether it
12+
serves **a single developer** running their own hawk daemon — i.e.
13+
it improves ergonomics, lowers latency, or simplifies integration.
14+
15+
Before submitting:
16+
- Search [existing issues](https://github.com/GrayCodeAI/hawk-sdk-python/issues) to avoid duplicates.
17+
- For new daemon endpoints, the daemon side must land first.
18+
19+
- type: dropdown
20+
id: kind
21+
attributes:
22+
label: Kind of feature
23+
description: What flavour of change is this?
24+
options:
25+
- "New client method (wraps a daemon endpoint)"
26+
- "Streaming / SSE handling"
27+
- "Retry / backoff / resilience"
28+
- "Typed errors / error categories"
29+
- "Tools (chat_with_tools, @tool decorator)"
30+
- "Agent / Workflow orchestration"
31+
- "Pydantic model / type-hint improvement"
32+
- "Configuration (httpx transport, timeouts, etc.)"
33+
- "Tooling / CI / docs / packaging"
34+
validations:
35+
required: true
36+
37+
- type: textarea
38+
id: problem
39+
attributes:
40+
label: What problem are you trying to solve?
41+
description: Describe the user problem first. Solutions can come later.
42+
placeholder: When I call <method>, I have to write boilerplate Y because the SDK doesn't expose X.
43+
validations:
44+
required: true
45+
46+
- type: textarea
47+
id: proposal
48+
attributes:
49+
label: Proposed solution
50+
description: How would you like the SDK to behave? Snippet of API you'd want.
51+
render: python
52+
validations:
53+
required: true
54+
55+
- type: textarea
56+
id: alternatives
57+
attributes:
58+
label: Alternatives considered
59+
description: |
60+
What did you try? What do other SDKs (`openai-python`,
61+
`anthropic-sdk-python`, `langchain`, `llama-index`, `dspy`,
62+
`instructor`, `marvin`, `pydantic-ai`, `mirascope`, `magentic`)
63+
do? Why isn't that enough?
64+
65+
- type: checkboxes
66+
id: principles
67+
attributes:
68+
label: Solo-developer fit
69+
description: hawk-sdk avoids enterprise scope. Confirm this feature respects that.
70+
options:
71+
- label: Works with zero configuration (sensible defaults).
72+
- label: Does not introduce a third-party network dependency.
73+
- label: Does not break wire-compatibility with existing daemon versions.
74+
- label: Sync and async variants are kept in lock-step.
75+
- label: Has an escape hatch (override via parameter, transport, or env).

0 commit comments

Comments
 (0)