Understanding the @contract Decorator in SymbolicAI: Design-by-Contract for Neuro-Symbolic AI

The @contract decorator in SymbolicAI enforces Design-by-Contract (DbC) principles on Expression classes by wrapping the forward method with automatic type validation, pre/post-condition checks, and LLM-driven remediation retries.

The @contract decorator is a core component of the SymbolicAI framework, implementing Design-by-Contract for neuro-symbolic applications. Applied to classes inheriting from symai.Expression, it automatically validates inputs and outputs against LLMDataModel schemas and attempts autonomous remediation when validation fails, significantly improving reliability when underlying language models produce unexpected outputs.

What Is the @contract Decorator in SymbolicAI?

The @contract decorator is defined in symai/strategy.py and serves as a class-level decorator that transforms Expression subclasses into self-validating components. When applied, it intercepts the forward method and injects a validation-and-remediation pipeline that ensures data integrity before and after the core logic executes.

According to the SymbolicAI source code, the decorator specifically targets the forward method because this is the standard entry point for all Expression subclasses. By wrapping this method, @contract can enforce type safety, run pre-conditions, validate post-conditions, and trigger automatic retries without modifying the underlying business logic.

How the Contract Decorator Works Under the Hood

The implementation in symai/strategy.py reveals a sophisticated pipeline that handles validation at multiple stages. The decorator creates a TypeValidationFunction instance that orchestrates the entire flow.

Type Safety Enforcement

The decorator enforces that all inputs and outputs conform to LLMDataModel subclasses or valid Python types. The helper functions _is_valid_input and _is_valid_output perform these checks at runtime【symai/strategy.py#L30-L55](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py#L30-L55).

When a type mismatch occurs, the decorator attempts to coerce the data into the expected schema using the configured LLM engine. This dynamic wrapping ensures that even raw strings or dictionaries can be validated against strict Pydantic-based models.

Pre-Condition Validation and Remediation

Before executing the forward method, the decorator checks for a pre method on the class. If pre_remedy=True is passed to the decorator, validation failures trigger an LLM-driven remediation attempt to correct the input【symai/strategy.py#L12-L28](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py#L12-L28).

The pre method receives the input instance and returns a boolean. If it raises an exception or returns False, the remediation pipeline attempts to fix the input using the LLM before retrying the validation.

Post-Condition Validation and Remediation

After the forward method completes, the decorator checks for a post method to validate the output. With post_remedy=True, failures here also trigger LLM-based fixes【symai/strategy.py#L14-L30](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py#L14-L30).

This two-stage validation ensures that both inputs and outputs maintain semantic consistency with the declared data models, catching hallucinations or format violations immediately after generation.

Automatic Retry Logic with remedy_retry_params

The decorator accepts a remedy_retry_params dictionary that configures exponential backoff and retry behavior for validation failures. These parameters control the number of attempts, delay timing, jitter, and whether failures are swallowed gracefully【symai/strategy.py#L94-L102](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py#L94-L102).

Available parameters include:

  • tries: Maximum number of remediation attempts
  • delay: Initial delay between retries
  • max_delay: Cap on delay growth
  • jitter: Random variation to prevent thundering herd
  • backoff: Exponential multiplier
  • graceful: Whether to suppress exceptions after exhaustion

Performance Monitoring and State Tracking

The decorator automatically instruments each validation stage, storing timing metrics in self._contract_timing. This dictionary contains durations for input_validation, act (forward execution), and output_validationsymai/strategy.py#L38-L40](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py#L38-L40).

After execution, the instance receives state flags:

Practical Implementation: Using @contract in SymbolicAI

Implementing the @contract decorator requires inheriting from Expression and defining data models that extend LLMDataModel.

Basic Contract-Decorated Expression

The following example demonstrates a complete implementation with input and output validation:

from symai import Expression
from symai.strategy import contract
from symai.models import LLMDataModel
from pydantic import Field
from typing import Optional

class GreetingInput(LLMDataModel):
    name: str = Field(description="Name of the person to greet.")
    shout: Optional[bool] = Field(default=False, description="Whether to uppercase the greeting.")

