Skip to content

Contracts

Contracts provide typed input/output validation for vertices. They enforce that handlers receive the expected types and return the expected shape.

VertexContract

from honey_badgeria.back.contracts import VertexContract

contract = VertexContract(
    inputs={"user_id": "int", "email": "str"},
    outputs={"user": "dict", "error": "str"},
    version="1.0",
    effect="pure",
)

Validating Inputs

# Valid input
contract.validate_input({"user_id": 123, "email": "alice@example.com"})
# OK — no exception

# Invalid type
contract.validate_input({"user_id": "not_an_int", "email": "alice@example.com"})
# Raises ContractInputError!

# Missing key
contract.validate_input({"user_id": 123})
# Raises ContractInputError! (missing "email")

Validating Outputs

# Valid output
contract.validate_output({"user": {"id": 1}, "error": None})
# OK

# Missing key
contract.validate_output({"user": {"id": 1}})
# Raises ContractOutputError! (missing "error")

Supported Types

The contract system maps type names to Python types:

Type Name Python Type
str str
int int
float float
bool bool
dict dict
list list
tuple tuple
set set
bytes bytes
none NoneType

Data Binding References

Unknown type names (like "fetch.user_id") are skipped during validation. This is by design — they are data-binding references, not type constraints:

contract = VertexContract(
    inputs={"user_id": "fetch.user_id"},  # Data binding, not type
    outputs={},
)
contract.validate_input({"user_id": 42})  # Skipped — no type check

Using Contracts in Flows

Contracts are derived from the inputs and outputs declared in your YAML:

validate_user:
  handler: vertices.users.validate
  effect: pure
  version: "1"
  inputs:
    username: str              # Type declaration → contract checks type
    email: str                 # Type declaration → contract checks type
  outputs:
    is_valid: bool             # Contract validates output has this key and type
    errors: list

When the execution engine runs validate_user:

  1. Inputs are resolved from the DataStore.
  2. A VertexContract is constructed from the vertex's inputs and outputs.
  3. contract.validate_input(resolved_inputs) checks types.
  4. The handler executes.
  5. contract.validate_output(handler_result) checks the return value.

Exceptions

Exception When
ContractInputError Input doesn't match declared types or is missing
ContractOutputError Output doesn't match declared types or is missing
ContractError Base class for both
from honey_badgeria.core.exceptions import ContractError, ContractInputError, ContractOutputError

try:
    contract.validate_input(data)
except ContractInputError as e:
    print(f"Input validation failed: {e}")
except ContractError as e:
    print(f"Contract error: {e}")