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

# Guide : Obtenir des Résultats en Temps Réel avec SSE

> Apprenez à streamer des événements en temps réel depuis une exécution d'outil en utilisant les Server-Sent Events (SSE).

L'interrogation des résultats est efficace, mais pour une expérience plus réactive et en temps réel, vous pouvez streamer les événements directement depuis un outil en cours d'exécution. Ce guide couvre les deux principaux types de streaming disponibles dans l'API :

1. **Streaming d'Exécution d'Outil** : Pour surveiller les tâches d'outils de longue durée.
2. **Streaming de Session d'Agent** : Pour recevoir des réponses de chat en temps réel des agents.

## Streaming d'Exécution d'Outil

Ce guide montre comment se connecter au flux d'événements d'un outil en utilisant les Server-Sent Events (SSE). Il suppose que vous avez déjà lancé une exécution d'outil comme indiqué dans le guide [Exécuter Votre Premier Outil](/fr/guides/executing-tools).

<Steps>
  <Step title="Exécuter un Outil et Obtenir l'URL de Streaming">
    Tout d'abord, exécutez un outil comme vous le feriez normalement. La réponse initiale `202 Accepted` contiendra une `stream_url`. C'est l'endpoint que nous utiliserons pour recevoir les événements en direct.

    ```json Réponse theme={null}
    {
      "execution_id": "exec_123456789",
      "status": "pending",
      "tool_id": "d1e2f3a4-b5c6-7890-1234-567890abcdef",
      "details_url": "https://app.ubik-agent.com/api/v1/tool-executions/exec_123456789",
      "stream_url": "https://app.ubik-agent.com/api/v1/tool-executions/exec_123456789/stream"
    }
    ```

    Copiez la `stream_url` pour l'étape suivante.
  </Step>

  <Step title="Se Connecter au Flux d'Événements">
    Maintenant, vous pouvez vous connecter à la `stream_url` en utilisant n'importe quel client compatible SSE. La connexion restera ouverte, et le serveur enverra les événements au fur et à mesure qu'ils se produisent.

    <CodeGroup title="GET /tool-executions/{execution_id}/stream">
      ```bash cURL theme={null}
      # Utilisez l'option -N pour désactiver la mise en mémoire tampon
      curl -X GET "https://app.ubik-agent.com/api/v1/tool-executions/exec_123456789/stream" \
           -H "X-API-KEY: VOTRE_CLE_API" \
           -N
      ```

      ```python Python theme={null}
      import requests
      import json

      api_key = "VOTRE_CLE_API"
      stream_url = "https://app.ubik-agent.com/api/v1/tool-executions/exec_123456789/stream"

      headers = {"X-API-KEY": api_key, "Accept": "text/event-stream"}

      with requests.get(stream_url, headers=headers, stream=True) as response:
          if response.status_code == 200:
              current_event = None
              for line in response.iter_lines():
                  if line:
                      decoded_line = line.decode('utf-8')
                      
                      if decoded_line.startswith('event:'):
                          current_event = decoded_line[6:].strip()
                      
                      elif decoded_line.startswith('data:'):
                          try:
                              # Extraire la partie JSON du message
                              json_data = decoded_line[5:].strip()
                              data_payload = json.loads(json_data)
                              print(f"Événement: {current_event}, Données: {data_payload}")
                              
                              # Vérifier la fin du flux
                              if current_event in ["tool_end", "final_result"]:
                                  print("Flux terminé.")
                                  break
                          except json.JSONDecodeError:
                              print(f"Impossible de décoder la ligne: {decoded_line}")
          else:
              print(f"Erreur de connexion au flux: {response.status_code}", response.text)
      ```

      ```javascript JavaScript theme={null}
      const apiKey = "VOTRE_CLE_API";
      const streamUrl = "https://app.ubik-agent.com/api/v1/tool-executions/exec_123456789/stream";

      // Note: EventSource ne supporte pas les en-têtes personnalisés, nous passons la clé API en paramètre
      const eventSource = new EventSource(`${streamUrl}?api_key=${apiKey}`);

      eventSource.onopen = () => {
        console.log("Connexion au flux d'événements ouverte.");
      };

      // Écouter les événements spécifiques
      eventSource.addEventListener('tool_update', (event) => {
        const data = JSON.parse(event.data);
        console.log("Mise à jour:", data);
      });

      eventSource.addEventListener('tool_partial_update', (event) => {
        const data = JSON.parse(event.data);
        console.log("Contenu partiel:", data.content);
      });

      const handleCompletion = (event) => {
        const data = JSON.parse(event.data);
        console.log("Terminé:", data);
        eventSource.close();
      };

      eventSource.addEventListener('tool_end', handleCompletion);
      eventSource.addEventListener('final_result', handleCompletion);

      eventSource.addEventListener('error', (event) => {
          // Note: 'error' ici peut être un événement SSE nommé 'error' ou une erreur réseau générique
          if (event.data) {
              try {
                const data = JSON.parse(event.data);
                console.error("Erreur outil:", data);
              } catch(e) {
                 console.error("Erreur EventSource:", event);
              }
          } else {
              console.error("Erreur connexion EventSource");
          }
          eventSource.close();
      });
      ```
    </CodeGroup>

    <Note>
      Dans l'exemple JavaScript `EventSource`, nous passons la clé API comme paramètre de requête. Notre serveur est configuré pour accepter la clé API soit via l'en-tête `X-API-KEY`, soit via un paramètre de requête nommé `api_key` pour les connexions SSE, car `EventSource` ne prend pas en charge les en-têtes personnalisés.
    </Note>
  </Step>

  <Step title="Comprendre les Événements">
    Au fur et à mesure que l'outil s'exécute, vous recevrez une série d'objets JSON. Chaque objet contient un `event_type` et une charge utile `data`. Voici les types d'événements détaillés et leurs structures :

    ### `tool_update`

    Utilisé pour les mises à jour générales de progression ou les changements de données structurées.

    ```json theme={null}
    {
      "phase": "processing",        // Optionnel : Phase d'exécution actuelle
      "message": "Traitement...",   // Optionnel : Statut lisible par l'humain
      "output_key": "result",       // Optionnel : Clé pour les données structurées
      "data": { ... }               // Optionnel : Données structurées arbitraires
    }
    ```

    ### `tool_partial_update`

    Utilisé pour le contenu en streaming (par exemple, génération de texte, téléchargements partiels d'images).

    ```json theme={null}
    {
      "content": "texte partiel...", // Le fragment de contenu
      "output_key": "response",      // Le champ de sortie en cours de streaming
      "data": { ... }                // Optionnel : Métadonnées supplémentaires
    }
    ```

    ### `tool_input_required`

    Envoyé lorsqu'un outil interactif met en pause l'exécution pour attendre une entrée de l'utilisateur.

    ```json theme={null}
    {
      "prompt": "Veuillez confirmer...", // Question pour l'utilisateur
      "input_types": ["text"],           // Types d'entrée autorisés
      "timeout": 300,                    // Délai d'attente en secondes
      "state": { ... }                   // État interne (opaque)
    }
    ```

    ### `tool_end`

    L'événement final d'une exécution réussie. Les données contiennent les sorties directes de l'outil.

    <Note>
      Pour des raisons de compatibilité ou selon la méthode d'exécution, cet événement peut parfois apparaître sous le nom `final_result`. Il doit être traité de la même manière que `tool_end`.
    </Note>

    ```json theme={null}
    {
      "execution_id": "exec_123...", // ID de l'exécution
      "response": "Texte complet...", // Exemple de sortie (dépend de l'outil)
      "images": [...]                 // Exemple de sortie
    }
    ```

    ### `error`

    Indique qu'une erreur est survenue pendant l'exécution.

    ```json theme={null}
    {
      "message": "Description de l'erreur",
      "code": "CODE_ERREUR"
    }
    ```

    ### Gestion des Événements Larges (Chunking)

    Pour les événements qui dépassent la limite de taille (par exemple, de grandes images encodées en base64 ou de longs textes), l'API utilise un mécanisme de "chunking". Ces événements sont divisés en plusieurs parties avec le suffixe `_delta_sse` ajouté au nom de l'événement d'origine.

    Par exemple, si un événement `tool_update` est trop volumineux, vous recevrez une série d'événements `tool_update_delta_sse`.

    **Structure d'un événement chunké :**

    ```json theme={null}
    {
      "chunk_id": "uuid-unique-pour-ce-groupe",
      "chunk_index": 0,           // Index du morceau (commence à 0)
      "total_chunks": 5,          // Nombre total de morceaux
      "original_event_type": "tool_update",
      "chunk_data": "...",        // Fragment de la donnée JSON originale (string)
      "is_last_chunk": false      // true pour le dernier morceau
    }
    ```

    **Logique de réassemblage (Côté Client) :**

    1. Détectez si le nom de l'événement se termine par `_delta_sse`.
    2. Stockez les `chunk_data` dans un buffer, ordonnés par `chunk_index`.
    3. Lorsque `is_last_chunk` est `true`, concaténez tous les fragments de `chunk_data`.
    4. Analysez la chaîne concaténée comme du JSON.
    5. Traitez l'objet résultant comme s'il s'agissait de l'événement `original_event_type`.

    **Exemple JavaScript de gestion du chunking :**

    ```javascript theme={null}
    const eventBuffers = {}; // Stocker les morceaux par chunk_id

    eventSource.addEventListener('message', (event) => {
        // Note: EventSource peut ne pas exposer le type d'événement directement dans 'message'
        // Il est préférable d'utiliser un gestionnaire générique ou d'écouter tous les types possibles
    });

    // Fonction utilitaire pour gérer tous les événements entrants
    function handleIncomingEvent(eventType, eventData) {
        if (eventType.endsWith('_delta_sse')) {
            const { chunk_id, chunk_index, chunk_data, is_last_chunk, original_event_type } = eventData;
            
            if (!eventBuffers[chunk_id]) {
                eventBuffers[chunk_id] = [];
            }
            eventBuffers[chunk_id][chunk_index] = chunk_data;

            if (is_last_chunk) {
                const fullDataString = eventBuffers[chunk_id].join('');
                delete eventBuffers[chunk_id]; // Nettoyage
                
                try {
                    const fullData = JSON.parse(fullDataString);
                    // Traiter récursivement l'événement reconstitué
                    handleIncomingEvent(original_event_type, fullData);
                } catch (e) {
                    console.error("Erreur de parsing du chunk réassemblé", e);
                }
            }
            return; // Attendre les autres morceaux
        }

        // Traitement normal des événements
        console.log(`Événement reçu: ${eventType}`, eventData);
        if (eventType === 'tool_end') {
            console.log("Résultat final:", eventData);
        }
    }
    ```

    ### Exemple de Flux d'Événements

    ```
    event: tool_update
    data: {"phase": "retrieval", "message": "Récupération des documents pertinents..."}

    event: tool_update
    data: {"phase": "generation", "message": "Génération de la réponse..."}

    event: tool_partial_update
    data: {"content": "Les résultats financiers montrent..."}

    event: tool_partial_update
    data: {"content": " une augmentation significative du chiffre d'affaires."}

    event: tool_end
    data: {"execution_id": "...", "status": "completed", "outputs": {"response": "Les résultats financiers montrent une augmentation significative du chiffre d'affaires."}}
    ```
  </Step>
</Steps>

## Intégrer les Événements d'Outils dans l'Interface de Chat

Lorsque vous construisez une interface de chat personnalisée, vous devez souvent combiner l'**historique permanent de la conversation** (texte) avec les **mises à jour transitoires en temps réel** (événements). Cela garantit que votre interface utilisateur est à la fois réactive pendant l'exécution et précise lors du rechargement de l'historique.

### La Stratégie à Double Flux

1. **Flux de Texte (`response_chunk`)** : Contient la "source de vérité" de la conversation. Il inclut des délimiteurs qui marquent où un outil a été appelé et quel a été le résultat final.
2. **Flux d'Événements (`tool_update`, `tool_partial_update`)** : Contient les mises à jour de statut en temps réel, les journaux et les indicateurs de progression qui doivent être affichés *pendant* l'exécution de l'outil.

### Logique d'Implémentation

Pour construire une interface utilisateur riche comme celle d'Ubik, suivez ce modèle :

1. **Détecter le Début** : Lorsque vous analysez `<<TOOL_STEP_START/nom_outil:id_exec>>` dans le flux de texte, créez un **Conteneur d'Outil** dans votre interface. Marquez-le comme "Chargement" ou "En cours".
2. **Mises à jour en Direct** : Écoutez les événements SSE. Si vous recevez un `tool_update` ou `tool_partial_update` où `tool_execution_id` correspond à `id_exec`, mettez à jour le contenu de votre **Conteneur d'Outil** (par exemple, affichez "Recherche sur le web...", mettez à jour une barre de progression ou diffusez des journaux).
3. **Détecter la Fin** : Lorsque vous analysez `<<TOOL_STEP_RESULT_START>>` ... `<<TOOL_STEP_RESULT_END>>` dans le flux de texte, vous avez la sortie finale et permanente. Vous pouvez maintenant remplacer l'état "Chargement" dans votre **Conteneur d'Outil** par le résultat statique final.

Cette approche garantit que même si l'utilisateur actualise la page (perdant les événements transitoires), le flux de texte contient toujours l'historique complet de l'exécution (entrées et résultats) nécessaire pour afficher l'état terminé de l'outil.

### Flux d'Événements Spécifiques aux Outils

Chaque outil peut émettre des structures de données différentes dans ses événements `tool_update` et `tool_partial_update` en fonction de sa fonction (par exemple, un outil de recherche web peut diffuser des mises à jour "Recherche d'URL...", tandis qu'un outil d'exécution de code diffuse des journaux de console).

Pour construire une interface utilisateur robuste, vous devrez analyser le flux d'événements spécifique pour les outils que vous avez l'intention de prendre en charge.

> **Note** : Des guides détaillés documentant les structures d'événements spécifiques pour chaque outil natif seront disponibles à l'avenir. Pour l'instant, nous vous recommandons d'inspecter les événements renvoyés par les outils pendant le développement.

Pour une liste des outils disponibles et de leurs capacités, veuillez consulter le guide **[Outils Natifs](/fr/core-features/native-tools)**.

## Streaming de Session d'Agent

Les Sessions d'Agent fournissent un ensemble différent d'événements adaptés aux interfaces conversationnelles. Lorsque vous envoyez un message avec `"stream": true`, le serveur diffuse le processus de réflexion de l'agent et les morceaux de réponse.

### Événements de Session d'Agent

Contrairement aux exécutions d'outils, les événements de session d'agent sont souvent utilisés pour afficher une interface de chat en temps réel.

Pour une liste complète des types d'événements et leur structure, veuillez consulter le [Guide des Événements de Session d'Agent](/fr/guides/agent-session-events).

