> ## Documentation Index
> Fetch the complete documentation index at: https://loopguard.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> LoopGuard is a Python runtime guardrail SDK. Install loopguard-runtime and import loopguard. Use /getting-started and /guides/integration for application code.
> Use the Concepts section for agent loops, tests versus traces versus evals, and trajectory/convergence. The convergence helper compares counts without checking success; LoopGuard events are not OpenTelemetry spans. Distinguish these general concepts from implemented SDK features.
> The exact, semantic, calc, and trap UI examples illustrate Python SDK detection behavior. Read /demo/scenarios for runnable SDK examples and /guides/integration to attach callbacks or inspect streamed updates.
> These docs follow the checkout. PyPI 0.2.1 lacks Gemini support, controlled demo scenarios, and additional decision display fields; check /development/status before describing release capabilities.
> Repetition alone does not force STOP. REPLAN and PAUSE are recorded recommendations. Observe-only mode records decisions without raising. Follow /guides/integration for interrupt propagation.
> Use the Python API, CLI, and configuration references for supported names and flags. Screenshots illustrate observed runs, not guaranteed stop counts or benchmark results.

# Demo scenarios

> Run the Python SDK behind the UI examples and compare healthy and stuck behavior.

Use the UI or Python SDK to compare healthy runs with agents that get stuck.
Scripted agents make detection easy to reproduce; the ReAct scenarios use a real
model to choose tool calls. Controlled scenarios fix the tool responses, not the
model's decisions.

The four visual examples below connect what you see to actual Python SDK usage:
exact repetition, semantic repetition, a healthy calculation, and an impossible
search goal. The UI displays the SDK's events, signals, decisions, and final verdict;
you can inspect those messages directly in Python without starting the UI server.

