Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

722 lines
28KB

  1. using Services.Models;
  2. using System.Diagnostics;
  3. using System.Net.Http.Json;
  4. using System.Text;
  5. using System.Text.Json;
  6. using System.Text.RegularExpressions;
  7. using ToolsServices;
  8. using OllamaService.Models;
  9. namespace Services.Ollama
  10. {
  11. public static class OllamaService
  12. {
  13. #region Variables
  14. private static HttpClient? _HttpClient;
  15. private static readonly string NomFichierData = FichiersInternesService.ParamsOllama;// "paramsOllama.txt";
  16. private static List<string> ImagesFullFilename = new();
  17. #endregion
  18. #region Méthodes publiques
  19. #region InterActions
  20. /// <summary>
  21. /// Vérifie si Ollama est actif
  22. /// </summary>
  23. /// <param name="isOnDemand"></param>
  24. /// <returns></returns>
  25. public static async Task<bool> IsOllamaActif(bool isOnDemand)
  26. {
  27. var precisionContext = isOnDemand ? "OnDemand" : "Batch";
  28. LoggerService.LogInfo($"OllamaService.IsOllamaActif {precisionContext}");
  29. try
  30. {
  31. ParametresOllamaService? ollama = LoadParametres();
  32. if (ollama == null)
  33. {
  34. var msg = "Erreur de chargement des paramètres Ollama.";
  35. LoggerService.LogWarning(msg);
  36. return false;
  37. }
  38. var url = "";
  39. if(isOnDemand)
  40. url = $"{ollama.Ollama_URL}/api/version";
  41. else
  42. url = $"{ollama.Ollama_Batch_URL}/api/version";
  43. _HttpClient = new HttpClient();
  44. _HttpClient.Timeout = TimeSpan.FromSeconds(30);
  45. var response = await _HttpClient.GetAsync(url);
  46. LoggerService.LogDebug($"OllamaService.IsOllamaActif {precisionContext} : {response.IsSuccessStatusCode}");
  47. return response.IsSuccessStatusCode;
  48. }
  49. catch(Exception ex)
  50. {
  51. LoggerService.LogError($"OllamaService.IsOllamaActif {precisionContext} : False --> {ex.Message}");
  52. return false;
  53. }
  54. }
  55. /// <summary>
  56. /// Appelle l'api Generate de Ollama
  57. /// </summary>
  58. /// <param name="prompt"></param>
  59. /// <param name="precision"></param>
  60. /// <param name="model"></param>
  61. /// <param name="isOnDemand"></param>
  62. /// <returns></returns>
  63. public static async Task<(bool, string)> GenererAsync(string prompt, string precision, string model, bool isOnDemand)
  64. {
  65. var precisionContext = isOnDemand ? "OnDemand" : "Batch";
  66. LoggerService.LogInfo($"OllamaService.GenererAsync {precisionContext} : {model}");
  67. string? sReturn = "";
  68. Stopwatch chrono = new Stopwatch();
  69. chrono.Start();
  70. bool isSuccess = true;
  71. ParametresOllamaService? ollama = LoadParametres();
  72. if (ollama == null)
  73. {
  74. var msg = "Erreur de chargement des paramètres Ollama.";
  75. LoggerService.LogWarning(msg);
  76. return (false, msg);
  77. }
  78. try
  79. {
  80. //prompt = "Donne tes réponses dans la langue Française." + "\n" + prompt;
  81. prompt = PromptService.GetPrompt(PromptService.ePrompt.OllamaService_PromptSystem) + "\n" + prompt;
  82. var requestBody = new
  83. {
  84. model = model,
  85. prompt = TruncatePromptBySentenceAsync(prompt),
  86. temperature = isOnDemand ? ollama.Ollama_Temperature : ollama.Ollama_Batch_Temperature, // → 0 : réponses plus précises et conservatrices ; 1 : plus créatives
  87. top_p = isOnDemand ? ollama.Ollama_Top_p : ollama.Ollama_Batch_Top_p, // → on considère uniquement les mots parmi les plus probables (limite l’invention)
  88. max_tokens = isOnDemand ? ollama.Ollama_Max_tokens : ollama.Ollama_Batch_Max_tokens, // → pour ne pas générer des réponses trop longues
  89. stream = false
  90. };
  91. var url = isOnDemand ? ollama.Ollama_URL : ollama.Ollama_Batch_URL;
  92. int timeOut = isOnDemand ? ollama.Ollama_TimeOut : ollama.Ollama_Batch_TimeOut;
  93. url = $"{url}/api/generate";
  94. LoggerService.LogDebug($"Génération Ollama : {precision} - Model : {model} - URL : {url} - Time-out : {timeOut}");
  95. LoggerService.LogDebug($"Prompt : {requestBody.prompt}");
  96. _HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(timeOut) };
  97. var response = await _HttpClient.PostAsJsonAsync($"{url}", requestBody);
  98. if (!response.IsSuccessStatusCode)
  99. {
  100. var err = await response.Content.ReadAsStringAsync();
  101. var msg = $"Erreur Ollama({response.StatusCode}) : {err}";
  102. LoggerService.LogWarning(msg);
  103. return (false, $"{msg}");
  104. }
  105. var json = await response.Content.ReadFromJsonAsync<JsonElement>();
  106. sReturn = json!.GetProperty("response")!.GetString();
  107. if (sReturn != null)
  108. {
  109. sReturn = EpureReponse(sReturn);
  110. }
  111. LoggerService.LogDebug($"OllamaService.GenerateAsync : {sReturn}");
  112. }
  113. catch (HttpRequestException ex)
  114. {
  115. isSuccess = false;
  116. LoggerService.LogError("Erreur HTTP : " + ex.Message);
  117. }
  118. catch (OperationCanceledException)
  119. {
  120. isSuccess = false;
  121. LoggerService.LogError($"La requête a été annulée après {ollama.Ollama_TimeOut} secondes.");
  122. }
  123. catch (Exception ex)
  124. {
  125. isSuccess = false;
  126. sReturn = $"Erreur lors de la génération : {ex.Message}";
  127. LoggerService.LogError(sReturn);
  128. //return $"{msg}";
  129. }
  130. finally
  131. {
  132. chrono.Stop();
  133. LoggerService.LogInfo($"Temps de la génération : {chrono.ElapsedMilliseconds / 1000} s ({precision})");
  134. }
  135. return (isSuccess,$"{sReturn}");
  136. }
  137. /// <summary>
  138. /// Appelle l'api Chat de Ollama
  139. /// </summary>
  140. /// <param name="model"></param>
  141. /// <param name="messages"></param>
  142. /// <returns></returns>
  143. public static async Task<(bool, string)> ChatAsync(string model, List<ChatMessage> messages)
  144. {
  145. LoggerService.LogInfo($"OllamaService.SendMessageAsync : {model}");
  146. Stopwatch chrono = new Stopwatch();
  147. chrono.Start();
  148. string? sReturn = "";
  149. bool isSuccess = true;
  150. ParametresOllamaService? ollama = LoadParametres();
  151. if (ollama == null)
  152. {
  153. var msg = "Erreur de chargement des paramètres Ollama.";
  154. LoggerService.LogWarning(msg);
  155. return (false, msg);
  156. }
  157. try
  158. {
  159. var promptSystem = PromptService.GetPrompt(PromptService.ePrompt.OllamaService_PromptSystem);
  160. var systemPrompt = new ChatMessage { Role = "system", Content = promptSystem, Model = model };
  161. var allMessages = new List<ChatMessage> { systemPrompt };
  162. messages[0].Content = promptSystem + "\n" + messages[0].Content;
  163. allMessages.AddRange(messages);
  164. var request = new
  165. {
  166. model = model,
  167. messages = allMessages.Select(m => new { role = m.Role, content = m.Content }).ToList(),
  168. temperature = ollama.Ollama_Temperature, // → 0 : réponses plus précises et conservatrices ; 1 : plus créatives
  169. top_p = ollama.Ollama_Top_p, // → on considère uniquement les mots parmi les plus probables (limite l’invention)
  170. max_tokens = ollama.Ollama_Max_tokens, // → pour ne pas générer des réponses trop longues
  171. stream = false
  172. };
  173. var url = $"{ollama.Ollama_URL}/api/chat";
  174. LoggerService.LogDebug($"Génération Ollama : CHAT - Model : {model} - URL : {url} - Time-out : {ollama.Ollama_TimeOut}");
  175. LoggerService.LogDebug($"Prompt : {request.messages[1].content}");
  176. _HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(ollama.Ollama_TimeOut) };
  177. var response = await _HttpClient.PostAsync(url, new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"));
  178. if (!response.IsSuccessStatusCode)
  179. {
  180. var err = await response.Content.ReadAsStringAsync();
  181. var msg = $"Erreur Ollama({response.StatusCode}) : {err}";
  182. LoggerService.LogWarning(msg);
  183. return (false, $"{msg}");
  184. }
  185. var result = await response.Content.ReadAsStringAsync();
  186. using var doc = JsonDocument.Parse(result);
  187. sReturn = doc.RootElement.GetProperty("message").GetProperty("content").GetString();
  188. if (sReturn != null)
  189. {
  190. sReturn = EpureReponse(sReturn);
  191. }
  192. LoggerService.LogDebug($"OllamaService.SendMessageAsync : {sReturn}");
  193. }
  194. catch (Exception ex)
  195. {
  196. isSuccess = false;
  197. sReturn = $"Erreur lors de la génération : {ex.Message}";
  198. LoggerService.LogError(sReturn);
  199. }
  200. finally
  201. {
  202. chrono.Stop();
  203. LoggerService.LogInfo($"Temps de la génération : {chrono.ElapsedMilliseconds / 1000} s (LLM)");
  204. if (sReturn == null)
  205. sReturn = "";
  206. }
  207. return (isSuccess, $"{sReturn}");
  208. }
  209. /// <summary>
  210. /// Appelle l'api Generate de Ollama avec envoi d'images dans le prompt
  211. /// </summary>
  212. /// <param name="model"></param>
  213. /// <param name="prompt"></param>
  214. /// <param name="imagesFullFilename"></param>
  215. /// <returns></returns>
  216. public static async Task<(bool, string)> GenererAsync(string model, string prompt, List<string> imagesFullFilename)
  217. {
  218. LoggerService.LogInfo($"OllamaService.SendMessageAsync : {model}");
  219. Stopwatch chrono = new Stopwatch();
  220. chrono.Start();
  221. bool isSuccess = true;
  222. var imagesBase64 = new List<string>();
  223. ParametresOllamaService? ollama = LoadParametres();
  224. if (ollama == null)
  225. {
  226. var msg = "Erreur de chargement des paramètres Ollama.";
  227. LoggerService.LogWarning(msg);
  228. return (false, msg);
  229. }
  230. foreach (var uneImage in imagesFullFilename)
  231. {
  232. byte[] imageBytes = File.ReadAllBytes(uneImage);
  233. imagesBase64.Add(Convert.ToBase64String(imageBytes));
  234. }
  235. var request = new OllamaRequest
  236. {
  237. Model = model,
  238. Prompt = PromptService.GetPrompt(PromptService.ePrompt.OllamaService_PromptSystem) + "\n" + prompt,
  239. Temperature = ollama.Ollama_Temperature, // → 0 : réponses plus précises et conservatrices ; 1 : plus créatives
  240. Top_p = ollama.Ollama_Top_p, // → on considère uniquement les mots parmi les plus probables (limite l’invention)
  241. Max_tokens = ollama.Ollama_Max_tokens, // → pour ne pas générer des réponses trop longues
  242. Images = imagesBase64,
  243. Stream = false
  244. };
  245. string? sReturn = "";
  246. try
  247. {
  248. var url = $"{ollama.Ollama_URL}/api/generate";
  249. LoggerService.LogDebug($"Prompt envoyé à {url}");
  250. _HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(ollama.Ollama_TimeOut) };
  251. var response = await _HttpClient.PostAsJsonAsync(url, request);
  252. if (!response.IsSuccessStatusCode)
  253. {
  254. var err = await response.Content.ReadAsStringAsync();
  255. var msg = $"Erreur Ollama({response.StatusCode}) : {err}";
  256. LoggerService.LogWarning(msg);
  257. return (false, $"{msg}");
  258. }
  259. var result = await response.Content.ReadFromJsonAsync<OllamaResponse>();
  260. if (result != null)
  261. {
  262. sReturn = result.Completion.Trim();
  263. sReturn = EpureReponse(sReturn);
  264. }
  265. LoggerService.LogDebug($"OllamaService.SendMessageAsync : {sReturn}");
  266. }
  267. catch (Exception ex)
  268. {
  269. isSuccess=false;
  270. sReturn = $"Erreur lors de la génération : {ex.Message}";
  271. LoggerService.LogError(sReturn);
  272. //return sReturn;
  273. }
  274. finally
  275. {
  276. chrono.Stop();
  277. LoggerService.LogInfo($"Temps de la génération : {chrono.ElapsedMilliseconds / 1000} s (Interprétation images)");
  278. if (sReturn == null)
  279. sReturn = "";
  280. }
  281. return (isSuccess, $"{sReturn}");
  282. }
  283. #endregion
  284. #region Gestion des paramètres
  285. public static ParametresOllamaService? LoadParametres()
  286. {
  287. LoggerService.LogInfo("OllamaService.LoadParametres");
  288. ParametresOllamaService SelectedItem = new();
  289. try
  290. {
  291. if (File.Exists(NomFichierData))
  292. {
  293. string[] lignes = File.ReadAllLines(NomFichierData);
  294. if (lignes.Length > 0)
  295. {
  296. SelectedItem.Ollama_URL = lignes[0];
  297. }
  298. if (lignes.Length > 1)
  299. {
  300. if(int.TryParse(lignes[1], out int timeout))
  301. SelectedItem.Ollama_TimeOut = timeout;
  302. }
  303. if (lignes.Length > 2)
  304. {
  305. if (double.TryParse(lignes[2], out double temperature))
  306. SelectedItem.Ollama_Temperature = temperature;
  307. }
  308. if (lignes.Length > 3)
  309. {
  310. if (double.TryParse(lignes[3], out double top_p))
  311. SelectedItem.Ollama_Top_p = top_p;
  312. }
  313. if (lignes.Length > 4)
  314. {
  315. if (int.TryParse(lignes[4], out int max_tokens))
  316. SelectedItem.Ollama_Max_tokens = max_tokens;
  317. }
  318. if (lignes.Length > 5)
  319. {
  320. SelectedItem.Ollama_Batch_URL = lignes[5];
  321. }
  322. if (lignes.Length > 6)
  323. {
  324. if (int.TryParse(lignes[6], out int timeout))
  325. SelectedItem.Ollama_Batch_TimeOut = timeout;
  326. }
  327. if (lignes.Length > 7)
  328. {
  329. if (double.TryParse(lignes[7], out double temperature))
  330. SelectedItem.Ollama_Batch_Temperature = temperature;
  331. }
  332. if (lignes.Length > 8)
  333. {
  334. if (double.TryParse(lignes[8], out double top_p))
  335. SelectedItem.Ollama_Batch_Top_p = top_p;
  336. }
  337. if (lignes.Length > 9)
  338. {
  339. if (int.TryParse(lignes[9], out int max_tokens))
  340. SelectedItem.Ollama_Batch_Max_tokens = max_tokens;
  341. }
  342. }
  343. return SelectedItem;
  344. }
  345. catch
  346. {
  347. return null;
  348. }
  349. }
  350. public static bool SaveParametres(ParametresOllamaService selectedItem)
  351. {
  352. LoggerService.LogInfo("OllamaService.SaveParametres");
  353. try
  354. {
  355. StringBuilder sb = new();
  356. sb.AppendLine(selectedItem.Ollama_URL);
  357. sb.AppendLine(selectedItem.Ollama_TimeOut.ToString());
  358. sb.AppendLine(selectedItem.Ollama_Temperature.ToString());
  359. sb.AppendLine(selectedItem.Ollama_Top_p.ToString());
  360. sb.AppendLine(selectedItem.Ollama_Max_tokens.ToString());
  361. sb.AppendLine(selectedItem.Ollama_Batch_URL);
  362. sb.AppendLine(selectedItem.Ollama_Batch_TimeOut.ToString());
  363. sb.AppendLine(selectedItem.Ollama_Batch_Temperature.ToString());
  364. sb.AppendLine(selectedItem.Ollama_Batch_Top_p.ToString());
  365. sb.AppendLine(selectedItem.Ollama_Batch_Max_tokens.ToString());
  366. File.WriteAllText(NomFichierData, sb.ToString());
  367. return true;
  368. }
  369. catch
  370. {
  371. return false;
  372. }
  373. }
  374. public static async Task<List<OllamaModel>> GetInstalledModelsAsync(bool isOnDemand)
  375. {
  376. LoggerService.LogInfo("OllamaService.GetInstalledModelsAsync");
  377. try
  378. {
  379. ParametresOllamaService? ollama = LoadParametres();
  380. if (ollama == null)
  381. {
  382. return new List<OllamaModel> { new OllamaModel { Name = "Erreur de chargement des paramètres Ollama." } };
  383. }
  384. var url = "";
  385. int timeOut = 360;
  386. if (isOnDemand)
  387. {
  388. url = $"{ollama.Ollama_URL}/api/tags";
  389. timeOut = ollama.Ollama_TimeOut;
  390. }
  391. else
  392. {
  393. url = $"{ollama.Ollama_Batch_URL}/api/tags";
  394. timeOut = ollama.Ollama_Batch_TimeOut;
  395. }
  396. _HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(timeOut) };
  397. var response = await _HttpClient.GetAsync($"{url}");
  398. LoggerService.LogDebug($"Requête envoyée à {url}");
  399. response.EnsureSuccessStatusCode();
  400. var json = await response.Content.ReadAsStringAsync();
  401. var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = false };
  402. var result = JsonSerializer.Deserialize<OllamaTagsResponse>(json, options);
  403. if (result?.Models == null)
  404. {
  405. return new List<OllamaModel> { new OllamaModel { Name = "Erreur de chargement des paramètres Ollama." } };
  406. }
  407. return result.Models;
  408. }
  409. catch (Exception ex)
  410. {
  411. LoggerService.LogError($"Erreur lors de la récupération des modèles installés : {ex.Message}");
  412. return new List<OllamaModel> { new OllamaModel { Name = $"Erreur : {ex.Message}" } };
  413. }
  414. }
  415. public static string GetModeleIA(string useCase)
  416. {
  417. LoggerService.LogInfo("OllamaService.GetModeleIA");
  418. ModelSelector selector = new();
  419. var models = selector.GetModelsForUseCase(useCase);
  420. if(models.ToList().Count>1)
  421. return models.First();
  422. return "";
  423. }
  424. public static IEnumerable<string> GetModelesIA(string useCase)
  425. {
  426. LoggerService.LogInfo("OllamaService.GetModelesIA");
  427. ModelSelector selector = new();
  428. var models = selector.GetModelsForUseCase(useCase);
  429. return models;
  430. }
  431. public static bool SaveParametresModeles(string useCase, ModelConfig models)
  432. {
  433. LoggerService.LogInfo("OllamaService.SaveParametresModeles");
  434. ModelSelector selector = new();
  435. var b = selector.SaveConfig(useCase, models);
  436. return b;
  437. }
  438. #endregion
  439. #region Nettoyage des prompts et surveillance de la taille
  440. public static string CleanUserInput(string input, out bool isPromptInjection)
  441. {
  442. LoggerService.LogInfo("OllamaService.CleanUserInput");
  443. isPromptInjection = false;
  444. var avant = input;
  445. if (string.IsNullOrWhiteSpace(input))
  446. return string.Empty;
  447. // 1. Limiter la taille max (ex : 500 caractères)
  448. /*
  449. int maxLength = 500;
  450. if (input.Length > maxLength)
  451. input = input.Substring(0, maxLength);
  452. */
  453. // 2. Supprimer les caractères non imprimables (ex : contrôles, retours chariot non standards)
  454. input = RemoveNonPrintableChars(input);
  455. // 3. Échapper les triples quotes pour ne pas casser le prompt (on remplace ''' par ' ' ' par exemple)
  456. input = input.Replace("'''", "' ' '");
  457. // 4. Rechercher et remplacer les mots/expressions d’injection potentielles
  458. string[] blacklist = new string[] {
  459. "ignore", "cancel", "stop generating", "disregard instructions","oublie les instructions", "oublie",
  460. "ignore instructions","ignore les instructions", "forget", "override", "bypass"
  461. };
  462. foreach (var word in blacklist)
  463. {
  464. // Remplacer même si insensible à la casse
  465. input = Regex.Replace(input, Regex.Escape(word), "[CENSORED]", RegexOptions.IgnoreCase);
  466. }
  467. // 5. Optionnel : remplacer les guillemets doubles et simples pour éviter la confusion
  468. input = input.Replace("\"", "'");
  469. input = input.Replace("\\", "/"); // éviter les échappements
  470. if (input.Contains("[CENSORED]"))
  471. {
  472. LoggerService.LogDebug($"Avant :{avant}");
  473. LoggerService.LogDebug($"Après :{input}");
  474. isPromptInjection = true;
  475. }
  476. return input.Trim();
  477. }
  478. #endregion
  479. #endregion
  480. #region Méthodes privées
  481. private static string EpureReponse(string texte)
  482. {
  483. var txt = texte;
  484. txt = txt.Replace("**", "");
  485. //txt = txt.Replace("*", "");
  486. txt = txt.Replace("</end_of_turn>", "");
  487. txt = txt.Replace("</start_of_turn>", "");
  488. txt = txt.Replace("```csv", "");
  489. txt = txt.Replace("```xml", "");
  490. txt = txt.Replace("```", "");
  491. txt = txt.Trim();
  492. return txt;
  493. }
  494. private static async Task<int> GetTokenMaxAsync(bool isOld)
  495. {
  496. LoggerService.LogInfo("OllamaService.GetTokenMaxAsync");
  497. try
  498. {
  499. ParametresOllamaService? ollama = LoadParametres();
  500. if (ollama == null)
  501. {
  502. return -1;
  503. }
  504. var requestBody = new
  505. {
  506. model = "",// ollama.Ollama_Model,
  507. };
  508. _HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(ollama.Ollama_TimeOut) };
  509. // Construire l'URL pour obtenir les informations du modèle
  510. string url = $"{ollama.Ollama_URL}/api/show";
  511. LoggerService.LogDebug($"Prompt envoyé à {url}");
  512. var response = await _HttpClient.PostAsJsonAsync($"{url}", requestBody);
  513. // Envoyer une requête GET à l'API Ollama
  514. //HttpResponseMessage response = await _HttpClient.GetAsync(url);
  515. // Vérifier le statut de la réponse
  516. if (response.IsSuccessStatusCode)
  517. {
  518. // Lire le contenu de la réponse
  519. string responseBody = await response.Content.ReadAsStringAsync();
  520. var json = await response.Content.ReadFromJsonAsync<JsonElement>();
  521. var contextLength = json
  522. .GetProperty("model_info")
  523. .GetProperty("llama.context_length")
  524. .GetInt32();
  525. return contextLength;
  526. //return maxContextSize.HasValue ? (int)maxContextSize : 0;
  527. }
  528. else
  529. {
  530. var model = "";//ollama.Ollama_Model
  531. LoggerService.LogError($"Erreur lors de la récupération des informations du modèle {model}: {response.StatusCode}");
  532. return -1;
  533. }
  534. }
  535. catch (Exception ex)
  536. {
  537. LoggerService.LogError($"Erreur lors de la récupération du modèle : {ex.Message}");
  538. return -3; // Erreur lors de la récupération du modèle
  539. }
  540. }
  541. // Supprime les caractères non imprimables
  542. private static string RemoveNonPrintableChars(string text)
  543. {
  544. LoggerService.LogInfo("OllamaService.RemoveNonPrintableChars");
  545. var sb = new StringBuilder();
  546. foreach (char c in text)
  547. {
  548. // Exclure uniquement les caractères de contrôle ASCII (< 32), sauf les classiques utiles
  549. if (!char.IsControl(c) || c == '\n' || c == '\r' || c == '\t')
  550. {
  551. sb.Append(c);
  552. }
  553. }
  554. return sb.ToString();
  555. }
  556. private static int GetTokenMaxAsync()
  557. {
  558. LoggerService.LogInfo("OllamaService.GetTokenMaxAsync");
  559. try
  560. {
  561. //await Task.Delay(1);
  562. return 4096;
  563. }
  564. catch (Exception ex)
  565. {
  566. LoggerService.LogError($"Erreur lors de la récupération du modèle : {ex.Message}");
  567. return -3; // Erreur lors de la récupération du modèle
  568. }
  569. }
  570. private static int CountTokensAsync(string prompt)
  571. {
  572. LoggerService.LogInfo("OllamaService.CountTokensAsync");
  573. try
  574. {
  575. // 1 token, en langue francaise, c'est 1.25 mots (environ)
  576. int estimatedTokens = prompt.Split(' ').Length * 5 / 4;
  577. return estimatedTokens;
  578. }
  579. catch (Exception ex)
  580. {
  581. LoggerService.LogError($"Erreur lors de la récupération du modèle : {ex.Message}");
  582. return -1; // Erreur lors de la récupération du modèle
  583. }
  584. }
  585. private static string TruncatePromptBySentenceAsync(string prompt)
  586. {
  587. LoggerService.LogInfo("OllamaService.TruncatePromptBySentenceAsync");
  588. prompt = prompt.Trim();
  589. LoggerService.LogDebug($"Taille avant : {prompt.Length}");
  590. int reserveReponse = 512 * 3; // Réserve pour la réponse
  591. var maxTokens = GetTokenMaxAsync();
  592. var nbrTokens = CountTokensAsync(prompt);
  593. // Cas simple : le prompt tient dans la limite
  594. // On garde 512 * 3 de réserve pour la réponse
  595. if ((nbrTokens + reserveReponse) <= maxTokens)
  596. {
  597. LoggerService.LogDebug($"Taille après : {prompt.Length}");
  598. return prompt;
  599. }
  600. // Découpe le texte en phrases (naïvement en utilisant les points)
  601. var sentences = prompt.Split(new[] { ". ", "? ", "! " }, StringSplitOptions.RemoveEmptyEntries);
  602. StringBuilder builder = new();
  603. foreach (var sentence in sentences)
  604. {
  605. var next = builder.Length > 0 ? builder + ". " + sentence.Trim() : sentence.Trim();
  606. var tokenCount = CountTokensAsync(next.ToString());
  607. if ((tokenCount + reserveReponse) > maxTokens)
  608. {
  609. break; // Trop long, on s'arrête
  610. }
  611. builder.Clear();
  612. builder.Append(next);
  613. }
  614. var rep = builder.ToString().Trim();
  615. LoggerService.LogDebug($"Taille après : {rep.Length}");
  616. return rep;// + "\n\n[Tronqué pour respecter la limite de contexte]";
  617. }
  618. #endregion
  619. }
  620. }