extend dialogflow webhook deadline time for gpt api call - python

I am trying to use a script I found on the internet to extend the maximum time for a webhook request through Google Dialogflow (max 5 seconds to timeout). I need to extend the time because I make an API call to openai and it sometimes takes longer than 5 seconds. My idea was to start the 2 functions in parallel. The broadbridge_webhook_results() function is there to extend the time by triggering a followupEventInput at Dialogflow after 3,5 seconds, so a new call comes through Dialogflow and the 5 seconds start from new. This goes apparently up to 2 times. In the meantime the API call should be made towards openai. As soon as the API call was successful, the answer should be sent back to Dialogflow. Unfortunately, I am currently not getting anywhere and I think that the thread functionality was set up or understood incorrectly by me.
The following code I have so far:
import os
import openai
import time
import backoff
from datetime import datetime, timedelta
from flask import Flask, request, render_template
from threading import Thread
import asyncio
app = Flask(__name__)
conversation_History = ""
user_Input = ""
reply=''
answer = ""
#app.route('/')
def Default():
return render_template('index.html')
#backoff.on_exception(backoff.expo, openai.error.RateLimitError)
def ask(question):
global conversation_History
global answer
global reply
openai.api_key = os.getenv("gtp_Secret_Key")
#start_sequence = "\nAI:"
#restart_sequence = "\nHuman: "
response = openai.Completion.create(
model="text-davinci-003",
prompt="I am a chatbot from OpenAI. I'm happy to answer your questions.\nHuman:" + conversation_History + " "+ question +"\nAI: ",
temperature=0.9,
max_tokens=500,
top_p=1,
frequency_penalty=0,
presence_penalty=0.6,
stop=[" Human:", " AI:"]
)
conversation_History = conversation_History + question + "\nAI" + answer + "\nHuman:"
answer = response.choices[0].text
def broadbridge_webhook_results():
global answer
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
print("Current Time =", current_time)
extended_time = now + timedelta(seconds=3)
print("extended Time =", extended_time.time())
req = request.get_json(force=True)
action = req.get('queryResult').get('action')
reply=''
if action=='input.unknown' or action=='input.welcome':
time.sleep(3.5)
if now<=extended_time and not len(answer) == 0:
reply={
"fulfillmentText": answer,
"source": "webhookdata"
}
reply={
"followupEventInput": {
"name": "extent_webhook_deadline",
"languageCode": "en-US"
}
}
if action=='followupevent':
print("enter into first followup event")
time.sleep(3.5)
if now<=extended_time and not len(answer) == 0:
reply={
"fulfillmentText": answer,
"source": "webhookdata"
}
reply={
"followupEventInput": {
"name": "extent_webhook_deadline_2",
"languageCode": "en-US"
}
}
if action=='followupevent_2':
print("enter into second followup event")
time.sleep(3.5)
reply={
"fulfillmentText": answer,
"source": "webhookdata"
}
print("Final time of execution:=>", now.strftime("%H:%M:%S"))
#app.route('/webhook', methods=['GET', 'POST'])
def webhook():
global answer
global reply
answer=""
req = request.get_json(silent=True, force=True)
user_Input = req.get('queryResult').get('queryText')
Thread(target=broadbridge_webhook_results()).start()
Thread(target=ask(user_Input)).start()
return reply
#conversation_History = conversation_History + user_Input + "\nAI" + answer + "\nHuman:"
#if now<=extended_time and not len(answer) == 0:
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)

Dialogflow supports increasing the webhook execution time limit up to 30 seconds by increasing the webhook execution time limit. This is done through the "extend_webhook_deadline" setting in the Dialogflow agent configuration file.
To enable this setting you will need to do the following:
Access the Dialogflow Console
Select the agent for which you want to enable the setting
Click "Settings" on the left sidebar
Click "Export and Import"
Select "Export as ZIP"
Locate the "agent.json" file inside the downloaded ZIP file
Open the "agent.json" file with a text editor
Locate a "greeting" section
Add the following line "extend_webhook_deadline": true
Save the changes and import the "agent.json" file back into Dialogflow
Bear in mind that by enabling this setting, Dialogflow will charge an additional fee to increase the duration of the webhook.

