Opérations de chat

Écrit par Stanislas

Dernière mise à jour Il y a 7 mois


Apprenez à envoyer des messages en continu, à régénérer les réponses, à interrompre la génération de messages et à utiliser les fonctions de synthèse vocale.

Présentation

Les opérations de chat constituent les interactions centrales d'une conversation. Vous pouvez envoyer des messages pour déclencher des réponses IA, écouter les réponses en streaming en temps réel, régénérer les réponses insatisfaisantes, arrêter les générations longues et convertir les réponses en audio. Ce guide couvre toutes les mutations et requêtes dont vous avez besoin pour une expérience de chat complète.

Chaque message envoyé peut éventuellement déclencher une réponse de l'IA. Les réponses sont diffusées caractère par caractère, ce qui permet aux utilisateurs d'obtenir un retour immédiat. Vous pouvez également utiliser des requêtes synchrones si vous avez besoin de la réponse complète avant de continuer.


Prérequis

Avant d'effectuer des opérations de chat, assurez-vous que vous disposez des éléments suivants :

  1. Vous êtes authentifié avec l'API — Suivez le guide Guide d'authentification et d'installation pour obtenir votre accessToken et votre workspaceId

  2. Créé ou récupéré une session — Suivez les instructions Guide de gestion des sessions de chat pour obtenir un sessionId

  3. Configuré votre client GraphQL — Votre client Apollo doit inclure les en-têtes d'authentification requis

  4. Configuré les abonnements (facultatif) — Pour le streaming en temps réel, suivez les instructions disponibles à l'adresse Guide des abonnements en temps réel


Pour commencer

Voici la configuration minimale pour envoyer un message et recevoir une réponse :

Étape 1 : envoyer un message

Envoyez un message au bot et déclenchez une réponse en streaming.

import { gql } from '@apollo/client';

const SEND_MESSAGE = gql`
  mutation SendNewMessage($newMessageData: NewMessageInput!) {
    sendNewMessage(newMessageData: $newMessageData) {
      id
      message
      createdAt
      sessionId
      sentBy {
        id
        firstName
      }
      isBotReply
    }
  }
`;

const sendMessage = async (client, sessionId, messageText) => {
  const { data } = await client.mutate({
    mutation: SEND_MESSAGE,
    variables: {
      newMessageData: {
        message: messageText,
        sessionId: sessionId,
        isForAiReply: true, // Trigger AI response
      },
    },
  });

  return data.sendNewMessage;
};

// Usage
const userMessage = await sendMessage(client, 67890, 'What is your pricing?');
console.log('Message sent:', userMessage.id);

Étape 2 : abonnez-vous aux réponses en streaming

Écoutez la réponse de l'IA en streaming en temps réel.

const MESSAGE_STREAM = gql`
  subscription OnMessageStream($sessionId: Float!) {
    onMessageStream(sessionId: $sessionId) {
      messageChunk
      botResponseMessageId
      isStoppable
    }
  }
`;

const subscribeToStream = (client, sessionId, onChunk) => {
  let fullMessage = '';

  return client
    .subscribe({
      query: MESSAGE_STREAM,
      variables: { sessionId },
    })
    .subscribe({
      next: ({ data }) => {
        fullMessage += data.onMessageStream.messageChunk;
        onChunk(fullMessage, data.onMessageStream);
      },
      error: (error) => {
        console.error('Stream error:', error);
      },
      complete: () => {
        console.log('Stream complete');
      },
    });
};

// Usage
const subscription = subscribeToStream(client, 67890, (fullMessage, data) => {
  console.log('Streaming:', fullMessage);
  if (data.isStoppable) {
    console.log('User can stop generation');
  }
});

// Cleanup when done
// subscription.unsubscribe();

Étape 3 : Arrêter la génération si nécessaire

Si la réponse prend trop de temps, arrêtez la génération.

const STOP_AI_RESPONSE = gql`
  mutation StopAIResponse($sessionId: Float!, $botId: Float!) {
    stopAIResponse(sessionId: $sessionId, botId: $botId)
  }
`;

const stopGeneration = async (client, sessionId, botId) => {
  const { data } = await client.mutate({
    mutation: STOP_AI_RESPONSE,
    variables: { sessionId, botId },
  });

  return data.stopAIResponse;
};

// Usage
const stopped = await stopGeneration(client, 67890, 123);
console.log('Generation stopped:', stopped);

Envoi de messages

Envoyez un nouveau message

Envoyez un message à la session et déclenchez éventuellement une réponse de l'IA.

