Aide pour streaming

Remarque préalable : à cause de l'utilisation du websocket, il sera nécessaire de redémarrer le serveur Payara au lieu de seulement déployer le jar de l'application.

Page JSF

Ajouter ceci dans la page (par exemple, à la fin, juste après le formulaire) :

<f:websocket channel="chat" scope="view" onmessage="socketListener"/>

Cette balise permet d'indiquer les fonctions JavaScript qui seront exécutées lors des événements liés au websocket : ouverture ou fermeture du websocket, arrivée d'un message sur le websocket (envoyé par le serveur), erreur lors de l'utilisation du websocket.

Le websocket sera créé automatiquement à l'ouverture de la page. Il est possible de modifier ce comportement.

Une communication bidirectionnelle est ainsi établie entre le serveur et la page JSF. Elle peut être utilisée dans le code Java avec l'identifiant "chat" (le nom du channel).

Si un message (une réponse partielle de l'API du LLM) est reçu par le websocket, il sera traité par la fonction JavaScript socketListener. Le plus souvent son rôle sera de mettre à jour la page JSF, en modifiant le DOM de la page. Pour cet exercice, c'est la zone d'affichage de la réponse qui sera modifiée. Quand le DOM d'une page affichée par un navigateur est modifié, le navigateur réaffiche automatiquement la page.

Pour que la réponse reste affichée, il faut que l'envoi de la requête au LLM soit un appel Ajax :

<h:commandButton value="Envoyer la question" action="#{bb.envoyer()}">
    <f:ajax execute="@form" render="conversationId"/>
</h:commandButton>

La balise <f:ajax> indique que les données du formulaire seront envoyées au serveur et qu'en retour seule la zone d'affichage de la conversation sera modifiée (sans cette balise, une nouvelle page serait retournée par le serveur et affichée par le navigateur).

Backing bean

La nouveauté par rapport aux versions précédentes sans streaming est que le backing bean reçoit un TokenStream en retour de l'envoi de la requête au LLM. Il doit configurer ce TokenStream.

Voici un exemple :

TokenStream tokenStream = llmClient.envoyerRequete(question);
// Définition des actions à effectuer pour chaque réponse partielle reçue.
tokenStream
   .onNext(token -> {
         webSocketPourChat.send(token);
   })
   .onComplete(response -> {
         AiMessage message = response.content();
         this.reponse = message.text();
         llmClient.addReponse(message);
         miseAJourConversation();
   })
   .onError(Throwable::printStackTrace)
   .start();