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

# Python SDK

> Install the Python SDK, classify user input and tool output, and handle blocks with synchronous or asynchronous clients.

## Prerequisites

Use Python 3.10 or later.

Set `SILMARIL_API_KEY` and `SILMARIL_API_URL` from your [deployment access](/docs/start/quickstart#get-access).

## Install

```sh theme={"theme":"github-light-default"}
pip install silmaril-security-sdk==0.6.1
```

<a id="initialize" />

<a id="classify" />

## Classify your first request

Create one client per protected system and reuse it. Label each call with the boundary it checks.

```python theme={"theme":"github-light-default"}
import logging
import os

from silmaril_security.sdk import (
    Firewall,
    FirewallBlockedException,
    HookLabel,
    SilmarilApiError,
)

logging.basicConfig(level=logging.INFO)

fw = Firewall(
    api_key=os.environ["SILMARIL_API_KEY"],
    api_url=os.environ["SILMARIL_API_URL"],
)

try:
    result = fw.classify(
        "Summarize this release note.",
        hook=HookLabel.USER_INPUT,
    )
    logging.info("prediction=%s score=%.4f", result.prediction, result.score)
except FirewallBlockedException as exc:
    logging.warning("block score=%.4f threshold=%.4f", exc.score, exc.threshold)
except SilmarilApiError as exc:
    logging.error("Silmaril API error: %s", exc)
    raise
```

For tool output, pass the tool name as well.

```python theme={"theme":"github-light-default"}
fw.classify(
    tool_output,
    hook=HookLabel.TOOL_RESPONSE,
    tool_name="read_file",
)
```

### Expected result

A benign request logs `prediction=BENIGN` and its score. When the effective mode is block, a malicious request raises `FirewallBlockedException`. Its `result` holds the full verdict. In shadow or warn mode the call returns the result instead, so route on `result.prediction`. Your deployment sets the mode unless the client or call sets `mode`. Outcome meanings and recommended actions are in the [outcome taxonomy](/docs/sdk/outcomes#outcome-taxonomy).

`SilmarilApiError` means the API returned a non-2xx response after retries. Network failures raise the underlying `requests` exception. Neither is a verdict, so decide whether that boundary fails open or closed.

LangChain and LangGraph handlers for this client are documented in [Framework SDKs](/docs/integrations/frameworks).

## Async classify

Install async support before using `AsyncFirewall`.

```sh theme={"theme":"github-light-default"}
pip install "silmaril-security-sdk[async]==0.6.1"
```

```python theme={"theme":"github-light-default"}
import asyncio
import os
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from silmaril_security.sdk import AsyncFirewall, HookLabel

@asynccontextmanager
async def firewall_lifespan() -> AsyncIterator[AsyncFirewall]:
    async with AsyncFirewall(
        api_key=os.environ["SILMARIL_API_KEY"],
        api_url=os.environ["SILMARIL_API_URL"],
    ) as fw:
        yield fw  # share with request handlers until shutdown

async def handle_request(fw: AsyncFirewall, first_input: str, second_input: str):
    return await asyncio.gather(
        fw.classify(first_input, hook=HookLabel.USER_INPUT),
        fw.classify(second_input, hook=HookLabel.USER_INPUT),
    )
```

Share one `AsyncFirewall` across tasks on one event loop for the service lifetime and close it at shutdown. Cancellation stops the calling task's request. Async calls raise the same block exceptions as the synchronous client. For a synchronous off-thread fallback, create one `Firewall` per worker thread.

## Shadow mode

Shadow mode returns results instead of raising block exceptions, so you can measure would-block decisions while traffic continues. Per-call overrides let one boundary enforce before you change the client default.

```python theme={"theme":"github-light-default"}
import logging
import os
from silmaril_security.sdk import ClassifyEvent, Firewall, HookLabel

def on_classify(event: ClassifyEvent) -> None:
    if event.blocked and event.shadow_mode:
        logging.info("would block %s score=%.4f", event.hook, event.result.score)

fw = Firewall(
    api_key=os.environ["SILMARIL_API_KEY"],
    api_url=os.environ["SILMARIL_API_URL"],
    shadow_mode=True,
    on_classify=on_classify,
)

fw.classify(text, hook=HookLabel.TOOL_RESPONSE, shadow_mode=False)
```

The last call passes `shadow_mode=False`, so that call enforces.

## Errors

Direct calls raise `FirewallBlockedException` when a block is enforced. It carries `score`, `threshold`, and the full `result`.

```python theme={"theme":"github-light-default"}
try:
    fw.classify(user_input, hook=HookLabel.USER_INPUT)
except FirewallBlockedException as exc:
    logging.warning("blocked score=%.4f threshold=%.4f", exc.score, exc.threshold)
```

Read `exc.result` for the verdict, or use shadow mode when you need the result without a block exception. Outcome meanings and recommended actions are in the [outcome taxonomy](/docs/sdk/outcomes#outcome-taxonomy).
