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, not mellea.core.base / .backend / .requirement.
  • Other components, contexts, requirements, strategies → mellea.stdlib.<subpkg>, not leaf modules like .chat or .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 @generative calls — Mellea handles retries internally
  • Don't use json.loads() — use typed returns
  • Don't wrap single functions in classes
  • Do use try/except at 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

ErrorFix
ComponentParseErrorLLM output didn't match type—add docstring examples
TypeError: missing positional argumentFirst arg must be session m
ConnectionRefusedErrorRun ollama serve
Output wrong/NoneModel 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.