24 | @code {
25 | [Parameter] public string? Caption { get; set; }
26 | [Parameter] public string? Value { get; set; }
27 | [Parameter] public EventCallback OnClose { get; set; }
28 |
29 | public string? ReturnValue { get; set; }
30 |
31 | private Task Cancel()
32 | {
33 | return OnClose.InvokeAsync("");
34 | }
35 | private Task Ok()
36 | {
37 | return OnClose.InvokeAsync(ReturnValue);
38 | }
39 |
40 | public Task Enter(KeyboardEventArgs e)
41 | {
42 | if (e.Code == "Enter" || e.Code == "NumpadEnter")
43 | {
44 | return OnClose.InvokeAsync(ReturnValue);
45 | }
46 | return Task.CompletedTask;
47 | }
48 | }
--------------------------------------------------------------------------------
/Search/Constants/Interface.cs:
--------------------------------------------------------------------------------
1 | namespace Search.Constants;
2 |
3 | public static class Interface
4 | {
5 | public static readonly string EMPTY_SESSION = "empty-session-404";
6 | }
--------------------------------------------------------------------------------
/Search/Constants/Participants.cs:
--------------------------------------------------------------------------------
1 | namespace Search.Constants;
2 |
3 | public enum Participants
4 | {
5 | User = 0,
6 | Assistant
7 | }
--------------------------------------------------------------------------------
/Search/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
--------------------------------------------------------------------------------
/Search/Options/Chat.cs:
--------------------------------------------------------------------------------
1 | using SharedLib.Services;
2 |
3 | namespace Search.Options;
4 |
5 | public record Chat
6 | {
7 | public required MongoDbService MongoDbService { get; set; }
8 |
9 | public required OpenAiService OpenAiService { get; set; }
10 |
11 | public required ILogger Logger { get; init; }
12 | }
13 |
--------------------------------------------------------------------------------
/Search/Pages/ChatPane.razor:
--------------------------------------------------------------------------------
1 | @using Search.Constants
2 | @using Search.Services
3 | @using SharedLib.Models
4 | @using Humanizer
5 | @inject ChatService chatService
6 | @inject IJSRuntime JSRuntime
7 |
8 |
32 | Swapping to the Development environment displays detailed information about the error that occurred.
33 |
34 |
35 | The Development environment shouldn't be enabled for deployed applications.
36 | It can result in displaying sensitive information from exceptions to end users.
37 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
38 | and restarting the app.
39 |
22 |
23 | An error has occurred. This application may no longer respond until reloaded.
24 |
25 |
26 | An unhandled exception has occurred. See browser dev tools for details.
27 |
28 | Reload
29 |
30 | @RenderBody()
31 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/Search/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 | using MongoDB.Driver;
3 | using SharedLib.Options;
4 | using SharedLib.Services;
5 | using Search.Options;
6 | using Search.Services;
7 |
8 | var builder = WebApplication.CreateBuilder(args);
9 |
10 | builder.RegisterConfiguration();
11 | builder.Services.AddRazorPages();
12 | builder.Logging.ClearProviders();
13 | builder.Logging.AddConsole();
14 | builder.Services.AddServerSideBlazor();
15 | builder.Services.RegisterServices();
16 |
17 | var app = builder.Build();
18 |
19 | if (!app.Environment.IsDevelopment())
20 | {
21 | app.UseExceptionHandler("/Error");
22 | app.UseHsts();
23 | }
24 |
25 | app.UseHttpsRedirection();
26 | app.UseStaticFiles();
27 | app.UseRouting();
28 |
29 | app.MapBlazorHub();
30 | app.MapFallbackToPage("/_Host");
31 |
32 | await app.RunAsync();
33 |
34 | static class ProgramExtensions
35 | {
36 | public static void RegisterConfiguration(this WebApplicationBuilder builder)
37 | {
38 | //builder.Configuration.AddUserSecrets(optional: true, reloadOnChange: true);
39 | builder.Configuration.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true);
40 |
41 | builder.Services.AddOptions()
42 | .Bind(builder.Configuration.GetSection(nameof(OpenAi)));
43 |
44 | builder.Services.AddOptions()
45 | .Bind(builder.Configuration.GetSection(nameof(MongoDb)));
46 | }
47 |
48 | public static void RegisterServices(this IServiceCollection services)
49 | {
50 |
51 | services.AddSingleton((provider) =>
52 | {
53 | var openAiOptions = provider.GetRequiredService>();
54 | if (openAiOptions is null)
55 | {
56 | throw new ArgumentException($"{nameof(IOptions)} was not resolved through dependency injection.");
57 | }
58 | else
59 | {
60 | return new OpenAiService(
61 | endpoint: openAiOptions.Value?.Endpoint ?? String.Empty,
62 | key: openAiOptions.Value?.Key ?? String.Empty,
63 | embeddingsDeployment: openAiOptions.Value?.EmbeddingsDeployment ?? String.Empty,
64 | completionsDeployment: openAiOptions.Value?.CompletionsDeployment ?? String.Empty,
65 | maxConversationTokens: openAiOptions.Value?.MaxConversationTokens ?? String.Empty,
66 | maxCompletionTokens: openAiOptions.Value?.MaxCompletionTokens ?? String.Empty,
67 | maxEmbeddingTokens: openAiOptions.Value?.MaxEmbeddingTokens ?? String.Empty,
68 | logger: provider.GetRequiredService>()
69 | );
70 | }
71 | });
72 |
73 | services.AddSingleton((provider) =>
74 | {
75 | var mongoDbOptions = provider.GetRequiredService>();
76 | if (mongoDbOptions is null)
77 | {
78 | throw new ArgumentException($"{nameof(IOptions)} was not resolved through dependency injection.");
79 | }
80 | else
81 | {
82 | return new MongoDbService(
83 | connection: mongoDbOptions.Value?.Connection ?? String.Empty,
84 | databaseName: mongoDbOptions.Value?.DatabaseName ?? String.Empty,
85 | collectionNames: mongoDbOptions.Value?.CollectionNames ?? String.Empty,
86 | maxVectorSearchResults: mongoDbOptions.Value?.MaxVectorSearchResults ?? String.Empty,
87 | vectorIndexType: mongoDbOptions.Value?.VectorIndexType ?? String.Empty,
88 | openAiService: provider.GetRequiredService(),
89 | logger: provider.GetRequiredService>()
90 | );
91 | }
92 | });
93 | services.AddSingleton((provider) =>
94 | {
95 | var chatOptions = provider.GetRequiredService>();
96 | if (chatOptions is null)
97 | {
98 | throw new ArgumentException($"{nameof(IOptions)} was not resolved through dependency injection");
99 | }
100 | else
101 | {
102 | return new ChatService(
103 | mongoDbService: provider.GetRequiredService(),
104 | openAiService: provider.GetRequiredService(),
105 | logger: provider.GetRequiredService>()
106 | );
107 | }
108 | });
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Search/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Search": {
4 | "commandName": "Project",
5 | "launchBrowser": true,
6 | "environmentVariables": {
7 | "ASPNETCORE_ENVIRONMENT": "Development"
8 | },
9 | "dotnetRunMessages": true,
10 | "applicationUrl": "http://localhost:8100",
11 | "hotReloadProfile": "blazorwasm"
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/Search/Search.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net7.0
4 | enable
5 | enable
6 | Search
7 | true
8 | 86b98284-0715-492f-80de-95ad9057b2d4
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Search/Services/ChatService.cs:
--------------------------------------------------------------------------------
1 | using Humanizer.Localisation.TimeToClockNotation;
2 | using Search.Constants;
3 | using SharedLib.Models;
4 | using SharedLib.Services;
5 | using SharpToken;
6 | using Microsoft.ML.Tokenizers;
7 |
8 | namespace Search.Services;
9 |
10 | public class ChatService
11 | {
12 | ///
13 | /// All data is cached in the _sessions List object.
14 | ///
15 | private static List _sessions = new();
16 |
17 | private readonly OpenAiService _openAiService;
18 | private readonly MongoDbService _mongoDbService;
19 | private readonly int _maxConversationTokens;
20 | private readonly int _maxCompletionTokens;
21 | private readonly ILogger _logger;
22 |
23 | public ChatService(OpenAiService openAiService, MongoDbService mongoDbService, ILogger logger)
24 | {
25 |
26 | _openAiService = openAiService;
27 | _mongoDbService = mongoDbService;
28 |
29 | _maxConversationTokens = openAiService.MaxConversationTokens;
30 | _maxCompletionTokens = openAiService.MaxCompletionTokens;
31 | _logger = logger;
32 | }
33 |
34 | ///
35 | /// Returns list of chat session ids and names for left-hand nav to bind to (display Name and ChatSessionId as hidden)
36 | ///
37 | public async Task> GetAllChatSessionsAsync()
38 | {
39 | return _sessions = await _mongoDbService.GetSessionsAsync();
40 | }
41 |
42 | ///
43 | /// Returns the chat messages to display on the main web page when the user selects a chat from the left-hand nav
44 | ///
45 | public async Task> GetChatSessionMessagesAsync(string? sessionId)
46 | {
47 | ArgumentNullException.ThrowIfNull(sessionId);
48 |
49 | List chatMessages = new();
50 |
51 | if (_sessions.Count == 0)
52 | {
53 | return Enumerable.Empty().ToList();
54 | }
55 |
56 | int index = _sessions.FindIndex(s => s.SessionId == sessionId);
57 |
58 | if (_sessions[index].Messages.Count == 0)
59 | {
60 | // Messages are not cached, go read from database
61 | chatMessages = await _mongoDbService.GetSessionMessagesAsync(sessionId);
62 |
63 | // Cache results
64 | _sessions[index].Messages = chatMessages;
65 | }
66 | else
67 | {
68 | // Load from cache
69 | chatMessages = _sessions[index].Messages;
70 | }
71 |
72 | return chatMessages;
73 | }
74 |
75 | ///
76 | /// User creates a new Chat Session.
77 | ///
78 | public async Task CreateNewChatSessionAsync()
79 | {
80 | Session session = new();
81 |
82 | _sessions.Add(session);
83 |
84 | await _mongoDbService.InsertSessionAsync(session);
85 |
86 | }
87 |
88 | ///
89 | /// Rename the Chat Ssssion from "New Chat" to the summary provided by OpenAI
90 | ///
91 | public async Task RenameChatSessionAsync(string? sessionId, string newChatSessionName)
92 | {
93 | ArgumentNullException.ThrowIfNull(sessionId);
94 |
95 | int index = _sessions.FindIndex(s => s.SessionId == sessionId);
96 |
97 | _sessions[index].Name = newChatSessionName;
98 |
99 | await _mongoDbService.UpdateSessionAsync(_sessions[index]);
100 | }
101 |
102 | ///
103 | /// User deletes a chat session
104 | ///
105 | public async Task DeleteChatSessionAsync(string? sessionId)
106 | {
107 | ArgumentNullException.ThrowIfNull(sessionId);
108 |
109 | int index = _sessions.FindIndex(s => s.SessionId == sessionId);
110 |
111 | _sessions.RemoveAt(index);
112 |
113 | await _mongoDbService.DeleteSessionAndMessagesAsync(sessionId);
114 | }
115 |
116 | ///
117 | /// Receive a prompt from a user, Vectorize it from _openAIService Get a completion from _openAiService
118 | ///
119 | public async Task GetChatCompletionAsync(string? sessionId, string userPrompt, string collectionName)
120 | {
121 |
122 | try
123 | {
124 | ArgumentNullException.ThrowIfNull(sessionId);
125 |
126 |
127 | //Get embeddings for user prompt and number of tokens it uses.
128 | (float[] promptVectors, int promptTokens) = await _openAiService.GetEmbeddingsAsync(sessionId, userPrompt);
129 | //Create the prompt message object. Created here to give it a timestamp that precedes the completion message.
130 | Message promptMessage = new Message(sessionId, nameof(Participants.User), promptTokens, default, userPrompt);
131 |
132 |
133 | //Do vector search on the user prompt, return list of documents
134 | string retrievedDocuments = await _mongoDbService.VectorSearchAsync(collectionName, promptVectors);
135 |
136 | //Get the most recent conversation history up to _maxConversationTokens
137 | string conversation = GetConversationHistory(sessionId);
138 |
139 |
140 | //Construct our prompts sent to Azure OpenAI. Calculate token usage and trim the RAG payload and conversation history to prevent exceeding token limits.
141 | (string augmentedContent, string conversationAndUserPrompt) = BuildPrompts(userPrompt, conversation, retrievedDocuments);
142 |
143 |
144 | //Generate the completion from Azure OpenAI to return to the user
145 | (string completionText, int ragTokens, int completionTokens) = await _openAiService.GetChatCompletionAsync(sessionId, conversationAndUserPrompt, augmentedContent);
146 |
147 |
148 | //Create the completion message object
149 | Message completionMessage = new Message(sessionId, nameof(Participants.Assistant), completionTokens, ragTokens, completionText);
150 |
151 | //Add the user prompt and completion to cache, then persist to Cosmos in a transaction
152 | await AddPromptCompletionMessagesAsync(sessionId, promptMessage, completionMessage);
153 |
154 |
155 | return completionText;
156 |
157 | }
158 | catch (Exception ex)
159 | {
160 | string message = $"ChatService.GetChatCompletionAsync(): {ex.Message}";
161 | _logger.LogError(message);
162 | throw;
163 |
164 | }
165 | }
166 |
167 | ///
168 | /// Estimate the token usage for OpenAI completion to prevent exceeding the OpenAI model's maximum token limit. This function estimates the
169 | /// amount of tokens the vector search result data and the user prompt will consume. If the search result data exceeds the configured amount
170 | /// the function reduces the number of vectors, reducing the amount of data sent.
171 | ///
172 | private (string augmentedContent, string conversationAndUserPrompt) BuildPrompts(string userPrompt, string conversation, string retrievedData)
173 | {
174 |
175 | string updatedAugmentedContent = "";
176 | string updatedConversationAndUserPrompt = "";
177 |
178 |
179 | //SharpToken only estimates token usage and often undercounts. Add a buffer of 200 tokens.
180 | int bufferTokens = 200;
181 |
182 | //Create a new instance of SharpToken
183 | var encoding = GptEncoding.GetEncoding("cl100k_base"); //encoding base for GPT 3.5 Turbo and GPT 4
184 | //var encoding = GptEncoding.GetEncodingForModel("gpt-35-turbo");
185 |
186 | List ragVectors = encoding.Encode(retrievedData);
187 | int ragTokens = ragVectors.Count;
188 |
189 | List convVectors = encoding.Encode(conversation);
190 | int convTokens = convVectors.Count;
191 |
192 | int userPromptTokens = encoding.Encode(userPrompt).Count;
193 |
194 |
195 | //If RAG data plus user prompt, plus conversation, plus tokens for completion is greater than max completion tokens we've defined, reduce the rag data and conversation by relative amount.
196 | int totalTokens = ragTokens + convTokens + userPromptTokens + bufferTokens;
197 |
198 | //Too much data, reduce the rag data and conversation data by the same percentage. Do not reduce the user prompt as this is required for the completion.
199 | if (totalTokens > _maxCompletionTokens)
200 | {
201 | //Get the number of tokens to reduce by
202 | int tokensToReduce = totalTokens - _maxCompletionTokens;
203 |
204 | //Get the percentage of tokens to reduce by
205 | float ragTokenPct = (float)ragTokens / totalTokens;
206 | float conTokenPct = (float)convTokens / totalTokens;
207 |
208 | //Calculate the new number of tokens for each data set
209 | int newRagTokens = (int)Math.Round(ragTokens - (ragTokenPct * tokensToReduce), 0);
210 | int newConvTokens = (int)Math.Round(convTokens - (conTokenPct * tokensToReduce), 0);
211 |
212 |
213 | //Get the reduced set of RAG vectors
214 | List trimmedRagVectors = ragVectors.GetRange(0, newRagTokens);
215 | //Convert the vectors back to text
216 | updatedAugmentedContent = encoding.Decode(trimmedRagVectors);
217 |
218 | int offset = convVectors.Count - newConvTokens;
219 |
220 | //Get the reduce set of conversation vectors
221 | List trimmedConvVectors = convVectors.GetRange(offset, newConvTokens);
222 |
223 | //Convert vectors back into reduced conversation length
224 | updatedConversationAndUserPrompt = encoding.Decode(trimmedConvVectors);
225 |
226 | //add user prompt
227 | updatedConversationAndUserPrompt += Environment.NewLine + userPrompt;
228 |
229 |
230 | }
231 | //If everything is less than _maxCompletionTokens then good to go.
232 | else
233 | {
234 |
235 | //Return all of the content
236 | updatedAugmentedContent = retrievedData;
237 | updatedConversationAndUserPrompt = conversation + Environment.NewLine + userPrompt;
238 | }
239 |
240 |
241 | return (augmentedContent: updatedAugmentedContent, conversationAndUserPrompt: updatedConversationAndUserPrompt);
242 |
243 | }
244 |
245 | ///
246 | /// Get the most recent conversation history to provide additional context for the completion LLM
247 | ///
248 | private string GetConversationHistory(string sessionId)
249 | {
250 |
251 | int? tokensUsed = 0;
252 |
253 | int index = _sessions.FindIndex(s => s.SessionId == sessionId);
254 |
255 | List conversationMessages = _sessions[index].Messages.ToList(); //make a full copy
256 |
257 | //Iterate through these in reverse order to get the most recent conversation history up to _maxConversationTokens
258 | var trimmedMessages = conversationMessages
259 | .OrderByDescending(m => m.TimeStamp)
260 | .TakeWhile(m => (tokensUsed += m.Tokens) <= _maxConversationTokens)
261 | .Select(m => m.Text)
262 | .ToList();
263 |
264 | trimmedMessages.Reverse();
265 |
266 | //Return as a string
267 | string conversation = string.Join(Environment.NewLine, trimmedMessages.ToArray());
268 |
269 | return conversation;
270 |
271 | }
272 |
273 | public async Task SummarizeChatSessionNameAsync(string? sessionId, string prompt)
274 | {
275 | ArgumentNullException.ThrowIfNull(sessionId);
276 |
277 | string response = await _openAiService.SummarizeAsync(sessionId, prompt);
278 |
279 | await RenameChatSessionAsync(sessionId, response);
280 |
281 | return response;
282 | }
283 |
284 | ///
285 | /// Add user prompt to the chat session message list object and insert into the data service.
286 | ///
287 | private async Task AddPromptMessageAsync(string sessionId, string promptText)
288 | {
289 | Message promptMessage = new(sessionId, nameof(Participants.User), default, default, promptText);
290 |
291 | int index = _sessions.FindIndex(s => s.SessionId == sessionId);
292 |
293 | _sessions[index].AddMessage(promptMessage);
294 |
295 | await _mongoDbService.InsertMessageAsync(promptMessage);
296 | }
297 |
298 |
299 | ///
300 | /// Add user prompt and AI assistance response to the chat session message list object and insert into the data service as a transaction.
301 | ///
302 | private async Task AddPromptCompletionMessagesAsync(string sessionId, Message promptMessage, Message completionMessage)
303 | {
304 |
305 | int index = _sessions.FindIndex(s => s.SessionId == sessionId);
306 |
307 |
308 | //Add prompt and completion to the cache
309 | _sessions[index].AddMessage(promptMessage);
310 | _sessions[index].AddMessage(completionMessage);
311 |
312 |
313 | //Update session cache with tokens used
314 | _sessions[index].TokensUsed += promptMessage.Tokens;
315 | _sessions[index].TokensUsed += completionMessage.PromptTokens;
316 | _sessions[index].TokensUsed += completionMessage.Tokens;
317 |
318 | await _mongoDbService.UpsertSessionBatchAsync(session: _sessions[index], promptMessage: promptMessage, completionMessage: completionMessage);
319 |
320 | }
321 | }
--------------------------------------------------------------------------------
/Search/Shared/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 | Data Copilot
4 | @Body
--------------------------------------------------------------------------------
/Search/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using Microsoft.AspNetCore.Authorization
3 | @using Microsoft.AspNetCore.Components.Authorization
4 | @using Microsoft.AspNetCore.Components.Forms
5 | @using Microsoft.AspNetCore.Components.Routing
6 | @using Microsoft.AspNetCore.Components.Web
7 | @using Microsoft.AspNetCore.Components.Web.Virtualization
8 | @using Microsoft.JSInterop
9 | @using SharedLib
10 | @using Search
11 | @using Search.Components
12 | @using Search.Shared
13 |
--------------------------------------------------------------------------------
/Search/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "DetailedErrors": true,
3 | "Logging": {
4 | "LogLevel": {
5 | "Default": "Information",
6 | "Microsoft.AspNetCore": "Warning"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "OpenAi": {
11 | "Endpoint": "",
12 | "Key": "",
13 | "EmbeddingsDeployment": "",
14 | "CompletionsDeployment": "",
15 | "MaxConversationTokens": "1000",
16 | "MaxCompletionTokens": "2000",
17 | "MaxEmbeddingTokens": "8000"
18 | },
19 | "MongoDb": {
20 | "Connection": "",
21 | "DatabaseName": "retaildb",
22 | "CollectionNames": "products, customers, salesOrders, completions",
23 | "MaxVectorSearchResults": "10",
24 | "VectorIndexType": "ivf" // ivf, hnsw
25 | }
26 | }
--------------------------------------------------------------------------------
/Search/readme.md:
--------------------------------------------------------------------------------
1 | ---
2 | page_type: sample
3 | languages:
4 | - csharp
5 | products:
6 | - azure-cosmos-db
7 | - azure-openai
8 | name: Sample chat app using Azure Cosmos DB for NoSQL and Azure OpenAI
9 | urlFragment: chat-app
10 | description: Sample application that implements multiple chat threads using the Azure OpenAI "text-davinci-003" model and Azure Cosmos DB for NoSQL for storage.
11 | azureDeploy: https://raw.githubusercontent.com/azure-samples/cosmosdb-chatgpt/main/azuredeploy.json
12 | ---
13 |
14 | # Azure Cosmos DB + OpenAI ChatGPT
15 |
16 | This sample application combines Azure Cosmos DB with OpenAI ChatGPT with a Blazor Server front-end for an intelligent chat bot application that shows off how you can build a
17 | simple chat application with OpenAi ChatGPT and Azure Cosmos DB.
18 |
19 | 
20 |
21 | ## Features
22 |
23 | This application has individual chat sessions which are displayed and can be selected in the left-hand nav. Clicking on a session will show the messages that contain
24 | human prompts and AI completions.
25 |
26 | When a new prompt is sent to the Azure OpenAI service, some of the conversation history is sent with it. This provides context allowing ChatGPT to respond
27 | as though it is having a conversation. The length of this conversation history can be configured from appsettings.json
28 | with the `OpenAiMaxTokens` value that is then translated to a maximum conversation string length that is 1/2 of this value.
29 |
30 | Please note that the "text-davinci-003" model used by this sample has a maximum of 4096 tokens. Token are used in both the request and reponse from the service. Overriding the maxConversationLength to values approaching maximum token value could result in completions that contain little to no text if all of it has been used in the request.
31 |
32 | The history for all prompts and completions for each chat session is stored in Azure Cosmos DB. Deleting a chat session in the UI will delete it's corresponding data as well.
33 |
34 | The application will also summarize the name of the chat session by asking ChatGPT to provide a one or two word summary of the first prompt. This allows you to easily
35 | identity different chat sessions.
36 |
37 | Please note this is a sample application. It is intended to demonstrate how to use Azure Cosmos DB and Azure OpenAI ChatGPT together. It is not intended for production or other large scale use
38 |
39 |
40 | ## Getting Started
41 |
42 | ### Prerequisites
43 |
44 | - Azure Subscription
45 | - Subscription access to Azure OpenAI service. Start here to [Request Acces to Azure OpenAI Service](https://customervoice.microsoft.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR7en2Ais5pxKtso_Pz4b1_xUOFA5Qk1UWDRBMjg0WFhPMkIzTzhKQ1dWNyQlQCN0PWcu)
46 | - Visual Studio, VS Code, or some editor if you want to edit or view the source for this sample.
47 |
48 |
49 | ### Installation
50 |
51 | 1. Fork this repository to your own GitHub account.
52 | 1. Depending on whether you deploy using the ARM Template or Bicep, modify this variable in one of those files to point to your fork of this repository, "webSiteRepository": "https://github.com/Azure-Samples/cosmosdb-chatgpt.git"
53 | 1. If using the Deploy to Azure button below, also modify this README.md file to change the path for the Deploy To Azure button to your local repository.
54 | 1. If you deploy this application without making either of these changes, you can update the repository by disconnecting and connecting an external git repository pointing to your fork.
55 |
56 |
57 | The provided ARM or Bicep Template will provision the following resources:
58 | 1. Azure Cosmos DB account with database and container at 400 RU/s. This can optionally be configured to run on the Cosmos DB free tier if available for your subscription.
59 | 1. Azure App service. This will be configured for CI/CD to your forked GitHub repository. This service can also be configured to run on App Service free tier.
60 | 1. Azure Open AI account. You must also specify a name for the deployment of the "text-davinci-003" model which is used by this application.
61 |
62 | Note: You must have access to Azure Open AI service from your subscription before attempting to deploy this application.
63 |
64 | All connection information for Azure Cosmos DB and Open AI is zero-touch and injected as environment variables in the Azure App Service instance at deployment time.
65 |
66 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Fcosmosdb-chatgpt%2Fmain%2Fazuredeploy.json)
67 |
68 |
69 | ### Quickstart
70 |
71 | 1. After deployment, go to the resource group for your deployment and open the Azure App Service in the Azure Portal. Click the web url to launch the website.
72 | 1. Click + New Chat to create a new chat session.
73 | 1. Type your question in the text box and press Enter.
74 |
75 |
76 | ## Clean up
77 |
78 | To remove all the resources used by this sample, you must first manually delete the deployed model within the Azure AI service. You can then delete the resource group for your deployment. This will delete all remaining resources.
79 |
80 | ## Resources
81 |
82 | - [Azure Cosmos DB + Azure OpenAI ChatGPT Blog Post Announcement](https://devblogs.microsoft.com/cosmosdb/)
83 | - [Azure Cosmos DB Free Trial](https://aka.ms/TryCosmos)
84 | - [Open AI Platform documentation](https://platform.openai.com/docs/introduction/overview)
85 | - [Azure Open AI Service documentation](https://learn.microsoft.com/azure/cognitive-services/openai/)
86 |
--------------------------------------------------------------------------------
/Search/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/f9afd2c3e5a7ba8b957d8118f3126234e268effd/Search/screenshot.png
--------------------------------------------------------------------------------
/Search/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/f9afd2c3e5a7ba8b957d8118f3126234e268effd/Search/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/Search/wwwroot/js/site.js:
--------------------------------------------------------------------------------
1 | function scrollToLastMessage()
2 | {
3 | if (document.getElementById('MessagesInChatdiv')) {
4 | var elem = document.getElementById('MessagesInChatdiv');
5 | elem.scrollTop = elem.scrollHeight;
6 | return true;
7 | }
8 | return false;
9 | }
--------------------------------------------------------------------------------
/SharedLib/Models/Customer.cs:
--------------------------------------------------------------------------------
1 | using MongoDB.Bson.Serialization.Attributes;
2 | using MongoDB.Bson;
3 |
4 | namespace SharedLib.Models;
5 |
6 | public class Customer
7 | {
8 | [BsonId]
9 | [BsonRepresentation(BsonType.String)]
10 | public string id { get; set; }
11 | public string type { get; set; }
12 | public string customerId { get; set; }
13 | public string title { get; set; }
14 | public string firstName { get; set; }
15 | public string lastName { get; set; }
16 | public string emailAddress { get; set; }
17 | public string phoneNumber { get; set; }
18 | public string creationDate { get; set; }
19 | public List addresses { get; set; }
20 | public Password password { get; set; }
21 | public int salesOrderCount { get; set; }
22 | public float[]? vector { get; set; }
23 |
24 | public Customer(string id, string type, string customerId, string title,
25 | string firstName, string lastName, string emailAddress, string phoneNumber,
26 | string creationDate, List addresses, Password password,
27 | int salesOrderCount, float[]? vector = null)
28 | {
29 | this.id = id;
30 | this.type = type;
31 | this.customerId = customerId;
32 | this.title = title;
33 | this.firstName = firstName;
34 | this.lastName = lastName;
35 | this.emailAddress = emailAddress;
36 | this.phoneNumber = phoneNumber;
37 | this.creationDate = creationDate;
38 | this.addresses = addresses;
39 | this.password = password;
40 | this.salesOrderCount = salesOrderCount;
41 | this.vector = vector;
42 | }
43 | }
44 |
45 | public class Password
46 | {
47 | public string hash { get; set; }
48 | public string salt { get; set; }
49 |
50 | public Password(string hash, string salt)
51 | {
52 | this.hash = hash;
53 | this.salt = salt;
54 | }
55 | }
56 |
57 | public class CustomerAddress
58 | {
59 | public string addressLine1 { get; set; }
60 | public string addressLine2 { get; set; }
61 | public string city { get; set; }
62 | public string state { get; set; }
63 | public string country { get; set; }
64 | public string zipCode { get; set; }
65 | public Location location { get; set; }
66 |
67 | public CustomerAddress(string addressLine1, string addressLine2, string city, string state, string country, string zipCode, Location location)
68 | {
69 | this.addressLine1 = addressLine1;
70 | this.addressLine2 = addressLine2;
71 | this.city = city;
72 | this.state = state;
73 | this.country = country;
74 | this.zipCode = zipCode;
75 | this.location = location;
76 | }
77 | }
78 |
79 | public class Location
80 | {
81 | public string type { get; set; }
82 | public List coordinates { get; set; }
83 |
84 | public Location(string type, List coordinates)
85 | {
86 | this.type = type;
87 | this.coordinates = coordinates;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/SharedLib/Models/Message.cs:
--------------------------------------------------------------------------------
1 | using MongoDB.Bson.Serialization.Attributes;
2 | using MongoDB.Bson;
3 |
4 | namespace SharedLib.Models;
5 |
6 | public record Message
7 | {
8 | [BsonId]
9 | [BsonRepresentation(BsonType.String)]
10 | public string Id { get; set; }
11 |
12 | public string Type { get; set; }
13 |
14 | public string SessionId { get; set; }
15 |
16 | public DateTime TimeStamp { get; set; }
17 |
18 | public string Sender { get; set; }
19 |
20 | public int Tokens { get; set; }
21 |
22 | public int PromptTokens { get; set; }
23 |
24 | public string Text { get; set; }
25 |
26 | public Message(string sessionId, string sender, int? tokens, int? promptTokens, string text)
27 | {
28 | Id = Guid.NewGuid().ToString();
29 | Type = nameof(Message);
30 | SessionId = sessionId;
31 | Sender = sender;
32 | Tokens = tokens ?? 0;
33 | PromptTokens = promptTokens ?? 0;
34 | TimeStamp = DateTime.UtcNow;
35 | Text = text;
36 | }
37 | }
--------------------------------------------------------------------------------
/SharedLib/Models/Product.cs:
--------------------------------------------------------------------------------
1 | using MongoDB.Bson.Serialization.Attributes;
2 | using MongoDB.Bson;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace SharedLib.Models;
6 |
7 |
8 | public class Product
9 | {
10 | [BsonId]
11 | [BsonRepresentation(BsonType.String)]
12 | public string id { get; set; }
13 | public string categoryId { get; set; }
14 | public string categoryName { get; set; }
15 | public string sku { get; set; }
16 | public string name { get; set; }
17 | public string description { get; set; }
18 | public double price { get; set; }
19 | public List tags { get; set; }
20 | public float[]? vector { get; set; }
21 |
22 | public Product(string id, string categoryId, string categoryName, string sku, string name, string description, double price, List tags, float[]? vector = null)
23 | {
24 | this.id = id;
25 | this.categoryId = categoryId;
26 | this.categoryName = categoryName;
27 | this.sku = sku;
28 | this.name = name;
29 | this.description = description;
30 | this.price = price;
31 | this.tags = tags;
32 | this.vector = vector;
33 | }
34 |
35 | }
36 |
37 | public class ProductCategory
38 | {
39 | public string id { get; set; }
40 | public string type { get; set; }
41 | public string name { get; set; }
42 |
43 | public ProductCategory(string id, string type, string name)
44 | {
45 | this.id = id;
46 | this.type = type;
47 | this.name = name;
48 | }
49 | }
50 |
51 | public class Tag
52 | {
53 | public string id { get; set; }
54 | public string name { get; set; }
55 |
56 | public Tag(string id, string name)
57 | {
58 | this.id = id;
59 | this.name = name;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/SharedLib/Models/SalesOrder.cs:
--------------------------------------------------------------------------------
1 | using MongoDB.Bson.Serialization.Attributes;
2 | using MongoDB.Bson;
3 |
4 | namespace SharedLib.Models;
5 |
6 | public class SalesOrder
7 | {
8 | [BsonId]
9 | [BsonRepresentation(BsonType.String)]
10 | public string id { get; set; }
11 | public string type { get; set; }
12 | public string customerId { get; set; }
13 | public string orderDate { get; set; }
14 | public string shipDate { get; set; }
15 | public List details { get; set; }
16 | public float[]? vector { get; set; }
17 |
18 | public SalesOrder(string id, string type, string customerId, string orderDate, string shipDate, List details, float[]? vector = null)
19 | {
20 | this.id = id;
21 | this.type = type;
22 | this.customerId = customerId;
23 | this.orderDate = orderDate;
24 | this.shipDate = shipDate;
25 | this.details = details;
26 | this.vector = vector;
27 | }
28 | }
29 |
30 | public class SalesOrderDetails
31 | {
32 | public string sku { get; set; }
33 | public string name { get; set; }
34 | public double price { get; set; }
35 | public int quantity { get; set; }
36 |
37 | public SalesOrderDetails(string sku, string name, double price, int quantity)
38 | {
39 | this.sku = sku;
40 | this.name = name;
41 | this.price = price;
42 | this.quantity = quantity;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/SharedLib/Models/Session.cs:
--------------------------------------------------------------------------------
1 | using MongoDB.Bson;
2 | using MongoDB.Bson.Serialization.Attributes;
3 | using Newtonsoft.Json;
4 |
5 | namespace SharedLib.Models;
6 |
7 | public record Session
8 | {
9 | [BsonId]
10 | [BsonRepresentation(BsonType.String)]
11 | public string Id { get; set; }
12 |
13 | public string Type { get; set; }
14 |
15 | public string SessionId { get; set; }
16 |
17 | public int? TokensUsed { get; set; }
18 |
19 | public string Name { get; set; }
20 |
21 | [BsonIgnore]
22 | public List Messages { get; set; }
23 |
24 | public Session()
25 | {
26 | Id = Guid.NewGuid().ToString();
27 | Type = nameof(Session);
28 | SessionId = Id;
29 | TokensUsed = 0;
30 | Name = "New Chat";
31 | Messages = new List();
32 | }
33 |
34 | public void AddMessage(Message message)
35 | {
36 | Messages.Add(message);
37 | }
38 |
39 | public void UpdateMessage(Message message)
40 | {
41 | var match = Messages.Single(m => m.Id == message.Id);
42 | var index = Messages.IndexOf(match);
43 | Messages[index] = message;
44 | }
45 | }
--------------------------------------------------------------------------------
/SharedLib/Options/MongoDb.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using SharedLib.Services;
3 |
4 | namespace SharedLib.Options
5 | {
6 | public record MongoDb
7 | {
8 | public string? Connection { get; set; }
9 | public string? DatabaseName { get; set; }
10 |
11 | public string? CollectionNames { get; set; }
12 |
13 | public string? MaxVectorSearchResults { get; set; }
14 |
15 | public string? VectorIndexType { get; set; }
16 |
17 | public OpenAiService? OpenAiService { get; set; }
18 |
19 | public ILogger? Logger { get; set; }
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SharedLib/Options/OpenAi.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | namespace SharedLib.Options
4 | {
5 | public record OpenAi
6 | {
7 | public required string Endpoint { get; init; }
8 |
9 | public required string Key { get; init; }
10 |
11 | public required string EmbeddingsDeployment { get; init; }
12 |
13 | public required string CompletionsDeployment { get; init; }
14 |
15 | public required string MaxConversationTokens { get; init; }
16 |
17 | public required string MaxCompletionTokens { get; init; }
18 |
19 | public required string MaxEmbeddingTokens { get; init; }
20 |
21 | public required ILogger Logger { get; init; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/SharedLib/Services/MongoDbService.cs:
--------------------------------------------------------------------------------
1 | using MongoDB.Bson;
2 | using MongoDB.Driver;
3 | using SharedLib.Models;
4 | using Microsoft.Extensions.Logging;
5 | using MongoDB.Bson.Serialization;
6 | using Newtonsoft.Json.Linq;
7 | using System.Collections;
8 |
9 | namespace SharedLib.Services;
10 |
11 | ///
12 | /// Service to access Azure Cosmos DB for Mongo vCore.
13 | ///
14 | public class MongoDbService
15 | {
16 | private readonly MongoClient _client;
17 | private readonly IMongoDatabase _database;
18 |
19 | private readonly IMongoCollection _products;
20 | private readonly IMongoCollection _customers;
21 | private readonly IMongoCollection _salesOrders;
22 | private readonly IMongoCollection _sessions;
23 | private readonly IMongoCollection _messages;
24 | private readonly string _vectorIndexType;
25 | private readonly int _maxVectorSearchResults = default;
26 |
27 |
28 | private readonly OpenAiService _openAiService;
29 | private readonly ILogger _logger;
30 |
31 | ///
32 | /// Creates a new instance of the service.
33 | ///
34 | /// Endpoint URI.
35 | /// Account key.
36 | /// Name of the database to access.
37 | /// Names of the collections for this retail sample.
38 | /// Thrown when endpoint, key, databaseName, or collectionNames is either null or empty.
39 | ///
40 | /// This constructor will validate credentials and create a service client instance.
41 | ///
42 | public MongoDbService(string connection, string databaseName, string collectionNames, string maxVectorSearchResults, string vectorIndexType, OpenAiService openAiService, ILogger logger)
43 | {
44 | ArgumentException.ThrowIfNullOrEmpty(connection);
45 | ArgumentException.ThrowIfNullOrEmpty(databaseName);
46 | ArgumentException.ThrowIfNullOrEmpty(collectionNames);
47 | ArgumentException.ThrowIfNullOrEmpty(maxVectorSearchResults);
48 | ArgumentException.ThrowIfNullOrEmpty(vectorIndexType);
49 |
50 |
51 | _openAiService = openAiService;
52 | _logger = logger;
53 |
54 | _client = new MongoClient(connection);
55 | _database = _client.GetDatabase(databaseName);
56 | _maxVectorSearchResults = int.TryParse(maxVectorSearchResults, out _maxVectorSearchResults) ? _maxVectorSearchResults : 10;
57 | _vectorIndexType = vectorIndexType;
58 |
59 | _products = _database.GetCollection("products");
60 | _customers = _database.GetCollection("customers");
61 | _salesOrders = _database.GetCollection("salesOrders");
62 | _sessions = _database.GetCollection("completions");
63 | _messages = _database.GetCollection("completions");
64 |
65 |
66 | CreateVectorIndexIfNotExists("products", _vectorIndexType);
67 | CreateVectorIndexIfNotExists("customers", _vectorIndexType);
68 | CreateVectorIndexIfNotExists("salesOrders", _vectorIndexType);
69 | }
70 |
71 | ///
72 | /// Create a vector index on the collection if one does not exist.
73 | ///
74 | /// Name of the collection to create the vector index on.
75 | /// void
76 | public void CreateVectorIndexIfNotExists(string collectionName, string vectorIndexType)
77 | {
78 |
79 | try
80 | {
81 |
82 | var vectorIndexDefinition = RetrieveVectorIndexDefinition(collectionName, vectorIndexType); //hnsw or ivf
83 |
84 | IMongoCollection collection = _database.GetCollection(collectionName);
85 |
86 | string vectorIndexName = "vectorSearchIndex";
87 |
88 | //Find if vector index exists in vectors collection
89 | using (IAsyncCursor indexCursor = collection.Indexes.List())
90 | {
91 | bool vectorIndexExists = indexCursor.ToList().Any(x => x["name"] == vectorIndexName);
92 | if (!vectorIndexExists)
93 | {
94 | BsonDocumentCommand command = new BsonDocumentCommand(
95 | vectorIndexDefinition
96 | );
97 |
98 | BsonDocument result = _database.RunCommand(command);
99 | if (result["ok"] != 1)
100 | {
101 | _logger.LogError("CreateIndex failed with response: " + result.ToJson());
102 | }
103 | }
104 | }
105 |
106 | }
107 | catch (MongoException ex)
108 | {
109 | _logger.LogError("MongoDbService InitializeVectorIndex: " + ex.Message);
110 | throw;
111 | }
112 |
113 | }
114 |
115 | private BsonDocument RetrieveVectorIndexDefinition(string collectionName, string vectorIndexType)
116 | {
117 | var vectorIndex = new BsonDocument();
118 |
119 | if(vectorIndexType == "hnsw")
120 | {
121 | vectorIndex = new BsonDocument
122 | {
123 | { "createIndexes", collectionName },
124 | { "indexes", new BsonArray
125 | {
126 | new BsonDocument
127 | {
128 | { "name", "vectorSearchIndex" },
129 | { "key", new BsonDocument { { "vector", "cosmosSearch" } } },
130 | { "cosmosSearchOptions", new BsonDocument
131 | {
132 | { "kind", "vector-hnsw" },
133 | { "m", 16 },
134 | { "efConstruction", 64 },
135 | { "similarity", "COS" },
136 | { "dimensions", 1536 }
137 | }
138 | }
139 | }
140 | }
141 | }
142 | };
143 | }
144 | else if(vectorIndexType == "ivf")
145 | {
146 | vectorIndex = new BsonDocument
147 | {
148 | { "createIndexes", collectionName },
149 | { "indexes", new BsonArray
150 | {
151 | new BsonDocument
152 | {
153 | { "name", "vectorSearchIndex" },
154 | { "key", new BsonDocument { { "vector", "cosmosSearch" } } },
155 | { "cosmosSearchOptions", new BsonDocument
156 | {
157 | { "kind", "vector-ivf" },
158 | { "numLists", 2 },
159 | { "similarity", "COS" },
160 | { "dimensions", 1536 }
161 | }
162 | }
163 | }
164 | }
165 | }
166 | };
167 | }
168 |
169 | return vectorIndex;
170 |
171 | }
172 |
173 | ///
174 | /// Perform a vector search on the collection.
175 | ///
176 | /// Name of the collection to execute the vector search.
177 | /// vectors to use in the vector search.
178 | /// string payload of documents returned from the vector query
179 | public async Task VectorSearchAsync(string collectionName, float[] embeddings)
180 | {
181 |
182 | string resultDocuments = string.Empty;
183 |
184 |
185 | try
186 | {
187 |
188 | IMongoCollection collection = _database.GetCollection(collectionName);
189 |
190 | var embeddingsArray = new BsonArray(embeddings.Select(e => new BsonDouble(Convert.ToDouble(e))));
191 |
192 | //Search MongoDB vCore collection for similar embeddings
193 |
194 | BsonDocument[] pipeline = new BsonDocument[]
195 | {
196 | new BsonDocument
197 | {
198 | {
199 | "$search", new BsonDocument
200 | {
201 | {
202 | "cosmosSearch", new BsonDocument
203 | {
204 | { "vector", embeddingsArray },
205 | { "path", "vector" },
206 | { "k", _maxVectorSearchResults }
207 | }
208 | },
209 | { "returnStoredSource", true }
210 | }
211 | }
212 | },
213 | new BsonDocument
214 | {
215 | {
216 | "$project", new BsonDocument
217 | {
218 | { "_id", 0 },
219 | { "vector", 0 }
220 | }
221 | }
222 | }
223 | };
224 |
225 |
226 | // Return results, combine into a single string
227 | List bsonDocuments = await collection.Aggregate(pipeline).ToListAsync();
228 | List result = bsonDocuments.ConvertAll(bsonDocument => bsonDocument.ToString());
229 | resultDocuments = string.Join(" ", result);
230 |
231 | }
232 | catch (MongoException ex)
233 | {
234 | _logger.LogError($"Exception: VectorSearchAsync(): {ex.Message}");
235 | throw;
236 | }
237 |
238 | return resultDocuments;
239 | }
240 |
241 | public async Task UpsertProductAsync(Product product)
242 | {
243 |
244 | //Vectorize and add new vector property and store in vectors collection.
245 |
246 | try
247 | {
248 |
249 | //Serialize the product object to send to OpenAI
250 | string sProduct = RemoveVectorAndSerialize(product);
251 |
252 | (product.vector, int tokens) = await _openAiService.GetEmbeddingsAsync(string.Empty, sProduct);
253 |
254 | await _products.ReplaceOneAsync(
255 | filter: Builders.Filter.Eq("categoryId", product.categoryId)
256 | & Builders.Filter.Eq("_id", product.id),
257 | options: new ReplaceOptions { IsUpsert = true },
258 | replacement: product);
259 |
260 | }
261 | catch (MongoException ex)
262 | {
263 | _logger.LogError($"Exception: UpsertProductAsync(): {ex.Message}");
264 | throw;
265 |
266 | }
267 |
268 | return product;
269 | }
270 |
271 | public async Task DeleteProductAsync(Product product)
272 | {
273 |
274 | try
275 | {
276 |
277 | var filter = Builders.Filter.And(
278 | Builders.Filter.Eq("categoryId", product.categoryId),
279 | Builders.Filter.Eq("_id", product.id));
280 |
281 | //Delete from the product collection
282 | await _products.DeleteOneAsync(filter);
283 |
284 |
285 | }
286 | catch (MongoException ex)
287 | {
288 | _logger.LogError($"Exception: DeleteProductAsync(): {ex.Message}");
289 | throw;
290 |
291 | }
292 |
293 | }
294 |
295 | public async Task UpsertCustomerAsync(Customer customer)
296 | {
297 |
298 | try
299 | {
300 | //Remove any existing vectors, then serialize the object to send to OpenAI
301 | string sObject = RemoveVectorAndSerialize(customer);
302 |
303 | (customer.vector, int tokens) = await _openAiService.GetEmbeddingsAsync(string.Empty, sObject);
304 |
305 | await _customers.ReplaceOneAsync(
306 | filter: Builders.Filter.Eq("customerId", customer.customerId)
307 | & Builders.Filter.Eq("_id", customer.id),
308 | options: new ReplaceOptions { IsUpsert = true },
309 | replacement: customer);
310 |
311 | }
312 | catch (MongoException ex)
313 | {
314 | _logger.LogError($"Exception: UpsertCustomerAsync(): {ex.Message}");
315 | throw;
316 |
317 | }
318 |
319 | return customer;
320 |
321 | }
322 |
323 | public async Task DeleteCustomerAsync(Customer customer)
324 | {
325 |
326 | try
327 | {
328 | var filter = Builders.Filter.And(
329 | Builders.Filter.Eq("customerId", customer.customerId),
330 | Builders.Filter.Eq("_id", customer.id));
331 |
332 | //Delete customer from customer collection
333 | await _customers.DeleteOneAsync(filter);
334 |
335 |
336 | }
337 | catch (MongoException ex)
338 | {
339 | _logger.LogError($"Exception: DeleteCustomerAsync(): {ex.Message}");
340 | throw;
341 |
342 | }
343 |
344 | }
345 |
346 | public async Task UpsertSalesOrderAsync(SalesOrder salesOrder)
347 | {
348 |
349 | try
350 | {
351 |
352 | //Remove any existing vectors, then serialize the object to send to OpenAI
353 | string sObject = RemoveVectorAndSerialize(salesOrder);
354 |
355 | (salesOrder.vector, int tokens) = await _openAiService.GetEmbeddingsAsync(string.Empty, sObject);
356 |
357 | await _salesOrders.ReplaceOneAsync(
358 | filter: Builders.Filter.Eq("customerId", salesOrder.customerId)
359 | & Builders.Filter.Eq("_id", salesOrder.id),
360 | options: new ReplaceOptions { IsUpsert = true },
361 | replacement: salesOrder);
362 |
363 |
364 | }
365 | catch (MongoException ex)
366 | {
367 | _logger.LogError($"Exception: UpsertSalesOrderAsync(): {ex.Message}");
368 | throw;
369 |
370 | }
371 |
372 | return salesOrder;
373 |
374 | }
375 |
376 | public async Task DeleteSalesOrderAsync(SalesOrder salesOrder)
377 | {
378 |
379 | try
380 | {
381 | var filter = Builders.Filter.And(
382 | Builders.Filter.Eq("customerId", salesOrder.customerId),
383 | Builders.Filter.Eq("_id", salesOrder.id));
384 |
385 | await _salesOrders.DeleteOneAsync(filter);
386 |
387 | }
388 | catch (MongoException ex)
389 | {
390 | _logger.LogError($"Exception: DeleteSalesOrderAsync(): {ex.Message}");
391 | throw;
392 |
393 | }
394 |
395 | }
396 |
397 | private string RemoveVectorAndSerialize(object o)
398 | {
399 | string sObject = string.Empty;
400 |
401 |
402 | try
403 | {
404 | JObject obj = JObject.FromObject(o);
405 |
406 | obj.Remove("vector");
407 |
408 | sObject = obj.ToString();
409 | }
410 | catch {}
411 |
412 | return sObject;
413 | }
414 |
415 | public async Task ImportAndVectorizeAsync(string collectionName, string json)
416 | {
417 | try
418 | {
419 |
420 | IEnumerable documents = BsonSerializer.Deserialize>(json);
421 |
422 | foreach (var document in documents)
423 | {
424 | //Vectorize item, add to vector property, save in collection.
425 | (float[] embeddings, int tokens) = await _openAiService.GetEmbeddingsAsync(string.Empty, document.ToString());
426 |
427 | document["vector"] = BsonValue.Create(embeddings);
428 |
429 | await _database.GetCollection(collectionName).InsertOneAsync(document);
430 | }
431 |
432 | }
433 |
434 | catch (MongoException ex)
435 | {
436 | _logger.LogError($"Exception: ImportJsonAsync(): {ex.Message}");
437 | throw;
438 | }
439 | }
440 |
441 |
442 | ///
443 | /// Gets a list of all current chat sessions.
444 | ///
445 | /// List of distinct chat session items.
446 | public async Task> GetSessionsAsync()
447 | {
448 | List sessions = new List();
449 | try
450 | {
451 |
452 | sessions = await _sessions.Find(
453 | filter: Builders.Filter.Eq("Type", nameof(Session)))
454 | .ToListAsync();
455 |
456 | }
457 | catch (MongoException ex)
458 | {
459 | _logger.LogError($"Exception: GetSessionsAsync(): {ex.Message}");
460 | throw;
461 | }
462 |
463 | return sessions;
464 | }
465 |
466 | ///
467 | /// Gets a list of all current chat messages for a specified session identifier.
468 | ///
469 | /// Chat session identifier used to filter messages.
470 | /// List of chat message items for the specified session.
471 | public async Task> GetSessionMessagesAsync(string sessionId)
472 | {
473 | List messages = new();
474 |
475 | try
476 | {
477 |
478 | messages = await _messages.Find(
479 | filter: Builders.Filter.Eq("Type", nameof(Message))
480 | & Builders.Filter.Eq("SessionId", sessionId))
481 | .ToListAsync();
482 |
483 | }
484 | catch (MongoException ex)
485 | {
486 | _logger.LogError($"Exception: GetSessionMessagesAsync(): {ex.Message}");
487 | throw;
488 | }
489 |
490 | return messages;
491 |
492 | }
493 |
494 | ///
495 | /// Creates a new chat session.
496 | ///
497 | /// Chat session item to create.
498 | /// Newly created chat session item.
499 | public async Task InsertSessionAsync(Session session)
500 | {
501 | try
502 | {
503 |
504 | await _sessions.InsertOneAsync(session);
505 |
506 | }
507 | catch (MongoException ex)
508 | {
509 | _logger.LogError($"Exception: InsertSessionAsync(): {ex.Message}");
510 | throw;
511 | }
512 | }
513 |
514 | ///
515 | /// Creates a new chat message.
516 | ///
517 | /// Chat message item to create.
518 | /// Newly created chat message item.
519 | public async Task InsertMessageAsync(Message message)
520 | {
521 | try
522 | {
523 |
524 | await _messages.InsertOneAsync(message);
525 |
526 | }
527 | catch (MongoException ex)
528 | {
529 | _logger.LogError($"Exception: InsertMessageAsync(): {ex.Message}");
530 | throw;
531 | }
532 |
533 | }
534 |
535 | ///
536 | /// Updates an existing chat session.
537 | ///
538 | /// Chat session item to update.
539 | /// Revised created chat session item.
540 | public async Task UpdateSessionAsync(Session session)
541 | {
542 |
543 | try
544 | {
545 |
546 | await _sessions.ReplaceOneAsync(
547 | filter: Builders.Filter.Eq("Type", nameof(Session))
548 | & Builders.Filter.Eq("SessionId", session.SessionId),
549 | replacement: session);
550 |
551 | }
552 | catch (MongoException ex)
553 | {
554 | _logger.LogError($"Exception: UpdateSessionAsync(): {ex.Message}");
555 | throw;
556 | }
557 | }
558 |
559 | ///
560 | /// Batch create or update chat messages and session.
561 | ///
562 | /// Chat message and session items to create or replace.
563 | public async Task UpsertSessionBatchAsync(Session session, Message promptMessage, Message completionMessage)
564 | {
565 | using (var transaction = await _client.StartSessionAsync())
566 | {
567 | transaction.StartTransaction();
568 |
569 | try
570 | {
571 |
572 | await _sessions.ReplaceOneAsync(
573 | filter: Builders.Filter.Eq("Type", nameof(Session))
574 | & Builders.Filter.Eq("SessionId", session.SessionId)
575 | & Builders.Filter.Eq("Id", session.Id),
576 | replacement: session);
577 |
578 | await _messages.InsertOneAsync(promptMessage);
579 | await _messages.InsertOneAsync(completionMessage);
580 |
581 | await transaction.CommitTransactionAsync();
582 | }
583 | catch (MongoException ex)
584 | {
585 | await transaction.AbortTransactionAsync();
586 | _logger.LogError($"Exception: UpsertSessionBatchAsync(): {ex.Message}");
587 | throw;
588 | }
589 | }
590 |
591 |
592 | }
593 |
594 | ///
595 | /// Batch deletes an existing chat session and all related messages.
596 | ///
597 | /// Chat session identifier used to flag messages and sessions for deletion.
598 | public async Task DeleteSessionAndMessagesAsync(string sessionId)
599 | {
600 | try
601 | {
602 |
603 | await _database.GetCollection("completions").DeleteManyAsync(
604 | filter: Builders.Filter.Eq("SessionId", sessionId));
605 |
606 | }
607 | catch (MongoException ex)
608 | {
609 | _logger.LogError($"Exception: DeleteSessionAndMessagesAsync(): {ex.Message}");
610 | throw;
611 | }
612 |
613 | }
614 |
615 | }
--------------------------------------------------------------------------------
/SharedLib/Services/OpenAiService.cs:
--------------------------------------------------------------------------------
1 | using Azure;
2 | using Azure.AI.OpenAI;
3 | using Azure.Core;
4 | using System.Text.RegularExpressions;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace SharedLib.Services;
8 |
9 | ///
10 | /// Service to access Azure OpenAI.
11 | ///
12 | public class OpenAiService
13 | {
14 | private readonly string _embeddingsModelOrDeployment = string.Empty;
15 | private readonly string _completionsModelOrDeployment = string.Empty;
16 | private readonly int _maxConversationTokens = default;
17 | private readonly int _maxCompletionTokens = default;
18 | private readonly int _maxEmbeddingTokens = default;
19 | private readonly ILogger _logger;
20 | private readonly OpenAIClient _client;
21 |
22 |
23 |
24 | //System prompts to send with user prompts to instruct the model for chat session
25 |
26 | private readonly string _systemPromptRetailAssistant = @"
27 | You are an intelligent assistant for the Cosmic Works Bike Company.
28 | You are designed to provide helpful answers to user questions about
29 | product, product category, customer and sales order information provided in JSON format below.
30 |
31 | Instructions:
32 | - Only answer questions related to the information provided below,
33 | - Don't reference any product, customer, or salesOrder data not provided below.
34 | - If you're unsure of an answer, you can say ""I don't know"" or ""I'm not sure"" and recommend users search themselves.
35 |
36 | Text of relevant information:";
37 |
38 | //System prompt to send with user prompts to instruct the model for summarization
39 | private readonly string _summarizePrompt = @"
40 | Summarize the text below in one or two words to use as a label in a button on a web page. Output words only. Summarize the text below here:" + Environment.NewLine;
41 |
42 |
43 | ///
44 | /// Gets the maximum number of tokens from the conversation to send as part of the user prompt.
45 | ///
46 | public int MaxConversationTokens
47 | {
48 | get => _maxConversationTokens;
49 | }
50 | ///
51 | /// Gets the maximum number of tokens that can be used in generating the completion.
52 | ///
53 | public int MaxCompletionTokens
54 | {
55 | get => _maxCompletionTokens;
56 | }
57 |
58 | ///
59 | /// Gets the maximum number of tokens that can be used in generating embeddings.
60 | ///
61 | public int MaxEmbeddingTokens
62 | {
63 | get => _maxEmbeddingTokens;
64 | }
65 |
66 | ///
67 | /// Creates a new instance of the service.
68 | ///
69 | /// Endpoint URI.
70 | /// Account key.
71 | /// Name of the model deployment for generating embeddings.
72 | /// Name of the model deployment for generating completions.
73 | /// Maximum number of bytes to limit conversation history sent for a completion.
74 | /// Logger instance.
75 | /// Thrown when endpoint, key, deploymentName, or maxConversationBytes is either null or empty.
76 | ///
77 | /// This constructor will validate credentials and create a HTTP client instance.
78 | ///
79 | public OpenAiService(string endpoint, string key, string embeddingsDeployment, string completionsDeployment, string maxCompletionTokens, string maxConversationTokens, string maxEmbeddingTokens, ILogger logger)
80 | {
81 | ArgumentException.ThrowIfNullOrEmpty(endpoint);
82 | ArgumentException.ThrowIfNullOrEmpty(key);
83 | ArgumentException.ThrowIfNullOrEmpty(embeddingsDeployment);
84 | ArgumentException.ThrowIfNullOrEmpty(completionsDeployment);
85 | ArgumentException.ThrowIfNullOrEmpty(maxConversationTokens);
86 | ArgumentException.ThrowIfNullOrEmpty(maxCompletionTokens);
87 | ArgumentException.ThrowIfNullOrEmpty(maxEmbeddingTokens);
88 |
89 | _embeddingsModelOrDeployment = embeddingsDeployment;
90 | _completionsModelOrDeployment = completionsDeployment;
91 | _maxConversationTokens = int.TryParse(maxConversationTokens, out _maxConversationTokens) ? _maxConversationTokens : 100;
92 | _maxCompletionTokens = int.TryParse(maxCompletionTokens, out _maxCompletionTokens) ? _maxCompletionTokens : 500;
93 | _maxEmbeddingTokens = int.TryParse(maxEmbeddingTokens, out _maxEmbeddingTokens) ? _maxEmbeddingTokens : 8000;
94 |
95 | _logger = logger;
96 |
97 | OpenAIClientOptions options = new OpenAIClientOptions()
98 | {
99 | Retry =
100 | {
101 | Delay = TimeSpan.FromSeconds(2),
102 | MaxRetries = 10,
103 | Mode = RetryMode.Exponential
104 | }
105 | };
106 |
107 | //Use this as endpoint in configuration to use non-Azure Open AI endpoint and OpenAI model names
108 | if (endpoint.Contains("api.openai.com"))
109 | _client = new OpenAIClient(key, options);
110 | else
111 | _client = new(new Uri(endpoint), new AzureKeyCredential(key), options);
112 |
113 |
114 | }
115 |
116 | ///
117 | /// Sends a prompt to the deployed OpenAI embeddings model and returns an array of vectors as a response.
118 | ///
119 | /// Chat session identifier for the current conversation.
120 | /// Prompt message to generated embeddings on.
121 | /// Response from the OpenAI model as an array of vectors along with tokens for the prompt and response.
122 | public async Task<(float[] vectors, int promptTokens)> GetEmbeddingsAsync(string sessionId, string input)
123 | {
124 |
125 | float[] embedding = new float[0];
126 | int responseTokens = 0;
127 |
128 | try
129 | {
130 | EmbeddingsOptions options = new EmbeddingsOptions(_embeddingsModelOrDeployment, new List { input })
131 | {
132 | User = sessionId
133 | };
134 |
135 |
136 | var response = await _client.GetEmbeddingsAsync(options);
137 |
138 |
139 | Embeddings embeddings = response.Value;
140 |
141 |
142 | responseTokens = embeddings.Usage.TotalTokens;
143 |
144 | embedding = embeddings.Data[0].Embedding.ToArray();
145 |
146 | return (embedding, responseTokens);
147 | }
148 | catch (Exception ex)
149 | {
150 | string message = $"OpenAiService.GetEmbeddingsAsync(): {ex.Message}";
151 | _logger.LogError(message);
152 | throw;
153 |
154 | }
155 | }
156 |
157 | ///
158 | /// Sends a prompt to the deployed OpenAI LLM model and returns the response.
159 | ///
160 | /// Chat session identifier for the current conversation.
161 | /// Prompt message to send to the deployment.
162 | /// Response from the OpenAI model along with tokens for the prompt and response.
163 | public async Task<(string response, int promptTokens, int responseTokens)> GetChatCompletionAsync(string sessionId, string userPrompt, string documents)
164 | {
165 |
166 | try
167 | {
168 |
169 | var systemMessage = new ChatRequestSystemMessage(_systemPromptRetailAssistant + documents);
170 | var userMessage = new ChatRequestUserMessage(userPrompt);
171 |
172 |
173 | ChatCompletionsOptions options = new()
174 | {
175 | DeploymentName = _completionsModelOrDeployment,
176 | Messages =
177 | {
178 | systemMessage,
179 | userMessage
180 | },
181 | MaxTokens = _maxCompletionTokens,
182 | User = sessionId,
183 | Temperature = 0.3f,
184 | NucleusSamplingFactor = 0.95f,
185 | FrequencyPenalty = 0,
186 | PresencePenalty = 0
187 | };
188 |
189 | Response completionsResponse = await _client.GetChatCompletionsAsync(options);
190 |
191 |
192 | ChatCompletions completions = completionsResponse.Value;
193 |
194 | return (
195 | response: completions.Choices[0].Message.Content,
196 | promptTokens: completions.Usage.PromptTokens,
197 | responseTokens: completions.Usage.CompletionTokens
198 | );
199 |
200 | }
201 | catch ( Exception ex )
202 | {
203 |
204 | string message = $"OpenAiService.GetChatCompletionAsync(): {ex.Message}";
205 | _logger.LogError(message);
206 | throw;
207 |
208 | }
209 | }
210 |
211 | ///
212 | /// Sends the existing conversation to the OpenAI model and returns a two word summary.
213 | ///
214 | /// Chat session identifier for the current conversation.
215 | /// The first User Prompt and Completion to send to the deployment.
216 | /// Summarization response from the OpenAI model deployment.
217 | public async Task SummarizeAsync(string sessionId, string userPrompt)
218 | {
219 |
220 | var systemMessage = new ChatRequestSystemMessage(_summarizePrompt);
221 | var userMessage = new ChatRequestUserMessage(userPrompt);
222 |
223 | ChatCompletionsOptions options = new()
224 | {
225 | DeploymentName = _completionsModelOrDeployment,
226 | Messages = {
227 | systemMessage,
228 | userMessage
229 | },
230 | User = sessionId,
231 | MaxTokens = 200,
232 | Temperature = 0.0f,
233 | NucleusSamplingFactor = 1.0f,
234 | FrequencyPenalty = 0,
235 | PresencePenalty = 0
236 | };
237 |
238 | Response completionsResponse = await _client.GetChatCompletionsAsync(options);
239 |
240 | ChatCompletions completions = completionsResponse.Value;
241 | string output = completions.Choices[0].Message.Content;
242 |
243 | //Remove all non-alpha numeric characters in case the model returns any
244 | string summary = Regex.Replace(output, @"[^a-zA-Z0-9\s]", "");
245 |
246 | return summary;
247 | }
248 | }
--------------------------------------------------------------------------------
/SharedLib/SharedLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/VectorSearchAiAssistantMongoDBvCore.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.5.33530.505
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vectorize", "Vectorize\Vectorize.csproj", "{68289028-AC86-47AD-8D31-234825508A2A}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Search", "Search\Search.csproj", "{E8C2FE79-83B4-460F-BACD-467E9200C9DD}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D7AF34B5-704A-406D-962A-B27B064F23C0}"
11 | ProjectSection(SolutionItems) = preProject
12 | azuredeploy.bicep = azuredeploy.bicep
13 | azuredeploy.json = azuredeploy.json
14 | LICENSE = LICENSE
15 | README.md = README.md
16 | EndProjectSection
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedLib", "SharedLib\SharedLib.csproj", "{FF807B70-8F25-4A28-A017-3DA79ADD4DB2}"
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {68289028-AC86-47AD-8D31-234825508A2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {68289028-AC86-47AD-8D31-234825508A2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {68289028-AC86-47AD-8D31-234825508A2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {68289028-AC86-47AD-8D31-234825508A2A}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {E8C2FE79-83B4-460F-BACD-467E9200C9DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {E8C2FE79-83B4-460F-BACD-467E9200C9DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {E8C2FE79-83B4-460F-BACD-467E9200C9DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {E8C2FE79-83B4-460F-BACD-467E9200C9DD}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {FF807B70-8F25-4A28-A017-3DA79ADD4DB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {FF807B70-8F25-4A28-A017-3DA79ADD4DB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {FF807B70-8F25-4A28-A017-3DA79ADD4DB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {FF807B70-8F25-4A28-A017-3DA79ADD4DB2}.Release|Any CPU.Build.0 = Release|Any CPU
38 | EndGlobalSection
39 | GlobalSection(SolutionProperties) = preSolution
40 | HideSolutionNode = FALSE
41 | EndGlobalSection
42 | GlobalSection(ExtensibilityGlobals) = postSolution
43 | SolutionGuid = {FF5DE858-4B85-4EE8-8A6D-46E8E4FBA078}
44 | EndGlobalSection
45 | EndGlobal
46 |
--------------------------------------------------------------------------------
/Vectorize/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Azure Functions localsettings file
5 | local.settings.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # DNX
47 | project.lock.json
48 | project.fragment.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | # TODO: Comment the next line if you want to checkin your web deploy settings
148 | # but database connection strings (with potential passwords) will be unencrypted
149 | #*.pubxml
150 | *.publishproj
151 |
152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
153 | # checkin your Azure Web App publish settings, but sensitive information contained
154 | # in these scripts will be unencrypted
155 | PublishScripts/
156 |
157 | # NuGet Packages
158 | *.nupkg
159 | # The packages folder can be ignored because of Package Restore
160 | **/packages/*
161 | # except build/, which is used as an MSBuild target.
162 | !**/packages/build/
163 | # Uncomment if necessary however generally it will be regenerated when needed
164 | #!**/packages/repositories.config
165 | # NuGet v3's project.json files produces more ignoreable files
166 | *.nuget.props
167 | *.nuget.targets
168 |
169 | # Microsoft Azure Build Output
170 | csx/
171 | *.build.csdef
172 |
173 | # Microsoft Azure Emulator
174 | ecf/
175 | rcf/
176 |
177 | # Windows Store app package directories and files
178 | AppPackages/
179 | BundleArtifacts/
180 | Package.StoreAssociation.xml
181 | _pkginfo.txt
182 |
183 | # Visual Studio cache files
184 | # files ending in .cache can be ignored
185 | *.[Cc]ache
186 | # but keep track of directories ending in .cache
187 | !*.[Cc]ache/
188 |
189 | # Others
190 | ClientBin/
191 | ~$*
192 | *~
193 | *.dbmdl
194 | *.dbproj.schemaview
195 | *.jfm
196 | *.pfx
197 | *.publishsettings
198 | node_modules/
199 | orleans.codegen.cs
200 |
201 | # Since there are multiple workflows, uncomment next line to ignore bower_components
202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
203 | #bower_components/
204 |
205 | # RIA/Silverlight projects
206 | Generated_Code/
207 |
208 | # Backup & report files from converting an old project file
209 | # to a newer Visual Studio version. Backup files are not needed,
210 | # because we have git ;-)
211 | _UpgradeReport_Files/
212 | Backup*/
213 | UpgradeLog*.XML
214 | UpgradeLog*.htm
215 |
216 | # SQL Server files
217 | *.mdf
218 | *.ldf
219 |
220 | # Business Intelligence projects
221 | *.rdl.data
222 | *.bim.layout
223 | *.bim_*.settings
224 |
225 | # Microsoft Fakes
226 | FakesAssemblies/
227 |
228 | # GhostDoc plugin setting file
229 | *.GhostDoc.xml
230 |
231 | # Node.js Tools for Visual Studio
232 | .ntvs_analysis.dat
233 |
234 | # Visual Studio 6 build log
235 | *.plg
236 |
237 | # Visual Studio 6 workspace options file
238 | *.opt
239 |
240 | # Visual Studio LightSwitch build output
241 | **/*.HTMLClient/GeneratedArtifacts
242 | **/*.DesktopClient/GeneratedArtifacts
243 | **/*.DesktopClient/ModelManifest.xml
244 | **/*.Server/GeneratedArtifacts
245 | **/*.Server/ModelManifest.xml
246 | _Pvt_Extensions
247 |
248 | # Paket dependency manager
249 | .paket/paket.exe
250 | paket-files/
251 |
252 | # FAKE - F# Make
253 | .fake/
254 |
255 | # JetBrains Rider
256 | .idea/
257 | *.sln.iml
258 |
259 | # CodeRush
260 | .cr/
261 |
262 | # Python Tools for Visual Studio (PTVS)
263 | __pycache__/
264 | *.pyc
265 |
266 | __blobstorage__
267 | __azurite*.json
268 |
--------------------------------------------------------------------------------
/Vectorize/AddRemoveData.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Azure.Functions.Worker;
2 | using Microsoft.Extensions.Logging;
3 | using System.Net;
4 | using Microsoft.Azure.Functions.Worker.Http;
5 | using SharedLib.Models;
6 | using SharedLib.Services;
7 |
8 | namespace Vectorize
9 | {
10 | public class AddRemoveData
11 | {
12 |
13 | private readonly MongoDbService _mongo;
14 | private readonly ILogger _logger;
15 |
16 | public AddRemoveData(MongoDbService mongo, ILoggerFactory loggerFactory)
17 | {
18 | _mongo = mongo;
19 | _logger = loggerFactory.CreateLogger();
20 | }
21 |
22 |
23 | [Function("AddRemoveData")]
24 | public async Task Run(
25 | [HttpTrigger(AuthorizationLevel.Anonymous, "get", "put", Route = null)] HttpRequestData req)
26 | {
27 | _logger.LogInformation("C# HTTP trigger function processed a request.");
28 |
29 | string? action = req.Query["action"];
30 |
31 | try
32 | {
33 |
34 | if (action == "add")
35 | {
36 | await AddProduct();
37 | }
38 | else if (action == "remove")
39 | {
40 | await RemoveProduct();
41 |
42 | }
43 | else
44 | {
45 | throw new Exception("Bad Request: AddRemoveData HTTP trigger. Missing value for action in query string, add or remove");
46 | }
47 |
48 | var response = req.CreateResponse(HttpStatusCode.OK);
49 | response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
50 | await response.WriteStringAsync("AddRemoveData HTTP trigger function executed successfully.");
51 |
52 | return response;
53 | }
54 | catch (Exception ex)
55 | {
56 |
57 | var response = req.CreateResponse(HttpStatusCode.BadRequest);
58 | await response.WriteStringAsync(ex.ToString());
59 | return response;
60 |
61 | }
62 | }
63 |
64 | public async Task AddProduct()
65 | {
66 |
67 | try
68 | {
69 |
70 | Product product = GetCosmicSock;
71 |
72 | await _mongo.UpsertProductAsync(product);
73 |
74 | _logger.LogInformation("Vector generated for Cosmic Sock and saved in product catalog");
75 |
76 | }
77 | catch (Exception ex)
78 | {
79 |
80 | _logger.LogError(ex.Message);
81 | throw;
82 |
83 | }
84 | }
85 |
86 | public async Task RemoveProduct()
87 | {
88 |
89 | try
90 | {
91 | Product product = GetCosmicSock;
92 |
93 | await _mongo.DeleteProductAsync(product);
94 |
95 | _logger.LogInformation("Cosmic Sock Vector deleted and removed from product catalog");
96 |
97 | }
98 | catch (Exception ex)
99 | {
100 | _logger.LogError(ex.Message);
101 | throw;
102 |
103 | }
104 |
105 | }
106 |
107 | public Product GetCosmicSock
108 | {
109 | get => new Product(
110 | id: "00001",
111 | categoryId: "C48B4EF4-D352-4CD2-BCB8-CE89B7DFA642",
112 | categoryName: "Clothing, Socks",
113 | sku: "SO-R999-M",
114 | name: "Cosmic Racing Socks, M",
115 | description: "The product called Cosmic Racing Socks, M",
116 | price: 6.00,
117 | tags: new List
118 | {
119 | new Tag(id: "51CD93BF-098C-4C25-9829-4AD42046D038", name: "Tag-25"),
120 | new Tag(id: "5D24B427-1402-49DE-B79B-5A7013579FBC", name: "Tag-76"),
121 | new Tag(id: "D4EC9C09-75F3-4ADD-A6EB-ACDD12C648FA", name: "Tag-153")
122 | });
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/Vectorize/IngestAndVectorize.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Azure.Functions.Worker;
2 | using Microsoft.Extensions.Logging;
3 | using System.Net;
4 | using Microsoft.Azure.Functions.Worker.Http;
5 | using Azure.Storage.Blobs;
6 | using Azure.Storage.Blobs.Models;
7 | using SharedLib.Services;
8 |
9 |
10 | namespace Vectorize
11 | {
12 | public class IngestAndVectorize
13 | {
14 |
15 | private readonly MongoDbService _mongo;
16 | private readonly ILogger _logger;
17 |
18 | public IngestAndVectorize(MongoDbService mongo, ILoggerFactory loggerFactory)
19 | {
20 | _mongo = mongo;
21 | _logger = loggerFactory.CreateLogger();
22 | }
23 |
24 | [Function("IngestAndVectorize")]
25 | public async Task Run(
26 | [HttpTrigger(AuthorizationLevel.Anonymous, "post", "get", Route = null)] HttpRequestData req)
27 | {
28 | _logger.LogInformation("Ingest and Vectorize HTTP trigger function is processing a request.");
29 | try
30 | {
31 |
32 | // Ingest json data into MongoDB collections
33 | await IngestDataFromBlobStorageAsync();
34 |
35 |
36 | var response = req.CreateResponse(HttpStatusCode.OK);
37 | response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
38 | await response.WriteStringAsync("Ingest and Vectorize HTTP trigger function executed successfully.");
39 |
40 | return response;
41 | }
42 | catch (Exception ex)
43 | {
44 |
45 | var response = req.CreateResponse(HttpStatusCode.BadRequest);
46 | await response.WriteStringAsync(ex.ToString());
47 | return response;
48 |
49 | }
50 | }
51 |
52 | public async Task IngestDataFromBlobStorageAsync()
53 | {
54 |
55 |
56 | try
57 | {
58 | BlobContainerClient blobContainerClient = new BlobContainerClient(new Uri("https://cosmosdbcosmicworks.blob.core.windows.net/cosmic-works-mongo-vcore/"));
59 |
60 | //hard-coded here. In a real-world scenario, you would want to dynamically get the list of blobs in the container and iterate through them.
61 | //as well as drive all of the schema and meta-data from a configuration file.
62 | List blobIds = new List() { "products", "customers", "salesOrders" };
63 |
64 |
65 | foreach(string blobId in blobIds)
66 | {
67 | BlobClient blob = blobContainerClient.GetBlobClient($"{blobId}.json");
68 | if (await blob.ExistsAsync())
69 | {
70 | //Download and ingest products.json
71 | _logger.LogInformation($"Ingesting {blobId} data from blob storage.");
72 |
73 | BlobClient blobClient = blobContainerClient.GetBlobClient($"{blobId}.json");
74 | BlobDownloadStreamingResult blobResult = await blobClient.DownloadStreamingAsync();
75 |
76 | using (StreamReader pReader = new StreamReader(blobResult.Content))
77 | {
78 | string json = await pReader.ReadToEndAsync();
79 | await _mongo.ImportAndVectorizeAsync(blobId, json);
80 |
81 | }
82 |
83 | _logger.LogInformation($"{blobId} data ingestion complete.");
84 |
85 | }
86 | }
87 |
88 | }
89 | catch(Exception ex)
90 | {
91 | _logger.LogError($"Exception: IngestDataFromBlobStorageAsync(): {ex.Message}");
92 | throw;
93 | }
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Vectorize/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.Hosting;
4 | using Microsoft.Extensions.Logging;
5 | using Microsoft.Extensions.Options;
6 | using SharedLib.Options;
7 | using SharedLib.Services;
8 |
9 |
10 |
11 | var host = new HostBuilder()
12 | .ConfigureFunctionsWorkerDefaults(builder =>
13 | {
14 |
15 | builder.Services.AddLogging();
16 |
17 |
18 | builder.Services.AddOptions()
19 | .Configure((settings, configuration) =>
20 | {
21 | configuration.GetSection(nameof(OpenAi)).Bind(settings);
22 | });
23 |
24 |
25 |
26 | builder.Services.AddOptions()
27 | .Configure((settings, configuration) =>
28 | {
29 | configuration.GetSection(nameof(MongoDb)).Bind(settings);
30 | });
31 |
32 | })
33 | .ConfigureAppConfiguration(con =>
34 | {
35 | //con.AddUserSecrets(optional: true, reloadOnChange: true);
36 | con.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true);
37 | })
38 | .ConfigureServices(s =>
39 | {
40 |
41 | s.AddSingleton((provider) =>
42 | {
43 | var openAiOptions = provider.GetRequiredService>();
44 |
45 | if (openAiOptions is null)
46 | {
47 | throw new ArgumentException($"{nameof(IOptions)} was not resolved through dependency injection.");
48 | }
49 | else
50 | {
51 | return new OpenAiService
52 | (
53 | endpoint: openAiOptions.Value?.Endpoint ?? String.Empty,
54 | key: openAiOptions.Value?.Key ?? String.Empty,
55 | embeddingsDeployment: openAiOptions.Value?.EmbeddingsDeployment ?? String.Empty,
56 | completionsDeployment: openAiOptions.Value?.CompletionsDeployment ?? String.Empty,
57 | maxConversationTokens: openAiOptions.Value?.MaxConversationTokens ?? String.Empty,
58 | maxCompletionTokens: openAiOptions.Value?.MaxCompletionTokens ?? String.Empty,
59 | maxEmbeddingTokens: openAiOptions.Value?.MaxEmbeddingTokens ?? String.Empty,
60 | logger: provider.GetRequiredService>()
61 | );
62 | }
63 |
64 | });
65 |
66 | s.AddSingleton((provider) =>
67 | {
68 | var mongoOptions = provider.GetRequiredService>();
69 |
70 | if (mongoOptions is null)
71 | {
72 | throw new ArgumentException($"{nameof(IOptions)} was not resolved through dependency injection.");
73 | }
74 | else
75 | {
76 | return new MongoDbService
77 | (
78 | connection: mongoOptions.Value?.Connection ?? string.Empty,
79 | databaseName: mongoOptions.Value?.DatabaseName ?? string.Empty,
80 | collectionNames: mongoOptions.Value?.CollectionNames ?? string.Empty,
81 | maxVectorSearchResults: mongoOptions.Value?.MaxVectorSearchResults ?? string.Empty,
82 | vectorIndexType: mongoOptions.Value?.VectorIndexType ?? string.Empty,
83 | openAiService: provider.GetRequiredService(),
84 | logger: provider.GetRequiredService>()
85 | );
86 | }
87 | });
88 |
89 | })
90 | .Build();
91 |
92 | host.Run();
93 |
--------------------------------------------------------------------------------
/Vectorize/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Vectorize": {
4 | "commandName": "Project",
5 | "commandLineArgs": "--port 7026",
6 | "launchBrowser": false
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/Vectorize/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Azure.Functions.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Vectorize.Services;
4 | using Vectorize.Options;
5 | using Microsoft.Extensions.Options;
6 | using Microsoft.Extensions.Logging;
7 | using Microsoft.Extensions.Configuration;
8 |
9 | [assembly: FunctionsStartup(typeof(Vectorize.Startup))]
10 |
11 | namespace Vectorize
12 | {
13 | public class Startup: FunctionsStartup
14 | {
15 | public override void Configure(IFunctionsHostBuilder builder)
16 | {
17 |
18 | builder.Services.AddLogging();
19 |
20 |
21 | builder.Services.AddOptions()
22 | .Configure((settings, configuration) =>
23 | {
24 | configuration.GetSection(nameof(OpenAi)).Bind(settings);
25 | });
26 |
27 |
28 |
29 | builder.Services.AddOptions()
30 | .Configure((settings, configuration) =>
31 | {
32 | configuration.GetSection(nameof(MongoDb)).Bind(settings);
33 | });
34 |
35 |
36 | builder.Services.AddSingleton((provider) =>
37 | {
38 | var openAiOptions = provider.GetRequiredService>();
39 |
40 | if (openAiOptions is null)
41 | {
42 | throw new ArgumentException($"{nameof(IOptions)} was not resolved through dependency injection.");
43 | }
44 | else
45 | {
46 | return new OpenAiService
47 | (
48 | endpoint: openAiOptions.Value?.Endpoint ?? string.Empty,
49 | key: openAiOptions.Value?.Key ?? string.Empty,
50 | embeddingsDeployment: openAiOptions.Value?.EmbeddingsDeployment ?? string.Empty,
51 | maxTokens: openAiOptions.Value?.MaxTokens ?? string.Empty,
52 | logger: provider.GetRequiredService>()
53 | );
54 | }
55 |
56 | });
57 |
58 | builder.Services.AddSingleton((provider) =>
59 | {
60 | var mongoOptions = provider.GetRequiredService>();
61 |
62 | if(mongoOptions is null)
63 | {
64 | throw new ArgumentException($"{nameof(IOptions)} was not resolved through dependency injection.");
65 | }
66 | else
67 | {
68 | return new MongoDbService
69 | (
70 | connection: mongoOptions.Value?.Connection ?? string.Empty,
71 | databaseName: mongoOptions.Value?.DatabaseName ?? string.Empty,
72 | collectionNames: mongoOptions.Value?.CollectionNames ?? string.Empty,
73 | openAiService: provider.GetRequiredService(),
74 | logger: provider.GetRequiredService>()
75 | ); ;
76 | }
77 | });
78 |
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Vectorize/Vectorize.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net7.0
4 | v4
5 | enable
6 | enable
7 | true
8 | 11
9 | Exe
10 | 09a6738d-a2cb-4ec1-aa4e-72147992d5a7
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | PreserveNewest
34 |
35 |
36 | Always
37 | Never
38 |
39 |
40 |
41 |
42 | ExecutionContext
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Vectorize/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "logging": {
4 | "applicationInsights": {
5 | "samplingSettings": {
6 | "isEnabled": true,
7 | "excludedTypes": "Request"
8 | }
9 | },
10 | "logLevel": {
11 | "Vectorize": "Information"
12 | }
13 | },
14 | "extensions": {
15 | "cosmosDB": {
16 | "connectionMode": "Direct",
17 | "protocol": "Tcp"
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/Vectorize/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Values": {
3 | "AzureWebJobsStorage": "UseDevelopmentStorage=true",
4 | "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
5 | "OpenAi__Endpoint": "",
6 | "OpenAi__Key": "",
7 | "OpenAI__CompletionsDeployment": "",
8 | "OpenAi__EmbeddingsDeployment": "",
9 | "OpenAi__MaxConversationTokens": "1000",
10 | "OpenAi__MaxCompletionTokens": "4000",
11 | "OpenAi__MaxEmbeddingTokens": "8000",
12 | "MongoDb__Connection": "",
13 | "MongoDb__DatabaseName": "retaildb",
14 | "MongoDb__CollectionNames": "products, customers, salesOrders, completions",
15 | "MongoDb__MaxVectorSearchResults": "10",
16 | "MongoDb__VectorIndexType": "ivf" // ivf, hnsw
17 | },
18 | "IsEncrypted": false
19 | }
--------------------------------------------------------------------------------
/azuredeploy.bicep:
--------------------------------------------------------------------------------
1 | @description('Location where all resources will be deployed. This value defaults to the **East US** region.')
2 | @allowed([
3 | 'australiaeast'
4 | 'canadaeast'
5 | 'westeurope'
6 | 'francecentral'
7 | 'japaneast'
8 | 'swedencentral'
9 | 'switzerlandnorth'
10 | 'uksouth'
11 | 'eastus'
12 | 'eastus2'
13 | 'northcentralus'
14 | 'southcentralus'
15 | ])
16 | param location string = 'eastus'
17 |
18 | @description('Unique name for the deployed services below. Max length 15 characters, alphanumeric only:\r\n- Azure Cosmos DB for MongoDB vCore\r\n- Azure OpenAI Service\r\n- Azure App Service\r\n- Azure Functions\r\n\r\nThe name defaults to a unique string generated from the resource group identifier.\r\n')
19 | @maxLength(15)
20 | param name string = uniqueString(resourceGroup().id)
21 |
22 | @description('Specifies the SKU for the Azure App Service plan. Defaults to **B1**')
23 | @allowed([
24 | 'B1'
25 | 'S1'
26 | ])
27 | param appServiceSku string = 'B1'
28 |
29 | @description('MongoDB vCore user Name. No dashes.')
30 | param mongoDbUserName string
31 |
32 | @description('MongoDB vCore password. 8-256 characters, 3 of the following: lower case, upper case, numeric, symbol.')
33 | @minLength(8)
34 | @maxLength(256)
35 | @secure()
36 | param mongoDbPassword string
37 |
38 | @description('Git repository URL for the application source. This defaults to the [`Azure/Vector-Search-Ai-Assistant`](https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore.git) repository.')
39 | param appGitRepository string = 'https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore.git'
40 |
41 | @description('Git repository branch for the application source. This defaults to the [**main** branch of the `Azure/Vector-Search-Ai-Assistant-MongoDBvCore`](https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/tree/main) repository.')
42 | param appGetRepositoryBranch string = 'main'
43 |
44 | var openAiSettings = {
45 | name: '${name}-openai'
46 | sku: 'S0'
47 | maxConversationTokens: '100'
48 | maxCompletionTokens: '500'
49 | maxEmbeddingTokens: '8000'
50 | completionsModel: {
51 | name: 'gpt-35-turbo'
52 | version: '0301'
53 | deployment: {
54 | name: 'completions'
55 | }
56 | }
57 | embeddingsModel: {
58 | name: 'text-embedding-ada-002'
59 | version: '2'
60 | deployment: {
61 | name: 'embeddings'
62 | }
63 | }
64 | }
65 | var mongovCoreSettings = {
66 | mongoClusterName: '${name}-mongo'
67 | mongoClusterLogin: mongoDbUserName
68 | mongoClusterPassword: mongoDbPassword
69 | }
70 | var appServiceSettings = {
71 | plan: {
72 | name: '${name}-web-plan'
73 | sku: appServiceSku
74 | }
75 | web: {
76 | name: '${name}-web'
77 | git: {
78 | repo: appGitRepository
79 | branch: appGetRepositoryBranch
80 | }
81 | }
82 | function: {
83 | name: '${name}-function'
84 | git: {
85 | repo: appGitRepository
86 | branch: appGetRepositoryBranch
87 | }
88 | }
89 | }
90 |
91 | resource mongoCluster 'Microsoft.DocumentDB/mongoClusters@2023-03-01-preview' = {
92 | name: mongovCoreSettings.mongoClusterName
93 | location: location
94 | properties: {
95 | administratorLogin: mongovCoreSettings.mongoClusterLogin
96 | administratorLoginPassword: mongovCoreSettings.mongoClusterPassword
97 | serverVersion: '5.0'
98 | nodeGroupSpecs: [
99 | {
100 | kind: 'Shard'
101 | sku: 'M30'
102 | diskSizeGB: 128
103 | enableHa: false
104 | nodeCount: 1
105 | }
106 | ]
107 | }
108 | }
109 |
110 | resource mongoClusterAllowAzure 'Microsoft.DocumentDB/mongoClusters/firewallRules@2023-03-01-preview' = {
111 | parent: mongoCluster
112 | name: 'allowAzure'
113 | properties: {
114 | startIpAddress: '0.0.0.0'
115 | endIpAddress: '0.0.0.0'
116 | }
117 | }
118 |
119 | resource mongoClusterAllowAll 'Microsoft.DocumentDB/mongoClusters/firewallRules@2023-03-01-preview' = {
120 | parent: mongoCluster
121 | name: 'allowAll'
122 | properties: {
123 | startIpAddress: '0.0.0.0'
124 | endIpAddress: '255.255.255.255'
125 | }
126 | }
127 |
128 | resource openAiAccount 'Microsoft.CognitiveServices/accounts@2022-12-01' = {
129 | name: openAiSettings.name
130 | location: location
131 | sku: {
132 | name: openAiSettings.sku
133 | }
134 | kind: 'OpenAI'
135 | properties: {
136 | customSubDomainName: openAiSettings.name
137 | publicNetworkAccess: 'Enabled'
138 | }
139 | }
140 |
141 | resource embeddingsModelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2022-12-01' = {
142 | parent: openAiAccount
143 | name: openAiSettings.embeddingsModel.deployment.name
144 | properties: {
145 | model: {
146 | format: 'OpenAI'
147 | name: openAiSettings.embeddingsModel.name
148 | version: openAiSettings.embeddingsModel.version
149 | }
150 | scaleSettings: {
151 | scaleType: 'Standard'
152 | }
153 | }
154 | }
155 |
156 | resource completionsModelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2022-12-01' = {
157 | parent: openAiAccount
158 | name: openAiSettings.completionsModel.deployment.name
159 | properties: {
160 | model: {
161 | format: 'OpenAI'
162 | name: openAiSettings.completionsModel.name
163 | version: openAiSettings.completionsModel.version
164 | }
165 | scaleSettings: {
166 | scaleType: 'Standard'
167 | }
168 | }
169 | dependsOn: [
170 | embeddingsModelDeployment //this is necessary because OpenAI can only deploy one model at a time
171 | ]
172 | }
173 |
174 | resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
175 | name: appServiceSettings.plan.name
176 | location: location
177 | sku: {
178 | name: appServiceSettings.plan.sku
179 | }
180 | }
181 |
182 | resource appServiceWeb 'Microsoft.Web/sites@2022-03-01' = {
183 | name: appServiceSettings.web.name
184 | location: location
185 | properties: {
186 | serverFarmId: appServicePlan.id
187 | httpsOnly: true
188 | }
189 | }
190 |
191 | resource functionStorage 'Microsoft.Storage/storageAccounts@2021-09-01' = {
192 | name: '${name}fnstorage'
193 | location: location
194 | kind: 'Storage'
195 | sku: {
196 | name: 'Standard_LRS'
197 | }
198 | }
199 |
200 | resource appServiceFunction 'Microsoft.Web/sites@2022-03-01' = {
201 | name: appServiceSettings.function.name
202 | location: location
203 | kind: 'functionapp'
204 | properties: {
205 | serverFarmId: appServicePlan.id
206 | httpsOnly: true
207 | siteConfig: {
208 | alwaysOn: true
209 | }
210 | }
211 | }
212 |
213 | resource appServiceWebSettings 'Microsoft.Web/sites/config@2022-03-01' = {
214 | parent: appServiceWeb
215 | name: 'appsettings'
216 | kind: 'string'
217 | properties: {
218 | APPINSIGHTS_INSTRUMENTATIONKEY: insightsWeb.properties.InstrumentationKey
219 | OPENAI__ENDPOINT: openAiAccount.properties.endpoint
220 | OPENAI__KEY: openAiAccount.listKeys().key1
221 | OPENAI__EMBEDDINGSDEPLOYMENT: openAiSettings.embeddingsModel.deployment.name
222 | OPENAI__COMPLETIONSDEPLOYMENT: openAiSettings.completionsModel.deployment.name
223 | OPENAI__MAXCONVERSATIONTOKENS: openAiSettings.maxConversationTokens
224 | OPENAI__MAXCOMPLETIONTOKENS: openAiSettings.maxCompletionTokens
225 | OPENAI__MAXEMBEDDINGTOKENS: openAiSettings.maxEmbeddingTokens
226 | MONGODB__CONNECTION: 'mongodb+srv://${mongovCoreSettings.mongoClusterLogin}:${mongovCoreSettings.mongoClusterPassword}@${mongovCoreSettings.mongoClusterName}.mongocluster.cosmos.azure.com/?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000'
227 | MONGODB__DATABASENAME: 'retaildb'
228 | MONGODB__COLLECTIONNAMES: 'product,customer,vectors,completions'
229 | MONGODB__MAXVECTORSEARCHRESULTS: '10'
230 | MONGODB__VECTORINDEXTYPE: 'ivf'
231 | }
232 | dependsOn: [
233 | completionsModelDeployment
234 | embeddingsModelDeployment
235 | ]
236 | }
237 |
238 | resource appServiceFunctionSettings 'Microsoft.Web/sites/config@2022-03-01' = {
239 | parent: appServiceFunction
240 | name: 'appsettings'
241 | kind: 'string'
242 | properties: {
243 | AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${name}fnstorage;EndpointSuffix=core.windows.net;AccountKey=${functionStorage.listKeys().keys[0].value}'
244 | APPLICATIONINSIGHTS_CONNECTION_STRING: insightsFunction.properties.ConnectionString
245 | FUNCTIONS_EXTENSION_VERSION: '~4'
246 | FUNCTIONS_WORKER_RUNTIME: 'dotnet-isolated'
247 | OPENAI__ENDPOINT: openAiAccount.properties.endpoint
248 | OPENAI__KEY: openAiAccount.listKeys().key1
249 | OPENAI__EMBEDDINGSDEPLOYMENT: openAiSettings.embeddingsModel.deployment.name
250 | OPENAI__COMPLETIONSDEPLOYMENT: openAiSettings.completionsModel.deployment.name
251 | OPENAI__MAXCONVERSATIONTOKENS: openAiSettings.maxConversationTokens
252 | OPENAI__MAXCOMPLETIONTOKENS: openAiSettings.maxCompletionTokens
253 | OPENAI__MAXEMBEDDINGTOKENS: openAiSettings.maxEmbeddingTokens
254 | MONGODB__CONNECTION: 'mongodb+srv://${mongovCoreSettings.mongoClusterLogin}:${mongovCoreSettings.mongoClusterPassword}@${mongovCoreSettings.mongoClusterName}.mongocluster.cosmos.azure.com/?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000'
255 | MONGODB__DATABASENAME: 'retaildb'
256 | MONGODB__COLLECTIONNAMES: 'product,customer,vectors,completions'
257 | MONGODB__MAXVECTORSEARCHRESULTS: '10'
258 | MONGODB__VECTORINDEXTYPE: 'ivf'
259 | }
260 | dependsOn: [
261 | completionsModelDeployment
262 | embeddingsModelDeployment
263 | ]
264 | }
265 |
266 | resource appServiceWebSourceControl 'Microsoft.Web/sites/sourcecontrols@2021-03-01' = {
267 | parent: appServiceWeb
268 | name: 'web'
269 | properties: {
270 | repoUrl: appServiceSettings.web.git.repo
271 | branch: appServiceSettings.web.git.branch
272 | isManualIntegration: true
273 | }
274 | }
275 |
276 | resource appServiceFunctionSourceControl 'Microsoft.Web/sites/sourcecontrols@2021-03-01' = {
277 | parent: appServiceFunction
278 | name: 'web'
279 | properties: {
280 | repoUrl: appServiceSettings.web.git.repo
281 | branch: appServiceSettings.web.git.branch
282 | isManualIntegration: true
283 | }
284 | }
285 |
286 | resource insightsFunction 'Microsoft.Insights/components@2020-02-02' = {
287 | name: appServiceSettings.function.name
288 | location: location
289 | kind: 'web'
290 | properties: {
291 | Application_Type: 'web'
292 | }
293 | }
294 |
295 | resource insightsWeb 'Microsoft.Insights/components@2020-02-02' = {
296 | name: appServiceSettings.web.name
297 | location: location
298 | kind: 'web'
299 | properties: {
300 | Application_Type: 'web'
301 | }
302 | }
303 |
304 | output deployedUrl string = appServiceWeb.properties.defaultHostName
305 |
--------------------------------------------------------------------------------
/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "_generator": {
6 | "name": "bicep",
7 | "version": "0.24.24.22086",
8 | "templateHash": "8722566338792790814"
9 | }
10 | },
11 | "parameters": {
12 | "location": {
13 | "type": "string",
14 | "defaultValue": "eastus",
15 | "allowedValues": [
16 | "australiaeast",
17 | "canadaeast",
18 | "westeurope",
19 | "francecentral",
20 | "japaneast",
21 | "swedencentral",
22 | "switzerlandnorth",
23 | "uksouth",
24 | "eastus",
25 | "eastus2",
26 | "northcentralus",
27 | "southcentralus"
28 | ],
29 | "metadata": {
30 | "description": "Location where all resources will be deployed. This value defaults to the **East US** region."
31 | }
32 | },
33 | "name": {
34 | "type": "string",
35 | "defaultValue": "[uniqueString(resourceGroup().id)]",
36 | "maxLength": 15,
37 | "metadata": {
38 | "description": "Unique name for the deployed services below. Max length 15 characters, alphanumeric only:\r\n- Azure Cosmos DB for MongoDB vCore\r\n- Azure OpenAI Service\r\n- Azure App Service\r\n- Azure Functions\r\n\r\nThe name defaults to a unique string generated from the resource group identifier.\r\n"
39 | }
40 | },
41 | "appServiceSku": {
42 | "type": "string",
43 | "defaultValue": "B1",
44 | "allowedValues": [
45 | "B1",
46 | "S1"
47 | ],
48 | "metadata": {
49 | "description": "Specifies the SKU for the Azure App Service plan. Defaults to **B1**"
50 | }
51 | },
52 | "mongoDbUserName": {
53 | "type": "string",
54 | "metadata": {
55 | "description": "MongoDB vCore user Name. No dashes."
56 | }
57 | },
58 | "mongoDbPassword": {
59 | "type": "securestring",
60 | "minLength": 8,
61 | "maxLength": 256,
62 | "metadata": {
63 | "description": "MongoDB vCore password. 8-256 characters, 3 of the following: lower case, upper case, numeric, symbol."
64 | }
65 | },
66 | "appGitRepository": {
67 | "type": "string",
68 | "defaultValue": "https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore.git",
69 | "metadata": {
70 | "description": "Git repository URL for the application source. This defaults to the [`Azure/Vector-Search-Ai-Assistant`](https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore.git) repository."
71 | }
72 | },
73 | "appGetRepositoryBranch": {
74 | "type": "string",
75 | "defaultValue": "main",
76 | "metadata": {
77 | "description": "Git repository branch for the application source. This defaults to the [**main** branch of the `Azure/Vector-Search-Ai-Assistant-MongoDBvCore`](https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/tree/main) repository."
78 | }
79 | }
80 | },
81 | "variables": {
82 | "openAiSettings": {
83 | "name": "[format('{0}-openai', parameters('name'))]",
84 | "sku": "S0",
85 | "maxConversationTokens": "100",
86 | "maxCompletionTokens": "500",
87 | "maxEmbeddingTokens": "8000",
88 | "completionsModel": {
89 | "name": "gpt-35-turbo",
90 | "version": "0301",
91 | "deployment": {
92 | "name": "completions"
93 | }
94 | },
95 | "embeddingsModel": {
96 | "name": "text-embedding-ada-002",
97 | "version": "2",
98 | "deployment": {
99 | "name": "embeddings"
100 | }
101 | }
102 | },
103 | "mongovCoreSettings": {
104 | "mongoClusterName": "[format('{0}-mongo', parameters('name'))]",
105 | "mongoClusterLogin": "[parameters('mongoDbUserName')]",
106 | "mongoClusterPassword": "[parameters('mongoDbPassword')]"
107 | },
108 | "appServiceSettings": {
109 | "plan": {
110 | "name": "[format('{0}-web-plan', parameters('name'))]",
111 | "sku": "[parameters('appServiceSku')]"
112 | },
113 | "web": {
114 | "name": "[format('{0}-web', parameters('name'))]",
115 | "git": {
116 | "repo": "[parameters('appGitRepository')]",
117 | "branch": "[parameters('appGetRepositoryBranch')]"
118 | }
119 | },
120 | "function": {
121 | "name": "[format('{0}-function', parameters('name'))]",
122 | "git": {
123 | "repo": "[parameters('appGitRepository')]",
124 | "branch": "[parameters('appGetRepositoryBranch')]"
125 | }
126 | }
127 | }
128 | },
129 | "resources": [
130 | {
131 | "type": "Microsoft.DocumentDB/mongoClusters",
132 | "apiVersion": "2023-03-01-preview",
133 | "name": "[variables('mongovCoreSettings').mongoClusterName]",
134 | "location": "[parameters('location')]",
135 | "properties": {
136 | "administratorLogin": "[variables('mongovCoreSettings').mongoClusterLogin]",
137 | "administratorLoginPassword": "[variables('mongovCoreSettings').mongoClusterPassword]",
138 | "serverVersion": "5.0",
139 | "nodeGroupSpecs": [
140 | {
141 | "kind": "Shard",
142 | "sku": "M30",
143 | "diskSizeGB": 128,
144 | "enableHa": false,
145 | "nodeCount": 1
146 | }
147 | ]
148 | }
149 | },
150 | {
151 | "type": "Microsoft.DocumentDB/mongoClusters/firewallRules",
152 | "apiVersion": "2023-03-01-preview",
153 | "name": "[format('{0}/{1}', variables('mongovCoreSettings').mongoClusterName, 'allowAzure')]",
154 | "properties": {
155 | "startIpAddress": "0.0.0.0",
156 | "endIpAddress": "0.0.0.0"
157 | },
158 | "dependsOn": [
159 | "[resourceId('Microsoft.DocumentDB/mongoClusters', variables('mongovCoreSettings').mongoClusterName)]"
160 | ]
161 | },
162 | {
163 | "type": "Microsoft.DocumentDB/mongoClusters/firewallRules",
164 | "apiVersion": "2023-03-01-preview",
165 | "name": "[format('{0}/{1}', variables('mongovCoreSettings').mongoClusterName, 'allowAll')]",
166 | "properties": {
167 | "startIpAddress": "0.0.0.0",
168 | "endIpAddress": "255.255.255.255"
169 | },
170 | "dependsOn": [
171 | "[resourceId('Microsoft.DocumentDB/mongoClusters', variables('mongovCoreSettings').mongoClusterName)]"
172 | ]
173 | },
174 | {
175 | "type": "Microsoft.CognitiveServices/accounts",
176 | "apiVersion": "2022-12-01",
177 | "name": "[variables('openAiSettings').name]",
178 | "location": "[parameters('location')]",
179 | "sku": {
180 | "name": "[variables('openAiSettings').sku]"
181 | },
182 | "kind": "OpenAI",
183 | "properties": {
184 | "customSubDomainName": "[variables('openAiSettings').name]",
185 | "publicNetworkAccess": "Enabled"
186 | }
187 | },
188 | {
189 | "type": "Microsoft.CognitiveServices/accounts/deployments",
190 | "apiVersion": "2022-12-01",
191 | "name": "[format('{0}/{1}', variables('openAiSettings').name, variables('openAiSettings').embeddingsModel.deployment.name)]",
192 | "properties": {
193 | "model": {
194 | "format": "OpenAI",
195 | "name": "[variables('openAiSettings').embeddingsModel.name]",
196 | "version": "[variables('openAiSettings').embeddingsModel.version]"
197 | },
198 | "scaleSettings": {
199 | "scaleType": "Standard"
200 | }
201 | },
202 | "dependsOn": [
203 | "[resourceId('Microsoft.CognitiveServices/accounts', variables('openAiSettings').name)]"
204 | ]
205 | },
206 | {
207 | "type": "Microsoft.CognitiveServices/accounts/deployments",
208 | "apiVersion": "2022-12-01",
209 | "name": "[format('{0}/{1}', variables('openAiSettings').name, variables('openAiSettings').completionsModel.deployment.name)]",
210 | "properties": {
211 | "model": {
212 | "format": "OpenAI",
213 | "name": "[variables('openAiSettings').completionsModel.name]",
214 | "version": "[variables('openAiSettings').completionsModel.version]"
215 | },
216 | "scaleSettings": {
217 | "scaleType": "Standard"
218 | }
219 | },
220 | "dependsOn": [
221 | "[resourceId('Microsoft.CognitiveServices/accounts/deployments', variables('openAiSettings').name, variables('openAiSettings').embeddingsModel.deployment.name)]",
222 | "[resourceId('Microsoft.CognitiveServices/accounts', variables('openAiSettings').name)]"
223 | ]
224 | },
225 | {
226 | "type": "Microsoft.Web/serverfarms",
227 | "apiVersion": "2022-03-01",
228 | "name": "[variables('appServiceSettings').plan.name]",
229 | "location": "[parameters('location')]",
230 | "sku": {
231 | "name": "[variables('appServiceSettings').plan.sku]"
232 | }
233 | },
234 | {
235 | "type": "Microsoft.Web/sites",
236 | "apiVersion": "2022-03-01",
237 | "name": "[variables('appServiceSettings').web.name]",
238 | "location": "[parameters('location')]",
239 | "properties": {
240 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServiceSettings').plan.name)]",
241 | "httpsOnly": true
242 | },
243 | "dependsOn": [
244 | "[resourceId('Microsoft.Web/serverfarms', variables('appServiceSettings').plan.name)]"
245 | ]
246 | },
247 | {
248 | "type": "Microsoft.Storage/storageAccounts",
249 | "apiVersion": "2021-09-01",
250 | "name": "[format('{0}fnstorage', parameters('name'))]",
251 | "location": "[parameters('location')]",
252 | "kind": "Storage",
253 | "sku": {
254 | "name": "Standard_LRS"
255 | }
256 | },
257 | {
258 | "type": "Microsoft.Web/sites",
259 | "apiVersion": "2022-03-01",
260 | "name": "[variables('appServiceSettings').function.name]",
261 | "location": "[parameters('location')]",
262 | "kind": "functionapp",
263 | "properties": {
264 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServiceSettings').plan.name)]",
265 | "httpsOnly": true,
266 | "siteConfig": {
267 | "alwaysOn": true
268 | }
269 | },
270 | "dependsOn": [
271 | "[resourceId('Microsoft.Web/serverfarms', variables('appServiceSettings').plan.name)]"
272 | ]
273 | },
274 | {
275 | "type": "Microsoft.Web/sites/config",
276 | "apiVersion": "2022-03-01",
277 | "name": "[format('{0}/{1}', variables('appServiceSettings').web.name, 'appsettings')]",
278 | "kind": "string",
279 | "properties": {
280 | "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.Insights/components', variables('appServiceSettings').web.name), '2020-02-02').InstrumentationKey]",
281 | "OPENAI__ENDPOINT": "[reference(resourceId('Microsoft.CognitiveServices/accounts', variables('openAiSettings').name), '2022-12-01').endpoint]",
282 | "OPENAI__KEY": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', variables('openAiSettings').name), '2022-12-01').key1]",
283 | "OPENAI__EMBEDDINGSDEPLOYMENT": "[variables('openAiSettings').embeddingsModel.deployment.name]",
284 | "OPENAI__COMPLETIONSDEPLOYMENT": "[variables('openAiSettings').completionsModel.deployment.name]",
285 | "OPENAI__MAXCONVERSATIONTOKENS": "[variables('openAiSettings').maxConversationTokens]",
286 | "OPENAI__MAXCOMPLETIONTOKENS": "[variables('openAiSettings').maxCompletionTokens]",
287 | "OPENAI__MAXEMBEDDINGTOKENS": "[variables('openAiSettings').maxEmbeddingTokens]",
288 | "MONGODB__CONNECTION": "[format('mongodb+srv://{0}:{1}@{2}.mongocluster.cosmos.azure.com/?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000', variables('mongovCoreSettings').mongoClusterLogin, variables('mongovCoreSettings').mongoClusterPassword, variables('mongovCoreSettings').mongoClusterName)]",
289 | "MONGODB__DATABASENAME": "retaildb",
290 | "MONGODB__COLLECTIONNAMES": "product,customer,vectors,completions",
291 | "MONGODB__MAXVECTORSEARCHRESULTS": "10",
292 | "MONGODB__VECTORINDEXTYPE": "ivf"
293 | },
294 | "dependsOn": [
295 | "[resourceId('Microsoft.Web/sites', variables('appServiceSettings').web.name)]",
296 | "[resourceId('Microsoft.CognitiveServices/accounts/deployments', variables('openAiSettings').name, variables('openAiSettings').completionsModel.deployment.name)]",
297 | "[resourceId('Microsoft.CognitiveServices/accounts/deployments', variables('openAiSettings').name, variables('openAiSettings').embeddingsModel.deployment.name)]",
298 | "[resourceId('Microsoft.Insights/components', variables('appServiceSettings').web.name)]",
299 | "[resourceId('Microsoft.CognitiveServices/accounts', variables('openAiSettings').name)]"
300 | ]
301 | },
302 | {
303 | "type": "Microsoft.Web/sites/config",
304 | "apiVersion": "2022-03-01",
305 | "name": "[format('{0}/{1}', variables('appServiceSettings').function.name, 'appsettings')]",
306 | "kind": "string",
307 | "properties": {
308 | "AzureWebJobsStorage": "[format('DefaultEndpointsProtocol=https;AccountName={0}fnstorage;EndpointSuffix=core.windows.net;AccountKey={1}', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', format('{0}fnstorage', parameters('name'))), '2021-09-01').keys[0].value)]",
309 | "APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference(resourceId('Microsoft.Insights/components', variables('appServiceSettings').function.name), '2020-02-02').ConnectionString]",
310 | "FUNCTIONS_EXTENSION_VERSION": "~4",
311 | "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
312 | "OPENAI__ENDPOINT": "[reference(resourceId('Microsoft.CognitiveServices/accounts', variables('openAiSettings').name), '2022-12-01').endpoint]",
313 | "OPENAI__KEY": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', variables('openAiSettings').name), '2022-12-01').key1]",
314 | "OPENAI__EMBEDDINGSDEPLOYMENT": "[variables('openAiSettings').embeddingsModel.deployment.name]",
315 | "OPENAI__COMPLETIONSDEPLOYMENT": "[variables('openAiSettings').completionsModel.deployment.name]",
316 | "OPENAI__MAXCONVERSATIONTOKENS": "[variables('openAiSettings').maxConversationTokens]",
317 | "OPENAI__MAXCOMPLETIONTOKENS": "[variables('openAiSettings').maxCompletionTokens]",
318 | "OPENAI__MAXEMBEDDINGTOKENS": "[variables('openAiSettings').maxEmbeddingTokens]",
319 | "MONGODB__CONNECTION": "[format('mongodb+srv://{0}:{1}@{2}.mongocluster.cosmos.azure.com/?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000', variables('mongovCoreSettings').mongoClusterLogin, variables('mongovCoreSettings').mongoClusterPassword, variables('mongovCoreSettings').mongoClusterName)]",
320 | "MONGODB__DATABASENAME": "retaildb",
321 | "MONGODB__COLLECTIONNAMES": "product,customer,vectors,completions",
322 | "MONGODB__MAXVECTORSEARCHRESULTS": "10",
323 | "MONGODB__VECTORINDEXTYPE": "ivf"
324 | },
325 | "dependsOn": [
326 | "[resourceId('Microsoft.Web/sites', variables('appServiceSettings').function.name)]",
327 | "[resourceId('Microsoft.CognitiveServices/accounts/deployments', variables('openAiSettings').name, variables('openAiSettings').completionsModel.deployment.name)]",
328 | "[resourceId('Microsoft.CognitiveServices/accounts/deployments', variables('openAiSettings').name, variables('openAiSettings').embeddingsModel.deployment.name)]",
329 | "[resourceId('Microsoft.Storage/storageAccounts', format('{0}fnstorage', parameters('name')))]",
330 | "[resourceId('Microsoft.Insights/components', variables('appServiceSettings').function.name)]",
331 | "[resourceId('Microsoft.CognitiveServices/accounts', variables('openAiSettings').name)]"
332 | ]
333 | },
334 | {
335 | "type": "Microsoft.Web/sites/sourcecontrols",
336 | "apiVersion": "2021-03-01",
337 | "name": "[format('{0}/{1}', variables('appServiceSettings').web.name, 'web')]",
338 | "properties": {
339 | "repoUrl": "[variables('appServiceSettings').web.git.repo]",
340 | "branch": "[variables('appServiceSettings').web.git.branch]",
341 | "isManualIntegration": true
342 | },
343 | "dependsOn": [
344 | "[resourceId('Microsoft.Web/sites', variables('appServiceSettings').web.name)]"
345 | ]
346 | },
347 | {
348 | "type": "Microsoft.Web/sites/sourcecontrols",
349 | "apiVersion": "2021-03-01",
350 | "name": "[format('{0}/{1}', variables('appServiceSettings').function.name, 'web')]",
351 | "properties": {
352 | "repoUrl": "[variables('appServiceSettings').web.git.repo]",
353 | "branch": "[variables('appServiceSettings').web.git.branch]",
354 | "isManualIntegration": true
355 | },
356 | "dependsOn": [
357 | "[resourceId('Microsoft.Web/sites', variables('appServiceSettings').function.name)]"
358 | ]
359 | },
360 | {
361 | "type": "Microsoft.Insights/components",
362 | "apiVersion": "2020-02-02",
363 | "name": "[variables('appServiceSettings').function.name]",
364 | "location": "[parameters('location')]",
365 | "kind": "web",
366 | "properties": {
367 | "Application_Type": "web"
368 | }
369 | },
370 | {
371 | "type": "Microsoft.Insights/components",
372 | "apiVersion": "2020-02-02",
373 | "name": "[variables('appServiceSettings').web.name]",
374 | "location": "[parameters('location')]",
375 | "kind": "web",
376 | "properties": {
377 | "Application_Type": "web"
378 | }
379 | }
380 | ],
381 | "outputs": {
382 | "deployedUrl": {
383 | "type": "string",
384 | "value": "[reference(resourceId('Microsoft.Web/sites', variables('appServiceSettings').web.name), '2022-03-01').defaultHostName]"
385 | }
386 | }
387 | }
--------------------------------------------------------------------------------
/azuredeploynoaoai.bicep:
--------------------------------------------------------------------------------
1 | @description('Location where all resources will be deployed. This value defaults to the **East US** region.')
2 | @allowed([
3 | 'australiaeast'
4 | 'canadaeast'
5 | 'westeurope'
6 | 'francecentral'
7 | 'japaneast'
8 | 'swedencentral'
9 | 'switzerlandnorth'
10 | 'uksouth'
11 | 'eastus'
12 | 'eastus2'
13 | 'northcentralus'
14 | 'southcentralus'
15 | ])
16 | param location string = 'eastus'
17 |
18 | @description('Unique name for the deployed services below. Max length 15 characters, alphanumeric only:\r\n- Azure Cosmos DB for MongoDB vCore\r\n- Azure App Service\r\n- Azure Functions\r\n\r\nThe name defaults to a unique string generated from the resource group identifier.\r\n')
19 | @maxLength(15)
20 | param name string = uniqueString(resourceGroup().id)
21 |
22 | @description('Specifies the SKU for the Azure App Service plan. Defaults to **B1**')
23 | @allowed([
24 | 'B1'
25 | 'S1'
26 | ])
27 | param appServiceSku string = 'B1'
28 |
29 | @description('MongoDB vCore user Name. No dashes.')
30 | param mongoDbUserName string
31 |
32 | @description('MongoDB vCore password. 8-256 characters, 3 of the following: lower case, upper case, numeric, symbol.')
33 | @minLength(8)
34 | @maxLength(256)
35 | @secure()
36 | param mongoDbPassword string
37 |
38 | @description('Specifies the Azure OpenAI account name.')
39 | param openAiAccountName string
40 |
41 | @description('Specifies the key for Azure OpenAI account.')
42 | @secure()
43 | param openAiAccountKey string
44 |
45 | @description('Specifies the DEPLOYMENT NAME for the GPT model in your Azure OpenAI account.')
46 | param openAiCompletionsModelDeploymentName string
47 |
48 | @description('Specifies the DEPLOYMENT NAME for the embbeddings model in you Azure OpenAI account.')
49 | param openAiEmbeddingsDeploymentName string
50 |
51 | @description('Git repository URL for the application source. This defaults to the [`Azure/Vector-Search-Ai-Assistant`](https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore.git) repository.')
52 | param appGitRepository string = 'https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore.git'
53 |
54 | @description('Git repository branch for the application source. This defaults to the [**main** branch of the `Azure/Vector-Search-Ai-Assistant-MongoDBvCore`](https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/tree/main) repository.')
55 | param appGetRepositoryBranch string = 'main'
56 |
57 | var openAiSettings = {
58 | accountName: openAiAccountName
59 | accountKey: openAiAccountKey
60 | endPoint: 'https://${openAiAccountName}.openai.azure.com/'
61 | maxConversationTokens: '100'
62 | maxCompletionTokens: '500'
63 | maxEmbeddingTokens: '8000'
64 | completionsModel: {
65 | deployment: {
66 | name: openAiCompletionsModelDeploymentName
67 | }
68 | }
69 | embeddingsModel: {
70 | deployment: {
71 | name: openAiEmbeddingsDeploymentName
72 | }
73 | }
74 | }
75 | var mongovCoreSettings = {
76 | mongoClusterName: '${name}-mongo'
77 | mongoClusterLogin: mongoDbUserName
78 | mongoClusterPassword: mongoDbPassword
79 | }
80 | var appServiceSettings = {
81 | plan: {
82 | name: '${name}-web-plan'
83 | sku: appServiceSku
84 | }
85 | web: {
86 | name: '${name}-web'
87 | git: {
88 | repo: appGitRepository
89 | branch: appGetRepositoryBranch
90 | }
91 | }
92 | function: {
93 | name: '${name}-function'
94 | git: {
95 | repo: appGitRepository
96 | branch: appGetRepositoryBranch
97 | }
98 | }
99 | }
100 |
101 | resource mongoCluster 'Microsoft.DocumentDB/mongoClusters@2023-09-15-preview' = {
102 | name: mongovCoreSettings.mongoClusterName
103 | location: location
104 | properties: {
105 | administratorLogin: mongovCoreSettings.mongoClusterLogin
106 | administratorLoginPassword: mongovCoreSettings.mongoClusterPassword
107 | serverVersion: '5.0'
108 | nodeGroupSpecs: [
109 | {
110 | kind: 'Shard'
111 | sku: 'M30'
112 | diskSizeGB: 128
113 | enableHa: false
114 | nodeCount: 1
115 | }
116 | ]
117 | }
118 | }
119 |
120 | resource mongoClusterAllowAzure 'Microsoft.DocumentDB/mongoClusters/firewallRules@2023-09-15-preview' = {
121 | parent: mongoCluster
122 | name: 'allowAzure'
123 | properties: {
124 | startIpAddress: '0.0.0.0'
125 | endIpAddress: '0.0.0.0'
126 | }
127 | }
128 |
129 | resource mongoClusterAllowAll 'Microsoft.DocumentDB/mongoClusters/firewallRules@2023-09-15-preview' = {
130 | parent: mongoCluster
131 | name: 'allowAll'
132 | properties: {
133 | startIpAddress: '0.0.0.0'
134 | endIpAddress: '255.255.255.255'
135 | }
136 | }
137 |
138 | resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
139 | name: appServiceSettings.plan.name
140 | location: location
141 | sku: {
142 | name: appServiceSettings.plan.sku
143 | }
144 | }
145 |
146 | resource appServiceWeb 'Microsoft.Web/sites@2022-03-01' = {
147 | name: appServiceSettings.web.name
148 | location: location
149 | properties: {
150 | serverFarmId: appServicePlan.id
151 | httpsOnly: true
152 | }
153 | }
154 |
155 | resource functionStorage 'Microsoft.Storage/storageAccounts@2021-09-01' = {
156 | name: '${name}fnstorage'
157 | location: location
158 | kind: 'Storage'
159 | sku: {
160 | name: 'Standard_LRS'
161 | }
162 | }
163 |
164 | resource appServiceFunction 'Microsoft.Web/sites@2022-03-01' = {
165 | name: appServiceSettings.function.name
166 | location: location
167 | kind: 'functionapp'
168 | properties: {
169 | serverFarmId: appServicePlan.id
170 | httpsOnly: true
171 | siteConfig: {
172 | alwaysOn: true
173 | }
174 | }
175 | }
176 |
177 | resource appServiceWebSettings 'Microsoft.Web/sites/config@2022-03-01' = {
178 | parent: appServiceWeb
179 | name: 'appsettings'
180 | kind: 'string'
181 | properties: {
182 | APPINSIGHTS_INSTRUMENTATIONKEY: insightsWeb.properties.InstrumentationKey
183 | OPENAI__ENDPOINT: openAiSettings.endPoint
184 | OPENAI__KEY: openAiSettings.accountKey
185 | OPENAI__EMBEDDINGSDEPLOYMENT: openAiSettings.embeddingsModel.deployment.name
186 | OPENAI__COMPLETIONSDEPLOYMENT: openAiSettings.completionsModel.deployment.name
187 | OPENAI__MAXCONVERSATIONTOKENS: openAiSettings.maxConversationTokens
188 | OPENAI__MAXCOMPLETIONTOKENS: openAiSettings.maxCompletionTokens
189 | MONGODB__CONNECTION: 'mongodb+srv://${mongovCoreSettings.mongoClusterLogin}:${mongovCoreSettings.mongoClusterPassword}@${mongovCoreSettings.mongoClusterName}.mongocluster.cosmos.azure.com/?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000'
190 | MONGODB__DATABASENAME: 'retaildb'
191 | MONGODB__COLLECTIONNAMES: 'product'
192 | MONGODB__MAXVECTORSEARCHRESULTS: '10'
193 | MONGODB__VECTORINDEXTYPE: 'ivf'
194 | }
195 | }
196 |
197 | resource appServiceFunctionSettings 'Microsoft.Web/sites/config@2022-03-01' = {
198 | parent: appServiceFunction
199 | name: 'appsettings'
200 | kind: 'string'
201 | properties: {
202 | AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${name}fnstorage;EndpointSuffix=core.windows.net;AccountKey=${functionStorage.listKeys().keys[0].value}'
203 | APPLICATIONINSIGHTS_CONNECTION_STRING: insightsFunction.properties.ConnectionString
204 | FUNCTIONS_EXTENSION_VERSION: '~4'
205 | FUNCTIONS_WORKER_RUNTIME: 'dotnet-isolated'
206 | OPENAI__ENDPOINT: openAiSettings.endPoint
207 | OPENAI__KEY: openAiSettings.accountKey
208 | OPENAI__EMBEDDINGSDEPLOYMENT: openAiSettings.embeddingsModel.deployment.name
209 | OPENAI__COMPLETIONSDEPLOYMENT: openAiSettings.completionsModel.deployment.name
210 | OPENAI__MAXCONVERSATIONTOKENS: openAiSettings.maxConversationTokens
211 | OPENAI__MAXCOMPLETIONTOKENS: openAiSettings.maxCompletionTokens
212 | OPENAI__MAXEMBEDDINGTOKENS: openAiSettings.maxEmbeddingTokens
213 | MONGODB__CONNECTION: 'mongodb+srv://${mongovCoreSettings.mongoClusterLogin}:${mongovCoreSettings.mongoClusterPassword}@${mongovCoreSettings.mongoClusterName}.mongocluster.cosmos.azure.com/?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000'
214 | MONGODB__DATABASENAME: 'retaildb'
215 | MONGODB__COLLECTIONNAMES: 'product,customer,vectors,completions'
216 | MONGODB__MAXVECTORSEARCHRESULTS: '10'
217 | MONGODB__VECTORINDEXTYPE: 'ivf'
218 | }
219 | }
220 |
221 | resource appServiceWebSourceControl 'Microsoft.Web/sites/sourcecontrols@2021-03-01' = {
222 | parent: appServiceWeb
223 | name: 'web'
224 | properties: {
225 | repoUrl: appServiceSettings.web.git.repo
226 | branch: appServiceSettings.web.git.branch
227 | isManualIntegration: true
228 | }
229 | }
230 |
231 | resource appServiceFunctionSourceControl 'Microsoft.Web/sites/sourcecontrols@2021-03-01' = {
232 | parent: appServiceFunction
233 | name: 'web'
234 | properties: {
235 | repoUrl: appServiceSettings.web.git.repo
236 | branch: appServiceSettings.web.git.branch
237 | isManualIntegration: true
238 | }
239 | }
240 |
241 | resource insightsFunction 'Microsoft.Insights/components@2020-02-02' = {
242 | name: appServiceSettings.function.name
243 | location: location
244 | kind: 'web'
245 | properties: {
246 | Application_Type: 'web'
247 | }
248 | }
249 |
250 | resource insightsWeb 'Microsoft.Insights/components@2020-02-02' = {
251 | name: appServiceSettings.web.name
252 | location: location
253 | kind: 'web'
254 | properties: {
255 | Application_Type: 'web'
256 | }
257 | }
258 |
259 | output deployedUrl string = appServiceWeb.properties.defaultHostName
260 |
--------------------------------------------------------------------------------
/azuredeploynoaoai.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "_generator": {
6 | "name": "bicep",
7 | "version": "0.24.24.22086",
8 | "templateHash": "1851409976161202992"
9 | }
10 | },
11 | "parameters": {
12 | "location": {
13 | "type": "string",
14 | "defaultValue": "eastus",
15 | "allowedValues": [
16 | "australiaeast",
17 | "canadaeast",
18 | "westeurope",
19 | "francecentral",
20 | "japaneast",
21 | "swedencentral",
22 | "switzerlandnorth",
23 | "uksouth",
24 | "eastus",
25 | "eastus2",
26 | "northcentralus",
27 | "southcentralus"
28 | ],
29 | "metadata": {
30 | "description": "Location where all resources will be deployed. This value defaults to the **East US** region."
31 | }
32 | },
33 | "name": {
34 | "type": "string",
35 | "defaultValue": "[uniqueString(resourceGroup().id)]",
36 | "maxLength": 15,
37 | "metadata": {
38 | "description": "Unique name for the deployed services below. Max length 15 characters, alphanumeric only:\r\n- Azure Cosmos DB for MongoDB vCore\r\n- Azure App Service\r\n- Azure Functions\r\n\r\nThe name defaults to a unique string generated from the resource group identifier.\r\n"
39 | }
40 | },
41 | "appServiceSku": {
42 | "type": "string",
43 | "defaultValue": "B1",
44 | "allowedValues": [
45 | "B1",
46 | "S1"
47 | ],
48 | "metadata": {
49 | "description": "Specifies the SKU for the Azure App Service plan. Defaults to **B1**"
50 | }
51 | },
52 | "mongoDbUserName": {
53 | "type": "string",
54 | "metadata": {
55 | "description": "MongoDB vCore user Name. No dashes."
56 | }
57 | },
58 | "mongoDbPassword": {
59 | "type": "securestring",
60 | "minLength": 8,
61 | "maxLength": 256,
62 | "metadata": {
63 | "description": "MongoDB vCore password. 8-256 characters, 3 of the following: lower case, upper case, numeric, symbol."
64 | }
65 | },
66 | "openAiAccountName": {
67 | "type": "string",
68 | "metadata": {
69 | "description": "Specifies the Azure OpenAI account name."
70 | }
71 | },
72 | "openAiAccountKey": {
73 | "type": "securestring",
74 | "metadata": {
75 | "description": "Specifies the key for Azure OpenAI account."
76 | }
77 | },
78 | "openAiCompletionsModelDeploymentName": {
79 | "type": "string",
80 | "metadata": {
81 | "description": "Specifies the DEPLOYMENT NAME for the GPT model in your Azure OpenAI account."
82 | }
83 | },
84 | "openAiEmbeddingsDeploymentName": {
85 | "type": "string",
86 | "metadata": {
87 | "description": "Specifies the DEPLOYMENT NAME for the embbeddings model in you Azure OpenAI account."
88 | }
89 | },
90 | "appGitRepository": {
91 | "type": "string",
92 | "defaultValue": "https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore.git",
93 | "metadata": {
94 | "description": "Git repository URL for the application source. This defaults to the [`Azure/Vector-Search-Ai-Assistant`](https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore.git) repository."
95 | }
96 | },
97 | "appGetRepositoryBranch": {
98 | "type": "string",
99 | "defaultValue": "main",
100 | "metadata": {
101 | "description": "Git repository branch for the application source. This defaults to the [**main** branch of the `Azure/Vector-Search-Ai-Assistant-MongoDBvCore`](https://github.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/tree/main) repository."
102 | }
103 | }
104 | },
105 | "variables": {
106 | "openAiSettings": {
107 | "accountName": "[parameters('openAiAccountName')]",
108 | "accountKey": "[parameters('openAiAccountKey')]",
109 | "endPoint": "[format('https://{0}.openai.azure.com/', parameters('openAiAccountName'))]",
110 | "maxConversationTokens": "100",
111 | "maxCompletionTokens": "500",
112 | "maxEmbeddingTokens": "8000",
113 | "completionsModel": {
114 | "deployment": {
115 | "name": "[parameters('openAiCompletionsModelDeploymentName')]"
116 | }
117 | },
118 | "embeddingsModel": {
119 | "deployment": {
120 | "name": "[parameters('openAiEmbeddingsDeploymentName')]"
121 | }
122 | }
123 | },
124 | "mongovCoreSettings": {
125 | "mongoClusterName": "[format('{0}-mongo', parameters('name'))]",
126 | "mongoClusterLogin": "[parameters('mongoDbUserName')]",
127 | "mongoClusterPassword": "[parameters('mongoDbPassword')]"
128 | },
129 | "appServiceSettings": {
130 | "plan": {
131 | "name": "[format('{0}-web-plan', parameters('name'))]",
132 | "sku": "[parameters('appServiceSku')]"
133 | },
134 | "web": {
135 | "name": "[format('{0}-web', parameters('name'))]",
136 | "git": {
137 | "repo": "[parameters('appGitRepository')]",
138 | "branch": "[parameters('appGetRepositoryBranch')]"
139 | }
140 | },
141 | "function": {
142 | "name": "[format('{0}-function', parameters('name'))]",
143 | "git": {
144 | "repo": "[parameters('appGitRepository')]",
145 | "branch": "[parameters('appGetRepositoryBranch')]"
146 | }
147 | }
148 | }
149 | },
150 | "resources": [
151 | {
152 | "type": "Microsoft.DocumentDB/mongoClusters",
153 | "apiVersion": "2023-09-15-preview",
154 | "name": "[variables('mongovCoreSettings').mongoClusterName]",
155 | "location": "[parameters('location')]",
156 | "properties": {
157 | "administratorLogin": "[variables('mongovCoreSettings').mongoClusterLogin]",
158 | "administratorLoginPassword": "[variables('mongovCoreSettings').mongoClusterPassword]",
159 | "serverVersion": "5.0",
160 | "nodeGroupSpecs": [
161 | {
162 | "kind": "Shard",
163 | "sku": "M30",
164 | "diskSizeGB": 128,
165 | "enableHa": false,
166 | "nodeCount": 1
167 | }
168 | ]
169 | }
170 | },
171 | {
172 | "type": "Microsoft.DocumentDB/mongoClusters/firewallRules",
173 | "apiVersion": "2023-09-15-preview",
174 | "name": "[format('{0}/{1}', variables('mongovCoreSettings').mongoClusterName, 'allowAzure')]",
175 | "properties": {
176 | "startIpAddress": "0.0.0.0",
177 | "endIpAddress": "0.0.0.0"
178 | },
179 | "dependsOn": [
180 | "[resourceId('Microsoft.DocumentDB/mongoClusters', variables('mongovCoreSettings').mongoClusterName)]"
181 | ]
182 | },
183 | {
184 | "type": "Microsoft.DocumentDB/mongoClusters/firewallRules",
185 | "apiVersion": "2023-09-15-preview",
186 | "name": "[format('{0}/{1}', variables('mongovCoreSettings').mongoClusterName, 'allowAll')]",
187 | "properties": {
188 | "startIpAddress": "0.0.0.0",
189 | "endIpAddress": "255.255.255.255"
190 | },
191 | "dependsOn": [
192 | "[resourceId('Microsoft.DocumentDB/mongoClusters', variables('mongovCoreSettings').mongoClusterName)]"
193 | ]
194 | },
195 | {
196 | "type": "Microsoft.Web/serverfarms",
197 | "apiVersion": "2022-03-01",
198 | "name": "[variables('appServiceSettings').plan.name]",
199 | "location": "[parameters('location')]",
200 | "sku": {
201 | "name": "[variables('appServiceSettings').plan.sku]"
202 | }
203 | },
204 | {
205 | "type": "Microsoft.Web/sites",
206 | "apiVersion": "2022-03-01",
207 | "name": "[variables('appServiceSettings').web.name]",
208 | "location": "[parameters('location')]",
209 | "properties": {
210 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServiceSettings').plan.name)]",
211 | "httpsOnly": true
212 | },
213 | "dependsOn": [
214 | "[resourceId('Microsoft.Web/serverfarms', variables('appServiceSettings').plan.name)]"
215 | ]
216 | },
217 | {
218 | "type": "Microsoft.Storage/storageAccounts",
219 | "apiVersion": "2021-09-01",
220 | "name": "[format('{0}fnstorage', parameters('name'))]",
221 | "location": "[parameters('location')]",
222 | "kind": "Storage",
223 | "sku": {
224 | "name": "Standard_LRS"
225 | }
226 | },
227 | {
228 | "type": "Microsoft.Web/sites",
229 | "apiVersion": "2022-03-01",
230 | "name": "[variables('appServiceSettings').function.name]",
231 | "location": "[parameters('location')]",
232 | "kind": "functionapp",
233 | "properties": {
234 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServiceSettings').plan.name)]",
235 | "httpsOnly": true,
236 | "siteConfig": {
237 | "alwaysOn": true
238 | }
239 | },
240 | "dependsOn": [
241 | "[resourceId('Microsoft.Web/serverfarms', variables('appServiceSettings').plan.name)]"
242 | ]
243 | },
244 | {
245 | "type": "Microsoft.Web/sites/config",
246 | "apiVersion": "2022-03-01",
247 | "name": "[format('{0}/{1}', variables('appServiceSettings').web.name, 'appsettings')]",
248 | "kind": "string",
249 | "properties": {
250 | "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.Insights/components', variables('appServiceSettings').web.name), '2020-02-02').InstrumentationKey]",
251 | "OPENAI__ENDPOINT": "[variables('openAiSettings').endPoint]",
252 | "OPENAI__KEY": "[variables('openAiSettings').accountKey]",
253 | "OPENAI__EMBEDDINGSDEPLOYMENT": "[variables('openAiSettings').embeddingsModel.deployment.name]",
254 | "OPENAI__COMPLETIONSDEPLOYMENT": "[variables('openAiSettings').completionsModel.deployment.name]",
255 | "OPENAI__MAXCONVERSATIONTOKENS": "[variables('openAiSettings').maxConversationTokens]",
256 | "OPENAI__MAXCOMPLETIONTOKENS": "[variables('openAiSettings').maxCompletionTokens]",
257 | "MONGODB__CONNECTION": "[format('mongodb+srv://{0}:{1}@{2}.mongocluster.cosmos.azure.com/?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000', variables('mongovCoreSettings').mongoClusterLogin, variables('mongovCoreSettings').mongoClusterPassword, variables('mongovCoreSettings').mongoClusterName)]",
258 | "MONGODB__DATABASENAME": "retaildb",
259 | "MONGODB__COLLECTIONNAMES": "product",
260 | "MONGODB__MAXVECTORSEARCHRESULTS": "10",
261 | "MONGODB__VECTORINDEXTYPE": "ivf"
262 | },
263 | "dependsOn": [
264 | "[resourceId('Microsoft.Web/sites', variables('appServiceSettings').web.name)]",
265 | "[resourceId('Microsoft.Insights/components', variables('appServiceSettings').web.name)]"
266 | ]
267 | },
268 | {
269 | "type": "Microsoft.Web/sites/config",
270 | "apiVersion": "2022-03-01",
271 | "name": "[format('{0}/{1}', variables('appServiceSettings').function.name, 'appsettings')]",
272 | "kind": "string",
273 | "properties": {
274 | "AzureWebJobsStorage": "[format('DefaultEndpointsProtocol=https;AccountName={0}fnstorage;EndpointSuffix=core.windows.net;AccountKey={1}', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', format('{0}fnstorage', parameters('name'))), '2021-09-01').keys[0].value)]",
275 | "APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference(resourceId('Microsoft.Insights/components', variables('appServiceSettings').function.name), '2020-02-02').ConnectionString]",
276 | "FUNCTIONS_EXTENSION_VERSION": "~4",
277 | "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
278 | "OPENAI__ENDPOINT": "[variables('openAiSettings').endPoint]",
279 | "OPENAI__KEY": "[variables('openAiSettings').accountKey]",
280 | "OPENAI__EMBEDDINGSDEPLOYMENT": "[variables('openAiSettings').embeddingsModel.deployment.name]",
281 | "OPENAI__COMPLETIONSDEPLOYMENT": "[variables('openAiSettings').completionsModel.deployment.name]",
282 | "OPENAI__MAXCONVERSATIONTOKENS": "[variables('openAiSettings').maxConversationTokens]",
283 | "OPENAI__MAXCOMPLETIONTOKENS": "[variables('openAiSettings').maxCompletionTokens]",
284 | "OPENAI__MAXEMBEDDINGTOKENS": "[variables('openAiSettings').maxEmbeddingTokens]",
285 | "MONGODB__CONNECTION": "[format('mongodb+srv://{0}:{1}@{2}.mongocluster.cosmos.azure.com/?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000', variables('mongovCoreSettings').mongoClusterLogin, variables('mongovCoreSettings').mongoClusterPassword, variables('mongovCoreSettings').mongoClusterName)]",
286 | "MONGODB__DATABASENAME": "retaildb",
287 | "MONGODB__COLLECTIONNAMES": "product,customer,vectors,completions",
288 | "MONGODB__MAXVECTORSEARCHRESULTS": "10",
289 | "MONGODB__VECTORINDEXTYPE": "ivf"
290 | },
291 | "dependsOn": [
292 | "[resourceId('Microsoft.Web/sites', variables('appServiceSettings').function.name)]",
293 | "[resourceId('Microsoft.Storage/storageAccounts', format('{0}fnstorage', parameters('name')))]",
294 | "[resourceId('Microsoft.Insights/components', variables('appServiceSettings').function.name)]"
295 | ]
296 | },
297 | {
298 | "type": "Microsoft.Web/sites/sourcecontrols",
299 | "apiVersion": "2021-03-01",
300 | "name": "[format('{0}/{1}', variables('appServiceSettings').web.name, 'web')]",
301 | "properties": {
302 | "repoUrl": "[variables('appServiceSettings').web.git.repo]",
303 | "branch": "[variables('appServiceSettings').web.git.branch]",
304 | "isManualIntegration": true
305 | },
306 | "dependsOn": [
307 | "[resourceId('Microsoft.Web/sites', variables('appServiceSettings').web.name)]"
308 | ]
309 | },
310 | {
311 | "type": "Microsoft.Web/sites/sourcecontrols",
312 | "apiVersion": "2021-03-01",
313 | "name": "[format('{0}/{1}', variables('appServiceSettings').function.name, 'web')]",
314 | "properties": {
315 | "repoUrl": "[variables('appServiceSettings').web.git.repo]",
316 | "branch": "[variables('appServiceSettings').web.git.branch]",
317 | "isManualIntegration": true
318 | },
319 | "dependsOn": [
320 | "[resourceId('Microsoft.Web/sites', variables('appServiceSettings').function.name)]"
321 | ]
322 | },
323 | {
324 | "type": "Microsoft.Insights/components",
325 | "apiVersion": "2020-02-02",
326 | "name": "[variables('appServiceSettings').function.name]",
327 | "location": "[parameters('location')]",
328 | "kind": "web",
329 | "properties": {
330 | "Application_Type": "web"
331 | }
332 | },
333 | {
334 | "type": "Microsoft.Insights/components",
335 | "apiVersion": "2020-02-02",
336 | "name": "[variables('appServiceSettings').web.name]",
337 | "location": "[parameters('location')]",
338 | "kind": "web",
339 | "properties": {
340 | "Application_Type": "web"
341 | }
342 | }
343 | ],
344 | "outputs": {
345 | "deployedUrl": {
346 | "type": "string",
347 | "value": "[reference(resourceId('Microsoft.Web/sites', variables('appServiceSettings').web.name), '2022-03-01').defaultHostName]"
348 | }
349 | }
350 | }
--------------------------------------------------------------------------------
/img/addsockconsolelog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/f9afd2c3e5a7ba8b957d8118f3126234e268effd/img/addsockconsolelog.png
--------------------------------------------------------------------------------
/img/architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/f9afd2c3e5a7ba8b957d8118f3126234e268effd/img/architecture.png
--------------------------------------------------------------------------------
/img/dataloadandvectors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/f9afd2c3e5a7ba8b957d8118f3126234e268effd/img/dataloadandvectors.png
--------------------------------------------------------------------------------
/img/socks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/f9afd2c3e5a7ba8b957d8118f3126234e268effd/img/socks.png
--------------------------------------------------------------------------------
/img/ui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure/Vector-Search-AI-Assistant-MongoDBvCore/f9afd2c3e5a7ba8b957d8118f3126234e268effd/img/ui.png
--------------------------------------------------------------------------------