0

I'm using custom alexa skill based on Python 3.7 and lambda function to trigger my skill. I have two slots (medicine_name and quantity) for my intent "MedicineAdderIntent" which I am filling and for that I'm using auto-delegation from skill kit console.

After collecting slots I am using rapidfuzz to match medicine_name with local database and if it exists I'm giving returning the quantity and asking whether you want to update it or not. What I am trying to achieve it is to update the quantity and keep the conversation going.

I have tried using ElicitSlot and Dialog Delegation to call another intent "MedicineExistsIntent" where I can continue my dialog.

But when I use them updated intent is not getting called. Also, if I don't use another intent then dialog state gets completed on AMAZON.YesIntent if using default AMAZON.YesIntent and AMAZON.NoIntent. If I use custom Yes and No intent handlers then they are getting called and dialog state is showing STARTED.

I've also tried intent chaining by keeping auto-delegation ON but alexa wasn't asking for user response and session attributes too to update my slot but didn't reached anywhere.

This is my lambda_function.py:

# -*- coding: utf-8 -*-

# This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK for Python.
# Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
# session persistence, api calls, and more.
# This sample is built using the handler classes approach in skill builder.
import logging
import ask_sdk_core.utils as ask_utils
from ask_sdk_core.utils import is_intent_name, get_slot_value, get_dialog_state
from ask_sdk_core.skill_builder import SkillBuilder
from ask_sdk_core.dispatch_components import (AbstractRequestHandler, AbstractExceptionHandler, AbstractResponseInterceptor, AbstractRequestInterceptor)
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import (Response, IntentRequest, DialogState, IntentConfirmationStatus, Slot, SlotConfirmationStatus)
# from ask_sdk_model.dialog import delegate_directive
from ask_sdk_model.dialog import (ElicitSlotDirective, DelegateDirective)
from ask_sdk_model.intent import Intent
from typing import Union, Dict, Any, List
from ask_sdk_model.slu.entityresolution import StatusCode
import requests
import json
from rapidfuzz import process, fuzz

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


class MedicineAdderIntentHandler(AbstractRequestHandler):
    """Handler for MedicineAdderIntent."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_intent_name("MedicineAdderIntent")(handler_input) and ask_utils.is_intent_name("AMAZON.YesIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        slots = handler_input.request_envelope.request.intent.slots
        # logger.info(f"slots: {slots}")
        session_attr = handler_input.attributes_manager.session_attributes
        logger.info(f"session_attr: {session_attr}")
        medicine_name = slots["medicine_name"].value
        quantity = slots["quantity"].value
        url = "http://my_ngrok_url/medicine/"
        res = requests.get(url)
        if res.status_code == 200:
            data = res.json()
            # logger.info(f"data: {res.json()}")
            med_list = [med["medicine_name"] for med in data]
            logger.info(f"med_list: {med_list}")
            if len(med_list) > 0:
                matched_medicine = process.extractOne(medicine_name, med_list, scorer=fuzz.token_set_ratio)[0]
                logger.info(f"matched_medicine: {matched_medicine}")
                if matched_medicine:
                    get_details = [med for med in data if med['medicine_name'] == matched_medicine]
                    logger.info(f"get_details: {get_details}")
                    quantitydB = get_details[0]['quantity']
                    logger.info(f"quantitydB: {quantitydB}")
                    session_attr["MedExists"] = f"{medicine_name} already exists. Quantity: {quantitydB}. Do you want to update the quantity?"
                    speak_output = f"{medicine_name} already exists. Quantity: {quantitydB}. Do you want to update the quantity?"
                    directive = ElicitSlotDirective(slot_to_elicit="quantity", updated_intent=Intent(name="MedicineExistsIntent"))
                    logger.info("Well this isn't working...")
                    return (
                        handler_input.response_builder
                            .speak(speak_output)
                            .add_directive(directive)
                            .response
                    )
                else:
                    speak_output = f"Medicine did not match. Do you want to add {medicine_name} to flag state?"
                return (
                    handler_input.response_builder
                        .speak(speak_output)
                        .response
                )
        else:
            speak_output = "Unable to fetch medicines from database"
            return (
                handler_input.response_builder
                    .speak(speak_output)
                    .response
            )


class MedicineExistsIntentHandler(AbstractRequestHandler):
    """Handler for MedicineAdderIntent."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        attribute_manager = handler_input.attributes_manager
        session_attr = attribute_manager.session_attributes
        return (is_intent_name("AMAZON.YesIntent")(handler_input) and "MedExists" in session_attr)
        # return ask_utils.is_intent_name("MedicineExistsIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        attribute_manager = handler_input.attributes_manager
        session_attr = attribute_manager.session_attributes
        speak_output = "How much do you want to add?"
        # return (
        #     handler_input.response_builder
        #         .speak(speak_output)
        #         # .ask("add a reprompt if you want to keep the session open for the user to respond")
        #         .response
        # )
        if "MedExists" in session_attr:
            speak_output = "How much do you want to add?"
            intent_name = "MedicineExistsIntent"
        return handler_input.response_builder.speak(speak_output).add_directive(delegate_directive.DelegateDirective(updated_intent=Intent(name=intent_name))).response