Mutation GraphQL :

mutation SendNewMessage($newMessageData: NewMessageInput!) {
  sendNewMessage(newMessageData: $newMessageData) {
    id
    message
    createdAt
    sessionId
    sentBy {
      id
      firstName
      lastName
    }
    isBotReply
  }
}

Paramètres d'entrée :

Paramètre

Type

Obligatoire

Description

message

chaîne

Non

Texte du message à envoyer

sessionId

nombre

Oui

ID de session (où envoyer le message)

isForAiReply

booléen

Non

Si vrai, déclenche une réponse de l'IA

fileIds

nombre

Non

ID des fichiers à joindre

mentionedBotId

nombre

Non

Bot spécifique à mentionner/invoquer

parentMessageId

nom

Non

ID du message parent (pour le threading)

botPromptId

numéro

Non

Invite prédéfinie à utiliser

command

chaîne

Non

Commande spéciale pour l'agent

extraConfig

objet

Non

Configuration supplémentaire

Retourne : l'objet message créé

Exemple de base :

const SEND_MESSAGE = gql`
  mutation SendNewMessage($newMessageData: NewMessageInput!) {
    sendNewMessage(newMessageData: $newMessageData) {
      id
      message
      createdAt
      isBotReply
    }
  }
`;

// Simple message without AI response
const message1 = await client.mutate({
  mutation: SEND_MESSAGE,
  variables: {
    newMessageData: {
      message: 'Just saving this note',
      sessionId: 67890,
    },
  },
});

// Message that triggers AI response
const message2 = await client.mutate({
  mutation: SEND_MESSAGE,
  variables: {
    newMessageData: {
      message: 'What can you do for me?',
      sessionId: 67890,
      isForAiReply: true,
    },
  },
});

Avec pièces jointes :

const sendMessageWithFiles = async (client, sessionId, messageText, fileIds) => {
  const { data } = await client.mutate({
    mutation: SEND_MESSAGE,
    variables: {
      newMessageData: {
        message: messageText,
        sessionId,
        fileIds, // Array of file IDs
        isForAiReply: true,
      },
    },
  });

  return data.sendNewMessage;
};

// Usage
const message = await sendMessageWithFiles(
  client,
  67890,
  'Please analyze these documents',
  [101, 102, 103]
);

Avec fil de discussion (réponse à un message spécifique) :

const sendReplyToMessage = async (client, sessionId, messageText, parentMessageId) => {
  const { data } = await client.mutate({
    mutation: SEND_MESSAGE,
    variables: {
      newMessageData: {
        message: messageText,
        sessionId,
        parentMessageId, // Reply to this message
        isForAiReply: true,
      },
    },
  });

  return data.sendNewMessage;
};

// Usage
const reply = await sendReplyToMessage(
  client,
  67890,
  'Can you elaborate on that?',
  12345 // ID of the message to reply to
);

À l'aide d'une invite prédéfinie :

const sendWithPrompt = async (client, sessionId, botPromptId) => {
  const { data } = await client.mutate({
    mutation: SEND_MESSAGE,
    variables: {
      newMessageData: {
        sessionId,
        botPromptId, // Use a predefined prompt instead of typing
        isForAiReply: true,
      },
    },
  });

  return data.sendNewMessage;
};

// Usage - useful for quick actions like "Summarize" or "Translate"
const summary = await sendWithPrompt(client, 67890, 456);

Demandes synchrones

Envoyer un message et attendre la réponse complète

Pour les questions-réponses simples sans streaming, utilisez le point de terminaison synchrone pour obtenir immédiatement la réponse complète.

Mutation GraphQL :

mutation SyncBotRequest($newMessageData: NewMessageInput!) {
  syncBotRequest(newMessageData: $newMessageData) {
    text
    botId
    botSlug
    isBotError
    sessionId
    files
    sourceMetadatas
    sources
    responseJson
    jobId
  }
}

Paramètres d'entrée : identiques à ceux d'SendNewMessage

Retourne : réponse complète avec métadonnées

Exemple :

const SYNC_BOT_REQUEST = gql`
  mutation SyncBotRequest($newMessageData: NewMessageInput!) {
    syncBotRequest(newMessageData: $newMessageData) {
      text
      isBotError
      sources
      botSlug
    }
  }
`;

const syncMessage = async (client, sessionId, messageText) => {
  const { data } = await client.mutate({
    mutation: SYNC_BOT_REQUEST,
    variables: {
      newMessageData: {
        message: messageText,
        sessionId,
      },
    },
  });

  return data.syncBotRequest;
};

