h3-harness-sdk 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- h3_harness_sdk-0.1.1/.gitignore +33 -0
- h3_harness_sdk-0.1.1/AGENTS.md +83 -0
- h3_harness_sdk-0.1.1/CHANGELOG.md +17 -0
- h3_harness_sdk-0.1.1/CODEOWNERS +3 -0
- h3_harness_sdk-0.1.1/CODE_OF_CONDUCT.md +33 -0
- h3_harness_sdk-0.1.1/CONTRIBUTING.md +136 -0
- h3_harness_sdk-0.1.1/GOVERNANCE.md +31 -0
- h3_harness_sdk-0.1.1/LICENSE +21 -0
- h3_harness_sdk-0.1.1/Makefile +39 -0
- h3_harness_sdk-0.1.1/PKG-INFO +160 -0
- h3_harness_sdk-0.1.1/README.md +132 -0
- h3_harness_sdk-0.1.1/SECURITY.md +13 -0
- h3_harness_sdk-0.1.1/SUPPORT.md +20 -0
- h3_harness_sdk-0.1.1/docs/api/examples.md +56 -0
- h3_harness_sdk-0.1.1/docs/api/harness.md +150 -0
- h3_harness_sdk-0.1.1/docs/api/index.md +92 -0
- h3_harness_sdk-0.1.1/docs/api/middleware.md +63 -0
- h3_harness_sdk-0.1.1/docs/api/protocol.md +425 -0
- h3_harness_sdk-0.1.1/docs/api/testbed.md +89 -0
- h3_harness_sdk-0.1.1/docs/audit-2026-08.md +33 -0
- h3_harness_sdk-0.1.1/docs/dogfood/2026-08-03-integration.md +190 -0
- h3_harness_sdk-0.1.1/docs/dogfood/diagnostics.md +108 -0
- h3_harness_sdk-0.1.1/pyproject.toml +70 -0
- h3_harness_sdk-0.1.1/scripts/generate-protocol.py +342 -0
- h3_harness_sdk-0.1.1/scripts/serve_echo.py +24 -0
- h3_harness_sdk-0.1.1/src/h3_harness/__init__.py +56 -0
- h3_harness_sdk-0.1.1/src/h3_harness/examples/__init__.py +2 -0
- h3_harness_sdk-0.1.1/src/h3_harness/examples/echo.py +90 -0
- h3_harness_sdk-0.1.1/src/h3_harness/examples/langchain_agent.py +101 -0
- h3_harness_sdk-0.1.1/src/h3_harness/examples/minimal.py +45 -0
- h3_harness_sdk-0.1.1/src/h3_harness/harness.py +227 -0
- h3_harness_sdk-0.1.1/src/h3_harness/middleware.py +71 -0
- h3_harness_sdk-0.1.1/src/h3_harness/protocol.py +308 -0
- h3_harness_sdk-0.1.1/src/h3_harness/testbed.py +115 -0
- h3_harness_sdk-0.1.1/tests/__init__.py +1 -0
- h3_harness_sdk-0.1.1/tests/test_benchmarks.py +260 -0
- h3_harness_sdk-0.1.1/tests/test_example_langchain.py +61 -0
- h3_harness_sdk-0.1.1/tests/test_harness.py +308 -0
- h3_harness_sdk-0.1.1/tests/test_middleware.py +190 -0
- h3_harness_sdk-0.1.1/tests/test_protocol.py +256 -0
- h3_harness_sdk-0.1.1/tests/test_quickstart.py +144 -0
- h3_harness_sdk-0.1.1/tests/test_schema_validation.py +496 -0
- h3_harness_sdk-0.1.1/tests/test_testbed.py +205 -0
- h3_harness_sdk-0.1.1/uv.lock +1369 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.ruff_cache/
|
|
9
|
+
|
|
10
|
+
# Hilo — local cache files (rebuildable)
|
|
11
|
+
.vfs/graph/graph.db
|
|
12
|
+
.vfs/graph/graph.db.wal
|
|
13
|
+
.vfs/graph/.last_warm
|
|
14
|
+
.gitreins/tasks.yaml
|
|
15
|
+
.coverage
|
|
16
|
+
|
|
17
|
+
# Temp audit / inspection scripts (generated by foreman ticks)
|
|
18
|
+
# Anchored to repo root: an unanchored `_*.py` also matched package
|
|
19
|
+
# __init__.py files, which hatchling's gitignore-based wheel file selection
|
|
20
|
+
# then excluded from built wheels (empty namespace package on install).
|
|
21
|
+
/_*.py
|
|
22
|
+
# GitReins verdict history (local)
|
|
23
|
+
.gitreins/history/
|
|
24
|
+
|
|
25
|
+
# pytest-benchmark cache (regenerated on every run)
|
|
26
|
+
.benchmarks/
|
|
27
|
+
|
|
28
|
+
# DuckDB board local cache (rebuildable from Parquet)
|
|
29
|
+
.coding-hermes/board/board.db
|
|
30
|
+
.coding-hermes/board/board.db.wal
|
|
31
|
+
.coding-hermes/board/*.parquet
|
|
32
|
+
|
|
33
|
+
.coding-hermes/board/*.duckdb
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# AGENTS.md — H3 SDK for Python
|
|
2
|
+
|
|
3
|
+
Python SDK for building H3-compliant agent harnesses.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install h3-harness-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Install fallback (source / git)
|
|
12
|
+
|
|
13
|
+
If a release isn't published to PyPI yet (or you want the latest unreleased
|
|
14
|
+
changes), install directly from the repository:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# From git
|
|
18
|
+
pip install git+https://github.com/get-h3/sdk-python.git
|
|
19
|
+
|
|
20
|
+
# Editable source install (development)
|
|
21
|
+
git clone https://github.com/get-h3/sdk-python.git
|
|
22
|
+
cd sdk-python
|
|
23
|
+
pip install -e .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quickstart
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from h3_harness import (
|
|
30
|
+
BaseHarness,
|
|
31
|
+
Decision,
|
|
32
|
+
DecisionType,
|
|
33
|
+
End,
|
|
34
|
+
TextResponse,
|
|
35
|
+
create_router,
|
|
36
|
+
)
|
|
37
|
+
from fastapi import FastAPI
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MyHarness(BaseHarness):
|
|
41
|
+
async def on_process(self, req):
|
|
42
|
+
# Echo conversation history from context (battery: history preserved).
|
|
43
|
+
history = list(req.context.history)
|
|
44
|
+
# Streaming: "do not finish" in message -> unfinished text.
|
|
45
|
+
streaming = "do not finish" in req.message.content
|
|
46
|
+
finished = not streaming
|
|
47
|
+
return Decision(
|
|
48
|
+
decision=DecisionType.TEXT,
|
|
49
|
+
text=TextResponse(
|
|
50
|
+
content=f"Echo: {req.message.content}",
|
|
51
|
+
finished=finished,
|
|
52
|
+
),
|
|
53
|
+
history=history,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
async def on_result(self, req):
|
|
57
|
+
return Decision(decision=DecisionType.END, end=End(reason="task_complete"))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
app = FastAPI()
|
|
61
|
+
app.include_router(create_router(MyHarness()))
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Package Structure
|
|
65
|
+
|
|
66
|
+
- `protocol.py` — Pydantic models (generated from get-h3/protocol JSON Schema)
|
|
67
|
+
- `harness.py` — BaseHarness ABC + FastAPI router
|
|
68
|
+
- `middleware.py` — Request logging middleware via BaseHTTPMiddleware
|
|
69
|
+
- `testbed.py` — MockHermes for pytest
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
- GitReins quality gate mandatory
|
|
74
|
+
- Must pass `h3-test` from get-h3/shim before release
|
|
75
|
+
- To score 43/43 on the battery: echo `context.history` in every Decision,
|
|
76
|
+
never issue `llm_call` when `context.models` is empty, and return
|
|
77
|
+
`text.finished=false` for "do not finish" prompts. See README → *Passing the
|
|
78
|
+
battery (h3-test compliance)*. `src/h3_harness/examples/echo.py` is the
|
|
79
|
+
battery-ready template.
|
|
80
|
+
|
|
81
|
+
## Reference
|
|
82
|
+
|
|
83
|
+
Spec: `get-h3/h3` → `specs/04-SDK-Libraries.md`
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the H3 Python SDK.
|
|
4
|
+
|
|
5
|
+
## [1.0.0] — 2026-07-19
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `protocol.py`: Pydantic models generated from H3 JSON Schema
|
|
9
|
+
- `harness.py`: BaseHarness ABC + FastAPI router
|
|
10
|
+
- `middleware.py`: Request logging middleware
|
|
11
|
+
- `testbed.py`: MockHermes for pytest
|
|
12
|
+
- Echo example harness (examples/echo/)
|
|
13
|
+
- Structured access logging
|
|
14
|
+
- Pydantic defaults for optional fields (max_iterations, session_state)
|
|
15
|
+
- response_model_exclude_none=True for wire-compatible JSON
|
|
16
|
+
- GitReins quality gate
|
|
17
|
+
- Hilo code graph
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
|
6
|
+
|
|
7
|
+
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
|
8
|
+
|
|
9
|
+
## Our Standards
|
|
10
|
+
|
|
11
|
+
Examples of behavior that contributes to a positive environment:
|
|
12
|
+
|
|
13
|
+
- Demonstrating empathy and kindness toward other people
|
|
14
|
+
- Being respectful of differing opinions, viewpoints, and experiences
|
|
15
|
+
- Giving and gracefully accepting constructive feedback
|
|
16
|
+
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
|
17
|
+
- Focusing on what is best not just for us as individuals, but for the overall community
|
|
18
|
+
|
|
19
|
+
Examples of unacceptable behavior:
|
|
20
|
+
|
|
21
|
+
- The use of sexualized language or imagery, and sexual attention or advances of any kind
|
|
22
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
23
|
+
- Public or private harassment
|
|
24
|
+
- Publishing others' private information without explicit permission
|
|
25
|
+
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
26
|
+
|
|
27
|
+
## Enforcement
|
|
28
|
+
|
|
29
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at the GitHub organization [get-h3](https://github.com/get-h3). All complaints will be reviewed and investigated promptly and fairly.
|
|
30
|
+
|
|
31
|
+
## Attribution
|
|
32
|
+
|
|
33
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Contributing to H3 SDK for Python
|
|
2
|
+
|
|
3
|
+
Python SDK for building H3-compliant agent harnesses. Implements the harness side of the H3 protocol using Pydantic + FastAPI.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd sdk-python/
|
|
9
|
+
python -m venv .venv
|
|
10
|
+
source .venv/bin/activate
|
|
11
|
+
uv pip install -e ".[dev]"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Package Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
sdk-python/
|
|
18
|
+
├── src/h3_harness/
|
|
19
|
+
│ ├── protocol.py # Pydantic models (generated from protocol repo JSON Schema)
|
|
20
|
+
│ ├── harness.py # BaseHarness ABC + FastAPI router
|
|
21
|
+
│ ├── middleware.py # Request logging middleware
|
|
22
|
+
│ └── testbed.py # MockHermes for pytest
|
|
23
|
+
├── tests/
|
|
24
|
+
│ ├── test_protocol.py
|
|
25
|
+
│ ├── test_harness.py
|
|
26
|
+
│ └── test_testbed.py
|
|
27
|
+
└── examples/
|
|
28
|
+
├── echo/ # Echo harness (returns messages back)
|
|
29
|
+
├── minimal/ # Bare-minimum example
|
|
30
|
+
└── langchain/ # LangChain integration demo
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Before Making Changes
|
|
34
|
+
|
|
35
|
+
### Run Tests
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
python -m pytest tests/ -v
|
|
39
|
+
# 34 tests
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Run Lint + Type Check
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
ruff check src/ tests/
|
|
46
|
+
mypy src/
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Run the Test Battery
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Start the echo example in one terminal:
|
|
53
|
+
python examples/echo/main.py
|
|
54
|
+
|
|
55
|
+
# In another terminal, run the compliance test battery:
|
|
56
|
+
h3-test --endpoint http://localhost:9191
|
|
57
|
+
# 43 compliance tests, exit code 0 = compliant
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Sync Protocol Types
|
|
61
|
+
|
|
62
|
+
If the upstream protocol changed:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
python scripts/sync_protocol.py
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This regenerates `src/h3_harness/protocol.py` from `get-h3/protocol` schemas. Never hand-edit generated Pydantic models.
|
|
69
|
+
|
|
70
|
+
## Making Changes
|
|
71
|
+
|
|
72
|
+
### BaseHarness Interface
|
|
73
|
+
|
|
74
|
+
- `harness.py` defines the `BaseHarness` ABC with `on_process` and `on_result`
|
|
75
|
+
- Changes to the ABC are MAJOR — they break all existing harnesses
|
|
76
|
+
- New optional hooks should use separate mixins
|
|
77
|
+
|
|
78
|
+
### FastAPI Router
|
|
79
|
+
|
|
80
|
+
- `create_router()` builds a FastAPI APIRouter with `/v1/health`, `/v1/process`, `/v1/result`
|
|
81
|
+
- Must follow the H3 protocol exactly — see `get-h3/protocol/h3-protocol.yaml`
|
|
82
|
+
- All endpoints log METHOD /path STATUS DURATION via middleware
|
|
83
|
+
|
|
84
|
+
### Middleware
|
|
85
|
+
|
|
86
|
+
- `middleware.py` uses FastAPI's `BaseHTTPMiddleware`
|
|
87
|
+
- Logs structured request info without leaking credentials
|
|
88
|
+
|
|
89
|
+
### Pydantic Models
|
|
90
|
+
|
|
91
|
+
- Models use `Optional` types for protocol-optional fields
|
|
92
|
+
- Validation must match JSON Schema constraints from `get-h3/protocol/schemas/v1/`
|
|
93
|
+
- `model_dump(exclude_none=True)` for wire format compatibility
|
|
94
|
+
|
|
95
|
+
## Quality Gates
|
|
96
|
+
|
|
97
|
+
### Pre-Commit
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
ruff check src/ tests/ # Lint
|
|
101
|
+
ruff format --check src/ tests/ # Format
|
|
102
|
+
mypy src/ # Type check
|
|
103
|
+
python -m pytest tests/ -v # Tests (34)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### CI Pipeline
|
|
107
|
+
|
|
108
|
+
GitHub Actions runs on every PR:
|
|
109
|
+
1. Lint (ruff)
|
|
110
|
+
2. Type check (mypy)
|
|
111
|
+
3. Tests (pytest, 34 tests)
|
|
112
|
+
4. `h3-test --endpoint http://localhost:9191` (against echo example)
|
|
113
|
+
|
|
114
|
+
All must pass.
|
|
115
|
+
|
|
116
|
+
## Release
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
git tag v1.0.0
|
|
120
|
+
git push origin v1.0.0
|
|
121
|
+
# CI publishes to PyPI automatically
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Review Checklist
|
|
125
|
+
|
|
126
|
+
- [ ] `pytest tests/ -v` passes (34 tests)
|
|
127
|
+
- [ ] `ruff check` passes
|
|
128
|
+
- [ ] `mypy src/` passes
|
|
129
|
+
- [ ] `h3-test --endpoint http://localhost:9191` passes against echo example
|
|
130
|
+
- [ ] New Pydantic fields use `Optional` where appropriate
|
|
131
|
+
- [ ] Protocol changes regenerated via `sync_protocol.py`
|
|
132
|
+
- [ ] No hand-edits to generated types
|
|
133
|
+
|
|
134
|
+
## Questions?
|
|
135
|
+
|
|
136
|
+
See the umbrella project at [get-h3/h3](https://github.com/get-h3/h3) for architecture, specs, and the cross-repo task board.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Governance
|
|
2
|
+
|
|
3
|
+
This document describes the governance model for the H3 Python SDK project.
|
|
4
|
+
|
|
5
|
+
## Roles
|
|
6
|
+
|
|
7
|
+
- **Maintainers**: Responsible for reviewing and merging contributions, managing releases, and enforcing the code of conduct.
|
|
8
|
+
- **Contributors**: Anyone who submits patches, documentation, or bug reports.
|
|
9
|
+
|
|
10
|
+
## Decision Making
|
|
11
|
+
|
|
12
|
+
Decisions are made through consensus among maintainers. For significant changes (new features, API changes, breaking changes), a design discussion should precede implementation.
|
|
13
|
+
|
|
14
|
+
## Pull Request Process
|
|
15
|
+
|
|
16
|
+
1. Open an issue describing the change before submitting a PR (for non-trivial changes).
|
|
17
|
+
2. All PRs must pass CI (tests + lint).
|
|
18
|
+
3. At least one maintainer must approve before merge.
|
|
19
|
+
4. The PR author is responsible for merging after approval.
|
|
20
|
+
|
|
21
|
+
## Release Process
|
|
22
|
+
|
|
23
|
+
Releases follow [Semantic Versioning](https://semver.org/). Maintainers publish releases to PyPI.
|
|
24
|
+
|
|
25
|
+
## Code of Conduct
|
|
26
|
+
|
|
27
|
+
See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
|
|
28
|
+
|
|
29
|
+
## License
|
|
30
|
+
|
|
31
|
+
This project is licensed under the terms in [LICENSE](LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 H3 Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
.PHONY: install build test lint fmt clean generate
|
|
2
|
+
|
|
3
|
+
VENV := .venv
|
|
4
|
+
PYTHON := $(VENV)/bin/python
|
|
5
|
+
|
|
6
|
+
$(VENV):
|
|
7
|
+
python3 -m venv $(VENV)
|
|
8
|
+
$(PYTHON) -m pip install --upgrade pip
|
|
9
|
+
$(PYTHON) -m pip install -e ".[dev]"
|
|
10
|
+
|
|
11
|
+
install: $(VENV)
|
|
12
|
+
|
|
13
|
+
build:
|
|
14
|
+
uv build
|
|
15
|
+
@echo "build: OK — dist/ now contains the wheel (and sdist)"
|
|
16
|
+
|
|
17
|
+
test:
|
|
18
|
+
uv run pytest -x --tb=short -q
|
|
19
|
+
|
|
20
|
+
test-full:
|
|
21
|
+
uv run pytest -x -v
|
|
22
|
+
|
|
23
|
+
lint:
|
|
24
|
+
uv run ruff check src/ tests/
|
|
25
|
+
|
|
26
|
+
fmt:
|
|
27
|
+
uv run ruff format src/ tests/
|
|
28
|
+
|
|
29
|
+
clean:
|
|
30
|
+
rm -rf $(VENV) __pycache__ src/h3_harness/__pycache__
|
|
31
|
+
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
32
|
+
find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true
|
|
33
|
+
|
|
34
|
+
all: install lint build test
|
|
35
|
+
|
|
36
|
+
generate:
|
|
37
|
+
uv run python scripts/generate-protocol.py
|
|
38
|
+
uv run ruff check --fix src/h3_harness/protocol.py
|
|
39
|
+
uv run ruff format src/h3_harness/protocol.py
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: h3-harness-sdk
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Python SDK for building H3-compliant agent harnesses
|
|
5
|
+
Author: Total Windup Flight Systems
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: agent,h3,harness,hermes
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: fastapi>=0.109.0
|
|
18
|
+
Requires-Dist: pydantic>=2.0
|
|
19
|
+
Requires-Dist: uvicorn[standard]>=0.25.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: httpx>=0.26; extra == 'dev'
|
|
22
|
+
Requires-Dist: jsonschema>=4.20; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-benchmark>=5.2.3; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# H3 Harness SDK for Python
|
|
30
|
+
|
|
31
|
+
Python SDK for building H3-compliant agent harnesses.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install h3-harness-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Install fallback (source / git)
|
|
40
|
+
|
|
41
|
+
If a release isn't published to PyPI yet (or you want the latest unreleased
|
|
42
|
+
changes), install directly from the repository:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# From git
|
|
46
|
+
pip install git+https://github.com/get-h3/sdk-python.git
|
|
47
|
+
|
|
48
|
+
# Editable source install (development)
|
|
49
|
+
git clone https://github.com/get-h3/sdk-python.git
|
|
50
|
+
cd sdk-python
|
|
51
|
+
pip install -e .
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quickstart
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from h3_harness import (
|
|
58
|
+
BaseHarness,
|
|
59
|
+
Decision,
|
|
60
|
+
DecisionType,
|
|
61
|
+
End,
|
|
62
|
+
TextResponse,
|
|
63
|
+
create_router,
|
|
64
|
+
)
|
|
65
|
+
from fastapi import FastAPI
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class MyHarness(BaseHarness):
|
|
69
|
+
async def on_process(self, req):
|
|
70
|
+
# Echo conversation history from context (battery: history preserved).
|
|
71
|
+
history = list(req.context.history)
|
|
72
|
+
# Streaming: "do not finish" in message -> unfinished text.
|
|
73
|
+
streaming = "do not finish" in req.message.content
|
|
74
|
+
finished = not streaming
|
|
75
|
+
return Decision(
|
|
76
|
+
decision=DecisionType.TEXT,
|
|
77
|
+
text=TextResponse(
|
|
78
|
+
content=f"Echo: {req.message.content}",
|
|
79
|
+
finished=finished,
|
|
80
|
+
),
|
|
81
|
+
history=history,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
async def on_result(self, req):
|
|
85
|
+
return Decision(decision=DecisionType.END, end=End(reason="task_complete"))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
app = FastAPI()
|
|
89
|
+
app.include_router(create_router(MyHarness()))
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Testbed
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from h3_harness.testbed import MockHermes
|
|
96
|
+
from h3_harness.examples.echo import EchoHarness
|
|
97
|
+
|
|
98
|
+
mock = MockHermes(EchoHarness())
|
|
99
|
+
decision = await mock.send_message("Hello!")
|
|
100
|
+
assert decision.text.content == "Echo: Hello!"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Examples
|
|
104
|
+
|
|
105
|
+
- **[echo.py](src/h3_harness/examples/echo.py)** — Echo harness that mirrors user messages
|
|
106
|
+
- **[minimal.py](src/h3_harness/examples/minimal.py)** — Minimal harness with health endpoint, uvicorn runner
|
|
107
|
+
- **[langchain_agent.py](src/h3_harness/examples/langchain_agent.py)** — LangChain integration: LLM call with text response
|
|
108
|
+
|
|
109
|
+
## Passing the battery (h3-test compliance)
|
|
110
|
+
|
|
111
|
+
The gate for any H3 harness is the **test battery** (`test_battery.py` from
|
|
112
|
+
[get-h3/shim](https://github.com/get-h3/shim) — 43 tests across 6 categories).
|
|
113
|
+
Run it against any running harness endpoint:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# The shim is not yet published to PyPI — install from source (get-h3/shim)
|
|
117
|
+
pip install git+https://github.com/get-h3/shim
|
|
118
|
+
h3-test --endpoint http://localhost:9191 # exit 0 = compliant
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The Quickstart harness above implements all three conventions and is fully
|
|
122
|
+
battery-compliant (**43/43**). If you modify it, keep the conventions intact —
|
|
123
|
+
a naive harness that drops them scores **41/43**. The three conventions the
|
|
124
|
+
battery checks (beyond "return a Decision") are:
|
|
125
|
+
|
|
126
|
+
1. **Echo `context.history` in every Decision.** The battery sends a session
|
|
127
|
+
with prior history and asserts it flows back through the response
|
|
128
|
+
(`test_2_8_process_preserves_history`). Pass it through explicitly:
|
|
129
|
+
```python
|
|
130
|
+
history = list(req.context.history)
|
|
131
|
+
return Decision(..., history=history)
|
|
132
|
+
```
|
|
133
|
+
2. **Never issue `llm_call` when `context.models` is empty.** The battery
|
|
134
|
+
sends `context.models: []` and FAILS any harness that returns an `llm_call`
|
|
135
|
+
decision (`test_5_8_no_models_available` — "hallucinated model"). Only
|
|
136
|
+
return `LLM_CALL` when the request actually lists models.
|
|
137
|
+
3. **Return `text.finished=false` for "do not finish" prompts.** The battery
|
|
138
|
+
sends *"Just start a thought, do not finish it yet."* and asserts the
|
|
139
|
+
response has `text.finished == False` (`test_2_4_process_text_finished_false`).
|
|
140
|
+
Detect streaming/unfinished intent and set `finished` accordingly.
|
|
141
|
+
|
|
142
|
+
The canonical battery-ready template is **[echo.py](src/h3_harness/examples/echo.py)**
|
|
143
|
+
— it implements all three conventions and scores 43/43. Use it as the starting
|
|
144
|
+
point for your own harness.
|
|
145
|
+
|
|
146
|
+
## Development
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
make install # create venv + install deps
|
|
150
|
+
make build # build wheel (and sdist) into dist/
|
|
151
|
+
make test # run tests
|
|
152
|
+
make lint # ruff check
|
|
153
|
+
make fmt # ruff format
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Reference
|
|
157
|
+
|
|
158
|
+
- Spec: [get-h3/h3 — specs/04-SDK-Libraries.md](https://github.com/get-h3/h3/blob/main/specs/04-SDK-Libraries.md)
|
|
159
|
+
- Protocol: [get-h3/protocol](https://github.com/get-h3/protocol)
|
|
160
|
+
- API reference: [docs/api/index.md](docs/api/index.md)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# H3 Harness SDK for Python
|
|
2
|
+
|
|
3
|
+
Python SDK for building H3-compliant agent harnesses.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install h3-harness-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Install fallback (source / git)
|
|
12
|
+
|
|
13
|
+
If a release isn't published to PyPI yet (or you want the latest unreleased
|
|
14
|
+
changes), install directly from the repository:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# From git
|
|
18
|
+
pip install git+https://github.com/get-h3/sdk-python.git
|
|
19
|
+
|
|
20
|
+
# Editable source install (development)
|
|
21
|
+
git clone https://github.com/get-h3/sdk-python.git
|
|
22
|
+
cd sdk-python
|
|
23
|
+
pip install -e .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quickstart
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from h3_harness import (
|
|
30
|
+
BaseHarness,
|
|
31
|
+
Decision,
|
|
32
|
+
DecisionType,
|
|
33
|
+
End,
|
|
34
|
+
TextResponse,
|
|
35
|
+
create_router,
|
|
36
|
+
)
|
|
37
|
+
from fastapi import FastAPI
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MyHarness(BaseHarness):
|
|
41
|
+
async def on_process(self, req):
|
|
42
|
+
# Echo conversation history from context (battery: history preserved).
|
|
43
|
+
history = list(req.context.history)
|
|
44
|
+
# Streaming: "do not finish" in message -> unfinished text.
|
|
45
|
+
streaming = "do not finish" in req.message.content
|
|
46
|
+
finished = not streaming
|
|
47
|
+
return Decision(
|
|
48
|
+
decision=DecisionType.TEXT,
|
|
49
|
+
text=TextResponse(
|
|
50
|
+
content=f"Echo: {req.message.content}",
|
|
51
|
+
finished=finished,
|
|
52
|
+
),
|
|
53
|
+
history=history,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
async def on_result(self, req):
|
|
57
|
+
return Decision(decision=DecisionType.END, end=End(reason="task_complete"))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
app = FastAPI()
|
|
61
|
+
app.include_router(create_router(MyHarness()))
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Testbed
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from h3_harness.testbed import MockHermes
|
|
68
|
+
from h3_harness.examples.echo import EchoHarness
|
|
69
|
+
|
|
70
|
+
mock = MockHermes(EchoHarness())
|
|
71
|
+
decision = await mock.send_message("Hello!")
|
|
72
|
+
assert decision.text.content == "Echo: Hello!"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Examples
|
|
76
|
+
|
|
77
|
+
- **[echo.py](src/h3_harness/examples/echo.py)** — Echo harness that mirrors user messages
|
|
78
|
+
- **[minimal.py](src/h3_harness/examples/minimal.py)** — Minimal harness with health endpoint, uvicorn runner
|
|
79
|
+
- **[langchain_agent.py](src/h3_harness/examples/langchain_agent.py)** — LangChain integration: LLM call with text response
|
|
80
|
+
|
|
81
|
+
## Passing the battery (h3-test compliance)
|
|
82
|
+
|
|
83
|
+
The gate for any H3 harness is the **test battery** (`test_battery.py` from
|
|
84
|
+
[get-h3/shim](https://github.com/get-h3/shim) — 43 tests across 6 categories).
|
|
85
|
+
Run it against any running harness endpoint:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# The shim is not yet published to PyPI — install from source (get-h3/shim)
|
|
89
|
+
pip install git+https://github.com/get-h3/shim
|
|
90
|
+
h3-test --endpoint http://localhost:9191 # exit 0 = compliant
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The Quickstart harness above implements all three conventions and is fully
|
|
94
|
+
battery-compliant (**43/43**). If you modify it, keep the conventions intact —
|
|
95
|
+
a naive harness that drops them scores **41/43**. The three conventions the
|
|
96
|
+
battery checks (beyond "return a Decision") are:
|
|
97
|
+
|
|
98
|
+
1. **Echo `context.history` in every Decision.** The battery sends a session
|
|
99
|
+
with prior history and asserts it flows back through the response
|
|
100
|
+
(`test_2_8_process_preserves_history`). Pass it through explicitly:
|
|
101
|
+
```python
|
|
102
|
+
history = list(req.context.history)
|
|
103
|
+
return Decision(..., history=history)
|
|
104
|
+
```
|
|
105
|
+
2. **Never issue `llm_call` when `context.models` is empty.** The battery
|
|
106
|
+
sends `context.models: []` and FAILS any harness that returns an `llm_call`
|
|
107
|
+
decision (`test_5_8_no_models_available` — "hallucinated model"). Only
|
|
108
|
+
return `LLM_CALL` when the request actually lists models.
|
|
109
|
+
3. **Return `text.finished=false` for "do not finish" prompts.** The battery
|
|
110
|
+
sends *"Just start a thought, do not finish it yet."* and asserts the
|
|
111
|
+
response has `text.finished == False` (`test_2_4_process_text_finished_false`).
|
|
112
|
+
Detect streaming/unfinished intent and set `finished` accordingly.
|
|
113
|
+
|
|
114
|
+
The canonical battery-ready template is **[echo.py](src/h3_harness/examples/echo.py)**
|
|
115
|
+
— it implements all three conventions and scores 43/43. Use it as the starting
|
|
116
|
+
point for your own harness.
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
make install # create venv + install deps
|
|
122
|
+
make build # build wheel (and sdist) into dist/
|
|
123
|
+
make test # run tests
|
|
124
|
+
make lint # ruff check
|
|
125
|
+
make fmt # ruff format
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Reference
|
|
129
|
+
|
|
130
|
+
- Spec: [get-h3/h3 — specs/04-SDK-Libraries.md](https://github.com/get-h3/h3/blob/main/specs/04-SDK-Libraries.md)
|
|
131
|
+
- Protocol: [get-h3/protocol](https://github.com/get-h3/protocol)
|
|
132
|
+
- API reference: [docs/api/index.md](docs/api/index.md)
|