166 lines
8.3 KiB
C#
166 lines
8.3 KiB
C#
using ManagerService.Data;
|
|
using ManagerService.DTOs;
|
|
using Microsoft.Extensions.AI;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace ManagerService.Services
|
|
{
|
|
public class AssistantService
|
|
{
|
|
private readonly IChatClient _chatClient;
|
|
private readonly MyInfoMateDbContext _context;
|
|
|
|
private const int MaxHistoryMessages = 10;
|
|
|
|
public AssistantService(IChatClient chatClient, MyInfoMateDbContext context)
|
|
{
|
|
_chatClient = chatClient;
|
|
_context = context;
|
|
}
|
|
|
|
public async Task<AiChatResponse> ChatAsync(AiChatRequest request)
|
|
{
|
|
var messages = new List<ChatMessage>();
|
|
ChatOptions options;
|
|
|
|
if (request.ConfigurationId != null)
|
|
{
|
|
// Scope configuration : l'IA connaît uniquement les sections de cette configuration
|
|
var sections = _context.Sections.Where(s => s.ConfigurationId == request.ConfigurationId).ToList();
|
|
var config = _context.Configurations.FirstOrDefault(c => c.Id == request.ConfigurationId);
|
|
|
|
var sectionsSummary = string.Join("\n", sections.Select(s =>
|
|
{
|
|
var title = s.Title?.FirstOrDefault(t => t.language == request.Language)?.value
|
|
?? s.Title?.FirstOrDefault()?.value
|
|
?? s.Label;
|
|
return $"- id:{s.Id} | type:{s.Type} | titre:\"{title}\"";
|
|
}));
|
|
|
|
messages.Add(new ChatMessage(ChatRole.System, $"""
|
|
Tu es l'assistant de visite de "{config?.Label ?? "cette application"}".
|
|
Tu réponds en {request.Language}. Tu es chaleureux, concis et utile.
|
|
|
|
Voici les sections disponibles dans cette application :
|
|
{sectionsSummary}
|
|
|
|
Pour obtenir le détail d'une section spécifique, utilise l'outil GetSectionDetail.
|
|
Pour afficher une liste d'éléments (événements, activités...) de façon visuelle, appelle show_cards avec les titres et sous-titres.
|
|
Pour proposer à l'utilisateur d'aller directement dans une section, appelle navigate_to_section.
|
|
Accompagne toujours tes outils d'un texte explicatif.
|
|
Ne parle jamais de sections qui ne sont pas dans cette liste.
|
|
"""));
|
|
|
|
foreach (var h in request.History.TakeLast(MaxHistoryMessages))
|
|
messages.Add(new ChatMessage(h.Role == "user" ? ChatRole.User : ChatRole.Assistant, h.Content));
|
|
|
|
messages.Add(new ChatMessage(ChatRole.User, request.Message));
|
|
|
|
NavigationActionDTO? navigation = null;
|
|
List<AiCardDTO>? cards = null;
|
|
|
|
// Outil scopé : vérifie que la section appartient bien à cette configuration
|
|
var scopedSections = sections;
|
|
var tools = new List<AITool>
|
|
{
|
|
AIFunctionFactory.Create(
|
|
(string sectionId) =>
|
|
{
|
|
var section = scopedSections.FirstOrDefault(s => s.Id == sectionId);
|
|
if (section == null) return "Section non trouvée dans cette application.";
|
|
|
|
var title = section.Title?.FirstOrDefault(t => t.language == request.Language)?.value
|
|
?? section.Title?.FirstOrDefault()?.value
|
|
?? section.Label;
|
|
var desc = section.Description?.FirstOrDefault(t => t.language == request.Language)?.value
|
|
?? section.Description?.FirstOrDefault()?.value
|
|
?? "";
|
|
return $"Titre: {title}\nDescription: {desc}\nType: {section.Type}";
|
|
},
|
|
"GetSectionDetail",
|
|
"Récupère le titre et la description complète d'une section à partir de son id"
|
|
),
|
|
AIFunctionFactory.Create(
|
|
(string sectionId, string sectionTitle, string sectionType) =>
|
|
{
|
|
navigation = new NavigationActionDTO { SectionId = sectionId, SectionTitle = sectionTitle, SectionType = sectionType };
|
|
return Task.FromResult("Navigation proposée à l'utilisateur.");
|
|
},
|
|
"navigate_to_section",
|
|
"Propose navigation to a specific section when the user wants to go there or see it."
|
|
),
|
|
AIFunctionFactory.Create(
|
|
(string[] titles, string[] subtitles, string[]? icons) =>
|
|
{
|
|
cards = titles.Select((t, i) => new AiCardDTO
|
|
{
|
|
Title = t,
|
|
Subtitle = i < subtitles.Length ? subtitles[i] : "",
|
|
Icon = icons != null && i < icons.Length ? icons[i] : null
|
|
}).ToList();
|
|
return Task.FromResult("Cartes affichées.");
|
|
},
|
|
"show_cards",
|
|
"Display structured info cards in the chat. Use for lists of items (events, activities...) that benefit from visual presentation."
|
|
)
|
|
};
|
|
|
|
options = new ChatOptions { Tools = tools };
|
|
var response = await _chatClient.GetResponseAsync(messages, options);
|
|
return new AiChatResponse { Reply = response.Text ?? "", Cards = cards, Navigation = navigation };
|
|
}
|
|
else
|
|
{
|
|
// Scope instance : l'IA connaît les configurations disponibles
|
|
var configurations = _context.Configurations.Where(c => c.InstanceId == request.InstanceId).ToList();
|
|
|
|
var configsSummary = string.Join("\n", configurations.Select(c =>
|
|
{
|
|
var title = c.Title?.FirstOrDefault(t => t.language == request.Language)?.value
|
|
?? c.Title?.FirstOrDefault()?.value
|
|
?? c.Label;
|
|
return $"- id:{c.Id} | titre:\"{title}\"";
|
|
}));
|
|
|
|
messages.Add(new ChatMessage(ChatRole.System, $"""
|
|
Tu es l'assistant de visite.
|
|
Tu réponds en {request.Language}. Tu es chaleureux, concis et utile.
|
|
|
|
Voici les expériences de visite disponibles :
|
|
{configsSummary}
|
|
|
|
Aide le visiteur à trouver ce qui l'intéresse parmi ces expériences.
|
|
Pour orienter le visiteur vers une visite spécifique, appelle navigate_to_configuration.
|
|
Accompagne toujours tes outils d'un texte explicatif.
|
|
"""));
|
|
|
|
foreach (var h in request.History.TakeLast(MaxHistoryMessages))
|
|
messages.Add(new ChatMessage(h.Role == "user" ? ChatRole.User : ChatRole.Assistant, h.Content));
|
|
|
|
messages.Add(new ChatMessage(ChatRole.User, request.Message));
|
|
|
|
NavigationActionDTO? navigation = null;
|
|
|
|
var tools = new List<AITool>
|
|
{
|
|
AIFunctionFactory.Create(
|
|
(string configurationId, string configurationTitle) =>
|
|
{
|
|
navigation = new NavigationActionDTO { SectionId = configurationId, SectionTitle = configurationTitle, SectionType = "Configuration" };
|
|
return Task.FromResult("Navigation vers la visite proposée.");
|
|
},
|
|
"navigate_to_configuration",
|
|
"Propose navigation to a specific visit/configuration when the user wants to start or explore it."
|
|
)
|
|
};
|
|
|
|
options = new ChatOptions { Tools = tools };
|
|
var response = await _chatClient.GetResponseAsync(messages, options);
|
|
return new AiChatResponse { Reply = response.Text ?? "", Navigation = navigation };
|
|
}
|
|
}
|
|
}
|
|
}
|