mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-04-10 21:09:51 +02:00
functional ux standing till
This commit is contained in:
parent
9edbb0806d
commit
de18ec7ea4
@ -754,6 +754,7 @@ def stream_chat_message_objects(
|
||||
elif isinstance(packet, StreamStopInfo):
|
||||
print("PACKET IS ENINDG")
|
||||
print(packet)
|
||||
|
||||
if packet.stop_reason is not StreamStopReason.NEW_RESPONSE:
|
||||
break
|
||||
|
||||
@ -766,7 +767,6 @@ def stream_chat_message_objects(
|
||||
)
|
||||
|
||||
# Saving Gen AI answer and responding with message info
|
||||
|
||||
if tool_result is None:
|
||||
tool_call = None
|
||||
else:
|
||||
@ -786,7 +786,7 @@ def stream_chat_message_objects(
|
||||
reference_docs=reference_db_search_docs,
|
||||
files=ai_message_files,
|
||||
token_count=len(llm_tokenizer_encode_func(answer.llm_answer)),
|
||||
citations=db_citations,
|
||||
citations=db_citations.citation_map if db_citations else None,
|
||||
error=None,
|
||||
tool_call=tool_call,
|
||||
)
|
||||
@ -823,6 +823,7 @@ def stream_chat_message_objects(
|
||||
db_session=db_session,
|
||||
commit=False,
|
||||
)
|
||||
reference_db_search_docs = None
|
||||
else:
|
||||
if isinstance(packet, ToolCallFinalResult):
|
||||
tool_result = packet
|
||||
@ -845,6 +846,7 @@ def stream_chat_message_objects(
|
||||
|
||||
# Post-LLM answer processing
|
||||
try:
|
||||
print("I am in the final ")
|
||||
message_specific_citations: MessageSpecificCitations | None = None
|
||||
if reference_db_search_docs:
|
||||
message_specific_citations = _translate_citations(
|
||||
|
@ -488,7 +488,6 @@ def create_new_chat_message(
|
||||
if reserved_message_id is not None:
|
||||
# Edit existing message
|
||||
existing_message = db_session.query(ChatMessage).get(reserved_message_id)
|
||||
print(f"creating with reserved id {reserved_message_id}")
|
||||
if existing_message is None:
|
||||
raise ValueError(f"No message found with id {reserved_message_id}")
|
||||
|
||||
|
@ -1143,6 +1143,7 @@ export function ChatPage({
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log("PACKET IS: ", packet);
|
||||
if (!initialFetchDetails) {
|
||||
if (!Object.hasOwn(packet, "user_message_id")) {
|
||||
console.error(
|
||||
@ -1329,6 +1330,12 @@ export function ChatPage({
|
||||
tool_args: (packet as ToolCallMetadata).tool_args,
|
||||
tool_result: (packet as ToolCallMetadata).tool_result,
|
||||
};
|
||||
if (
|
||||
dynamicAssistantMessage.toolCall.tool_name === SEARCH_TOOL_NAME
|
||||
) {
|
||||
dynamicAssistantMessage.query =
|
||||
dynamicAssistantMessage.toolCall.tool_args.query;
|
||||
}
|
||||
|
||||
if (
|
||||
!dynamicAssistantMessage.toolCall ||
|
||||
|
@ -213,7 +213,10 @@ export const AIMessage = ({
|
||||
return content;
|
||||
}
|
||||
}
|
||||
if (toolCall?.tool_result) {
|
||||
if (
|
||||
toolCall?.tool_result &&
|
||||
toolCall.tool_result.tool_name == INTERNET_SEARCH_TOOL_NAME
|
||||
) {
|
||||
return content + ` [${toolCall.tool_name}]()`;
|
||||
}
|
||||
|
||||
@ -306,27 +309,35 @@ export const AIMessage = ({
|
||||
) : (
|
||||
<div className="w-6" />
|
||||
)}
|
||||
|
||||
<div className="w-full">
|
||||
<div className="max-w-message-max break-words">
|
||||
<div className="w-full ml-4">
|
||||
<div className="max-w-message-max break-words">
|
||||
{!toolCall || toolCall.tool_name === SEARCH_TOOL_NAME ? (
|
||||
{(!toolCall || toolCall.tool_name === SEARCH_TOOL_NAME) && (
|
||||
<>
|
||||
{query !== undefined &&
|
||||
handleShowRetrieved !== undefined &&
|
||||
isCurrentlyShowingRetrieved !== undefined &&
|
||||
!retrievalDisabled && (
|
||||
<div className="mb-1">
|
||||
<SearchSummary
|
||||
docs={docs}
|
||||
filteredDocs={filteredDocs}
|
||||
query={query}
|
||||
finished={toolCall?.tool_result != undefined}
|
||||
hasDocs={hasDocs || false}
|
||||
messageId={messageId}
|
||||
handleShowRetrieved={handleShowRetrieved}
|
||||
finished={
|
||||
toolCall?.tool_result != undefined ||
|
||||
isComplete!
|
||||
}
|
||||
toggleDocumentSelection={
|
||||
toggleDocumentSelection
|
||||
}
|
||||
handleSearchQueryEdit={handleSearchQueryEdit}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{handleForceSearch &&
|
||||
!hasChildAI &&
|
||||
content &&
|
||||
query === undefined &&
|
||||
!hasDocs &&
|
||||
@ -338,7 +349,7 @@ export const AIMessage = ({
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
)}
|
||||
|
||||
{toolCall &&
|
||||
!TOOLS_WITH_CUSTOM_HANDLING.includes(
|
||||
@ -439,10 +450,6 @@ export const AIMessage = ({
|
||||
} else if (
|
||||
value?.toString().startsWith("[")
|
||||
) {
|
||||
// for some reason <a> tags cause the onClick to not apply
|
||||
// and the links are unclickable
|
||||
// TODO: fix the fact that you have to double click to follow link
|
||||
// for the first link
|
||||
return (
|
||||
<Citation link={rest?.href}>
|
||||
{rest.children}
|
||||
@ -489,79 +496,6 @@ export const AIMessage = ({
|
||||
) : isComplete ? null : (
|
||||
<></>
|
||||
)}
|
||||
{isComplete && docs && docs.length > 0 && (
|
||||
<div className="mt-2 -mx-8 w-full mb-4 flex relative">
|
||||
<div className="w-full">
|
||||
<div className="px-8 flex gap-x-2">
|
||||
{!settings?.isMobile &&
|
||||
filteredDocs.length > 0 &&
|
||||
filteredDocs.slice(0, 2).map((doc, ind) => (
|
||||
<div
|
||||
key={doc.document_id}
|
||||
className={`w-[200px] rounded-lg flex-none transition-all duration-500 hover:bg-background-125 bg-text-100 px-4 pb-2 pt-1 border-b
|
||||
`}
|
||||
>
|
||||
<a
|
||||
href={doc.link || undefined}
|
||||
target="_blank"
|
||||
className="text-sm flex w-full pt-1 gap-x-1.5 overflow-hidden justify-between font-semibold text-text-700"
|
||||
>
|
||||
<Citation link={doc.link} index={ind + 1} />
|
||||
<p className="shrink truncate ellipsis break-all">
|
||||
{doc.semantic_identifier ||
|
||||
doc.document_id}
|
||||
</p>
|
||||
<div className="ml-auto flex-none">
|
||||
{doc.is_internet ? (
|
||||
<InternetSearchIcon url={doc.link} />
|
||||
) : (
|
||||
<SourceIcon
|
||||
sourceType={doc.source_type}
|
||||
iconSize={18}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
<div className="flex overscroll-x-scroll mt-.5">
|
||||
<DocumentMetadataBlock document={doc} />
|
||||
</div>
|
||||
<div className="line-clamp-3 text-xs break-words pt-1">
|
||||
{doc.blurb}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
onClick={() => {
|
||||
if (toggleDocumentSelection) {
|
||||
toggleDocumentSelection();
|
||||
}
|
||||
}}
|
||||
key={-1}
|
||||
className="cursor-pointer w-[200px] rounded-lg flex-none transition-all duration-500 hover:bg-background-125 bg-text-100 px-4 py-2 border-b"
|
||||
>
|
||||
<div className="text-sm flex justify-between font-semibold text-text-700">
|
||||
<p className="line-clamp-1">See context</p>
|
||||
<div className="flex gap-x-1">
|
||||
{uniqueSources.map((sourceType, ind) => {
|
||||
return (
|
||||
<div key={ind} className="flex-none">
|
||||
<SourceIcon
|
||||
sourceType={sourceType}
|
||||
iconSize={18}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="line-clamp-3 text-xs break-words pt-1">
|
||||
See more
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!hasChildAI &&
|
||||
|
@ -4,9 +4,21 @@ import {
|
||||
} from "@/components/BasicClickable";
|
||||
import { HoverPopup } from "@/components/HoverPopup";
|
||||
import { Hoverable } from "@/components/Hoverable";
|
||||
import { InternetSearchIcon } from "@/components/InternetSearchIcon";
|
||||
import { SourceIcon } from "@/components/SourceIcon";
|
||||
import { ChevronDownIcon, InfoIcon } from "@/components/icons/icons";
|
||||
import { DocumentMetadataBlock } from "@/components/search/DocumentDisplay";
|
||||
import { Citation } from "@/components/search/results/Citation";
|
||||
import { SettingsContext } from "@/components/settings/SettingsProvider";
|
||||
import { Tooltip } from "@/components/tooltip/Tooltip";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
DanswerDocument,
|
||||
FilteredDanswerDocument,
|
||||
} from "@/lib/search/interfaces";
|
||||
import { ValidSources } from "@/lib/types";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { FiCheck, FiEdit2, FiSearch, FiX } from "react-icons/fi";
|
||||
import { DownChevron } from "react-select/dist/declarations/src/components/indicators";
|
||||
|
||||
export function ShowHideDocsButton({
|
||||
messageId,
|
||||
@ -37,24 +49,35 @@ export function ShowHideDocsButton({
|
||||
|
||||
export function SearchSummary({
|
||||
query,
|
||||
hasDocs,
|
||||
filteredDocs,
|
||||
finished,
|
||||
messageId,
|
||||
handleShowRetrieved,
|
||||
docs,
|
||||
toggleDocumentSelection,
|
||||
handleSearchQueryEdit,
|
||||
}: {
|
||||
toggleDocumentSelection?: () => void;
|
||||
docs?: DanswerDocument[] | null;
|
||||
filteredDocs: FilteredDanswerDocument[];
|
||||
finished: boolean;
|
||||
query: string;
|
||||
hasDocs: boolean;
|
||||
messageId: number | null;
|
||||
handleShowRetrieved: (messageId: number | null) => void;
|
||||
handleSearchQueryEdit?: (query: string) => void;
|
||||
}) {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [finalQuery, setFinalQuery] = useState(query);
|
||||
const [isOverflowed, setIsOverflowed] = useState(false);
|
||||
const searchingForRef = useRef<HTMLDivElement>(null);
|
||||
const editQueryRef = useRef<HTMLInputElement>(null);
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const searchingForRef = useRef<HTMLDivElement | null>(null);
|
||||
const editQueryRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const settings = useContext(SettingsContext);
|
||||
|
||||
const uniqueSourceTypes = Array.from(
|
||||
new Set((docs || []).map((doc) => doc.source_type))
|
||||
).slice(0, 3);
|
||||
|
||||
const toggleDropdown = () => {
|
||||
setIsDropdownOpen(!isDropdownOpen);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const checkOverflow = () => {
|
||||
@ -68,7 +91,7 @@ export function SearchSummary({
|
||||
};
|
||||
|
||||
checkOverflow();
|
||||
window.addEventListener("resize", checkOverflow); // Recheck on window resize
|
||||
window.addEventListener("resize", checkOverflow);
|
||||
|
||||
return () => window.removeEventListener("resize", checkOverflow);
|
||||
}, []);
|
||||
@ -86,15 +109,30 @@ export function SearchSummary({
|
||||
}, [query]);
|
||||
|
||||
const searchingForDisplay = (
|
||||
<div className={`flex p-1 rounded ${isOverflowed && "cursor-default"}`}>
|
||||
<FiSearch className="flex-none mr-2 my-auto" size={14} />
|
||||
<div
|
||||
className={`${!finished && "loading-text"}
|
||||
!text-sm !line-clamp-1 !break-all px-0.5`}
|
||||
ref={searchingForRef}
|
||||
>
|
||||
{finished ? "Searched" : "Searching"} for: <i> {finalQuery}</i>
|
||||
</div>
|
||||
<div
|
||||
className={`flex my-auto items-center ${isOverflowed && "cursor-default"}`}
|
||||
>
|
||||
{finished ? (
|
||||
<>
|
||||
<div
|
||||
onClick={() => {
|
||||
toggleDropdown();
|
||||
}}
|
||||
className={` transition-colors duration-300 group-hover:text-text-toolhover cursor-pointer text-text-toolrun !line-clamp-1 !break-all pr-0.5`}
|
||||
ref={searchingForRef}
|
||||
>
|
||||
Searched {filteredDocs.length > 0 && filteredDocs.length} document
|
||||
{filteredDocs.length != 1 && "s"} for {query}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div
|
||||
className={`loading-text !text-sm !line-clamp-1 !break-all px-0.5`}
|
||||
ref={searchingForRef}
|
||||
>
|
||||
Searching for: <i> {finalQuery}</i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -145,43 +183,123 @@ export function SearchSummary({
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
const SearchBlock = ({ doc, ind }: { doc: DanswerDocument; ind: number }) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (toggleDocumentSelection) {
|
||||
toggleDocumentSelection();
|
||||
}
|
||||
}}
|
||||
key={doc.document_id}
|
||||
className={`flex items-start gap-3 px-4 py-3 text-token-text-secondary ${ind == 0 && "rounded-t-xl"} hover:bg-background-100 group relative text-sm`}
|
||||
>
|
||||
<div className="mt-1 scale-[.9] flex-none">
|
||||
{doc.is_internet ? (
|
||||
<InternetSearchIcon url={doc.link} />
|
||||
) : (
|
||||
<SourceIcon sourceType={doc.source_type} iconSize={18} />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<a
|
||||
href={doc.link}
|
||||
target="_blank"
|
||||
className="line-clamp-1 text-text-900"
|
||||
>
|
||||
{/* <Citation link={doc.link} index={ind + 1} /> */}
|
||||
<p className="shrink truncate ellipsis break-all ">
|
||||
{doc.semantic_identifier || doc.document_id}
|
||||
</p>
|
||||
<p className="line-clamp-3 text-text-500 break-words">
|
||||
{doc.blurb}
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
{isEditing ? (
|
||||
editInput
|
||||
) : (
|
||||
<>
|
||||
<div className="text-sm">
|
||||
{isOverflowed ? (
|
||||
<HoverPopup
|
||||
mainContent={searchingForDisplay}
|
||||
popupContent={
|
||||
<div>
|
||||
<b>Full query:</b>{" "}
|
||||
<div className="mt-1 italic">{query}</div>
|
||||
</div>
|
||||
}
|
||||
direction="top"
|
||||
/>
|
||||
) : (
|
||||
searchingForDisplay
|
||||
<>
|
||||
<div className="flex gap-x-2 group">
|
||||
{isEditing ? (
|
||||
editInput
|
||||
) : (
|
||||
<>
|
||||
<div className="my-auto text-sm">
|
||||
{isOverflowed ? (
|
||||
<HoverPopup
|
||||
mainContent={searchingForDisplay}
|
||||
popupContent={
|
||||
<div>
|
||||
<b>Full query:</b>{" "}
|
||||
<div className="mt-1 italic">{query}</div>
|
||||
</div>
|
||||
}
|
||||
direction="top"
|
||||
/>
|
||||
) : (
|
||||
searchingForDisplay
|
||||
)}
|
||||
</div>
|
||||
{handleSearchQueryEdit && (
|
||||
<Tooltip delayDuration={1000} content={"Edit Search"}>
|
||||
<button
|
||||
className="my-auto cursor-pointer rounded"
|
||||
onClick={() => {
|
||||
setIsEditing(true);
|
||||
}}
|
||||
>
|
||||
<FiEdit2 />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
{handleSearchQueryEdit && (
|
||||
<Tooltip delayDuration={1000} content={"Edit Search"}>
|
||||
<button
|
||||
className="my-auto hover:bg-hover p-1.5 rounded"
|
||||
<button
|
||||
className="my-auto invisible group-hover:visible transition-all duration-300 hover:bg-hover rounded"
|
||||
onClick={toggleDropdown}
|
||||
>
|
||||
<ChevronDownIcon
|
||||
className={`transform transition-transform ${isDropdownOpen ? "rotate-180" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isDropdownOpen && docs && docs.length > 0 && (
|
||||
<div
|
||||
className={`mt-2 -mx-8 w-full mb-4 flex relative transition-all duration-300 ${isDropdownOpen ? "opacity-100 max-h-[1000px]" : "opacity-0 max-h-0"}`}
|
||||
>
|
||||
<div className="w-full">
|
||||
<div className="mx-8 flex rounded overflow-hidden rounded-lg border-1.5 border divide-y divider-y-1.5 divider-y-border border-border flex-col gap-x-4">
|
||||
{!settings?.isMobile &&
|
||||
filteredDocs.length > 0 &&
|
||||
filteredDocs.map((doc, ind) => (
|
||||
<SearchBlock key={ind} doc={doc} ind={ind} />
|
||||
))}
|
||||
|
||||
<div
|
||||
onClick={() => {
|
||||
setIsEditing(true);
|
||||
if (toggleDocumentSelection) {
|
||||
toggleDocumentSelection();
|
||||
}
|
||||
}}
|
||||
key={-1}
|
||||
className="cursor-pointer w-full flex transition-all duration-500 hover:bg-background-100 py-3 border-b"
|
||||
>
|
||||
<FiEdit2 />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
<div key={0} className="px-3 invisible scale-[.9] flex-none">
|
||||
<SourceIcon sourceType={"file"} iconSize={18} />
|
||||
</div>
|
||||
<div className="text-sm flex justify-between text-text-900">
|
||||
<p className="line-clamp-1">See context</p>
|
||||
<div className="flex gap-x-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user