From b6441683c6aa308ed62dca4edaaeef714b3889db Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 10 Nov 2023 18:29:52 -0800 Subject: [PATCH 01/10] Increase reference text on 1st expansion to 3 lines and 140 characters --- src/khoj/interface/web/chat.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/khoj/interface/web/chat.html b/src/khoj/interface/web/chat.html index f15489ec..e1c9979d 100644 --- a/src/khoj/interface/web/chat.html +++ b/src/khoj/interface/web/chat.html @@ -417,6 +417,9 @@ To get started, just start typing below. You can also type / to see a list of co display: block; } + div.references { + padding-top: 8px; + } div.reference { display: grid; grid-template-rows: auto; From 8585976f378f6fe76d297d7e664a7fb2409c7fb7 Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 10 Nov 2023 21:44:53 -0800 Subject: [PATCH 02/10] Revert "Use notes in system prompt, rather than in the user message" This reverts commit e695b9ab8cdfb265cba109b0659767f0b5bef9f7. --- src/khoj/processor/conversation/openai/gpt.py | 6 ++---- src/khoj/processor/conversation/prompts.py | 19 ------------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/khoj/processor/conversation/openai/gpt.py b/src/khoj/processor/conversation/openai/gpt.py index 71088817..27782573 100644 --- a/src/khoj/processor/conversation/openai/gpt.py +++ b/src/khoj/processor/conversation/openai/gpt.py @@ -111,15 +111,13 @@ def converse( return iter([prompts.no_notes_found.format()]) elif conversation_command == ConversationCommand.General or is_none_or_empty(compiled_references): conversation_primer = prompts.general_conversation.format(query=user_query) - personality = prompts.personality.format(current_date=current_date) else: - conversation_primer = prompts.general_conversation.format(query=user_query) - personality = prompts.personality_with_notes.format(current_date=current_date, references=compiled_references) + conversation_primer = prompts.notes_conversation.format(query=user_query, references=compiled_references) # Setup Prompt with Primer or Conversation History messages = generate_chatml_messages_with_context( conversation_primer, - personality, + prompts.personality.format(current_date=current_date), conversation_log, model, max_prompt_size, diff --git a/src/khoj/processor/conversation/prompts.py b/src/khoj/processor/conversation/prompts.py index 78a42995..d15fc773 100644 --- a/src/khoj/processor/conversation/prompts.py +++ b/src/khoj/processor/conversation/prompts.py @@ -21,25 +21,6 @@ Today is {current_date} in UTC. """.strip() ) -personality_with_notes = PromptTemplate.from_template( - """ -You are Khoj, a smart, inquisitive and helpful personal assistant. -Use your general knowledge and the past conversation with the user as context to inform your responses. -You were created by Khoj Inc. with the following capabilities: - -- You *CAN REMEMBER ALL NOTES and PERSONAL INFORMATION FOREVER* that the user ever shares with you. -- You cannot set reminders. -- Say "I don't know" or "I don't understand" if you don't know what to say or if you don't know the answer to a question. -- You ask friendly, inquisitive follow-up QUESTIONS to collect more detail about their experiences and better understand the user's intent. These questions end with a question mark and seek to better understand the user. -- Sometimes the user will share personal information that needs to be remembered, like an account ID or a residential address. These can be acknowledged with a simple "Got it" or "Okay". - -Note: More information about you, the company or other Khoj apps can be found at https://khoj.dev. -Today is {current_date} in UTC. - -User's Notes: -{references} -""".strip() -) ## General Conversation ## -- general_conversation = PromptTemplate.from_template( From cba371678d7fc7b00d0ca4eccec359d137bdede2 Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 10 Nov 2023 22:27:24 -0800 Subject: [PATCH 03/10] Stop OpenAI chat from emitting reference notes directly in chat body The Chat models sometime output reference notes directly in the chat body in unformatted form, specifically as Notes:\n['. Prevent that. Reference notes are shown in clean, formatted form anyway --- src/khoj/processor/conversation/openai/gpt.py | 1 + src/khoj/processor/conversation/openai/utils.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/khoj/processor/conversation/openai/gpt.py b/src/khoj/processor/conversation/openai/gpt.py index 27782573..b86ebc6b 100644 --- a/src/khoj/processor/conversation/openai/gpt.py +++ b/src/khoj/processor/conversation/openai/gpt.py @@ -134,4 +134,5 @@ def converse( temperature=temperature, openai_api_key=api_key, completion_func=completion_func, + model_kwargs={"stop": ["Notes:\n["]}, ) diff --git a/src/khoj/processor/conversation/openai/utils.py b/src/khoj/processor/conversation/openai/utils.py index 130532e0..dce72e1f 100644 --- a/src/khoj/processor/conversation/openai/utils.py +++ b/src/khoj/processor/conversation/openai/utils.py @@ -69,15 +69,15 @@ def completion_with_backoff(**kwargs): reraise=True, ) def chat_completion_with_backoff( - messages, compiled_references, model_name, temperature, openai_api_key=None, completion_func=None + messages, compiled_references, model_name, temperature, openai_api_key=None, completion_func=None, model_kwargs=None ): g = ThreadedGenerator(compiled_references, completion_func=completion_func) - t = Thread(target=llm_thread, args=(g, messages, model_name, temperature, openai_api_key)) + t = Thread(target=llm_thread, args=(g, messages, model_name, temperature, openai_api_key, model_kwargs)) t.start() return g -def llm_thread(g, messages, model_name, temperature, openai_api_key=None): +def llm_thread(g, messages, model_name, temperature, openai_api_key=None, model_kwargs=None): callback_handler = StreamingChatCallbackHandler(g) chat = ChatOpenAI( streaming=True, @@ -86,6 +86,7 @@ def llm_thread(g, messages, model_name, temperature, openai_api_key=None): model_name=model_name, # type: ignore temperature=temperature, openai_api_key=openai_api_key or os.getenv("OPENAI_API_KEY"), + model_kwargs=model_kwargs, request_timeout=20, max_retries=1, client=None, From c4364b91004a82bf0d1c6ca8da29c2891c72bb4d Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 10 Nov 2023 22:45:00 -0800 Subject: [PATCH 04/10] Weaken asking follow-up qs and q&a mode in notes prompt to OpenAI models - Notes prompt doesn't need to be so tuned to question answering. User could just want to talk about life. The notes need to be used to response to those, not necessarily only retrieve answers from notes - System and notes prompts were forcing asking follow-up questions a little too much. Reduce strength of follow-up question asking --- src/khoj/processor/conversation/prompts.py | 9 ++++----- tests/test_openai_chat_director.py | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/khoj/processor/conversation/prompts.py b/src/khoj/processor/conversation/prompts.py index d15fc773..c11c38ba 100644 --- a/src/khoj/processor/conversation/prompts.py +++ b/src/khoj/processor/conversation/prompts.py @@ -13,7 +13,7 @@ You were created by Khoj Inc. with the following capabilities: - You *CAN REMEMBER ALL NOTES and PERSONAL INFORMATION FOREVER* that the user ever shares with you. - You cannot set reminders. - Say "I don't know" or "I don't understand" if you don't know what to say or if you don't know the answer to a question. -- You ask friendly, inquisitive follow-up QUESTIONS to collect more detail about their experiences and better understand the user's intent. These questions end with a question mark and seek to better understand the user. +- Ask crisp follow-up questions to get additional context, when the answer cannot be inferred from the provided notes or past conversations. - Sometimes the user will share personal information that needs to be remembered, like an account ID or a residential address. These can be acknowledged with a simple "Got it" or "Okay". Note: More information about you, the company or other Khoj apps can be found at https://khoj.dev. @@ -89,14 +89,13 @@ conversation_llamav2 = PromptTemplate.from_template( ## -- notes_conversation = PromptTemplate.from_template( """ -Using my personal notes and our past conversations as context, answer the following question. -Ask crisp follow-up questions to get additional context, when the answer cannot be inferred from the provided notes or past conversations. -These questions should end with a question mark. +Use my personal notes and our past conversations to inform your response. +Ask crisp follow-up questions to get additional context, when a helpful response cannot be provided from the provided notes or past conversations. Notes: {references} -Question: {query} +Query: {query} """.strip() ) diff --git a/tests/test_openai_chat_director.py b/tests/test_openai_chat_director.py index c7d2e0ec..a8c85787 100644 --- a/tests/test_openai_chat_director.py +++ b/tests/test_openai_chat_director.py @@ -308,6 +308,7 @@ def test_ask_for_clarification_if_not_enough_context_in_question(chat_client_no_ "which of namita's sons", "the birth order", "provide more context", + "provide me with more context", ] assert response.status_code == 200 assert any([expected_response in response_message.lower() for expected_response in expected_responses]), ( From b34d4fa741082b842a3408721203e40df6f6c249 Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Sat, 11 Nov 2023 00:33:49 -0800 Subject: [PATCH 05/10] Save config, update index on save of Github, Notion config in web app Reduce user confusion by joining config update with index updation for each content type. So only a single click required to configure any content type instead of two clicks on two separate pages --- .../web/content_source_github_input.html | 26 ++++++++++++++----- .../web/content_source_notion_input.html | 25 +++++++++++++----- src/khoj/routers/api.py | 26 ++++++++++++------- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/src/khoj/interface/web/content_source_github_input.html b/src/khoj/interface/web/content_source_github_input.html index ff82b1f2..705e5672 100644 --- a/src/khoj/interface/web/content_source_github_input.html +++ b/src/khoj/interface/web/content_source_github_input.html @@ -124,6 +124,7 @@ return; } + // Save Github config on server const csrfToken = document.cookie.split('; ').find(row => row.startsWith('csrftoken'))?.split('=')[1]; fetch('/api/config/data/content-source/github', { method: 'POST', @@ -137,15 +138,26 @@ }) }) .then(response => response.json()) + .then(data => { data["status"] === "ok" ? data : Promise.reject(data) }) + .catch(error => { + document.getElementById("success").innerHTML = "⚠️ Failed to update settings"; + document.getElementById("success").style.display = "block"; + return; + }); + + // Index Github content on server + fetch('/api/update?t=github') + .then(response => response.json()) + .then(data => { data["status"] == "ok" ? data : Promise.reject(data) }) .then(data => { - if (data["status"] == "ok") { - document.getElementById("success").innerHTML = "✅ Successfully updated. Go to your settings page to complete setup."; - document.getElementById("success").style.display = "block"; - } else { - document.getElementById("success").innerHTML = "⚠️ Failed to update settings."; - document.getElementById("success").style.display = "block"; - } + document.getElementById("success").innerHTML = "✅ Successfully updated"; + document.getElementById("success").style.display = "block"; }) + .catch(error => { + document.getElementById("success").innerHTML = "⚠️ Failed to update settings"; + document.getElementById("success").style.display = "block"; + }); + }); {% endblock %} diff --git a/src/khoj/interface/web/content_source_notion_input.html b/src/khoj/interface/web/content_source_notion_input.html index 18eb5a7f..4bc4a20d 100644 --- a/src/khoj/interface/web/content_source_notion_input.html +++ b/src/khoj/interface/web/content_source_notion_input.html @@ -41,6 +41,7 @@ return; } + // Save Notion config on server const csrfToken = document.cookie.split('; ').find(row => row.startsWith('csrftoken'))?.split('=')[1]; fetch('/api/config/data/content-source/notion', { method: 'POST', @@ -53,15 +54,25 @@ }) }) .then(response => response.json()) + .then(data => { data["status"] === "ok" ? data : Promise.reject(data) }) + .catch(error => { + document.getElementById("success").innerHTML = "⚠️ Failed to update settings"; + document.getElementById("success").style.display = "block"; + return; + }); + + // Index Notion content on server + fetch('/api/update?t=notion') + .then(response => response.json()) + .then(data => { data["status"] == "ok" ? data : Promise.reject(data) }) .then(data => { - if (data["status"] == "ok") { - document.getElementById("success").innerHTML = "✅ Successfully updated. Go to your settings page to complete setup."; - document.getElementById("success").style.display = "block"; - } else { - document.getElementById("success").innerHTML = "⚠️ Failed to update settings."; - document.getElementById("success").style.display = "block"; - } + document.getElementById("success").innerHTML = "✅ Successfully updated"; + document.getElementById("success").style.display = "block"; }) + .catch(error => { + document.getElementById("success").innerHTML = "⚠️ Failed to update settings"; + document.getElementById("success").style.display = "block"; + }); }); {% endblock %} diff --git a/src/khoj/routers/api.py b/src/khoj/routers/api.py index ddfe9bc1..4e050eee 100644 --- a/src/khoj/routers/api.py +++ b/src/khoj/routers/api.py @@ -177,11 +177,15 @@ async def set_content_config_github_data( user = request.user.object - await adapters.set_user_github_config( - user=user, - pat_token=updated_config.pat_token, - repos=updated_config.repos, - ) + try: + await adapters.set_user_github_config( + user=user, + pat_token=updated_config.pat_token, + repos=updated_config.repos, + ) + except Exception as e: + logger.error(e, exc_info=True) + raise HTTPException(status_code=500, detail="Failed to set Github config") update_telemetry_state( request=request, @@ -205,10 +209,14 @@ async def set_content_config_notion_data( user = request.user.object - await adapters.set_notion_config( - user=user, - token=updated_config.token, - ) + try: + await adapters.set_notion_config( + user=user, + token=updated_config.token, + ) + except Exception as e: + logger.error(e, exc_info=True) + raise HTTPException(status_code=500, detail="Failed to set Github config") update_telemetry_state( request=request, From 325cb0f7fbf919c25963929eb0286c53908e75c8 Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Sat, 11 Nov 2023 00:46:16 -0800 Subject: [PATCH 06/10] Show message in Save button of Github, Notion config save in web app Show the success, failure message only temporarily. Previously it stuck around after clicking save until page refresh --- .../web/content_source_github_input.html | 29 ++++++++++++++++--- .../web/content_source_notion_input.html | 26 ++++++++++++++--- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/khoj/interface/web/content_source_github_input.html b/src/khoj/interface/web/content_source_github_input.html index 705e5672..cce0d083 100644 --- a/src/khoj/interface/web/content_source_github_input.html +++ b/src/khoj/interface/web/content_source_github_input.html @@ -46,6 +46,9 @@