From ec86a8cef09729f5c56a2700c4668c92ae521138 Mon Sep 17 00:00:00 2001 From: David Xue Date: Tue, 23 Jan 2024 18:54:25 -0800 Subject: [PATCH 1/4] Add request preprecessor function --- api/ask_astro/services/questions.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/api/ask_astro/services/questions.py b/api/ask_astro/services/questions.py index 1057d52c..9730a945 100644 --- a/api/ask_astro/services/questions.py +++ b/api/ask_astro/services/questions.py @@ -2,6 +2,7 @@ from __future__ import annotations import asyncio +import re import time from logging import getLogger @@ -27,6 +28,20 @@ async def _update_firestore_request(request: AskAstroRequest) -> None: ) +def _preprocess_request(request: AskAstroRequest) -> None: + if len(request.prompt) > 20000: + error_msg = "Question text is too long. Please try making a new thread and shortening your question." + request.response = error_msg + raise Exception(error_msg) + if len(request.prompt) == 0: + error_msg = "Question text cannot be empty. Please try again with a different question." + request.response = error_msg + raise Exception(error_msg) + if len(request.messages) > 10: + request.messages = request.messages[-10:] + request.prompt = re.sub(r"(? None: """ @@ -43,6 +58,9 @@ async def answer_question(request: AskAstroRequest) -> None: request.status = "in_progress" await _update_firestore_request(request) + # Preprocess request + _preprocess_request(request=request) + # Run the question answering chain with callbacks.collect_runs() as cb: result = await asyncio.to_thread( @@ -74,7 +92,9 @@ async def answer_question(request: AskAstroRequest) -> None: except Exception as e: # If there's an error, mark the request as errored and add it to the database request.status = "error" - request.response = "Sorry, something went wrong. Please try again later." + if request.response == "": + request.response = "Sorry, something went wrong. Please try again later." + await _update_firestore_request(request) # Propagate the error From 0f7c4ade2a84beb3293ea59afefa8fdf01bd9e40 Mon Sep 17 00:00:00 2001 From: David Xue Date: Thu, 25 Jan 2024 14:18:51 -0800 Subject: [PATCH 2/4] Update api/ask_astro/services/questions.py Co-authored-by: Wei Lee --- api/ask_astro/services/questions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/ask_astro/services/questions.py b/api/ask_astro/services/questions.py index 9730a945..b533d387 100644 --- a/api/ask_astro/services/questions.py +++ b/api/ask_astro/services/questions.py @@ -33,7 +33,7 @@ def _preprocess_request(request: AskAstroRequest) -> None: error_msg = "Question text is too long. Please try making a new thread and shortening your question." request.response = error_msg raise Exception(error_msg) - if len(request.prompt) == 0: + if not request.prompt: error_msg = "Question text cannot be empty. Please try again with a different question." request.response = error_msg raise Exception(error_msg) From 0128617560b51dfd81f376bc91bd1b7a1c83b316 Mon Sep 17 00:00:00 2001 From: David Xue Date: Thu, 25 Jan 2024 15:49:27 -0800 Subject: [PATCH 3/4] Add custom error types and change error propagation --- api/ask_astro/services/questions.py | 33 ++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/api/ask_astro/services/questions.py b/api/ask_astro/services/questions.py index 9730a945..fdf8a458 100644 --- a/api/ask_astro/services/questions.py +++ b/api/ask_astro/services/questions.py @@ -6,7 +6,7 @@ import time from logging import getLogger -from tenacity import retry, stop_after_attempt, wait_exponential +from tenacity import retry, retry_if_not_exception_type, stop_after_attempt, wait_exponential from ask_astro.clients.firestore import firestore_client from ask_astro.config import FirestoreCollections @@ -15,6 +15,14 @@ logger = getLogger(__name__) +class InvalidRequestPromptError(Exception): + """Exception raised when the prompt string in the request object is invalid""" + + +class QuestionAnsweringError(Exception): + """Exception raised when an error occurs during question answering""" + + async def _update_firestore_request(request: AskAstroRequest) -> None: """ Update the Firestore database with the given request. @@ -32,17 +40,23 @@ def _preprocess_request(request: AskAstroRequest) -> None: if len(request.prompt) > 20000: error_msg = "Question text is too long. Please try making a new thread and shortening your question." request.response = error_msg - raise Exception(error_msg) + raise InvalidRequestPromptError(error_msg) if len(request.prompt) == 0: error_msg = "Question text cannot be empty. Please try again with a different question." request.response = error_msg - raise Exception(error_msg) + raise InvalidRequestPromptError(error_msg) + # take the most recent 10 question and answers in the history if len(request.messages) > 10: request.messages = request.messages[-10:] + # parse out backslack escape character to prevent hybrid search erroring out with invalid syntax string request.prompt = re.sub(r"(? None: """ Performs the actual question answering logic and updates the request object. @@ -87,15 +101,14 @@ async def answer_question(request: AskAstroRequest) -> None: if doc.metadata.get("docLink", "").startswith("https://") ] - await _update_firestore_request(request) - except Exception as e: # If there's an error, mark the request as errored and add it to the database request.status = "error" - if request.response == "": + if not isinstance(e, InvalidRequestPromptError): request.response = "Sorry, something went wrong. Please try again later." + raise QuestionAnsweringError("An error occurred during question answering.") from e + else: + raise e + finally: await _update_firestore_request(request) - - # Propagate the error - raise Exception("An error occurred during question answering.") from e From 428e264f46eb59453d2e5cd876086edb75d09f83 Mon Sep 17 00:00:00 2001 From: David Xue Date: Mon, 29 Jan 2024 16:04:31 -0800 Subject: [PATCH 4/4] Add prompt preprocessing nums to env var --- api/ask_astro/config.py | 7 +++++++ api/ask_astro/services/questions.py | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/api/ask_astro/config.py b/api/ask_astro/config.py index 976306b6..b167ba99 100644 --- a/api/ask_astro/config.py +++ b/api/ask_astro/config.py @@ -70,3 +70,10 @@ class CohereConfig: """Contains the config variables for the Cohere API.""" rerank_top_n = int(os.environ.get("COHERE_RERANK_TOP_N", 10)) + + +class PromptPreprocessingConfig: + """Contains the config variables for user prompt's preprocessing function.""" + + max_char = int(os.environ.get("PROMPT_PREPROCESSING_MAX_CHAR", 20000)) + max_chat_history_len = int(os.environ.get("PROMPT_PREPROCESSING_MAX_CHAT_HISTORY_LEN", 10)) diff --git a/api/ask_astro/services/questions.py b/api/ask_astro/services/questions.py index 45a33235..edac6d21 100644 --- a/api/ask_astro/services/questions.py +++ b/api/ask_astro/services/questions.py @@ -9,7 +9,7 @@ from tenacity import retry, retry_if_not_exception_type, stop_after_attempt, wait_exponential from ask_astro.clients.firestore import firestore_client -from ask_astro.config import FirestoreCollections +from ask_astro.config import FirestoreCollections, PromptPreprocessingConfig from ask_astro.models.request import AskAstroRequest, Source logger = getLogger(__name__) @@ -37,7 +37,7 @@ async def _update_firestore_request(request: AskAstroRequest) -> None: def _preprocess_request(request: AskAstroRequest) -> None: - if len(request.prompt) > 20000: + if len(request.prompt) > PromptPreprocessingConfig.max_char: error_msg = "Question text is too long. Please try making a new thread and shortening your question." request.response = error_msg raise InvalidRequestPromptError(error_msg) @@ -46,7 +46,7 @@ def _preprocess_request(request: AskAstroRequest) -> None: request.response = error_msg raise InvalidRequestPromptError(error_msg) # take the most recent 10 question and answers in the history - if len(request.messages) > 10: + if len(request.messages) > PromptPreprocessingConfig.max_chat_history_len: request.messages = request.messages[-10:] # parse out backslack escape character to prevent hybrid search erroring out with invalid syntax string request.prompt = re.sub(r"(?