Skip to content
Open
Show file tree
Hide file tree
Changes from all 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.


- **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