diff --git a/backend/onyx/server/query_and_chat/chat_backend.py b/backend/onyx/server/query_and_chat/chat_backend.py index b9e3dc06686..57c3fadff62 100644 --- a/backend/onyx/server/query_and_chat/chat_backend.py +++ b/backend/onyx/server/query_and_chat/chat_backend.py @@ -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, diff --git a/backend/onyx/server/query_and_chat/models.py b/backend/onyx/server/query_and_chat/models.py index 068591c0a52..e44982f754c 100644 --- a/backend/onyx/server/query_and_chat/models.py +++ b/backend/onyx/server/query_and_chat/models.py @@ -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 diff --git a/web/src/app/chat/ChatPage.tsx b/web/src/app/chat/ChatPage.tsx index 1438920e677..ecc7a63934e 100644 --- a/web/src/app/chat/ChatPage.tsx +++ b/web/src/app/chat/ChatPage.tsx @@ -2105,6 +2105,7 @@ export function ChatPage({ } /> )} + {sharingModalVisible && chatSessionIdRef.current !== null && ( void; tokenLimitReached: boolean; + hideSelection?: boolean; setPresentingDocument: Dispatch>; } @@ -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 ( -
+
- {!isInternet && ( + {!isInternet && !hideSelection && ( handleSelect(document.document_id)} diff --git a/web/src/app/chat/documentSidebar/ChatFilters.tsx b/web/src/app/chat/documentSidebar/ChatFilters.tsx index 585a33c59a8..f91529f918f 100644 --- a/web/src/app/chat/documentSidebar/ChatFilters.tsx +++ b/web/src/app/chat/documentSidebar/ChatFilters.tsx @@ -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( selectedDocumentTokens, maxTokens, initialWidth, + isSharedChat, isOpen, ccPairs, tags, @@ -79,13 +81,14 @@ export const ChatFilters = forwardRef( const dedupedDocuments = removeDuplicateDocs(currentDocuments || []); const tokenLimitReached = selectedDocumentTokens > maxTokens - 75; + console.log("SELECTED MESSAGE is", selectedMessage); const hasSelectedDocuments = selectedDocumentIds.length > 0; return (
{ @@ -122,10 +125,10 @@ export const ChatFilters = forwardRef(
{showFilters ? ( ccPair.source)} availableTags={tags} @@ -157,6 +160,7 @@ export const ChatFilters = forwardRef( )! ); }} + hideSelection={isSharedChat} tokenLimitReached={tokenLimitReached} />
diff --git a/web/src/app/chat/interfaces.ts b/web/src/app/chat/interfaces.ts index e4f03f25173..ee8320a24c3 100644 --- a/web/src/app/chat/interfaces.ts +++ b/web/src/app/chat/interfaces.ts @@ -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; diff --git a/web/src/app/chat/message/MemoizedTextComponents.tsx b/web/src/app/chat/message/MemoizedTextComponents.tsx index 876a5e26835..a29038e62ec 100644 --- a/web/src/app/chat/message/MemoizedTextComponents.tsx +++ b/web/src/app/chat/message/MemoizedTextComponents.tsx @@ -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 = ( - - ); + const icon = + (associatedDoc && ( + + )) || + null; return ( ( {props.children} @@ -378,6 +378,7 @@ export const AIMessage = ({ onMessageSelection && otherMessagesCanSwitchTo && otherMessagesCanSwitchTo.length > 1; + return (
{!toolCall || toolCall.tool_name === SEARCH_TOOL_NAME ? ( <> - {query !== undefined && - handleShowRetrieved !== undefined && - !retrievalDisabled && ( -
- -
- )} + {query !== undefined && !retrievalDisabled && ( +
+ +
+ )} {handleForceSearch && content && query === undefined && diff --git a/web/src/app/chat/message/SearchSummary.tsx b/web/src/app/chat/message/SearchSummary.tsx index 7349ec6ca35..9c22f81d3fa 100644 --- a/web/src/app/chat/message/SearchSummary.tsx +++ b/web/src/app/chat/message/SearchSummary.tsx @@ -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); diff --git a/web/src/app/chat/modal/ShareChatSessionModal.tsx b/web/src/app/chat/modal/ShareChatSessionModal.tsx index ffff8bb9714..e438f0a3c41 100644 --- a/web/src/app/chat/modal/ShareChatSessionModal.tsx +++ b/web/src/app/chat/modal/ShareChatSessionModal.tsx @@ -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 ? (
- 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: @@ -157,10 +157,8 @@ export function ShareChatSessionModal({ ) : (
- 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.
diff --git a/web/src/app/chat/shared/[chatId]/SharedChatDisplay.tsx b/web/src/app/chat/shared/[chatId]/SharedChatDisplay.tsx index 13ef287a620..b5a1d16c870 100644 --- a/web/src/app/chat/shared/[chatId]/SharedChatDisplay.tsx +++ b/web/src/app/chat/shared/[chatId]/SharedChatDisplay.tsx @@ -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"}
+
); } @@ -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(null); const [isReady, setIsReady] = useState(false); const [presentingDocument, setPresentingDocument] = useState(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.
- +
); } @@ -75,62 +104,215 @@ export function SharedChatDisplay({ onClose={() => setPresentingDocument(null)} /> )} -
-
-
-
-
-

- {chatSession.description || - `Chat ${chatSession.chat_session_id}`} -

-

- {humanReadableFormat(chatSession.time_created)} -

+ {documentSidebarToggled && settings?.isMobile && ( +
+ + + 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); + }} + /> + +
+ )} - +
+
+
+ {!settings?.isMobile && ( +
+ + 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={[]} + />
- {isReady ? ( -
- {messages.map((message) => { - if (message.type === "user") { - return ( - - ); - } else { - return ( - - ); - } - })} -
- ) : ( -
-
- + )} +
+ {}} + page="chat" + reset={() => {}} + /> +
+ +
+
+
+
+

+ {chatSession.description || + `Chat ${chatSession.chat_session_id}`} +

+

+ {humanReadableFormat(chatSession.time_created)} +

+
+ {isReady ? ( +
+ {messages.map((message) => { + if (message.type === "user") { + return ( + + ); + } else { + return ( + 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 + /> + ); + } + })} +
+ ) : ( +
+
+ +
+
+ )} +
+ {!settings?.isMobile && ( +
)}
-
- + + +
); diff --git a/web/src/app/chat/shared/[chatId]/page.tsx b/web/src/app/chat/shared/[chatId]/page.tsx index 11e624ff687..f001038f88e 100644 --- a/web/src/app/chat/shared/[chatId]/page.tsx +++ b/web/src/app/chat/shared/[chatId]/page.tsx @@ -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 ( -
-
- -
- -
- -
-
+ const persona: Persona = constructMiniFiedPersona( + chatSession?.persona_icon_color ?? null, + chatSession?.persona_icon_shape ?? null, + chatSession?.persona_name ?? "", + chatSession?.persona_id ?? 0 ); + + return ; } diff --git a/web/src/components/MetadataBadge.tsx b/web/src/components/MetadataBadge.tsx index 0af63e58da0..bbae230c77f 100644 --- a/web/src/components/MetadataBadge.tsx +++ b/web/src/components/MetadataBadge.tsx @@ -28,7 +28,7 @@ export function MetadataBadge({ className: flexNone ? "flex-none" : "mr-0.5 my-auto", })}

- {value}lllaasfasdf + {value}

); diff --git a/web/src/components/UserDropdown.tsx b/web/src/components/UserDropdown.tsx index e2fd8fce167..928b73b6c1b 100644 --- a/web/src/components/UserDropdown.tsx +++ b/web/src/components/UserDropdown.tsx @@ -35,7 +35,7 @@ const DropdownOption: React.FC = ({ openInNewTab, }) => { const content = ( -
+
{icon} {label}
diff --git a/web/src/components/assistants/AssistantIcon.tsx b/web/src/components/assistants/AssistantIcon.tsx index 7926928b7c7..df1ac725b40 100644 --- a/web/src/components/assistants/AssistantIcon.tsx +++ b/web/src/components/assistants/AssistantIcon.tsx @@ -33,7 +33,7 @@ export function AssistantIcon({ return ( -
+
diff --git a/web/src/lib/assistantIconUtils.tsx b/web/src/lib/assistantIconUtils.tsx index e6e06123b25..59ed9710d8d 100644 --- a/web/src/lib/assistantIconUtils.tsx +++ b/web/src/lib/assistantIconUtils.tsx @@ -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: [], + }; +};