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

# Middleware

> Des wrappers de fournisseurs prêts à l'emploi qui interceptent chaque réponse de modèle et y attachent automatiquement un score RAIL.

Le middleware est le pattern qui consiste à intercepter chaque réponse du modèle et à y attacher un score RAIL avant qu'elle n'atteigne le reste de votre application. Vous remplacez votre client de modèle par un wrapper RAIL. Le wrapper appelle le modèle, évalue la réponse et retourne à la fois le contenu et les scores dans un seul objet.

<Info>
  **SDK Python :** [Référence des intégrations](/integrations/overview) | **API :** [Endpoint d'évaluation](/api-reference/evaluation)
</Info>

## Le problème que ça résout

Sans middleware, ajouter des vérifications d'IA responsable à chaque appel de modèle implique d'écrire du code d'évaluation partout où vous appelez le modèle, de dupliquer la logique, de risquer des lacunes de couverture et d'encombrer votre code applicatif :

<CodeGroup>
  ```python Without middleware theme={null}
  # Eval code scattered everywhere
  async def get_response(user_message):
      response = await openai_client.chat.completions.create(
          model="gpt-4o", messages=[{"role": "user", "content": user_message}]
      )
      content = response.choices[0].message.content

      # Must remember to eval in every function
      score = rail_client.eval(content=content, mode="basic")
      if score.rail_score.score < 7.0:
          raise ValueError("Response below quality threshold")

      return content
  ```

  ```python With middleware theme={null}
  # Scoring is automatic - set up once, forget about it
  from rail_score_sdk.integrations import RAILOpenAI

  client = RAILOpenAI(
      openai_api_key="...",
      rail_api_key="YOUR_RAIL_API_KEY",
      eval_mode="basic",
      threshold=7.0,
  )

  async def get_response(user_message):
      response = await client.chat(messages=[{"role": "user", "content": user_message}])
      return response.content  # .rail_score and .threshold_met are also available
  ```
</CodeGroup>

## Comment ça fonctionne

```mermaid theme={null}
flowchart LR
    App["Your Application"] --> Wrapper["RAIL Wrapper"]
    Wrapper -->|"1. Forward request"| Model["Model API\n(OpenAI / Gemini / Anthropic)"]
    Model -->|"2. Response content"| Wrapper
    Wrapper -->|"3. Evaluate"| RAIL["RAIL Score API"]
    RAIL -->|"Score + dimensions"| Wrapper
    Wrapper -->|"content + rail_score\n+ threshold_met"| App
```

Lorsque vous appelez une méthode sur le wrapper RAIL, trois choses se passent de manière transparente :

1. Vos messages sont transmis à l'API du modèle sous-jacent comme un appel API normal.
2. La réponse du modèle est soumise à l'endpoint d'évaluation RAIL dans le mode que vous avez configuré.
3. Un objet de réponse enrichi est retourné, contenant le contenu original, le score RAIL, les scores par dimension et un booléen `threshold_met`, le tout en une seule valeur de retour.

## Fournisseurs pris en charge

| Wrapper              | Encapsule                 | Python | JavaScript |
| -------------------- | ------------------------- | ------ | ---------- |
| `RAILOpenAI`         | OpenAI chat completions   | Oui    | Oui        |
| `RAILGemini`         | Google Gemini             | Oui    | Oui        |
| `RAILAnthropic`      | Anthropic Claude          | Oui    | Oui        |
| `RAILLangChain`      | Tout modèle LangChain     | Oui    | —          |
| Wrapper personnalisé | Tout modèle basé sur HTTP | Oui    | Oui        |

## Mode observation vs mode application

<Tabs>
  <Tab title="Observation seule">
    Évaluez chaque réponse, sans jamais bloquer. Utilisez ce mode pour mesurer la qualité sans interrompre le flux de réponses.

    ```python theme={null}
    client = RAILOpenAI(
        openai_api_key="...",
        rail_api_key="...",
        eval_mode="basic",
        # No threshold — always returns response
    )

    response = await client.chat(messages=[...])
    print(response.content)        # The model's response
    print(response.rail_score)     # RAIL score (always present)
    print(response.threshold_met)  # None — no threshold configured
    ```
  </Tab>

  <Tab title="Application du seuil">
    Lève une `ThresholdError` lorsqu'une réponse ne respecte pas le seuil.

    ```python theme={null}
    client = RAILOpenAI(
        openai_api_key="...",
        rail_api_key="...",
        eval_mode="basic",
        threshold=7.0,
    )

    try:
        response = await client.chat(messages=[...])
        return response.content
    except ThresholdError as e:
        # e.rail_score and e.failed_dimensions are available
        return fallback_response()
    ```
  </Tab>

  <Tab title="Régénération automatique">
    Déclenche automatiquement la régénération sécurisée lorsqu'une réponse est en dessous du seuil.

    ```python theme={null}
    client = RAILOpenAI(
        openai_api_key="...",
        rail_api_key="...",
        eval_mode="basic",
        threshold=7.0,
        on_fail="regenerate",
        max_iterations=3,
    )

    # Returns the best content — original or regenerated
    response = await client.chat(messages=[...])
    print(response.content)
    print(response.iterations_taken)  # 1 if original passed
    ```
  </Tab>
</Tabs>

## Écrire un middleware personnalisé

Si vous utilisez un fournisseur de modèle sans wrapper intégré, construisez votre propre middleware en utilisant l'appel `eval()` de base :

```python theme={null}
from rail_score_sdk import RailScoreClient

rail = RailScoreClient(api_key="...")

async def rail_middleware(model_call, messages, threshold=7.0):
    """Generic RAIL middleware for any async model call."""
    content = await model_call(messages)

    result = rail.eval(content=content, mode="basic")

    if result.rail_score.score < threshold:
        raise ValueError(
            f"Response scored {result.rail_score.score:.1f} — below threshold {threshold}. "
            f"Failed: {[d for d, s in result.dimension_scores.items() if s.score < threshold]}"
        )

    return content, result

# Use with any model:
content, score = await rail_middleware(my_model_call, messages, threshold=7.5)
```

## Et ensuite

<CardGroup cols={2}>
  <Card title="Concepts : Moteur de politiques" icon="gavel" href="/concepts/policy-engine">
    Des règles déclaratives pour agir sur les scores au sein d'une session.
  </Card>

  <Card title="Python : Intégrations" icon="python" href="/integrations/overview">
    Documentation complète des wrappers de fournisseurs et options.
  </Card>

  <Card title="JavaScript : Fournisseurs" icon="js" href="/sdk/javascript/overview">
    Wrappers TypeScript pour OpenAI, Gemini, Anthropic.
  </Card>

  <Card title="Python : SDK Middleware" icon="layer-group" href="/sdk/python/middleware">
    RAILMiddleware - encapsulez n'importe quelle fonction de modèle.
  </Card>
</CardGroup>