class GreetingOutput(LLMDataModel):
    message: str = Field(description="The final greeting string.")

@contract(pre_remedy=True, post_remedy=True)
class Greeter(Expression):
    @property
    def prompt(self) -> str:
        return "Create a friendly greeting based on the given input."

    def pre(self, input: GreetingInput) -> bool:
        if not input.name.strip():
            raise ValueError("Name cannot be empty.")
        return True

    def forward(self, input: GreetingInput) -> GreetingOutput:
        text = f"Hello, {input.name}"
        if input.shout:
            text = text.upper()
        return GreetingOutput(message=text)

When instantiated and called, this class automatically validates that inputs conform to GreetingInput, runs the pre check, executes forward, validates the output against GreetingOutput, and attempts LLM-driven fixes if any stage fails.

Configuring Custom Retry Parameters

For production workloads requiring resilient validation, configure the retry behavior explicitly:

@contract(
    pre_remedy=True,
    post_remedy=True,
    remedy_retry_params={
        "tries": 4,
        "delay": 0.3,
        "max_delay": 5,
        "jitter": 0.1,
        "backoff": 2,
        "graceful": False,
    },
)
class RobustGreeter(Expression):
    ...

These parameters are passed directly to the TypeValidationFunction class, implementing exponential backoff with jitter to handle transient LLM failures during remediation.

Inspecting Contract Execution Metrics

After execution, inspect the contract state and timing information:

greeter = Greeter()
result = greeter(GreetingInput(name="Alice"))

print(greeter.contract_successful)      # Boolean: True if all validations passed

print(greeter._contract_timing)        # Dict with stage durations

print(greeter.contract_result)         # Final returned value

if hasattr(greeter, 'contract_exception'):
    print(greeter.contract_exception)  # Captured error if not graceful

The _contract_timing dictionary contains granular performance data for input_validation, act (the forward pass), and output_validation, enabling bottleneck analysis in production pipelines.

Key Source Files for the Contract Decorator

Understanding the @contract implementation requires familiarity with three primary locations in the SymbolicAI repository:

Summary

The @contract decorator in SymbolicAI provides robust Design-by-Contract capabilities for neuro-symbolic applications:

  • Automatic type validation ensures all inputs and outputs conform to LLMDataModel schemas, with dynamic coercion attempts for mismatched types.
  • Pre-condition and post-condition hooks allow custom validation logic via pre and post methods, with optional LLM-driven remediation when checks fail.
  • Configurable retry mechanisms via remedy_retry_params support exponential backoff, jitter, and graceful degradation for production resilience.
  • Built-in instrumentation captures detailed timing metrics and success states, enabling performance monitoring and debugging of validation pipelines.

Frequently Asked Questions

What happens when a pre-condition fails and pre_remedy is enabled?

When the pre method raises an exception or returns False and pre_remedy=True, the decorator invokes the LLM to generate a corrected input that satisfies the contract. This remediation attempt follows the remedy_retry_params configuration, potentially executing multiple LLM calls with exponential backoff until validation succeeds or retries are exhausted.

Can I use @contract without LLMDataModel types?

While the decorator is optimized for LLMDataModel subclasses, it can validate standard Python types through dynamic wrapping. However, the full remediation capabilities and strict schema validation require Pydantic-based LLMDataModel definitions to provide the necessary structure for the LLM to understand and correct violations.

How do I disable retries for contract violations?

Set remedy_retry_params={"tries": 1, "graceful": True} to execute exactly one attempt without retries, or set pre_remedy=False and post_remedy=False to disable remediation entirely. In this configuration, validation failures immediately raise exceptions or set contract_successful to False depending on the graceful parameter.

Where can I find examples of @contract usage in the test suite?

The tests/contract/test_contract.py file contains comprehensive examples demonstrating pre-remedy validation failures, post-remedy output correction, retry exhaustion scenarios, and graceful error handling. These tests illustrate how the decorator interacts with the TypeValidationFunction and validate the timing metrics collected in _contract_timing.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →