Skip to content

Gaia IR API

Status: Generated from current Python docstrings and type hints.

Pydantic models and helpers for Gaia's persistent intermediate representation. Use this page when checking the structural contract consumed by validation, lowering, and review tooling.

Logic backends that analyze compiled IR live under IR Logic. They are intentionally documented as IR submodules rather than top-level engine facades.

gaia.engine.ir

Gaia IR — data models for the Gaia reasoning hypergraph.

Three entities: Knowledge (propositions), Operator (deterministic constraints), Strategy (reasoning declarations with three forms).

Parameterization (probability parameters) acts on LocalCanonicalGraph.

Spec: docs/foundations/gaia-ir/

CROMWELL_EPS module-attribute

CROMWELL_EPS: float = 0.001

Cromwell's rule epsilon — all probabilities clamped to [EPS, 1-EPS].

DEFAULT_PRIORITY_ORDER module-attribute

DEFAULT_PRIORITY_ORDER: tuple[str, ...] = ('calibration_*', 'user_priors', 'reviewer_*', 'continuous_inference', 'evidence_factor_*', 'agent_*', 'claim_inline', '*')

Default priority order for the explicit_priority resolution strategy.

The ranking embodies two principles:

  1. Explicit deliberation outranks shortcuts. Any prior set via an explicit register_prior() call ("user_priors" and beyond) wins over a claim(prior=X) inline shortcut, which sits at the second-to-last tier. The inline form is convenient for first-pass guesses but its justification is an auto-generated placeholder, so a properly documented register_prior call should always be able to override it.
  2. Author intent outranks engine output, except for retrospective calibration. Calibration based on real outcome data sits above user_priors because it incorporates evidence the author may not have had at write-time. The author's hand-written register_prior comes next, ahead of reviewer overrides, engine outputs (continuous_inference, evidence_factor_*), automated agent suggestions, the claim_inline shortcut, and finally the catch-all.

Authors may override this ranking per-package by exporting a custom RESOLUTION_POLICY in priors.py.

Wildcards: "*" matches any source_id (catch-all); "prefix_*" matches any source_id starting with prefix_. Wildcards may only appear at the end of a pattern.

Compose

Bases: BaseModel

A named DAG of action targets with one public conclusion Claim.

FormalizationResult dataclass

FormalizationResult(knowledges: list[Knowledge], strategy: FormalStrategy)

Generated intermediate claims plus the fully-expanded FormalStrategy.

FormulaEdge

Bases: BaseModel

Directed formula edge with a semantic role.

FormulaGraph

Bases: BaseModel

Formula graph attached to a source claim.

FormulaNode

Bases: BaseModel

Content-addressed formula node.

LocalCanonicalGraph

Bases: BaseModel

Local canonical graph — single package, content-addressed hash.

Stores complete content + Strategy steps (content repository). Auto-assigns QIDs to Knowledge nodes that have a label but no id.

Knowledge

Bases: BaseModel

Knowledge node — a proposition in the Gaia reasoning hypergraph.

id is a QID ({namespace}:{package_name}::{label}), content is populated.

KnowledgeType

Bases: StrEnum

Knowledge types (§1.2).

PackageRef

Bases: BaseModel

Reference to a package version.

Parameter

Bases: BaseModel

Quantified variable in a universal claim.

Operator

Bases: BaseModel

Deterministic logical constraint between Knowledge nodes.

Operators have no probability parameters — they encode logical structure. They can appear standalone (top-level operators array) or embedded in FormalExpr.

OperatorType

Bases: StrEnum

Operator types (§2.2). All are deterministic (ψ ∈ {0,1}, no free parameters).

ParameterizationSource

Bases: BaseModel

Metadata about the model/policy that produced a batch of records.

PriorRecord

Bases: BaseModel

Prior probability for a claim Knowledge.

Only type=claim Knowledge has PriorRecord. Values are Cromwell-clamped. Multiple records for the same knowledge_id may exist from different sources.

model_post_init

model_post_init(__context: Any) -> None

Set default timestamp and apply Cromwell clamping after validation.

