From b733fed89fa7729d08bd694c5e3c6c72e9565e30 Mon Sep 17 00:00:00 2001 From: pablonyx Date: Sat, 22 Mar 2025 11:22:09 -0700 Subject: [PATCH] misc improvements --- .../versions/9aadf32dfeb4_add_user_files.py | 5 +- backend/onyx/db/models.py | 1 + backend/onyx/db/user_documents.py | 2 + backend/onyx/indexing/indexing_pipeline.py | 7 +- backend/onyx/main.py | 4 + backend/onyx/server/user_documents/api.py | 62 ++++++++++++- backend/onyx/server/user_documents/models.py | 5 ++ backend/onyx/setup.py | 5 ++ web/src/app/chat/ChatPage.tsx | 23 ++++- .../chat/documentSidebar/DocumentResults.tsx | 14 ++- web/src/app/chat/interfaces.ts | 4 + .../chat/message/MemoizedTextComponents.tsx | 13 ++- web/src/app/chat/message/Messages.tsx | 66 ++++++++++++-- web/src/app/chat/message/SearchSummary.tsx | 3 +- web/src/app/chat/message/SourcesDisplay.tsx | 46 ++++++---- .../chat/my-documents/DocumentsContext.tsx | 5 ++ .../[id]/components/DocumentList.tsx | 10 ++- .../components/upload/FileUploadSection.tsx | 2 +- .../my-documents/components/FileListItem.tsx | 87 ++++++++++++++++++- .../my-documents/components/FilePicker.tsx | 18 ++-- .../components/SelectedItemsList.tsx | 17 ++-- .../components/SharedFolderItem.tsx | 38 ++++---- .../app/chat/my-documents/components/types.ts | 1 + .../components/chat/sources/SourceCard.tsx | 51 ++++------- web/src/lib/assistantIconUtils.tsx | 9 +- 25 files changed, 389 insertions(+), 109 deletions(-) diff --git a/backend/alembic/versions/9aadf32dfeb4_add_user_files.py b/backend/alembic/versions/9aadf32dfeb4_add_user_files.py index 941ce7f6b..b20addc9c 100644 --- a/backend/alembic/versions/9aadf32dfeb4_add_user_files.py +++ b/backend/alembic/versions/9aadf32dfeb4_add_user_files.py @@ -1,7 +1,7 @@ """add user files Revision ID: 9aadf32dfeb4 -Revises: df46c75b714e +Revises: 3781a5eb12cb Create Date: 2025-01-26 16:08:21.551022 """ @@ -12,7 +12,7 @@ import datetime # revision identifiers, used by Alembic. revision = "9aadf32dfeb4" -down_revision = "df46c75b714e" +down_revision = "3781a5eb12cb" branch_labels = None depends_on = None @@ -42,6 +42,7 @@ def upgrade() -> None: sa.ForeignKey("user_folder.id"), nullable=True, ), + sa.Column("link_url", sa.String(), nullable=True), sa.Column("token_count", sa.Integer(), nullable=True), sa.Column("file_type", sa.String(), nullable=True), sa.Column("file_id", sa.String(length=255), nullable=False), diff --git a/backend/onyx/db/models.py b/backend/onyx/db/models.py index 70b66ff53..999e0082b 100644 --- a/backend/onyx/db/models.py +++ b/backend/onyx/db/models.py @@ -2430,6 +2430,7 @@ class UserFile(Base): cc_pair: Mapped["ConnectorCredentialPair"] = relationship( "ConnectorCredentialPair", back_populates="user_file" ) + link_url: Mapped[str | None] = mapped_column(String, nullable=True) """ diff --git a/backend/onyx/db/user_documents.py b/backend/onyx/db/user_documents.py index 3cffd9b13..17174b42b 100644 --- a/backend/onyx/db/user_documents.py +++ b/backend/onyx/db/user_documents.py @@ -38,6 +38,7 @@ def create_user_files( folder_id: int | None, user: User | None, db_session: Session, + link_url: str | None = None, ) -> list[UserFile]: upload_response = upload_files(files, db_session) user_files = [] @@ -50,6 +51,7 @@ def create_user_files( document_id="USER_FILE_CONNECTOR__" + file_path, name=file.filename, token_count=None, + link_url=link_url, ) db_session.add(new_file) user_files.append(new_file) diff --git a/backend/onyx/indexing/indexing_pipeline.py b/backend/onyx/indexing/indexing_pipeline.py index af73c48fc..b3849bb01 100644 --- a/backend/onyx/indexing/indexing_pipeline.py +++ b/backend/onyx/indexing/indexing_pipeline.py @@ -12,9 +12,6 @@ from onyx.configs.app_configs import MAX_DOCUMENT_CHARS from onyx.configs.constants import DEFAULT_BOOST from onyx.configs.llm_configs import get_image_extraction_and_analysis_enabled from onyx.configs.model_configs import USE_INFORMATION_CONTENT_CLASSIFICATION -from onyx.configs.constants import ( - DEFAULT_BOOST, -) from onyx.connectors.cross_connector_utils.miscellaneous_utils import ( get_experts_stores_representations, ) @@ -65,10 +62,10 @@ from onyx.indexing.models import IndexChunk from onyx.indexing.models import UpdatableChunkData from onyx.indexing.vector_db_insertion import write_chunks_to_vector_db_with_backoff from onyx.llm.factory import get_default_llm_with_vision +from onyx.llm.factory import get_default_llms from onyx.natural_language_processing.search_nlp_models import ( InformationContentClassificationModel, ) -from onyx.llm.factory import get_default_llms from onyx.natural_language_processing.utils import get_tokenizer from onyx.utils.logger import setup_logger from onyx.utils.timing import log_function_time @@ -836,6 +833,8 @@ def index_doc_batch( chunk_data=updatable_chunk_data, db_session=db_session ) + # Pause user file ccpairs + db_session.commit() result = IndexingPipelineResult( diff --git a/backend/onyx/main.py b/backend/onyx/main.py index 1efbd306d..dbaff5497 100644 --- a/backend/onyx/main.py +++ b/backend/onyx/main.py @@ -211,7 +211,9 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: logger.notice("Generative AI Q&A disabled") # fill up Postgres connection pools + print("Warming up connections") await warm_up_connections() + print("Connections warmed up") if not MULTI_TENANT: # We cache this at the beginning so there is no delay in the first telemetry @@ -219,10 +221,12 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: get_or_generate_uuid() # If we are multi-tenant, we need to only set up initial public tables + print("Setting up onyx") with Session(engine) as db_session: setup_onyx(db_session, POSTGRES_DEFAULT_SCHEMA) else: setup_multitenant_onyx() + print("onyz set up") if not MULTI_TENANT: # don't emit a metric for every pod rollover/restart diff --git a/backend/onyx/server/user_documents/api.py b/backend/onyx/server/user_documents/api.py index 424330988..53aaf65a2 100644 --- a/backend/onyx/server/user_documents/api.py +++ b/backend/onyx/server/user_documents/api.py @@ -25,6 +25,7 @@ from onyx.db.connector_credential_pair import add_credential_to_connector from onyx.db.credentials import create_credential from onyx.db.engine import get_session from onyx.db.enums import AccessType +from onyx.db.models import ConnectorCredentialPair from onyx.db.models import User from onyx.db.models import UserFile from onyx.db.models import UserFolder @@ -363,11 +364,13 @@ def create_file_from_link( soup = BeautifulSoup(content, "html.parser") parsed_html = web_html_cleanup(soup, mintlify_cleanup_enabled=False) - file_name = f"{parsed_html.title or 'Untitled'}.txt" + file_name = f"{parsed_html.title or 'Untitled'}" file_content = parsed_html.cleaned_text.encode() file = UploadFile(filename=file_name, file=io.BytesIO(file_content)) - user_files = create_user_files([file], request.folder_id, user, db_session) + user_files = create_user_files( + [file], request.folder_id, user, db_session, link_url=request.url + ) # Create connector and credential (same as in upload_user_files) for user_file in user_files: @@ -448,6 +451,61 @@ def get_files_token_estimate( return {"total_tokens": total_tokens} +class ReindexFileRequest(BaseModel): + file_id: int + + +@router.post("/user/file/reindex") +def reindex_file( + request: ReindexFileRequest, + user: User = Depends(current_user), + db_session: Session = Depends(get_session), +) -> MessageResponse: + user_id = user.id if user else None + user_file_to_reindex = ( + db_session.query(UserFile) + .filter(UserFile.id == request.file_id, UserFile.user_id == user_id) + .first() + ) + + if not user_file_to_reindex: + raise HTTPException(status_code=404, detail="File not found") + + if not user_file_to_reindex.cc_pair_id: + raise HTTPException( + status_code=400, + detail="File does not have an associated connector-credential pair", + ) + + # Get the connector id from the cc_pair + cc_pair = ( + db_session.query(ConnectorCredentialPair) + .filter_by(id=user_file_to_reindex.cc_pair_id) + .first() + ) + if not cc_pair: + raise HTTPException( + status_code=404, detail="Associated connector-credential pair not found" + ) + + # Trigger immediate reindexing with highest priority + tenant_id = get_current_tenant_id() + try: + trigger_indexing_for_cc_pair( + [], cc_pair.connector_id, True, tenant_id, db_session, is_user_file=True + ) + return MessageResponse( + message="File reindexing has been triggered successfully" + ) + except Exception as e: + logger.error( + f"Error triggering reindexing for file {request.file_id}: {str(e)}" + ) + raise HTTPException( + status_code=500, detail=f"Failed to trigger reindexing: {str(e)}" + ) + + class BulkCleanupRequest(BaseModel): folder_id: int days_older_than: int | None = None diff --git a/backend/onyx/server/user_documents/models.py b/backend/onyx/server/user_documents/models.py index 11d84e036..207191192 100644 --- a/backend/onyx/server/user_documents/models.py +++ b/backend/onyx/server/user_documents/models.py @@ -19,6 +19,8 @@ class UserFileSnapshot(BaseModel): assistant_ids: List[int] = [] # List of assistant IDs token_count: int | None indexed: bool + link_url: str | None + failed: bool | None @classmethod def from_model(cls, model: UserFile) -> "UserFileSnapshot": @@ -32,9 +34,12 @@ class UserFileSnapshot(BaseModel): created_at=model.created_at, assistant_ids=[assistant.id for assistant in model.assistants], token_count=model.token_count, + failed=len(model.cc_pair.index_attempts) > 0 + and model.cc_pair.last_successful_index_time is None, indexed=model.cc_pair.last_successful_index_time is not None if model.cc_pair else False, + link_url=model.link_url, ) diff --git a/backend/onyx/setup.py b/backend/onyx/setup.py index b1d2a4c04..03239ca28 100644 --- a/backend/onyx/setup.py +++ b/backend/onyx/setup.py @@ -74,12 +74,15 @@ def setup_onyx( The Tenant Service calls the tenants/create endpoint which runs this. """ + print("checking and performing index swap") check_and_perform_index_swap(db_session=db_session) + print("getting active search settings") active_search_settings = get_active_search_settings(db_session) search_settings = active_search_settings.primary secondary_search_settings = active_search_settings.secondary + print("getting current search settings") # search_settings = get_current_search_settings(db_session) # multipass_config_1 = get_multipass_config(search_settings) @@ -91,6 +94,7 @@ def setup_onyx( # Break bad state for thrashing indexes if secondary_search_settings and DISABLE_INDEX_UPDATE_ON_SWAP: + print("expiring index attempts") expire_index_attempts( search_settings_id=search_settings.id, db_session=db_session ) @@ -99,6 +103,7 @@ def setup_onyx( resync_cc_pair(cc_pair, db_session=db_session) # Expire all old embedding models indexing attempts, technically redundant + print("cancelling indexing attempts past model") cancel_indexing_attempts_past_model(db_session) logger.notice(f'Using Embedding model: "{search_settings.model_name}"') diff --git a/web/src/app/chat/ChatPage.tsx b/web/src/app/chat/ChatPage.tsx index b6f503a78..ca080ef02 100644 --- a/web/src/app/chat/ChatPage.tsx +++ b/web/src/app/chat/ChatPage.tsx @@ -189,6 +189,7 @@ export function ChatPage({ removeSelectedFolder, clearSelectedItems, folders: userFolders, + files: allUserFiles, uploadFile, removeSelectedFile, currentMessageFiles, @@ -2769,11 +2770,25 @@ export function ChatPage({ ? messageHistory[i + 1] : undefined; - const userFiles = previousMessage?.files.filter( - (file) => - file.type == ChatFileType.USER_KNOWLEDGE + const attachedFileDescriptors = + previousMessage?.files.filter( + (file) => + file.type == ChatFileType.USER_KNOWLEDGE + ); + const userFiles = allUserFiles?.filter((file) => + attachedFileDescriptors?.some( + (descriptor) => + descriptor.id === file.file_id + ) ); - + console.log("alluser files"); + console.log(allUserFiles); + console.log( + "current attached file descriptors" + ); + console.log(attachedFileDescriptors); + console.log("user files"); + console.log(userFiles); return (
( return () => clearTimeout(timer); }, [selectedDocuments]); + const { files: allUserFiles } = useDocumentsContext(); - const userFiles = humanMessage?.files.filter( + const humanFileDescriptors = humanMessage?.files.filter( (file) => file.type == ChatFileType.USER_KNOWLEDGE ); + const userFiles = allUserFiles?.filter((file) => + humanFileDescriptors?.some((descriptor) => descriptor.id === file.file_id) + ); const selectedDocumentIds = selectedDocuments?.map((document) => document.document_id) || []; @@ -127,7 +132,12 @@ export const DocumentResults = forwardRef( + setPresentingDocument({ + document_id: file.document_id, + semantic_identifier: file.file_id || null, + }) + } /> ))}
diff --git a/web/src/app/chat/interfaces.ts b/web/src/app/chat/interfaces.ts index 4a75c6dcd..a236290c9 100644 --- a/web/src/app/chat/interfaces.ts +++ b/web/src/app/chat/interfaces.ts @@ -50,6 +50,10 @@ export interface FileDescriptor { isUploading?: boolean; } +export interface FileDescriptorWithHighlights extends FileDescriptor { + match_highlights: string[]; +} + export interface LLMRelevanceFilterPacket { relevant_chunk_indices: number[]; } diff --git a/web/src/app/chat/message/MemoizedTextComponents.tsx b/web/src/app/chat/message/MemoizedTextComponents.tsx index 7a440964d..167215004 100644 --- a/web/src/app/chat/message/MemoizedTextComponents.tsx +++ b/web/src/app/chat/message/MemoizedTextComponents.tsx @@ -10,12 +10,14 @@ import { SourceIcon } from "@/components/SourceIcon"; import { WebResultIcon } from "@/components/WebResultIcon"; import { SubQuestionDetail } from "../interfaces"; import { ValidSources } from "@/lib/types"; +import { FileResponse } from "../my-documents/DocumentsContext"; export const MemoizedAnchor = memo( ({ docs, subQuestions, openQuestion, + userFiles, href, updatePresentingDocument, children, @@ -23,6 +25,7 @@ export const MemoizedAnchor = memo( subQuestions?: SubQuestionDetail[]; openQuestion?: (question: SubQuestionDetail) => void; docs?: OnyxDocument[] | null; + userFiles?: FileResponse[] | null; updatePresentingDocument: (doc: OnyxDocument) => void; href?: string; children: React.ReactNode; @@ -31,8 +34,14 @@ export const MemoizedAnchor = memo( if (value?.startsWith("[") && value?.endsWith("]")) { const match = value.match(/\[(D|Q)?(\d+)\]/); if (match) { - const isSubQuestion = match[1] === "Q"; - if (!isSubQuestion) { + const isUserFileCitation = userFiles?.length && userFiles.length > 0; + if (isUserFileCitation) { + const index = parseInt(match[2], 10) - 1; + const associatedUserFile = userFiles?.[index]; + if (!associatedUserFile) { + return {children}; + } + } else if (!isUserFileCitation) { const index = parseInt(match[2], 10) - 1; const associatedDoc = docs?.[index]; if (!associatedDoc) { diff --git a/web/src/app/chat/message/Messages.tsx b/web/src/app/chat/message/Messages.tsx index d8e57f51f..e75fa3f75 100644 --- a/web/src/app/chat/message/Messages.tsx +++ b/web/src/app/chat/message/Messages.tsx @@ -73,6 +73,7 @@ import rehypeKatex from "rehype-katex"; import "katex/dist/katex.min.css"; import { copyAll, handleCopy } from "./copyingUtils"; import { transformLinkUri } from "@/lib/utils"; +import { FileResponse } from "../my-documents/DocumentsContext"; const TOOLS_WITH_CUSTOM_HANDLING = [ SEARCH_TOOL_NAME, @@ -164,6 +165,46 @@ function FileDisplay({ ); } +function FileResponseDisplay({ + files, + alignBubble, + setPresentingDocument, +}: { + files: FileResponse[]; + alignBubble?: boolean; + setPresentingDocument: (document: MinimalOnyxDocument) => void; +}) { + if (!files || files.length === 0) { + return null; + } + + return ( +
+
+ {files.map((file) => { + return ( +
+ + setPresentingDocument({ + document_id: file.document_id, + semantic_identifier: file.name || file.document_id, + }) + } + /> +
+ ); + })} +
+
+ ); +} + export const AIMessage = ({ userKnowledgeFiles = [], regenerate, @@ -195,7 +236,7 @@ export const AIMessage = ({ documentSidebarVisible, removePadding, }: { - userKnowledgeFiles?: FileDescriptor[]; + userKnowledgeFiles?: FileResponse[]; index?: number; shared?: boolean; isActive?: boolean; @@ -323,6 +364,7 @@ export const AIMessage = ({ {props.children} @@ -522,8 +564,11 @@ export const AIMessage = ({ + setPresentingDocument({ + document_id: doc.document_id, + semantic_identifier: doc.document_id, + }) } /> ))} @@ -555,15 +600,19 @@ export const AIMessage = ({ userKnowledgeFiles.length > 0 && userKnowledgeFiles .slice(0, 2) - .map((file: FileDescriptor, ind: number) => ( + .map((file: FileResponse, ind: number) => ( + setPresentingDocument({ + document_id: file.document_id, + semantic_identifier: file.name, + }) } /> ))} + {userKnowledgeFiles.length > 2 && ( )} - {content || userKnowledgeFiles || files ? ( + + {content || files ? ( <> {typeof content === "string" ? (
diff --git a/web/src/app/chat/message/SearchSummary.tsx b/web/src/app/chat/message/SearchSummary.tsx index 9c15410aa..cb94c31bd 100644 --- a/web/src/app/chat/message/SearchSummary.tsx +++ b/web/src/app/chat/message/SearchSummary.tsx @@ -16,6 +16,7 @@ import { ValidSources } from "@/lib/types"; import { useEffect, useRef, useState } from "react"; import { FiBook, FiCheck, FiEdit2, FiSearch, FiX } from "react-icons/fi"; import { FileDescriptor } from "../interfaces"; +import { FileResponse } from "../my-documents/DocumentsContext"; export function ShowHideDocsButton({ messageId, @@ -250,7 +251,7 @@ export function SearchSummary({ export function UserKnowledgeFiles({ userKnowledgeFiles, }: { - userKnowledgeFiles: FileDescriptor[]; + userKnowledgeFiles: FileResponse[]; }): JSX.Element { if (!userKnowledgeFiles || userKnowledgeFiles.length === 0) { return <>; diff --git a/web/src/app/chat/message/SourcesDisplay.tsx b/web/src/app/chat/message/SourcesDisplay.tsx index 3fe728096..3e7cdc74d 100644 --- a/web/src/app/chat/message/SourcesDisplay.tsx +++ b/web/src/app/chat/message/SourcesDisplay.tsx @@ -6,8 +6,9 @@ import { buildDocumentSummaryDisplay } from "@/components/search/DocumentDisplay import { ValidSources } from "@/lib/types"; import { FiFileText } from "react-icons/fi"; import { FileDescriptor } from "../interfaces"; -import { getFileIconFromFileName } from "@/lib/assistantIconUtils"; +import { getFileIconFromFileNameAndLink } from "@/lib/assistantIconUtils"; import { truncateString } from "@/lib/utils"; +import { FileResponse } from "../my-documents/DocumentsContext"; interface SourcesDisplayProps { documents: OnyxDocument[]; @@ -68,14 +69,21 @@ export const SourceCard: React.FC<{ }; export const FileSourceCard: React.FC<{ - document: FileDescriptor; - setPresentingDocument: (document: MinimalOnyxDocument) => void; + document: FileResponse; + setPresentingDocument: (document: FileResponse) => void; }> = ({ document, setPresentingDocument }) => { + const openDocument = () => { + if (document.link_url) { + window.open(document.link_url, "_blank"); + } else { + setPresentingDocument(document as any); + } + }; const fileName = document.name || document.id; return (
- {getFileIconFromFileName(fileName)} - + {getFileIconFromFileNameAndLink(document.name, document.link_url)}
- Document + {truncateString(document.name, 45)}
@@ -106,14 +113,19 @@ export const FileSourceCard: React.FC<{ }; export const FileSourceCardInResults: React.FC<{ - document: FileDescriptor; - setPresentingDocument: (document: MinimalOnyxDocument) => void; + document: FileResponse; + setPresentingDocument: (document: FileResponse) => void; }> = ({ document, setPresentingDocument }) => { - const fileName = document.name || document.id; - + const openDocument = () => { + if (document.link_url) { + window.open(document.link_url, "_blank"); + } else { + setPresentingDocument(document as any); + } + }; return ( diff --git a/web/src/app/chat/my-documents/DocumentsContext.tsx b/web/src/app/chat/my-documents/DocumentsContext.tsx index 90613dc3c..b84b59697 100644 --- a/web/src/app/chat/my-documents/DocumentsContext.tsx +++ b/web/src/app/chat/my-documents/DocumentsContext.tsx @@ -34,7 +34,10 @@ export type FileResponse = { assistant_ids?: number[]; indexed?: boolean; created_at?: string; + file_id?: string; file_type?: string; + link_url?: string | null; + failed?: boolean; }; export interface FileUploadResponse { @@ -43,6 +46,7 @@ export interface FileUploadResponse { export interface DocumentsContextType { folders: FolderResponse[]; + files: FileResponse[]; currentFolder: number | null; presentingDocument: MinimalOnyxDocument | null; searchQuery: string; @@ -486,6 +490,7 @@ export const DocumentsProvider: React.FC = ({ ); const value: DocumentsContextType = { + files: folders.map((folder) => folder.files).flat(), folders, currentFolder, presentingDocument, diff --git a/web/src/app/chat/my-documents/[id]/components/DocumentList.tsx b/web/src/app/chat/my-documents/[id]/components/DocumentList.tsx index 06c6040e7..bca130313 100644 --- a/web/src/app/chat/my-documents/[id]/components/DocumentList.tsx +++ b/web/src/app/chat/my-documents/[id]/components/DocumentList.tsx @@ -116,6 +116,13 @@ export const DocumentList: React.FC = ({ }) => { const [presentingDocument, setPresentingDocument] = useState(null); + const openDocument = (file: FileResponse) => { + if (file.link_url) { + window.open(file.link_url, "_blank"); + } else { + setPresentingDocument(file); + } + }; const [uploadingFiles, setUploadingFiles] = useState([]); const [completedFiles, setCompletedFiles] = useState([]); const [refreshInterval, setRefreshInterval] = useState( @@ -499,8 +506,9 @@ export const DocumentList: React.FC = ({ onDownload={onDownload} onMove={onMove} folders={folders} - onSelect={() => setPresentingDocument(file)} + onSelect={() => openDocument(file)} isIndexed={file.indexed || false} + failed={file.failed || false} /> )} diff --git a/web/src/app/chat/my-documents/[id]/components/upload/FileUploadSection.tsx b/web/src/app/chat/my-documents/[id]/components/upload/FileUploadSection.tsx index 3fb527f9e..befa49e9c 100644 --- a/web/src/app/chat/my-documents/[id]/components/upload/FileUploadSection.tsx +++ b/web/src/app/chat/my-documents/[id]/components/upload/FileUploadSection.tsx @@ -535,7 +535,7 @@ export const FileUploadSection: React.FC = ({ {/* Content area - different for each mode but with consistent spacing */}
-
+
Promise; folders: FolderResponse[]; isIndexed: boolean; + failed: boolean; } export const FileListItem: React.FC = ({ @@ -52,6 +62,7 @@ export const FileListItem: React.FC = ({ onMove, folders, isIndexed, + failed, }) => { const [showMoveOptions, setShowMoveOptions] = useState(false); const [indexingStatus, setIndexingStatus] = useState(null); @@ -82,6 +93,72 @@ export const FileListItem: React.FC = ({ onMove(file.id, targetFolderId); setShowMoveOptions(false); }; + const FailureWithPopover = () => { + return ( + + e.stopPropagation()} asChild> +
+ +
+
+ +
+
+

+ Indexing failed. +
+ You can attempt a reindex to continue using this file, or delete + the file. +

+
+
+ + +
+
+
+
+ ); + }; return (
= ({ {isSelected !== undefined && ( )} - {getFileIconFromFileName(file.name)} + {file.failed ? ( + + ) : ( + getFileIconFromFileNameAndLink(file.name, file.link_url) + )} {file.name.length > 50 ? ( diff --git a/web/src/app/chat/my-documents/components/FilePicker.tsx b/web/src/app/chat/my-documents/components/FilePicker.tsx index a76877028..a240067a9 100644 --- a/web/src/app/chat/my-documents/components/FilePicker.tsx +++ b/web/src/app/chat/my-documents/components/FilePicker.tsx @@ -53,7 +53,7 @@ import { getFormattedDateTime } from "@/lib/dateUtils"; import { FileUploadSection } from "../[id]/components/upload/FileUploadSection"; import { truncateString } from "@/lib/utils"; import { MinimalOnyxDocument } from "@/lib/search/interfaces"; -import { getFileIconFromFileName } from "@/lib/assistantIconUtils"; +import { getFileIconFromFileNameAndLink } from "@/lib/assistantIconUtils"; import { TokenDisplay } from "@/components/TokenDisplay"; // Define a type for uploading files that includes progress @@ -114,8 +114,6 @@ const DraggableItem: React.FC<{ {...listeners} className="flex group w-full items-center" > - {/* // className="flex group items-center" */} -
- {getFileIconFromFileName(file.name)} + {getFileIconFromFileNameAndLink(file.name, file.link_url)} {file.name.length > 34 ? ( @@ -460,10 +458,14 @@ export const FilePickerModal: React.FC = ({ } }; const handleFileClick = (file: FileResponse) => { - setPresentingDocument({ - document_id: file.document_id, - semantic_identifier: file.name, - }); + if (file.link_url) { + window.open(file.link_url, "_blank"); + } else { + setPresentingDocument({ + document_id: file.document_id, + semantic_identifier: file.name, + }); + } }; const handleFileSelect = ( diff --git a/web/src/app/chat/my-documents/components/SelectedItemsList.tsx b/web/src/app/chat/my-documents/components/SelectedItemsList.tsx index f2da20971..9256cbc3b 100644 --- a/web/src/app/chat/my-documents/components/SelectedItemsList.tsx +++ b/web/src/app/chat/my-documents/components/SelectedItemsList.tsx @@ -11,7 +11,7 @@ import { useDocumentsContext, } from "../DocumentsContext"; import { useDocumentSelection } from "../../useDocumentSelection"; -import { getFileIconFromFileName } from "@/lib/assistantIconUtils"; +import { getFileIconFromFileNameAndLink } from "@/lib/assistantIconUtils"; import { MinimalOnyxDocument } from "@/lib/search/interfaces"; import { UploadingFile } from "./FilePicker"; import { CircularProgress } from "../[id]/components/upload/CircularProgress"; @@ -35,10 +35,14 @@ export const SelectedItemsList: React.FC = ({ }) => { const hasItems = folders.length > 0 || files.length > 0; const openFile = (file: FileResponse) => { - setPresentingDocument({ - semantic_identifier: file.name, - document_id: file.document_id, - }); + if (file.link_url) { + window.open(file.link_url, "_blank"); + } else { + setPresentingDocument({ + semantic_identifier: file.name, + document_id: file.document_id, + }); + } }; return ( @@ -116,13 +120,12 @@ export const SelectedItemsList: React.FC = ({ onClick={() => openFile(file)} >
- {getFileIconFromFileName(file.name)} + {getFileIconFromFileNameAndLink(file.name, file.link_url)} {truncateString(file.name, 34)}
-