From 1a56344f12fdc0ad333af707d22e75e2522d1166 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 10 Nov 2023 10:10:07 -0800 Subject: [PATCH 01/36] Remove the old syncData reference as it no longer exists --- src/interface/desktop/renderer.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/interface/desktop/renderer.js b/src/interface/desktop/renderer.js index 7e3dba4c..7d0d906e 100644 --- a/src/interface/desktop/renderer.js +++ b/src/interface/desktop/renderer.js @@ -198,12 +198,6 @@ khojKeyInput.addEventListener('blur', async () => { khojKeyInput.value = token; }); -const syncButton = document.getElementById('sync-data'); -syncButton.addEventListener('click', async () => { - loadingBar.style.display = 'block'; - await window.syncDataAPI.syncData(false); -}); - const syncForceButton = document.getElementById('sync-force'); syncForceButton.addEventListener('click', async () => { loadingBar.style.display = 'block'; From e62788ad7909fb90a18af484b75bd90af93104fa Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 10 Nov 2023 13:51:56 -0800 Subject: [PATCH 02/36] Await result for determining if user has entries --- src/khoj/routers/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/khoj/routers/api.py b/src/khoj/routers/api.py index 5228a6fb..32793d7c 100644 --- a/src/khoj/routers/api.py +++ b/src/khoj/routers/api.py @@ -663,7 +663,7 @@ async def extract_references_and_questions( if conversation_type == ConversationCommand.General: return compiled_references, inferred_queries, q - if not sync_to_async(EntryAdapters.user_has_entries)(user=user): + if not await sync_to_async(EntryAdapters.user_has_entries)(user=user): logger.warning( "No content index loaded, so cannot extract references from knowledge base. Please configure your data sources and update the index to chat with your notes." ) From 262a8574d1af4b36bcc906c90bdfab231772186a Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 10 Nov 2023 14:00:58 -0800 Subject: [PATCH 03/36] Add a test to verify that a user without data sucessfully returns a respones to the /search endpoint --- tests/conftest.py | 29 +++++++++++++++++++++++++++++ tests/test_client.py | 20 +++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 59104123..08ff7033 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -85,6 +85,22 @@ def default_user2(): ) +@pytest.mark.django_db +@pytest.fixture +def default_user3(): + """ + This user should not have any data associated with it + """ + if KhojUser.objects.filter(username="default3").exists(): + return KhojUser.objects.get(username="default3") + + return KhojUser.objects.create( + username="default3", + email="default3@example.com", + password="default3", + ) + + @pytest.mark.django_db @pytest.fixture def api_user(default_user): @@ -111,6 +127,19 @@ def api_user2(default_user2): ) +@pytest.mark.django_db +@pytest.fixture +def api_user3(default_user3): + if KhojApiUser.objects.filter(user=default_user3).exists(): + return KhojApiUser.objects.get(user=default_user3) + + return KhojApiUser.objects.create( + user=default_user3, + name="api-key", + token="kk-diff-secret-3", + ) + + @pytest.fixture(scope="session") def search_models(search_config: SearchConfig): search_models = SearchModels() diff --git a/tests/test_client.py b/tests/test_client.py index c105c605..1894577c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -16,7 +16,7 @@ from khoj.utils.state import search_models, content_index, config from khoj.search_type import text_search, image_search from khoj.utils.rawconfig import ContentConfig, SearchConfig from khoj.processor.org_mode.org_to_entries import OrgToEntries -from database.models import KhojUser +from database.models import KhojUser, KhojApiUser from database.adapters import EntryAdapters @@ -351,6 +351,24 @@ def test_different_user_data_not_accessed(client, sample_org_data, default_user: assert len(response.json()) == 1 and response.json()["detail"] == "Forbidden" +# ---------------------------------------------------------------------------------------------------- +@pytest.mark.django_db(transaction=True) +def test_user_no_data_returns_empty(client, sample_org_data, api_user3: KhojApiUser): + # Arrange + token = api_user3.token + headers = {"Authorization": "Bearer " + token} + user_query = quote("How to git install application?") + + # Act + response = client.get(f"/api/search?q={user_query}&n=1&t=org", headers=headers) + + # Assert + assert response.status_code == 200 + # assert actual response has no data as the default_user3, though other users have data + assert len(response.json()) == 0 + assert response.json() == [] + + def get_sample_files_data(): return [ ("files", ("path/to/filename.org", "* practicing piano", "text/org")), From cec932d88ae78d6b0df1ecc9e72333f5bb70d7d6 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 10 Nov 2023 14:37:11 -0800 Subject: [PATCH 04/36] Update prompt so that GPT is more context aware with its capabilities --- src/khoj/processor/conversation/openai/gpt.py | 29 ++---------------- src/khoj/processor/conversation/prompts.py | 30 +++++++++++-------- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/src/khoj/processor/conversation/openai/gpt.py b/src/khoj/processor/conversation/openai/gpt.py index 73b4f176..27782573 100644 --- a/src/khoj/processor/conversation/openai/gpt.py +++ b/src/khoj/processor/conversation/openai/gpt.py @@ -20,27 +20,6 @@ from khoj.utils.helpers import ConversationCommand, is_none_or_empty logger = logging.getLogger(__name__) -def summarize(session, model, api_key=None, temperature=0.5, max_tokens=200): - """ - Summarize conversation session using the specified OpenAI chat model - """ - messages = [ChatMessage(content=prompts.summarize_chat.format(), role="system")] + session - - # Get Response from GPT - logger.debug(f"Prompt for GPT: {messages}") - response = completion_with_backoff( - messages=messages, - model_name=model, - temperature=temperature, - max_tokens=max_tokens, - model_kwargs={"stop": ['"""'], "frequency_penalty": 0.2}, - openai_api_key=api_key, - ) - - # Extract, Clean Message from GPT's Response - return str(response.content).replace("\n\n", "") - - def extract_questions( text, model: Optional[str] = "gpt-4", @@ -131,16 +110,14 @@ def converse( completion_func(chat_response=prompts.no_notes_found.format()) 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(current_date=current_date, query=user_query) + conversation_primer = prompts.general_conversation.format(query=user_query) else: - conversation_primer = prompts.notes_conversation.format( - current_date=current_date, query=user_query, 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, - prompts.personality.format(), + 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 ef9100e0..d15fc773 100644 --- a/src/khoj/processor/conversation/prompts.py +++ b/src/khoj/processor/conversation/prompts.py @@ -4,19 +4,31 @@ from langchain.prompts import PromptTemplate ## Personality ## -- -personality = PromptTemplate.from_template("You are Khoj, a smart, inquisitive and helpful personal assistant.") +personality = 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. +""".strip() +) ## General Conversation ## -- general_conversation = PromptTemplate.from_template( """ -Using your general knowledge and our past conversations as context, answer the following question. -Current Date: {current_date} - -Question: {query} +{query} """.strip() ) + no_notes_found = PromptTemplate.from_template( """ I'm sorry, I couldn't find any relevant notes to respond to your message. @@ -80,7 +92,6 @@ 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. -Current Date: {current_date} Notes: {references} @@ -98,13 +109,6 @@ Question: {query} ) -## Summarize Chat -## -- -summarize_chat = PromptTemplate.from_template( - f"{personality.format()} Summarize the conversation from your first person perspective" -) - - ## Summarize Notes ## -- summarize_notes = PromptTemplate.from_template( From e695b9ab8cdfb265cba109b0659767f0b5bef9f7 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 10 Nov 2023 15:09:33 -0800 Subject: [PATCH 05/36] Use notes in system prompt, rather than in the user message --- src/khoj/processor/conversation/openai/gpt.py | 6 ++++-- src/khoj/processor/conversation/prompts.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/khoj/processor/conversation/openai/gpt.py b/src/khoj/processor/conversation/openai/gpt.py index 27782573..71088817 100644 --- a/src/khoj/processor/conversation/openai/gpt.py +++ b/src/khoj/processor/conversation/openai/gpt.py @@ -111,13 +111,15 @@ 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.notes_conversation.format(query=user_query, references=compiled_references) + conversation_primer = prompts.general_conversation.format(query=user_query) + personality = prompts.personality_with_notes.format(current_date=current_date, references=compiled_references) # Setup Prompt with Primer or Conversation History messages = generate_chatml_messages_with_context( conversation_primer, - prompts.personality.format(current_date=current_date), + personality, conversation_log, model, max_prompt_size, diff --git a/src/khoj/processor/conversation/prompts.py b/src/khoj/processor/conversation/prompts.py index d15fc773..78a42995 100644 --- a/src/khoj/processor/conversation/prompts.py +++ b/src/khoj/processor/conversation/prompts.py @@ -21,6 +21,25 @@ 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 54d4fd0e084db3de9392e157671512d61b01b843 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 10 Nov 2023 16:46:34 -0800 Subject: [PATCH 06/36] Add chat_model data for logging selected models to telemetry --- src/khoj/routers/api.py | 25 ++++++++++++++----------- src/khoj/routers/helpers.py | 6 +++++- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/khoj/routers/api.py b/src/khoj/routers/api.py index 32793d7c..ddfe9bc1 100644 --- a/src/khoj/routers/api.py +++ b/src/khoj/routers/api.py @@ -606,7 +606,7 @@ async def chat( return StreamingResponse(iter([formatted_help]), media_type="text/event-stream", status_code=200) # Get the (streamed) chat response from the LLM of choice. - llm_response = await agenerate_chat_response( + llm_response, chat_metadata = await agenerate_chat_response( defiltered_query, meta_log, compiled_references, @@ -615,6 +615,19 @@ async def chat( user, ) + chat_metadata.update({"conversation_command": conversation_command.value}) + + update_telemetry_state( + request=request, + telemetry_type="api", + api="chat", + client=client, + user_agent=user_agent, + referer=referer, + host=host, + metadata=chat_metadata, + ) + if llm_response is None: return Response(content=llm_response, media_type="text/plain", status_code=500) @@ -634,16 +647,6 @@ async def chat( response_obj = {"response": actual_response, "context": compiled_references} - update_telemetry_state( - request=request, - telemetry_type="api", - api="chat", - client=client, - user_agent=user_agent, - referer=referer, - host=host, - ) - return Response(content=json.dumps(response_obj), media_type="application/json", status_code=200) diff --git a/src/khoj/routers/helpers.py b/src/khoj/routers/helpers.py index ebb661ef..99c1b281 100644 --- a/src/khoj/routers/helpers.py +++ b/src/khoj/routers/helpers.py @@ -132,6 +132,8 @@ def generate_chat_response( chat_response = None logger.debug(f"Conversation Type: {conversation_command.name}") + metadata = {} + try: partial_completion = partial( _save_to_conversation_log, @@ -179,8 +181,10 @@ def generate_chat_response( tokenizer_name=conversation_config.tokenizer, ) + metadata.update({"chat_model": conversation_config.chat_model}) + except Exception as e: logger.error(e, exc_info=True) raise HTTPException(status_code=500, detail=str(e)) - return chat_response + return chat_response, metadata From 9b6c5ddba42431e6a6ee44e566ed52879e31d63e Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 10 Nov 2023 16:32:22 -0800 Subject: [PATCH 07/36] Update action row padding in cards on config page of web app --- src/khoj/interface/web/base_config.html | 12 +++++--- src/khoj/interface/web/config.html | 40 ++++++++++++------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/khoj/interface/web/base_config.html b/src/khoj/interface/web/base_config.html index 309fdba6..05119fad 100644 --- a/src/khoj/interface/web/base_config.html +++ b/src/khoj/interface/web/base_config.html @@ -109,7 +109,7 @@ display: grid; grid-template-rows: repeat(3, 1fr); gap: 8px; - padding: 24px 16px; + padding: 24px 16px 8px; width: 320px; height: 180px; background: var(--background-color); @@ -162,10 +162,13 @@ color: grey; font-size: 16px; } - .card-button-row { + .card-description-row { + padding-top: 4px; + } + .card-action-row { display: grid; - grid-template-columns: auto; - text-align: right; + grid-auto-flow: row; + justify-content: left; } .card-button { border: none; @@ -287,6 +290,7 @@ select#chat-models { margin-bottom: 0; + padding: 8px; } diff --git a/src/khoj/interface/web/config.html b/src/khoj/interface/web/config.html index b2b7fbb3..34a4f642 100644 --- a/src/khoj/interface/web/config.html +++ b/src/khoj/interface/web/config.html @@ -29,12 +29,12 @@ {% endif %} - -
- +
+ +
@@ -61,13 +61,13 @@ {% endif %} -
-
- +
+ +
@@ -94,13 +94,13 @@ {% endif %} -
-
- +
+ +
From c9c0ba67c68812c31c678ae32d8c0120f93fa828 Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 10 Nov 2023 17:29:23 -0800 Subject: [PATCH 08/36] Fix chat_client configurations for OpenAI chat director tests --- tests/conftest.py | 4 +++- tests/test_openai_chat_director.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 08ff7033..8cf0a391 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -235,7 +235,7 @@ def chat_client(search_config: SearchConfig, default_user2: KhojUser): OpenAIProcessorConversationConfigFactory() UserConversationProcessorConfigFactory(user=default_user2, setting=chat_model) - state.anonymous_mode = False + state.anonymous_mode = True app = FastAPI() @@ -253,7 +253,9 @@ def chat_client_no_background(search_config: SearchConfig, default_user2: KhojUs # Initialize Processor from Config if os.getenv("OPENAI_API_KEY"): + chat_model = ChatModelOptionsFactory(chat_model="gpt-3.5-turbo", model_type="openai") OpenAIProcessorConversationConfigFactory() + UserConversationProcessorConfigFactory(user=default_user2, setting=chat_model) state.anonymous_mode = True diff --git a/tests/test_openai_chat_director.py b/tests/test_openai_chat_director.py index 14a73f15..c7d2e0ec 100644 --- a/tests/test_openai_chat_director.py +++ b/tests/test_openai_chat_director.py @@ -307,6 +307,7 @@ def test_ask_for_clarification_if_not_enough_context_in_question(chat_client_no_ "which one is", "which of namita's sons", "the birth order", + "provide more context", ] assert response.status_code == 200 assert any([expected_response in response_message.lower() for expected_response in expected_responses]), ( From 45b8670c25bb6c17c7ef022b22eab6d949acf3eb Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 10 Nov 2023 17:34:19 -0800 Subject: [PATCH 09/36] Fix return type hint for generate_chat_response func --- src/khoj/routers/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/khoj/routers/helpers.py b/src/khoj/routers/helpers.py index 99c1b281..e2e62d96 100644 --- a/src/khoj/routers/helpers.py +++ b/src/khoj/routers/helpers.py @@ -2,7 +2,7 @@ import logging import asyncio from datetime import datetime from functools import partial -from typing import Iterator, List, Optional, Union +from typing import Iterator, List, Optional, Tuple, Union from concurrent.futures import ThreadPoolExecutor from fastapi import HTTPException, Request @@ -109,7 +109,7 @@ def generate_chat_response( inferred_queries: List[str] = [], conversation_command: ConversationCommand = ConversationCommand.Default, user: KhojUser = None, -) -> Union[ThreadedGenerator, Iterator[str]]: +) -> Tuple[Union[ThreadedGenerator, Iterator[str]], dict]: def _save_to_conversation_log( q: str, chat_response: str, From 14f8c151c814fa8e00cd4ff8982d7572290ef88c Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 10 Nov 2023 17:48:54 -0800 Subject: [PATCH 10/36] Fix return type of the generate_chat_response method --- src/khoj/routers/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/khoj/routers/helpers.py b/src/khoj/routers/helpers.py index 99c1b281..a5263d56 100644 --- a/src/khoj/routers/helpers.py +++ b/src/khoj/routers/helpers.py @@ -2,7 +2,7 @@ import logging import asyncio from datetime import datetime from functools import partial -from typing import Iterator, List, Optional, Union +from typing import Iterator, List, Optional, Union, Tuple, Dict from concurrent.futures import ThreadPoolExecutor from fastapi import HTTPException, Request @@ -109,7 +109,7 @@ def generate_chat_response( inferred_queries: List[str] = [], conversation_command: ConversationCommand = ConversationCommand.Default, user: KhojUser = None, -) -> Union[ThreadedGenerator, Iterator[str]]: +) -> Tuple[Union[ThreadedGenerator, Iterator[str]], Dict[str, str]]: def _save_to_conversation_log( q: str, chat_response: str, From b0b07bde6ccf354e7cc09a81debe5d24ffb82dc1 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 10 Nov 2023 17:49:20 -0800 Subject: [PATCH 11/36] Allow chat reference to expand enough to show the whole reference, rather than constraining the height --- src/interface/desktop/chat.html | 2 +- src/khoj/interface/web/chat.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interface/desktop/chat.html b/src/interface/desktop/chat.html index 302d4a54..551f9c08 100644 --- a/src/interface/desktop/chat.html +++ b/src/interface/desktop/chat.html @@ -582,7 +582,7 @@ overflow: hidden; } button.reference-button.expanded { - max-height: 200px; + max-height: none; } button.reference-button::before { diff --git a/src/khoj/interface/web/chat.html b/src/khoj/interface/web/chat.html index 919350aa..7955387e 100644 --- a/src/khoj/interface/web/chat.html +++ b/src/khoj/interface/web/chat.html @@ -452,7 +452,7 @@ To get started, just start typing below. You can also type / to see a list of co overflow: hidden; } button.reference-button.expanded { - max-height: 200px; + max-height: none; } button.reference-button::before { From 0a950d9382ef0794cbf85463ab48943879434284 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 10 Nov 2023 19:21:58 -0800 Subject: [PATCH 12/36] Fix checker to determine if obsidian client is connected --- src/interface/obsidian/src/main.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/interface/obsidian/src/main.ts b/src/interface/obsidian/src/main.ts index 0285fb6c..9f0560e8 100644 --- a/src/interface/obsidian/src/main.ts +++ b/src/interface/obsidian/src/main.ts @@ -1,4 +1,4 @@ -import { Notice, Plugin } from 'obsidian'; +import { Notice, Plugin, request } from 'obsidian'; import { KhojSetting, KhojSettingTab, DEFAULT_SETTINGS } from 'src/settings' import { KhojSearchModal } from 'src/search_modal' import { KhojChatModal } from 'src/chat_modal' @@ -69,6 +69,25 @@ export default class Khoj extends Plugin { async loadSettings() { // Load khoj obsidian plugin settings this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + + // Check if khoj backend is configured, note if cannot connect to backend + let headers = { "Authorization": `Bearer ${this.settings.khojApiKey}` }; + + if (this.settings.khojUrl === "https://app.khoj.dev") { + if (this.settings.khojApiKey === "") { + new Notice(`❗️Khoj API key is not configured. Please visit https://app.khoj.dev to get an API key.`); + return; + } + + await request({ url: this.settings.khojUrl ,method: "GET", headers: headers }) + .then(response => { + this.settings.connectedToBackend = true; + }) + .catch(error => { + this.settings.connectedToBackend = false; + new Notice(`❗️Ensure Khoj backend is running and Khoj URL is pointing to it in the plugin settings.\n\n${error}`); + }); + } } async saveSettings() { From 501e7606a00d1c55ce852567b1c28e20a322524a Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 10 Nov 2023 18:29:52 -0800 Subject: [PATCH 13/36] Increase reference text on 1st expansion to 3 lines and 140 characters --- src/interface/desktop/chat.html | 2 +- src/khoj/interface/web/chat.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interface/desktop/chat.html b/src/interface/desktop/chat.html index 551f9c08..a089939d 100644 --- a/src/interface/desktop/chat.html +++ b/src/interface/desktop/chat.html @@ -577,7 +577,7 @@ cursor: pointer; transition: background 0.2s ease-in-out; text-align: left; - max-height: 50px; + max-height: 75px; transition: max-height 0.3s ease-in-out; overflow: hidden; } diff --git a/src/khoj/interface/web/chat.html b/src/khoj/interface/web/chat.html index 7955387e..f15489ec 100644 --- a/src/khoj/interface/web/chat.html +++ b/src/khoj/interface/web/chat.html @@ -43,7 +43,7 @@ To get started, just start typing below. You can also type / to see a list of co let escaped_ref = reference.replaceAll('"', '"'); // Generate HTML for Chat Reference - let short_ref = escaped_ref.slice(0, 100); + let short_ref = escaped_ref.slice(0, 140); short_ref = short_ref.length < escaped_ref.length ? short_ref + "..." : short_ref; let referenceButton = document.createElement('button'); referenceButton.innerHTML = short_ref; @@ -447,7 +447,7 @@ To get started, just start typing below. You can also type / to see a list of co cursor: pointer; transition: background 0.2s ease-in-out; text-align: left; - max-height: 50px; + max-height: 75px; transition: max-height 0.3s ease-in-out; overflow: hidden; } From e2e96f9aa45117c6fec1456c9454ded345ed297c Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 10 Nov 2023 22:38:28 -0800 Subject: [PATCH 14/36] Add default settings to let new users be subscribed on trial - Add the default user to a subscription trial - Update associated unit tests --- src/database/adapters/__init__.py | 2 ++ src/database/admin.py | 2 ++ .../0015_alter_subscription_user.py | 21 +++++++++++++++++ .../0016_alter_subscription_renewal_date.py | 17 ++++++++++++++ src/database/models/__init__.py | 4 ++-- src/khoj/configure.py | 23 +++++++++++++++---- src/khoj/routers/helpers.py | 5 +++- tests/conftest.py | 13 ++++++++--- tests/helpers.py | 10 ++++++++ 9 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 src/database/migrations/0015_alter_subscription_user.py create mode 100644 src/database/migrations/0016_alter_subscription_renewal_date.py diff --git a/src/database/adapters/__init__.py b/src/database/adapters/__init__.py index 70d94df3..28999369 100644 --- a/src/database/adapters/__init__.py +++ b/src/database/adapters/__init__.py @@ -101,6 +101,8 @@ async def create_google_user(token: dict) -> KhojUser: user=user, ) + await Subscription.objects.acreate(user=user, type="trial") + return user diff --git a/src/database/admin.py b/src/database/admin.py index 5f41f54a..03c2ca42 100644 --- a/src/database/admin.py +++ b/src/database/admin.py @@ -8,6 +8,7 @@ from database.models import ( ChatModelOptions, OpenAIProcessorConversationConfig, OfflineChatProcessorConversationConfig, + Subscription, ) admin.site.register(KhojUser, UserAdmin) @@ -15,3 +16,4 @@ admin.site.register(KhojUser, UserAdmin) admin.site.register(ChatModelOptions) admin.site.register(OpenAIProcessorConversationConfig) admin.site.register(OfflineChatProcessorConversationConfig) +admin.site.register(Subscription) diff --git a/src/database/migrations/0015_alter_subscription_user.py b/src/database/migrations/0015_alter_subscription_user.py new file mode 100644 index 00000000..e4ba6ab0 --- /dev/null +++ b/src/database/migrations/0015_alter_subscription_user.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.5 on 2023-11-11 05:39 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("database", "0014_alter_googleuser_picture"), + ] + + operations = [ + migrations.AlterField( + model_name="subscription", + name="user", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, related_name="subscription", to=settings.AUTH_USER_MODEL + ), + ), + ] diff --git a/src/database/migrations/0016_alter_subscription_renewal_date.py b/src/database/migrations/0016_alter_subscription_renewal_date.py new file mode 100644 index 00000000..bc7c5ada --- /dev/null +++ b/src/database/migrations/0016_alter_subscription_renewal_date.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.5 on 2023-11-11 06:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("database", "0015_alter_subscription_user"), + ] + + operations = [ + migrations.AlterField( + model_name="subscription", + name="renewal_date", + field=models.DateTimeField(blank=True, default=None, null=True), + ), + ] diff --git a/src/database/models/__init__.py b/src/database/models/__init__.py index 73f19c36..437d86ed 100644 --- a/src/database/models/__init__.py +++ b/src/database/models/__init__.py @@ -51,10 +51,10 @@ class Subscription(BaseModel): TRIAL = "trial" STANDARD = "standard" - user = models.OneToOneField(KhojUser, on_delete=models.CASCADE) + user = models.OneToOneField(KhojUser, on_delete=models.CASCADE, related_name="subscription") type = models.CharField(max_length=20, choices=Type.choices, default=Type.TRIAL) is_recurring = models.BooleanField(default=False) - renewal_date = models.DateTimeField(null=True, default=None) + renewal_date = models.DateTimeField(null=True, default=None, blank=True) class NotionConfig(BaseModel): diff --git a/src/khoj/configure.py b/src/khoj/configure.py index 6f0589a8..cfb9fac4 100644 --- a/src/khoj/configure.py +++ b/src/khoj/configure.py @@ -28,7 +28,7 @@ from khoj.utils.config import ( from khoj.utils.fs_syncer import collect_files from khoj.utils.rawconfig import FullConfig from khoj.routers.indexer import configure_content, load_content, configure_search -from database.models import KhojUser +from database.models import KhojUser, Subscription from database.adapters import get_all_users @@ -54,27 +54,40 @@ class UserAuthenticationBackend(AuthenticationBackend): def _initialize_default_user(self): if not self.khojuser_manager.filter(username="default").exists(): - self.khojuser_manager.create_user( + default_user = self.khojuser_manager.create_user( username="default", email="default@example.com", password="default", ) + Subscription.objects.create( + user=default_user, + type="trial", + ) async def authenticate(self, request: HTTPConnection): current_user = request.session.get("user") if current_user and current_user.get("email"): - user = await self.khojuser_manager.filter(email=current_user.get("email")).afirst() + user = ( + await self.khojuser_manager.filter(email=current_user.get("email")) + .prefetch_related("subscription") + .afirst() + ) if user: return AuthCredentials(["authenticated"]), AuthenticatedKhojUser(user) if len(request.headers.get("Authorization", "").split("Bearer ")) == 2: # Get bearer token from header bearer_token = request.headers["Authorization"].split("Bearer ")[1] # Get user owning token - user_with_token = await self.khojapiuser_manager.filter(token=bearer_token).select_related("user").afirst() + user_with_token = ( + await self.khojapiuser_manager.filter(token=bearer_token) + .select_related("user") + .prefetch_related("user__subscription") + .afirst() + ) if user_with_token: return AuthCredentials(["authenticated"]), AuthenticatedKhojUser(user_with_token.user) if state.anonymous_mode: - user = await self.khojuser_manager.filter(username="default").afirst() + user = await self.khojuser_manager.filter(username="default").prefetch_related("subscription").afirst() if user: return AuthCredentials(["authenticated"]), AuthenticatedKhojUser(user) diff --git a/src/khoj/routers/helpers.py b/src/khoj/routers/helpers.py index a5263d56..46ef0641 100644 --- a/src/khoj/routers/helpers.py +++ b/src/khoj/routers/helpers.py @@ -13,7 +13,7 @@ from khoj.utils.helpers import ConversationCommand, log_telemetry from khoj.processor.conversation.openai.gpt import converse from khoj.processor.conversation.gpt4all.chat_model import converse_offline from khoj.processor.conversation.utils import message_to_log, ThreadedGenerator -from database.models import KhojUser +from database.models import KhojUser, Subscription from database.adapters import ConversationAdapters logger = logging.getLogger(__name__) @@ -61,12 +61,15 @@ def update_telemetry_state( metadata: Optional[dict] = None, ): user: KhojUser = request.user.object if request.user.is_authenticated else None + subscription: Subscription = user.subscription if user and user.subscription else None user_state = { "client_host": request.client.host if request.client else None, "user_agent": user_agent or "unknown", "referer": referer or "unknown", "host": host or "unknown", "server_id": str(user.uuid) if user else None, + "subscription_type": subscription.type if subscription else None, + "is_recurring": subscription.is_recurring if subscription else None, } if metadata: diff --git a/tests/conftest.py b/tests/conftest.py index 8cf0a391..95fa9a99 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -43,6 +43,7 @@ from tests.helpers import ( OpenAIProcessorConversationConfigFactory, OfflineChatProcessorConversationConfigFactory, UserConversationProcessorConfigFactory, + SubscriptionFactory, ) @@ -69,7 +70,9 @@ def search_config() -> SearchConfig: @pytest.mark.django_db @pytest.fixture def default_user(): - return UserFactory() + user = UserFactory() + SubscriptionFactory(user=user) + return user @pytest.mark.django_db @@ -78,11 +81,13 @@ def default_user2(): if KhojUser.objects.filter(username="default").exists(): return KhojUser.objects.get(username="default") - return KhojUser.objects.create( + user = KhojUser.objects.create( username="default", email="default@example.com", password="default", ) + SubscriptionFactory(user=user) + return user @pytest.mark.django_db @@ -94,11 +99,13 @@ def default_user3(): if KhojUser.objects.filter(username="default3").exists(): return KhojUser.objects.get(username="default3") - return KhojUser.objects.create( + user = KhojUser.objects.create( username="default3", email="default3@example.com", password="default3", ) + SubscriptionFactory(user=user) + return user @pytest.mark.django_db diff --git a/tests/helpers.py b/tests/helpers.py index 3aa7c435..0f0f9cf2 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -9,6 +9,7 @@ from database.models import ( OpenAIProcessorConversationConfig, UserConversationConfig, Conversation, + Subscription, ) @@ -68,3 +69,12 @@ class ConversationFactory(factory.django.DjangoModelFactory): model = Conversation user = factory.SubFactory(UserFactory) + + +class SubscriptionFactory(factory.django.DjangoModelFactory): + class Meta: + model = Subscription + + user = factory.SubFactory(UserFactory) + type = "trial" + is_recurring = False From b6441683c6aa308ed62dca4edaaeef714b3889db Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 10 Nov 2023 18:29:52 -0800 Subject: [PATCH 15/36] 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 16/36] 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 17/36] 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 18/36] 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 19/36] 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 20/36] 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 @@