This commit is contained in:
pablodanswer 2024-09-04 18:45:52 -07:00 committed by GitHub
parent 61a17319c9
commit 420aabc963
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 176 additions and 89 deletions

View File

@ -1,3 +1,13 @@
import {
AnthropicIcon,
AWSIcon,
AzureIcon,
CPUIcon,
OpenAIIcon,
OpenSourceIcon,
} from "@/components/icons/icons";
import { FaRobot } from "react-icons/fa";
export interface CustomConfigKey { export interface CustomConfigKey {
name: string; name: string;
description: string | null; description: string | null;
@ -53,3 +63,18 @@ export interface LLMProviderDescriptor {
groups: number[]; groups: number[];
display_model_names: string[] | null; display_model_names: string[] | null;
} }
export const getProviderIcon = (providerName: string) => {
switch (providerName) {
case "openai":
return OpenAIIcon;
case "anthropic":
return AnthropicIcon;
case "bedrock":
return AWSIcon;
case "azure":
return AzureIcon;
default:
return CPUIcon;
}
};

View File

@ -482,7 +482,7 @@ export function CCPairIndexingStatusTable({
if (sourceMatches || matchingConnectors.length > 0) { if (sourceMatches || matchingConnectors.length > 0) {
return ( return (
<React.Fragment key={ind}> <React.Fragment key={ind}>
<div className="mt-4" /> <br className="mt-4" />
<SummaryRow <SummaryRow
source={source} source={source}

View File

@ -57,6 +57,7 @@ export function ChatBanner() {
className={`${settings.enterpriseSettings.two_lines_for_chat_header ? "line-clamp-2" : "line-clamp-1"} text-center w-full overflow-hidden pr-8`} className={`${settings.enterpriseSettings.two_lines_for_chat_header ? "line-clamp-2" : "line-clamp-1"} text-center w-full overflow-hidden pr-8`}
> >
<MinimalMarkdown <MinimalMarkdown
className="prose text-sm max-w-full"
content={settings.enterpriseSettings.custom_header_content} content={settings.enterpriseSettings.custom_header_content}
/> />
</div> </div>
@ -65,6 +66,7 @@ export function ChatBanner() {
className="absolute top-0 left-0 invisible w-full" className="absolute top-0 left-0 invisible w-full"
> >
<MinimalMarkdown <MinimalMarkdown
className="prose text-sm max-w-full"
content={settings.enterpriseSettings.custom_header_content} content={settings.enterpriseSettings.custom_header_content}
/> />
</div> </div>

View File

@ -338,10 +338,10 @@ export function ChatInputBar({
updateInputPrompt(currentPrompt); updateInputPrompt(currentPrompt);
}} }}
> >
<p className="font-bold">{currentPrompt.prompt}</p> <p className="font-bold">{currentPrompt.prompt}:</p>
<p className="line-clamp-1"> <p className="text-left flex-grow mr-auto line-clamp-1">
{currentPrompt.id == selectedAssistant.id && "(default) "} {currentPrompt.id == selectedAssistant.id && "(default) "}
{currentPrompt.content} {currentPrompt.content?.trim()}
</p> </p>
</button> </button>
))} ))}

View File

@ -46,6 +46,7 @@ export function ChatSessionDisplay({
showDeleteModal?: (chatSession: ChatSession) => void; showDeleteModal?: (chatSession: ChatSession) => void;
}) { }) {
const router = useRouter(); const router = useRouter();
const [isHovering, setIsHovering] = useState(false);
const [isRenamingChat, setIsRenamingChat] = useState(false); const [isRenamingChat, setIsRenamingChat] = useState(false);
const [isMoreOptionsDropdownOpen, setIsMoreOptionsDropdownOpen] = const [isMoreOptionsDropdownOpen, setIsMoreOptionsDropdownOpen] =
useState(false); useState(false);
@ -97,6 +98,11 @@ export function ChatSessionDisplay({
<Link <Link
className="flex my-1 group relative" className="flex my-1 group relative"
key={chatSession.id} key={chatSession.id}
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => {
setIsMoreOptionsDropdownOpen(false);
setIsHovering(false);
}}
onClick={() => { onClick={() => {
if (settings?.isMobile && closeSidebar) { if (settings?.isMobile && closeSidebar) {
closeSidebar(); closeSidebar();
@ -145,7 +151,7 @@ export function ChatSessionDisplay({
</p> </p>
)} )}
{isSelected && {isHovering &&
(isRenamingChat ? ( (isRenamingChat ? (
<div className="ml-auto my-auto items-center flex"> <div className="ml-auto my-auto items-center flex">
<div <div
@ -185,54 +191,68 @@ export function ChatSessionDisplay({
)} )}
<div> <div>
<div {search ? (
onClick={() => { showDeleteModal && (
setIsMoreOptionsDropdownOpen( <div
!isMoreOptionsDropdownOpen onClick={() => showDeleteModal(chatSession)}
); className={`p-1 -m-1 rounded ml-1`}
}} >
className={"-my-1"} <FiTrash size={16} />
> </div>
<Popover )
open={isMoreOptionsDropdownOpen} ) : (
onOpenChange={(open: boolean) => <div
setIsMoreOptionsDropdownOpen(open) onClick={() => {
} setIsMoreOptionsDropdownOpen(
content={ !isMoreOptionsDropdownOpen
<div className="hover:bg-black/10 p-1 rounded"> );
<FiMoreHorizontal size={16} /> }}
</div> className="-my-1"
} >
popover={ <Popover
<div className="border border-border rounded-lg bg-background z-50 w-32"> open={isMoreOptionsDropdownOpen}
{showShareModal && ( onOpenChange={(open: boolean) =>
<DefaultDropdownElement setIsMoreOptionsDropdownOpen(open)
name="Share" }
icon={FiShare2} content={
onSelect={() => showShareModal(chatSession)} <div className="p-1 rounded">
/> <FiMoreHorizontal size={16} />
)} </div>
<DefaultDropdownElement }
name="Rename" popover={
icon={FiEdit2} <div className="border border-border rounded-lg bg-background z-50 w-32">
onSelect={() => setIsRenamingChat(true)} {showShareModal && (
/> <DefaultDropdownElement
</div> name="Share"
} icon={FiShare2}
requiresContentPadding onSelect={() => showShareModal(chatSession)}
sideOffset={6} />
triggerMaxWidth )}
/> {!search && (
</div> <DefaultDropdownElement
name="Rename"
icon={FiEdit2}
onSelect={() => setIsRenamingChat(true)}
/>
)}
{showDeleteModal && (
<DefaultDropdownElement
name="Delete"
icon={FiTrash}
onSelect={() =>
showDeleteModal(chatSession)
}
/>
)}
</div>
}
requiresContentPadding
sideOffset={6}
triggerMaxWidth
/>
</div>
)}
</div> </div>
{showDeleteModal && (
<div
onClick={() => showDeleteModal(chatSession)}
className={`hover:bg-black/10 p-1 -m-1 rounded ml-1`}
>
<FiTrash size={16} />
</div>
)}
</div> </div>
))} ))}
</div> </div>

View File

@ -53,9 +53,14 @@ const ToggleSwitch = () => {
onClick={() => handleTabChange("search")} onClick={() => handleTabChange("search")}
> >
<SearchIcon size={16} className="mr-2" /> <SearchIcon size={16} className="mr-2" />
<div className="flex items-end"> <div className="flex items-center">
Search Search
<div className="ml-2 flex items-end">{commandSymbol}S</div> <div className="ml-2 flex content-center">
<span className="leading-none pb-[1px] my-auto">
{commandSymbol}
</span>
<span className="my-auto">S</span>
</div>
</div> </div>
</button> </button>
<button <button
@ -69,7 +74,12 @@ const ToggleSwitch = () => {
<ChatIcon size={16} className="mr-2" /> <ChatIcon size={16} className="mr-2" />
<div className="items-end flex"> <div className="items-end flex">
Chat Chat
<div className="ml-2 flex items-end">{commandSymbol}D</div> <div className="ml-2 flex content-center">
<span className="leading-none pb-[1px] my-auto">
{commandSymbol}
</span>
<span className="my-auto">D</span>
</div>
</div> </div>
</button> </button>
</div> </div>

View File

@ -36,7 +36,7 @@
.include-scrollbar { .include-scrollbar {
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: #888 #f1f1f1; scrollbar-color: #888 transparent;
} }
.inputscroll::-webkit-scrollbar-track { .inputscroll::-webkit-scrollbar-track {

View File

@ -1,3 +1,4 @@
import { CodeBlock } from "@/app/chat/message/CodeBlock";
import React from "react"; import React from "react";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm";
@ -5,11 +6,13 @@ import remarkGfm from "remark-gfm";
interface MinimalMarkdownProps { interface MinimalMarkdownProps {
content: string; content: string;
className?: string; className?: string;
useCodeBlock?: boolean;
} }
export const MinimalMarkdown: React.FC<MinimalMarkdownProps> = ({ export const MinimalMarkdown: React.FC<MinimalMarkdownProps> = ({
content, content,
className = "", className = "",
useCodeBlock = false,
}) => { }) => {
return ( return (
<ReactMarkdown <ReactMarkdown
@ -26,6 +29,11 @@ export const MinimalMarkdown: React.FC<MinimalMarkdownProps> = ({
p: ({ node, ...props }) => ( p: ({ node, ...props }) => (
<p {...props} className="text-wrap break-word text-sm m-0 w-full" /> <p {...props} className="text-wrap break-word text-sm m-0 w-full" />
), ),
code: useCodeBlock
? (props) => (
<CodeBlock className="w-full" {...props} content={content} />
)
: (props) => <code {...props} />,
}} }}
remarkPlugins={[remarkGfm]} remarkPlugins={[remarkGfm]}
> >

View File

@ -1,7 +1,10 @@
import React from "react"; import React from "react";
import { getDisplayNameForModel } from "@/lib/hooks"; import { getDisplayNameForModel } from "@/lib/hooks";
import { structureValue } from "@/lib/llm/utils"; import { structureValue } from "@/lib/llm/utils";
import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces"; import {
getProviderIcon,
LLMProviderDescriptor,
} from "@/app/admin/configuration/llm/interfaces";
interface LlmListProps { interface LlmListProps {
llmProviders: LLMProviderDescriptor[]; llmProviders: LLMProviderDescriptor[];
@ -9,6 +12,7 @@ interface LlmListProps {
onSelect: (value: string | null) => void; onSelect: (value: string | null) => void;
userDefault?: string | null; userDefault?: string | null;
scrollable?: boolean; scrollable?: boolean;
hideProviderIcon?: boolean;
} }
export const LlmList: React.FC<LlmListProps> = ({ export const LlmList: React.FC<LlmListProps> = ({
@ -19,7 +23,11 @@ export const LlmList: React.FC<LlmListProps> = ({
scrollable, scrollable,
}) => { }) => {
const llmOptionsByProvider: { const llmOptionsByProvider: {
[provider: string]: { name: string; value: string }[]; [provider: string]: {
name: string;
value: string;
icon: React.FC<{ size?: number; className?: string }>;
}[];
} = {}; } = {};
const uniqueModelNames = new Set<string>(); const uniqueModelNames = new Set<string>();
@ -39,6 +47,7 @@ export const LlmList: React.FC<LlmListProps> = ({
llmProvider.provider, llmProvider.provider,
modelName modelName
), ),
icon: getProviderIcon(llmProvider.provider),
}); });
} }
} }
@ -67,17 +76,18 @@ export const LlmList: React.FC<LlmListProps> = ({
User Default (currently {getDisplayNameForModel(userDefault)}) User Default (currently {getDisplayNameForModel(userDefault)})
</button> </button>
)} )}
{llmOptions.map(({ name, value }, index) => ( {llmOptions.map(({ name, icon, value }, index) => (
<button <button
type="button" type="button"
key={index} key={index}
className={`w-full py-1.5 px-2 text-sm ${ className={`w-full py-1.5 flex gap-x-2 px-2 text-sm ${
currentLlm == name currentLlm == name
? "bg-background-200" ? "bg-background-200"
: "bg-background hover:bg-background-100" : "bg-background hover:bg-background-100"
} text-left rounded`} } text-left rounded`}
onClick={() => onSelect(value)} onClick={() => onSelect(value)}
> >
{icon({ size: 16 })}
{getDisplayNameForModel(name)} {getDisplayNameForModel(name)}
</button> </button>
))} ))}

View File

@ -71,7 +71,7 @@ export const AnimatedToggle = ({
Get quality results immediately, best suited for instant access to Get quality results immediately, best suited for instant access to
your documents. your documents.
</p> </p>
<p className="mt-2 text-xs">Shortcut: ({commandSymbol}/)</p> <p className="mt-2 flex text-xs">Shortcut: ({commandSymbol}/)</p>
</div> </div>
} }
> >

View File

@ -34,8 +34,9 @@ import FixedLogo from "@/app/chat/shared_chat_search/FixedLogo";
import { usePopup } from "../admin/connectors/Popup"; import { usePopup } from "../admin/connectors/Popup";
import { FeedbackType } from "@/app/chat/types"; import { FeedbackType } from "@/app/chat/types";
import { FeedbackModal } from "@/app/chat/modal/FeedbackModal"; import { FeedbackModal } from "@/app/chat/modal/FeedbackModal";
import { handleChatFeedback } from "@/app/chat/lib"; import { deleteChatSession, handleChatFeedback } from "@/app/chat/lib";
import SearchAnswer from "./SearchAnswer"; import SearchAnswer from "./SearchAnswer";
import { DeleteEntityModal } from "../modals/DeleteEntityModal";
export type searchState = export type searchState =
| "input" | "input"
@ -511,7 +512,12 @@ export const SearchSection = ({
}; };
const [firstSearch, setFirstSearch] = useState(true); const [firstSearch, setFirstSearch] = useState(true);
const [searchState, setSearchState] = useState<searchState>("input"); const [searchState, setSearchState] = useState<searchState>("input");
const [deletingChatSession, setDeletingChatSession] =
useState<ChatSession | null>();
const showDeleteModal = (chatSession: ChatSession) => {
setDeletingChatSession(chatSession);
};
// Used to maintain a "time out" for history sidebar so our existing refs can have time to process change // Used to maintain a "time out" for history sidebar so our existing refs can have time to process change
const [untoggled, setUntoggled] = useState(false); const [untoggled, setUntoggled] = useState(false);
@ -591,6 +597,24 @@ export const SearchSection = ({
<> <>
<div className="flex relative pr-[8px] h-full text-default"> <div className="flex relative pr-[8px] h-full text-default">
{popup} {popup}
{deletingChatSession && (
<DeleteEntityModal
entityType="search"
entityName={deletingChatSession.name}
onClose={() => setDeletingChatSession(null)}
onSubmit={async () => {
const response = await deleteChatSession(deletingChatSession.id);
if (response.ok) {
setDeletingChatSession(null);
// go back to the main page
router.push("/search");
} else {
alert("Failed to delete chat session");
}
}}
/>
)}
{currentFeedback && ( {currentFeedback && (
<FeedbackModal <FeedbackModal
feedbackType={currentFeedback[0]} feedbackType={currentFeedback[0]}
@ -628,6 +652,7 @@ export const SearchSection = ({
> >
<div className="w-full relative"> <div className="w-full relative">
<HistorySidebar <HistorySidebar
showDeleteModal={showDeleteModal}
explicitlyUntoggle={explicitlyUntoggle} explicitlyUntoggle={explicitlyUntoggle}
reset={() => setQuery("")} reset={() => setQuery("")}
page="search" page="search"
@ -672,7 +697,7 @@ export const SearchSection = ({
(ccPairs.length > 0 || documentSets.length > 0) && ( (ccPairs.length > 0 || documentSets.length > 0) && (
<SourceSelector <SourceSelector
{...filterManager} {...filterManager}
showDocSidebar={showDocSidebar || toggledSidebar} showDocSidebar={toggledSidebar}
availableDocumentSets={finalAvailableDocumentSets} availableDocumentSets={finalAvailableDocumentSets}
existingSources={finalAvailableSources} existingSources={finalAvailableSources}
availableTags={tags} availableTags={tags}
@ -727,7 +752,7 @@ export const SearchSection = ({
toggleAgentic={ toggleAgentic={
disabledAgentic ? undefined : toggleAgentic disabledAgentic ? undefined : toggleAgentic
} }
showingSidebar={showDocSidebar || toggledSidebar} showingSidebar={toggledSidebar}
agentic={agentic} agentic={agentic}
query={query} query={query}
setQuery={setQuery} setQuery={setQuery}

View File

@ -115,7 +115,7 @@ export function TagFilter({
<FiTag className="mr-1 my-auto" /> <FiTag className="mr-1 my-auto" />
Tags Tags
</div> </div>
<div className="flex overflow-y-scroll max-h-96 flex-wrap gap-x-1 gap-y-1"> <div className="flex overflow-y-scroll overflow-x-hidden input-scrollbar max-h-96 flex-wrap gap-x-1 gap-y-1">
{filteredTags.length > 0 ? ( {filteredTags.length > 0 ? (
filteredTags.map((tag) => ( filteredTags.map((tag) => (
<div <div

View File

@ -2,6 +2,7 @@ import { Quote } from "@/lib/search/interfaces";
import { ResponseSection, StatusOptions } from "./ResponseSection"; import { ResponseSection, StatusOptions } from "./ResponseSection";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm";
import { MinimalMarkdown } from "@/components/chat_search/MinimalMarkdown";
const TEMP_STRING = "__$%^TEMP$%^__"; const TEMP_STRING = "__$%^TEMP$%^__";
@ -40,12 +41,10 @@ export const AnswerSection = (props: AnswerSectionProps) => {
header = <></>; header = <></>;
body = ( body = (
<ReactMarkdown <MinimalMarkdown
className="prose text-sm max-w-full" useCodeBlock
remarkPlugins={[remarkGfm]} content={replaceNewlines(props.answer || "")}
> />
{replaceNewlines(props.answer || "")}
</ReactMarkdown>
); );
// error while building answer (NOTE: if error occurs during quote generation // error while building answer (NOTE: if error occurs during quote generation
@ -63,12 +62,7 @@ export const AnswerSection = (props: AnswerSectionProps) => {
status = "success"; status = "success";
header = <></>; header = <></>;
body = ( body = (
<ReactMarkdown <MinimalMarkdown useCodeBlock content={replaceNewlines(props.answer)} />
className="prose text-sm max-w-full"
remarkPlugins={[remarkGfm]}
>
{replaceNewlines(props.answer)}
</ReactMarkdown>
); );
} }

View File

@ -2,7 +2,10 @@ import { Persona } from "@/app/admin/assistants/interfaces";
import { CCPairBasicInfo, DocumentSet, User } from "../types"; import { CCPairBasicInfo, DocumentSet, User } from "../types";
import { getCurrentUserSS } from "../userSS"; import { getCurrentUserSS } from "../userSS";
import { fetchSS } from "../utilsSS"; import { fetchSS } from "../utilsSS";
import { FullLLMProvider } from "@/app/admin/configuration/llm/interfaces"; import {
FullLLMProvider,
getProviderIcon,
} from "@/app/admin/configuration/llm/interfaces";
import { ToolSnapshot } from "../tools/interfaces"; import { ToolSnapshot } from "../tools/interfaces";
import { fetchToolsSS } from "../tools/fetchTools"; import { fetchToolsSS } from "../tools/fetchTools";
import { import {
@ -94,17 +97,7 @@ export async function fetchAssistantEditorInfoSS(
} }
for (const provider of llmProviders) { for (const provider of llmProviders) {
if (provider.provider == "openai") { provider.icon = getProviderIcon(provider.provider);
provider.icon = OpenAIIcon;
} else if (provider.provider == "anthropic") {
provider.icon = AnthropicIcon;
} else if (provider.provider == "bedrock") {
provider.icon = AWSIcon;
} else if (provider.provider == "azure") {
provider.icon = AzureIcon;
} else {
provider.icon = OpenSourceIcon;
}
} }
const existingPersona = personaResponse const existingPersona = personaResponse