From 69287c519852be81313b97b0ae882c01d4facfc1 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 18 Jun 2024 16:12:18 +0530 Subject: [PATCH 1/6] feat: err handling /stream --- application/api/answer/routes.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index 6c5e3e9c..20c8e1cf 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -9,13 +9,11 @@ import traceback from pymongo import MongoClient from bson.objectid import ObjectId - from application.core.settings import settings from application.llm.llm_creator import LLMCreator from application.retriever.retriever_creator import RetrieverCreator from application.error import bad_request - logger = logging.getLogger(__name__) mongo = MongoClient(settings.MONGO_URI) @@ -75,8 +73,10 @@ def run_async_chain(chain, question, chat_history): def get_data_from_api_key(api_key): data = api_key_collection.find_one({"key": api_key}) + + # # Raise custom exception if the API key is not found if data is None: - return bad_request(401, "Invalid API key") + raise Exception("API key is invalid", 401) return data @@ -128,10 +128,10 @@ def save_conversation(conversation_id, question, response, source_log_docs, llm) "content": "Summarise following conversation in no more than 3 " "words, respond ONLY with the summary, use the same " "language as the system \n\nUser: " - + question - + "\n\n" - + "AI: " - + response, + +question + +"\n\n" + +"AI: " + +response, }, { "role": "user", @@ -200,6 +200,7 @@ def complete_stream(question, retriever, conversation_id, user_api_key): @answer.route("/stream", methods=["POST"]) def stream(): + try: data = request.get_json() # get parameter from url question question = data["question"] @@ -273,6 +274,22 @@ def stream(): ), mimetype="text/event-stream", ) + except Exception as e: + message = e.args[0] + status_code = 400 + # # Custom exceptions with two arguments, index 1 as status code + if(len(e.args) >= 2): + status_code = e.args[1] + + def error_stream_generate(): + data = json.dumps({"type": "error", "error":message}) + yield f"data: {data}\n\n" + + return Response( + error_stream_generate(), + status=status_code, + mimetype="text/event-stream", + ) @answer.route("/api/answer", methods=["POST"]) From ad29d2765f0aaa78fd594b3e6eae41370810b918 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 20 Jun 2024 00:10:29 +0530 Subject: [PATCH 2/6] fix: add reducers to raise error, handle complete_stream() --- application/api/answer/routes.py | 69 +++++++++++-------- .../src/conversation/conversationSlice.ts | 16 +++++ 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index 20c8e1cf..3e3b5d6b 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -173,30 +173,33 @@ def get_prompt(prompt_id): def complete_stream(question, retriever, conversation_id, user_api_key): - response_full = "" - source_log_docs = [] - answer = retriever.gen() - for line in answer: - if "answer" in line: - response_full += str(line["answer"]) - data = json.dumps(line) + try: + response_full = "" + source_log_docs = [] + answer = retriever.gen() + for line in answer: + if "answer" in line: + response_full += str(line["answer"]) + data = json.dumps(line) + yield f"data: {data}\n\n" + elif "source" in line: + source_log_docs.append(line["source"]) + + llm = LLMCreator.create_llm( + settings.LLM_NAME, api_key=settings.API_KEY, user_api_key=user_api_key + ) + conversation_id = save_conversation( + conversation_id, question, response_full, source_log_docs, llm + ) + + # send data.type = "end" to indicate that the stream has ended as json + data = json.dumps({"type": "id", "id": str(conversation_id)}) yield f"data: {data}\n\n" - elif "source" in line: - source_log_docs.append(line["source"]) - - llm = LLMCreator.create_llm( - settings.LLM_NAME, api_key=settings.API_KEY, user_api_key=user_api_key - ) - conversation_id = save_conversation( - conversation_id, question, response_full, source_log_docs, llm - ) - - # send data.type = "end" to indicate that the stream has ended as json - data = json.dumps({"type": "id", "id": str(conversation_id)}) - yield f"data: {data}\n\n" - data = json.dumps({"type": "end"}) - yield f"data: {data}\n\n" - + data = json.dumps({"type": "end"}) + yield f"data: {data}\n\n" + except Exception: + data = json.dumps({"type": "error","error":"Please try again later. We apologize for any inconvenience."}) + yield f"data: {data}\n\n" @answer.route("/stream", methods=["POST"]) def stream(): @@ -274,23 +277,29 @@ def stream(): ), mimetype="text/event-stream", ) + + except ValueError: + message = "Malformed request body" + return Response( + error_stream_generate(message), + status=400, + mimetype="text/event-stream", + ) except Exception as e: + print("err",str(e)) message = e.args[0] status_code = 400 # # Custom exceptions with two arguments, index 1 as status code if(len(e.args) >= 2): status_code = e.args[1] - - def error_stream_generate(): - data = json.dumps({"type": "error", "error":message}) - yield f"data: {data}\n\n" - return Response( - error_stream_generate(), + error_stream_generate(message), status=status_code, mimetype="text/event-stream", ) - +def error_stream_generate(err_response): + data = json.dumps({"type": "error", "error":err_response}) + yield f"data: {data}\n\n" @answer.route("/api/answer", methods=["POST"]) def api_answer(): diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index 73880698..a5e1189c 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -68,6 +68,15 @@ export const fetchAnswer = createAsyncThunk( query: { conversationId: data.id }, }), ); + } else if (data.type === 'error') { + // set status to 'failed' + dispatch(conversationSlice.actions.setStatus('failed')); + dispatch( + conversationSlice.actions.raiseError({ + index: state.conversation.queries.length - 1, + message: data.error, + }), + ); } else { const result = data.answer; dispatch( @@ -191,6 +200,13 @@ export const conversationSlice = createSlice({ setStatus(state, action: PayloadAction) { state.status = action.payload; }, + raiseError( + state, + action: PayloadAction<{ index: number; message: string }>, + ) { + const { index, message } = action.payload; + state.queries[index].error = message; + }, }, extraReducers(builder) { builder From dba3b1c559ab6f298fc97b6840c29c09c2d08c07 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 20 Jun 2024 17:58:59 +0530 Subject: [PATCH 3/6] sort local vectors in latest first order --- application/api/answer/routes.py | 3 ++- application/api/user/routes.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index 3e3b5d6b..6c0a23b7 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -76,7 +76,7 @@ def get_data_from_api_key(api_key): # # Raise custom exception if the API key is not found if data is None: - raise Exception("API key is invalid", 401) + raise Exception("Invalid API Key, please generate new key", 401) return data @@ -200,6 +200,7 @@ def complete_stream(question, retriever, conversation_id, user_api_key): except Exception: data = json.dumps({"type": "error","error":"Please try again later. We apologize for any inconvenience."}) yield f"data: {data}\n\n" + return @answer.route("/stream", methods=["POST"]) def stream(): diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 51101492..625b1d6b 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -257,8 +257,8 @@ def combined_json(): } ] # structure: name, language, version, description, fullName, date, docLink - # append data from vectors_collection - for index in vectors_collection.find({"user": user}): + # append data from vectors_collection in sorted order in descending order of date + for index in vectors_collection.find({"user": user}).sort("date", -1): data.append( { "name": index["name"], From 70bb9477c5da0dbb3583be5e3edcfb8180ed1e80 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 20 Jun 2024 18:21:19 +0530 Subject: [PATCH 4/6] update err msg, if req fails from client --- frontend/src/conversation/conversationSlice.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index a5e1189c..7ab9f8fe 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -220,7 +220,7 @@ export const conversationSlice = createSlice({ } state.status = 'failed'; state.queries[state.queries.length - 1].error = - 'Something went wrong. Please try again later.'; + 'Something went wrong. Please check your internet connection.'; }); }, }); From 2e2149c110050f48111661d418cdc2ac6a96fd90 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 20 Jun 2024 19:40:29 +0100 Subject: [PATCH 5/6] fix: stream stuff --- application/api/answer/routes.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index 6c0a23b7..1fe9843d 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -185,20 +185,20 @@ def complete_stream(question, retriever, conversation_id, user_api_key): elif "source" in line: source_log_docs.append(line["source"]) - llm = LLMCreator.create_llm( - settings.LLM_NAME, api_key=settings.API_KEY, user_api_key=user_api_key - ) - conversation_id = save_conversation( - conversation_id, question, response_full, source_log_docs, llm + llm = LLMCreator.create_llm( + settings.LLM_NAME, api_key=settings.API_KEY, user_api_key=user_api_key ) - - # send data.type = "end" to indicate that the stream has ended as json - data = json.dumps({"type": "id", "id": str(conversation_id)}) - yield f"data: {data}\n\n" - data = json.dumps({"type": "end"}) - yield f"data: {data}\n\n" - except Exception: - data = json.dumps({"type": "error","error":"Please try again later. We apologize for any inconvenience."}) + conversation_id = save_conversation( + conversation_id, question, response_full, source_log_docs, llm + ) + + # send data.type = "end" to indicate that the stream has ended as json + data = json.dumps({"type": "id", "id": str(conversation_id)}) + yield f"data: {data}\n\n" + data = json.dumps({"type": "end"}) + yield f"data: {data}\n\n" + except Exception as e: + data = json.dumps({"type": "error","error": str(e)}) yield f"data: {data}\n\n" return From 4dde7eaea18c46731ec61245ab56b256cbf806bf Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 20 Jun 2024 19:51:35 +0100 Subject: [PATCH 6/6] feat: Improve error handling in /stream route --- application/api/answer/routes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index 1fe9843d..cafc8c66 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -198,7 +198,8 @@ def complete_stream(question, retriever, conversation_id, user_api_key): data = json.dumps({"type": "end"}) yield f"data: {data}\n\n" except Exception as e: - data = json.dumps({"type": "error","error": str(e)}) + data = json.dumps({"type": "error","error":"Please try again later. We apologize for any inconvenience.", + "error_exception": str(e)}) yield f"data: {data}\n\n" return