Related

Cloud SQL instance not being stopped using Cloud Scheduler

Cloud SQL instance is not being stopped using Cloud Schedule after these steps:
Create a pub/sub topic that is supposed to trigger the cloud function.
Deploy a cloud function using the topic already created in step 1, with the below python (3.8) code file and requirements. (Entry point: start_stop)
Create a cloud scheduler job to trigger the cloud function on a regular basis with the topic created in step 1.
The payload is set to start [CloudSQL instance name] or stop [CloudSQL instance name] to start or stop the specified instance
Main.py:
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
import base64
from pprint import pprint
credentials = GoogleCredentials.get_application_default()
service = discovery.build('sqladmin', 'v1beta4', credentials=credentials, cache_discovery=False)
project = 'projectID'
def start_stop(event, context):
print(event)
pubsub_message = base64.b64decode(event['data']).decode('utf-8')
print(pubsub_message)
command, instance_name = pubsub_message.split(' ', 1)
if command == 'start':
start(instance_name)
elif command == 'stop':
stop(instance_name)
else:
print("unknown command " + command)
def start(instance_name):
print("starting " + instance_name)
patch(instance_name, "ALWAYS")
def stop(instance_name):
print("stopping " + instance_name)
patch(instance_name, "NEVER")
def patch(instance, activation_policy):
request = service.instances().get(project=project, instance=instance)
response = request.execute()
j = response["settings"]
settingsVersion = int(j["settingsVersion"])
dbinstancebody = {
"settings": {
"settingsVersion": settingsVersion,
"activationPolicy": activation_policy
}
}
dbinstancebody = {
"settings": {
"settingsVersion": response["settings"]["settingsVersion"],
"activationPolicy": activation_policy
}
}
request = service.instances().update(
project=project,
instance=instance,
body=dbinstancebody)
response = request.execute()
pprint(response)
Requirements.txt
google-api-python-client==1.10.0
google-auth-httplib2==0.0.4
google-auth==1.21.1
oauth2client==4.1.3
When I click RUN NOW button in the stop scheduler, it's executed successfully, but when I navigate to SQL instance, it is not stopped.
Can someone spot what I am missing? If you need more details, just let me know please, I have just started with GCP. :)
Tier configuration was missing in the body sent to the GCP api:
dbinstancebody = {
"settings": {
"settingsVersion": settingsVersion,
"tier": "db-custom-2-13312"
"activationPolicy": activation_policy
}
}
If you click on the deployed function you will see all the details (along with the graphs), but in the end, there are also the errors displayed. (My PC didn't fit all the screen, that's why I noticed this section later on 😅)

How to get current callers number from Twilio IVR system?

