From 0afda5dc2743ffb5f9dd48295f15bc0495ed008c Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Thu, 15 Aug 2024 18:23:13 +0530 Subject: [PATCH 1/6] feat: new sources section + sidebar component --- frontend/src/assets/sources.svg | 25 ++ frontend/src/components/Sidebar.tsx | 54 ++++ .../src/conversation/ConversationBubble.tsx | 235 ++++++++++++------ frontend/src/index.css | 8 + 4 files changed, 245 insertions(+), 77 deletions(-) create mode 100644 frontend/src/assets/sources.svg create mode 100644 frontend/src/components/Sidebar.tsx diff --git a/frontend/src/assets/sources.svg b/frontend/src/assets/sources.svg new file mode 100644 index 000000000..4bb7d30b1 --- /dev/null +++ b/frontend/src/assets/sources.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx new file mode 100644 index 000000000..f0f4878b9 --- /dev/null +++ b/frontend/src/components/Sidebar.tsx @@ -0,0 +1,54 @@ +import React from 'react'; + +import Exit from '../assets/exit.svg'; + +type SidebarProps = { + isOpen: boolean; + toggleState: (arg0: boolean) => void; + children: React.ReactNode; +}; + +export default function Sidebar({ + isOpen, + toggleState, + children, +}: SidebarProps) { + const sidebarRef = React.useRef(null); + + const handleClickOutside = (event: MouseEvent) => { + if ( + sidebarRef.current && + !sidebarRef.current.contains(event.target as Node) + ) { + toggleState(false); + } + }; + + React.useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + return ( +
+
+
+ +
+
+ {children} +
+
+
+ ); +} diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 4adfbf942..8f8c0ef0f 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -1,17 +1,20 @@ import { forwardRef, useState } from 'react'; -import Avatar from '../components/Avatar'; -import CopyButton from '../components/CopyButton'; -import remarkGfm from 'remark-gfm'; -import { FEEDBACK, MESSAGE_TYPE } from './conversationModels'; -import classes from './ConversationBubble.module.css'; -import Alert from './../assets/alert.svg'; -import Like from './../assets/like.svg?react'; -import Dislike from './../assets/dislike.svg?react'; - import ReactMarkdown from 'react-markdown'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism'; +import remarkGfm from 'remark-gfm'; + +import Alert from '../assets/alert.svg'; import DocsGPT3 from '../assets/cute_docsgpt3.svg'; +import Dislike from '../assets/dislike.svg?react'; +import Like from '../assets/like.svg?react'; +import Sources from '../assets/sources.svg'; +import Avatar from '../components/Avatar'; +import CopyButton from '../components/CopyButton'; +import Sidebar from '../components/Sidebar'; +import classes from './ConversationBubble.module.css'; +import { FEEDBACK, MESSAGE_TYPE } from './conversationModels'; + const DisableSourceFE = import.meta.env.VITE_DISABLE_SOURCE_FE || false; const ConversationBubble = forwardRef< @@ -35,9 +38,10 @@ const ConversationBubble = forwardRef< const [isDislikeHovered, setIsDislikeHovered] = useState(false); const [isLikeClicked, setIsLikeClicked] = useState(false); const [isDislikeClicked, setIsDislikeClicked] = useState(false); + const [activeTooltip, setActiveTooltip] = useState(null); + const [isSidebarOpen, setIsSidebarOpen] = useState(false); let bubble; - if (type === 'QUESTION') { bubble = (
@@ -55,18 +59,115 @@ const ConversationBubble = forwardRef< ref={ref} className={`flex flex-wrap self-start ${className} group flex-col dark:text-bright-gray`} > -
- +
+ + } /> - } - /> - +

Sources

+
+
+ {Array.from({ length: 4 }).map((_, index) => ( +
+ + + + + +
+ ))} +
+
+ ) : ( +
+
+ + } + /> +

Sources

+
+
+
+ {sources?.slice(0, 3)?.map((source, index) => ( +
+
setActiveTooltip(index)} + onMouseOut={() => setActiveTooltip(null)} + onClick={() => + source.source && source.source !== 'local' + ? window.open( + source.source, + '_blank', + 'noopener, noreferrer', + ) + : setOpenSource(openSource === index ? null : index) + } + > +

+ {source.title} +

+
+ {activeTooltip === index && ( +
setActiveTooltip(index)} + onMouseOut={() => setActiveTooltip(null)} + > +

+ {source.text} +

+
+ )} +
+ ))} + {(sources?.length ?? 0) > 3 && ( +
setIsSidebarOpen(true)} + > +

{`View ${ + sources?.length ? sources.length - 3 : 0 + } more`}

+
+ )} +
+
+
+ )} +
+
+ + } + /> +

