mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-23 08:56:42 +08:00
### What problem does this PR solve?
1. **List agents**
**Prev API**:
- `/v1/canvas/list GET`
- `/api/v1/agents GET`
**Current API**: `/api/v2/agents GET`
2. **Get canvas template**
**Prev API**: `/v1/canvas/templates GET`
**Current API**: `/api/v2/agents/templates GET`
3. **Delete an agent**
**Prev API**:
- `/v1/canvas/rm POST`
- `/api/v1/agents/<agent_id> DELETE`
**Current API**: `/api/v2/agents/<agent_id> DELETE`
4. **Update an agent**
**Prev API**:
- `/api/v1/agents/<agent_id> PUT`
- `/v1/canvas/setting POST `
**Current API**: `/api/v2/agents/<agent_id> PATCH`
5. **Create an agent**
**Prev API**:
- `/v1/canvas/set POST`
- `/api/v1/agents POST`
**Current API**: `/api/v2/agents POST`
6. **Get an agent**
**Prev API**:
- `/v1/canvas/get/<canvas_id> GET `
**Current API**: `/api/v2/agents/<agent_id> GET`
7. **Reset an agent**
**Prev API**:
- `/v1/canvas/reset POST`
**Current API**: `/api/v2/agents/<agent_id>/reset POST`
8. **Upload a file to an agent**
**Prev API**:
- `/v1/canvas/upload/<canvas_id> POST`
**Current API**: `/api/v2/agents/<agent_id>/upload POST`
9. **Input form**
**Prev API**:
- `/v1/canvas/input_form GET`
**Current API**:
`/api/v2/agents/<agent_id>/components/<component_id>/input-form GET`
10. **Debug an agent**
**Prev API**:
- `/v1/canvas/debug POST`
**Current API**:
`/api/v2/agents/<agent_id>/components/<component_id>/debug POST`
11. **Trace an agent**
**Prev API**:
- `/v1/canvas/trace GET`
**Current API**: `/api/v2/agents/<agent_id>/logs/<message_id> GET`
12. **Get an agent version list**
**Prev API**:
- `/v1/canvas/getlistversion/<canvas_id>`
**Current API**: `/api/v2/agents/<agent_id>/versions GET`
13. **Get a version of agent**
**Prev API**:
- `/v1/canvas/getversion/<version_id>`
**Current API**: `/api/v2/agents/<agent_id>/versions/<version_id> GET`
14. **Test db connection**
**Prev API**:
- `/v1/canvas/test_db_connect POST`
**Current API**: `/api/v2/agents/test_db_connection`
15. **Rerun the agent**
**Prev API**:
- `/v1/canvas/rerun POST`
**Current API**: `/api/v2/agents/rerun POST`
16. **Get prompts**
**Prev API**:
- `/v1/canvas/prompts GET`
**Current API**: `/api/v2/agents/prompts GET`
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: chanx <1243304602@qq.com>
144 lines
4.5 KiB
TypeScript
144 lines
4.5 KiB
TypeScript
'use client';
|
||
|
||
import {
|
||
FileUpload,
|
||
FileUploadDropzone,
|
||
FileUploadItem,
|
||
FileUploadItemDelete,
|
||
FileUploadItemMetadata,
|
||
FileUploadItemPreview,
|
||
FileUploadItemProgress,
|
||
FileUploadList,
|
||
FileUploadTrigger,
|
||
type FileUploadProps,
|
||
} from '@/components/file-upload';
|
||
import { Button } from '@/components/ui/button';
|
||
import { useUploadAgentFile } from '@/hooks/use-agent-request';
|
||
import { Upload, X } from 'lucide-react';
|
||
import * as React from 'react';
|
||
import { toast } from 'sonner';
|
||
|
||
type FileUploadDirectUploadProps = {
|
||
value: Record<string, any> | Record<string, any>[];
|
||
onChange(value: Record<string, any>[]): void;
|
||
maxFiles?: number;
|
||
};
|
||
|
||
export function FileUploadDirectUpload({
|
||
value,
|
||
onChange,
|
||
maxFiles,
|
||
}: FileUploadDirectUploadProps) {
|
||
const [files, setFiles] = React.useState<File[]>([]);
|
||
const uploadedFilesRef = React.useRef<Record<string, any>[]>(
|
||
Array.isArray(value) ? value : value ? [value] : [],
|
||
);
|
||
|
||
const { uploadAgentFile } = useUploadAgentFile();
|
||
|
||
const onUpload: NonNullable<FileUploadProps['onUpload']> = React.useCallback(
|
||
async (files, { onSuccess, onError }) => {
|
||
try {
|
||
const uploadPromises = files.map(async (file) => {
|
||
const handleError = (error?: any) => {
|
||
onError(
|
||
file,
|
||
error instanceof Error ? error : new Error('Upload failed'),
|
||
);
|
||
};
|
||
try {
|
||
const ret = await uploadAgentFile([file]);
|
||
if (ret.code === 0) {
|
||
onSuccess(file);
|
||
uploadedFilesRef.current = [
|
||
...uploadedFilesRef.current,
|
||
ret.data,
|
||
];
|
||
onChange(uploadedFilesRef.current);
|
||
} else {
|
||
handleError();
|
||
}
|
||
} catch (error) {
|
||
handleError(error);
|
||
}
|
||
});
|
||
|
||
// Wait for all uploads to complete
|
||
await Promise.all(uploadPromises);
|
||
} catch (error) {
|
||
// This handles any error that might occur outside the individual upload processes
|
||
console.error('Unexpected error during upload:', error);
|
||
}
|
||
},
|
||
[onChange, uploadAgentFile],
|
||
);
|
||
|
||
const onFileReject = React.useCallback((file: File, message: string) => {
|
||
toast(message, {
|
||
description: `"${file.name.length > 20 ? `${file.name.slice(0, 20)}...` : file.name}" has been rejected`,
|
||
});
|
||
}, []);
|
||
|
||
const handleFilesChange = React.useCallback(
|
||
(newFiles: File[]) => {
|
||
// Find removed files and update uploadedFilesRef
|
||
const removedFiles = files.filter((f) => !newFiles.includes(f));
|
||
if (removedFiles.length > 0) {
|
||
const removedIndices = removedFiles.map((f) => files.indexOf(f));
|
||
uploadedFilesRef.current = uploadedFilesRef.current.filter(
|
||
(_, idx) => !removedIndices.includes(idx),
|
||
);
|
||
onChange(uploadedFilesRef.current);
|
||
}
|
||
setFiles(newFiles);
|
||
},
|
||
[files, onChange],
|
||
);
|
||
|
||
return (
|
||
<FileUpload
|
||
value={files}
|
||
onValueChange={handleFilesChange}
|
||
onUpload={onUpload}
|
||
onFileReject={onFileReject}
|
||
// TODO: DEFALUT to 5 / 1 for params
|
||
maxFiles={(maxFiles ?? 5) as number}
|
||
className="w-full"
|
||
multiple={!maxFiles || !!(maxFiles && maxFiles > 1)}
|
||
>
|
||
<FileUploadDropzone>
|
||
<div className="flex flex-col items-center gap-1 text-center">
|
||
<div className="flex items-center justify-center rounded-full border p-2.5">
|
||
<Upload className="size-6 text-muted-foreground" />
|
||
</div>
|
||
<p className="font-medium text-sm">Drag & drop files here</p>
|
||
<p className="text-muted-foreground text-xs">
|
||
Or click to browse (max {(maxFiles ?? 5) as number} files)
|
||
</p>
|
||
</div>
|
||
<FileUploadTrigger asChild>
|
||
<Button variant="outline" size="sm" className="mt-2 w-fit">
|
||
Browse files
|
||
</Button>
|
||
</FileUploadTrigger>
|
||
</FileUploadDropzone>
|
||
<FileUploadList>
|
||
{files.map((file, index) => (
|
||
<FileUploadItem key={index} value={file} className="flex-col">
|
||
<div className="flex w-full items-center gap-2">
|
||
<FileUploadItemPreview />
|
||
<FileUploadItemMetadata />
|
||
<FileUploadItemDelete asChild>
|
||
<Button variant="ghost" size="icon" className="size-7">
|
||
<X />
|
||
</Button>
|
||
</FileUploadItemDelete>
|
||
</div>
|
||
<FileUploadItemProgress />
|
||
</FileUploadItem>
|
||
))}
|
||
</FileUploadList>
|
||
</FileUpload>
|
||
);
|
||
}
|