hello.I’m facing an issue where the values of slots in your Rasa chatbot reset after the user provides a response. This problem occurs particularly when the action_ask_questions
action is called after the action_check_response
action. The slots job_id
, headers
, and user_id
are supposed to retain their values throughout the conversation, but they appear to reset, causing the chatbot to fetch these values again from the last message in the tracker. This leads to errors, especially when the system expects numeric values but receives string responses instead. here is my code from rasa_sdk import Action, Tracker
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.events import SlotSet, ReminderScheduled, FollowupAction, AllSlotsReset
from typing import Dict, List, Text, Any
from datetime import datetime, timedelta
import pytz
import logging
import jwt
from asgiref.sync import sync_to_async
import sys
import os
import django
from django.conf import settings
logger = logging.getLogger(name)
Ajouter le chemin du projet Django à PYTHONPATH
sys.path.append(r’C:\Users\Asus\Desktop\chatbot\ProjetDjango’)
Initialiser les paramètres Django si ce n’est pas déjà fait
if not settings.configured: os.environ.setdefault(‘DJANGO_SETTINGS_MODULE’, ‘ProjetDjango.settings’) django.setup()
from ProjetDjango.models import APIMessages, Application, UserResponse, JobQuestion
@sync_to_async def save_response_to_db(application_id, question_id, response_text): response, created = UserResponse.objects.update_or_create( application_id=application_id, question_id=question_id, defaults={ ‘response_text’: response_text, ‘response_date’: datetime.now(pytz.timezone(‘Europe/Paris’)) } ) action = “Updated” if not created else “Saved new” logger.info(f"{action} response to DB: application_id={application_id}, question_id={question_id}, response_text={response_text}")
@sync_to_async def get_job_id_for_user(user_id): try: application = Application.objects.filter(user_id=user_id).latest(‘timestamp’) return application.job_id except Application.DoesNotExist: return None
class ActionAskQuestions(Action): def name(self) → str: return “action_ask_questions”
async def run(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: dict):
logger.debug(f"Received message: {tracker.latest_message}")
# Vérifier si les en-têtes et le job_id sont déjà dans les slots, sinon les récupérer à partir des messages API
job_id = tracker.get_slot("job_id")
headers = tracker.get_slot("headers")
user_id = tracker.get_slot("user_id")
if not job_id or not headers or not user_id:
try:
last_message = await sync_to_async(APIMessages.objects.latest)('timestamp')
logger.debug(f"last_message : {last_message }")
metadata = last_message.metadata
headers = metadata.get("headers")
# Décoder le token pour obtenir user_id
token = headers.get('Authorization').split()[1]
decoded_payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
user_id = decoded_payload.get('user_id')
job_id = metadata.get("job_id")
logger.debug(f"job_id : {job_id }")
if not job_id:
dispatcher.utter_message(text="No valid job ID found in the message history.")
logger.error("No valid job ID found.")
return [SlotSet("job_id", None), SlotSet("headers", None), SlotSet("user_id", None)]
logger.debug(f"Headers from metadata: {headers}")
logger.debug(f"user from headers: {user_id}")
logger.debug(f"Processing application for job ID {job_id}")
return [SlotSet("job_id", job_id), SlotSet("headers", headers), SlotSet("user_id", user_id), FollowupAction("action_ask_questions")]
except APIMessages.DoesNotExist:
dispatcher.utter_message(text="No messages found in the API messages table.")
logger.error("No messages found in the API messages table.")
return [SlotSet("job_id", None), SlotSet("headers", None), SlotSet("user_id", None)]
logger.debug(f"Headers from slot: {headers}")
logger.debug(f"Processing application for job ID {job_id}")
if job_id:
try:
questions = await sync_to_async(self.get_questions_for_job)(job_id)
if not questions:
dispatcher.utter_message(text="No questions found for the job ID.")
return []
question_index = tracker.get_slot("question_index") or 0
question_answers = tracker.get_slot("question_answers") or []
job_title = f"Job ID {job_id}"
all_questions = []
for question in questions:
all_questions.extend([q.strip() for q in question.split('/') if q.strip()])
if all_questions and question_index < len(all_questions):
current_question = all_questions[question_index]
dispatcher.utter_message(text=f"Question {question_index + 1}: {current_question}")
logger.info(f"Asking question {question_index + 1}: {current_question}")
reminder_time = datetime.now(pytz.timezone('Europe/Paris')) + timedelta(minutes=2)
reminder_event = ReminderScheduled(
"EXTERNAL_no_user_input",
trigger_date_time=reminder_time,
name=f"reminder_for_{job_title}_{question_index}",
kill_on_user_message=True
)
return [
SlotSet("question_index", question_index + 1),
SlotSet("current_question", current_question),
SlotSet("last_question_time", datetime.now(pytz.timezone('Europe/Paris')).isoformat()),
reminder_event,
FollowupAction("action_listen")
]
else:
dispatcher.utter_message(text="Thank you for your participation! We have completed the questions for this post.")
logger.info("All questions answered.")
return [SlotSet("all_questions_answered", True), AllSlotsReset()]
except JobQuestion.DoesNotExist:
dispatcher.utter_message(text="Error retrieving questions for the job.")
logger.error("Error retrieving questions for the job.")
return [SlotSet("job_id", None), SlotSet("headers", None), SlotSet("user_id", None)]
else:
dispatcher.utter_message(text="No valid job ID found in the message history.")
logger.error("No valid job ID found.")
return [SlotSet("job_id", None), SlotSet("headers", None), SlotSet("user_id", None)]
def get_questions_for_job(self, job_id):
questions = JobQuestion.objects.filter(job_id=job_id).values_list('question', flat=True)
return list(questions)
class ActionCheckResponse(Action): def name(self) → str: return “action_check_response”
async def run(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: dict) -> list:
user_intent = tracker.latest_message.get("intent", {}).get("name")
current_question = tracker.get_slot("current_question")
question_answers = tracker.get_slot("question_answers") or []
entities = tracker.latest_message.get("entities", [])
if user_intent == "job_response" and entities:
response = entities[0].get("value")
if response:
question_answers.append((current_question, response))
logger.info(f"Received response: {response} for question: {current_question}")
try:
application = await sync_to_async(Application.objects.latest)('application_id')
application_id = application.application_id
question_obj = await sync_to_async(JobQuestion.objects.get)(question__icontains=current_question)
question_id = question_obj.question_id
await save_response_to_db(application_id, question_id, response)
except JobQuestion.DoesNotExist:
logger.error(f"JobQuestion matching query '{current_question}' does not exist.")
return [SlotSet("question_answers", question_answers), FollowupAction("action_ask_questions")]
return []
class ActionHandleNoUserInput(Action): def name(self) → str: return “action_handle_no_user_input”
async def run(self, dispatcher, tracker, domain):
try:
job_title = tracker.get_slot("job_title")
question_answers = tracker.get_slot("question_answers") or []
last_question_time = datetime.fromisoformat(tracker.get_slot("last_question_time"))
elapsed_time = (datetime.now(pytz.timezone('Europe/Paris')) - last_question_time).total_seconds()
if (elapsed_time > 120):
current_question = tracker.get_slot("current_question")
question_answers.append((current_question, "no response"))
logger.info(f"No response received for question: {current_question}")
application = await sync_to_async(Application.objects.latest)('application_id')
application_id = application.application_id
# Sauvegarder la réponse "no response" et marquer l'interview comme terminé
try:
question_obj = await sync_to_async(JobQuestion.objects.get)(question__icontains=current_question)
question_id = question_obj.question_id
await save_response_to_db(application_id, question_id, "no response")
except JobQuestion.DoesNotExist:
logger.error(f"JobQuestion matching query '{current_question}' does not exist.")
dispatcher.utter_message(text="You did not respond in the specified time, the interview is terminated.")
logger.info("Sent message: You did not respond in the specified time, the interview is terminated.")
return [AllSlotsReset()]
return []
except Exception as e:
logger.error("Failed to handle no user input: " + str(e))
return []
class ActionCheckTimeout(Action): def name(self) → Text: return “action_check_timeout”
def run(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:
last_question_time_str = tracker.get_slot("last_question_time")
if last_question_time_str:
last_question_time = datetime.fromisoformat(last_question_time_str)
elapsed_time = (datetime.now(pytz.timezone('Europe/Paris')) - last_question_time).total_seconds()
if elapsed_time > 120:
dispatcher.utter_message(text="You did not respond in the specified time, the interview is terminated.")
logger.info("Sent timeout message: You did not respond in the specified time, the interview is terminated.")
return [SlotSet("question_index", None), SlotSet("job_title", None), SlotSet("last_question_time", last_question_time_str), AllSlotsReset()]
return []
and also the logs that appears in the shell 2024-06-10 12:31:29 INFO rasa_sdk.endpoint - Starting action endpoint server… 2024-06-10 12:31:29 INFO rasa_sdk.executor - Registered function for ‘action_ask_questions’. 2024-06-10 12:31:29 INFO rasa_sdk.executor - Registered function for ‘action_check_response’. 2024-06-10 12:31:29 INFO rasa_sdk.executor - Registered function for ‘action_handle_no_user_input’. 2024-06-10 12:31:29 INFO rasa_sdk.executor - Registered function for ‘action_check_timeout’. 2024-06-10 12:31:29 INFO rasa_sdk.endpoint - Starting plugins… 2024-06-10 12:31:29 DEBUG rasa_sdk.plugin - No plugins found: No module named ‘rasa_sdk_plugins’ 2024-06-10 12:31:29 INFO rasa_sdk.endpoint - Action endpoint is up and running on http://0.0.0.0:5055 2024-06-10 12:31:29 DEBUG rasa_sdk.utils - Using the default number of Sanic workers (1). 2024-06-10 12:31:45 DEBUG rasa_sdk.executor - Received request to run ‘action_ask_questions’ 2024-06-10 12:31:45 DEBUG actions.actions - Received message: {‘intent’: {‘name’: ‘affirm’, ‘confidence’: 0.9984036087989807}, ‘entities’: , ‘text’: ‘yes’, ‘message_id’: ‘54fc342920dc48fb91eec041ee5cb8ec’, ‘metadata’: {}, ‘text_tokens’: [[0, 3]], ‘intent_ranking’: [{‘name’: ‘affirm’, ‘confidence’: 0.9984036087989807}, {‘name’: ‘greet’, ‘confidence’: 0.0005639670416712761}, {‘name’: ‘bot_challenge’, ‘confidence’: 0.0002074303338304162}, {‘name’: ‘start_application’, ‘confidence’: 0.00018906929471995682}, {‘name’: ‘mood_great’, ‘confidence’: 0.00015406652528326958}, {‘name’: ‘deny’, ‘confidence’: 0.00014801209908910096}, {‘name’: ‘mood_unhappy’, ‘confidence’: 0.00010086417751153931}, {‘name’: ‘job_response’, ‘confidence’: 5.817746205138974e-05}, {‘name’: ‘EXTERNAL_did_not_respond’, ‘confidence’: 5.7690493122208863e-05}, {‘name’: ‘inform’, ‘confidence’: 4.0838131099008024e-05}], ‘response_selector’: {‘all_retrieval_intents’: , ‘default’: {‘response’: {‘responses’: None, ‘confidence’: 0.0, ‘intent_response_key’: None, ‘utter_action’: ‘utter_None’}, ‘ranking’: }}} 2024-06-10 12:31:45 DEBUG actions.actions - last_message : 21 - /start_application 2024-06-10 12:31:45 DEBUG actions.actions - job_id : 1 2024-06-10 12:31:45 DEBUG actions.actions - Headers from metadata: {‘Authorization’: ‘Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzE4MDE4ODE3LCJpYXQiOjE3MTgwMTUyMTcsImp0aSI6IjczYmMzYWQ0YzhlZDRlMjc5OGU2ZDc1ZGM4ZjM5OGJmIiwidXNlcl9pZCI6MjF9.1e2LwTjCt1pPN23QB-Agm53IMQlx-hkQjcMATpTSPJk’} 2024-06-10 12:31:45 DEBUG actions.actions - user from headers: 21 2024-06-10 12:31:45 DEBUG actions.actions - Processing application for job ID 1 2024-06-10 12:31:45 DEBUG rasa_sdk.executor - Finished running ‘action_ask_questions’ 2024-06-10 12:31:47 DEBUG rasa_sdk.executor - Received request to run ‘action_ask_questions’ 2024-06-10 12:31:47 DEBUG actions.actions - Received message: {‘intent’: {‘name’: ‘affirm’, ‘confidence’: 0.9984036087989807}, ‘entities’: , ‘text’: ‘yes’, ‘message_id’: ‘54fc342920dc48fb91eec041ee5cb8ec’, ‘metadata’: {}, ‘text_tokens’: [[0, 3]], ‘intent_ranking’: [{‘name’: ‘affirm’, ‘confidence’: 0.9984036087989807}, {‘name’: ‘greet’, ‘confidence’: 0.0005639670416712761}, {‘name’: ‘bot_challenge’, ‘confidence’: 0.0002074303338304162}, {‘name’: ‘start_application’, ‘confidence’: 0.00018906929471995682}, {‘name’: ‘mood_great’, ‘confidence’: 0.00015406652528326958}, {‘name’: ‘deny’, ‘confidence’: 0.00014801209908910096}, {‘name’: ‘mood_unhappy’, ‘confidence’: 0.00010086417751153931}, {‘name’: ‘job_response’, ‘confidence’: 5.817746205138974e-05}, {‘name’: ‘EXTERNAL_did_not_respond’, ‘confidence’: 5.7690493122208863e-05}, {‘name’: ‘inform’, ‘confidence’: 4.0838131099008024e-05}], ‘response_selector’: {‘all_retrieval_intents’: , ‘default’: {‘response’: {‘responses’: None, ‘confidence’: 0.0, ‘intent_response_key’: None, ‘utter_action’: ‘utter_None’}, ‘ranking’: }}} 2024-06-10 12:31:47 DEBUG actions.actions - Headers from slot: {‘Authorization’: ‘Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzE4MDE4ODE3LCJpYXQiOjE3MTgwMTUyMTcsImp0aSI6IjczYmMzYWQ0YzhlZDRlMjc5OGU2ZDc1ZGM4ZjM5OGJmIiwidXNlcl9pZCI6MjF9.1e2LwTjCt1pPN23QB-Agm53IMQlx-hkQjcMATpTSPJk’} 2024-06-10 12:31:47 DEBUG actions.actions - Processing application for job ID 1 2024-06-10 12:31:47 INFO actions.actions - Asking question 1: What else does a Wedding Consultant help with? 2024-06-10 12:31:47 DEBUG rasa_sdk.executor - Finished running ‘action_ask_questions’ 2024-06-10 12:32:09 DEBUG rasa_sdk.executor - Received request to run ‘action_check_response’ 2024-06-10 12:32:12 DEBUG rasa_sdk.executor - Finished running ‘action_check_response’ 2024-06-10 12:32:23 DEBUG rasa_sdk.executor - Received request to run ‘action_check_response’ 2024-06-10 12:32:23 INFO actions.actions - Received response: my answer for question: What else does a Wedding Consultant help with? 2024-06-10 12:32:23 INFO actions.actions - Saved new response to DB: application_id=584, question_id=1, response_text=my answer 2024-06-10 12:32:23 DEBUG rasa_sdk.executor - Finished running ‘action_check_response’ 2024-06-10 12:32:25 DEBUG rasa_sdk.executor - Received request to run ‘action_ask_questions’ 2024-06-10 12:32:25 DEBUG actions.actions - Received message: {‘intent’: {‘name’: ‘job_response’, ‘confidence’: 0.999651312828064}, ‘entities’: [{‘entity’: ‘response’, ‘start’: 16, ‘end’: 25, ‘confidence_entity’: 0.9779952764511108, ‘value’: ‘my answer’, ‘extractor’: ‘DIETClassifier’, ‘processors’: [‘EntitySynonymMapper’]}], ‘text’: ‘the response is my answer’, ‘message_id’: ‘b05f9acaaea7496985b117f302e0a88b’, ‘metadata’: {}, ‘text_tokens’: [[0, 3], [4, 12], [13, 15], [16, 18], [19, 25]], ‘intent_ranking’: [{‘name’: ‘job_response’, ‘confidence’: 0.999651312828064}, {‘name’: ‘EXTERNAL_did_not_respond’, ‘confidence’: 0.0002438767987769097}, {‘name’: ‘deny’, ‘confidence’: 2.3025791961117648e-05}, {‘name’: ‘bot_challenge’, ‘confidence’: 1.3832501281285658e-05}, {‘name’: ‘mood_great’, ‘confidence’: 1.2423614862200338e-05}, {‘name’: ‘goodbye’, ‘confidence’: 1.0116561497852672e-05}, {‘name’: ‘start_application’, ‘confidence’: 1.0019623005064204e-05}, {‘name’: ‘bonjour’, ‘confidence’: 9.722833965497557e-06}, {‘name’: ‘affirm’, ‘confidence’: 8.647617505630478e-06}, {‘name’: ‘inform’, ‘confidence’: 6.499853043351322e-06}], ‘response_selector’: {‘all_retrieval_intents’: , ‘default’: {‘response’: {‘responses’: None, ‘confidence’: 0.0, ‘intent_response_key’: None, ‘utter_action’: ‘utter_None’}, ‘ranking’: }}} 2024-06-10 12:32:25 DEBUG actions.actions - Headers from slot: the response is my answer 2024-06-10 12:32:25 DEBUG actions.actions - Processing application for job ID the response is my answer 2024-06-10 12:32:25 ERROR rasa_sdk.endpoint - Exception occurred during execution of request <Request: POST /webhook> Traceback (most recent call last): File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\fields_init_.py”, line 2053, in get_prep_value return int(value) ValueError: invalid literal for int() with base 10: ‘the response is my answer’
The above exception was the direct cause of the following exception:
Traceback (most recent call last): File “handle_request”, line 83, in handle_request ) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\rasa_sdk\endpoint.py”, line 113, in webhook result = await executor.run(action_call) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\rasa_sdk\executor.py”, line 398, in run events = await utils.call_potential_coroutine( File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\rasa_sdk\utils.py”, line 297, in call_potential_coroutine return await coroutine_or_return_value File “C:\Users\Asus\Desktop\chatbot\ChatbotRassa\actions\actions.py”, line 95, in run questions = await sync_to_async(self.get_questions_for_job)(job_id) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\asgiref\sync.py”, line 468, in call ret = await asyncio.shield(exec_coro) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\concurrent\futures\thread.py”, line 57, in run result = self.fn(*self.args, **self.kwargs) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\asgiref\sync.py”, line 522, in thread_handler return func(*args, **kwargs) File “C:\Users\Asus\Desktop\chatbot\ChatbotRassa\actions\actions.py”, line 142, in get_questions_for_job questions = JobQuestion.objects.filter(job_id=job_id).values_list(‘question’, flat=True) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\manager.py”, line 87, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\query.py”, line 1436, in filter return self._filter_or_exclude(False, args, kwargs) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\query.py”, line 1454, in _filter_or_exclude clone._filter_or_exclude_inplace(negate, args, kwargs) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\query.py”, line 1461, in _filter_or_exclude_inplace self._query.add_q(Q(*args, **kwargs)) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\sql\query.py”, line 1546, in add_q clause, _ = self._add_q(q_object, self.used_aliases) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\sql\query.py”, line 1577, in add_q child_clause, needed_inner = self.build_filter( File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\sql\query.py”, line 1492, in build_filter condition = self.build_lookup(lookups, col, value) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\sql\query.py”, line 1319, in build_lookup lookup = lookup_class(lhs, rhs) File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\lookups.py”, line 27, in init self.rhs = self.get_prep_lookup() File “C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\fields\related_lookups.py”, line 166, in get_prep_lookup self.rhs = target_field.get_prep_value(self.rhs) File "C:\Users\Asus\anaconda3\envs\ProjetBot\lib\site-packages\django\db\models\fields_init.py", line 2055, in get_prep_value raise e.class( ValueError: Field ‘job_id’ expected a number but got ‘the response is my answer’. can someone of you help me to fix this problem plz