Source code in gaia/engine/ir/parameterization.py
100
101
102
103
104
def model_post_init(self, __context: Any) -> None:
    """Set default timestamp and apply Cromwell clamping after validation."""
    if self.created_at is None:
        object.__setattr__(self, "created_at", datetime.now(UTC))
    object.__setattr__(self, "value", _clamp(self.value))

ResolutionPolicy

Bases: BaseModel

Policy for resolving multiple parameterization records before BP runs.

Strategies:

  • "explicit_priority" (default): rank records by priority_order pattern matching, with most-recent record winning within each pattern group. Patterns support trailing wildcards ("reviewer_*") and a catch-all ("*"). When priority_order is omitted, :data:DEFAULT_PRIORITY_ORDER is used.
  • "latest": pick the most recent record per Knowledge/Strategy by created_at timestamp, source-agnostic.
  • "source": use only records matching the configured source_id, latest within that source.

prior_cutoff filters records to those created at or before the given timestamp, enabling reproducible BP runs against a historical snapshot of the claim-prior layer.

resolve

resolve(records: list[PriorRecord]) -> PriorRecord | None

Pick the winning PriorRecord under this policy.

Returns None when no record passes the filter (e.g. empty input, cutoff filters everything out, or a "source" strategy whose target source_id is absent).

Algorithm:

  1. Filter by prior_cutoff if set.
  2. Dispatch on strategy:

  3. "latest": return the record with the most recent created_at.

  4. "source": filter to records matching self.source_id, return the latest within that subset (or None if subset empty).
  5. "explicit_priority": walk priority_order (or :data:DEFAULT_PRIORITY_ORDER) and for each pattern, find all matching records; the first non-empty pattern wins, with the most recent record breaking ties within that pattern. If no pattern matches any record, fall back to global recency.
Source code in gaia/engine/ir/parameterization.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
def resolve(self, records: list[PriorRecord]) -> PriorRecord | None:
    """Pick the winning PriorRecord under this policy.

    Returns ``None`` when no record passes the filter (e.g. empty input,
    cutoff filters everything out, or a ``"source"`` strategy whose target
    ``source_id`` is absent).

    Algorithm:

    1. Filter by ``prior_cutoff`` if set.
    2. Dispatch on ``strategy``:

       - ``"latest"``: return the record with the most recent ``created_at``.
       - ``"source"``: filter to records matching ``self.source_id``,
         return the latest within that subset (or None if subset empty).
       - ``"explicit_priority"``: walk ``priority_order`` (or
         :data:`DEFAULT_PRIORITY_ORDER`) and for each pattern, find all
         matching records; the first non-empty pattern wins, with the
         most recent record breaking ties within that pattern. If no
         pattern matches any record, fall back to global recency.
    """
    candidates = list(records)
    if self.prior_cutoff is not None:
        cutoff = self.prior_cutoff
        candidates = [r for r in candidates if r.created_at <= cutoff]
    if not candidates:
        return None

    if self.strategy == "latest":
        return max(candidates, key=lambda r: r.created_at)

    if self.strategy == "source":
        assert self.source_id is not None  # validator-enforced
        matching = [r for r in candidates if r.source_id == self.source_id]
        if not matching:
            return None
        return max(matching, key=lambda r: r.created_at)

    if self.strategy == "explicit_priority":
        order = self.priority_order or list(DEFAULT_PRIORITY_ORDER)
        for pattern in order:
            matching = [r for r in candidates if _matches(r.source_id, pattern)]
            if matching:
                return max(matching, key=lambda r: r.created_at)
        # No pattern matched (DEFAULT_PRIORITY_ORDER includes "*" so this
        # is unreachable in practice; covered for custom orders without
        # a catch-all).
        return max(candidates, key=lambda r: r.created_at)

    raise ValueError(f"Unknown ResolutionPolicy strategy: {self.strategy!r}")

Review

Bases: BaseModel

Qualitative review record for a compiled action, strategy, or operator target.

ReviewManifest

Bases: BaseModel

Collection of qualitative review records.

latest_status

latest_status(target_id: str) -> ReviewStatus | None

