Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,14 @@
"displayName": "Add aspire?",
"type": "parameter",
"datatype": "bool",
"defaultValue": "false",
"defaultValue": "true",
"description": "Adds aspire"
},
"notification": {
"displayName": "Add push notification?",
"type": "parameter",
"datatype": "bool",
"defaultValue": "false",
"defaultValue": "true",
"description": "Adds push notification."
},
"sample": {
Expand Down Expand Up @@ -484,7 +484,8 @@
"src/Server/Boilerplate.Server.Api/Models/Todo/**",
"src/Server/Boilerplate.Server.Api/Data/Configurations/Todo/**",
"src/Shared/Controllers/Todo/**",
"src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.*"
"src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.*",
"src/Client/Boilerplate.Client.Core/images/backgrounds/empty-todo-list-bg.svg"
]
},
{
Expand Down
8 changes: 4 additions & 4 deletions src/Templates/Boilerplate/Bit.Boilerplate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ This project gets generated by bit-bp template v-9.11.3 using the following comm
<!--#if (captcha == "reCaptcha")-->
--captcha reCaptcha
<!--#endif-->
<!--#if (aspire == true)-->
--aspire
<!--#if (aspire == false)-->
--aspire false
<!--#endif-->
<!--#if (notification == true)-->
--notification
<!--#if (notification == false)-->
--notification false
<!--#endif-->
<!--#if (sample == true)-->
--sample
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ void AddDbContext(DbContextOptionsBuilder options)
}
options.UseSqlite(connectionStringBuilder.ConnectionString, dbOptions =>
{

// dbOptions.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
});
//#endif
//#if (IsInsideProjectTemplate == true)
Expand All @@ -260,18 +260,20 @@ void AddDbContext(DbContextOptionsBuilder options)
{
dbOptions.UseVectorSearch();
}
// dbOptions.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
});
//#elif (database == "PostgreSQL")
var dataSourceBuilder = new Npgsql.NpgsqlDataSourceBuilder(configuration.GetConnectionString("PostgreSQLConnectionString"));
dataSourceBuilder.EnableDynamicJson();
options.UseNpgsql(dataSourceBuilder.Build(), dbOptions =>
{
dbOptions.UseVector();
// dbOptions.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
});
//#elif (database == "MySql")
options.UseMySql(configuration.GetConnectionString("MySqlConnectionString"), ServerVersion.AutoDetect(configuration.GetConnectionString("MySqlConnectionString")), dbOptions =>
{

// dbOptions.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
});
//#elif (database == "Other")
throw new NotImplementedException("Install and configure any database supported by ef core (https://learn.microsoft.com/en-us/ef/core/providers)");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@
namespace Boilerplate.Server.Api.Services;

/// <summary>
/// This class stores vectorized products and provides methods to query/manage them.
/// Approaches to implement text search:
/// 1- Simple string matching (e.g., `Contains` method).
/// 2- Full-text search using database capabilities (e.g., PostgreSQL's full-text search).
/// 3- Vector-based search using embeddings (e.g., using OpenAI's embeddings).
/// This service implements vector-based search using embeddings that has the following advantages:
/// - More accurate search results based on semantic meaning rather than just similarity matching.
/// - Multi-language support, as embeddings can capture the meaning of words across different languages.
/// And has the following disadvantages:
/// - Requires additional processing to generate embeddings for the text.
/// - Require more storage space for embeddings compared to simple text search.
/// The simple full-text search would be enough for product search case, but we have implemented the vector-based search to demonstrate how to use embeddings in the project.
/// </summary>
public partial class ProductEmbeddingService
{
Expand All @@ -20,7 +30,7 @@ public partial class ProductEmbeddingService
public async Task<IQueryable<Product>> GetProductsBySearchQuery(string searchQuery, CancellationToken cancellationToken)
{
//#if (database != "PostgreSQL" && database != "SqlServer")
// The RAG has been implemented for PostgreSQL only. Check out https://github.com/bitfoundation/bitplatform/blob/develop/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ProductEmbeddingService.cs
// The RAG has been implemented for PostgreSQL / SQL Server only. Check out https://github.com/bitfoundation/bitplatform/blob/develop/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ProductEmbeddingService.cs
return dbContext.Products.Where(p => p.Name!.Contains(searchQuery) || p.Category!.Name!.Contains(searchQuery));
//#else
var embeddedUserQuery = await EmbedText(searchQuery, cancellationToken);
Expand Down Expand Up @@ -60,7 +70,11 @@ public async Task Embed(Product product, CancellationToken cancellationToken)
await dbContext.Entry(product).Reference(p => p.Category).LoadAsync(cancellationToken);

// TODO: Needs to be improved.
var embedding = await EmbedText($"Name: {product.Name}, Manufacture: {product.Category!.Name}, Description: {product.DescriptionText} {product.PrimaryImageAltText}, Price: {product.Price}", cancellationToken);
var embedding = await EmbedText($@"
Name: **{product.Name}**
Manufacture: **{product.Category!.Name}**
Description: {product.DescriptionText}
Appearance: {product.PrimaryImageAltText}", cancellationToken);

if (embedding.HasValue)
{
Expand Down Expand Up @@ -89,6 +103,14 @@ public async Task Embed(Product product, CancellationToken cancellationToken)
var embeddingGenerator = serviceProvider.GetService<IEmbeddingGenerator<string, Embedding<float>>>();
if (embeddingGenerator is null)
return env.IsDevelopment() ? null : throw new InvalidOperationException("Embedding generator is not registered.");

input = $@"
Name: **{input}**
Manufacture: **{input}**
Description: {input}
Appearance: {input}";


var embedding = await embeddingGenerator.GenerateVectorAsync(input, options: new() { }, cancellationToken);
return embedding.ToArray();
//#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ async Task HandleIncomingMessage(string incomingMessage, CancellationToken messa
.LogError("Chat reported issue: User email: {emailAddress}, Conversation history: {conversationHistory}", emailAddress, conversationHistory);
}, name: "SaveUserEmailAndConversationHistory", description: "Saves the user's email address and the conversation history for future reference. Use this tool when the user provides their email address during the conversation. Parameters: emailAddress (string), conversationHistory (string)"),
//#if (module == "Sales")
AIFunctionFactory.Create(async ([Description("Concise summary of these user requirements in English Language")] string userNeeds, [Description("Car manufacturer's English name (Optional)")] string? manufacturer) =>
AIFunctionFactory.Create(async ([Description("Concise summary of these user requirements")] string userNeeds,
[Description("Car manufacturer's name (Optional)")] string? manufacturer,
[Description("Car price below this value (Optional)")] decimal? maxPrice,
[Description("Car price above this value (Optional)")] decimal? minPrice) =>
{
if (messageSpecificCancellationToken.IsCancellationRequested)
return null;
Expand All @@ -104,8 +107,10 @@ async Task HandleIncomingMessage(string incomingMessage, CancellationToken messa
var productEmbeddingService = scope.ServiceProvider.GetRequiredService<ProductEmbeddingService>();
var searchQuery = string.IsNullOrWhiteSpace(manufacturer)
? userNeeds
: $"{userNeeds}, Manufacturer: {manufacturer}";
: $"**{manufacturer}** {userNeeds}";
var recommendedProducts = await (await productEmbeddingService.GetProductsBySearchQuery(searchQuery, messageSpecificCancellationToken))
.WhereIf(maxPrice.HasValue, p => p.Price <= maxPrice!.Value)
.WhereIf(minPrice.HasValue, p => p.Price >= minPrice!.Value)
.Take(10)
.Project()
.Select(p => new
Expand Down
Loading