Skip to content

Public surface (guardrail_calculus)

The builder/authoring API plus domain scalars. This layer is still being shaped by Phases 2–3, so several builders are documented by signature only for now.

guardrail_calculus

Public guardrail-calculus surface.

This module is mostly public re-export and high-level API assembly. The foundational nodes, predicates, dimensional protocols, and the local proxy _ live in the import-safe :mod:guardrail_kernel; the generic dimension-algebra dispatch engine and symbolic conditional in :mod:guardrail_calculus.dimensions; concrete temporal values (duration_hours, clock_hm, ...) in :mod:guardrail_calculus.dimension_value_builtins; propositions and the typed check chain in :mod:guardrail_calculus.facts; the explanation DAG, solver utilities, and puzzle solving in the optional :mod:guardrail_solver package.

What remains defined here is the builder/authoring surface: Routes, schema-rooted refs, IR serialization, and the agent/graph/subgraph/subject builders. (The last two bespoke domain-scalar classes, Probability and Money, migrated onto the values API — probability/money_gbp/money_usd in :mod:guardrail_calculus.dimension_value_builtins.)

money_gbp module-attribute

money_gbp = ctor(GBP)

money_usd module-attribute

money_usd = ctor(USD)

routes module-attribute

routes = RoutesFactory()

ref module-attribute

ref = RefFactory()

agent module-attribute

agent = AgentFactory()

graph module-attribute

graph = GraphFactory()

subgraph module-attribute

subgraph = SubgraphFactory()

subject module-attribute

subject = SubjectFactory()

Routes dataclass

Represents a set of target destinations for graph routing.

Attributes:

Name Type Description
targets frozenset[RouteTarget]

Set of successor vertex references.

is_out bool

True if routing out of a subgraph.

Examples:

>>> from guardrail_calculus import Path, Routes, VertexRef
>>> r = Routes(frozenset([VertexRef(Path(("a",)))]))
>>> r.label
'routes.to(a)'
Source code in src/guardrail_calculus/__init__.py
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
@typing.final
@dataclass(frozen=True)
class Routes:
    """Represents a set of target destinations for graph routing.

    Attributes:
        targets: Set of successor vertex references.
        is_out: True if routing out of a subgraph.

    Examples:
        >>> from guardrail_calculus import Path, Routes, VertexRef
        >>> r = Routes(frozenset([VertexRef(Path(("a",)))]))
        >>> r.label
        'routes.to(a)'
    """

    targets: frozenset[RouteTarget] = frozenset()
    is_out: bool = False

    def includes(self, target: RouteTarget) -> Predicate[_Includes]:
        return Predicate(
            _node=_Includes(self, target),
            label=f"{display_value(self)} includes {display_value(target)}",
        )

    @property
    def label(self) -> str:
        if self.is_out:
            return "routes.out"

        if not self.targets:
            return "routes.none()"

        return "routes.to(" + ", ".join(display_value(t) for t in self.targets) + ")"

targets class-attribute instance-attribute

targets = frozenset()

is_out class-attribute instance-attribute

is_out = False

label property

label

includes

includes(target)
Source code in src/guardrail_calculus/__init__.py
541
542
543
544
545
def includes(self, target: RouteTarget) -> Predicate[_Includes]:
    return Predicate(
        _node=_Includes(self, target),
        label=f"{display_value(self)} includes {display_value(target)}",
    )

RoutesFactory

Factory to build Routes instances using helper methods.

Examples:

>>> from guardrail_calculus import Path, VertexRef, routes
>>> routes.to(VertexRef(Path(("a",)))).label
'routes.to(a)'
>>> routes.none().label
'routes.none()'
Source code in src/guardrail_calculus/__init__.py
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
class RoutesFactory:
    """Factory to build [Routes][guardrail_calculus.Routes] instances using helper methods.

    Examples:
        >>> from guardrail_calculus import Path, VertexRef, routes
        >>> routes.to(VertexRef(Path(("a",)))).label
        'routes.to(a)'
        >>> routes.none().label
        'routes.none()'
    """

    @property
    def out(self) -> Routes:
        return Routes(is_out=True)

    def none(self) -> Routes:
        return Routes()

    def to(self, *targets: RouteTarget) -> Routes:
        return Routes(targets=frozenset(targets))

out property

out

none

none()
Source code in src/guardrail_calculus/__init__.py
573
574
def none(self) -> Routes:
    return Routes()

to

to(*targets)
Source code in src/guardrail_calculus/__init__.py
576
577
def to(self, *targets: RouteTarget) -> Routes:
    return Routes(targets=frozenset(targets))

RefFactory

