Skip to main content

H3 Harness SDK for Python

Python SDK for building H3-compliant agent harnesses.

Install

pip install h3-harness-sdk

Install fallback (source / git)

If a release isn't published to PyPI yet (or you want the latest unreleased changes), install directly from the repository:

# From git
pip install git+https://github.com/get-h3/sdk-python.git

# Editable source install (development)
git clone https://github.com/get-h3/sdk-python.git
cd sdk-python
pip install -e .

Quickstart

from datetime import datetime, timezone

from h3_harness import (
    BaseHarness,
    Decision,
    DecisionType,
    End,
    TextResponse,
    create_router,
)
from fastapi import FastAPI


class MyHarness(BaseHarness):
    def __init__(self):
        # Track sessions so cancel/session lookups 404 on unknown ids
        # (battery: test_5_9b cancel_unknown_session, test_5_10 session_not_found).
        self._sessions: dict[str, dict] = {}

    async def on_process(self, req):
        # Echo conversation history from context (battery: history preserved).
        history = list(req.context.history)
        # Streaming: "do not finish" in message -> unfinished text.
        streaming = "do not finish" in req.message.content
        finished = not streaming
        self._sessions[req.session_id] = {
            "started_at": datetime.now(timezone.utc).isoformat(),
            "turn_count": (
                self._sessions.get(req.session_id, {}).get("turn_count", 0) + 1
            ),
        }
        return Decision(
            decision=DecisionType.TEXT,
            text=TextResponse(
                content=f"Echo: {req.message.content}",
                finished=finished,
            ),
            history=history,
        )

    async def on_result(self, req):
        return Decision(decision=DecisionType.END, end=End(reason="task_complete"))

    def get_session_info(self, session_id: str) -> dict | None:
        return self._sessions.get(session_id)


app = FastAPI()
app.include_router(create_router(MyHarness()))
# Run with: uvicorn my_harness:app --port 9191

Testbed

import asyncio

from h3_harness.testbed import MockHermes
from h3_harness.examples.echo import EchoHarness


async def main() -> None:
    mock = MockHermes(EchoHarness())
    decision = await mock.send_message("Hello!")
    assert decision.text.content == "Echo: Hello!"


if __name__ == "__main__":
    asyncio.run(main())

Examples

  • echo.py — Echo harness that mirrors user messages
  • minimal.py — Minimal harness with health endpoint, uvicorn runner
  • langchain_agent.py — LangChain integration: LLM call with text response

Passing the battery (h3-test compliance)

The gate for any H3 harness is the test battery (test_battery.py from get-h3/shim — 44 tests across 6 categories). Run it against any running harness endpoint:

# The shim is not yet published to PyPI — install from source (get-h3/shim)
pip install git+https://github.com/get-h3/shim
h3-test --endpoint http://localhost:9191   # exit 0 = compliant

The Quickstart harness above implements all four conventions and is fully battery-compliant (44/44). If you modify it, keep the conventions intact — a naive harness that drops them scores 41/44. The four conventions the battery checks (beyond "return a Decision") are:

  1. Echo context.history in every Decision returned from on_process. The battery sends a session with prior history and asserts it flows back through the response (test_2_8_process_preserves_history). Pass it through explicitly:
    history = list(req.context.history)
    return Decision(..., history=history)
    
    This applies to decisions returned from on_process — the ProcessRequest carries the context field. on_result receives a ResultRequest, which has no context field (only decision_id/result/session_id); a decision returned from on_result simply omits history. Echoing req.context.history there raises AttributeError.
  2. Never issue llm_call when context.models is empty. The battery sends context.models: [] and FAILS any harness that returns an llm_call decision (test_5_8_no_models_available — "hallucinated model"). Only return LLM_CALL when the request actually lists models.
  3. Return text.finished=false for "do not finish" prompts. The battery sends "Just start a thought, do not finish it yet." and asserts the response has text.finished == False (test_2_4_process_text_finished_false). Detect streaming/unfinished intent and set finished accordingly.
  4. 404 unknown sessions. The battery cancels a nonexistent session (test_5_9b cancel_unknown_session) and GETs one (test_5_10 session_not_found) and asserts a 404. Track sessions in the harness (get_session_info returning None for unknown ids) — the router turns that into the 404.

The canonical battery-ready template is echo.py — it implements all four conventions and scores 44/44. Use it as the starting point for your own harness.

Development

make install   # create venv + install deps
make build     # build wheel (and sdist) into dist/
make test      # run tests
make lint      # ruff check
make fmt       # ruff format

Reference

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

h3_harness_sdk-0.1.3.tar.gz (152.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

h3_harness_sdk-0.1.3-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

Details for the file h3_harness_sdk-0.1.3.tar.gz.

File metadata

  • Download URL: h3_harness_sdk-0.1.3.tar.gz
  • Upload date:
  • Size: 152.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for h3_harness_sdk-0.1.3.tar.gz
Algorithm Hash digest
SHA256 1f33a4fd042f6ad69a2275dadb94407842e07587e04bbd651f65eb6b805799cc
MD5 9fdd932e9bdee566a58b5d2ca37a5ff5
BLAKE2b-256 cbc8149c5b496b8377a1665441cfde3208946f2034182cc592e10a63ef6479e9

See more details on using hashes here.

File details

Details for the file h3_harness_sdk-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: h3_harness_sdk-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 17.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for h3_harness_sdk-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 5907987e1028822bb513fe398f605c9495d5db28fa8d1d9f34a116250c6fade9
MD5 9ebd2363d9477c39ddab39d0e0b62991
BLAKE2b-256 d8986bb1059bf5fd39c3d94f56abff679e69766d47bf4a1375d503606090b7ae

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page