Skip to content

Commit

Permalink
feat: migrate database to Azure Cosmos DB for NoSQL (#94)
Browse files Browse the repository at this point in the history
* feat: migrate infra to cosmosdb nosql

* feat: migrate code to CosmosDB NoSQL

* chore: remove ai search template

* docs: update docs

* fix: remove key vault references
  • Loading branch information
sinedied authored Aug 8, 2024
1 parent 48b1be9 commit 0cf132c
Show file tree
Hide file tree
Showing 17 changed files with 438 additions and 138 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

</div>

This sample shows how to build a serverless AI chat experience with Retrieval-Augmented Generation using [LangChain.js](https://js.langchain.com/) and Azure. The application is hosted on [Azure Static Web Apps](https://learn.microsoft.com/azure/static-web-apps/overview) and [Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-overview?pivots=programming-language-javascript), with [Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-azure-search) as the vector database. You can use it as a starting point for building more complex AI applications.
This sample shows how to build a serverless AI chat experience with Retrieval-Augmented Generation using [LangChain.js](https://js.langchain.com/) and Azure. The application is hosted on [Azure Static Web Apps](https://learn.microsoft.com/azure/static-web-apps/overview) and [Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-overview?pivots=programming-language-javascript), with [Azure Cosmos DB for NoSQL](https://learn.microsoft.com/azure/cosmos-db/nosql/vector-) as the vector database. You can use it as a starting point for building more complex AI applications.

> [!TIP]
> You can test this application locally without any cost using [Ollama](https://ollama.com/). Follow the instructions in the [Local Development](#local-development) section to get started.
Expand All @@ -44,7 +44,7 @@ This application is made from multiple components:

- A serverless API built with [Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-overview?pivots=programming-language-javascript) and using [LangChain.js](https://js.langchain.com/) to ingest the documents and generate responses to the user chat queries. The code is located in the `packages/api` folder.

- A database to store the text extracted from the documents and the vectors generated by LangChain.js, using [Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-azure-search).
- A database to store the text extracted from the documents and the vectors generated by LangChain.js, using [Azure Cosmos DB for NoSQL](https://learn.microsoft.com/azure/cosmos-db/nosql/).

- A file storage to store the source documents, using [Azure Blob Storage](https://learn.microsoft.com/azure/storage/blobs/storage-blobs-introduction).

Expand All @@ -53,7 +53,7 @@ We use the [HTTP protocol for AI chat apps](https://aka.ms/chatprotocol) to comm
## Features

- **Serverless Architecture**: Utilizes Azure Functions and Azure Static Web Apps for a fully serverless deployment.
- **Retrieval-Augmented Generation (RAG)**: Combines the power of Azure AI Search and LangChain.js to provide relevant and accurate responses.
- **Retrieval-Augmented Generation (RAG)**: Combines the power of Azure Cosmos DB and LangChain.js to provide relevant and accurate responses.
- **Scalable and Cost-Effective**: Leverages Azure's serverless offerings to provide a scalable and cost-effective solution.
- **Local Development**: Supports local development using Ollama for testing without any cloud costs.

Expand Down Expand Up @@ -212,7 +212,7 @@ Here are some resources to learn more about the technologies used in this sample
- [LangChain.js documentation](https://js.langchain.com)
- [Generative AI For Beginners](https://github.com/microsoft/generative-ai-for-beginners)
- [Azure OpenAI Service](https://learn.microsoft.com/azure/ai-services/openai/overview)
- [Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-azure-search)
- [Azure Cosmos DB for NoSQL](https://learn.microsoft.com/azure/cosmos-db/nosql/)
- [Ask YouTube: LangChain.js + Azure Quickstart sample](https://github.com/Azure-Samples/langchainjs-quickstart-demo)
- [Chat + Enterprise data with Azure OpenAI and Azure AI Search](https://github.com/Azure-Samples/azure-search-openai-javascript)
- [Revolutionize your Enterprise Data with Chat: Next-gen Apps w/ Azure OpenAI and AI Search](https://aka.ms/entgptsearchblog)
Expand Down
4 changes: 2 additions & 2 deletions docs/cost.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
## Cost estimation

Pricing varies per region and usage, so it isn't possible to predict exact costs for your usage.
However, you can use the [Azure pricing calculator](https://azure.com/e/c504007c9f024699a37f5d947dbb1e79) for the resources below to get an estimate.
However, you can use the [Azure pricing calculator](https://azure.com/e/a586bf32fdfa4bb9b368a6b6543c4b50) for the resources below to get an estimate.

- Azure Functions: Consumption plan, Free for the first 1M executions. Pricing per execution and memory used. [Pricing](https://azure.microsoft.com/pricing/details/functions/)
- Azure Static Web Apps: Free tier, 100GB bandwidth. Pricing per GB served. [Pricing](https://azure.microsoft.com/pricing/details/app-service/static/)
- Azure OpenAI: Standard tier, GPT and Ada models. Pricing per 1K tokens used, and at least 1K tokens are used per question. [Pricing](https://azure.microsoft.com/pricing/details/cognitive-services/openai-service/)
- Azure AI Search: Basic tier, 1 replica. Pricing per hour. [Pricing](https://azure.microsoft.com/pricing/details/search/)
- Azure Cosmos DB: Serverless tier. Pricing per request unit (RU). [Pricing](https://azure.microsoft.com/pricing/details/cosmos-db/autoscale-provisioned/)
- Azure Blob Storage: Standard tier with LRS. Pricing per GB stored and data transfer. [Pricing](https://azure.microsoft.com/pricing/details/storage/blobs/)

⚠️ To avoid unnecessary costs, remember to take down your app if it's no longer in use,
Expand Down
4 changes: 2 additions & 2 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Retrieval-Augmented Generation (RAG) is a method used in artificial intelligence

At its core, RAG involves two main components:

- **Retriever**: Think "_like a search engine_", finding relevant information from a knowledgebase, usually a vector database. In this sample, we're using Azure AI Search as our vector database.
- **Retriever**: Think "_like a search engine_", finding relevant information from a knowledgebase, usually a vector database. In this sample, we're using Azure Cosmos DB for NoSQL as our vector database.

- **Generator**: Acts like a writer, taking the prompt and information retrieved to create a response. We're using here a Large Language Model (LLM) for this task.

Expand Down Expand Up @@ -114,7 +114,7 @@ The `azd up` command comes from the [Azure Developer CLI](https://learn.microsof

The `azd up` command uses the `azure.yaml` file combined with the infrastructure-as-code `.bicep` files in the `infra/` folder. The `azure.yaml` file for this project declares several "hooks" for the prepackage step and postprovision steps. The `up` command first runs the `prepackage` hook which installs Node dependencies and builds the TypeScript files. It then packages all the code (both frontend and backend services) into a zip file which it will deploy later.

Next, it provisions the resources based on `main.bicep` and `main.parameters.json`. At that point, since there is no default value for the OpenAI resource location, it asks you to pick a location from a short list of available regions. Then it will send requests to Azure to provision all the required resources. With everything provisioned, it runs the `postprovision` hook to process the local data and add it to an Azure AI Search index.
Next, it provisions the resources based on `main.bicep` and `main.parameters.json`. At that point, since there is no default value for the OpenAI resource location, it asks you to pick a location from a short list of available regions. Then it will send requests to Azure to provision all the required resources. With everything provisioned, it runs the `postprovision` hook to process the local data and add it to an Azure Cosmos DB index.

Finally, it looks at `azure.yaml` to determine the Azure host (Functions and Static Web Apps, in this case) and uploads the zip to Azure. The `azd up` command is now complete, but it may take some time for the app to be fully available and working after the initial deploy.

Expand Down
6 changes: 3 additions & 3 deletions docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ description: Build your own serverless AI Chat with Retrieval-Augmented-Generati

<!-- Learn samples onboarding: https://review.learn.microsoft.com/en-us/help/contribute/samples/process/onboarding?branch=main -->
<!-- prettier-ignore -->
This sample shows how to build a serverless AI chat experience with Retrieval-Augmented Generation using [LangChain.js](https://js.langchain.com/) and Azure. The application is hosted on [Azure Static Web Apps](https://learn.microsoft.com/azure/static-web-apps/overview) and [Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-overview?pivots=programming-language-javascript), with [Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-azure-search) as the vector database. You can use it as a starting point for building more complex AI applications.
This sample shows how to build a serverless AI chat experience with Retrieval-Augmented Generation using [LangChain.js](https://js.langchain.com/) and Azure. The application is hosted on [Azure Static Web Apps](https://learn.microsoft.com/azure/static-web-apps/overview) and [Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-overview?pivots=programming-language-javascript), with [Azure Cosmos DB for NoSQL](https://learn.microsoft.com/azure/cosmos-db/nosql/vector-search) as the vector database. You can use it as a starting point for building more complex AI applications.

![Animation showing the chat app in action](./images/demo.gif)

Expand All @@ -35,7 +35,7 @@ This application is made from multiple components:

- A serverless API built with [Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-overview?pivots=programming-language-javascript) and using [LangChain.js](https://js.langchain.com/) to ingest the documents and generate responses to the user chat queries. The code is located in the `packages/api` folder.

- A database to store the text extracted from the documents and the vectors generated by LangChain.js, using [Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-azure-search).
- A database to store the text extracted from the documents and the vectors generated by LangChain.js, using [Azure Cosmos DB for NoSQL](https://learn.microsoft.com/azure/cosmos-db/nosql/).

- A file storage to store the source documents, using [Azure Blob Storage](https://learn.microsoft.com/azure/storage/blobs/storage-blobs-introduction).

Expand Down Expand Up @@ -106,7 +106,7 @@ Here are some resources to learn more about the technologies used in this sample
- [LangChain.js documentation](https://js.langchain.com)
- [Generative AI For Beginners](https://github.com/microsoft/generative-ai-for-beginners)
- [Azure OpenAI Service](https://learn.microsoft.com/azure/ai-services/openai/overview)
- [Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-azure-search)
- [Azure Cosmos DB for NoSQL](https://learn.microsoft.com/azure/cosmos-db/nosql/)
- [Ask YouTube: LangChain.js + Azure Quickstart sample](https://github.com/Azure-Samples/langchainjs-quickstart-demo)
- [Chat + Enterprise data with Azure OpenAI and Azure AI Search](https://github.com/Azure-Samples/azure-search-openai-javascript)
- [Revolutionize your Enterprise Data with Chat: Next-gen Apps w/ Azure OpenAI and AI Search](https://aka.ms/entgptsearchblog)
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/03-understanding-rag.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ RAG has significant implications in many fields:

- **Educational Content:** Educators can update their materials with the latest research and studies to ensure relevance and accuracy.

> **Note:** To learn more about the RAG architecture, please refer to the official documentation of the Azure AI Search Documentation service, which can be accessed [here](https://learn.microsoft.com/azure/search/retrieval-augmented-generation-overview).
> **Note:** To learn more about the RAG architecture, please refer to the official documentation of the Azure Cosmos DB Documentation service, which can be accessed [here](https://learn.microsoft.com/azure/cosmos-db/gen-ai/rag).
## Next Steps

Expand Down
40 changes: 40 additions & 0 deletions infra/core/database/cosmos/cosmos-account.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
metadata description = 'Creates an Azure Cosmos DB account.'
param name string
param location string = resourceGroup().location
param tags object = {}

@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ])
param kind string

param disableLocalAuth bool = false

resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' = {
name: name
kind: kind
location: location
tags: tags
properties: {
consistencyPolicy: { defaultConsistencyLevel: 'Session' }
locations: [
{
locationName: location
failoverPriority: 0
isZoneRedundant: false
}
]
databaseAccountOfferType: 'Standard'
enableAutomaticFailover: false
enableMultipleWriteLocations: false
apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {}
capabilities: [
{ name: 'EnableServerless' }
{ name: 'EnableNoSQLVectorSearch' }
]
minimalTlsVersion: 'Tls12'
disableLocalAuth: disableLocalAuth
}
}

output endpoint string = cosmos.properties.documentEndpoint
output id string = cosmos.id
output name string = cosmos.name
21 changes: 21 additions & 0 deletions infra/core/database/cosmos/sql/cosmos-sql-account.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
metadata description = 'Creates an Azure Cosmos DB for NoSQL account.'
param name string
param location string = resourceGroup().location
param tags object = {}

param disableLocalAuth bool = false

module cosmos '../../cosmos/cosmos-account.bicep' = {
name: 'cosmos-account'
params: {
name: name
location: location
tags: tags
kind: 'GlobalDocumentDB'
disableLocalAuth: disableLocalAuth
}
}

output endpoint string = cosmos.outputs.endpoint
output id string = cosmos.outputs.id
output name string = cosmos.outputs.name
73 changes: 73 additions & 0 deletions infra/core/database/cosmos/sql/cosmos-sql-db.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.'
param accountName string
param databaseName string
param location string = resourceGroup().location
param tags object = {}

param containers array = []
param principalIds array = []
param disableLocalAuth bool = false

module cosmos 'cosmos-sql-account.bicep' = {
name: 'cosmos-sql-account'
params: {
name: accountName
location: location
tags: tags
disableLocalAuth: disableLocalAuth
}
}

resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = {
name: '${accountName}/${databaseName}'
properties: {
resource: { id: databaseName }
}

resource list 'containers' = [for container in containers: {
name: container.name
properties: {
resource: {
id: container.id
partitionKey: { paths: [ container.partitionKey ] }
}
options: {}
}
}]

dependsOn: [
cosmos
]
}

module roleDefinition 'cosmos-sql-role-def.bicep' = {
name: 'cosmos-sql-role-definition'
params: {
accountName: accountName
}
dependsOn: [
cosmos
database
]
}

// We need batchSize(1) here because sql role assignments have to be done sequentially
@batchSize(1)
module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) {
name: 'cosmos-sql-user-role-${uniqueString(principalId)}'
params: {
accountName: accountName
roleDefinitionId: roleDefinition.outputs.id
principalId: principalId
}
dependsOn: [
cosmos
database
]
}]

output accountId string = cosmos.outputs.id
output accountName string = cosmos.outputs.name
output databaseName string = databaseName
output endpoint string = cosmos.outputs.endpoint
output roleDefinitionId string = roleDefinition.outputs.id
19 changes: 19 additions & 0 deletions infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.'
param accountName string

param roleDefinitionId string
param principalId string = ''

resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = {
parent: cosmos
name: guid(roleDefinitionId, principalId, cosmos.id)
properties: {
principalId: principalId
roleDefinitionId: roleDefinitionId
scope: cosmos.id
}
}

resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = {
name: accountName
}
30 changes: 30 additions & 0 deletions infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.'
param accountName string

resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = {
parent: cosmos
name: guid(cosmos.id, accountName, 'sql-role')
properties: {
assignableScopes: [
cosmos.id
]
permissions: [
{
dataActions: [
'Microsoft.DocumentDB/databaseAccounts/readMetadata'
'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*'
'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*'
]
notDataActions: []
}
]
roleName: 'Reader Writer'
type: 'CustomRole'
}
}

resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = {
name: accountName
}

output id string = roleDefinition.id
68 changes: 0 additions & 68 deletions infra/core/search/search-services.bicep

This file was deleted.

Loading

0 comments on commit 0cf132c

Please sign in to comment.