Source code in src/guardrail_calculus/__init__.py
761
762
763
class RefFactory:
    def __getitem__[T](self, schema_type: type[T]) -> T:
        return cast(T, Proxy(schema_type))

AgentBuilder dataclass

Builder for constructing an AgentBlock.

Attributes:

Name Type Description
output_type type[OutputsT] | None

Optional class representing outputs.

rules tuple[AgentRule, ...]

Tuple of accumulated rules.

pending_given Prop[Any] | None

The pending given predicate.

Examples:

>>> from guardrail_calculus import agent
>>> builder = agent().given(True).then(True)
>>> len(builder.rules)
1
Source code in src/guardrail_calculus/__init__.py
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
@dataclass(frozen=True)
class AgentBuilder[OutputsT]:
    """Builder for constructing an [AgentBlock][guardrail_calculus.AgentBlock].

    Attributes:
        output_type: Optional class representing outputs.
        rules: Tuple of accumulated rules.
        pending_given: The pending given predicate.

    Examples:
        >>> from guardrail_calculus import agent
        >>> builder = agent().given(True).then(True)
        >>> len(builder.rules)
        1
    """

    output_type: type[OutputsT] | None = None
    rules: tuple[AgentRule, ...] = ()
    pending_given: Prop[Any] | None = None

    def given(self, value: Boolish_Bound | Prop[Any]) -> AgentBuilder[OutputsT]:
        return dataclasses.replace(self, pending_given=prop(value))

    def then(
        self,
        *values: Boolish_Bound | Prop[Any],
    ) -> AgentBuilder[OutputsT]:
        rule = AgentRule(
            given=self.pending_given,
            consequences=tuple(prop(value) for value in values),
        )
        return AgentBuilder(
            output_type=self.output_type,
            rules=(*self.rules, rule),
            pending_given=None,
        )

    def block(self) -> AgentBlock[OutputsT]:
        return AgentBlock(output_type=self.output_type, rules=self.rules)

output_type class-attribute instance-attribute

output_type = None

rules class-attribute instance-attribute

rules = ()

pending_given class-attribute instance-attribute

pending_given = None

given

given(value)
Source code in src/guardrail_calculus/__init__.py
881
882
def given(self, value: Boolish_Bound | Prop[Any]) -> AgentBuilder[OutputsT]:
    return dataclasses.replace(self, pending_given=prop(value))

then

then(*values)
Source code in src/guardrail_calculus/__init__.py
884
885
886
887
888
889
890
891
892
893
894
895
896
def then(
    self,
    *values: Boolish_Bound | Prop[Any],
) -> AgentBuilder[OutputsT]:
    rule = AgentRule(
        given=self.pending_given,
        consequences=tuple(prop(value) for value in values),
    )
    return AgentBuilder(
        output_type=self.output_type,
        rules=(*self.rules, rule),
        pending_given=None,
    )

block

block()
Source code in src/guardrail_calculus/__init__.py
898
899
def block(self) -> AgentBlock[OutputsT]:
    return AgentBlock(output_type=self.output_type, rules=self.rules)

AgentBlock dataclass

A constructed agent block with a set of rules.

Attributes:

Name Type Description
output_type type[OutputsT] | None

Optional class representing outputs.

rules tuple[AgentRule, ...]

Tuple of rules inside the agent.

Examples:

>>> from guardrail_calculus import AgentBlock
>>> block = AgentBlock(output_type=None, rules=())
>>> len(block.rules)
0
Source code in src/guardrail_calculus/__init__.py
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
@dataclass(frozen=True)
class AgentBlock[OutputsT]:
    """A constructed agent block with a set of rules.

    Attributes:
        output_type: Optional class representing outputs.
        rules: Tuple of rules inside the agent.

    Examples:
        >>> from guardrail_calculus import AgentBlock
        >>> block = AgentBlock(output_type=None, rules=())
        >>> len(block.rules)
        0
    """

    output_type: type[OutputsT] | None
    rules: tuple[AgentRule, ...]

output_type instance-attribute

output_type

rules instance-attribute

rules

AgentRule dataclass

A rule inside an agent containing a premise and a set of consequences.

Attributes:

Name Type Description
given Prop[Any] | None

Optional premise proposition.

consequences tuple[Prop[Any], ...]

Sequence of consequences to apply.

Examples:

>>> from guardrail_calculus import AgentRule
>>> rule = AgentRule(given=None, consequences=())
>>> rule.given is None
True
Source code in src/guardrail_calculus/__init__.py
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
@dataclass(frozen=True)
class AgentRule:
    """A rule inside an agent containing a premise and a set of consequences.

    Attributes:
        given: Optional premise proposition.
        consequences: Sequence of consequences to apply.

    Examples:
        >>> from guardrail_calculus import AgentRule
        >>> rule = AgentRule(given=None, consequences=())
        >>> rule.given is None
        True
    """

    given: Prop[Any] | None
    consequences: tuple[Prop[Any], ...]

