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

# Summarizer

> Build a text summarizer with Guardrails to ensure quality and length constraints

In this example we will use Guardrails to summarize text in terms of length, quality and output read time.

<Note>
  To download this example as a Jupyter notebook, click [here](https://github.com/guardrails-ai/guardrails/blob/main/docs/examples/summarizer.ipynb).
</Note>

In this example, we will use Guardrails in the summarization of a text document. We will check whether the summarized document has a high semantic similarity with the original document. We will ensure the output meets a certain range of length and read time.

## Setup

In order to check semantic similarity we will need the `numpy` package via the command below. We will also install the validators we intend to use.

```bash theme={null}
pip install numpy
guardrails hub install hub://guardrails/reading_time --quiet --install-local-models
guardrails hub install hub://guardrails/similar_to_document --quiet --install-local-models
guardrails hub install hub://guardrails/valid_length --quiet --install-local-models
```

## Step 1: Load data and create Pydantic model

Load our text with the code below:

```python theme={null}
with open("data/twain.txt", "r") as file:
    document = file.read()
    file.seek(0)
    content = "".join(line.strip() for line in file.readlines())
```

Next we can define our return output with a pydantic model:

```python theme={null}
from pydantic import BaseModel, Field

from guardrails.hub import SimilarToDocument, ValidLength, ReadingTime

prompt = """
Summarize the following text faithfully:

${document}

${gr.complete_xml_suffix}
"""

THREE_MINUTES = 180 / 60


class TextSummary(BaseModel):
    summary: str = Field(
        description="Faithful summary of the text",
        validators=[
            ReadingTime(reading_time=THREE_MINUTES, on_fail="exception"),
            ValidLength(min=100, max=1000, on_fail="exception"),
            SimilarToDocument(
                document=f"'{content}'", threshold=0.60, on_fail="filter"
            ),
        ],
    )
```

## Step 2: Create Guard from pydantic

The guard we create will:

1. Enforce reading time
2. Enforce length
3. Enforce similarity

```python theme={null}
import guardrails as gd

guard = gd.Guard().for_pydantic(TextSummary)
```

## Step 3: Call LLM via `guard()`

We use the tools API to ensure our data is returned in a structured form.

```python theme={null}
# TODO: Replace OPENAI_API_KEY with your OpenAI API key, uncomment
# os.environ["OPENAI_API_KEY"] = "OPENAI_API_KEY"

response = guard(
    messages=[{"role": "user", "content": prompt}],
    prompt_params={"document": document},
    model="gpt-4o",
    tools=guard.json_function_calling_tool(),
    tool_choice="required",
)

print(f"Validated Output: {response.validated_output}")
```

We can see the step-wise history of the `Guard` object below:

```python theme={null}
guard.history.last.tree
```

The `guard` wrapper returns the raw\_llm\_response (which is a simple string), and the validated and corrected output (which is a dictionary). We can see that the output is a dictionary with the correct schema and types.

Now let's try a model that's not as proficient at summarization and we can see the output is filtered and validation has failed.

The final validated output is `None` due to the failed validation.

```python theme={null}
response = guard(
    messages=[{"role": "user", "content": prompt}],
    prompt_params={"document": document},
    model="babbage-002",
    max_tokens=512,
    temperature=0,
)

print(f"Validated Output: {response.validated_output}")
```

We can see the step wise history of the guard execution below:

```python theme={null}
guard.history.last.tree
```
