Improve FE for no-retrieval personas

This commit is contained in:
Weves
2024-02-17 19:44:11 -08:00
committed by Chris Weaver
parent 927e85319c
commit 61e2e68cf9
7 changed files with 143 additions and 95 deletions

View File

@@ -5,7 +5,7 @@ personas:
# this is for DanswerBot to use when tagged in a non-configured channel # this is for DanswerBot to use when tagged in a non-configured channel
# Careful setting specific IDs, this won't autoincrement the next ID value for postgres # Careful setting specific IDs, this won't autoincrement the next ID value for postgres
- id: 0 - id: 0
name: "Knowledge Assistant" name: "Danswer"
description: > description: >
Assistant with access to documents from your Connected Sources. Assistant with access to documents from your Connected Sources.
# Default Prompt objects attached to the persona, see prompts.yaml # Default Prompt objects attached to the persona, see prompts.yaml
@@ -40,7 +40,7 @@ personas:
- id: 1 - id: 1
name: "AI Assistant (No Sources)" name: "GPT"
description: > description: >
Assistant with no access to documents. Chat with just the Language Model. Assistant with no access to documents. Chat with just the Language Model.
prompts: prompts:
@@ -53,7 +53,7 @@ personas:
- id: 2 - id: 2
name: "Paraphrase Assistant" name: "Paraphrase"
description: > description: >
Assistant that is heavily constrained and only provides exact quotes from Connected Sources. Assistant that is heavily constrained and only provides exact quotes from Connected Sources.
prompts: prompts:

View File

