using ManagerService.Data; using System.Collections.Generic; namespace ManagerService.DTOs { public class AiChatRequest { public string Message { get; set; } public string InstanceId { get; set; } public AppType AppType { get; set; } public string ConfigurationId { get; set; } // null = scope instance, fourni = scope configuration public string Language { get; set; } /// /// GUID généré par le client, stable sur une session de conversation. /// History étant reconstruit côté client à chaque appel, c'est le seul moyen /// de relier deux questions d'un même visiteur côté serveur. /// Optionnel : un appel sans ConversationId reste valide, la ligne journalisée est isolée. /// public string ConversationId { get; set; } public List History { get; set; } = new(); /// /// true = interaction vocale (lunettes, assistant vocal). /// Le backend adapte le prompt : pas de markdown, pas de navigation, /// dates en toutes lettres, réponses courtes audio-friendly. /// public bool IsVoice { get; set; } = false; /// /// false = ce tour n'est pas une question posée par un visiteur, et ne doit donc /// pas être journalisé dans VisitorQuestion. Deux cas aujourd'hui : /// /// le mode proactif, dont la « question » est une consigne que le /// système s'écrit à lui-même (« Tu es un guide audio de musée. Le visiteur vient /// d'entrer dans la zone X… ») ; /// l'aperçu de conversation du Guide IA, où c'est le gestionnaire qui /// teste sa personnalité — sans quoi il pollue son propre rapport. /// /// /// ⚠️ Les jetons restent comptés dans les deux cas : ils sont réellement consommés. /// /// ⚠️ Défaut à true, et c'est délibéré : un client qui n'envoie pas le champ /// journalise, ce qui est le bon comportement pour les apps visiteur déjà publiées. /// public bool IsVisitorQuestion { get; set; } = true; } public class AiChatMessage { public string Role { get; set; } // "user" | "assistant" public string Content { get; set; } } public class AiCardDTO { public string Title { get; set; } public string Subtitle { get; set; } public string? Icon { get; set; } } public class NavigationActionDTO { public string SectionId { get; set; } public string SectionTitle { get; set; } public string SectionType { get; set; } public string? ImageUrl { get; set; } } public class AiChatResponse { public string Reply { get; set; } public List? Cards { get; set; } public NavigationActionDTO? Navigation { get; set; } /// /// false = le LLM ne s'attend pas à une réponse du visiteur (info pure, hors-sujet, politesse, beacon). /// Le front peut désactiver l'écoute automatique après le TTS. /// public bool ExpectsReply { get; set; } = true; public long TokensUsed { get; set; } /// /// Contenus sur lesquels la réponse s'appuie, relevés côté serveur à chaque appel de /// SearchKnowledge. Ils ne passent pas par le modèle : la règle « ne mentionne jamais /// les identifiants techniques » lui interdit précisément de les recopier. /// Destinés au client, pas au visiteur — c'est ce qui rend une réponse vérifiable /// quand un client conteste ce que dit son guide. /// public List? Sources { get; set; } } public class AiSourceDTO { public string ContentId { get; set; } public string ContentType { get; set; } /// Null hors document paginé. public int? PageNumber { get; set; } } public class AiTranslateRequest { public string Text { get; set; } public string SourceLang { get; set; } public List TargetLangs { get; set; } = new(); } public class AiTranslateResponse { public Dictionary Translations { get; set; } = new(); public long TokensUsed { get; set; } } }