### Endpoint de Stream Dédié

Bien que `POST /agent-sessions/{id}/messages` prenne en charge le streaming direct, UBIK fournit également un endpoint dédié pour s'abonner aux événements de session. Ceci est utile pour :

1. **Reconnexion** : Reprendre un flux si la connexion est interrompue (les événements passés sont rejoués).
2. **Compatibilité Navigateur** : Utiliser `EventSource` qui nécessite une requête GET et une authentification basée sur l'URL.

**Endpoint** : `GET /agent-sessions/{session_id}/stream`

#### Authentification avec JWT

Comme l'API `EventSource` standard des navigateurs ne peut pas envoyer d'en-têtes personnalisés (comme `X-API-KEY`), cet endpoint accepte un paramètre de requête `token`. Vous devez générer un JWT à courte durée de vie en utilisant `/auth/token` et le passer ici.

Pour plus de détails sur la génération de jetons, voir le [Guide Authentification & Sécurité](/fr/guides/authentication).

```javascript theme={null}
// 1. Obtenir un jeton à courte durée de vie depuis votre backend
const token = "eyJhbGciOiJIUz..."; 

// 2. Se connecter en utilisant le paramètre token
const streamUrl = `https://app.ubik-agent.com/api/v1/agent-sessions/${sessionId}/stream?token=${token}`;
const eventSource = new EventSource(streamUrl);
```