@@ -214,9 +214,27 @@ function smallerNumberFirstComparator(a: number, b: number) {
return a > b ? 1 : -1; return a > b ? 1 : -1;
} }
function closerToZeroNegativesFirstComparator(a: number, b: number) {
if (a < 0 && b > 0) {
return -1;
}
if (a > 0 && b < 0) {
return 1;
}
const absA = Math.abs(a);
const absB = Math.abs(b);
if (absA === absB) {
return a > b ? 1 : -1;
}
return absA > absB ? 1 : -1;
}
export function personaComparator(a: Persona, b: Persona) { export function personaComparator(a: Persona, b: Persona) {
if (a.display_priority === null && b.display_priority === null) { if (a.display_priority === null && b.display_priority === null) {
return smallerNumberFirstComparator(a.id, b.id); return closerToZeroNegativesFirstComparator(a.id, b.id);
} }
if (a.display_priority !== b.display_priority) { if (a.display_priority !== b.display_priority) {
@@ -230,5 +248,5 @@ export function personaComparator(a: Persona, b: Persona) {
return smallerNumberFirstComparator(a.display_priority, b.display_priority); return smallerNumberFirstComparator(a.display_priority, b.display_priority);
} }
return smallerNumberFirstComparator(a.id, b.id); return closerToZeroNegativesFirstComparator(a.id, b.id);
} }

View File

@@ -22,6 +22,7 @@ import {
handleAutoScroll, handleAutoScroll,
handleChatFeedback, handleChatFeedback,
nameChatSession, nameChatSession,
personaIncludesRetrieval,
processRawChatHistory, processRawChatHistory,
sendMessage, sendMessage,
} from "./lib"; } from "./lib";
@@ -172,7 +173,7 @@ export const Chat = ({
const livePersona = selectedPersona || availablePersonas[0]; const livePersona = selectedPersona || availablePersonas[0];
useEffect(() => { useEffect(() => {
if (messageHistory.length === 0) { if (messageHistory.length === 0 && chatSessionId === null) {
setSelectedPersona( setSelectedPersona(
availablePersonas.find( availablePersonas.find(
(persona) => persona.id === defaultSelectedPersonaId (persona) => persona.id === defaultSelectedPersonaId
@@ -480,6 +481,10 @@ export const Chat = ({
} }
}; };
const retrievalDisabled = selectedPersona
? !personaIncludesRetrieval(selectedPersona)
: false;
return ( return (
<div className="flex w-full overflow-x-hidden" ref={masterFlexboxRef}> <div className="flex w-full overflow-x-hidden" ref={masterFlexboxRef}>
{popup} {popup}
@@ -510,6 +515,7 @@ export const Chat = ({
onPersonaChange={(persona) => { onPersonaChange={(persona) => {
if (persona) { if (persona) {
setSelectedPersona(persona); setSelectedPersona(persona);
textareaRef.current?.focus();
router.push(`/chat?personaId=${persona.id}`); router.push(`/chat?personaId=${persona.id}`);
} }
}} }}
@@ -527,6 +533,7 @@ export const Chat = ({
selectedPersona={selectedPersona} selectedPersona={selectedPersona}
handlePersonaSelect={(persona) => { handlePersonaSelect={(persona) => {
setSelectedPersona(persona); setSelectedPersona(persona);
textareaRef.current?.focus();
router.push(`/chat?personaId=${persona.id}`); router.push(`/chat?personaId=${persona.id}`);
}} }}
/> />
@@ -630,6 +637,7 @@ export const Chat = ({
}); });
} }
}} }}
retrievalDisabled={retrievalDisabled}
/> />
</div> </div>
); );
@@ -682,22 +690,24 @@ export const Chat = ({
<div className="absolute bottom-0 z-10 w-full bg-background border-t border-border"> <div className="absolute bottom-0 z-10 w-full bg-background border-t border-border">
<div className="w-full pb-4 pt-2"> <div className="w-full pb-4 pt-2">
<div className="flex"> {!retrievalDisabled && (
<div className="w-searchbar-xs 2xl:w-searchbar-sm 3xl:w-searchbar mx-auto px-4 pt-1 flex"> <div className="flex">
{selectedDocuments.length > 0 ? ( <div className="w-searchbar-xs 2xl:w-searchbar-sm 3xl:w-searchbar mx-auto px-4 pt-1 flex">
<SelectedDocuments {selectedDocuments.length > 0 ? (
selectedDocuments={selectedDocuments} <SelectedDocuments
/> selectedDocuments={selectedDocuments}
) : ( />
<ChatFilters ) : (
{...filterManager} <ChatFilters
existingSources={finalAvailableSources} {...filterManager}
availableDocumentSets={finalAvailableDocumentSets} existingSources={finalAvailableSources}
availableTags={availableTags} availableDocumentSets={finalAvailableDocumentSets}
/> availableTags={availableTags}
)} />
)}
</div>
</div> </div>
</div> )}
<div className="flex justify-center py-2 max-w-screen-lg mx-auto mb-2"> <div className="flex justify-center py-2 max-w-screen-lg mx-auto mb-2">
<div className="w-full shrink relative px-4 w-searchbar-xs 2xl:w-searchbar-sm 3xl:w-searchbar mx-auto"> <div className="w-full shrink relative px-4 w-searchbar-xs 2xl:w-searchbar-sm 3xl:w-searchbar mx-auto">
@@ -785,21 +795,26 @@ export const Chat = ({
</div> </div>
</div> </div>
<ResizableSection {!retrievalDisabled ? (
intialWidth={documentSidebarInitialWidth} <ResizableSection
minWidth={400} intialWidth={documentSidebarInitialWidth}
maxWidth={maxDocumentSidebarWidth || undefined} minWidth={400}
> maxWidth={maxDocumentSidebarWidth || undefined}
<DocumentSidebar >
selectedMessage={aiMessage} <DocumentSidebar
selectedDocuments={selectedDocuments} selectedMessage={aiMessage}
toggleDocumentSelection={toggleDocumentSelection} selectedDocuments={selectedDocuments}
clearSelectedDocuments={clearSelectedDocuments} toggleDocumentSelection={toggleDocumentSelection}
selectedDocumentTokens={selectedDocumentTokens} clearSelectedDocuments={clearSelectedDocuments}
maxTokens={maxTokens} selectedDocumentTokens={selectedDocumentTokens}
isLoading={isFetchingChatMessages} maxTokens={maxTokens}
/> isLoading={isFetchingChatMessages}
</ResizableSection> />
</ResizableSection>
) : // Another option is to use a div with the width set to the initial width, so that the
// chat section appears in the same place as before
// <div style={documentSidebarInitialWidth ? {width: documentSidebarInitialWidth} : {}}></div>
null}
</> </>
) : ( ) : (
<div className="mx-auto h-full flex flex-col"> <div className="mx-auto h-full flex flex-col">

View File

@@ -124,63 +124,67 @@ export function ChatIntro({
</div> </div>
</div> </div>
<Divider /> {selectedPersona && selectedPersona.num_chunks !== 0 && (
<div> <>
{selectedPersona && selectedPersona.document_sets.length > 0 && ( <Divider />
<div className="mt-2"> <div>
<p className="font-bold mb-1 mt-4 text-emphasis"> {selectedPersona.document_sets.length > 0 && (
Knowledge Sets:{" "} <div className="mt-2">
</p> <p className="font-bold mb-1 mt-4 text-emphasis">
<div className="flex flex-wrap gap-x-2"> Knowledge Sets:{" "}
{selectedPersona.document_sets.map((documentSet) => ( </p>
<div key={documentSet.id} className="w-fit"> <div className="flex flex-wrap gap-x-2">
<HoverPopup {selectedPersona.document_sets.map((documentSet) => (
mainContent={ <div key={documentSet.id} className="w-fit">
<span className="flex w-fit p-1 rounded border border-border text-xs font-medium cursor-default"> <HoverPopup
<div className="mr-1 my-auto"> mainContent={
<FiBookmark /> <span className="flex w-fit p-1 rounded border border-border text-xs font-medium cursor-default">
</div> <div className="mr-1 my-auto">
{documentSet.name} <FiBookmark />
</span> </div>
} {documentSet.name}
popupContent={ </span>
<div className="flex py-1 w-96"> }
<FiInfo className="my-auto mr-2" /> popupContent={
<div className="text-sm"> <div className="flex py-1 w-96">
{documentSet.description} <FiInfo className="my-auto mr-2" />
</div> <div className="text-sm">
</div> {documentSet.description}
} </div>
direction="top" </div>
/> }
direction="top"
/>
</div>
))}
</div> </div>
))} </div>
</div> )}
{availableSources.length > 0 && (
<div className="mt-2">
<p className="font-bold mb-1 mt-4 text-emphasis">
Connected Sources:{" "}
</p>
<div className="flex flex-wrap gap-x-2">
{availableSourceMetadata.map((sourceMetadata) => (
<span
key={sourceMetadata.internalName}
className="flex w-fit p-1 rounded border border-border text-xs font-medium cursor-default"
>
<div className="mr-1 my-auto">
{sourceMetadata.icon({})}
</div>
<div className="my-auto">
{sourceMetadata.displayName}
</div>
</span>
))}
</div>
</div>
)}
</div> </div>
)} </>
{availableSources.length > 0 && ( )}
<div className="mt-2">
<p className="font-bold mb-1 mt-4 text-emphasis">
Connected Sources:{" "}
</p>
<div className="flex flex-wrap gap-x-2">
{availableSourceMetadata.map((sourceMetadata) => (
<span
key={sourceMetadata.internalName}
className="flex w-fit p-1 rounded border border-border text-xs font-medium cursor-default"
>
<div className="mr-1 my-auto">
{sourceMetadata.icon({})}
</div>
<div className="my-auto">
{sourceMetadata.displayName}
</div>
</span>
))}
</div>
</div>
)}
</div>
</div> </div>
) : ( ) : (
<div className="px-12 w-searchbar-xs 2xl:w-searchbar-sm 3xl:w-searchbar"> <div className="px-12 w-searchbar-xs 2xl:w-searchbar-sm 3xl:w-searchbar">

View File

@@ -14,6 +14,7 @@ import {
RetrievalType, RetrievalType,
StreamingError, StreamingError,
} from "./interfaces"; } from "./interfaces";
import { Persona } from "../admin/personas/interfaces";
export async function createChatSession(personaId: number): Promise<number> { export async function createChatSession(personaId: number): Promise<number> {
const createChatSessionResponse = await fetch( const createChatSessionResponse = await fetch(
@@ -349,3 +350,7 @@ export function processRawChatHistory(rawMessages: BackendMessage[]) {
return messages; return messages;
} }
export function personaIncludesRetrieval(selectedPersona: Persona) {
return selectedPersona.num_chunks !== 0;
}

View File

@@ -14,7 +14,6 @@ import { SearchSummary, ShowHideDocsButton } from "./SearchSummary";
import { SourceIcon } from "@/components/SourceIcon"; import { SourceIcon } from "@/components/SourceIcon";
import { ThreeDots } from "react-loader-spinner"; import { ThreeDots } from "react-loader-spinner";
import { SkippedSearch } from "./SkippedSearch"; import { SkippedSearch } from "./SkippedSearch";
import { SelectedDocuments } from "../modifiers/SelectedDocuments";
export const Hoverable: React.FC<{ export const Hoverable: React.FC<{
children: JSX.Element; children: JSX.Element;
@@ -42,6 +41,7 @@ export const AIMessage = ({
handleShowRetrieved, handleShowRetrieved,
handleSearchQueryEdit, handleSearchQueryEdit,
handleForceSearch, handleForceSearch,
retrievalDisabled,
}: { }: {
messageId: number | null; messageId: number | null;
content: string | JSX.Element; content: string | JSX.Element;
@@ -54,6 +54,7 @@ export const AIMessage = ({
handleShowRetrieved?: (messageNumber: number | null) => void; handleShowRetrieved?: (messageNumber: number | null) => void;
handleSearchQueryEdit?: (query: string) => void; handleSearchQueryEdit?: (query: string) => void;
handleForceSearch?: () => void; handleForceSearch?: () => void;
retrievalDisabled?: boolean;
}) => { }) => {
const [copyClicked, setCopyClicked] = useState(false); const [copyClicked, setCopyClicked] = useState(false);
return ( return (
@@ -72,7 +73,8 @@ export const AIMessage = ({
{query === undefined && {query === undefined &&
hasDocs && hasDocs &&
handleShowRetrieved !== undefined && handleShowRetrieved !== undefined &&
isCurrentlyShowingRetrieved !== undefined && ( isCurrentlyShowingRetrieved !== undefined &&
!retrievalDisabled && (
<div className="flex w-message-xs 2xl:w-message-sm 3xl:w-message-default absolute ml-8"> <div className="flex w-message-xs 2xl:w-message-sm 3xl:w-message-default absolute ml-8">
<div className="ml-auto"> <div className="ml-auto">
<ShowHideDocsButton <ShowHideDocsButton
@@ -88,7 +90,8 @@ export const AIMessage = ({
<div className="w-message-xs 2xl:w-message-sm 3xl:w-message-default break-words mt-1 ml-8"> <div className="w-message-xs 2xl:w-message-sm 3xl:w-message-default break-words mt-1 ml-8">
{query !== undefined && {query !== undefined &&
handleShowRetrieved !== undefined && handleShowRetrieved !== undefined &&
isCurrentlyShowingRetrieved !== undefined && ( isCurrentlyShowingRetrieved !== undefined &&
!retrievalDisabled && (
<div className="my-1"> <div className="my-1">
<SearchSummary <SearchSummary
query={query} query={query}
@@ -103,7 +106,8 @@ export const AIMessage = ({
{handleForceSearch && {handleForceSearch &&
content && content &&
query === undefined && query === undefined &&
!hasDocs && ( !hasDocs &&
!retrievalDisabled && (
<div className="my-1"> <div className="my-1">
<SkippedSearch handleForceSearch={handleForceSearch} /> <SkippedSearch handleForceSearch={handleForceSearch} />
</div> </div>

View File

@@ -95,6 +95,8 @@ export default async function Home() {
} }
// remove those marked as hidden by an admin // remove those marked as hidden by an admin
personas = personas.filter((persona) => persona.is_visible); personas = personas.filter((persona) => persona.is_visible);
// hide personas with no retrieval
personas = personas.filter((persona) => persona.num_chunks !== 0);
// sort them in priority order // sort them in priority order
personas.sort(personaComparator); personas.sort(personaComparator);