mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-22 11:20:15 +01:00
🤖 Assistants V2 Support: Part 2
🎹 fix: Autocompletion Chrome Bug on Action API Key Input
chore: remove `useOriginNavigate`
chore: set correct OpenAI Storage Source
fix: azure file deletions, instantiate clients by source for deletion
update code interpret files info
feat: deleteResourceFileId
chore: increase poll interval as azure easily rate limits
fix: openai file deletions, TODO: evaluate rejected deletion settled promises to determine which to delete from db records
file source icons
update table file filters
chore: file search info and versioning
fix: retrieval update with necessary tool_resources if specified
fix(useMentions): add optional chaining in case listMap value is undefined
fix: force assistant avatar roundedness
fix: azure assistants, check correct flag
chore: bump data-provider
This commit is contained in:
parent
2bdbff5141
commit
bc46ccdcad
44 changed files with 420 additions and 174 deletions
|
|
@ -2,6 +2,7 @@ import type { TFile } from 'librechat-data-provider';
|
|||
import type { ExtendedFile } from '~/common';
|
||||
import FileIcon from '~/components/svg/Files/FileIcon';
|
||||
import ProgressCircle from './ProgressCircle';
|
||||
import SourceIcon from './SourceIcon';
|
||||
import { useProgress } from '~/hooks';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
|
|
@ -21,7 +22,6 @@ const FilePreview = ({
|
|||
const radius = 55; // Radius of the SVG circle
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
const progress = useProgress(file?.['progress'] ?? 1, 0.001, (file as ExtendedFile)?.size ?? 1);
|
||||
console.log(progress);
|
||||
|
||||
// Calculate the offset based on the loading progress
|
||||
const offset = circumference - progress * circumference;
|
||||
|
|
@ -32,6 +32,7 @@ const FilePreview = ({
|
|||
return (
|
||||
<div className={cn('h-10 w-10 shrink-0 overflow-hidden rounded-md', className)}>
|
||||
<FileIcon file={file} fileType={fileType} />
|
||||
<SourceIcon source={file?.source} />
|
||||
{progress < 1 && (
|
||||
<ProgressCircle
|
||||
circumference={circumference}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useEffect } from 'react';
|
||||
import { EToolResources } from 'librechat-data-provider';
|
||||
import type { ExtendedFile } from '~/common';
|
||||
import { useDeleteFilesMutation } from '~/data-provider';
|
||||
import { useFileDeletion } from '~/hooks/Files';
|
||||
|
|
@ -10,6 +11,7 @@ export default function FileRow({
|
|||
setFiles,
|
||||
setFilesLoading,
|
||||
assistant_id,
|
||||
tool_resource,
|
||||
fileFilter,
|
||||
Wrapper,
|
||||
}: {
|
||||
|
|
@ -18,6 +20,7 @@ export default function FileRow({
|
|||
setFilesLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
fileFilter?: (file: ExtendedFile) => boolean;
|
||||
assistant_id?: string;
|
||||
tool_resource?: EToolResources;
|
||||
Wrapper?: React.FC<{ children: React.ReactNode }>;
|
||||
}) {
|
||||
const files = Array.from(_files.values()).filter((file) =>
|
||||
|
|
@ -25,7 +28,8 @@ export default function FileRow({
|
|||
);
|
||||
|
||||
const { mutateAsync } = useDeleteFilesMutation({
|
||||
onMutate: async () => console.log('Deleting files: assistant_id', assistant_id),
|
||||
onMutate: async () =>
|
||||
console.log('Deleting files: assistant_id, tool_resource', assistant_id, tool_resource),
|
||||
onSuccess: () => {
|
||||
console.log('Files deleted');
|
||||
},
|
||||
|
|
@ -34,7 +38,7 @@ export default function FileRow({
|
|||
},
|
||||
});
|
||||
|
||||
const { deleteFile } = useFileDeletion({ mutateAsync, assistant_id });
|
||||
const { deleteFile } = useFileDeletion({ mutateAsync, assistant_id, tool_resource });
|
||||
|
||||
useEffect(() => {
|
||||
if (!files) {
|
||||
|
|
@ -82,6 +86,7 @@ export default function FileRow({
|
|||
url={file.preview}
|
||||
onDelete={handleDelete}
|
||||
progress={file.progress}
|
||||
source={file.source}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,16 +12,9 @@ export default function Files({ open, onOpenChange }) {
|
|||
const { data: files = [] } = useGetFiles<TFile[]>({
|
||||
select: (files) =>
|
||||
files.map((file) => {
|
||||
if (file.source === FileSources.local || file.source === FileSources.openai) {
|
||||
file.context = file.context ?? FileContext.unknown;
|
||||
return file;
|
||||
} else {
|
||||
return {
|
||||
...file,
|
||||
context: file.context ?? FileContext.unknown,
|
||||
source: FileSources.local,
|
||||
};
|
||||
}
|
||||
file.context = file.context ?? FileContext.unknown;
|
||||
file.filterSource = file.source === FileSources.firebase ? FileSources.local : file.source;
|
||||
return file;
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { FileSources } from 'librechat-data-provider';
|
||||
import ImagePreview from './ImagePreview';
|
||||
import RemoveFile from './RemoveFile';
|
||||
|
||||
|
|
@ -6,16 +7,18 @@ const Image = ({
|
|||
url,
|
||||
onDelete,
|
||||
progress = 1,
|
||||
source = FileSources.local,
|
||||
}: {
|
||||
imageBase64?: string;
|
||||
url?: string;
|
||||
onDelete: () => void;
|
||||
progress: number; // between 0 and 1
|
||||
source?: FileSources;
|
||||
}) => {
|
||||
return (
|
||||
<div className="group relative inline-block text-sm text-black/70 dark:text-white/90">
|
||||
<div className="relative overflow-hidden rounded-xl border border-gray-200 dark:border-gray-600">
|
||||
<ImagePreview imageBase64={imageBase64} url={url} progress={progress} />
|
||||
<ImagePreview source={source} imageBase64={imageBase64} url={url} progress={progress} />
|
||||
</div>
|
||||
<RemoveFile onRemove={onDelete} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { FileSources } from 'librechat-data-provider';
|
||||
import ProgressCircle from './ProgressCircle';
|
||||
import SourceIcon from './SourceIcon';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
type styleProps = {
|
||||
|
|
@ -13,11 +15,13 @@ const ImagePreview = ({
|
|||
url,
|
||||
progress = 1,
|
||||
className = '',
|
||||
source,
|
||||
}: {
|
||||
imageBase64?: string;
|
||||
url?: string;
|
||||
progress?: number; // between 0 and 1
|
||||
className?: string;
|
||||
source?: FileSources;
|
||||
}) => {
|
||||
let style: styleProps = {
|
||||
backgroundSize: 'cover',
|
||||
|
|
@ -65,6 +69,7 @@ const ImagePreview = ({
|
|||
circleCSSProperties={circleCSSProperties}
|
||||
/>
|
||||
)}
|
||||
<SourceIcon source={source} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
45
client/src/components/Chat/Input/Files/SourceIcon.tsx
Normal file
45
client/src/components/Chat/Input/Files/SourceIcon.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { EModelEndpoint, FileSources } from 'librechat-data-provider';
|
||||
import { MinimalIcon } from '~/components/Endpoints';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const sourceToEndpoint = {
|
||||
[FileSources.openai]: EModelEndpoint.openAI,
|
||||
[FileSources.azure]: EModelEndpoint.azureOpenAI,
|
||||
};
|
||||
const sourceToClassname = {
|
||||
[FileSources.openai]: 'bg-black/65',
|
||||
[FileSources.azure]: 'azure-bg-color opacity-85',
|
||||
};
|
||||
|
||||
const defaultClassName =
|
||||
'absolute right-0 bottom-0 rounded-full p-[0.15rem] text-gray-600 transition-colors';
|
||||
|
||||
export default function SourceIcon({
|
||||
source,
|
||||
className = defaultClassName,
|
||||
}: {
|
||||
source?: FileSources;
|
||||
className?: string;
|
||||
}) {
|
||||
if (source === FileSources.local || source === FileSources.firebase) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const endpoint = sourceToEndpoint[source ?? ''];
|
||||
|
||||
if (!endpoint) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<button type="button" className={cn(className, sourceToClassname[source ?? ''] ?? '')}>
|
||||
<span className="flex items-center justify-center">
|
||||
<MinimalIcon
|
||||
endpoint={endpoint}
|
||||
size={14}
|
||||
isCreatedByUser={false}
|
||||
iconClassName="h-3 w-3"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import ImagePreview from '~/components/Chat/Input/Files/ImagePreview';
|
|||
import FilePreview from '~/components/Chat/Input/Files/FilePreview';
|
||||
import { SortFilterHeader } from './SortFilterHeader';
|
||||
import { OpenAIMinimalIcon } from '~/components/svg';
|
||||
import { AzureMinimalIcon } from '~/components/svg';
|
||||
import { Button, Checkbox } from '~/components/ui';
|
||||
import { formatDate, getFileType } from '~/utils';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
|
|
@ -71,10 +72,11 @@ export const columns: ColumnDef<TFile>[] = [
|
|||
const file = row.original;
|
||||
if (file.type?.startsWith('image')) {
|
||||
return (
|
||||
<div className="flex gap-2 ">
|
||||
<div className="flex gap-2">
|
||||
<ImagePreview
|
||||
url={file.filepath}
|
||||
className="h-10 w-10 shrink-0 overflow-hidden rounded-md"
|
||||
className="relative h-10 w-10 shrink-0 overflow-hidden rounded-md"
|
||||
source={file?.source}
|
||||
/>
|
||||
<span className="self-center truncate ">{file.filename}</span>
|
||||
</div>
|
||||
|
|
@ -84,7 +86,7 @@ export const columns: ColumnDef<TFile>[] = [
|
|||
const fileType = getFileType(file.type);
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
{fileType && <FilePreview fileType={fileType} />}
|
||||
{fileType && <FilePreview fileType={fileType} className="relative" file={file} />}
|
||||
<span className="self-center truncate">{file.filename}</span>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -108,7 +110,7 @@ export const columns: ColumnDef<TFile>[] = [
|
|||
cell: ({ row }) => formatDate(row.original.updatedAt),
|
||||
},
|
||||
{
|
||||
accessorKey: 'source',
|
||||
accessorKey: 'filterSource',
|
||||
header: ({ column }) => {
|
||||
const localize = useLocalize();
|
||||
return (
|
||||
|
|
@ -117,10 +119,14 @@ export const columns: ColumnDef<TFile>[] = [
|
|||
title={localize('com_ui_storage')}
|
||||
filters={{
|
||||
Storage: Object.values(FileSources).filter(
|
||||
(value) => value === FileSources.local || value === FileSources.openai,
|
||||
(value) =>
|
||||
value === FileSources.local ||
|
||||
value === FileSources.openai ||
|
||||
value === FileSources.azure,
|
||||
),
|
||||
}}
|
||||
valueMap={{
|
||||
[FileSources.azure]: 'Azure',
|
||||
[FileSources.openai]: 'OpenAI',
|
||||
[FileSources.local]: 'com_ui_host',
|
||||
}}
|
||||
|
|
@ -137,6 +143,13 @@ export const columns: ColumnDef<TFile>[] = [
|
|||
{'OpenAI'}
|
||||
</div>
|
||||
);
|
||||
} else if (source === FileSources.azure) {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<AzureMinimalIcon className="icon-sm text-cyan-700" />
|
||||
{'Azure'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
|
|
|
|||
|
|
@ -48,7 +48,12 @@ const contextMap = {
|
|||
[FileContext.bytes]: 'com_ui_size',
|
||||
};
|
||||
|
||||
type Style = { width?: number | string; maxWidth?: number | string; minWidth?: number | string };
|
||||
type Style = {
|
||||
width?: number | string;
|
||||
maxWidth?: number | string;
|
||||
minWidth?: number | string;
|
||||
zIndex?: number;
|
||||
};
|
||||
|
||||
export default function DataTable<TData, TValue>({ columns, data }: DataTableProps<TData, TValue>) {
|
||||
const localize = useLocalize();
|
||||
|
|
@ -142,7 +147,7 @@ export default function DataTable<TData, TValue>({ columns, data }: DataTablePro
|
|||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header, index) => {
|
||||
const style: Style = { maxWidth: '32px', minWidth: '125px' };
|
||||
const style: Style = { maxWidth: '32px', minWidth: '125px', zIndex: 50 };
|
||||
if (header.id === 'filename') {
|
||||
style.maxWidth = '50%';
|
||||
style.width = '50%';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue