Evaluation Decorators
March 7, 2025 ยท View on GitHub
The evaluation decorator system provides a pytest-like interface for running evaluations on function outputs. This allows you to easily integrate content safety checks into your existing code.
Relationship with OpenLIT Evaluators
Hapax provides two complementary ways to evaluate content:
- Using Evaluation Decorators (covered in this document) - A lightweight, decorator-based approach for adding evaluations to any Python function
- OpenLIT Integration for Graphs (covered in Advanced OpenLIT Integration) - Direct integration with OpenLIT evaluators in graph execution
The @eval decorator can work with both:
- Built-in local evaluators
- OpenLIT-based evaluators (when
use_openlit=True)
Setup
First, register the evaluators you want to use:
from hapax.core.decorators import register_evaluator
from your_evaluators import CustomEvaluator
# Register built-in evaluators (done automatically)
# - "hallucination"
# - "bias"
# - "toxicity"
# Register custom evaluators
register_evaluator("custom", CustomEvaluator)
Basic Usage
from hapax.core.decorators import eval
@eval(evals=["hallucination"], threshold=0.7)
def generate_response(prompt: str) -> str:
return f"Response to {prompt}"
Features
Registry System
The decorator uses a registry system to manage available evaluators. Built-in evaluators are registered automatically:
hallucination: Check for factual accuracybias: Detect potential biasestoxicity: Identify harmful contentall: Run all registered evaluators
You can add your own evaluators:
class MyEvaluator:
def __init__(self, **config):
self.config = config
def evaluate(self, text: str) -> float:
# Return score between 0 and 1
return 0.5
register_evaluator("my_eval", MyEvaluator)
Using with OpenLIT Evaluators
To use the OpenLIT-based evaluators (which use LLMs for evaluation):
@eval(
evals=["hallucination"],
threshold=0.7,
use_openlit=True, # This enables OpenLIT evaluators
openlit_provider="openai", # "openai" or "anthropic"
metadata={
"contexts": ["Einstein won the Nobel Prize in Physics in 1921."],
"prompt": "When did Einstein win the Nobel Prize?"
}
)
def generate_response(prompt: str) -> str:
# Your generation code
return "Einstein won the Nobel Prize in 1922." # Will fail evaluation
Result Caching
Results are automatically cached based on function inputs:
@eval(evals=["bias"], threshold=0.3, cache_results=True) # cache_results is True by default
def generate_content(prompt: str) -> str:
return f"Content for {prompt}"
# First call: evaluates and caches result
result1 = generate_content("test")
# Second call: uses cached result
result2 = generate_content("test") # faster!
Custom Configuration
Provide additional metadata and evaluation-specific configuration:
@eval(
evals=["toxicity"],
threshold=0.5,
metadata={"domain": "social"},
openlit_config={"model": "toxicity-v2"},
cache_results=False # disable caching if needed
)
def generate_social_post(topic: str) -> str:
return f"Post about {topic}"
Integration with Operations
Seamlessly combine with the @ops decorator for full integration with the Hapax framework:
@ops(name="safe_generate", tags=["nlp", "safe"])
@eval(evals=["all"], threshold=0.6)
def safe_generation(context: str) -> str:
return f"Safely generated response for: {context}"
Example Output
Here's what happens when running different types of evaluations:
# Basic usage - passes evaluation
result = simple_generation("what is the meaning of life?")
# Output: "The answer to what is the meaning of life? is always 42."
# Multiple evaluations - passes all checks
result = comprehensive_check("Tell me about history")
# Output: "Processing Tell me about history with comprehensive checks."
# Combined with @ops - passes evaluation
result = safe_generation("Generate a story")
# Output: "Safely generated response for: Generate a story"
# Strict threshold - fails bias check
try:
result = financial_advice("Should I invest in stocks?")
except EvaluationError as e:
# Output: "Evaluation failed: Evaluations failed: ['bias']. Scores: {'bias': 0.5}"
Error Handling
When an evaluation fails, an EvaluationError is raised with detailed information about:
- Which evaluations failed
- The scores for each evaluation
- The threshold that was exceeded
Example error:
EvaluationError: Evaluations failed: ['bias']. Scores: {'bias': 0.5}
Best Practices
-
Register Custom Evaluators Early
- Register evaluators at module import time
- Use descriptive names for evaluators
-
Choose Appropriate Thresholds
- Lower thresholds (0.3-0.5) for strict checking
- Higher thresholds (0.7-0.9) for more permissive checking
-
Caching Considerations
- Enable caching for expensive evaluations
- Disable caching for evaluations that should always run
- Cache is per-process, not persistent
-
Type Safety
- Evaluators only work with string outputs
- Functions must have type hints
- Return type must be
str
Complete Example
See the examples directory for a complete working example that demonstrates all these features.