MicrosoftLearning / mslearn-ai-language

Lab files for Azure AI Language modules
https://microsoftlearning.github.io/mslearn-ai-language/
MIT License
120 stars 253 forks source link

AI-LEARN-03c Conversational Language (Learn) - clock-client.py returns "key must be a string." #48

Closed michaelmiscanuk closed 2 months ago

michaelmiscanuk commented 2 months ago

Hi, I filled the code according to the lab, and have setup model in studio, but the code returns for me "key must be a string." for every input. Can you help me out? Thanks

ivorb commented 2 months ago

without any code to look at, it's hard for me to help. I'm guessing the key would need to be in quotation marks and it isn't? if you post your completed code here (both code file and env) that might help

michaelmiscanuk commented 2 months ago

.env

AI_SERVICE_ENDPOINT=https://...........test.cognitiveservices.azure.com/
AI_SERVICE_KEY=771fe6b5.............................fe83e

clock-client.py

from dotenv import load_dotenv
import os
import json
from datetime import datetime, timedelta, date, timezone
from dateutil.parser import parse as is_date

# Import namespaces
from azure.core.credentials import AzureKeyCredential
from azure.ai.language.conversations import ConversationAnalysisClient

def main():

    try:
        # Get Configuration Settings
        load_dotenv()
        ls_prediction_endpoint = os.getenv('LS_CONVERSATIONS_ENDPOINT')
        ls_prediction_key = os.getenv('LS_CONVERSATIONS_KEY')

        # Get user input (until they enter "quit")
        userText = ''
        while userText.lower() != 'quit':
            userText = input('\nEnter some text ("quit" to stop)\n')
            if userText.lower() != 'quit':

                # Create a client for the Language service model
                client = ConversationAnalysisClient(
                    ls_prediction_endpoint, AzureKeyCredential(ls_prediction_key))

                # Call the Language service model to get intent and entities
                cls_project = 'Clock'
                deployment_slot = 'production'

                with client:
                    query = userText
                    result = client.analyze_conversation(
                        task={
                            "kind": "Conversation",
                            "analysisInput": {
                                "conversationItem": {
                                    "participantId": "1",
                                    "id": "1",
                                    "modality": "text",
                                    "language": "en",
                                    "text": query
                                },
                                "isLoggingEnabled": False
                            },
                            "parameters": {
                                "projectName": cls_project,
                                "deploymentName": deployment_slot,
                                "verbose": True
                            }
                        }
                    )

                top_intent = result["result"]["prediction"]["topIntent"]
                entities = result["result"]["prediction"]["entities"]

                print("view top intent:")
                print("\ttop intent: {}".format(result["result"]["prediction"]["topIntent"]))
                print("\tcategory: {}".format(result["result"]["prediction"]["intents"][0]["category"]))
                print("\tconfidence score: {}\n".format(result["result"]["prediction"]["intents"][0]["confidenceScore"]))

                print("view entities:")
                for entity in entities:
                    print("\tcategory: {}".format(entity["category"]))
                    print("\ttext: {}".format(entity["text"]))
                    print("\tconfidence score: {}".format(entity["confidenceScore"]))

                print("query: {}".format(result["result"]["query"]))

                # Apply the appropriate action
                if top_intent == 'GetTime':
                    location = 'local'
                    # Check for entities
                    if len(entities) > 0:
                        # Check for a location entity
                        for entity in entities:
                            if 'Location' == entity["category"]:
                                # ML entities are strings, get the first one
                                location = entity["text"]
                    # Get the time for the specified location
                    print(GetTime(location))

                elif top_intent == 'GetDay':
                    date_string = date.today().strftime("%m/%d/%Y")
                    # Check for entities
                    if len(entities) > 0:
                        # Check for a Date entity
                        for entity in entities:
                            if 'Date' == entity["category"]:
                                # Regex entities are strings, get the first one
                                date_string = entity["text"]
                    # Get the day for the specified date
                    print(GetDay(date_string))

                elif top_intent == 'GetDate':
                    day = 'today'
                    # Check for entities
                    if len(entities) > 0:
                        # Check for a Weekday entity
                        for entity in entities:
                            if 'Weekday' == entity["category"]:
                            # List entities are lists
                                day = entity["text"]
                    # Get the date for the specified day
                    print(GetDate(day))

                else:
                    # Some other intent (for example, "None") was predicted
                    print('Try asking me for the time, the day, or the date.')

    except Exception as ex:
        print(ex)

