eShopLite is a reference .NET application that implements an eCommerce site with advanced search features, including both keyword and semantic search capabilities. This version utilizes Chroma DB, an open-source database designed for AI applications, to enhance semantic search functionality.
- Keyword Search: Traditional search based on exact keyword matches.
- Semantic Search: Leverages Chroma DB to understand the context and intent behind user queries, providing more relevant search results.
- GitHub Codespaces Integration: Easily deploy and run the solution entirely in the browser using GitHub Codespaces.
The solution is in the ./src
folder, the main solution is eShopLite-ChromaDB.sln.
Once you've opened the project in Codespaces, or locally, you can deploy it to Azure.
From a Terminal window, open the folder with the clone of this repo and run the following commands.
-
Login to Azure:
azd auth login
-
Provision and deploy all the resources:
azd up
It will prompt you to provide an
azd
environment name (like "eShopLite"), select a subscription from your Azure account, and select a location where OpenAI the models gpt-4o-mini and ADA-002 are available (like "eastus2"). -
When
azd
has finished deploying, you'll see the list of resources created in Azure and a set of URIs in the command output. -
Visit the store URI, and you should see the eShop Lite app! 🎉
-
This is an example of the command output:
-
Create a new Codespace using the
Code
button at the top of the repository. -
The Codespace creation process can take a couple of minutes.
-
Once the Codespace is loaded, it should have all the necessary requirements to deploy the solution.
- .NET 9
- Git
- Azure Developer CLI (azd)
- Visual Studio Code or Visual Studio
- If using Visual Studio Code, install the C# Dev Kit
- .NET Aspire workload: Installed with the Visual Studio installer or the .NET CLI workload.
- An OCI compliant container runtime, such as:
- Docker Desktop or Podman.
-
The AspireHost project will be in charge to provision and run Chroma DB (locally for dev environments):
Instead of using the the Chroma Docker image to start a local instance of Chroma DB.
docker run -p 8000:8000 chromadb/chroma
Aspire Host will create a persistent container for Chroma DB, which will be used to store the embeddings and metadata for the products.
var chromaDB = builder.AddContainer("chroma", "chromadb/chroma") .WithHttpEndpoint(port: 8000, targetPort: 8000, name: "chromaendpoint") .WithLifetime(ContainerLifetime.Persistent); var endpoint = chromaDB.GetEndpoint("chromaendpoint"); var products = builder.AddProject<Projects.Products>("products") .WithReference(endpoint) .WithReference(sqldb) .WaitFor(sqldb);
-
Connect to Chroma DB in Your Application:
The
ChromaDB.Client
NuGet package will allow the connections to the ChromaDB in yourProducts
project.using ChromaDB.Client; var chromaDbService = _config.GetSection("services:chroma:chromaendpoint:0"); var chromaDbUri = chromaDbService.Value; var configOptions = new ChromaConfigurationOptions(uri: $"{chromaDbUri}/api/v1/"); _httpChromaClient = new HttpClient(); var client = new ChromaClient(configOptions, _httpChromaClient);
-
Create a Collection: Create a collection in Chroma DB to store your product data.
var collection = await client.GetOrCreateCollection("products"); _collectionClient = new ChromaCollectionClient(collection, configOptions, _httpChromaClient);
-
Add Data to the Collection: Add your product data, including embeddings and metadata, to the collection.
var productIds = new List<string>(); var productDescriptionEmbeddings = new List<ReadOnlyMemory<float>>(); var productMetadata = new List<Dictionary<string, object>>(); // iterate over the products and add them to the memory foreach (var product in products) { try { _logger.LogInformation("Adding product to memory: {Product}", product.Name); var productInfo = $"[{product.Name}] is a product that costs [{product.Price}] and is described as [{product.Description}]"; var result = await _embeddingClient.GenerateEmbeddingAsync(productInfo); productIds.Add(product.Id.ToString()); productDescriptionEmbeddings.Add(result.Value.ToFloats()); _logger.LogInformation($"Product added to collections: {product.Name}"); } catch (Exception exc) { _logger.LogError(exc, "Error adding product to memory"); } } // add the products to the memory await _collectionClient.Upsert(productIds, productDescriptionEmbeddings, productMetadata);
-
Search products: search the products using the Chroma DB client.
var resultGenEmbeddings = await _embeddingClient.GenerateEmbeddingAsync(search); var embeddingsSearchQuery = resultGenEmbeddings.Value.ToFloats(); var searchOptions = new VectorSearchOptions { Top = 1, VectorPropertyName = "Vector" }; // search the vector database for the most similar product var queryResult = await _collectionClient.Query( queryEmbeddings: embeddingsSearchQuery, nResults: 2, include: ChromaQueryInclude.Metadatas | ChromaQueryInclude.Distances); var sbFoundProducts = new StringBuilder(); int productPosition = 1; foreach (var result in queryResult) { if (result.Distance > 0.3) { // product found, magic happens here } }
Follow these steps to run the project, locally or in CodeSpaces:
-
Navigate to the Aspire Host folder project using the command:
cd ./src/eShopAppHost/
-
If you are running the project in Codespaces, you need to run this command:
dotnet dev-certs https --trust
-
By default the AppHost project creates the necessary resources on Azure. Check the .NET Aspire Azure Resources creation section to learn how to configure the project to create Azure resources.
-
Run the project:
dotnet run
Check the Video Resources for a step-by-step on how to run this project.
Note: Working with .NET Aspire in GitHub Codespaces is not fully supported yet. As a developer you need to perform a lot of manual steps to access the .NET Aspire portal, like changing ports to public, copy the access token and more. The .NET Aspire version 9.1 will improve the whole developer experience. We will update these steps when the version 9.1 is released.
In order to use existing models: gpt-4o-mini and text-embedding-ada-002, you need to define the specific connection string in the Products
project.
-
Configure the Application: Add user secrets in Products project to connect to Azure OpenAI services.
dotnet user-secrets init dotnet user-secrets set "openai" "Endpoint=https://<endpoint>.openai.azure.com/;Key=<ApiKey>;"
For Azure OpenAI Services, pricing varies per region and usage, so it isn't possible to predict exact costs for your usage. The majority of the Azure resources used in this infrastructure are on usage-based pricing tiers. However, Azure Container Registry has a fixed cost per registry per day.
You can try the Azure pricing calculator for the resources:
- Azure OpenAI Service: S0 tier, gpt-4o-mini and text-embedding-ada-002 models. Pricing is based on token count. Pricing
- Azure Container App: Consumption tier with 0.5 CPU, 1GiB memory/storage. Pricing is based on resource allocation, and each month allows for a certain amount of free usage. Pricing
- Azure Container Registry: Basic tier. Pricing
- Log analytics: Pay-as-you-go tier. Costs based on data ingested. Pricing
- Azure Application Insights pricing is based on a Pay-As-You-Go model. Pricing.
azd down
.
Samples in this templates uses Azure OpenAI Services with ApiKey and Managed Identity for authenticating to the Azure OpenAI service.
The Main Sample uses Managed Identity](https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/overview) for authenticating to the Azure OpenAI service.
Additionally, we have added a GitHub Action that scans the infrastructure-as-code files and generates a report containing any detected issues. To ensure continued best practices in your own repository, we recommend that anyone creating solutions based on our templates ensure that the Github secret scanning setting is enabled.
You may want to consider additional security measures, such as:
- Protecting the Azure Container Apps instance with a firewall and/or Virtual Network.