I am trying to get the current call's 'To' and 'From' numbers in Twilio. I am using the Twilio IVR system example at https://www.twilio.com/docs/voice/tutorials/ivr-phone-tree-python-flask.
This is my current code:
rom twilio.twiml.voice_response import VoiceResponse
from ivr_phone_tree_python import app
from ivr_phone_tree_python.view_helpers import twiml
#app.route('/ivr/welcome', methods=['POST'])
def welcome():
response = VoiceResponse()
# TODO
# get current calls to and from numbers here
# query subaccounts and main account and find which account the caller is calling based on the To number
with response.gather(
num_digits=1, action=url_for('menu'), method="POST"
) as g:
g.say(message="Thanks for calling the E T Phone Home Service. " +
"Please press 1 for directions." +
"Press 2 for a list of planets to call.", loop=3)
return twiml(response)
#app.route('/ivr/menu', methods=['POST'])
def menu():
selected_option = request.form['Digits']
option_actions = {'1': _give_instructions,
'2': _list_planets}
if option_actions.has_key(selected_option):
response = VoiceResponse()
option_actions[selected_option](response)
return twiml(response)
return _redirect_welcome()
#app.route('/ivr/planets', methods=['POST'])
def planets():
selected_option = request.form['Digits']
option_actions = {'2': "+12024173378",
'3': "+12027336386",
"4": "+12027336637"}
if selected_option in option_actions:
response = VoiceResponse()
response.dial(option_actions[selected_option])
return twiml(response)
return _redirect_welcome()
# private methods
def _give_instructions(response):
response.say("To get to your extraction point, get on your bike and go " +
"down the street. Then Left down an alley. Avoid the police" +
" cars. Turn left into an unfinished housing development." +
"Fly over the roadblock. Go past the moon. Soon after " +
"you will see your mother ship.",
voice="alice", language="en-GB")
response.say("Thank you for calling the E T Phone Home Service - the " +
"adventurous alien's first choice in intergalactic travel")
response.hangup()
return response
def _list_planets(response):
with response.gather(
numDigits=1, action=url_for('planets'), method="POST"
) as g:
g.say("To call the planet Broh doe As O G, press 2. To call the " +
"planet DuhGo bah, press 3. To call an oober asteroid " +
"to your location, press 4. To go back to the main menu " +
" press the star key.",
voice="alice", language="en-GB", loop=3)
return response
def _redirect_welcome():
response = VoiceResponse()
response.say("Returning to the main menu", voice="alice", language="en-GB")
response.redirect(url_for('welcome'))
return twiml(response)
If I wanted to use this IVR structure for all my subaccounts when I update to a paid account, would ngrok https output and post it to all subaccounts on my Twilio dashboard? That is the reason I want to know who the To and From callers are in the IVR call, so that I can distinguish which subaccount they are calling (and use the callers number for other reasons).
To get the To and From number, you would do something as follows request.POST['From'] and request.POST['To'] this is how you would do it in Django, you'll need to modify for flask. ngrok is a way for you to host your localhost website on the web so Twilio can communicate with it. You need to update the ngrok url Twilio needs to watch for, either manually in Twilio dashboard or through Twilio api. Also look into a TwilioML App.

Restart a conversation in Dialogflow

Hello stack overflow community,
I would like your help in the following problem:
Currently I am running a Dialogflow chatbot that has some intents and gives back some responses. The chat bot works perfectly until now. For your information, I use webhook to get back responses.
Here is a screenshot of my intents flow: dialogflow chatbot
What I struggle to do is to give the user the opportunity to rerun the whole conversation from the start. Do you know how can I achieve this in Dialogflow?
Python code snippet to run webhook:
app = Flask(__name__)
# default route
#app.route('/')
def index():
return 'Chatbot is online and running!'
#app.route('/webhook', methods=['GET', 'POST'])
def webhook():
# return response
req = request.get_json(force=True, silent = True)
action = req.get('queryResult').get('action')
intent_name = req.get('queryResult').get('intent').get('displayName')
if action == "get_results" and intent_name == 'KellyMovieBot':
return make_response(jsonify(suggest_movie(req)))
elif action == "ask_question" and intent_name == 'ask_first_time':
return make_response(jsonify(propose_second_movie(req)))
elif action == "thanks_giving_end" and intent_name == "thanks_giving":
return make_response(jsonify(thanks(req)))
elif action == "rerun_conversation" and intent_name == "rerun_conversation":
user_answer = req.get('queryResult').get('parameters').get('user_answer')
if user_answer == "No" or user_answer == "no":
return {'fulfillmentText': "Enjoy your film!"}
elif user_answer == "Yes" or user_answer == "yes":
return {'fulfillmentText': "Let's start again..."}
# run the app
if __name__ == '__main__':
app.run()
Note: The functions suggest_movie(), propose_second_movie(), thanks() are three functions that have been created in order to return 3 different outputs based on the communication flow.
What I want is find the correct syntax that will allow me to RERUN the whole conversation again if the user says 'Yes' else Dialogflow should end the conversation.
I would appreciate any help or advise you can give me!
You can do this by sending a follow-up event rather than a response. In this case when the user responds with yes, in your code, you'd return:
{
"followupEventInput": {
"name": "KellyMovieBot",
"languageCode": "en-US"
}
}
Make sure your KellyMovieBot intent has the same event name. You don't have to use the specified value, but the event name as sent in the response should also match the event name as configured on the intent.
https://cloud.google.com/dialogflow/docs/events-custom#invoke_event_from_webhook