class LaunchRequestHandler(AbstractRequestHandler):
    """Handler for Skill Launch."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_request_type("LaunchRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speak_output = "Welcome, how can I help you?"

        return (
            handler_input.response_builder
                .speak(speak_output)
                .ask(speak_output)
                .response
        )


class YesIntentHandler(AbstractRequestHandler):
    """Handler for YesIntentHandler."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_request_type("IntentRequest")(handler_input) and ask_utils.is_intent_name("AMAZON.YesIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speak_output = "YesIntent"

        return (
            handler_input.response_builder
                .speak(speak_output)
                .ask(speak_output)
                .response
        )


class NoIntentHandler(AbstractRequestHandler):
    """Handler for NoIntentHandler."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_request_type("IntentRequest")(handler_input) and ask_utils.is_intent_name("AMAZON.NoIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speak_output = "NoIntent"

        return (
            handler_input.response_builder
                .speak(speak_output)
                .ask(speak_output)
                .response
        )



# The SkillBuilder object acts as the entry point for your skill, routing all request and response
# payloads to the handlers above. Make sure any new handlers or interceptors you've
# defined are included below. The order matters - they're processed top to bottom.

# from ask_sdk_core.skill_builder import CustomSkillBuilder
# from ask_sdk_core.api_client import DefaultApiClient
# sb = CustomSkillBuilder(api_client=DefaultApiClient())

sb = SkillBuilder()

sb.add_request_handler(LaunchRequestHandler())
sb.add_request_handler(YesIntentHandler())
sb.add_request_handler(NoIntentHandler())
sb.add_request_handler(MedicineAdderIntentHandler())
sb.add_request_handler(MedicineExistsIntentHandler())

lambda_handler = sb.lambda_handler()

Also, I would like to know whether how can I improve my conversation and achieve all the ask, prompts, reprompts and speak programatically with python. Currently I am using skill kit console and lambda_function.py. Would it be better to switch to Lambda Function Handler?

Please, provide your valuable suggestions.

K V
  • 1
  • 2
  • I solved it by replacing MedicineExistsIntent with AddQuantityIntent. Also, kept medicine_name slot in MedicineAdderIntent and quantity slot in AddQuantityIntent both with Dialog Delegation Strategy set to auto-delegation. Chained both intents with Dialog Delegation (referred color-picker skill). Now the problem is I am getting "AddQuantityIntent Triggered" instead of speak_output text. Trying to figure it out. – K V Nov 30 '22 at 06:05
  • Found that Dialog.Delegate directive does not send outputSpeech and reprompt from code. Need to use Dialog.ElicitSlot, Dialog.ConfirmSlot and Dialog.ConfirmIntent to use them. – K V Nov 30 '22 at 07:17

0 Answers0