// Usage
const response = await syncMessage(client, 67890, 'What is 2 + 2?');
console.log('Bot response:', response.text);
console.log('Sources used:', response.sources);

if (response.isBotError) {
  console.error('Bot encountered an error');
}

Quand utiliser les requêtes synchrones :

  • Questions-réponses simples où vous avez besoin de la réponse complète avant de continuer

  • Traitement par lots de plusieurs messages

  • Lorsque vous n'avez pas besoin d'un retour en temps réel

  • Intégrations API où vous avez besoin d'une réponse complète immédiatement

Gestion des réponses

Régénérer une réponse

Si la réponse du bot n'était pas satisfaisante, régénérez-la.

Mutation GraphQL :

mutation RegenerateResponse($messageId: Float!) {
  regenerateResponse(messageId: $messageId) {
    id
    message
    createdAt
  }
}

Paramètres :

  • messageId (obligatoire) — ID du message de réponse du bot à régénérer

Retourne : Le nouveau message de réponse

Exemple :

const REGENERATE_RESPONSE = gql`
  mutation RegenerateResponse($messageId: Float!) {
    regenerateResponse(messageId: $messageId) {
      id
      message
      createdAt
    }
  }
`;

const regenerateResponse = async (client, messageId) => {
  const { data } = await client.mutate({
    mutation: REGENERATE_RESPONSE,
    variables: { messageId },
  });

  return data.regenerateResponse;
};

// Usage
const newResponse = await regenerateResponse(client, 54321);
console.log('New response generated:', newResponse.id);

Workflow avec régénération :

// User sends a message
const userMessage = await sendMessage(client, sessionId, 'Explain quantum computing');

// Subscribe to streaming response
let botMessageId;
subscribeToStream(client, sessionId, (fullMessage, data) => {
  botMessageId = data.botResponseMessageId;
  updateUI(fullMessage);
});

// Later, user clicks "Regenerate" button
const newResponse = await regenerateResponse(client, botMessageId);
console.log('Response regenerated');

Arrêter la réponse IA

Arrête la génération de réponse IA en cours.

Mutation GraphQL :

mutation StopAIResponse($sessionId: Float!, $botId: Float!) {
  stopAIResponse(sessionId: $sessionId, botId: $botId)
}

Paramètres :

  • sessionId (obligatoire) — ID de session

  • botId (obligatoire) — ID du bot

