Update citations in shared chat display (#3487)

* update shared chat display

* Change Copy

* fix icon

* remove secret!

---------

Co-authored-by: Yuhong Sun <yuhongsun96@gmail.com>
This commit is contained in:
pablonyx 2024-12-19 17:48:29 -08:00 committed by GitHub
parent 59c774353a
commit 9011b8a139
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 345 additions and 127 deletions

View File

@ -182,12 +182,15 @@ def get_chat_session(
description=chat_session.description,
persona_id=chat_session.persona_id,
persona_name=chat_session.persona.name if chat_session.persona else None,
persona_icon_color=chat_session.persona.icon_color
if chat_session.persona
else None,
persona_icon_shape=chat_session.persona.icon_shape
if chat_session.persona
else None,
current_alternate_model=chat_session.current_alternate_model,
messages=[
translate_db_message_to_chat_message_detail(
msg, remove_doc_content=is_shared # if shared, don't leak doc content
)
for msg in session_messages
translate_db_message_to_chat_message_detail(msg) for msg in session_messages
],
time_created=chat_session.time_created,
shared_status=chat_session.shared_status,

View File

@ -225,6 +225,8 @@ class ChatSessionDetailResponse(BaseModel):
description: str | None
persona_id: int | None = None
persona_name: str | None
persona_icon_color: str | None
persona_icon_shape: int | None
messages: list[ChatMessageDetail]
time_created: datetime
shared_status: ChatSessionSharedStatus

View File

@ -2105,6 +2105,7 @@ export function ChatPage({
}
/>
)}
{sharingModalVisible && chatSessionIdRef.current !== null && (
<ShareChatSessionModal
message={message}

View File

@ -16,6 +16,7 @@ interface DocumentDisplayProps {
isSelected: boolean;
handleSelect: (documentId: string) => void;
tokenLimitReached: boolean;
hideSelection?: boolean;
setPresentingDocument: Dispatch<SetStateAction<OnyxDocument | null>>;
}
@ -62,6 +63,7 @@ export function ChatDocumentDisplay({
closeSidebar,
document,
modal,
hideSelection,
isSelected,
handleSelect,
tokenLimitReached,
@ -76,7 +78,9 @@ export function ChatDocumentDisplay({
const hasMetadata =
document.updated_at || Object.keys(document.metadata).length > 0;
return (
<div className={`opacity-100 ${modal ? "w-[90vw]" : "w-full"}`}>
<div
className={`max-w-[400px] opacity-100 ${modal ? "w-[90vw]" : "w-full"}`}
>
<div
className={`flex relative flex-col gap-0.5 rounded-xl mx-2 my-1 ${
isSelected ? "bg-gray-200" : "hover:bg-background-125"
@ -115,7 +119,7 @@ export function ChatDocumentDisplay({
)}
</div>
<div className="absolute top-2 right-2">
{!isInternet && (
{!isInternet && !hideSelection && (
<DocumentSelector
isSelected={isSelected}
handleSelect={() => handleSelect(document.document_id)}

View File

@ -17,7 +17,7 @@ import { SourceSelector } from "../shared_chat_search/SearchFilters";
import { XIcon } from "@/components/icons/icons";
interface ChatFiltersProps {
filterManager: FilterManager;
filterManager?: FilterManager;
closeSidebar: () => void;
selectedMessage: Message | null;
selectedDocuments: OnyxDocument[] | null;
@ -27,6 +27,7 @@ interface ChatFiltersProps {
maxTokens: number;
initialWidth: number;
isOpen: boolean;
isSharedChat?: boolean;
modal: boolean;
ccPairs: CCPairBasicInfo[];
tags: Tag[];
@ -48,6 +49,7 @@ export const ChatFilters = forwardRef<HTMLDivElement, ChatFiltersProps>(
selectedDocumentTokens,
maxTokens,
initialWidth,
isSharedChat,
isOpen,
ccPairs,
tags,
@ -79,13 +81,14 @@ export const ChatFilters = forwardRef<HTMLDivElement, ChatFiltersProps>(
const dedupedDocuments = removeDuplicateDocs(currentDocuments || []);
const tokenLimitReached = selectedDocumentTokens > maxTokens - 75;
console.log("SELECTED MESSAGE is", selectedMessage);
const hasSelectedDocuments = selectedDocumentIds.length > 0;
return (
<div
id="onyx-chat-sidebar"
className={`relative max-w-full ${
className={`relative bg-background max-w-full ${
!modal ? "border-l h-full border-sidebar-border" : ""
}`}
onClick={(e) => {
@ -122,10 +125,10 @@ export const ChatFilters = forwardRef<HTMLDivElement, ChatFiltersProps>(
<div className="overflow-y-auto -mx-1 sm:mx-0 flex-grow gap-y-0 default-scrollbar dark-scrollbar flex flex-col">
{showFilters ? (
<SourceSelector
{...filterManager!}
modal={modal}
tagsOnLeft={true}
filtersUntoggled={false}
{...filterManager}
availableDocumentSets={documentSets}
existingSources={ccPairs.map((ccPair) => ccPair.source)}
availableTags={tags}
@ -157,6 +160,7 @@ export const ChatFilters = forwardRef<HTMLDivElement, ChatFiltersProps>(
)!
);
}}
hideSelection={isSharedChat}
tokenLimitReached={tokenLimitReached}
/>
</div>

View File

@ -102,6 +102,8 @@ export interface BackendChatSession {
description: string;
persona_id: number;
persona_name: string;
persona_icon_color: string | null;
persona_icon_shape: number | null;
messages: BackendMessage[];
time_created: string;
shared_status: ChatSessionSharedStatus;

View File

@ -1,6 +1,6 @@
import { Citation } from "@/components/search/results/Citation";
import { WebResultIcon } from "@/components/WebResultIcon";
import { LoadedOnyxDocument } from "@/lib/search/interfaces";
import { LoadedOnyxDocument, OnyxDocument } from "@/lib/search/interfaces";
import { getSourceMetadata, SOURCE_METADATA_MAP } from "@/lib/sources";
import { ValidSources } from "@/lib/types";
import React, { memo } from "react";
@ -9,7 +9,15 @@ import { SlackIcon } from "@/components/icons/icons";
import { SourceIcon } from "@/components/SourceIcon";
export const MemoizedAnchor = memo(
({ docs, updatePresentingDocument, children }: any) => {
({
docs,
updatePresentingDocument,
children,
}: {
docs?: OnyxDocument[] | null;
updatePresentingDocument: (doc: OnyxDocument) => void;
children: React.ReactNode;
}) => {
const value = children?.toString();
if (value?.startsWith("[") && value?.endsWith("]")) {
const match = value.match(/\[(\d+)\]/);
@ -21,9 +29,11 @@ export const MemoizedAnchor = memo(
? new URL(associatedDoc.link).origin + "/favicon.ico"
: "";
const icon = (
<SourceIcon sourceType={associatedDoc?.source_type} iconSize={18} />
);
const icon =
(associatedDoc && (
<SourceIcon sourceType={associatedDoc?.source_type} iconSize={18} />
)) ||
null;
return (
<MemoizedLink

View File

@ -322,7 +322,7 @@ export const AIMessage = ({
const anchorCallback = useCallback(
(props: any) => (
<MemoizedAnchor
updatePresentingDocument={setPresentingDocument}
updatePresentingDocument={setPresentingDocument!}
docs={docs}
>
{props.children}
@ -378,6 +378,7 @@ export const AIMessage = ({
onMessageSelection &&
otherMessagesCanSwitchTo &&
otherMessagesCanSwitchTo.length > 1;
return (
<div
id="onyx-ai-message"
@ -402,21 +403,16 @@ export const AIMessage = ({
<div className="max-w-message-max break-words">
{!toolCall || toolCall.tool_name === SEARCH_TOOL_NAME ? (
<>
{query !== undefined &&
handleShowRetrieved !== undefined &&
!retrievalDisabled && (
<div className="mb-1">
<SearchSummary
index={index || 0}
query={query}
finished={toolCall?.tool_result != undefined}
hasDocs={hasDocs || false}
messageId={messageId}
handleShowRetrieved={handleShowRetrieved}
handleSearchQueryEdit={handleSearchQueryEdit}
/>
</div>
)}
{query !== undefined && !retrievalDisabled && (
<div className="mb-1">
<SearchSummary
index={index || 0}
query={query}
finished={toolCall?.tool_result != undefined}
handleSearchQueryEdit={handleSearchQueryEdit}
/>
</div>
)}
{handleForceSearch &&
content &&
query === undefined &&

View File

@ -43,18 +43,12 @@ export function ShowHideDocsButton({
export function SearchSummary({
index,
query,
hasDocs,
finished,
messageId,
handleShowRetrieved,
handleSearchQueryEdit,
}: {
index: number;
finished: boolean;
query: string;
hasDocs: boolean;
messageId: number | null;
handleShowRetrieved: (messageId: number | null) => void;
handleSearchQueryEdit?: (query: string) => void;
}) {
const [isEditing, setIsEditing] = useState(false);

View File

@ -34,7 +34,7 @@ async function generateShareLink(chatSessionId: string) {
return null;
}
async function generateCloneLink(
async function generateSeedLink(
message?: string,
assistantId?: number,
modelOverride?: LlmOverride
@ -115,7 +115,7 @@ export function ShareChatSessionModal({
{shareLink ? (
<div>
<Text>
This chat session is currently shared. Anyone at your
This chat session is currently shared. Anyone in your
organization can view the message history using the following
link:
</Text>
@ -157,10 +157,8 @@ export function ShareChatSessionModal({
) : (
<div>
<Callout type="warning" title="Warning" className="mb-4">
Ensure that all content in the chat is safe to share with the
whole organization. The content of the retrieved documents
will not be visible, but the names of cited documents as well
as the AI and human messages will be visible.
Please make sure that all content in this chat is safe to
share with the whole organization.
</Callout>
<div className="flex w-full justify-between">
<Button
@ -194,10 +192,9 @@ export function ShareChatSessionModal({
<Separator className="my-4" />
<div className="mb-4">
<Callout type="notice" title="Clone Chat">
Generate a link to clone this chat session with the current query.
This allows others to start a new chat with the same initial
message and settings.
<Callout type="notice" title="Seed New Chat">
Generate a link to a new chat session with the same settings as
this chat (including the assistant and model).
</Callout>
</div>
<div className="flex w-full justify-between">
@ -207,18 +204,18 @@ export function ShareChatSessionModal({
// NOTE: for "insecure" non-https setup, the `navigator.clipboard.writeText` may fail
// as the browser may not allow the clipboard to be accessed.
try {
const cloneLink = await generateCloneLink(
const seedLink = await generateSeedLink(
message,
assistantId,
modelOverride
);
if (!cloneLink) {
if (!seedLink) {
setPopup({
message: "Failed to generate clone link",
message: "Failed to generate seed link",
type: "error",
});
} else {
navigator.clipboard.writeText(cloneLink);
navigator.clipboard.writeText(seedLink);
setPopup({
message: "Link copied to clipboard!",
type: "success",
@ -232,7 +229,7 @@ export function ShareChatSessionModal({
size="sm"
variant="secondary"
>
Generate and Copy Clone Link
Generate and Copy Seed Link
</Button>
</div>
</>

View File

@ -6,6 +6,7 @@ import { BackendChatSession } from "../../interfaces";
import {
buildLatestMessageChain,
getCitedDocumentsFromMessage,
getHumanAndAIMessageFromMessageNumber,
processRawChatHistory,
} from "../../lib";
import { AIMessage, HumanMessage } from "../../message/Messages";
@ -19,8 +20,17 @@ import { Persona } from "@/app/admin/assistants/interfaces";
import { Button } from "@/components/ui/button";
import { OnyxDocument } from "@/lib/search/interfaces";
import TextView from "@/components/chat_search/TextView";
import { ChatFilters } from "../../documentSidebar/ChatFilters";
import { Modal } from "@/components/Modal";
import FunctionalHeader from "@/components/chat_search/Header";
import FixedLogo from "../../shared_chat_search/FixedLogo";
import { useDocumentSelection } from "../../useDocumentSelection";
function BackToOnyxButton() {
function BackToOnyxButton({
documentSidebarToggled,
}: {
documentSidebarToggled: boolean;
}) {
const router = useRouter();
const enterpriseSettings = useContext(SettingsContext)?.enterpriseSettings;
@ -31,6 +41,17 @@ function BackToOnyxButton() {
Back to {enterpriseSettings?.application_name || "Onyx Chat"}
</Button>
</div>
<div
style={{ transition: "width 0.30s ease-out" }}
className={`
flex-none
overflow-y-hidden
transition-all
duration-300
ease-in-out
${documentSidebarToggled ? "w-[400px]" : "w-[0px]"}
`}
></div>
</div>
);
}
@ -42,10 +63,18 @@ export function SharedChatDisplay({
chatSession: BackendChatSession | null;
persona: Persona;
}) {
const settings = useContext(SettingsContext);
const [documentSidebarToggled, setDocumentSidebarToggled] = useState(false);
const [selectedMessageForDocDisplay, setSelectedMessageForDocDisplay] =
useState<number | null>(null);
const [isReady, setIsReady] = useState(false);
const [presentingDocument, setPresentingDocument] =
useState<OnyxDocument | null>(null);
const toggleDocumentSidebar = () => {
setDocumentSidebarToggled(!documentSidebarToggled);
};
useEffect(() => {
Prism.highlightAll();
setIsReady(true);
@ -58,7 +87,7 @@ export function SharedChatDisplay({
Did not find a shared chat with the specified ID.
</Callout>
</div>
<BackToOnyxButton />
<BackToOnyxButton documentSidebarToggled={documentSidebarToggled} />
</div>
);
}
@ -75,62 +104,215 @@ export function SharedChatDisplay({
onClose={() => setPresentingDocument(null)}
/>
)}
<div className="w-full h-[100dvh] overflow-hidden">
<div className="flex max-h-full overflow-hidden pb-[72px]">
<div className="flex w-full overflow-hidden overflow-y-scroll">
<div className="w-full h-full flex-col flex max-w-message-max mx-auto">
<div className="px-5 pt-8">
<h1 className="text-3xl text-strong font-bold">
{chatSession.description ||
`Chat ${chatSession.chat_session_id}`}
</h1>
<p className="text-emphasis">
{humanReadableFormat(chatSession.time_created)}
</p>
{documentSidebarToggled && settings?.isMobile && (
<div className="md:hidden">
<Modal noPadding noScroll>
<ChatFilters
isSharedChat={true}
selectedMessage={
selectedMessageForDocDisplay
? messages.find(
(message) =>
message.messageId === selectedMessageForDocDisplay
) || null
: null
}
toggleDocumentSelection={() => {
setDocumentSidebarToggled(true);
}}
selectedDocuments={[]}
clearSelectedDocuments={() => {}}
selectedDocumentTokens={0}
maxTokens={0}
initialWidth={400}
isOpen={true}
setPresentingDocument={setPresentingDocument}
modal={true}
ccPairs={[]}
tags={[]}
documentSets={[]}
showFilters={false}
closeSidebar={() => {
setDocumentSidebarToggled(false);
}}
/>
</Modal>
</div>
)}
<Separator />
<div className="fixed inset-0 flex flex-col text-default">
<div className="h-[100dvh] px-2 overflow-y-hidden">
<div className="w-full h-[100dvh] flex flex-col overflow-hidden">
{!settings?.isMobile && (
<div
style={{ transition: "width 0.30s ease-out" }}
className={`
flex-none
fixed
right-0
z-[1000]
bg-background
h-screen
transition-all
bg-opacity-80
duration-300
ease-in-out
bg-transparent
transition-all
bg-opacity-80
duration-300
ease-in-out
h-full
${documentSidebarToggled ? "w-[400px]" : "w-[0px]"}
`}
>
<ChatFilters
modal={false}
isSharedChat={true}
selectedMessage={
selectedMessageForDocDisplay
? messages.find(
(message) =>
message.messageId === selectedMessageForDocDisplay
) || null
: null
}
toggleDocumentSelection={() => {
setDocumentSidebarToggled(true);
}}
clearSelectedDocuments={() => {}}
selectedDocumentTokens={0}
maxTokens={0}
initialWidth={400}
isOpen={true}
setPresentingDocument={setPresentingDocument}
ccPairs={[]}
tags={[]}
documentSets={[]}
showFilters={false}
closeSidebar={() => {
setDocumentSidebarToggled(false);
}}
selectedDocuments={[]}
/>
</div>
{isReady ? (
<div className="w-full pb-16">
{messages.map((message) => {
if (message.type === "user") {
return (
<HumanMessage
shared
key={message.messageId}
content={message.message}
files={message.files}
/>
);
} else {
return (
<AIMessage
shared
setPresentingDocument={setPresentingDocument}
currentPersona={persona}
key={message.messageId}
messageId={message.messageId}
content={message.message}
files={message.files || []}
citedDocuments={getCitedDocumentsFromMessage(message)}
isComplete
/>
);
}
})}
</div>
) : (
<div className="grow flex-0 h-screen w-full flex items-center justify-center">
<div className="mb-[33vh]">
<OnyxInitializingLoader />
)}
<div className="flex mobile:hidden max-h-full overflow-hidden ">
<FunctionalHeader
documentSidebarToggled={documentSidebarToggled}
sidebarToggled={false}
toggleSidebar={() => {}}
page="chat"
reset={() => {}}
/>
</div>
<div className="flex w-full overflow-hidden overflow-y-scroll">
<div className="w-full h-full flex-col flex max-w-message-max mx-auto">
<div className="fixed z-10 w-full ">
<div className="bg-background relative px-5 pt-4 w-full">
<h1 className="text-3xl text-strong font-bold">
{chatSession.description ||
`Chat ${chatSession.chat_session_id}`}
</h1>
<p className=" text-emphasis">
{humanReadableFormat(chatSession.time_created)}
</p>
<div
className={`
h-full absolute top-0 z-10 w-full sm:w-[90%] lg:w-[70%]
bg-gradient-to-b via-50% z-[-1] from-background via-background to-background/10 flex
transition-all duration-300 ease-in-out
${
documentSidebarToggled
? "left-[200px] transform -translate-x-[calc(50%+100px)]"
: "left-1/2 transform -translate-x-1/2"
}
`}
/>
</div>
</div>
{isReady ? (
<div className="w-full pt-24 pb-16">
{messages.map((message) => {
if (message.type === "user") {
return (
<HumanMessage
shared
key={message.messageId}
content={message.message}
files={message.files}
/>
);
} else {
return (
<AIMessage
shared
query={message.query || undefined}
hasDocs={
(message.documents &&
message.documents.length > 0) === true
}
toolCall={message.toolCall}
docs={message.documents}
setPresentingDocument={setPresentingDocument}
currentPersona={persona}
key={message.messageId}
messageId={message.messageId}
content={message.message}
files={message.files || []}
citedDocuments={getCitedDocumentsFromMessage(
message
)}
// toggleDocumentSelection={() => {
// setDocumentSidebarToggled(true);
// }}
toggleDocumentSelection={() => {
if (
!documentSidebarToggled ||
(documentSidebarToggled &&
selectedMessageForDocDisplay ===
message.messageId)
) {
toggleDocumentSidebar();
}
setSelectedMessageForDocDisplay(
message.messageId
);
}}
isComplete
/>
);
}
})}
</div>
) : (
<div className="grow flex-0 h-screen w-full flex items-center justify-center">
<div className="mb-[33vh]">
<OnyxInitializingLoader />
</div>
</div>
)}
</div>
{!settings?.isMobile && (
<div
style={{ transition: "width 0.30s ease-out" }}
className={`
flex-none
overflow-y-hidden
transition-all
duration-300
ease-in-out
${documentSidebarToggled ? "w-[400px]" : "w-[0px]"}
`}
></div>
)}
</div>
</div>
</div>
<BackToOnyxButton />
<FixedLogo backgroundToggled={false} />
<BackToOnyxButton documentSidebarToggled={documentSidebarToggled} />
</div>
</div>
</>
);

View File

@ -13,8 +13,8 @@ import {
FetchAssistantsResponse,
fetchAssistantsSS,
} from "@/lib/assistants/fetchAssistantsSS";
import FunctionalHeader from "@/components/chat_search/Header";
import { defaultPersona } from "@/app/admin/assistants/lib";
import { constructMiniFiedPersona } from "@/lib/assistantIconUtils";
async function getSharedChat(chatId: string) {
const response = await fetchSS(
@ -34,7 +34,6 @@ export default async function Page(props: {
getAuthTypeMetadataSS(),
getCurrentUserSS(),
getSharedChat(params.chatId),
fetchAssistantsSS(),
];
// catch cases where the backend is completely unreachable here
@ -50,8 +49,6 @@ export default async function Page(props: {
const authTypeMetadata = results[0] as AuthTypeMetadata | null;
const user = results[1] as User | null;
const chatSession = results[2] as BackendChatSession | null;
const assistantsResponse = results[3] as FetchAssistantsResponse | null;
const [availableAssistants, error] = assistantsResponse ?? [[], null];
const authDisabled = authTypeMetadata?.authType === "disabled";
if (!authDisabled && !user) {
@ -61,22 +58,13 @@ export default async function Page(props: {
if (user && !user.is_verified && authTypeMetadata?.requiresVerification) {
return redirect("/auth/waiting-on-verification");
}
// prettier-ignore
const persona: Persona =
chatSession?.persona_id && availableAssistants?.length
? (availableAssistants.find((p) => p.id === chatSession.persona_id) ??
defaultPersona)
: (availableAssistants?.[0] ?? defaultPersona);
return (
<div>
<div className="absolute top-0 z-40 w-full">
<FunctionalHeader page="shared" />
</div>
<div className="flex relative bg-background text-default overflow-hidden pt-16 h-screen">
<SharedChatDisplay chatSession={chatSession} persona={persona} />
</div>
</div>
const persona: Persona = constructMiniFiedPersona(
chatSession?.persona_icon_color ?? null,
chatSession?.persona_icon_shape ?? null,
chatSession?.persona_name ?? "",
chatSession?.persona_id ?? 0
);
return <SharedChatDisplay chatSession={chatSession} persona={persona} />;
}

View File

@ -28,7 +28,7 @@ export function MetadataBadge({
className: flexNone ? "flex-none" : "mr-0.5 my-auto",
})}
<p className="max-w-[6rem] text-ellipsis overflow-hidden truncate whitespace-nowrap">
{value}lllaasfasdf
{value}
</p>
</div>
);

View File

@ -35,7 +35,7 @@ const DropdownOption: React.FC<DropdownOptionProps> = ({
openInNewTab,
}) => {
const content = (
<div className="flex py-3 px-4 cursor-pointer rounded hover:bg-hover-light">
<div className="flex py-3 px-4 cursor-pointer rounded hover:bg-hover">
{icon}
{label}
</div>

View File

@ -33,7 +33,7 @@ export function AssistantIcon({
return (
<CustomTooltip
disabled={disableToolip}
disabled={disableToolip || !assistant.description}
showTick
line
wrap

View File

@ -130,7 +130,7 @@ export default function FunctionalHeader({
: "")
}
>
<div className="cursor-pointer mr-4 flex-none text-text-700 hover:text-text-600 transition-colors duration-300">
<div className="cursor-pointer ml-2 mr-4 flex-none text-text-700 hover:text-text-600 transition-colors duration-300">
<NewChatIcon size={20} />
</div>
</Link>

View File

@ -1,3 +1,5 @@
import { Persona } from "@/app/admin/assistants/interfaces";
export interface GridShape {
encodedGrid: number;
filledSquares: number;
@ -45,7 +47,9 @@ export function generateRandomIconShape(): GridShape {
if (grid[row][col]) {
const x = col * 12;
const y = row * 12;
path += `M ${x} ${y} L ${x + 12} ${y} L ${x + 12} ${y + 12} L ${x} ${y + 12} Z `;
path += `M ${x} ${y} L ${x + 12} ${y} L ${x + 12} ${y + 12} L ${x} ${
y + 12
} Z `;
}
}
}
@ -94,7 +98,9 @@ export function createSVG(
if (grid[row][col]) {
const x = col * 12;
const y = row * 12;
path += `M ${x} ${y} L ${x + 12} ${y} L ${x + 12} ${y + 12} L ${x} ${y + 12} Z `;
path += `M ${x} ${y} L ${x + 12} ${y} L ${x + 12} ${y + 12} L ${x} ${
y + 12
} Z `;
}
}
}
@ -132,3 +138,32 @@ function shuffleArray(array: any[]) {
[array[i], array[j]] = [array[j], array[i]];
}
}
// This is used for rendering a persona in the shared chat display
export const constructMiniFiedPersona = (
assistant_icon_color: string | null,
assistant_icon_shape: number | null,
name: string,
id: number
): Persona => {
return {
id,
name,
icon_color: assistant_icon_color ?? undefined,
icon_shape: assistant_icon_shape ?? undefined,
is_visible: true,
is_public: true,
display_priority: 0,
description: "",
document_sets: [],
prompts: [],
tools: [],
search_start_date: null,
owner: null,
starter_messages: null,
builtin_persona: false,
is_default_persona: false,
users: [],
groups: [],
};
};