Return the most recent review status for a target, if one exists.

Source code in gaia/engine/ir/review.py
43
44
45
46
47
48
def latest_status(self, target_id: str) -> ReviewStatus | None:
    """Return the most recent review status for a target, if one exists."""
    relevant = [review for review in self.reviews if review.target_id == target_id]
    if not relevant:
        return None
    return max(relevant, key=lambda review: review.round).status

ReviewStatus

Bases: StrEnum

Lifecycle states for qualitative review records.

CallableRef

Bases: BaseModel

Provenance pointer for a callable, not a runtime execution pointer.

DistributionLiteral

Bases: BaseModel

JSON-native distribution declaration for IR and adapter boundaries.

QuantityLiteral

Bases: BaseModel

JSON-native IR carrier for unit-bearing scalar values.

CompositeStrategy

Bases: Strategy

Strategy with sub-strategies (by reference), supporting recursive nesting.

Generic container — any StrategyType is allowed. Sub-strategies are referenced by strategy_id strings, not embedded objects.

FormalExpr

Bases: BaseModel

Deterministic Operator expansion embedded in FormalStrategy.

Contains only deterministic Operators — no probability parameters. Intermediate Knowledge referenced by operators must exist as independent Knowledge nodes in the graph (created by compiler/reviewer/agent).

FormalStrategy

Bases: Strategy

Strategy with deterministic Operator expansion.

Used for named strategies (deduction, elimination, mathematical_induction, case_analysis, abduction, analogy, extrapolation) and as sub-parts of CompositeStrategy.

Step

Bases: BaseModel

A single reasoning step (local layer only).

Strategy

Bases: BaseModel

Base strategy — leaf reasoning (single ↝).

Can be instantiated directly for basic strategies (infer, noisy_and).

formalize

formalize(*, namespace: str | None = None, package_name: str | None = None) -> FormalizationResult

Expand a named leaf Strategy into generated intermediates + FormalStrategy.

For local scope, namespace and package_name are required so that generated intermediate Knowledge IDs use QID format.

Source code in gaia/engine/ir/strategy.py
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
def formalize(
    self, *, namespace: str | None = None, package_name: str | None = None
) -> FormalizationResult:
    """Expand a named leaf Strategy into generated intermediates + FormalStrategy.

    For local scope, ``namespace`` and ``package_name`` are required so that
    generated intermediate Knowledge IDs use QID format.
    """
    from gaia.engine.ir.formalize import formalize_named_strategy

    if isinstance(self, CompositeStrategy):
        raise TypeError("CompositeStrategy cannot be directly formalized")
    if isinstance(self, FormalStrategy):
        raise TypeError("FormalStrategy is already formalized")
    if self.conclusion is None:
        raise ValueError("formalize() requires the strategy to set a conclusion")

    return formalize_named_strategy(
        scope=self.scope,
        type_=self.type,
        premises=self.premises,
        conclusion=self.conclusion,
        namespace=namespace,
        package_name=package_name,
        background=self.background,
        steps=self.steps,
        metadata=self.metadata,
    )

StrategyType

Bases: StrEnum

Strategy types (§3.3). Orthogonal to form (Strategy/Composite/Formal).

formalize_named_strategy

formalize_named_strategy(*, scope: str, type_: StrategyType | str, premises: list[str], conclusion: str, namespace: str | None = None, package_name: str | None = None, background: list[str] | None = None, steps: list[Step] | None = None, metadata: dict[str, Any] | None = None) -> FormalizationResult

Generate the canonical FormalStrategy skeleton for a named strategy family.

namespace and package_name are required so that generated intermediate Knowledge IDs use the QID format ({namespace}:{package_name}::__{role}_{hash8}).

