|
- using Models;
- using Qdrant.Client.Grpc;
- using System.Collections;
- using System.ComponentModel.DataAnnotations;
- using System.Numerics;
- using System.Text;
- using ToolsServices;
-
-
- namespace Services;
-
- #region enum des type de documents
- public enum Domain
- {
- RH,
- Juridique,
- Global,
- Emails,
- CV,
- Technique
- }
- #endregion
-
- #region Classe statique d'extension pour Domain
- public static class DomainExtensions
- {
- private static readonly string _CollectionRH = "RH";
- private static readonly string _CollectionJuridique = "Juridique";
- private static readonly string _CollectionGlobal = "Global";
- private static readonly string _CollectionEmails = "Emails";
- private static readonly string _CollectionCV = "CV";
- private static readonly string _CollectionTechnique = "Technique";
-
- public static List<string> CollectionsName = GetCollectionsName();
- public static List<string> CollectionsNameRestricted = GetCollectionsNameRestricted(false);
-
- private static List<string> GetCollectionsName()
- {
- List<string> strings = new()
- {
- _CollectionRH,
- _CollectionJuridique,
- _CollectionGlobal,
- _CollectionEmails,
- _CollectionCV,
- _CollectionTechnique
- };
- return strings;
- }
-
- private static List<string> GetCollectionsNameRestricted(bool isVeryRestricted)
- {
- List<string> strings = new();
- strings.Add(_CollectionGlobal);
- strings.Add(_CollectionTechnique);
-
- if (!isVeryRestricted)
- {
- strings.Add(_CollectionJuridique);
- strings.Add(_CollectionRH);
- }
- return strings;
- }
-
- /// <summary>
- /// Retourne le nom de collection Qdrant associé à un domaine.
- /// </summary>
- public static string ToCollectionName(this Domain domain) => domain switch
- {
- Domain.RH => _CollectionRH,
- Domain.Juridique => _CollectionJuridique,
- Domain.Global => _CollectionGlobal,
- Domain.Emails => _CollectionEmails,
- Domain.CV => _CollectionCV,
- Domain.Technique => _CollectionTechnique,
- _ => throw new ArgumentOutOfRangeException(nameof(domain), domain, null)
- };
-
- /// <summary>
- /// Retourne le nom de collection Qdrant associé à un domaine.
- /// </summary>
- public static Domain GetDomainByCollectionName(string CollectionName)
- {
- Domain domain = Domain.Global;
-
- switch (CollectionName)
- {
- case "RH":
- domain = Domain.RH;
- break;
- case "Juridique":
- domain = Domain.Juridique;
- break;
- case "Global":
- domain = Domain.Global;
- break;
- case "Emails":
- domain = Domain.Emails;
- break;
- case "CV":
- domain = Domain.CV;
- break;
- case "Technique":
- domain = Domain.Technique;
- break;
-
- }
- return domain;
- }
-
- public static Domain GetDomainByPath(string filePath)
- {
- var dirs = Path.GetDirectoryName(filePath)?
- .Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar,
- StringSplitOptions.RemoveEmptyEntries);
-
- if (dirs == null)
- return Domain.Global;
-
- var collections = GetCollectionsName();
-
- // On cherche la première correspondance
- var collectionName = dirs.FirstOrDefault(d =>
- collections.Any(c => string.Equals(c, d, StringComparison.OrdinalIgnoreCase)));
-
- return GetDomainByCollectionName(collectionName ?? "Global");
- }
-
-
- }
- #endregion
-
- #region Classe QdrantService
- /// <summary>
- /// http://localhost:6333/dashboard
- /// </summary>
- public class QdrantService
- {
-
- #region Variables
- private static readonly string NomFichierData = FichiersInternesService.ParamsQdrant;// "paramsOllama.txt";
-
- private readonly Qdrant.Client.QdrantClient _client;
- private const int EmbeddingDim = 768;
- private const Distance DefaultDistance = Distance.Cosine;
- #endregion
-
- #region Constructeur
- public QdrantService()
- {
- var parametres = LoadParametres();
- //_client = new Qdrant.Client.QdrantClient(parametres!.Qdrant_URL, parametres!.Qdrant_Port, parametres!.Qdrant_IsHttps);
- var httpString = parametres!.Qdrant_IsHttps ? "https://" : "http://";
- _client = new Qdrant.Client.QdrantClient(new Uri($"{httpString}{parametres!.Qdrant_URL}"));
- }
- #endregion
-
- #region Méthodes publiques
-
- #region Gestion des Paramètres
- public static ParametresQdrantService? LoadParametres()
- {
- ParametresQdrantService? item = new();
- LoggerService.LogInfo("QdrantService.LoadParametres");
- try
- {
- if (File.Exists(NomFichierData))
- {
-
- string[] lignes = File.ReadAllLines(NomFichierData);
-
- if (lignes.Length > 0)
- item.Qdrant_URL = lignes[0];
-
- if (lignes.Length > 1)
- {
- if (bool.TryParse(lignes[1], out bool isHttps))
- item.Qdrant_IsHttps = isHttps;
- }
- /*
- if (lignes.Length > 2)
- {
- if (int.TryParse(lignes[1], out int port))
- item.Qdrant_Port = port;
- }
- */
- }
- return item;
-
- }
- catch
- {
- return null;
- }
-
- }
-
- public static bool SaveParametres(ParametresQdrantService item)
- {
- LoggerService.LogInfo("QdrantService.SaveParametres");
- try
- {
- StringBuilder sb = new();
- sb.AppendLine(item.Qdrant_URL);
- sb.AppendLine(item.Qdrant_IsHttps.ToString());
- //sb.AppendLine(item.Qdrant_Port.ToString());
-
- File.WriteAllText(NomFichierData, sb.ToString());
-
- return true;
- }
- catch
- {
- return false;
- }
- }
- #endregion
-
- public async Task<bool> IsAlive()
- {
- try
- {
- var health = await _client.HealthAsync();
- return (health != null);
- }
- catch
- {
- LoggerService.LogError("QdrantService.IsAlive - Qdrant n'est pas accessible.");
- return false;
- }
- }
-
- public async Task InitializeCollectionsAsync(bool isRAZCollections)
- {
- if(isRAZCollections)
- await DeleteCollectionAsync();
-
- foreach (Domain domain in Enum.GetValues(typeof(Domain)))
- {
- await EnsureCollectionAsync(domain);
- }
- }
-
- public async Task DeleteDocumentInQdrant(Domain domain, string nomFichier)
- {
- var pointId = new PointId { Uuid = Guid.NewGuid().ToString() };
- var collectionName = domain.ToCollectionName();
-
- // 1. Définir le filtre pour trouver les points avec le même `nom_fichier`
- var filter = new Qdrant.Client.Grpc.Filter
- {
- Must =
- {
- new Condition
- {
- Field = new FieldCondition
- {
- Key = "nom_fichier",
- Match = new Match { Text = nomFichier }
- }
- }
- }
- };
- // 2. Chercher les points existants avec ce filtre
- var searchResult = await _client.ScrollAsync(
- collectionName,
- filter: filter,
- limit: 1 // On n'a besoin de vérifier qu'un seul résultat
- );
- // 3. Vérifier si des points ont été trouvés
- if (searchResult.Result.Count > 0)
- {
- LoggerService.LogDebug($"Un ou plusieurs documents avec le nom de fichier '{nomFichier}' ont été trouvés. Suppression...");
-
- // Si des points existent, on utilise le même filtre pour les supprimer
- await _client.DeleteAsync(
- collectionName,
- filter
- );
-
- LoggerService.LogDebug("Documents existants supprimés avec succès.");
- }
- }
-
- public async Task IngestDocument(Domain domain, float[] vector, string nomFichier, string nomChunck, string content, string emailObject = "", string proprietaire = "", string niveauAcces="")
- {
- var pointId = new PointId { Uuid = Guid.NewGuid().ToString() };
- var collectionName = domain.ToCollectionName();
- var vectorObj = new Qdrant.Client.Grpc.Vector();
- vectorObj.Data.Add(vector);
-
- /*
- // 1. Définir le filtre pour trouver les points avec le même `nom_fichier`
- var filter = new Qdrant.Client.Grpc.Filter
- {
- Must =
- {
- new Condition
- {
- Field = new FieldCondition
- {
- Key = "nom_fichier",
- Match = new Match { Text = nomFichier }
- }
- }
- }
- };
- // 2. Chercher les points existants avec ce filtre
- var searchResult = await _client.ScrollAsync(
- collectionName,
- filter: filter,
- limit: 1 // On n'a besoin de vérifier qu'un seul résultat
- );
- // 3. Vérifier si des points ont été trouvés
- if (searchResult.Result.Count > 0)
- {
- LoggerService.LogDebug($"Un ou plusieurs documents avec le nom de fichier '{nomFichier}' ont été trouvés. Suppression...");
-
- // Si des points existent, on utilise le même filtre pour les supprimer
- await _client.DeleteAsync(
- collectionName,
- filter
- );
-
- LoggerService.LogDebug("Documents existants supprimés avec succès.");
- }
- */
-
- await _client.UpsertAsync(collectionName, new[]
- {
- new PointStruct
- {
- Id = pointId,
- Vectors = new Vectors { Vector = vectorObj },
- Payload =
- {
- ["categorie"] = collectionName,
- ["nom_fichier"] = nomFichier,
- ["email_object"] = emailObject,
- ["nom_chunck"] = nomChunck,
- ["niveau_acces"] = niveauAcces,
- ["content"] = content,
- ["proprietaire"] = proprietaire
- }
- }
- });
- }
-
- //public async Task<IReadOnlyList<ScoredPoint>> SearchAsync(Domain domain, float[] queryVector, float seuilScore, int topK = 5, string adresseMail = "")
- public async Task<List<SearchResult>> SearchAsync(Domain domain, float[] queryVector, float seuilScore, int topK=5, string adresseMail="")
- {
- var collectionName = domain.ToCollectionName();
- Filter filter = new();
- if (domain == Domain.Emails)
- {
- filter = new Qdrant.Client.Grpc.Filter
- {
- Must = {
- new Condition
- {
- Field = new FieldCondition
- {
- Key = "categorie",
- Match = new Match { Text = collectionName }
- }
- },
- new Condition
- {
- Field = new FieldCondition
- {
- Key = "proprietaire",
- Match = new Match { Text = adresseMail }
- }
- }
- }
-
- };
- }
- else
- {
- filter = new Qdrant.Client.Grpc.Filter
- {
- Must = { new Condition
- {
- Field = new FieldCondition
- {
- Key = "categorie",
- Match = new Match { Text = collectionName }
- }
- }}
- };
- }
-
-
- var rep = await _client.SearchAsync(collectionName, queryVector, filter, null, (ulong)topK);
- var filteredChunks = rep
- .Where(r => r.Score >= seuilScore)
- .ToArray();
-
- //
- var resultList = filteredChunks.Select(r => new SearchResult
- {
- Text = r.Payload["content"].StringValue,
- Nom_Fichier = r.Payload["nom_fichier"].StringValue
- }).ToList();
- return resultList;
- //return filteredChunks;
-
- }
- #endregion
-
- #region Méthodes privées
- private async Task EnsureCollectionAsync(Domain domain)
- {
- var collectionName = domain.ToCollectionName();
- var existing = await _client.ListCollectionsAsync();
-
- if (!existing.Contains(collectionName))
- {
- await _client.CreateCollectionAsync(collectionName, new VectorParams
- {
- Size = (ulong)EmbeddingDim,
- Distance = DefaultDistance
- });
- }
- }
-
- private async Task DeleteCollectionAsync()
- {
- var collections = await _client.ListCollectionsAsync();
- foreach (var col in collections)
- {
- await DeleteCollectionAsync(col);
- }
- }
-
- private async Task DeleteCollectionAsync(Domain domain)
- {
- var collectionName = domain.ToCollectionName();
- await DeleteCollectionAsync(collectionName);
- }
-
- private async Task DeleteCollectionAsync(string collectionName)
- {
- await _client.DeleteCollectionAsync(collectionName);
- }
- #endregion
- }
- #endregion
|