code to query LLM:
import os
import requests
import azure.functions as func
import openai
from pinecone import Pinecone, ServerlessSpec
import logging
import json

openai_key = "<TRUFFLEHOG_REDACTED_OPENAI>"  # os.getenv("openai_key")
openAI_client = openai.OpenAI(api_key=openai_key)
pckey = "ad5d3f6f-ae7d-4880-9b1b-0fa937fe67ad"  # os.getenv("pinecone_key")
pc = Pinecone(api_key=pckey)

index_name = os.getenv('index_name') or 'id-pc1-01'

embed_model = "text-embedding-ada-002"
CHAT_WINDOW_SIZE = 2
MAX_TOKENS = 500

# UTILITY FUNCTIONS 👇
def get_index():
    try:
        if not pc.has_index(index_name):
            pc.create_index(
                name=index_name,
                dimension=1536,
                metric='cosine',
                spec=ServerlessSpec(cloud='aws', region='us-east-1')
            )
        
        return pc.Index(index_name)
    
    except Exception as e:
        logging.error("Error while getting index.")

def embed_text(text):   # convert text to embeddings
    try:
        res = openAI_client.embeddings.create(
            input=[text],
            model=embed_model
        )
        return res.data[0].embedding
    except Exception as e:
        logging.error("Error while embedding text.")

def retrieve_data(query, index, ns):
    """
    Performs similarity search on pinecone and retrieves relevant data.

    Args:
        query (string): Users query.
        index (index): Pinecone index.
        ns (string): Users device id (used as namespace).
    
    Returns:
        list: A list of query-relevant data from pinecone.
    """
    try:
        query_embeds = embed_text(query)
        results = index.query(
            namespace=ns,
            vector=query_embeds,
            top_k=20,
            include_metadata=True
        )

        return [m['metadata']['text'] for m in results['matches']]
    except Exception as e:
        logging.error("Error while retrieving data from pinecone.")

def reply(prompt):  # get reply from LLM
    try:
        response = openAI_client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": "You are a helpful assistant. Use context to answer questions if relevant, even if incorrect."
                        "Correct mistakes politely after using context. If irrelevant, rely on your own knowledge without mentioning the lack of context."},
                {"role": "user", "content": f"{prompt}"},
            ]
        )

        return response.choices[0].message.content.strip()
    
    except openai.error.OpenAIError as e:
        logging.error(f"Error while getting reply from LLM: {str(e)}")
        return f"An error occurred: {e}"

def update_summary(summary, query, text):
    try:
        prompt = f"""Update the summary below with the latest conversation. Prioritize new information without losing existing summary.\n
                    Summary: {summary}\n
                    New Conversation:\nUser: {query}\nBot: {text}\n
                    Updated Summary:"""

        response = openAI_client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {'role': "system", "content": "You are a professional summarizer. Your task is to concisely summarize conversations into key points, prioritizing new information while retaining essential details from previous summaries."},
                {"role": "user", "content": f"{prompt}"},
            ],
            max_tokens=MAX_TOKENS
        )

        return response.choices[0].message.content.strip()
    
    except openai.error.OpenAIError as e:
        logging.error(f"Error while getting reply from LLM while updating summary: {str(e)}")
        return f"An error occurred: {e}"


# MAIN FUNCTION 👇
def main(req: func.HttpRequest) -> func.HttpResponse:   # query LLM based on context similar to the query received from pinecone
    """
    Generates an output based on user query

    Args(req: func.HttpRequest):
        device_id(string): Users device id (used as namespace)
        chat_session_id(Int): Chat session ID on users account
        query(string): Users query that needs to be resolved
    
    Returns(func.HttpResponse):
        string: Solution to users query
    """
    try:
        query = req.get_json()
        device_id = query.get('device_id')
        chat_session_id = query.get('chat_session_id')
        if not device_id:
            logging.error("No user ID found.")
            return func.HttpResponse(json.dumps({"error": "Device ID is required"}), status_code=400, mimetype="application/json")
        if not chat_session_id:
            logging.error("No chat session ID found.")
            return func.HttpResponse(json.dumps({"error": "Chat session ID is required"}), status_code=400, mimetype="application/json")
        
        try:
            index = get_index()
        except Exception as e:
            logging.error(f"{str(e)}")
        query_text = query.get('query')

        if not query_text:
            logging.error("No query text.")
            return func.HttpResponse(json.dumps({"error": "Query text is required"}), status_code=400, mimetype="application/json")

        contexts = retrieve_data(query_text, index, ns=device_id)

        # chat_retrieval_URL = os.getenv('chat_retrieval_url') or "https://memory-assist-twt.azurewebsites.net/api/retrieve_chat?"
        chat_retrieval_URL = os.getenv('chat_retrieval_url') or "http://localhost:7071/api/retrieve_chat"
        cr_payload = {
            'device_id': device_id,
            'chat_session_id': chat_session_id,
            'chat_window_size': CHAT_WINDOW_SIZE
        }
        chat_retrieval_response = requests.post(chat_retrieval_URL, json=cr_payload)
        chat_retrieval_response.raise_for_status()

        chat_data = chat_retrieval_response.json()
        previous_chat = chat_data['chats']
        summary = chat_data['summary']

        prompt = (
            "Context: \n" + "\n".join(contexts) +
            "Previous chat for context: \n" + previous_chat +
            "Summary of the whole chat: \n" + summary +
            f"\nQuestion: {query_text}"
        )

        answer = reply(prompt)
        new_summary = update_summary(summary, query, answer)
        cs_payload = {
            'device_id': device_id,
            'chat_session_id': chat_session_id,
            'user_query': query_text,
            'bot_reply': answer,
            'summary': new_summary
        }

        # chat_storage_URL = os.getenv('chat_storage_url') or "https://memory-assist-twt.azurewebsites.net/api/store_chat?"
        chat_storage_URL = os.getenv('chat_storage_url') or "http://localhost:7071/api/store_chat"
        chat_storage = requests.post(chat_storage_URL, json=cs_payload)
        chat_storage.raise_for_status()

        return func.HttpResponse(json.dumps({"answer": answer}), status_code=200, mimetype="application/json")
    
    except Exception as e:
        logging.error(f"Error while querying: {str(e)}, (main)")
        return func.HttpResponse(json.dumps({"error": str(e)}), status_code=500, mimetype="application/json")

