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;
|
2024-09-18 21:17:54 +00:00
|
|
|
using System;
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-09 19:16:09 +00:00
|
|
|
public async Task<bool> CreateVectorCollection(string collectionName, string collectionType, int dimension, string provider, string model)
|
2024-09-09 15:00:46 +00:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(collectionName))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-09 19:16:09 +00:00
|
|
|
var vectorDb = GetVectorDb();
|
|
|
|
|
var created = await vectorDb.CreateCollection(collectionName, dimension);
|
|
|
|
|
if (created)
|
|
|
|
|
{
|
|
|
|
|
var db = _services.GetRequiredService<IBotSharpRepository>();
|
|
|
|
|
var userId = await GetUserId();
|
|
|
|
|
|
|
|
|
|
db.AddKnowledgeCollectionConfigs(new List<VectorCollectionConfig>
|
|
|
|
|
{
|
|
|
|
|
new VectorCollectionConfig
|
|
|
|
|
{
|
|
|
|
|
Name = collectionName,
|
|
|
|
|
Type = collectionType,
|
2024-09-12 16:51:23 +00:00
|
|
|
VectorStore = new VectorStoreConfig
|
2024-09-10 19:02:25 +00:00
|
|
|
{
|
|
|
|
|
Provider = _settings.VectorDb.Provider
|
|
|
|
|
},
|
2024-09-09 19:16:09 +00:00
|
|
|
TextEmbedding = new KnowledgeEmbeddingConfig
|
|
|
|
|
{
|
|
|
|
|
Provider = provider,
|
|
|
|
|
Model = model,
|
|
|
|
|
Dimension = dimension
|
2024-09-10 19:02:25 +00:00
|
|
|
}
|
2024-09-09 19:16:09 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return created;
|
2024-09-09 15:00:46 +00:00
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning($"Error when creating a vector collection ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-09 19:16:09 +00:00
|
|
|
public async Task<IEnumerable<string>> GetVectorCollections(string type)
|
2024-09-09 15:00:46 +00:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2024-09-09 19:16:09 +00:00
|
|
|
var db = _services.GetRequiredService<IBotSharpRepository>();
|
|
|
|
|
var collectionNames = db.GetKnowledgeCollectionConfigs(new VectorCollectionConfigFilter
|
|
|
|
|
{
|
2024-09-10 19:02:25 +00:00
|
|
|
CollectionTypes = new[] { type },
|
|
|
|
|
VectorStroageProviders = new[] { _settings.VectorDb.Provider }
|
2024-09-09 19:16:09 +00:00
|
|
|
}).Select(x => x.Name).ToList();
|
|
|
|
|
|
|
|
|
|
var vectorDb = GetVectorDb();
|
|
|
|
|
var vectorCollections = await vectorDb.GetCollections();
|
|
|
|
|
return vectorCollections.Where(x => collectionNames.Contains(x));
|
2024-09-09 15:00:46 +00:00
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning($"Error when getting vector db collections. {ex.Message}\r\n{ex.InnerException}");
|
|
|
|
|
return Enumerable.Empty<string>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning($"Error when deleting collection ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
|
|
|
|
|
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();
|
|
|
|
|
payload[KnowledgePayloadName.DataSource] = !string.IsNullOrWhiteSpace(create.DataSource) ? create.DataSource : VectorDataSource.Api;
|
|
|
|
|
|
|
|
|
|
return await db.Upsert(collectionName, guid, vector, create.Text, payload);
|
2024-09-09 15:00:46 +00:00
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning($"Error when creating vector collection data. {ex.Message}\r\n{ex.InnerException}");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> UpdateVectorCollectionData(string collectionName, VectorUpdateModel update)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(update.Text) || !Guid.TryParse(update.Id, out var guid))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var db = GetVectorDb();
|
|
|
|
|
var found = await db.GetCollectionData(collectionName, new List<Guid> { guid });
|
|
|
|
|
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();
|
|
|
|
|
payload[KnowledgePayloadName.DataSource] = !string.IsNullOrWhiteSpace(update.DataSource) ? update.DataSource : VectorDataSource.Api;
|
|
|
|
|
|
2024-09-22 20:05:26 +00:00
|
|
|
return await db.Upsert(collectionName, guid, vector, update.Text, payload);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning($"Error when updating vector collection data. {ex.Message}\r\n{ex.InnerException}");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> UpsertVectorCollectionData(string collectionName, VectorUpdateModel update)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(update.Text) || !Guid.TryParse(update.Id, out var guid))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var db = GetVectorDb();
|
|
|
|
|
var found = await db.GetCollectionData(collectionName, new List<Guid> { guid },
|
|
|
|
|
withVector: true,
|
|
|
|
|
withPayload: true);
|
|
|
|
|
if (!found.IsNullOrEmpty())
|
|
|
|
|
{
|
2024-09-29 01:00:35 +00:00
|
|
|
if (found.First().Data["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();
|
|
|
|
|
payload[KnowledgePayloadName.DataSource] = !string.IsNullOrWhiteSpace(update.DataSource) ? update.DataSource : VectorDataSource.Api;
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning($"Error when updating vector collection data. {ex.Message}\r\n{ex.InnerException}");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> DeleteVectorCollectionData(string collectionName, string id)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (!Guid.TryParse(id, out var guid))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var db = GetVectorDb();
|
2024-09-10 19:02:25 +00:00
|
|
|
return await db.DeleteCollectionData(collectionName, new List<Guid> { guid });
|
2024-09-09 15:00:46 +00:00
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning($"Error when deleting vector collection data ({collectionName}-{id}). {ex.Message}\r\n{ex.InnerException}");
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning($"Error when deleting vector collection data ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning($"Error when getting vector knowledge collection data ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
|
|
|
|
|
return new StringIdPagedItems<VectorSearchResult>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
var found = await db.Search(collectionName, vector, options.Fields, limit: options.Limit ?? 5, confidence: options.Confidence ?? 0.5f, withVector: options.WithVector);
|
|
|
|
|
|
|
|
|
|
var results = found.Select(x => VectorSearchResult.CopyFrom(x)).ToList();
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning($"Error when searching vector knowledge ({collectionName}). {ex.Message}\r\n{ex.InnerException}");
|
2024-09-10 19:02:25 +00:00
|
|
|
return Enumerable.Empty<VectorSearchResult>();
|
2024-09-09 15:00:46 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|