def GetTime(location):
    time_string = ''

    # Note: To keep things simple, we'll ignore daylight savings time and support only a few cities.
    # In a real app, you'd likely use a web service API (or write  more complex code!)
    # Hopefully this simplified example is enough to get the the idea that you
    # use LU to determine the intent and entities, then implement the appropriate logic

    if location.lower() == 'local':
        now = datetime.now()
        time_string = '{}:{:02d}'.format(now.hour,now.minute)
    elif location.lower() == 'london':
        utc = datetime.now(timezone.utc)
        time_string = '{}:{:02d}'.format(utc.hour,utc.minute)
    elif location.lower() == 'sydney':
        time = datetime.now(timezone.utc) + timedelta(hours=11)
        time_string = '{}:{:02d}'.format(time.hour,time.minute)
    elif location.lower() == 'new york':
        time = datetime.now(timezone.utc) + timedelta(hours=-5)
        time_string = '{}:{:02d}'.format(time.hour,time.minute)
    elif location.lower() == 'nairobi':
        time = datetime.now(timezone.utc) + timedelta(hours=3)
        time_string = '{}:{:02d}'.format(time.hour,time.minute)
    elif location.lower() == 'tokyo':
        time = datetime.now(timezone.utc) + timedelta(hours=9)
        time_string = '{}:{:02d}'.format(time.hour,time.minute)
    elif location.lower() == 'delhi':
        time = datetime.now(timezone.utc) + timedelta(hours=5.5)
        time_string = '{}:{:02d}'.format(time.hour,time.minute)
    else:
        time_string = "I don't know what time it is in {}".format(location)

    return time_string

def GetDate(day):
    date_string = 'I can only determine dates for today or named days of the week.'

    weekdays = {
        "monday":0,
        "tuesday":1,
        "wednesday":2,
        "thursday":3,
        "friday":4,
        "saturday":5,
        "sunday":6
    }

    today = date.today()

    # To keep things simple, assume the named day is in the current week (Sunday to Saturday)
    day = day.lower()
    if day == 'today':
        date_string = today.strftime("%m/%d/%Y")
    elif day in weekdays:
        todayNum = today.weekday()
        weekDayNum = weekdays[day]
        offset = weekDayNum - todayNum
        date_string = (today + timedelta(days=offset)).strftime("%m/%d/%Y")

    return date_string

def GetDay(date_string):
    # Note: To keep things simple, dates must be entered in US format (MM/DD/YYYY)
    try:
        date_object = datetime.strptime(date_string, "%m/%d/%Y")
        day_string = date_object.strftime("%A")
    except:
        day_string = 'Enter a date in MM/DD/YYYY format.'
    return day_string

if __name__ == "__main__":
    main()

In env I dont put any quotes around endpoint and key, this works elsewhere fine. And I also just tried to put key in '' and in "" and still same output message. Also, I didnt change much in code. Just put code pieces from lab under comments where supposed to. Also I made sure key is correct to the endpoint. Thanks

ivorb commented 2 months ago

You're correct the .env file doesn't need quotations. Are you sure that you're using the right .env file? The one you posted uses "AI_SERVICE_KEY" which is used for other labs in this repo but the code for lab 3/the code you posted is looking for LS_CONVERSATIONS_KEY.

Check that the .env you have your values in is next to clock-client.py. You can also test by changing lines 16 and 17 to not use the getenv method and paste your resource values directly there.

ivorb commented 2 months ago

Closing due lack of response

michaelmiscanuk commented 2 months ago

You're correct the .env file doesn't need quotations. Are you sure that you're using the right .env file? The one you posted uses "AI_SERVICE_KEY" which is used for other labs in this repo but the code for lab 3/the code you posted is looking for LS_CONVERSATIONS_KEY.

Check that the .env you have your values in is next to clock-client.py. You can also test by changing lines 16 and 17 to not use the getenv method and paste your resource values directly there.

Hi, sorry for late response. You are very correct, I made a mistake copying the .env from other lab :( Thank you for your help!!!!!