package fr.******.*****.jsf; // A modifier... import jakarta.faces.application.FacesMessage; import jakarta.faces.context.FacesContext; import jakarta.faces.model.SelectItem; import jakarta.faces.view.ViewScoped; import jakarta.inject.Inject; import jakarta.inject.Named; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Locale; /** * Backing bean pour la page JSF index.xhtml. * Portée view pour conserver l'état de la conversation qui dure pendant plusieurs requêtes HTTP. */ @Named @ViewScoped public class Bb implements Serializable { /** * Rôle "système" que l'on attribuera plus tard à un LLM. * Valeur par défaut que l'utilisateur peut modifier. * Possible d'ajouter de nouveaux rôles dans la méthode getSystemRoles. */ private String roleSysteme; /** * Ce qui est affiché dans la liste déroulante pour le rôle système choisi par l'utilisateur. */ private String labelRoleSysteme; /** * Quand le rôle est choisi par l'utilisateur dans la liste déroulante, * il n'est plus possible de le modifier (voir code de la page JSF). */ private boolean roleSystemeChangeable = true; private List<SelectItem> listeSystemRoles; /** * Dernière question posée par l'utilisateur. */ private String question; /** * Dernière réponse de l'API OpenAI. */ private String reponse; /** * La conversation depuis le début. */ private StringBuilder conversation = new StringBuilder(); /** * Contexte JSF. Utilisé pour qu'un message d'erreur s'affiche dans le formulaire. */ @Inject private FacesContext facesContext; /** * Obligatoire pour un bean CDI (classe gérée par CDI). */ public Bb() { } public String getRoleSysteme() { return roleSysteme; } public void setRoleSysteme(String roleSysteme) { this.roleSysteme = roleSysteme; } public boolean isRoleSystemeChangeable() { return roleSystemeChangeable; } public String getQuestion() { return question; } public void setQuestion(String question) { this.question = question; } public String getReponse() { return reponse; } /** * setter indispensable pour le textarea. * * @param reponse la réponse à la question. */ public void setReponse(String reponse) { this.reponse = reponse; } public String getConversation() { return conversation.toString(); } public void setConversation(String conversation) { this.conversation = new StringBuilder(conversation); } /** * Envoie la question au serveur. * En attendant de l'envoyer à un LLM, le serveur fait un traitement quelconque, juste pour tester : * Le traitement consiste à copier la question en minuscules et à l'entourer avec "||". Le rôle système * est ajouté au début de la première réponse. * * @return null pour rester sur la même page. */ public String envoyer() { if (question == null || question.isBlank()) { // Erreur ! Le formulaire va être réaffiché en réponse à la requête POST, avec un message d'erreur. FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Texte question vide", "Il manque le texte de la question"); facesContext.addMessage(null, message); return null; } // Entourer la réponse avec "||". this.reponse = "||"; // Si la conversation n'a pas encore commencé, ajouter le rôle système au début de la réponse if (this.conversation.isEmpty()) { // Ajouter le rôle système au début de la réponse this.reponse += roleSysteme.toUpperCase(Locale.FRENCH) + "\n"; // Invalide le bouton pour changer le rôle système this.roleSystemeChangeable = false; } this.reponse += question.toLowerCase(Locale.FRENCH) + "||"; // La conversation contient l'historique des questions-réponses depuis le début. afficherConversation(); return null; } /** * Pour un nouveau chat. * Termine la portée view en retournant "index" (la page index.xhtml sera affichée après le traitement * effectué pour construire la réponse) et pas null. null aurait indiqué de rester dans la même page (index.xhtml) * sans changer de vue. * Le fait de changer de vue va faire supprimer l'instance en cours du backing bean par CDI et donc on reprend * tout comme au début puisqu'une nouvelle instance du backing va être utilisée par la page index.xhtml. * @return "index" */ public String nouveauChat() { return "index"; } /** * Pour afficher la conversation dans le textArea de la page JSF. */ private void afficherConversation() { this.conversation.append("== User:\n").append(question).append("\n== Serveur:\n").append(reponse).append("\n"); } public List<SelectItem> getRolesSysteme() { List<SelectItem> listeSystemRoles = new ArrayList<>(); // Ces rôles ne seront utilisés que lorsque la réponse sera données par un LLM. String role = """ You are a helpful assistant. You help the user to find the information they need. If the user type a question, you answer it. """; listeSystemRoles.add(new SelectItem(role, "Assistant")); role = """ You are an interpreter. You translate from English to French and from French to English. If the user type a French text, you translate it into English. If the user type an English text, you translate it into French. If the text contains only one to three words, give some examples of usage of these words in English. """; // 1er argument : la valeur du rôle, 2ème argument : le libellé du rôle listeSystemRoles.add(new SelectItem(role, "Traducteur Anglais-Français")); role = """ Your are a travel guide. If the user type the name of a country or of a town, you tell them what are the main places to visit in the country or the town are you tell them the average price of a meal. """; listeSystemRoles.add(new SelectItem(role, "Guide touristique")); this.listeSystemRoles = listeSystemRoles; // Présélectionne le premier rôle de la liste. this.roleSysteme = (String) listeSystemRoles.getFirst().getValue(); return listeSystemRoles; } /** * Retourne le label du rôle système dans la liste déroulante. * Il sera affiché à la place de la liste déroulante quand le rôle système est choisi. * * @return label du rôle système. */ public String getLabelRoleSysteme() { if (this.labelRoleSysteme == null) { for (SelectItem item : this.listeSystemRoles) { if (item.getValue().equals(this.roleSysteme)) { this.labelRoleSysteme = item.getLabel(); return this.labelRoleSysteme; } } return this.roleSysteme; // Ne devrait jamais arriver. } else { return this.labelRoleSysteme; } } }