Retourne : Booléen (vrai si l'arrêt a réussi)

Exemple :

const STOP_AI_RESPONSE = gql`
  mutation StopAIResponse($sessionId: Float!, $botId: Float!) {
    stopAIResponse(sessionId: $sessionId, botId: $botId)
  }
`;

const stopGeneration = async (client, sessionId, botId) => {
  const { data } = await client.mutate({
    mutation: STOP_AI_RESPONSE,
    variables: { sessionId, botId },
  });

  return data.stopAIResponse;
};

// Usage
const stopped = await stopGeneration(client, 67890, 123);
if (stopped) {
  console.log('Generation stopped by user');
}

Implémentation de l'interface utilisateur avec bouton d'arrêt :

let isGenerating = false;
let currentBotId = 123;

const startGeneration = async (sessionId) => {
  isGenerating = true;
  showStopButton();

  subscribeToStream(client, sessionId, (fullMessage, data) => {
    updateMessageUI(fullMessage);
    if (!data.isStoppable) {
      hideStopButton();
    }
  });
};

const handleStopClick = async (sessionId) => {
  if (isGenerating) {
    await stopGeneration(client, sessionId, currentBotId);
    isGenerating = false;
    hideStopButton();
  }
};

Réessayer le message ayant échoué

Réessayer d'envoyer un message qui a échoué.

Mutation GraphQL :

mutation RetryBotMessage($sessionId: Float!) {
  retryBotMessage(sessionId: $sessionId)
}

Paramètres :

  • sessionId (obligatoire) — ID de session

Retourne : Booléen (vrai si la nouvelle tentative a réussi)

Exemple

const RETRY_MESSAGE = gql`
  mutation RetryBotMessage($sessionId: Float!) {
    retryBotMessage(sessionId: $sessionId)
  }
`;

const retryMessage = async (client, sessionId) => {
  const { data } = await client.mutate({
    mutation: RETRY_MESSAGE,
    variables: { sessionId },
  });

  return data.retryBotMessage;
};

// Usage
const success = await retryMessage(client, 67890);
if (success) {
  console.log('Message retry successful');
}

Synthèse vocale

Convertir un message en audio

Convertit un message en audio à l'aide de la synthèse vocale.

Requête GraphQL :

query GetSpeechFromText($messageId: Float!) {
  getSpeechFromText(messageId: $messageId)
}

Paramètres :

  • messageId (obligatoire) — ID du message à convertir

Renvoie : Chaîne URL audio

Exemple :

const GET_SPEECH = gql`
  query GetSpeechFromText($messageId: Float!) {
    getSpeechFromText(messageId: $messageId)
  }
`;

const getAudioUrl = async (client, messageId) => {
  const { data } = await client.query({
    query: GET_SPEECH,
    variables: { messageId },
  });

  return data.getSpeechFromText;
};

// Usage
const audioUrl = await getAudioUrl(client, 12345);
const audio = new Audio(audioUrl);
audio.play();

Avec interface utilisateur du lecteur audio :

const playMessageAudio = async (client, messageId) => {
  try {
    const audioUrl = await getAudioUrl(client, messageId);
    
    const audio = new Audio(audioUrl);
    audio.addEventListener('ended', () => {
      console.log('Audio playback finished');
    });
    
    audio.play();
    return audio;
  } catch (error) {
    console.error('Failed to play audio:', error);
  }
};

// Usage in UI
const audioElement = await playMessageAudio(client, messageId);

Classe complète de gestionnaire de chat

Voici une classe réutilisable qui encapsule toutes les opérations de chat :

class ChatManager {
  constructor(client, sessionId) {
    this.client = client;
    this.sessionId = sessionId;
    this.currentBotId = null;
    this.isGenerating = false;
  }

  async sendMessage(messageText, options = {}) {
    const SEND_MESSAGE = gql`
      mutation SendNewMessage($newMessageData: NewMessageInput!) {
        sendNewMessage(newMessageData: $newMessageData) {
          id
          message
          createdAt
          sessionId
          isBotReply
        }
      }
    `;

    const { data } = await this.client.mutate({
      mutation: SEND_MESSAGE,
      variables: {
        newMessageData: {
          message: messageText,
          sessionId: this.sessionId,
          isForAiReply: true,
          ...options,
        },
      },
    });

    return data.sendNewMessage;
  }

  async sendSyncMessage(messageText, options = {}) {
    const SYNC_REQUEST = gql`
      mutation SyncBotRequest($newMessageData: NewMessageInput!) {
        syncBotRequest(newMessageData: $newMessageData) {
          text
          isBotError
          sources
          botId
        }
      }
    `;

    const { data } = await this.client.mutate({
      mutation: SYNC_REQUEST,
      variables: {
        newMessageData: {
          message: messageText,
          sessionId: this.sessionId,
          ...options,
        },
      },
    });

    return data.syncBotRequest;
  }

  subscribeToStream(onChunk, onComplete) {
    const MESSAGE_STREAM = gql`
      subscription OnMessageStream($sessionId: Float!) {
        onMessageStream(sessionId: $sessionId) {
          messageChunk
          botResponseMessageId
          isStoppable
          botId
        }
      }
    `;

    let fullMessage = '';

    return this.client
      .subscribe({
        query: MESSAGE_STREAM,
        variables: { sessionId: this.sessionId },
      })
      .subscribe({
        next: ({ data }) => {
          fullMessage += data.onMessageStream.messageChunk;
          this.currentBotId = data.onMessageStream.botId;
          this.isGenerating = true;
          onChunk(fullMessage, data.onMessageStream);
        },
        error: (error) => {
          console.error('Stream error:', error);
          this.isGenerating = false;
        },
        complete: () => {
          this.isGenerating = false;
          if (onComplete) onComplete(fullMessage);
        },
      });
  }

  async stopGeneration() {
    if (!this.isGenerating || !this.currentBotId) {
      return false;
    }

    const STOP = gql`
      mutation StopAIResponse($sessionId: Float!, $botId: Float!) {
        stopAIResponse(sessionId: $sessionId, botId: $botId)
      }
    `;

    const { data } = await this.client.mutate({
      mutation: STOP,
      variables: {
        sessionId: this.sessionId,
        botId: this.currentBotId,
      },
    });

    this.isGenerating = false;
    return data.stopAIResponse;
  }

  async regenerateResponse(messageId) {
    const REGENERATE = gql`
      mutation RegenerateResponse($messageId: Float!) {
        regenerateResponse(messageId: $messageId) {
          id
          message
        }
      }
    `;

    const { data } = await this.client.mutate({
      mutation: REGENERATE,
      variables: { messageId },
    });

    return data.regenerateResponse;
  }

  async retryMessage() {
    const RETRY = gql`
      mutation RetryBotMessage($sessionId: Float!) {
        retryBotMessage(sessionId: $sessionId)
      }
    `;

    const { data } = await this.client.mutate({
      mutation: RETRY,
      variables: { sessionId: this.sessionId },
    });

    return data.retryBotMessage;
  }

  async getAudioUrl(messageId) {
    const GET_SPEECH = gql`
      query GetSpeechFromText($messageId: Float!) {
        getSpeechFromText(messageId: $messageId)
      }
    `;

    const { data } = await this.client.query({
      query: GET_SPEECH,
      variables: { messageId },
    });

    return data.getSpeechFromText;
  }

  async playAudio(messageId) {
    const audioUrl = await this.getAudioUrl(messageId);
    const audio = new Audio(audioUrl);
    audio.play();
    return audio;
  }
}

// Usage example
const chatManager = new ChatManager(client, 67890);

// Send a message and listen to streaming
await chatManager.sendMessage('Tell me a story');

const subscription = chatManager.subscribeToStream(
  (fullMessage, data) => {
    console.log('Streaming:', fullMessage);
    updateUI(fullMessage);
  },
  (finalMessage) => {
    console.log('Response complete');
  }
);

// User clicks stop button
await chatManager.stopGeneration();

// User clicks regenerate
await chatManager.regenerateResponse(messageId);

// User clicks speaker icon
await chatManager.playAudio(messageId);

Cas d'utilisation pratiques

Interface utilisateur de chat en temps réel

Créez une interface de chat avec des réponses en streaming :

const chatManager = new ChatManager(client, sessionId);

// User types and sends message
const handleSendMessage = async (userInput) => {
  // Display user message immediately
  addMessageToUI(userInput, 'user');

  // Send message and start streaming
  await chatManager.sendMessage(userInput);

  // Create placeholder for bot response
  let botMessageElement = addMessageToUI('', 'bot');

  // Subscribe to streaming
  chatManager.subscribeToStream(
    (fullMessage) => {
      // Update bot message as it streams
      updateMessageContent(botMessageElement, fullMessage);
    },
    () => {
      // Finalize when complete
      finalizeMessage(botMessageElement);
    }
  );
};

Réponse rapide sans streaming

Pour les questions-réponses simples, obtenez immédiatement la réponse complète :

const handleQuickQuestion = async (question) => {
  try {
    const response = await chatManager.sendSyncMessage(question);
    
    if (response.isBotError) {
      showError('Bot encountered an error');
      return;
    }

    displayAnswer(response.text);
    
    if (response.sources) {
      displaySources(response.sources);
    }
  } catch (error) {
    showError('Failed to get answer');
  }
};

Lecture audio pour l'accessibilité

Permettez aux utilisateurs d'écouter les réponses :

const handleAudioButton = async (messageId) => {
  try {
    showLoadingSpinner();
    const audio = await chatManager.playAudio(messageId);
    hideLoadingSpinner();

    audio.addEventListener('ended', () => {
      console.log('Audio finished');
    });
  } catch (error) {
    hideLoadingSpinner();
    showError('Failed to play audio');
  }
};

Meilleures pratiques

  1. Définissez toujours l'isForAiReply: true lorsque vous souhaitez une réponse de l'IA ; sinon, le message est simplement stocké sans déclencher la génération.

  2. Utilisez le streaming pour une meilleure expérience utilisateur — Le streaming affiche les réponses caractère par caractère, offrant un retour immédiat. N'utilisez le mode synchrone que pour les cas simples.

  3. Gérez l'arrêt avec élégance : affichez un bouton d'arrêt pendant la génération et gérez l'action d'arrêt de manière propre.

  4. Stockez les identifiants des messages — Conservez les identifiants des messages pour la régénération et les fonctionnalités audio.

  5. Validez les entrées avant l'envoi — Vérifiez la longueur et le contenu des messages avant l'envoi afin d'éviter les erreurs.

  6. Implémentez la gestion des erreurs — Enveloppez toujours les mutations dans des blocs try-catch et affichez des messages d'erreur conviviaux.

  7. Utilisez les pièces jointes à bon escient — Ne joignez des fichiers que lorsque cela est nécessaire ; validez les identifiants de fichiers avant l'envoi.

  8. Mettre en cache les URL audio — Stockez les URL audio générées pour éviter de régénérer plusieurs fois le même fichier audio.