Source code in gaia/engine/ir/formalize.py
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
def formalize_named_strategy(
    *,
    scope: str,
    type_: StrategyType | str,
    premises: list[str],
    conclusion: str,
    namespace: str | None = None,
    package_name: str | None = None,
    background: list[str] | None = None,
    steps: list[Step] | None = None,
    metadata: dict[str, Any] | None = None,
) -> FormalizationResult:
    """Generate the canonical FormalStrategy skeleton for a named strategy family.

    ``namespace`` and ``package_name`` are required so that generated
    intermediate Knowledge IDs use the QID format
    ({namespace}:{package_name}::__{role}_{hash8}).
    """
    if scope != "local":
        raise ValueError("formalize_named_strategy only supports scope='local'")
    if namespace is None or package_name is None:
        raise ValueError("formalize_named_strategy requires namespace and package_name")
    if type_ == "reductio":
        raise ValueError(
            "reductio is deferred in Gaia IR core; the public-interface contract for "
            "hypothetical assumption/consequence nodes is not yet fixed"
        )
    strategy_type = StrategyType(type_)
    if strategy_type not in _FORMAL_STRATEGY_TYPES:
        allowed = ", ".join(sorted(t.value for t in _FORMAL_STRATEGY_TYPES))
        raise ValueError(
            f"formalize_named_strategy only supports named FormalStrategy types: {allowed}; "
            f"got {strategy_type.value}"
        )

    builder = _TemplateBuilder(
        scope=scope,
        strategy_type=strategy_type,
        premises=premises,
        conclusion=conclusion,
        namespace=namespace,
        package_name=package_name,
        metadata=metadata,
    )
    if strategy_type == StrategyType.CASE_ANALYSIS and (metadata or {}).get(
        "include_other_relevant_case"
    ):
        raise ValueError(
            "open-world case_analysis is deferred; model residual uncertainty on the "
            "coverage/exhaustiveness claim instead"
        )
    operators = _BUILDERS[strategy_type](builder)
    strategy_metadata = dict(metadata or {})
    strategy_metadata["generated_formal_expr"] = True
    strategy_metadata["formalization_template"] = strategy_type.value
    if builder.interface_roles:
        strategy_metadata["interface_roles"] = dict(builder.interface_roles)

    strategy = FormalStrategy(
        scope=scope,
        type=strategy_type,
        premises=builder.premises,
        conclusion=conclusion,
        background=background,
        steps=steps,
        metadata=strategy_metadata,
        formal_expr=FormalExpr(operators=operators),
    )

    for knowledge in builder.knowledges:
        knowledge.metadata = dict(knowledge.metadata or {})
        knowledge.metadata["owning_strategy_id"] = strategy.strategy_id

    return FormalizationResult(knowledges=builder.knowledges, strategy=strategy)

formula_node_id

formula_node_id(descriptor: dict[str, Any]) -> str

Return the canonical content-addressed ID for a formula node descriptor.

Source code in gaia/engine/ir/formula.py
35
36
37
38
39
40
41
42
43
def formula_node_id(descriptor: dict[str, Any]) -> str:
    """Return the canonical content-addressed ID for a formula node descriptor."""
    payload = json.dumps(
        descriptor,
        sort_keys=True,
        ensure_ascii=False,
        separators=(",", ":"),
    )
    return f"fg:{hashlib.sha256(payload.encode()).hexdigest()[:16]}"

make_qid

make_qid(namespace: str, package_name: str, label: str) -> str

Compose a Qualified Node ID: {namespace}:{package_name}::{label}.

Source code in gaia/engine/ir/knowledge.py
18
19
20
def make_qid(namespace: str, package_name: str, label: str) -> str:
    """Compose a Qualified Node ID: {namespace}:{package_name}::{label}."""
    return f"{namespace}:{package_name}::{label}"

default_resolution_policy

default_resolution_policy() -> ResolutionPolicy

Return the package-default ResolutionPolicy.

Equivalent to ResolutionPolicy() but more discoverable. Use this when a package's priors.py does not export a custom RESOLUTION_POLICY.

Source code in gaia/engine/ir/parameterization.py
205
206
207
208
209
210
211
def default_resolution_policy() -> ResolutionPolicy:
    """Return the package-default ResolutionPolicy.

    Equivalent to ``ResolutionPolicy()`` but more discoverable. Use this when
    a package's ``priors.py`` does not export a custom ``RESOLUTION_POLICY``.
    """
    return ResolutionPolicy()