Mellea Usage Guidelines
August 20, 2026 · View on GitHub
This file: For code that imports Mellea. For Mellea internals, see
../AGENTS.md.
Copy below into your AGENTS.md or system prompt.
Library: Mellea
Use mellea for LLM interactions. No direct OpenAI/Anthropic calls or LangChain OutputParsers.
Prerequisites: pip install mellea · Docs · Repo
Imports — prefer the top-level prelude. Most names are re-exported from
mellea itself, so a program rarely needs more than one or two import lines:
from mellea import ChatContext, Instruction, Message, Requirement, req, start_session
The prelude covers sessions (start_session, MelleaSession, start_backend,
generative, mfuncs), components (Message, Instruction, Document,
Intrinsic, SimpleComponent, mify, CBlock, Component, ModelOutputThunk,
TemplateRepresentation), contexts (ChatContext, SimpleContext, Context),
requirements (Requirement, ValidationResult, req, check, simple_validate),
sampling (RejectionSamplingStrategy, SamplingResult), and backend config
(Backend, ModelOption, model_ids).
Many symbols are reachable from several modules. Use the canonical path so imports stay consistent across a codebase:
- Anything listed above →
mellea, not a sub-package. - Other protocols and data types →
mellea.core, notmellea.core.base/.backend/.requirement. - Other components, contexts, requirements, strategies →
mellea.stdlib.<subpkg>, not leaf modules like.chator.simple. - A concrete backend →
mellea.backends.<provider>.
Concrete backends are intentionally not in the prelude — import them explicitly so a missing optional dependency gives a targeted install hint:
from mellea.backends.ollama import OllamaModelBackend
1. The @generative Pattern
Don't write prompt templates or regex parsers:
# BAD - don't do this
response = openai.chat.completions.create(...)
age = int(re.search(r"\d+", response).group())
Do use typed function signatures:
from mellea import generative, start_session
@generative
def extract_age(text: str) -> int:
"""Extract the user's age from text."""
...
m = start_session()
age = extract_age(m, text="Alice is 30") # Returns int(30)
2. Complex Types
from pydantic import BaseModel
from mellea import generative
class UserProfile(BaseModel):
name: str
age: int
interests: list[str]
@generative
def parse_profile(bio: str) -> UserProfile: ...
3. Chain-of-Thought
Add reasoning field to force the LLM to "think" before answering:
from typing import Literal
from pydantic import BaseModel, Field
class AnalysisResult(BaseModel):
reasoning: str # LLM fills first
conclusion: Literal["approve", "reject"]
confidence: float = Field(ge=0.0, le=1.0)
@generative
def analyze_document(doc: str) -> AnalysisResult: ...
4. Control Flow
Use Python if/for/while. No graph frameworks needed:
if analyze_sentiment(m, email) == "negative":
draft = draft_apology(m, email)
else:
draft = draft_response(m, email)
5. Instruct-Validate-Repair
For strict requirements, use m.instruct():
from mellea.stdlib.requirements import req, simple_validate
from mellea.stdlib.sampling import RejectionSamplingStrategy
email = m.instruct(
"Write an invite for {{name}}",
requirements=[
req("Must be formal"),
req("Lowercase only", validation_fn=simple_validate(lambda x: x.islower()))
],
strategy=RejectionSamplingStrategy(loop_budget=3),
user_variables={"name": "Alice"}
)
6. Small Model Fix
Small models (1B-8B) can't calculate. Extract params with LLM, compute in Python:
from pydantic import BaseModel
class PhysicsParams(BaseModel):
speed_a: float
speed_b: float
delay_hours: float
@generative
def extract_params(text: str) -> PhysicsParams:
"""EXTRACT numbers only. Do not calculate."""
...
def calculate_gap(p: PhysicsParams) -> float:
return p.speed_a * p.delay_hours
7. One-Shot Examples
If model struggles, add examples to docstring:
@generative
def identify_fruit(text: str) -> str | None:
"""
Extract fruit from text, or None if none mentioned.
Ex: "I ate an apple" -> "apple"
Ex: "The sky is blue" -> None
"""
...
8. Backend Config
from mellea import start_session
from mellea.backends.model_options import ModelOption
m = start_session(
model_id="granite3.3:8b",
model_options={ModelOption.TEMPERATURE: 0.0, ModelOption.MAX_NEW_TOKENS: 500}
)
Options: TEMPERATURE, MAX_NEW_TOKENS, SYSTEM_PROMPT, SEED, TOOLS, CONTEXT_WINDOW, THINKING, STREAM
9. Async
@generative
async def extract_age(text: str) -> int:
"""Extract age."""
...
result = await extract_age(m, text="Alice is 30")
Session methods: ainstruct, achat, aact, avalidate, aquery, atransform
10. Auth
- Ollama:
start_session()(no setup) - OpenAI:
export OPENAI_API_KEY="..." - Watsonx:
export WATSONX_API_KEY="...",WATSONX_URL,WATSONX_PROJECT_ID
Never hardcode API keys.
11. Anti-Patterns
- Don't retry
@generativecalls — Mellea handles retries internally - Don't use
json.loads()— use typed returns - Don't wrap single functions in classes
- Do use
try/exceptat app boundaries for network errors
12. Debugging
from mellea.core import MelleaLogger
MelleaLogger.get_logger().setLevel("DEBUG")
m.last_prompt()— see exact prompt sent
13. Common Errors
| Error | Fix |
|---|---|
ComponentParseError | LLM output didn't match type—add docstring examples |
TypeError: missing positional argument | First arg must be session m |
ConnectionRefusedError | Run ollama serve |
| Output wrong/None | Model too small—try larger or add reasoning field |
14. Testing
uv run pytest test/ -m "not qualitative" # Fast: tests only, skip quality checks
uv run pytest # Full: tests + examples + quality checks
15. Feedback
Found a workaround or pattern? Add it to Section 13 (Common Errors) above, or update this file with new guidance.