package chat import ( "context" "log/slog" "strings" "trankilou.fr/lassistanoque/backend/internal/domain" ) // markdownDecorations liste les marqueurs markdown susceptibles d'apparaître // dans un titre généré par un LLM. var markdownDecorations = []string{"**", "__", "~~", "*", "_", "~", "#", "`"} func sanitizeTitle(title string) string { title = strings.TrimSpace(title) // le titre doit tenir sur une ligne title = strings.ReplaceAll(title, "\n", " ") // guillemets et tirets d'encadrement ajoutés par le LLM title = strings.Trim(title, `"'«»`) // décorations markdown for _, marker := range markdownDecorations { title = strings.ReplaceAll(title, marker, "") } // espaces multiples créés par les suppressions title = strings.Join(strings.Fields(title), " ") return strings.TrimSpace(title) } func (s *Service) RefreshTitles(provider *domain.Provider, modelID string, teamID string) { ctx := context.Background() chats, err := s.repoChat.ListChatsWithUnfreshTitles(teamID) if err != nil { slog.Error("RefreshTitles.ListChats", "error", err) } for _, chat := range chats { newchat, _ := s.repoChat.GetChat(chat.UserID, chat.TeamID, chat.ID) if !chat.FreshTitle { messages, err := s.repoChat.GetChatMessages(newchat.UserID, newchat.TeamID, newchat.ID, true) if err != nil { slog.Error("RefreshTitles.GetMessages", "error", err) } title, err := s.llmEngine.Generate(ctx, provider, modelID, append(messages, &domain.Message{ Role: string(domain.RoleSystem), Content: "Tu génères un titre de conversation. Réponds uniquement par le titre, " + "en texte brut : jamais de markdown (pas de **, *, _, ~, #), " + "pas de guillemets, pas de liste, pas de ponctuation finale.", }, &domain.Message{ Role: string(domain.RoleUser), Content: "Résume toute la conversation dans un titre entre 5 et 10 mots, en texte brut, sans aucun formatage.", })) if err != nil { slog.Error("RefreshTitles.Generate", "error", err) } newchat.FreshTitle = true newchat.Title = sanitizeTitle(title) s.repoChat.UpdateChat(newchat.UserID, newchat) } } }