Documentation Index
Fetch the complete documentation index at: https://docs.mellea.ai/llms.txt
Use this file to discover all available pages before exploring further.
Prerequisites: The Requirements System,
Quick Start complete, pip install mellea.
Mellea programs encounter two categories of failure: expected failures (IVR
exhaustion, precondition violations) that are part of normal operation, and
unexpected errors (backend connectivity, parse failures) that indicate
configuration or implementation problems.
Expected failures
IVR loop exhaustion: SamplingResult.success = False
When instruct() is called with return_sampling_results=True and the IVR loop
exhausts its budget without satisfying all requirements, SamplingResult.success is
False. This is not a Python exception — it is a normal return value that your code
should handle.
from mellea import start_session
from mellea.stdlib.requirements import req, simple_validate
from mellea.stdlib.sampling import RejectionSamplingStrategy
m = start_session()
result = m.instruct(
"Write a haiku about the ocean.",
requirements=[
req(
"Must have exactly 17 syllables (5-7-5).",
validation_fn=simple_validate(
lambda x: (
len(x.split()) <= 20, # rough proxy; replace with a real syllable counter
"Syllable count does not match the 5-7-5 pattern.",
)
),
),
],
strategy=RejectionSamplingStrategy(loop_budget=5),
return_sampling_results=True,
)
if result.success:
print(str(result.result))
else:
# All attempts failed — decide what to do
print("Could not generate a valid haiku after 5 attempts.")
print("Best attempt:", str(result.sample_generations[0].value))
Common fallback patterns when success is False:
- Use the best attempt anyway —
result.sample_generations[0].value gives the
first (often the best) generation, even if requirements were not fully satisfied.
- Lower the bar — retry with reduced requirements or a higher
loop_budget.
- Return an error indicator — tell the caller the operation could not be
completed to spec, and let it decide.
- Log and alert — if this should rarely fail, log the attempts and notify.
Inspecting failure reasons
SamplingResult.sample_validations gives per-attempt validation details. Use them
to understand which requirements are failing and why:
if not result.success:
for attempt_idx, validations in enumerate(result.sample_validations):
print(f"Attempt {attempt_idx + 1}:")
for requirement, val_result in validations:
if not val_result:
print(f" FAIL: {requirement.description}")
print(f" Reason: {val_result.reason}")
A requirement that fails on every attempt usually indicates one of:
- The model cannot satisfy this constraint with the current prompt and model.
- The
validation_fn has a bug (returns False unconditionally or has a logic error).
- The requirement is genuinely contradictory with the instruction.
Precondition failures: PreconditionException
When precondition_requirements are attached to a @generative call, Mellea
validates the inputs before calling the model. If any precondition fails,
PreconditionException is raised immediately — no model call is made:
from typing import Literal
from mellea import generative, start_session
from mellea.core import Requirement
from mellea.stdlib.components.genslot import PreconditionException
from mellea.stdlib.requirements import simple_validate
@generative
def classify_sentiment(text: str) -> Literal["positive", "negative", "neutral"]:
"""Classify the sentiment of the text."""
m = start_session()
try:
result = classify_sentiment(
m,
text="I love this!",
precondition_requirements=[
Requirement(
"Input must be fewer than 500 characters.",
validation_fn=simple_validate(
lambda x: (
len(x) < 500,
f"Input is {len(x)} characters; must be under 500.",
)
),
)
],
)
print(result)
except PreconditionException as e:
print(f"Invalid input: {e}")
for val_result in e.validation:
print(f" - {val_result.reason}")
# Handle gracefully: sanitize input, reject the request, etc.
PreconditionException.validation is a list of ValidationResult objects for the
requirements that failed. Each .reason field explains what was wrong.
Use preconditions to:
- Validate untrusted inputs before they reach the model
- Enforce interface contracts between pipeline stages
- Fail fast on inputs that are guaranteed to produce bad output
Unexpected errors
Backend connection errors
If Ollama is not running, or a cloud API key is invalid, the backend raises an
exception on the first model call:
import mellea
try:
m = mellea.start_session()
result = m.instruct("Hello.")
print(str(result))
except Exception as e:
# Backend errors are not Mellea-specific exceptions — they come from the
# underlying HTTP client or the backend constructor.
print(f"Backend error: {e}")
# Handle: check connectivity, validate credentials, fall back to another backend
For production code, wrap session creation and the first call together:
import mellea
def create_session_or_none():
try:
m = mellea.start_session()
# Probe the connection with a cheap call
m.chat("ping")
return m
except Exception as e:
print(f"Could not connect to backend: {e}")
return None
Parse failures: ComponentParseError
When @generative or instruct(format=...) is used with a Pydantic model or
Literal return type, Mellea parses the raw model output into the declared type.
If parsing fails, a ComponentParseError is raised.
This typically means the model produced output that does not conform to the schema.
The IVR loop retries on parse failure automatically — ComponentParseError surfaces
only if all retries are exhausted.
from typing import Literal
from mellea import generative, start_session
from mellea.core.base import ComponentParseError
@generative
def classify(text: str) -> Literal["a", "b", "c"]:
"""Classify the text into category a, b, or c."""
m = start_session()
try:
result = classify(m, text="...")
except ComponentParseError as e:
print(f"Model output could not be parsed: {e}")
# Fall back to a raw string extraction or a default value
If ComponentParseError occurs in practice, check:
- Whether the model is large enough to follow the output format instructions.
- Whether the instruction and docstring are clear about the expected format.
- Whether the backend supports constrained decoding for the return type.
Fallback and retry patterns
Fallback to a simpler call
If a structured call fails, fall back to a plain instruct():
from pydantic import BaseModel
from mellea import generative, start_session
from mellea.core.base import ComponentParseError
class ExtractedData(BaseModel):
name: str
email: str
@generative
def extract(text: str) -> ExtractedData:
"""Extract name and email from the text."""
m = start_session()
try:
data = extract(m, text="Contact Alice at alice@example.com.")
print(data.name, data.email)
except ComponentParseError:
# Fall back: get the raw text and parse manually
raw = m.instruct("Extract the name and email from: {{text}}",
user_variables={"text": "Contact Alice at alice@example.com."})
print("Raw fallback:", str(raw))
Fallback to a different model
For calls that require higher capability, escalate to a stronger model on failure:
from mellea import MelleaSession
from mellea.backends.ollama import OllamaModelBackend
from mellea.backends import model_ids
from mellea.stdlib.sampling import RejectionSamplingStrategy
def instruct_with_fallback(text: str) -> str:
m_fast = MelleaSession(OllamaModelBackend(model_ids.IBM_GRANITE_4_MICRO_3B))
result = m_fast.instruct(
text,
strategy=RejectionSamplingStrategy(loop_budget=3),
return_sampling_results=True,
)
if result.success:
return str(result.result)
# Escalate to a larger model
m_strong = MelleaSession(OllamaModelBackend(model_ids.IBM_GRANITE_3_3_8B))
return str(m_strong.instruct(text))
This is the basis of the SOFAI (System 1 / System 2) pattern — fast model first,
strong model only when needed. Mellea provides SOFAISamplingStrategy as a
built-in implementation. See Inference-Time Scaling.
Logging failures
Use Python’s standard logging module to record failures alongside generation
details:
import logging
from mellea import start_session
from mellea.stdlib.sampling import RejectionSamplingStrategy
logger = logging.getLogger(__name__)
m = start_session()
result = m.instruct(
"Classify: {{text}}",
strategy=RejectionSamplingStrategy(loop_budget=3),
user_variables={"text": "..."},
return_sampling_results=True,
)
if not result.success:
logger.warning(
"instruct() failed after %d attempts",
len(result.sample_generations),
extra={
"attempts": len(result.sample_generations),
"first_output": str(result.sample_generations[0].value),
},
)
For structured telemetry across all calls, see
Telemetry.
See also: The Requirements System |
Write Custom Verifiers