Skip to content

Commit bbd17a2

Browse files
authored
Merge pull request #35 from jcodella/main
Updated CosmosDB for MongoDB vCore notebooks...
2 parents 4b6945d + 936807b commit bbd17a2

File tree

3 files changed

+347
-71
lines changed

3 files changed

+347
-71
lines changed

Python/CosmosDB-MongoDB-vCore/CosmosDB-MongoDB-vCore_AzureOpenAI_Tutorial.ipynb

Lines changed: 142 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@
3030
"! pip install pymongo\n",
3131
"! pip install python-dotenv\n",
3232
"! pip install azure-core\n",
33-
"! pip install azure-cosmos\n",
34-
"! pip install tenacity"
33+
"! pip install azure-cosmos"
3534
]
3635
},
3736
{
@@ -43,14 +42,14 @@
4342
"import json\n",
4443
"import datetime\n",
4544
"import time\n",
45+
"import urllib \n",
4646
"\n",
4747
"from azure.core.exceptions import AzureError\n",
4848
"from azure.core.credentials import AzureKeyCredential\n",
4949
"import pymongo\n",
5050
"\n",
51-
"import openai\n",
52-
"from dotenv import load_dotenv\n",
53-
"from tenacity import retry, wait_random_exponential, stop_after_attempt"
51+
"from openai import AzureOpenAI\n",
52+
"from dotenv import load_dotenv"
5453
]
5554
},
5655
{
@@ -73,25 +72,18 @@
7372
"env_name = \"example.env\" # following example.env template change to your own .env file name\n",
7473
"config = dotenv_values(env_name)\n",
7574
"\n",
76-
"cosmosdb_endpoint = config['cosmos_db_api_endpoint']\n",
77-
"cosmosdb_key = config['cosmos_db_api_key']\n",
78-
"cosmosdb_connection_str = config['cosmos_db_connection_string']\n",
79-
"\n",
8075
"COSMOS_MONGO_USER = config['cosmos_db_mongo_user']\n",
8176
"COSMOS_MONGO_PWD = config['cosmos_db_mongo_pwd']\n",
8277
"COSMOS_MONGO_SERVER = config['cosmos_db_mongo_server']\n",
8378
"\n",
84-
"openai.api_type = config['openai_api_type']\n",
85-
"openai.api_key = config['openai_api_key']\n",
86-
"openai.api_base = config['openai_api_endpoint']\n",
87-
"openai.api_version = config['openai_api_version']\n",
88-
"embeddings_deployment = config['openai_embeddings_deployment']\n",
89-
"completions_deployment = config['openai_completions_deployment']"
79+
"AOAI_client = AzureOpenAI(api_key=config['openai_api_key'], azure_endpoint=config['openai_api_endpoint'], api_version=config['openai_api_version'],)"
9080
]
9181
},
9282
{
9383
"cell_type": "markdown",
94-
"metadata": {},
84+
"metadata": {
85+
"jp-MarkdownHeadingCollapsed": true
86+
},
9587
"source": [
9688
"## Create an Azure Cosmos DB for MongoDB vCore resource<a class=\"anchor\" id=\"cosmosdb\"></a>\n",
9789
"Let's start by creating an Azure Cosmos DB for MongoDB vCore Resource following this quick start guide: https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/quickstart-portal\n",
@@ -128,11 +120,12 @@
128120
"metadata": {},
129121
"outputs": [],
130122
"source": [
131-
"# Load text-sample.json data file. Embeddings will need to be generated using the function below.\n",
132-
"#data_file = open(file=\"../../DataSet/AzureServices/text-sample.json\", mode=\"r\")\n",
133-
"\n",
134-
"# OR Load text-sample_w_embeddings.json which has embeddings pre-computed\n",
123+
"# Load text-sample_w_embeddings.json which has embeddings pre-computed\n",
135124
"data_file = open(file=\"../../DataSet/AzureServices/text-sample_w_embeddings.json\", mode=\"r\") \n",
125+
"\n",
126+
"# OR Load text-sample.json data file. Embeddings will need to be generated using the function below.\n",
127+
"# data_file = open(file=\"../../DataSet/AzureServices/text-sample.json\", mode=\"r\")\n",
128+
"\n",
136129
"data = json.load(data_file)\n",
137130
"data_file.close()"
138131
]
@@ -155,17 +148,15 @@
155148
"metadata": {},
156149
"outputs": [],
157150
"source": [
158-
"@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(10))\n",
159151
"def generate_embeddings(text):\n",
160152
" '''\n",
161153
" Generate embeddings from string of text.\n",
162154
" This will be used to vectorize data and user input for interactions with Azure OpenAI.\n",
163155
" '''\n",
164-
" response = openai.Embedding.create(\n",
165-
" input=text, engine=\"text-embedding-ada-002\")\n",
166-
" embeddings = response['data'][0]['embedding']\n",
167-
" time.sleep(0.5) # rest period to avoid rate limiting on AOAI for free tier\n",
168-
" return embeddings"
156+
" response = AOAI_client.embeddings.create(input=text, model=config['openai_embeddings_deployment'])\n",
157+
" embeddings =response.model_dump()\n",
158+
" time.sleep(0.5) \n",
159+
" return embeddings['data'][0]['embedding']"
169160
]
170161
},
171162
{
@@ -191,6 +182,16 @@
191182
" json.dump(data, f)"
192183
]
193184
},
185+
{
186+
"cell_type": "code",
187+
"execution_count": null,
188+
"metadata": {},
189+
"outputs": [],
190+
"source": [
191+
"# Take a peek at one data item with embeddings created\n",
192+
"print(json.dumps(data[0], indent=2))"
193+
]
194+
},
194195
{
195196
"cell_type": "markdown",
196197
"metadata": {},
@@ -211,7 +212,7 @@
211212
"metadata": {},
212213
"outputs": [],
213214
"source": [
214-
"mongo_conn = \"mongodb+srv://\"+COSMOS_MONGO_USER+\":\"+COSMOS_MONGO_PWD+\"@\"+COSMOS_MONGO_SERVER+\"?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000\"\n",
215+
"mongo_conn = \"mongodb+srv://\"+urllib.parse.quote(COSMOS_MONGO_USER)+\":\"+urllib.parse.quote(COSMOS_MONGO_PWD)+\"@\"+COSMOS_MONGO_SERVER+\"?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000\"\n",
215216
"mongo_client = pymongo.MongoClient(mongo_conn)"
216217
]
217218
},
@@ -250,8 +251,8 @@
250251
"metadata": {},
251252
"outputs": [],
252253
"source": [
253-
"## Use only if re-reunning code and want to reset db and collection\n",
254-
"collection.drop_index(\"VectorSearchIndex\")\n",
254+
"# Use only if re-reunning code and want to reset db and collection\n",
255+
"collection.drop_indexes()\n",
255256
"mongo_client.drop_database(\"ExampleDB\")"
256257
]
257258
},
@@ -268,8 +269,10 @@
268269
"cell_type": "markdown",
269270
"metadata": {},
270271
"source": [
271-
"### IVF\n",
272-
"IVF is the default vector indexing algorithm, which works on all cluster tiers. It's an approximate nerarest neighbors (ANN) approach that uses clustering to speeding up the search for similar vectors in a dataset. "
272+
"### IVF index\n",
273+
"IVF is an approximate nerarest neighbors (ANN) approach that uses clustering to speed up the search for similar vectors in a dataset. It's a good choice for proof-of-concepts and smaller datasets (under a few thousand documents). However it's not recommended to use at scale or when higher throughput is needed.\n",
274+
"\n",
275+
"IVF is supported on all cluster tiers, including the free tier. "
273276
]
274277
},
275278
{
@@ -301,11 +304,9 @@
301304
"cell_type": "markdown",
302305
"metadata": {},
303306
"source": [
304-
"### HNSW (preview)\n",
305-
"\n",
306-
"HNSW stands for Hierarchical Navigable Small World, a graph-based data structure that partitions vectors into clusters and subclusters. With HNSW, you can perform fast approximate nearest neighbor search at higher speeds with greater accuracy. HNSW is an approximate (ANN) method. As a preview feature, this must be enabled using Azure Feature Enablement Control (AFEC) by selecting the \"mongoHnswIndex\" feature. For more information, see [enable preview features](https://learn.microsoft.com/azure/azure-resource-manager/management/preview-features).\n",
307+
"### HNSW Index\n",
307308
"\n",
308-
"HNSW works on M50 cluster tiers and higher while in preview."
309+
"HNSW stands for Hierarchical Navigable Small World, a graph-based index that partitions vectors into clusters and subclusters. With HNSW, you can perform fast approximate nearest neighbor search at higher speeds with greater accuracy. HNSW is now available on M40 and higher cluster tiers."
309310
]
310311
},
311312
{
@@ -314,8 +315,7 @@
314315
"metadata": {},
315316
"outputs": [],
316317
"source": [
317-
"db.command(\n",
318-
"{ \n",
318+
"db.command({ \n",
319319
" \"createIndexes\": \"ExampleCollection\",\n",
320320
" \"indexes\": [\n",
321321
" {\n",
@@ -367,7 +367,7 @@
367367
"outputs": [],
368368
"source": [
369369
"# Simple function to assist with vector search\n",
370-
"def vector_search(query, num_results=3):\n",
370+
"def vector_search(query, num_results=5):\n",
371371
" query_embedding = generate_embeddings(query)\n",
372372
" embeddings_list = []\n",
373373
" pipeline = [\n",
@@ -376,7 +376,8 @@
376376
" \"cosmosSearch\": {\n",
377377
" \"vector\": query_embedding,\n",
378378
" \"path\": \"contentVector\",\n",
379-
" \"k\": num_results #, \"efsearch\": 40 # optional for HNSW only \n",
379+
" \"k\": num_results#, #, \"efsearch\": 40 # optional for HNSW only \n",
380+
" #\"filter\": {\"title\": {\"$ne\": \"Azure Cosmos DB\"}}\n",
380381
" },\n",
381382
" \"returnStoredSource\": True }},\n",
382383
" {'$project': { 'similarityScore': { '$meta': 'searchScore' }, 'document' : '$$ROOT' } }\n",
@@ -400,7 +401,7 @@
400401
},
401402
"outputs": [],
402403
"source": [
403-
"query = \"What are the services for running ML models?\"\n",
404+
"query = \"What are some NoSQL databases in Azure?\"#\"What are the services for running ML models?\"\n",
404405
"results = vector_search(query)\n",
405406
"for result in results: \n",
406407
"# print(result)\n",
@@ -410,6 +411,89 @@
410411
" print(f\"Category: {result['document']['category']}\\n\") "
411412
]
412413
},
414+
{
415+
"cell_type": "markdown",
416+
"metadata": {},
417+
"source": [
418+
"## Filtered vector search (Preview)\n",
419+
"You can add additional query filters to your vector search by creating a filtered index and specifying it in the search pipeline.\n",
420+
"\n",
421+
"**Note:** filtered vector search preview and needs to be enabled via Azure Preview Features for your subscription. Search for the preview feature \"filtering on vector search\". Learn more about it here: https://learn.microsoft.com/azure/azure-resource-manager/management/preview-features?tabs=azure-portal"
422+
]
423+
},
424+
{
425+
"cell_type": "code",
426+
"execution_count": null,
427+
"metadata": {},
428+
"outputs": [],
429+
"source": [
430+
"# Add a filter index\n",
431+
"db.command( {\n",
432+
" \"createIndexes\": \"ExampleCollection\",\n",
433+
" \"indexes\": [ {\n",
434+
" \"key\": { \n",
435+
" \"title\": 1 \n",
436+
" }, \n",
437+
" \"name\": \"title_filter\" \n",
438+
" }\n",
439+
" ] \n",
440+
"} \n",
441+
")"
442+
]
443+
},
444+
{
445+
"cell_type": "code",
446+
"execution_count": null,
447+
"metadata": {},
448+
"outputs": [],
449+
"source": [
450+
"# Verify all indexes are present\n",
451+
"for i in collection.list_indexes():\n",
452+
" print(i)"
453+
]
454+
},
455+
{
456+
"cell_type": "code",
457+
"execution_count": null,
458+
"metadata": {},
459+
"outputs": [],
460+
"source": [
461+
"# Simple function to assist with vector search\n",
462+
"def filtered_vector_search(query, num_results=5):\n",
463+
" query_embedding = generate_embeddings(query)\n",
464+
" embeddings_list = []\n",
465+
" pipeline = [\n",
466+
" {\n",
467+
" '$search': {\n",
468+
" \"cosmosSearch\": {\n",
469+
" \"vector\": query_embedding,\n",
470+
" \"path\": \"contentVector\",\n",
471+
" \"k\": num_results,\n",
472+
" \"filter\": {\"title\": {\"$nin\": [\"Azure SQL Database\", \"Azure Database for MySQL\"]}}\n",
473+
" },\n",
474+
" \"returnStoredSource\": True }},\n",
475+
" {'$project': { 'similarityScore': { '$meta': 'searchScore' }, 'document' : '$$ROOT' } }\n",
476+
" ]\n",
477+
" results = collection.aggregate(pipeline)\n",
478+
" return results"
479+
]
480+
},
481+
{
482+
"cell_type": "code",
483+
"execution_count": null,
484+
"metadata": {},
485+
"outputs": [],
486+
"source": [
487+
"query = \"What are some NoSQL databases in Azure?\"#\"What are the services for running ML models?\"\n",
488+
"results = filtered_vector_search(query)\n",
489+
"for result in results: \n",
490+
"# print(result)\n",
491+
" print(f\"Similarity Score: {result['similarityScore']}\") \n",
492+
" print(f\"Title: {result['document']['title']}\") \n",
493+
" print(f\"Content: {result['document']['content']}\") \n",
494+
" print(f\"Category: {result['document']['category']}\\n\") "
495+
]
496+
},
413497
{
414498
"cell_type": "markdown",
415499
"metadata": {},
@@ -427,47 +511,44 @@
427511
"source": [
428512
"#This function helps to ground the model with prompts and system instructions.\n",
429513
"\n",
430-
"def generate_completion(prompt):\n",
514+
"def generate_completion(vector_search_results, user_prompt):\n",
431515
" system_prompt = '''\n",
432516
" You are an intelligent assistant for Microsoft Azure services.\n",
433517
" You are designed to provide helpful answers to user questions about Azure services given the information about to be provided.\n",
434-
" - Only answer questions related to the information provided below, provide 3 clear suggestions in a list format.\n",
518+
" - Only answer questions related to the information provided below, provide at least 3 clear suggestions in a list format.\n",
435519
" - Write two lines of whitespace between each answer in the list.\n",
436-
" - Only provide answers that have products that are part of Microsoft Azure.\n",
437520
" - If you're unsure of an answer, you can say \"\"I don't know\"\" or \"\"I'm not sure\"\" and recommend users search themselves.\"\n",
521+
" - Only provide answers that have products that are part of Microsoft Azure and part of these following prompts.\n",
438522
" '''\n",
439523
"\n",
440-
" messages=[\n",
441-
" {\"role\": \"system\", \"content\": system_prompt},\n",
442-
" {\"role\": \"user\", \"content\": user_input},\n",
443-
" ]\n",
444-
"\n",
445-
" for item in results:\n",
446-
" messages.append({\"role\": \"system\", \"content\": prompt['content']})\n",
447-
"\n",
448-
" response = openai.ChatCompletion.create(engine=completions_deployment, messages=messages)\n",
524+
" messages=[{\"role\": \"system\", \"content\": system_prompt}]\n",
525+
" for item in vector_search_results:\n",
526+
" messages.append({\"role\": \"system\", \"content\": item['document']['content']})\n",
527+
" messages.append({\"role\": \"user\", \"content\": user_prompt})\n",
528+
" response = AOAI_client.chat.completions.create(model=config['openai_completions_deployment'], messages=messages,temperature=0)\n",
449529
" \n",
450-
" return response"
530+
" return response.dict()"
451531
]
452532
},
453533
{
454534
"cell_type": "code",
455535
"execution_count": null,
456-
"metadata": {},
536+
"metadata": {
537+
"scrolled": true
538+
},
457539
"outputs": [],
458540
"source": [
459541
"# Create a loop of user input and model output. You can now perform Q&A over the sample data!\n",
460542
"\n",
461543
"user_input = \"\"\n",
462544
"print(\"*** Please ask your model questions about Azure services. Type 'end' to end the session.\\n\")\n",
463-
"user_input = input(\"Prompt: \")\n",
545+
"user_input = input(\"User prompt: \")\n",
464546
"while user_input.lower() != \"end\":\n",
465-
" results_for_prompt = vector_search(user_input)\n",
466-
" # print(f\"User Prompt: {user_input}\")\n",
467-
" completions_results = generate_completion(results_for_prompt)\n",
547+
" search_results = vector_search(user_input)\n",
548+
" completions_results = generate_completion(search_results, user_input)\n",
468549
" print(\"\\n\")\n",
469550
" print(completions_results['choices'][0]['message']['content'])\n",
470-
" user_input = input(\"Prompt: \")\n"
551+
" user_input = input(\"User prompt: \")"
471552
]
472553
}
473554
],
@@ -487,7 +568,7 @@
487568
"name": "python",
488569
"nbconvert_exporter": "python",
489570
"pygments_lexer": "ipython3",
490-
"version": "3.9.7"
571+
"version": "3.11.0"
491572
},
492573
"varInspector": {
493574
"cols": {
@@ -520,5 +601,5 @@
520601
}
521602
},
522603
"nbformat": 4,
523-
"nbformat_minor": 2
604+
"nbformat_minor": 4
524605
}

0 commit comments

Comments
 (0)