code to store chat:
# Requirement: pymongo bson

import os
import json
import logging
from pymongo import MongoClient
from datetime import datetime
import azure.functions as func

mongoclient = "mongodb+srv://navin:<PRESIDIO_ANONYMIZED_EMAIL_ADDRESS>"
try:
    client = MongoClient(f"{mongoclient}/mydatabase?retryWrites=true&w=majority")
except Exception as e:
    logging.error(f"Error while connecting to MongoDB: {str(e)}")
    raise

db = client['TwT-memory-assist']
chat_sessions_col = db['ChatSessions']
message_pairs_col = db['MessagePairs']

def create_chat_session(device_id, chat_session_id, summary, title=None):
    try:
        existing_chat_session = chat_sessions_col.find_one({
            'device_id': device_id,
            'chat_session_id': chat_session_id
        })
        if not existing_chat_session:
            chat_session = {
                # 'userId': ObjectId(user_id),
                'device_id': device_id,
                'chat_session_id': chat_session_id,
                'summary': summary,
                'title': title if title else 'New Chat Session',
                'isActive': True,
                'createdAt': datetime.now().isoformat(),
                'lastUpdatedAt': datetime.now().isoformat(),
                'metadata': {}
            }
            result = chat_sessions_col.insert_one(chat_session)
            logging.info(f"Chat session successfully created with id: {result.inserted_id}")
            return result.inserted_id
        else:
            chat_sessions_col.update_one(
                {'device_id': device_id, 'chat_session_id': chat_session_id},
                {'$set': {'summary': summary, 'lastUpdatedAt': datetime.now().isoformat()}}
            )
            logging.info("Summary successfully updated in existing chat session.")
        
    except Exception as e:
        logging.error(f"Error when creating chat session: {str(e)}")
        return None

def store_message_pair(device_id, chat_session_id, user_message, bot_reply):
    message_pair = {
        'device_id': device_id,
        'chat_session_id': chat_session_id,
        'userMessage': {
            # 'messageType': user_message['type'],
            'content': str(user_message),
            # 'media': user_message.get('media', {}),
            'createdAt': datetime.now().isoformat()
        },
        'botReply': {
            # 'messageType': bot_reply['type'],
            'content': str(bot_reply),
            'createdAt': datetime.now().isoformat()
        },
        'metadata': {}
    }
    result = message_pairs_col.insert_one(message_pair)
    return result.inserted_id

def main(req: func.HttpRequest) -> func.HttpResponse:
    try:
        data = req.get_json()
        device_id = data.get('device_id')
        if not device_id:
            logging.error("Device ID missing!")
            return func.HttpResponse(json.dumps({'error': 'Device ID not found.'}), status_code=400, mimetype='application/json')
        chat_session_id = data.get('chat_session_id')
        if not chat_session_id:
            logging.error("Chat session ID not found!")
            return func.HttpResponse(json.dumps({'error': 'Chat session ID not found.'}), status_code=400, mimetype='application/json')
        user_query = data.get('user_query')
        bot_reply = data.get('bot_reply')
        if not user_query or not bot_reply:
            logging.error("Chat incomplete.")
            return func.HttpResponse(json.dumps({'error': 'Incomplete chat received'}), status_code=400, mimetype='application/json')
        summary = data.get('summary')
        
        create_chat_session(device_id, chat_session_id, summary)
        store_message_pair(device_id, chat_session_id, user_query, bot_reply)

        return func.HttpResponse(json.dumps({'message': 'Chat successfully stored.'}), status_code=200, mimetype='application/json')
    
    except Exception as e:
        logging.error("Error while storing chat. (main)")
        return func.HttpResponse(json.dumps({'error': 'Error while storing chat. (main)'}), status_code=400, mimetype='application/json')

how do I make the chat store API call async in query code? we don't want the user to wait for the reply while the chat gets stored. (APIs to be deployed on azure functions app)