given instance-attribute

given

consequences instance-attribute

consequences

GraphBlock dataclass

A constructed graph block.

Attributes:

Name Type Description
vertices Mapping[str, object]

Mapping from vertex names to blocks.

Examples:

>>> from guardrail_calculus import graph
>>> block = graph(a="vertex_a")
>>> block.vertices["a"]
'vertex_a'
Source code in src/guardrail_calculus/__init__.py
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
@dataclass(frozen=True)
class GraphBlock:
    """A constructed graph block.

    Attributes:
        vertices: Mapping from vertex names to blocks.

    Examples:
        >>> from guardrail_calculus import graph
        >>> block = graph(a="vertex_a")
        >>> block.vertices["a"]
        'vertex_a'
    """

    vertices: Mapping[str, object]

vertices instance-attribute

vertices

SubgraphBlock dataclass

A constructed subgraph block.

Attributes:

Name Type Description
output_type type[OutputsT] | None

Optional class representing outputs.

vertices Mapping[str, object]

Mapping from vertex names to sibling blocks.

Examples:

>>> from guardrail_calculus import subgraph
>>> block = subgraph(a="vertex_a")
>>> block.vertices["a"]
'vertex_a'
Source code in src/guardrail_calculus/__init__.py
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
@dataclass(frozen=True)
class SubgraphBlock[OutputsT]:
    """A constructed subgraph block.

    Attributes:
        output_type: Optional class representing outputs.
        vertices: Mapping from vertex names to sibling blocks.

    Examples:
        >>> from guardrail_calculus import subgraph
        >>> block = subgraph(a="vertex_a")
        >>> block.vertices["a"]
        'vertex_a'
    """

    output_type: type[OutputsT] | None
    vertices: Mapping[str, object]

output_type instance-attribute

output_type

vertices instance-attribute

vertices

probability

probability(value)

Construct a probability literal in [0, 1].

A thin authoring name only: Probability is comparison-only with no registered constructors, so this direct builder is its one authoring path — but it restates no bounds. checked_real gates finiteness (the REAL carrier's wire precondition), and DimensionValue itself evaluates the dimension's declared registry invariant (0 <= v <= 1) at construction — the same node tree the solver asserts on every Probability slot.

Parameters:

Name Type Description Default
value float

The probability, between 0 and 1 inclusive.

required

Returns:

Type Description
DisplacementValue[Literal['Probability'], float]

The constructed probability value.

Source code in src/guardrail_calculus/dimension_value_builtins.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def probability(value: float) -> DisplacementValue[Literal["Probability"], float]:
    """Construct a probability literal in [0, 1].

    A thin authoring name only: Probability is comparison-only with no
    registered constructors, so this direct builder is its one authoring
    path — but it restates *no* bounds. ``checked_real`` gates finiteness
    (the REAL carrier's wire precondition), and ``DimensionValue`` itself
    evaluates the dimension's declared registry invariant (``0 <= v <= 1``)
    at construction — the same node tree the solver asserts on every
    Probability slot.

    Args:
        value: The probability, between 0 and 1 inclusive.

    Returns:
        The constructed probability value.
    """
    # Probability is ``Vector``-structured, hence a displacement, and this is
    # a constructor, so the concrete class is both what it builds and what it
    # declares -- no cast, and no union for a caller's arithmetic to widen on.
    return DisplacementValue[Literal["Probability"], float](
        _node=checked_real(value), unit=None, _dimension=PROBABILITY
    )

validate_routes_out

validate_routes_out(block, *, in_subgraph=False)

Validate that routes.out is only used inside subgraphs.

Parameters:

Name Type Description Default
block AgentBlock[Any]

The agent block to validate.

required
in_subgraph bool

True if nested inside a subgraph.

False

Raises:

Type Description
ValueError

If routes.out is used outside a subgraph.

Source code in src/guardrail_calculus/__init__.py
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
def validate_routes_out(
    block: AgentBlock[Any],
    *,
    in_subgraph: bool = False,
) -> None:
    """Validate that `routes.out` is only used inside subgraphs.

    Args:
        block: The agent block to validate.
        in_subgraph: True if nested inside a subgraph.

    Raises:
        ValueError: If `routes.out` is used outside a subgraph.
    """
    for rule in block.rules:
        for consequence in rule.consequences:
            if _is_routes_out_assignment(consequence) and not in_subgraph:
                raise ValueError("routes.out is only valid inside a subgraph.")