async def send_to_cai( self, request: DetectIntentRequest, ) -> Tuple[DetectIntentResponse, str]: text = request.query_input.text.text logger_console.warning({ "message": f"CAI-DetectIntentRequest to CAI, text input: {text}", "content": text, "text": text, "tags": ["text"], }) cai_response: DetectIntentResponse = await self.loops[ request.session]["loop"].run_in_executor( None, self.client.services.sessions.detect_intent, request, ) intent_name_cai = cai_response.query_result.intent.display_name logger_console.warning({ "message": f"CAI-DetectIntentResponse from CAI, intent_name_cai: {intent_name_cai}", "content": intent_name_cai, "intent_name_cai": intent_name_cai, "tags": ["text"], }) return cai_response, "cai_response"
def handle_if_intent_reached_number_triggers_max( response: session_pb2.DetectIntentResponse, nlu_client: Client) -> session_pb2.DetectIntentResponse: logger_console.warning( "Intent was triggered a maximum amount of times!") IntentMaxTriggerHandler.handle_if_intent_reached_number_triggers_max( response, nlu_client)
def quicksend_to_api(self, response: session_pb2.DetectIntentResponse, message: Optional[intent_pb2.Intent.Message], count: int) -> None: logger_console.warning({ "message": "quicksend_to_api not written, please subclass and implement" })
async def send_to_qa( self, request: DetectIntentRequest, ) -> Tuple[DetectIntentResponse, str]: text = request.query_input.text.text qa_request = qa_pb2.GetAnswerRequest( session_id=request.session, text=session_pb2.TextInput(text=text, language_code=f"{QA_LANG}"), max_num_answers=QA_MAX_ANSWERS, threshold_reader=QA_THRESHOLD_READER, threshold_retriever=QA_THRESHOLD_RETRIEVER, ) logger_console.warning({ "message": f"QA-GetAnswerRequest to QA, text input: {text}", "content": text, "text": text, "tags": ["text"], }) qa_response: DetectIntentResponse = await self.loops[ request.session]["loop"].run_in_executor( None, self.qa_client_stub.GetAnswer, qa_request, ) # intent_name_qa = qa_response.query_result.intent.display_name logger_console.warning({ "message": "QA-DetectIntentResponse from QA", "tags": ["text"] }) return qa_response, "qa_response"
def test_log(log_store): CONSOLE_TEXT = "console log" logger_console.addHandler(log_store) logger_console.debug(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["debug"] logger_console.info(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["info"] logger_console.warning(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["warning"] logger_console.error(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["error"] logger_console.critical(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["critical"] CONSOLE_TEXT = "debug log" logger_debug.addHandler(log_store) logger_debug.debug(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["debug"] logger_debug.info(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["info"] logger_debug.warning(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["warning"] logger_debug.error(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["error"] logger_debug.critical(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["critical"] CONSOLE_TEXT = "root log" logger_root.addHandler(log_store) logger_root.debug(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["debug"] logger_root.info(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["info"] logger_root.warning(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["warning"] logger_root.error(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["error"] logger_root.critical(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["critical"] assert len(log_store.messages["critical"]) == 3 log_store.reset() assert len(log_store.messages["critical"]) == 0 CONSOLE_TEXT = "root log 2" logger.addHandler(log_store) logger.debug(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["debug"] logger.info(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["info"] logger.warning(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["warning"] logger.error(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["error"] logger.critical(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["critical"]
def detect_intent( client: Client, response: session_pb2.DetectIntentResponse, text: str ) -> session_pb2.DetectIntentResponse: logger_console.info({"message": "detect intent triggered in bpi helpers", "tags": ["timing"]}) request = get_detect_intent_request(text=text, session=get_session_from_response(response=response),) logger_console.info({"message": "detect intent returned in bpi helpers", "tags": ["timing"]}) result = client.services.sessions.detect_intent(request) logger_console.warning(f"wrote {text}, received {result.query_result.fulfillment_messages}") return result
def trigger_function_not_implemented( self, response: session_pb2.DetectIntentResponse, message: intent_pb2.Intent.Message, trigger: str, found_triggers: Dict[str, List[str]], ) -> None: logger_console.warning({ "message": f"no function for the trigger {trigger}, please subclass and implement", "trigger": trigger, "content": found_triggers[trigger], })
def serve(self) -> None: logger_console.info(f"attempting to start server on port {PORT}") self._setup_server() logger_console.warning({"message": f"Server started on port {PORT}", "content": PORT}) logger_console.warning( { "message": f"using intent handlers list: {self.intent_handlers}", "content": self.intent_handlers, } ) try: while True: time.sleep(10) except KeyboardInterrupt: logger_console.info("Keyboard interrupt, shutting down") logger_console.info({"message": "server shut down", "tags": ["timing"]})
def check_session_id(self, request: DetectIntentRequest) -> None: session_pop_timeout: int = SESSION_TIMEOUT_MINUTES * 60 for session in self.loops.copy(): if time.time() - self.loops[session][ "timestamp"] > session_pop_timeout: # type: ignore logger_console.warning(f"Popping old session: {session}.") loop = self.loops.pop(session) loop["loop"].stop() loop["loop"].close() if request.session not in self.loops.keys(): self.loops[request.session] = { "loop": asyncio.new_event_loop(), "timestamp": time.time(), } logger_console.warning( f"New session in bpi: {request.session}. {len(self.loops)} sessions currently stored." )
def DetectIntent(self, request: DetectIntentRequest, context: grpc.ServicerContext) -> DetectIntentResponse: self.check_session_id(request) if len(request.query_input.text.text) > SENTENCE_TRUNCATION: logger_console.warning( f'The received text is too long, it will be truncated ' f'to {SENTENCE_TRUNCATION} characters!') truncated_text: TextInput = TextInput( text=request.query_input.text.text[:SENTENCE_TRUNCATION]) request.query_input.text.CopyFrom(truncated_text) response, response_name = self.handle_async(request) if response_name == "cai_response": # Process CAI response response = self.process_messages(response) response = self.process_intent_handler(response) return response
def handle_async( self, request: DetectIntentRequest, ) -> Tuple[DetectIntentResponse, str]: tasks: List[Coroutine] = [self.send_to_cai(request)] if QA_ACTIVE: tasks.append(self.send_to_qa(request)) while len(tasks): logger_console.warning( f"Starting async loop with {len(tasks)} tasks of type: {type(tasks)}" ) try: finished, tasks = self.loops[ request.session]["loop"].run_until_complete( asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED ) # type: ignore ) except Exception: logger_console.exception("Task returned an exception!") return DetectIntentResponse(), "exception" # Allow the cai_response to return early if it finishes first for task in finished: result = task.result() if result[1] == "cai_response": cai_response = result[0] intent_name_cai = cai_response.query_result.intent.display_name if intent_name_cai != "Default Fallback Intent" or not QA_ACTIVE: logger_console.warning( "CAI response good, returning early") return cai_response, "cai_response" # If the QA response finishes first, save it for later else: assert result[ 1] == "qa_response", "Somehow a different response snuck in!" qa_response = result[0] qa_confidence = qa_response.query_result.query_result.intent_detection_confidence logger_console.warning( f"QA confidence is {qa_confidence}, cutoff is {QA_THRESHOLD_READER}" ) messages = qa_response.query_result.query_result.fulfillment_messages if messages: return qa_response.query_result, "qa_response" logger_console.warning( "No response from QA, passing back Default Fallback.") return cai_response, "cai_response"
def trigger_intent( client: Client, session: str, intent_name: str, language: str = "de-DE", additional_contexts: Optional[List[context_pb2.Context]] = None, ) -> session_pb2.DetectIntentResponse: """ Trigger a specific intent in the NLU backend without intent matching. Args: client: nlu client session: full session to perform the trigger in ('parent/<PROJECT_ID>/agent/sessions/<SESSION_ID>') intent_name: intent that you want to trigger language: language of the project additional_contexts: if you want to add additional contexts to the session Returns: session_pb2.DetectIntentResponse """ if not additional_contexts: additional_contexts = [] logger_console.warning({"message": "triggering specific intent", "intent_name": intent_name}) trigger_context = create_context_struct( context=f"{session}/contexts/exact_intent", parameters=create_parameter_dict({"intent_name": intent_name}), lifespan_count=1, ) request = get_detect_intent_request( text=f"Triggering Specific Intent: {intent_name}", session=session, language=language, query_params=session_pb2.QueryParameters(contexts=[trigger_context, *additional_contexts]), ) result = client.services.sessions.detect_intent(request) logger_console.warning(f"triggered {intent_name}") return result
def DetectIntent( self, request: session_pb2.DetectIntentRequest, context: grpc.ServicerContext) -> session_pb2.DetectIntentResponse: try: if len(request.query_input.text.text) > SENTENCE_TRUNCATION: logger_console.warning( f'The received text is too long, it will be truncated ' f'to {SENTENCE_TRUNCATION} characters!') truncated_text: TextInput = TextInput( text=request.query_input.text.text[:SENTENCE_TRUNCATION]) request.query_input.text.CopyFrom(truncated_text) text = request.query_input.text.text except Exception as e: logger_console.exception( f"An issue was encountered in BPI:\n" f"\tSeems like the request query_input data was not properly formatted\n" f"\tDetails: {e}") text = "error" logger_console.warning({ "message": f"CAI-DetectIntentRequest to CAI, text input: {text}", "content": text, "text": text, "tags": ["text"], }) cai_response = self.perform_detect_intent(request) intent_name = cai_response.query_result.intent.display_name logger_console.warning({ "message": f"CAI-DetectIntentResponse from CAI, intent_name: {intent_name}", "content": intent_name, "intent_name": intent_name, "session_id": get_session_from_response(cai_response), "tags": ["text"], }) cai_response = self.process_messages(cai_response) return self.process_intent_handler(cai_response)
def handle_default_exit( response: session_pb2.DetectIntentResponse, nlu_client: Client) -> session_pb2.DetectIntentResponse: logger_console.warning("Default exit was triggered!") return response
def handle_default_fallback( response: session_pb2.DetectIntentResponse ) -> session_pb2.DetectIntentResponse: logger_console.warning("Default fallback was triggered!") return response
def test_level_manipulation(log_store): CONSOLE_TEXT = "console log" logger_console.addHandler(log_store) logger_console.debug(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["debug"] logger_console.setLevel(logging.INFO) log_store.reset() assert len(log_store.messages["debug"]) == 0 logger_console.debug(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["debug"] logger_console.info(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["info"] logger_console.setLevel(logging.WARN) log_store.reset() assert len(log_store.messages["info"]) == 0 logger_console.debug(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["debug"] logger_console.info(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["info"] logger_console.warning(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["warning"] logger_console.setLevel(logging.ERROR) log_store.reset() assert len(log_store.messages["warning"]) == 0 logger_console.debug(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["debug"] logger_console.info(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["info"] logger_console.warning(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["warning"] logger_console.error(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["error"] logger_console.setLevel(logging.CRITICAL) log_store.reset() assert len(log_store.messages["error"]) == 0 logger_console.debug(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["debug"] logger_console.info(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["info"] logger_console.warning(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["warning"] logger_console.error(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["error"] logger_console.critical(CONSOLE_TEXT) assert CONSOLE_TEXT in log_store.messages["critical"] logger_console.setLevel(logging.CRITICAL + 1) log_store.reset() assert len(log_store.messages["error"]) == 0 logger_console.debug(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["debug"] logger_console.info(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["info"] logger_console.warning(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["warning"] logger_console.error(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["error"] logger_console.critical(CONSOLE_TEXT) assert CONSOLE_TEXT not in log_store.messages["critical"]