You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

486 lines
16KB

  1. using Blazored.Toast.Services;
  2. using Microsoft.AspNetCore.Components;
  3. using Microsoft.AspNetCore.Components.Authorization;
  4. using Microsoft.AspNetCore.Components.Forms;
  5. using Microsoft.AspNetCore.Components.Web;
  6. using Microsoft.JSInterop;
  7. using ReAct_PME.Domain;
  8. using ReAct_PME.WebUI.ServicesUI;
  9. using System.Net.Http.Json;
  10. namespace ReAct_PME.WebUI.Pages.ChatRoom
  11. {
  12. public partial class ChatRoom_base : ComponentBase
  13. {
  14. [Parameter] public int TypeLLM { get; set; }
  15. [Parameter] public string? ConversationId { get; set; }
  16. [Inject] public HttpClient Http { get; set; } = default!;
  17. [Inject] private ReAct_PME.WebUI.ServicesUI.AuthService AuthService { get; set; } = default!;
  18. [Inject] private ReAct_PME.WebUI.ServicesUI.ApiService ApiService { get; set; } = default!;
  19. [Inject] private AuthenticationStateProvider AuthenticationStateProvider { get; set; } = default!;
  20. [Inject] private NavigationManager Navigation { get; set; } = default!;
  21. [Inject] private IToastService ToastService { get; set; } = default!;
  22. [Inject] private IJSRuntime JSRuntime { get; set; } = default!;
  23. public class UploadedDoc
  24. {
  25. public string FileName { get; set; } = "";
  26. public byte[] Bytes { get; set; } = Array.Empty<byte>();
  27. }
  28. public List<UploadedDoc> UploadedDocuments { get; set; } = new();
  29. // État de l'UI
  30. public List<ConversationDto> Conversations { get; set; } = new();
  31. public ConversationDto? SelectedConversation { get; set; }
  32. public string SelectedConversationId { get; set; } = "";
  33. public List<MessageDto> Messages { get; set; } = new();
  34. private bool IsWithHistorique { get; set; }
  35. private bool isTyping { get; set; } = false;
  36. private bool isDisabled { get; set; } = false;
  37. public string CurrentInput { get; set; } = "";
  38. public int SelectedAgentType { get; set; } = 1; // Agent par défaut sélectionné
  39. public string ClientId { get; set; } = ""; // ID Client pour AUDIT
  40. // Options
  41. public List<string> RagDomains { get; set; } = new();
  42. public List<string> OllamaModels { get; set; } = new();
  43. public string SelectedModel { get; set; } = "";
  44. public string SelectedMode { get; set; } = "";
  45. public string SelectedDomain { get; set; } = "";
  46. private bool IsNewConversation = false;
  47. public bool hiddenDomain = true;
  48. protected override async Task OnParametersSetAsync()
  49. {
  50. // Configuration selon le type de LLM
  51. if (TypeLLM == 0)
  52. {
  53. // Mode Home - pas de configuration particulière
  54. return;
  55. }
  56. await LoadConversations();
  57. if (TypeLLM == 1)
  58. {
  59. SelectedMode = "llm";
  60. IsWithHistorique = true;
  61. hiddenDomain = true;
  62. }
  63. else if (TypeLLM == 2)
  64. {
  65. SelectedMode = "rag";
  66. IsWithHistorique = false;
  67. hiddenDomain = false;
  68. await LoadRagDomains();
  69. if (RagDomains.Any())
  70. SelectedDomain = RagDomains[0];
  71. }
  72. else if (TypeLLM == 3)
  73. {
  74. SelectedMode = "audit";
  75. IsWithHistorique = false;
  76. hiddenDomain = true;
  77. SelectedDomain = "comptabilite"; // Domaine fixe pour AUDIT
  78. }
  79. else if (TypeLLM == 4)
  80. {
  81. SelectedMode = "image";
  82. IsWithHistorique = false;
  83. hiddenDomain = true;
  84. }
  85. else
  86. {
  87. SelectedMode = "llm";
  88. IsWithHistorique = true;
  89. hiddenDomain = true;
  90. }
  91. if (!string.IsNullOrEmpty(ConversationId))
  92. {
  93. SelectConversation(ConversationId);
  94. }
  95. else
  96. {
  97. Console.WriteLine("No conversationId in URL");
  98. }
  99. StateHasChanged();
  100. }
  101. protected override async Task OnInitializedAsync()
  102. {
  103. #region Vérification si utilisateur est connecté et s'il a les droits
  104. var tok = await PagesInitializer.VerifConnexionAndRules(
  105. AuthenticationStateProvider,
  106. Navigation,
  107. Http,
  108. ToastService,
  109. "/api/ChatRoom/llm/test"
  110. );
  111. if (tok == null || tok.Length == 0)
  112. return;
  113. else
  114. AuthService.SetToken(tok);
  115. #endregion
  116. }
  117. private async Task UploadDocuments(InputFileChangeEventArgs e)
  118. {
  119. foreach (var file in e.GetMultipleFiles())
  120. {
  121. using var stream = file.OpenReadStream(maxAllowedSize: 20 * 1024 * 1024); // 20 Mo
  122. using var ms = new MemoryStream();
  123. await stream.CopyToAsync(ms);
  124. UploadedDocuments.Add(new UploadedDoc
  125. {
  126. FileName = file.Name,
  127. Bytes = ms.ToArray()
  128. });
  129. }
  130. StateHasChanged();
  131. }
  132. private void RemoveDocument(UploadedDoc doc)
  133. {
  134. UploadedDocuments.Remove(doc);
  135. StateHasChanged();
  136. }
  137. private async Task LoadConversations()
  138. {
  139. if (string.IsNullOrEmpty(ConversationId))
  140. return;
  141. var oneConversation = await Http.GetFromJsonAsync<ConversationDto>($"/api/ChatRoom/conversation/{ConversationId}")
  142. ?? new();
  143. Conversations = new();
  144. Conversations.Add(oneConversation);
  145. StateHasChanged();
  146. }
  147. private async Task LoadRagDomains()
  148. {
  149. RagDomains = await Http.GetFromJsonAsync<List<string>>("/api/ChatRoom/rag/listeDomaines")
  150. ?? new();
  151. }
  152. private async Task LoadOllamaModels()
  153. {
  154. OllamaModels = await Http.GetFromJsonAsync<List<string>>("/api/ChatRoom/ollama/models")
  155. ?? new();
  156. if (OllamaModels.Count > 0)
  157. SelectedModel = OllamaModels[0];
  158. }
  159. public void CreateConversation()
  160. {
  161. ConversationDto newConversation = new();
  162. if (newConversation != null)
  163. {
  164. IsNewConversation = true;
  165. Conversations.Add(newConversation);
  166. SelectConversation(newConversation.Id);
  167. }
  168. }
  169. public void SelectConversation(string id)
  170. {
  171. SelectedConversationId = id;
  172. SelectedConversation = Conversations.Where(c => c.Id == id).FirstOrDefault();
  173. Messages = SelectedConversation?.Messages.OrderBy(m => m.CreatedAt).ToList() ?? new();
  174. StateHasChanged();
  175. }
  176. // Méthode pour envoyer un message depuis la page Home
  177. private async Task SendMessageFromHome()
  178. {
  179. if (string.IsNullOrWhiteSpace(CurrentInput) && !UploadedDocuments.Any())
  180. return;
  181. // Vérification de l'ID Client pour AUDIT
  182. if (SelectedAgentType == 3 && string.IsNullOrWhiteSpace(ClientId))
  183. {
  184. ToastService.ShowWarning("Veuillez saisir l'ID Client pour l'audit");
  185. return;
  186. }
  187. try
  188. {
  189. isDisabled = true;
  190. isTyping = true;
  191. StateHasChanged();
  192. // Ajoute le message utilisateur dans l'UI
  193. var userMsg = new MessageDto
  194. {
  195. IsUser = true,
  196. Text = CurrentInput
  197. };
  198. Messages.Add(userMsg);
  199. var newConversationId = Guid.NewGuid().ToString();
  200. // Déterminer le mode selon l'agent sélectionné
  201. string mode = SelectedAgentType switch
  202. {
  203. 1 => "LLM",
  204. 2 => "RAG",
  205. 3 => "AUDIT",
  206. 4 => "IMAGE",
  207. _ => "LLM"
  208. };
  209. // Pour le mode RAG, charger les domaines si nécessaire
  210. string domain = "";
  211. if (SelectedAgentType == 2)
  212. {
  213. if (!RagDomains.Any())
  214. await LoadRagDomains();
  215. domain = !string.IsNullOrEmpty(SelectedDomain) ? SelectedDomain : (RagDomains.Any() ? RagDomains[0] : "");
  216. }
  217. else if (SelectedAgentType == 3)
  218. {
  219. domain = "comptabilite"; // Domaine fixe pour AUDIT
  220. }
  221. var payload = new SendMessageRequest
  222. {
  223. ConversationId = newConversationId,
  224. IsNewConversation = true,
  225. IdWorkForce = AuthService!.ID!,
  226. Mode = mode,
  227. Domain = domain,
  228. Documents64 = UploadedDocuments.Select(d => Convert.ToBase64String(d.Bytes)).ToList(),
  229. Documents = UploadedDocuments.Select(d => d.FileName).ToList(),
  230. Role = "user",
  231. Text = CurrentInput,
  232. Context = ""
  233. };
  234. CurrentInput = "";
  235. var assistantMsg = await ApiService.EnvoiRequete<MessageDto>("/api/ChatRoom/sendMessage", payload);
  236. isTyping = false;
  237. isDisabled = false;
  238. if (assistantMsg != null)
  239. {
  240. Messages.Add(assistantMsg);
  241. // Navigation vers la conversation créée avec le type d'agent sélectionné
  242. Navigation.NavigateTo($"/chatroom_base/{SelectedAgentType}/{newConversationId}");
  243. }
  244. UploadedDocuments.Clear();
  245. StateHasChanged();
  246. }
  247. catch (Exception ex)
  248. {
  249. Console.WriteLine(ex.Message);
  250. isTyping = false;
  251. isDisabled = false;
  252. ToastService.ShowError($"Erreur lors de l'envoi du message : {ex.Message}");
  253. StateHasChanged();
  254. }
  255. }
  256. public async Task SendMessage()
  257. {
  258. try
  259. {
  260. if (string.IsNullOrWhiteSpace(CurrentInput) || SelectedConversation == null)
  261. {
  262. // Si on est en mode TypeLLM > 0 mais sans conversation, on la crée
  263. if (TypeLLM > 0 && !string.IsNullOrWhiteSpace(CurrentInput))
  264. {
  265. CreateConversation();
  266. if (SelectedConversation == null)
  267. return;
  268. }
  269. else
  270. {
  271. return;
  272. }
  273. }
  274. // Vérification de l'ID Client pour AUDIT
  275. if (TypeLLM == 3 && string.IsNullOrWhiteSpace(ClientId))
  276. {
  277. ToastService.ShowWarning("Veuillez saisir l'ID Client pour l'audit");
  278. return;
  279. }
  280. isTyping = true;
  281. StateHasChanged();
  282. // Ajoute ton message dans l'UI immédiatement
  283. var userMsg = new MessageDto
  284. {
  285. IsUser = true,
  286. Text = CurrentInput
  287. };
  288. Messages.Add(userMsg);
  289. var payload = new SendMessageRequest
  290. {
  291. ConversationId = IsNewConversation ? Guid.NewGuid().ToString() : SelectedConversation.Id,
  292. IsNewConversation = IsNewConversation,
  293. IdWorkForce = AuthService!.ID!,
  294. Mode = SelectedMode.ToUpper(),
  295. Domain = SelectedDomain,
  296. Documents64 = UploadedDocuments.Select(d => Convert.ToBase64String(d.Bytes)).ToList(),
  297. Documents = UploadedDocuments.Select(d => d.FileName).ToList(),
  298. Role = "user",
  299. Text = CurrentInput,
  300. Context = IsWithHistorique ? string.Join("\n ", Messages.Select(m => m.Text)) : ""
  301. };
  302. CurrentInput = "";
  303. var assistantMsg = await ApiService.EnvoiRequete<MessageDto>("/api/ChatRoom/sendMessage", payload);
  304. isTyping = false;
  305. if (assistantMsg != null)
  306. {
  307. Messages.Add(assistantMsg);
  308. if (IsNewConversation)
  309. {
  310. // Met à jour la liste des conversations
  311. await LoadConversations();
  312. SelectConversation(payload.ConversationId);
  313. IsNewConversation = false;
  314. // Mettre à jour l'URL pour inclure l'ID de la conversation
  315. Navigation.NavigateTo($"/chatroom_base/{TypeLLM}/{payload.ConversationId}", false);
  316. }
  317. }
  318. UploadedDocuments.Clear();
  319. StateHasChanged();
  320. }
  321. catch (Exception ex)
  322. {
  323. Console.WriteLine(ex.Message);
  324. isTyping = false;
  325. StateHasChanged();
  326. }
  327. }
  328. private async Task HandleKeyPress(KeyboardEventArgs e)
  329. {
  330. if (e.Key == "Enter" && !e.ShiftKey)
  331. {
  332. await SendMessage();
  333. }
  334. }
  335. private void GoBack()
  336. {
  337. Navigation.NavigateTo("/chatroom_base/0");
  338. }
  339. private void ToggleHistory()
  340. {
  341. IsWithHistorique = !IsWithHistorique;
  342. ToastService.ShowInfo(IsWithHistorique ? "Historique activé" : "Historique désactivé");
  343. }
  344. private async Task CopyMessage(string text)
  345. {
  346. try
  347. {
  348. await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text);
  349. ToastService.ShowSuccess("Message copié !");
  350. }
  351. catch (Exception)
  352. {
  353. ToastService.ShowWarning("Impossible de copier le message");
  354. }
  355. }
  356. private void RecordVoice()
  357. {
  358. ToastService.ShowInfo("Fonctionnalité d'enregistrement vocal en cours de développement");
  359. }
  360. private string FormatFileSize(long bytes)
  361. {
  362. string[] sizes = { "B", "KB", "MB", "GB" };
  363. double len = bytes;
  364. int order = 0;
  365. while (len >= 1024 && order < sizes.Length - 1)
  366. {
  367. order++;
  368. len = len / 1024;
  369. }
  370. return $"{len:0.##} {sizes[order]}";
  371. }
  372. private async Task SelectAgent(int agentId)
  373. {
  374. SelectedAgentType = agentId;
  375. if (agentId == 2 && !RagDomains.Any())
  376. {
  377. await LoadRagDomains();
  378. if (RagDomains.Any())
  379. SelectedDomain = RagDomains[0];
  380. }
  381. else if (agentId == 3)
  382. {
  383. SelectedDomain = "compta"; // Domaine fixe pour AUDIT
  384. }
  385. StateHasChanged();
  386. }
  387. private async Task HandleFileSelection(InputFileChangeEventArgs e)
  388. {
  389. await UploadDocuments(e);
  390. }
  391. private async Task OnHomeTextareaInput()
  392. {
  393. await JSRuntime.InvokeVoidAsync("autoResizeTextarea", "homeTextarea");
  394. }
  395. private async Task OnChatroomTextareaInput()
  396. {
  397. await JSRuntime.InvokeVoidAsync("autoResizeTextarea", "chatroomTextarea");
  398. }
  399. protected override async Task OnAfterRenderAsync(bool firstRender)
  400. {
  401. if (firstRender)
  402. {
  403. try
  404. {
  405. if (TypeLLM == 0)
  406. {
  407. await JSRuntime.InvokeVoidAsync("setupTextareaAutoResize", "homeTextarea");
  408. }
  409. else
  410. {
  411. await JSRuntime.InvokeVoidAsync("setupTextareaAutoResize", "chatroomTextarea");
  412. }
  413. }
  414. catch
  415. {
  416. }
  417. }
  418. }
  419. }
  420. }