BotSharp/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Vector.cs

364 lines
12 KiB
C#
Raw Normal View History

2024-09-10 19:02:25 +00:00
using BotSharp.Abstraction.Files;
2024-09-12 22:48:47 +00:00
using BotSharp.Abstraction.VectorStorage.Enums;
2025-08-15 04:40:15 +00:00
using Microsoft.AspNetCore.Http.HttpResults;
2024-09-10 19:02:25 +00:00
2024-09-09 15:00:46 +00:00
namespace BotSharp.Plugin.KnowledgeBase.Services;
public partial class KnowledgeService
{
#region Collection
2024-09-24 20:34:47 +00:00
public async Task<bool> ExistVectorCollection(string collectionName)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var vectorDb = GetVectorDb();
var exist = await vectorDb.DoesCollectionExist(collectionName);
if (exist) return true;
var configs = db.GetKnowledgeCollectionConfigs(new VectorCollectionConfigFilter
{
CollectionNames = [collectionName],
VectorStroageProviders = [_settings.VectorDb.Provider]
});
return !configs.IsNullOrEmpty();
}
2025-08-15 03:33:22 +00:00
public async Task<bool> CreateVectorCollection(string collectionName, string collectionType, VectorCollectionCreateOptions options)
2024-09-09 15:00:46 +00:00
{
try
{
if (string.IsNullOrWhiteSpace(collectionName))
{
return false;
}
2025-08-05 16:09:15 +00:00
var db = _services.GetRequiredService<IBotSharpRepository>();
var created = db.AddKnowledgeCollectionConfigs(new List<VectorCollectionConfig>
2024-09-09 19:16:09 +00:00
{
2025-08-05 16:09:15 +00:00
new VectorCollectionConfig
2024-09-09 19:16:09 +00:00
{
2025-08-05 16:09:15 +00:00
Name = collectionName,
Type = collectionType,
VectorStore = new VectorStoreConfig
{
Provider = _settings.VectorDb.Provider
},
TextEmbedding = new KnowledgeEmbeddingConfig
2024-09-09 19:16:09 +00:00
{
2025-08-15 03:33:22 +00:00
Provider = options.Provider,
Model = options.Model,
Dimension = options.Dimension
2024-09-09 19:16:09 +00:00
}
2025-08-05 16:09:15 +00:00
}
});
if (created)
{
var vectorDb = GetVectorDb();
2025-08-15 03:33:22 +00:00
created = await vectorDb.CreateCollection(collectionName, options);
2024-09-09 19:16:09 +00:00
}
return created;
2024-09-09 15:00:46 +00:00
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when creating a vector collection ({collectionName}).");
2024-09-09 15:00:46 +00:00
return false;
}
}
2024-12-30 03:49:38 +00:00
public async Task<IEnumerable<VectorCollectionConfig>> GetVectorCollections(string? type = null)
2024-09-09 15:00:46 +00:00
{
try
{
2024-09-09 19:16:09 +00:00
var db = _services.GetRequiredService<IBotSharpRepository>();
2024-12-30 03:49:38 +00:00
var configs = db.GetKnowledgeCollectionConfigs(new VectorCollectionConfigFilter
2024-09-09 19:16:09 +00:00
{
2024-12-30 03:49:38 +00:00
CollectionTypes = !string.IsNullOrEmpty(type) ? [type] : null,
VectorStroageProviders = [_settings.VectorDb.Provider]
}).ToList();
2024-09-09 19:16:09 +00:00
var vectorDb = GetVectorDb();
2024-12-30 03:49:38 +00:00
var dbCollections = await vectorDb.GetCollections();
return configs.Where(x => dbCollections.Contains(x.Name));
2024-09-09 15:00:46 +00:00
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when getting vector db collections.");
2025-08-07 04:14:39 +00:00
return [];
2024-09-09 15:00:46 +00:00
}
}
2025-04-30 16:55:25 +00:00
public async Task<VectorCollectionDetails?> GetVectorCollectionDetails(string collectionName)
{
try
{
if (string.IsNullOrWhiteSpace(collectionName)) return null;
var db = _services.GetRequiredService<IBotSharpRepository>();
var configs = db.GetKnowledgeCollectionConfigs(new VectorCollectionConfigFilter
{
CollectionNames = [collectionName]
}).ToList();
var vectorDb = GetVectorDb();
var details = await vectorDb.GetCollectionDetails(collectionName);
if (details != null)
{
details.BasicInfo = configs.FirstOrDefault();
}
return details;
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when getting vector db collection details.");
2025-04-30 16:55:25 +00:00
return null;
}
}
2024-09-09 15:00:46 +00:00
public async Task<bool> DeleteVectorCollection(string collectionName)
{
try
{
if (string.IsNullOrWhiteSpace(collectionName))
{
return false;
}
2024-09-09 19:16:09 +00:00
var vectorDb = GetVectorDb();
var deleted = await vectorDb.DeleteCollection(collectionName);
if (deleted)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
2024-09-10 19:02:25 +00:00
var fileStorage = _services.GetRequiredService<IFileStorageService>();
var vectorStoreProvider = _settings.VectorDb.Provider;
2024-09-09 19:16:09 +00:00
db.DeleteKnowledgeCollectionConfig(collectionName);
2024-09-17 17:50:39 +00:00
fileStorage.DeleteKnowledgeFile(collectionName, vectorStoreProvider);
db.DeleteKnolwedgeBaseFileMeta(collectionName, vectorStoreProvider);
2024-09-09 19:16:09 +00:00
}
return deleted;
2024-09-09 15:00:46 +00:00
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when deleting collection ({collectionName}).");
2024-09-09 15:00:46 +00:00
return false;
}
}
#endregion
#region Collection data
public async Task<bool> CreateVectorCollectionData(string collectionName, VectorCreateModel create)
{
try
{
if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(create.Text))
{
return false;
}
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(create.Text);
var db = GetVectorDb();
var guid = Guid.NewGuid();
2024-09-12 22:48:47 +00:00
var payload = create.Payload ?? new();
2025-08-08 20:27:38 +00:00
if (!payload.TryGetValue(KnowledgePayloadName.DataSource, out _))
{
2025-08-15 04:40:15 +00:00
var dataSource = !string.IsNullOrWhiteSpace(create.DataSource) ? create.DataSource : VectorDataSource.Api;
payload[KnowledgePayloadName.DataSource] = VectorPayloadValue.BuildStringValue(dataSource);
2025-08-08 20:27:38 +00:00
}
2024-09-12 22:48:47 +00:00
return await db.Upsert(collectionName, guid, vector, create.Text, payload);
2024-09-09 15:00:46 +00:00
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when creating vector collection data.");
2024-09-09 15:00:46 +00:00
return false;
}
}
public async Task<bool> UpdateVectorCollectionData(string collectionName, VectorUpdateModel update)
{
try
{
2025-04-30 16:55:25 +00:00
if (string.IsNullOrWhiteSpace(collectionName)
|| string.IsNullOrWhiteSpace(update.Text)
|| !Guid.TryParse(update.Id, out var guid))
2024-09-09 15:00:46 +00:00
{
return false;
}
var db = GetVectorDb();
2025-04-30 16:55:25 +00:00
var found = await db.GetCollectionData(collectionName, [guid]);
2024-09-09 15:00:46 +00:00
if (found.IsNullOrEmpty())
{
return false;
}
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(update.Text);
2024-09-12 22:48:47 +00:00
var payload = update.Payload ?? new();
2025-08-08 20:27:38 +00:00
if (!payload.TryGetValue(KnowledgePayloadName.DataSource, out _))
{
2025-08-15 04:40:15 +00:00
var dataSource = !string.IsNullOrWhiteSpace(update.DataSource) ? update.DataSource : VectorDataSource.Api;
payload[KnowledgePayloadName.DataSource] = VectorPayloadValue.BuildStringValue(dataSource);
2025-08-08 20:27:38 +00:00
}
2024-09-12 22:48:47 +00:00
2024-09-22 20:05:26 +00:00
return await db.Upsert(collectionName, guid, vector, update.Text, payload);
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when updating vector collection data.");
2024-09-22 20:05:26 +00:00
return false;
}
}
public async Task<bool> UpsertVectorCollectionData(string collectionName, VectorUpdateModel update)
{
try
{
2025-04-30 16:55:25 +00:00
if (string.IsNullOrWhiteSpace(collectionName)
|| string.IsNullOrWhiteSpace(update.Text)
|| !Guid.TryParse(update.Id, out var guid))
2024-09-22 20:05:26 +00:00
{
return false;
}
var db = GetVectorDb();
2025-08-06 22:40:47 +00:00
var found = await db.GetCollectionData(collectionName, [guid], options: new() { WithVector = true, WithPayload = true });
2024-09-22 20:05:26 +00:00
if (!found.IsNullOrEmpty())
{
2025-04-30 16:55:25 +00:00
if (found.First().Data[KnowledgePayloadName.Text].ToString() == update.Text)
2024-09-22 20:05:26 +00:00
{
// Only update payload
return await db.Upsert(collectionName, guid, found.First().Vector, update.Text, update.Payload);
}
}
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(update.Text);
var payload = update.Payload ?? new();
2025-08-15 03:33:22 +00:00
if (!payload.TryGetValue(KnowledgePayloadName.DataSource, out _))
{
2025-08-15 04:40:15 +00:00
var dataSource = !string.IsNullOrWhiteSpace(update.DataSource) ? update.DataSource : VectorDataSource.Api;
payload[KnowledgePayloadName.DataSource] = VectorPayloadValue.BuildStringValue(dataSource);
2025-08-15 03:33:22 +00:00
}
2024-09-22 20:05:26 +00:00
2024-09-12 22:48:47 +00:00
return await db.Upsert(collectionName, guid, vector, update.Text, payload);
2024-09-09 15:00:46 +00:00
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when updating vector collection data.");
2024-09-09 15:00:46 +00:00
return false;
}
}
public async Task<bool> DeleteVectorCollectionData(string collectionName, string id)
{
try
{
if (!Guid.TryParse(id, out var guid))
{
return false;
}
var db = GetVectorDb();
2025-08-15 03:33:22 +00:00
return await db.DeleteCollectionData(collectionName, [guid]);
2024-09-09 15:00:46 +00:00
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when deleting vector collection data ({collectionName}-{id}).");
2024-09-09 15:00:46 +00:00
return false;
}
}
2024-09-18 21:17:54 +00:00
public async Task<bool> DeleteVectorCollectionAllData(string collectionName)
{
try
{
var db = GetVectorDb();
return await db.DeleteCollectionAllData(collectionName);
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when deleting vector collection data ({collectionName}).");
2024-09-18 21:17:54 +00:00
return false;
}
}
2024-09-09 15:00:46 +00:00
public async Task<StringIdPagedItems<VectorSearchResult>> GetPagedVectorCollectionData(string collectionName, VectorFilter filter)
{
try
{
var db = GetVectorDb();
var pagedResult = await db.GetPagedCollectionData(collectionName, filter);
return new StringIdPagedItems<VectorSearchResult>
{
Count = pagedResult.Count,
Items = pagedResult.Items.Select(x => VectorSearchResult.CopyFrom(x)),
NextId = pagedResult.NextId,
};
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when getting vector knowledge collection data ({collectionName}).");
2024-09-09 15:00:46 +00:00
return new StringIdPagedItems<VectorSearchResult>();
}
}
2025-08-07 04:14:39 +00:00
public async Task<IEnumerable<VectorCollectionData>> GetVectorCollectionData(string collectionName, IEnumerable<string> ids, VectorQueryOptions? options = null)
{
try
{
if (string.IsNullOrWhiteSpace(collectionName) || ids.IsNullOrEmpty())
{
return [];
}
var pointIds = ids.Select(x => new { Id = x, IsValid = Guid.TryParse(x, out var guid), ParseResult = guid })
.Where(x => x.IsValid)
.Select(x => x.ParseResult)
.ToList();
var db = GetVectorDb();
var points = await db.GetCollectionData(collectionName, pointIds, options);
return points;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error when querying vector collection {collectionName} points.");
return [];
}
}
2024-09-09 15:00:46 +00:00
public async Task<IEnumerable<VectorSearchResult>> SearchVectorKnowledge(string query, string collectionName, VectorSearchOptions options)
{
try
{
var textEmbedding = GetTextEmbedding(collectionName);
var vector = await textEmbedding.GetVectorAsync(query);
// Vector search
var db = GetVectorDb();
2025-08-05 20:46:07 +00:00
var found = await db.Search(collectionName, vector, options);
2024-09-09 15:00:46 +00:00
var results = found.Select(x => VectorSearchResult.CopyFrom(x)).ToList();
return results;
}
catch (Exception ex)
{
2025-08-06 22:40:47 +00:00
_logger.LogError(ex, $"Error when searching vector knowledge ({collectionName}).");
2025-08-07 04:14:39 +00:00
return [];
2024-09-09 15:00:46 +00:00
}
}
#endregion
}