Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/docs/examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ to run.
| -------- | ------------- |
| `instruct_validate_repair/` | The IVR loop end-to-end: basic generation, adding requirements, automatic repair on failure, custom validators |
| `generative_stubs/` | `@generative` functions with typed returns, pipeline composition, `ChatContext` persona injection, pre/postcondition checks |
| `context/` | Context inspection, sampling with context trees, parallel context branches |
| `sessions/` | Custom session types and backend selection |
| `context/` | Context inspection, sampling with context trees, sliding window and threshold compaction |
| `sessions/` | Custom session types, safety filtering, and backend selection |
| `async/` | How to utilize basic async capabilities |
| `streaming/` | `stream()` with per-chunk validation, typed event vocabulary, early-exit on fail |

Expand Down Expand Up @@ -62,6 +62,7 @@ to run.

| Category | What it shows |
| -------- | ------------- |
| `requirements/` | String and functional validation requirements, custom predicate functions, and validation repair |
| `intrinsics/` | [Guardian adapter functions](../how-to/safety-guardrails.md): `guardian_check()` for harm, jailbreak, social bias, groundedness; `policy_guardrails()`; `factuality_detection()` / `factuality_correction()` |
| `safety/` | *(Examples removed — see [Guardian how-to guide](../how-to/safety-guardrails.md) for the current API. The `RepairTemplateStrategy` gap is tracked in [#1071](https://github.com/generative-computing/mellea/issues/1071).)* |

Expand All @@ -79,6 +80,7 @@ to run.

| Category | What it shows |
| -------- | ------------- |
| `sampling/` | Pre-packaged sampling presets and custom rejection sampling configurations |
| `aLora/` | Training aLoRA adapters for fast constraint checking; performance optimisation |
| `intrinsics/` | *(Non-Guardian)* Answer relevance, hallucination detection, citation validation — specialised adapter-backed checks. For Guardian safety functions see [Safety and validation](#safety-and-validation) above |
| `granite-switch/` | Running adapter functions via OpenAI backend with Granite Switch embedded adapters |
Expand All @@ -88,6 +90,7 @@ to run.

| Category | What it shows |
| -------- | ------------- |
| `audio_text_models/` | Audio-language models, `AudioBlock`, speech prompting, backend support matrix |
| `image_text_models/` | Vision-language models, `ImageBlock`, multimodal prompting, backend support matrix |

### Observability
Expand Down
102 changes: 9 additions & 93 deletions docs/examples/context/README.md
Original file line number Diff line number Diff line change
@@ -1,97 +1,13 @@
# Context Examples

This directory contains examples demonstrating how to work with Mellea's context system: inspecting per-attempt contexts produced by sampling strategies, and shrinking contexts with the `Compactor` protocol.
Examples demonstrating context tree inspection with sampling strategies and context shrinking using the `Compactor` protocol.

## Files
## Examples

### [contexts_with_sampling.py](./contexts_with_sampling.py)

Shows how to retrieve and inspect context information when using sampling strategies and validation.

**Key Features:**

- Using `RejectionSamplingStrategy` with requirements
- Accessing `SamplingResult` objects to inspect generation attempts
- Retrieving context for different generation attempts
- Examining validation contexts for each requirement
- Understanding the context tree structure

**Usage:**

```
python docs/examples/context/contexts_with_sampling.py
```

### [window_compactor.py](./window_compactor.py)

`WindowCompactor` — opt-in by passing `compactor=` (or the `window_size=` sugar). Demonstrates system-prefix pinning, `pin_system_and_initial_user`, `pin_nothing` (pure last-N), and `size=0` to clear the body.

### [threshold_compactor.py](./threshold_compactor.py)

`ThresholdCompactor` — gate an inner compactor on the conversation's cumulative token size. The reading is taken from the most recent `ModelOutputThunk`'s `total_tokens`, which for a chat backend equals `prompt_tokens` (full conversation history sent to the model) + `completion_tokens` (reply). The gate fires once the running conversation size crosses the threshold; once compaction shrinks the context, the next call produces a smaller reading and the gate closes again.

### [custom_compactor.py](./custom_compactor.py)

Implement the `Compactor` protocol with a plain class (no inheritance). Shows Pattern 1 (wired into `ChatContext`) and Pattern 2 (manual `compact()` call).

### [react_compaction.py](./react_compaction.py)

Compose the ReACT loop with a sync `Compactor`. Two integration points:

- **Per-add** — wire a `Compactor` onto the `ChatContext` so it runs every time `react()` appends a Message, ToolMessage, or thunk.
- **Per-turn** — pass `compactor=` to `react()`; it fires once per ReACT iteration after the tool observation.

`LLMSummarizeCompactor` is also a sync `Compactor` — it hides the async backend call internally (worker thread when called from an already-running event loop) so callers don't have to think about sync vs async.

Use `pin_react_initiator` (from `mellea.stdlib.components.react`) as the predicate so the goal and tool registration survive compaction.

## Concepts Demonstrated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the deleted usage/concept material is covered in the docs. This part is the exception.

compaction. The context/ README previously carried the only conceptual explanation of the Compactor protocol (window vs threshold vs custom, pin predicates, per-add vs per-turn wiring with react()). After this PR, that story survives only in the example docstrings and the auto-generated API reference (api/mellea/stdlib/context/compactor.mdx) — no concepts/ or how-to/ page mentions compaction at all. Since the README collapse is the right call per #1746, should we either add a short compaction section to concepts/context-and-sessions.md or open a follow-up issue so the conceptual docs catch up?

@rafvasq rafvasq Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me like this is a documentation gap and deserves a follow up issue to include compaction somewhere on https://docs.mellea.ai/next/concepts/context-and-sessions, instead of laying it out in these examples.

@rafvasq rafvasq Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up including this doc update in this PR: a95df07


- **Sampling Results**: Working with `SamplingResult` objects
- **Context Inspection**: Accessing generation and validation contexts
- **Multiple Attempts**: Examining different generation attempts
- **Context Trees**: Understanding how contexts link together
- **Validation Context**: Inspecting how requirements were evaluated
- **Compaction Protocol**: Sync `Compactor` for per-`add()` shrinking
- **Pin Predicates**: Auto-protect leading system messages or the user's initial prompt during compaction

## Key APIs

```python
# Get sampling result with full context information
res = m.instruct(
"Write a sentence.",
requirements=[...],
strategy=RejectionSamplingStrategy(loop_budget=3),
return_sampling_results=True
)

# Access different generation attempts
res.sample_generations[index]
res.sample_contexts[index]
res.sample_validations[index]

# Navigate context tree
gen_ctx.previous_node.node_data
val_ctx.node_data
```

```python
# Wire a compactor into a ChatContext (Pattern 1 — runs on every add())
from mellea.stdlib.context import ChatContext, WindowCompactor, ThresholdCompactor

ctx = ChatContext(compactor=WindowCompactor(size=5)) # default: pin_system
ctx = ChatContext(window_size=5) # sugar for the line above
ctx = ChatContext(
compactor=ThresholdCompactor(WindowCompactor(size=5), threshold=8000),
)

# Manual compaction (Pattern 2)
ctx = WindowCompactor(size=0).compact(ctx) # drop body, keep pinned prefix
```

## Related Documentation

- See `mellea/stdlib/context/` for context and compactor implementations
- See `mellea/stdlib/sampling/` for sampling strategies
- See `mellea/stdlib/frameworks/react.py` for the ReACT loop
| File | Description |
| :--- | :--- |
| [`contexts_with_sampling.py`](./contexts_with_sampling.py) | Inspecting per-attempt generation contexts, validation contexts, and context tree navigation in sampling results. |
| [`window_compactor.py`](./window_compactor.py) | Sliding window compaction with system-prefix pinning and custom pin predicates. |
| [`threshold_compactor.py`](./threshold_compactor.py) | Gating window compaction on cumulative conversation token thresholds. |
| [`custom_compactor.py`](./custom_compactor.py) | Implementing custom `Compactor` protocols via class implementations and manual invocation. |
| [`react_compaction.py`](./react_compaction.py) | Composing ReACT agent loops with per-add and per-turn context compaction strategies. |
11 changes: 8 additions & 3 deletions docs/examples/context/contexts_with_sampling.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
# pytest: ollama, e2e
"""Inspecting context trees and validation contexts from sampling results.

Demonstrates retrieving and inspecting generation contexts, validation contexts,
and parent context trees across multiple rejection sampling attempts.

Usage:
uv run python docs/examples/context/contexts_with_sampling.py
"""

from mellea.stdlib.sampling import RejectionSamplingStrategy
from mellea.stdlib.session import start_session

# You can retrieve context information when using SamplingStrategies
# and validation.

m = start_session()

# We want the full SamplingResult.
Expand Down
25 changes: 12 additions & 13 deletions docs/examples/context/custom_compactor.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
# pytest: unit
"""Implementing the Compactor protocol — structural and marker typing.
"""Implementing custom compactors with structural and marker typing.

The Compactor Protocol is structurally typed: any class with a
The `Compactor` protocol is structurally typed: any class with a
`compact(ctx, *, backend=None) -> ChatContext` method satisfies it.

Two ways to use a custom compactor with a `ChatContext`:

- **Pattern 1 (wired into `ChatContext.add`)**: inherit from
`InlineCompactor` for simple compactors that don't make backend
calls. Backend-calling compactors should instead be wrapped in
`ThresholdCompactor` (which gates them by token count) before being
attached to a `ChatContext`.
- **Pattern 2 (manual call)**: invoke `compact()` directly between
turns. Any object satisfying the structural `Compactor` protocol
works — no inheritance needed. Works for backend-calling compactors
or any other custom compactor.
Two patterns for using custom compactors with `ChatContext`:

- **Pattern 1 (wired into `ChatContext.add`)**: Inherit from `InlineCompactor`
for lightweight compactors that don't make backend calls. Backend-calling
compactors should instead be wrapped in `ThresholdCompactor` before being attached.
- **Pattern 2 (manual call)**: Invoke `compact()` directly between turns. Any
object satisfying the structural `Compactor` protocol works without inheritance.

Usage:
uv run python docs/examples/context/custom_compactor.py
"""

from mellea.stdlib.components.chat import Message
Expand Down
25 changes: 11 additions & 14 deletions docs/examples/context/react_compaction.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
# pytest: unit
"""Compose the ReACT loop with a sync `Compactor`.
"""Compose the ReACT agent loop with context compaction.

Two integration points are available, and they're complementary:
Demonstrates integrating sync `Compactor` strategies with the ReACT agent loop across
two complementary integration points:

1. **Per-add** — the `ChatContext`'s own compactor runs every time the
ReACT loop appends a Message, ToolMessage, or thunk. This is fine
for cheap strategies like `WindowCompactor`.
2. **Per-turn** — pass `compactor=` to `react(...)` to invoke a
compactor once per ReACT iteration after the tool observation. Use
it for heavier strategies that should fire at turn boundaries
instead of on every component append.
1. **Per-add** — the `ChatContext`'s own compactor runs every time the ReACT loop
appends a Message, ToolMessage, or thunk (suitable for lightweight compactors like `WindowCompactor`).
2. **Per-turn** — pass `compactor=` to `react(...)` to invoke a compactor once per
ReACT iteration after the tool observation (suitable for turn-boundary compaction).

In both cases use `pin_react_initiator` (from
`mellea.stdlib.components.react`) so the goal and tool registration
survive compaction.
In both cases, use `pin_react_initiator` (from `mellea.stdlib.components.react`)
so the goal and tool registrations survive compaction.

This example exercises the wiring end-to-end against a fake backend so
no LLM is required.
Usage:
uv run python docs/examples/context/react_compaction.py
"""

from __future__ import annotations
Expand Down
17 changes: 9 additions & 8 deletions docs/examples/context/threshold_compactor.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# pytest: unit
"""ThresholdCompactor — gate an inner Compactor on conversation size.

Reads `ModelOutputThunk.generation.usage` from the most recent thunk
in the context. For a chat backend, `total_tokens` on that thunk is
`prompt_tokens` (full conversation history sent to the model) plus
`completion_tokens` (the reply), so it tracks *cumulative* context
size — not just one call's isolated tokens. The inner compactor fires
once that running size exceeds the configured threshold.
"""ThresholdCompactor — gate an inner compactor on conversation size.

Reads `ModelOutputThunk.generation.usage` from the most recent thunk in the context.
For a chat backend, `total_tokens` on that thunk is `prompt_tokens` (full conversation
history sent to the model) plus `completion_tokens` (the reply), tracking cumulative
context size. The inner compactor fires once running size exceeds the threshold.

Usage:
uv run python docs/examples/context/threshold_compactor.py
"""

from mellea.core.base import ModelOutputThunk
Expand Down
9 changes: 7 additions & 2 deletions docs/examples/context/window_compactor.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# pytest: unit
"""WindowCompactor — keep the last N body components.

Demonstrates the default behaviour, the `window_size=` sugar on
`ChatContext`, and how the auto-pinned system prefix is preserved.
Demonstrates sliding window compaction using `WindowCompactor` and the `window_size=`
sugar on `ChatContext`. Shows system-prefix pinning (`pin_system`), preserving initial
user prompts (`pin_system_and_initial_user`), pure last-N (`pin_nothing`), and `size=0`
to clear the conversation body while keeping pinned prefixes.

Usage:
uv run python docs/examples/context/window_compactor.py
"""

from mellea.stdlib.components.chat import Message
Expand Down
70 changes: 12 additions & 58 deletions docs/examples/generative_stubs/README.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,14 @@
# Generative Stubs Examples

This directory contains examples demonstrating the `@generative` decorator for creating type-safe, composable LLM functions.

## Files

### [generative_stubs.py](./generative_stubs.py)
Basic introduction to generative stubs with sentiment classification and text summarization.

**Key Features:**
- Using `@generative` decorator for type-safe LLM functions
- Literal types for constrained outputs
- Docstrings as prompts for the LLM
- Simple function composition

### [generate_with_context.py](./generate_with_context.py)
Shows how to use generative stubs with custom context and grounding information.

### [generative_gsm8k.py](./generative_gsm8k.py)
Demonstrates using generative stubs for mathematical reasoning tasks (GSM8K dataset).

### [generative_stubs_with_requirements.py](./generative_stubs_with_requirements.py)
Combines generative stubs with requirements for validated outputs.

### [investment_advice.py](./investment_advice.py)
A more complex example using generative stubs for financial analysis.

### [inter_module_composition/](inter_module_composition/)
Subdirectory with examples of composing multiple generative functions together.

## Concepts Demonstrated

- **Type Safety**: Using Python type hints for structured outputs
- **Docstring Prompts**: Leveraging docstrings as instructions to the LLM
- **Composition**: Building complex workflows from simple generative functions
- **Requirements**: Adding validation to generative functions
- **Context Management**: Using grounding context with generative stubs

## Basic Usage

```python
from mellea import generative, start_session
from typing import Literal

@generative
def classify_sentiment(text: str) -> Literal["positive", "negative"]:
"""Classify the sentiment of the given text."""

@generative
def generate_summary(text: str) -> str:
"""Generate a concise summary under 20 words."""

with start_session() as m:
sentiment = classify_sentiment(m, text="I love this!")
summary = generate_summary(m, text="Long document...")
```

## Related Documentation

- See `mellea/stdlib/components/genstub.py` for implementation
Examples demonstrating the `@generative` decorator for creating type-safe, composable LLM functions.

## Examples

| File | Description |
| :--- | :--- |
| [`generative_stubs.py`](./generative_stubs.py) | Basic sentiment classification and text summarization with `@generative` functions and `Literal` return types. |
| [`generate_with_context.py`](./generate_with_context.py) | Generative stubs with dynamic context and role modification. |
| [`generative_gsm8k.py`](./generative_gsm8k.py) | Stepwise chain-of-thought mathematical reasoning on GSM8K problems using structured Pydantic models. |
| [`generative_stubs_with_requirements.py`](./generative_stubs_with_requirements.py) | Enforcing preconditions and postcondition requirements on generative stub executions. |
| [`investment_advice.py`](./investment_advice.py) | Multi-step financial advice workflow with semantic preconditions and compliance checks. |
| [`inter_module_composition/`](./inter_module_composition/) | Modular generative pipeline composing summarization and decision-making modules. |
14 changes: 9 additions & 5 deletions docs/examples/generative_stubs/generate_with_context.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# pytest: ollama, e2e
"""Generative stubs with session context and role modification.

Demonstrates using generative stubs within a session containing `ChatContext`,
showing how injecting context blocks (such as personas) modifies the behavior
and evaluation style of multiple generative functions without changing their signatures.

Usage:
uv run python docs/examples/generative_stubs/generate_with_context.py
"""

from mellea import generative, start_session
from mellea.backends import ModelOption
from mellea.core import CBlock
from mellea.stdlib.context import ChatContext

# Generative stubs can be used with sessions that have context.
# By utilizing context, you can change the results of several
# functions at the same time. This lets you write a function
# once but use it in different ways.


@generative
def grade_essay(essay: str) -> int:
Expand Down
22 changes: 18 additions & 4 deletions docs/examples/generative_stubs/generative_gsm8k.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
# pytest: ollama, qualitative, e2e, slow

"""Example of chain-of-thought reasoning on a mathematical question from the GSM8K dataset, structured as code for improved performance with Granite 4 models. The original accuracy in standard "thinking" mode is approximately 80%, while this implementation achieves 85-89% accuracy—up to 9 points higher.

This demonstrates that generative decorators are sufficient for complex reasoning tasks: not only do they maintain or improve performance, but they also significantly enhance observability and control. For instance, the structured Thought titles can be easily surfaced in a UI, providing instant insight into the model's reasoning process.
# /// script
# dependencies = [
# "mellea",
# "datasets",
# "pydantic",
# ]
# ///
"""Chain-of-thought mathematical reasoning with generative stubs.

Demonstrates structured chain-of-thought reasoning on mathematical questions from
the GSM8K dataset using `@generative` functions returning structured Pydantic models.

Structuring reasoning steps as typed code improves accuracy on Granite 4 models
(from ~80% in standard thinking mode to 85-89%) while enhancing observability and
control by surfacing structured step titles.

Usage:
uv run docs/examples/generative_stubs/generative_gsm8k.py
"""

from datasets import load_dataset
Expand Down
Loading
Loading