Start with [local setup](https://loopguard.mintlify.site/demo/running-locally.md). Then choose a scenario in the UI,
or run `python main.py SCENARIO` from the repository root.

## Run the visual examples through the SDK

Use `LoopGuard.stream()` to inspect the same events, signals, and decisions shown
in the UI. `get_scenario()` supplies a demo agent, detectors, and initial input.
In your application, pass your own agent and input or use the
[callback integration](https://loopguard.mintlify.site/guides/integration.md).

Run these snippets from the source checkout after
[installing the demo dependencies](https://loopguard.mintlify.site/demo/running-locally.md#set-up-the-checkout).
They load the root `.env` just like the demo entry points. The `exact` example needs
no credentials; check the requirements below before running the other three.

## What each run needs

| Scenario | Chat model | Embeddings | Purpose |
| - | - | - | - |
| `exact` | No | No | Identical calls returning no results. |
| `semantic` | No | Yes | Paraphrased requests with unchanged results. |
| `controlled_progress` | Yes | No | Pending, processing, then completed. |
| `controlled_503` | Yes | No | Two temporary failures, then success. |
| `controlled_401` | Yes | No | A permanent authentication failure. |
| `controlled_empty` | Yes | Yes | Repeated empty results. |
| `calc` | Yes | Yes | A solvable arithmetic task. |
| `trap` | Yes | Yes | A real search agent pursuing an impossible goal. |

> [!NOTE]
>
> `exact` runs offline after dependency installation. Every other scenario can make
> provider calls. `calc` and `trap` include semantic detection, so model credentials
> alone are not enough when chat and embedding providers differ.

## Exact loop

```bash theme={null}
python main.py exact
```

**What happens:** a scripted agent repeatedly searches for the weather in Paris;
the tool always returns `no results`.

**Why it matters:** repeated calls become suspicious when the results never change.

**Expected behavior:** exact repetition accumulates, followed by stagnant-result
evidence. The policy reaches `STOP`, and the graph turns red in the UI.
This scenario uses `LoopDetector` and `StallDetector`, so its stop timing differs
from the default callback example, which also includes `ProgressDetector`.

**Python SDK:** run the same agent and detector mix, then inspect the evidence and
policy decisions that the UI displays.

```python theme={null}
from dotenv import load_dotenv
from loopguard import LoopGuard
from loopguard.scenarios import get_scenario

load_dotenv(".env")
_, agent, detectors, initial = get_scenario("exact")
guard = LoopGuard(detectors=detectors)

for message in guard.stream(agent, initial=initial):
    if message["type"] in {"signal", "decision", "error", "done"}:
        print(message)
```

The repeated calls first produce warning evidence. Stagnant output then combines
with repetition to reach a `stop` decision; the final `done` message has
`interrupted: True`.

![LoopGuard UI showing repeated weather searches, no-results observations, a red graph, and an interrupted verdict](https://mintcdn.com/loopguard/XpzeUOn7Yfd_-2Y1/images/exact-loop.png?fit=max\&auto=format\&n=XpzeUOn7Yfd_-2Y1\&q=85\&s=a83a57d59d6367ef991f704247894c34)

A LoopGuard demo run showing identical searches and empty results ending in interruption.

## Semantic loop

```bash theme={null}
python main.py semantic
```

**What happens:** a scripted agent changes the search wording while pursuing the
same intent. Every search still comes back empty.

**Why it matters:** exact signatures differ even though the approach hasn't changed.

**Expected behavior:** semantic similarity and stagnant results provide evidence
for stopping. The embedding provider affects similarity scores and timing.

**Python SDK:** select the paraphrasing agent and its embedding-backed detector.
The `signal` messages expose the similarity evidence behind the screenshot's alert.

```python theme={null}
from dotenv import load_dotenv
from loopguard import LoopGuard
from loopguard.scenarios import get_scenario

load_dotenv(".env")
_, agent, detectors, initial = get_scenario("semantic")
guard = LoopGuard(detectors=detectors)

for message in guard.stream(agent, initial=initial):
    if message["type"] in {"signal", "decision", "error", "done"}:
        print(message)
```

![LoopGuard UI displaying a semantic-loop alert, stagnant results, and a stopped paraphrasing search agent](https://mintcdn.com/loopguard/XpzeUOn7Yfd_-2Y1/images/semantic-loop.png?fit=max\&auto=format\&n=XpzeUOn7Yfd_-2Y1\&q=85\&s=fe982ef065a536076835ce217007ac39)

Semantic evidence catches repeated intent across different search phrases.

## Controlled progress

```bash theme={null}
python main.py controlled_progress
```

**What happens:** the real agent checks a job while the controlled tool returns
`pending`, `processing`, then `completed` with useful status details.

**Why it matters:** the same status tool is useful when its output advances.

**Expected behavior:** let the run finish. This scenario uses a looser exact-repeat
threshold of four so three changing status calls don't create an unnecessary warning.

## Controlled 503 recovery

```bash theme={null}
python main.py controlled_503
```

**What happens:** the tool returns `503 temporarily unavailable` twice and then
returns a success message.

**Why it matters:** repetition alone is not a reason to stop. Allowing short
retries lets the agent recover from a temporary failure.

**Expected behavior:** retryable-failure evidence can warn, but should allow this
short recovery sequence. The model still decides whether to issue the next call.

## Controlled 401 failure

```bash theme={null}
python main.py controlled_401
```

**What happens:** the controlled tool returns `401 unauthorized: token expired`.

**Why it matters:** repeating this request cannot repair the credential.

**Expected behavior:** stop for the permanent failure. This demo deliberately sets
the repeated-failure threshold to one, so the first observed auth failure can stop
the run. The default live detector uses a threshold of two.

## Controlled empty results

```bash theme={null}
python main.py controlled_empty
```

**What happens:** a real model keeps checking a tool that returns `no results`.

**Why it matters:** the agent can change its wording without finding useful information.

**Expected behavior:** repeated or similar actions combined with no progress
should reach `STOP`. This scenario includes an embedding-backed detector, even
though the tool responses are controlled.

## Healthy calculation

```bash theme={null}
python main.py calc
```

**What happens:** a real agent uses the calculator for `4801 * 379 + 1234` and
returns an answer.

**Why it matters:** a normal successful tool call should finish without interruption.

**Expected behavior:** useful output lets the run finish without a fatal loop alert.
This is the healthy comparison for the stuck search runs.

**Python SDK:** the real agent gets a useful result and finishes. Inspect the
decision stream and expect `interrupted: False` in the final `done` message when
the task succeeds. Check for `error` messages separately.

```python theme={null}
from dotenv import load_dotenv
from loopguard import LoopGuard
from loopguard.scenarios import get_scenario

load_dotenv(".env")
_, agent, detectors, initial = get_scenario("calc")
guard = LoopGuard(detectors=detectors, recursion_limit=30)

for message in guard.stream(agent, initial=initial):
    if message["type"] in {"signal", "decision", "error", "done"}:
        print(message)
```

![LoopGuard UI showing a calculator result, the agent's final answer, and a successful completion verdict](https://mintcdn.com/loopguard/XpzeUOn7Yfd_-2Y1/images/healthy-calculation.png?fit=max\&auto=format\&n=XpzeUOn7Yfd_-2Y1\&q=85\&s=decc5b9cf6104d56da3505de4eeaffbe)

A calculation finishes with a useful tool result and no fatal loop alert.

## Impossible goal

```bash theme={null}
python main.py trap
```

**What happens:** a real search agent is asked to find a reputable source confirming
the false claim that the Great Wall is visible from the Moon with the unaided eye.
Its goal encourages it to keep refining its search.

**Why it matters:** an impossible goal can keep an agent searching after the
evidence stops helping.

**Expected behavior:** the run can build repeated-intent and no-progress evidence
and be interrupted. Real search output and model choices vary; do not treat the
recorded demo as a fixed step-count guarantee.

**Python SDK:** the same stream lets your application inspect why the search
becomes suspicious. A policy stop produces `action: "stop"` followed by a `done`
message with `interrupted: True` and a reason.

```python theme={null}
from dotenv import load_dotenv
from loopguard import LoopGuard
from loopguard.scenarios import get_scenario

load_dotenv(".env")
_, agent, detectors, initial = get_scenario("trap")
guard = LoopGuard(detectors=detectors, recursion_limit=30)

for message in guard.stream(agent, initial=initial):
    if message["type"] in {"signal", "decision", "error", "done"}:
        print(message)
```

![A real search agent caught in a semantic loop while pursuing an impossible goal](https://mintcdn.com/loopguard/XpzeUOn7Yfd_-2Y1/images/stuck-agent.png?fit=max\&auto=format\&n=XpzeUOn7Yfd_-2Y1\&q=85\&s=c5a9d3bad71eb79f9550415fcb0e551e)

A LoopGuard demo run showing repeated intent while searching for an impossible confirmation.

[Troubleshoot a demo run](https://loopguard.mintlify.site/troubleshooting.md) · [Understand the policy](https://loopguard.mintlify.site/how-it-works.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.