Answer

+
{message} - {DisableSourceFE || - type === 'ERROR' || - !sources || - sources.length === 0 ? null : ( - <> - -
-
Sources:
-
- {sources?.map((source, index) => ( -
- source.source !== 'local' - ? window.open( - source.source, - '_blank', - 'noopener, noreferrer', - ) - : setOpenSource(openSource === index ? null : index) - } - > -

- {index + 1}. {source.title.substring(0, 45)} -

-
- ))} -
-
- - )}
-
+
)}
- - {sources && openSource !== null && sources[openSource] && ( -
-

- Source: {sources[openSource].title} -

- -
-

- {sources[openSource].text} -

-
-
+ {sources && ( + { + setIsSidebarOpen(state); + }} + children={} + /> )}
); @@ -312,4 +366,31 @@ const ConversationBubble = forwardRef< return bubble; }); +type AllSourcesProps = { + sources: { title: string; text: string; source: string }[]; +}; + +function AllSources(sources: AllSourcesProps) { + return ( +
+
+

{`${sources.sources.length} Sources`}

+
+
+
+ {sources.sources.map((source, index) => ( +
+

+ {`${index + 1}. ${source.title}`} +

+

+ {source.text} +

+
+ ))} +
+
+ ); +} + export default ConversationBubble; diff --git a/frontend/src/index.css b/frontend/src/index.css index cf90289f6..3b78e9de4 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -433,3 +433,11 @@ template { .bottom-safe { bottom: env(safe-area-inset-bottom, 0); } + +.ellipsis-text { + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + text-overflow: ellipsis; +} From 344a8a38872ed36d6d3f8601ce4f72e2ee7f4057 Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Sat, 17 Aug 2024 14:42:48 +0530 Subject: [PATCH 2/6] fix: added required changes --- frontend/src/assets/document.svg | 3 + .../src/conversation/ConversationBubble.tsx | 84 ++++++++++++++----- frontend/src/index.css | 2 +- 3 files changed, 66 insertions(+), 23 deletions(-) create mode 100644 frontend/src/assets/document.svg diff --git a/frontend/src/assets/document.svg b/frontend/src/assets/document.svg new file mode 100644 index 000000000..bf9504c80 --- /dev/null +++ b/frontend/src/assets/document.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 8f8c0ef0f..9c2aac9e5 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -7,7 +7,9 @@ import remarkGfm from 'remark-gfm'; import Alert from '../assets/alert.svg'; import DocsGPT3 from '../assets/cute_docsgpt3.svg'; import Dislike from '../assets/dislike.svg?react'; +import Document from '../assets/document.svg'; import Like from '../assets/like.svg?react'; +import Link from '../assets/link.svg'; import Sources from '../assets/sources.svg'; import Avatar from '../components/Avatar'; import CopyButton from '../components/CopyButton'; @@ -32,14 +34,12 @@ const ConversationBubble = forwardRef< { message, type, className, feedback, handleFeedback, sources, retryBtn }, ref, ) { - const [openSource, setOpenSource] = useState(null); - const [isLikeHovered, setIsLikeHovered] = useState(false); const [isDislikeHovered, setIsDislikeHovered] = useState(false); const [isLikeClicked, setIsLikeClicked] = useState(false); const [isDislikeClicked, setIsDislikeClicked] = useState(false); const [activeTooltip, setActiveTooltip] = useState(null); - const [isSidebarOpen, setIsSidebarOpen] = useState(false); + const [isSidebarOpen, setIsSidebarOpen] = useState(true); let bubble; if (type === 'QUESTION') { @@ -94,7 +94,7 @@ const ConversationBubble = forwardRef<
} /> -

Sources

+

Sources

{sources?.slice(0, 3)?.map((source, index) => (
setActiveTooltip(index)} onMouseOut={() => setActiveTooltip(null)} - onClick={() => - source.source && source.source !== 'local' - ? window.open( - source.source, - '_blank', - 'noopener, noreferrer', - ) - : setOpenSource(openSource === index ? null : index) - } > -

- {source.title} +

+ {source.text}

+
+ source.source && source.source !== 'local' + ? window.open( + source.source, + '_blank', + 'noopener, noreferrer', + ) + : null + } + > + Document +

+ {source.source && source.source !== 'local' + ? source.source + : source.title} +

+
{activeTooltip === index && (
} /> -

Answer

+

Answer

{sources.sources.map((source, index) => (
-

- {`${index + 1}. ${source.title}`} -

+ +

+ {`${index + 1}. ${source.title}`} +

+ {source.source && source.source !== 'local' ? ( + Link + window.open(source.source, '_blank', 'noopener, noreferrer') + } + > + ) : null} +

{source.text}

diff --git a/frontend/src/index.css b/frontend/src/index.css index 3b78e9de4..6ae4b762c 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -437,7 +437,7 @@ template { .ellipsis-text { overflow: hidden; display: -webkit-box; - -webkit-line-clamp: 4; + -webkit-line-clamp: 3; -webkit-box-orient: vertical; text-overflow: ellipsis; } From 4b7cb2a22af956b5e8527a2bb48d1e4619bcaf6b Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Sat, 17 Aug 2024 15:26:22 +0530 Subject: [PATCH 3/6] fix: minor fix --- frontend/src/conversation/ConversationBubble.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 9c2aac9e5..eb83c5f40 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -39,7 +39,7 @@ const ConversationBubble = forwardRef< const [isLikeClicked, setIsLikeClicked] = useState(false); const [isDislikeClicked, setIsDislikeClicked] = useState(false); const [activeTooltip, setActiveTooltip] = useState(null); - const [isSidebarOpen, setIsSidebarOpen] = useState(true); + const [isSidebarOpen, setIsSidebarOpen] = useState(false); let bubble; if (type === 'QUESTION') { @@ -79,13 +79,14 @@ const ConversationBubble = forwardRef< {Array.from({ length: 4 }).map((_, index) => (
+
))}
From 6da483b3ef645737ee8905baa6db9de10da1e81f Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Mon, 19 Aug 2024 17:33:15 +0530 Subject: [PATCH 4/6] fix: minor ui fix + added req changes --- application/api/answer/routes.py | 224 ++++++++++-------- .../src/conversation/ConversationBubble.tsx | 21 +- .../src/conversation/conversationHandlers.ts | 2 + .../src/conversation/conversationSlice.ts | 2 +- frontend/src/preferences/preferenceApi.ts | 10 +- frontend/src/preferences/preferenceSlice.ts | 3 +- 6 files changed, 144 insertions(+), 118 deletions(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index 7eed84340..893edd3a5 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -74,7 +74,7 @@ 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: raise Exception("Invalid API Key, please generate new key", 401) @@ -129,10 +129,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", @@ -172,7 +172,9 @@ def get_prompt(prompt_id): return prompt -def complete_stream(question, retriever, conversation_id, user_api_key): +def complete_stream( + question, retriever, conversation_id, user_api_key, isNoneDoc=False +): try: response_full = "" @@ -186,126 +188,136 @@ def complete_stream(question, retriever, conversation_id, user_api_key): elif "source" in line: source_log_docs.append(line["source"]) + if isNoneDoc: + for doc in source_log_docs: + doc["source"] = "None" + llm = LLMCreator.create_llm( settings.LLM_NAME, api_key=settings.API_KEY, user_api_key=user_api_key - ) - if(user_api_key is None): + ) + if user_api_key is None: 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: print("\033[91merr", str(e), file=sys.stderr) - data = json.dumps({"type": "error","error":"Please try again later. We apologize for any inconvenience.", - "error_exception": 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 + return + @answer.route("/stream", methods=["POST"]) def stream(): - try: - data = request.get_json() - # get parameter from url question - question = data["question"] - if "history" not in data: - history = [] - else: - history = data["history"] - history = json.loads(history) - if "conversation_id" not in data: - conversation_id = None - else: - conversation_id = data["conversation_id"] - if "prompt_id" in data: - prompt_id = data["prompt_id"] - else: - prompt_id = "default" - if "selectedDocs" in data and data["selectedDocs"] is None: - chunks = 0 - elif "chunks" in data: - chunks = int(data["chunks"]) - else: - chunks = 2 - if "token_limit" in data: - token_limit = data["token_limit"] - else: - token_limit = settings.DEFAULT_MAX_HISTORY + try: + data = request.get_json() + question = data["question"] + if "history" not in data: + history = [] + else: + history = data["history"] + history = json.loads(history) + if "conversation_id" not in data: + conversation_id = None + else: + conversation_id = data["conversation_id"] + if "prompt_id" in data: + prompt_id = data["prompt_id"] + else: + prompt_id = "default" + if "selectedDocs" in data and data["selectedDocs"] is None: + chunks = 0 + elif "chunks" in data: + chunks = int(data["chunks"]) + else: + chunks = 2 + if "token_limit" in data: + token_limit = data["token_limit"] + else: + token_limit = settings.DEFAULT_MAX_HISTORY - # check if active_docs or api_key is set + # check if active_docs or api_key is set - if "api_key" in data: - data_key = get_data_from_api_key(data["api_key"]) - chunks = int(data_key["chunks"]) - prompt_id = data_key["prompt_id"] - source = {"active_docs": data_key["source"]} - user_api_key = data["api_key"] - elif "active_docs" in data: - source = {"active_docs": data["active_docs"]} - user_api_key = None - else: - source = {} - user_api_key = None - - if ( - source["active_docs"].split("/")[0] == "default" - or source["active_docs"].split("/")[0] == "local" - ): - retriever_name = "classic" - else: - retriever_name = source["active_docs"] + if "api_key" in data: + data_key = get_data_from_api_key(data["api_key"]) + chunks = int(data_key["chunks"]) + prompt_id = data_key["prompt_id"] + source = {"active_docs": data_key["source"]} + user_api_key = data["api_key"] + elif "active_docs" in data: + source = {"active_docs": data["active_docs"]} + user_api_key = None + else: + source = {} + user_api_key = None - prompt = get_prompt(prompt_id) + if source["active_docs"].split("/")[0] in ["default", "local"]: + retriever_name = "classic" + else: + retriever_name = source["active_docs"] - retriever = RetrieverCreator.create_retriever( - retriever_name, - question=question, - source=source, - chat_history=history, - prompt=prompt, - chunks=chunks, - token_limit=token_limit, - gpt_model=gpt_model, - user_api_key=user_api_key, - ) + prompt = get_prompt(prompt_id) - return Response( - complete_stream( + retriever = RetrieverCreator.create_retriever( + retriever_name, question=question, - retriever=retriever, - conversation_id=conversation_id, + source=source, + chat_history=history, + prompt=prompt, + chunks=chunks, + token_limit=token_limit, + gpt_model=gpt_model, user_api_key=user_api_key, - ), - mimetype="text/event-stream", - ) - - except ValueError: - message = "Malformed request body" - print("\033[91merr", str(message), file=sys.stderr) - return Response( - error_stream_generate(message), - status=400, - mimetype="text/event-stream", - ) - except Exception as e: + ) + + return Response( + complete_stream( + question=question, + retriever=retriever, + conversation_id=conversation_id, + user_api_key=user_api_key, + isNoneDoc=data.get("isNoneDoc"), + ), + mimetype="text/event-stream", + ) + + except ValueError: + message = "Malformed request body" + print("\033[91merr", str(message), file=sys.stderr) + return Response( + error_stream_generate(message), + status=400, + mimetype="text/event-stream", + ) + except Exception as e: print("\033[91merr", str(e), file=sys.stderr) message = e.args[0] status_code = 400 # # Custom exceptions with two arguments, index 1 as status code - if(len(e.args) >= 2): + if len(e.args) >= 2: status_code = e.args[1] return Response( - error_stream_generate(message), - status=status_code, - mimetype="text/event-stream", - ) + 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" + data = json.dumps({"type": "error", "error": err_response}) + yield f"data: {data}\n\n" + @answer.route("/api/answer", methods=["POST"]) def api_answer(): @@ -346,10 +358,7 @@ def api_answer(): source = data user_api_key = None - if ( - source["active_docs"].split("/")[0] == "default" - or source["active_docs"].split("/")[0] == "local" - ): + if source["active_docs"].split("/")[0] in ["default", "local"]: retriever_name = "classic" else: retriever_name = source["active_docs"] @@ -375,6 +384,10 @@ def api_answer(): elif "answer" in line: response_full += line["answer"] + if data.get("isNoneDoc"): + for doc in source_log_docs: + doc["source"] = "None" + llm = LLMCreator.create_llm( settings.LLM_NAME, api_key=settings.API_KEY, user_api_key=user_api_key ) @@ -395,7 +408,6 @@ def api_answer(): @answer.route("/api/search", methods=["POST"]) def api_search(): data = request.get_json() - # get parameter from url question question = data["question"] if "chunks" in data: chunks = int(data["chunks"]) @@ -413,10 +425,7 @@ def api_search(): source = {} user_api_key = None - if ( - source["active_docs"].split("/")[0] == "default" - or source["active_docs"].split("/")[0] == "local" - ): + if source["active_docs"].split("/")[0] in ["default", "local"]: retriever_name = "classic" else: retriever_name = source["active_docs"] @@ -437,4 +446,9 @@ def api_search(): user_api_key=user_api_key, ) docs = retriever.search() + + if data.get("isNoneDoc"): + for doc in docs: + doc["source"] = "None" + return docs diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index eb83c5f40..4fc0f1720 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -1,5 +1,6 @@ import { forwardRef, useState } from 'react'; import ReactMarkdown from 'react-markdown'; +import { useSelector } from 'react-redux'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism'; import remarkGfm from 'remark-gfm'; @@ -14,6 +15,7 @@ import Sources from '../assets/sources.svg'; import Avatar from '../components/Avatar'; import CopyButton from '../components/CopyButton'; import Sidebar from '../components/Sidebar'; +import { selectChunks } from '../preferences/preferenceSlice'; import classes from './ConversationBubble.module.css'; import { FEEDBACK, MESSAGE_TYPE } from './conversationModels'; @@ -34,6 +36,7 @@ const ConversationBubble = forwardRef< { message, type, className, feedback, handleFeedback, sources, retryBtn }, ref, ) { + const chunks = useSelector(selectChunks); const [isLikeHovered, setIsLikeHovered] = useState(false); const [isDislikeHovered, setIsDislikeHovered] = useState(false); const [isLikeClicked, setIsLikeClicked] = useState(false); @@ -59,12 +62,17 @@ const ConversationBubble = forwardRef< ref={ref} className={`flex flex-wrap self-start ${className} group flex-col dark:text-bright-gray`} > - {DisableSourceFE || type === 'ERROR' ? null : !sources || - sources.length === 0 ? ( + {DisableSourceFE || + type === 'ERROR' || + chunks === '0' || + sources?.length === 0 || + sources?.some( + (source) => source.source === 'None', + ) ? null : !sources ? (
} /> -

Sources

+

Sources

{Array.from({ length: 4 }).map((_, index) => ( @@ -405,7 +413,10 @@ function AllSources(sources: AllSourcesProps) {
{sources.sources.map((source, index) => ( -
+

response.json()) .then((data) => { diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index 75c457a9a..23962a286 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -62,7 +62,7 @@ export const fetchAnswer = createAsyncThunk( dispatch( updateStreamingSource({ index: state.conversation.queries.length - 1, - query: { sources }, + query: { sources: sources ?? [] }, }), ); }); diff --git a/frontend/src/preferences/preferenceApi.ts b/frontend/src/preferences/preferenceApi.ts index 29a41645d..10494862c 100644 --- a/frontend/src/preferences/preferenceApi.ts +++ b/frontend/src/preferences/preferenceApi.ts @@ -76,17 +76,17 @@ export function setLocalPrompt(prompt: string): void { localStorage.setItem('DocsGPTPrompt', prompt); } -export function setLocalRecentDocs(doc: Doc): void { +export function setLocalRecentDocs(doc: Doc | null): void { localStorage.setItem('DocsGPTRecentDocs', JSON.stringify(doc)); - let namePath = doc.name; - if (doc.language === namePath) { + let namePath = doc?.name; + if (doc?.language === namePath) { namePath = '.project'; } let docPath = 'default'; - if (doc.location === 'local') { + if (doc?.location === 'local') { docPath = 'local' + '/' + doc.name + '/'; - } else if (doc.location === 'remote') { + } else if (doc?.location === 'remote') { docPath = doc.language + '/' + namePath + '/' + doc.version + '/' + doc.model + '/'; } diff --git a/frontend/src/preferences/preferenceSlice.ts b/frontend/src/preferences/preferenceSlice.ts index 370f260e2..aaf7fc2d8 100644 --- a/frontend/src/preferences/preferenceSlice.ts +++ b/frontend/src/preferences/preferenceSlice.ts @@ -95,8 +95,7 @@ prefListenerMiddleware.startListening({ matcher: isAnyOf(setSelectedDocs), effect: (action, listenerApi) => { setLocalRecentDocs( - (listenerApi.getState() as RootState).preference.selectedDocs ?? - ([] as unknown as Doc), + (listenerApi.getState() as RootState).preference.selectedDocs ?? null, ); }, }); From ca9e71087b9832dd4132d675d13a301ffc355ffd Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Mon, 19 Aug 2024 17:40:46 +0530 Subject: [PATCH 5/6] fix: revert changes --- frontend/src/conversation/ConversationBubble.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 4fc0f1720..6cb97f403 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -15,7 +15,6 @@ import Sources from '../assets/sources.svg'; import Avatar from '../components/Avatar'; import CopyButton from '../components/CopyButton'; import Sidebar from '../components/Sidebar'; -import { selectChunks } from '../preferences/preferenceSlice'; import classes from './ConversationBubble.module.css'; import { FEEDBACK, MESSAGE_TYPE } from './conversationModels'; @@ -36,7 +35,6 @@ const ConversationBubble = forwardRef< { message, type, className, feedback, handleFeedback, sources, retryBtn }, ref, ) { - const chunks = useSelector(selectChunks); const [isLikeHovered, setIsLikeHovered] = useState(false); const [isDislikeHovered, setIsDislikeHovered] = useState(false); const [isLikeClicked, setIsLikeClicked] = useState(false); @@ -64,7 +62,6 @@ const ConversationBubble = forwardRef< > {DisableSourceFE || type === 'ERROR' || - chunks === '0' || sources?.length === 0 || sources?.some( (source) => source.source === 'None', From 5f4f4a8ab9786ea0b64de14cb0de2215aa0ec340 Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Mon, 19 Aug 2024 18:14:49 +0530 Subject: [PATCH 6/6] fix: skeleton shown for 0 chunks and 'None' doc --- .../src/conversation/ConversationBubble.tsx | 170 +++++++++--------- 1 file changed, 89 insertions(+), 81 deletions(-) diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 6cb97f403..6ad877c33 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -15,6 +15,10 @@ import Sources from '../assets/sources.svg'; import Avatar from '../components/Avatar'; import CopyButton from '../components/CopyButton'; import Sidebar from '../components/Sidebar'; +import { + selectChunks, + selectSelectedDocs, +} from '../preferences/preferenceSlice'; import classes from './ConversationBubble.module.css'; import { FEEDBACK, MESSAGE_TYPE } from './conversationModels'; @@ -35,6 +39,8 @@ const ConversationBubble = forwardRef< { message, type, className, feedback, handleFeedback, sources, retryBtn }, ref, ) { + const chunks = useSelector(selectChunks); + const selectedDocs = useSelector(selectSelectedDocs); const [isLikeHovered, setIsLikeHovered] = useState(false); const [isDislikeHovered, setIsDislikeHovered] = useState(false); const [isLikeClicked, setIsLikeClicked] = useState(false); @@ -63,9 +69,9 @@ const ConversationBubble = forwardRef< {DisableSourceFE || type === 'ERROR' || sources?.length === 0 || - sources?.some( - (source) => source.source === 'None', - ) ? null : !sources ? ( + sources?.some((source) => source.source === 'None') ? null : !sources && + chunks !== '0' && + selectedDocs ? (

) : ( -
-
- - } - /> -

Sources

-
-
-
- {sources?.slice(0, 3)?.map((source, index) => ( -
-
setActiveTooltip(index)} - onMouseOut={() => setActiveTooltip(null)} - > -

- {source.text} -

-
- source.source && source.source !== 'local' - ? window.open( - source.source, - '_blank', - 'noopener, noreferrer', - ) - : null - } - > - Document -

- {source.source && source.source !== 'local' - ? source.source - : source.title} -

-
-
- {activeTooltip === index && ( + sources && ( +
+
+ + } + /> +

Sources

+
+
+
+ {sources?.slice(0, 3)?.map((source, index) => ( +
setActiveTooltip(index)} onMouseOut={() => setActiveTooltip(null)} > -

+

{source.text}

+
+ source.source && source.source !== 'local' + ? window.open( + source.source, + '_blank', + 'noopener, noreferrer', + ) + : null + } + > + Document +

+ {source.source && source.source !== 'local' + ? source.source + : source.title} +

+
- )} -
- ))} - {(sources?.length ?? 0) > 3 && ( -
setIsSidebarOpen(true)} - > -

{`View ${ - sources?.length ? sources.length - 3 : 0 - } more`}

-
- )} + {activeTooltip === index && ( +
setActiveTooltip(index)} + onMouseOut={() => setActiveTooltip(null)} + > +

+ {source.text} +

+
+ )} +
+ ))} + {(sources?.length ?? 0) > 3 && ( +
setIsSidebarOpen(true)} + > +

{`View ${ + sources?.length ? sources.length - 3 : 0 + } more`}

+
+ )} +
-
+ ) )}