Using Twilio Python to Get and Set Cookies

The definitive guide to Twilio conversation tracking uses outdated code as twiml no longer has a method Response(). Is it still possible to track conversations with cookies?
from flask import Flask, request, make_response
from datetime import datetime, timedelta
from twilio import twiml
app = Flask(__name__)
#app.route("/sms")
def sms():
#get the cookie value, or default to zero
messagecount = int(request.cookies.get('messagecount',0))
messagecount += 1
twml = twiml.Response()
twml.sms("You've sent " + str(messagecount) + " messages in this conversation so far")
resp = make_response(str(twml))
expires=datetime.utcnow() + timedelta(hours=4)
resp.set_cookie('messagecount',value=str(messagecount),expires=expires.strftime('%a, %d %b %Y %H:%M:%S GMT'))
return resp
if __name__ == "__main__":
app.debug = True
app.run()
I got the error AttributeError: module 'twilio.twiml' has no attribute 'Response' when I try to run the code supplied by the Twilio tutorial on their blog post twilio.com/blog/2014/07/…. The solution (haven't tried, but will probably work) is to use the code example here: twilio.com/docs/guides/… – Max D. 1 min ago
Twilio developer evangelist here.
The blog post you are referring to there was written in 2014 and the Python library has been updated since then. It shouldn't take much to update the code from the post though, and as long as Flask's cookie handling hasn't changed either, it will all still work.
The issue is that instead of twiml.Response we now have separate twiml.MessagingResponse and twiml.VoiceResponse. Your app is for messaging, so we'll include the MessagingResponse. Then we need to update how you generate the message from:
twml = twiml.Response()
twml.sms("You've sent " + str(messagecount) + " messages in this conversation so far")
to:
twml = MessagingResponse()
twml.message("You've sent " + str(messagecount) + " messages in this conversation so far")
All together that looks like:
from flask import Flask, request, make_response
from datetime import datetime, timedelta
from twilio.twiml.messaging_response import MessagingResponse
app = Flask(__name__)
#app.route("/sms")
def sms():
#get the cookie value, or default to zero
messagecount = int(request.cookies.get('messagecount',0))
messagecount += 1
twml = MessagingResponse()
twml.message("You've sent " + str(messagecount) + " messages in this conversation so far")
resp = make_response(str(twml))
expires=datetime.utcnow() + timedelta(hours=4)
resp.set_cookie('messagecount',value=str(messagecount),expires=expires.strftime('%a, %d %b %Y %H:%M:%S GMT'))
return resp
if __name__ == "__main__":
app.debug = True
app.run()
As you've pointed out, the guide to SMS conversations with cookies in the documentation is more up to date (and will be kept up to date).
Let me know if this helps.

How to speed up JSON for a flask application?

I'm currently implementing a webapp in flask. It's an app that does a visualization of data gathered. Each page or section will always have a GET call and each call will return a JSON response which then will be processed into displayed data.
The current problem is that some calculation is needed before the function could return a JSON response. This causes some of the response to arrive slower than others and thus making the page loads a bit slow. How do I properly deal with this? I have read into caching in flask and wonder whether that is what the app need right now. I have also researched a bit into implementing a Redis-Queue. I'm not really sure which is the correct method.
Any help or insights would be appreciated. Thanks in advance
Here are some ideas:
If the source data that you use for your calculations is not likely to change often then you can run the calculations once and save the results. Then you can serve the results directly for as long as the source data remains the same.
You can save the results back to your database, or as you suggest, you can save them in a faster storage such as Redis. Based on your description I suspect the big performance gain will be in not doing calculations so often, the difference between storing in a regular database vs. Redis or similar is probably not significant in comparison.
If the data changes often then you will still need to do calculations frequently. For such a case an option that you have is to push the calculations to the client. Your Flask app can just return the source data in JSON format and then the browser can do the processing on the user's computer.
I hope this helps.
You can use
copy_current_request_context and Redis, Thread
It is helpful when you need long time to make JSON response.
The first request maybe slow, but next request will faster.
Example
from datetime import timedelta, datetime
from threading import Thread
from . import dbb, redis_client
from flask import Blueprint, request, jsonify, flash, after_this_request, copy_current_request_context, \
current_app, send_from_directory
from .models import Shop, Customers
def save_customer_json_to_redis(request):
response_json = {
"have_customer": False,
"status": False,
"anythingelse": None,
"message":"False, you have to check..."
}
#print(request.data)
headers = request.headers
Authorization = headers['Authorization']
token = Authorization.replace("Bearer", "")
phone = request.args.get('phone')
if phone is not None and phone != "":
print('token', token, "phone", phone)
now = datetime.utcnow() + timedelta(hours=7)
shop = Shop.query.filter(Shop.private_token == token, Shop.ended_date > now, Shop.shop_active == True).first()
customer = Customers.query.filter_by(shop_id=shop.id, phone=phone).first()
if customer:
redis_name = f'{shop.id}_api_v2_customer_phone_{phone}_customer_id_{customer.id}'
print(redis_name)
response_json["anythingelse"] = ...# do want you want, it need long time to do
response_json["status"] = True
response_json["message"] = "Successful"
redis_client.set(redis_name, json.dumps(response_json)) #Save JSON to Redis
#app.route('/api/v2/customer', methods=['GET'])
def api_customer():
#copy_current_request_context
def do_update_customer_to_redis():# this function to save JSON you want to response next time to Redis
save_customer_json_to_redis(request)
Thread(target=do_update_customer_to_redis).start()
response_json = {
"have_customer": False,
"status": False,
"anythingelse": {},
"message": "False, you have to check..."
}
#print(request.data)
headers = request.headers
Authorization = headers['Authorization']
token = Authorization.replace("Bearer", "")
phone = request.args.get('phone')
if phone is not None and phone != "":
print('token', token, "phone", phone)
now = datetime.utcnow() + timedelta(hours=7)
shop = Shop.query.filter(Shop.private_token == token, Shop.ended_date > now,Shop.shop_active == True).first()
customer = Customers.query.filter_by(shop_id=shop.id, phone=phone).first()
if customer:
redis_name = f'{shop.id}_api_v2_customer_phone_{phone}_customer_id_{customer.id}'
print(redis_name)
try:
response_json = json.loads(redis_client.get(redis_name)) # if have json from app
print("json.loads(redis_client.get(redis_name))")
except Exception as e:
print("json.loads(redis_client.get(redis_name))", e)
#do any thing you want to response json
response_json["anythingelse"] = ...# do want you want, it need long time to do
response_json["message"]= ...#do want you want
#redis_client.set(redis_name, json.dumps(response_json))
response_json["status"] = True
response_json["message"] = "Successful"
return jsonify(response_json)
In the init.py
from flask import Flask
from flask_cors import CORS
from flask_mail import Mail
from flask_sqlalchemy import SQLAlchemy
from redis import Redis
# init SQLAlchemy so we can use it later in our models
dbb = SQLAlchemy(session_options={"autoflush": False})
redis_client = Redis(
host='localhost',
port='6379',
password='your_redis_password'
)
def create_app():
app = Flask(__name__)
...........

Categories

Resources