Spaces:
Runtime error
Runtime error
| <script lang="ts"> | |
| import { getContext, onMount } from "svelte"; | |
| import { config, knowledge, settings, user } from "$lib/stores"; | |
| import KnowledgeSelector from "./Knowledge/KnowledgeSelector.svelte"; | |
| import FileItem from "$lib/components/common/FileItem.svelte"; | |
| import { getKnowledgeBases } from "$lib/apis/knowledge"; | |
| import { uploadFile } from "$lib/apis/files"; | |
| import { toast } from "svelte-sonner"; | |
| import { v4 as uuidv4 } from "uuid"; | |
| import { REXPRO_API_BASE_URL } from "$lib/constants"; | |
| export let selectedItems = []; | |
| const i18n = getContext("i18n"); | |
| let loaded = false; | |
| let filesInputElement = null; | |
| let inputFiles = null; | |
| $: if (selectedItems === null) { | |
| selectedItems = []; | |
| } | |
| const uploadFileHandler = async (file, fullContext: boolean = false) => { | |
| if ( | |
| $user?.role !== "admin" && | |
| !($user?.permissions?.chat?.file_upload ?? true) | |
| ) { | |
| toast.error($i18n.t("You do not have permission to upload files.")); | |
| return null; | |
| } | |
| const tempItemId = uuidv4(); | |
| const fileItem = { | |
| type: "file", | |
| file: "", | |
| id: null, | |
| url: "", | |
| name: file.name, | |
| collection_name: "", | |
| status: "uploading", | |
| size: file.size, | |
| error: "", | |
| itemId: tempItemId, | |
| ...(fullContext ? { context: "full" } : {}), | |
| }; | |
| if (fileItem.size == 0) { | |
| toast.error($i18n.t("You cannot upload an empty file.")); | |
| return null; | |
| } | |
| selectedItems = [...selectedItems, fileItem]; | |
| try { | |
| // If the file is an audio file, provide the language for STT. | |
| let metadata = null; | |
| if ( | |
| (file.type.startsWith("audio/") || | |
| file.type.startsWith("video/")) && | |
| $settings?.audio?.stt?.language | |
| ) { | |
| metadata = { | |
| language: $settings?.audio?.stt?.language, | |
| }; | |
| } | |
| // During the file upload, file content is automatically extracted. | |
| const uploadedFile = await uploadFile( | |
| localStorage.token, | |
| file, | |
| metadata, | |
| ); | |
| if (uploadedFile) { | |
| console.log("File upload completed:", { | |
| id: uploadedFile.id, | |
| name: fileItem.name, | |
| collection: uploadedFile?.meta?.collection_name, | |
| }); | |
| if (uploadedFile.error) { | |
| console.warn("File upload warning:", uploadedFile.error); | |
| toast.warning(uploadedFile.error); | |
| } | |
| fileItem.status = "uploaded"; | |
| fileItem.file = uploadedFile; | |
| fileItem.id = uploadedFile.id; | |
| fileItem.collection_name = | |
| uploadedFile?.meta?.collection_name || | |
| uploadedFile?.collection_name; | |
| fileItem.url = `${uploadedFile.id}`; | |
| selectedItems = selectedItems; | |
| } else { | |
| selectedItems = selectedItems.filter( | |
| (item) => item?.itemId !== tempItemId, | |
| ); | |
| } | |
| } catch (e) { | |
| toast.error(`${e}`); | |
| selectedItems = selectedItems.filter( | |
| (item) => item?.itemId !== tempItemId, | |
| ); | |
| } | |
| }; | |
| const inputFilesHandler = async (inputFiles) => { | |
| console.log("Input files handler called with:", inputFiles); | |
| inputFiles.forEach(async (file) => { | |
| console.log("Processing file:", { | |
| name: file.name, | |
| type: file.type, | |
| size: file.size, | |
| extension: file.name.split(".").at(-1), | |
| }); | |
| if ( | |
| ($config?.file?.max_size ?? null) !== null && | |
| file.size > ($config?.file?.max_size ?? 0) * 1024 * 1024 | |
| ) { | |
| console.log("File exceeds max size limit:", { | |
| fileSize: file.size, | |
| maxSize: ($config?.file?.max_size ?? 0) * 1024 * 1024, | |
| }); | |
| toast.error( | |
| $i18n.t(`File size should not exceed {{maxSize}} MB.`, { | |
| maxSize: $config?.file?.max_size, | |
| }), | |
| ); | |
| return; | |
| } | |
| if (!file["type"].startsWith("image/")) { | |
| uploadFileHandler(file); | |
| } else { | |
| toast.error($i18n.t(`Unsupported file type.`)); | |
| } | |
| }); | |
| }; | |
| onMount(async () => { | |
| loaded = true; | |
| }); | |
| </script> | |
| <input | |
| bind:this={filesInputElement} | |
| bind:files={inputFiles} | |
| type="file" | |
| hidden | |
| multiple | |
| on:change={async () => { | |
| if (inputFiles && inputFiles.length > 0) { | |
| const _inputFiles = Array.from(inputFiles); | |
| inputFilesHandler(_inputFiles); | |
| } else { | |
| toast.error($i18n.t(`File not found.`)); | |
| } | |
| filesInputElement.value = ""; | |
| }} | |
| /> | |
| <div> | |
| <slot name="label"> | |
| <div class="mb-2"> | |
| <div class="flex w-full justify-between mb-1"> | |
| <div class=" self-center text-xs font-medium text-gray-500"> | |
| {$i18n.t("Knowledge")} | |
| </div> | |
| </div> | |
| </div> | |
| </slot> | |
| <div class="flex flex-col mb-1"> | |
| {#if selectedItems?.length > 0} | |
| <div class=" flex flex-wrap items-center gap-2 mb-2.5"> | |
| {#each selectedItems as file, fileIdx} | |
| <FileItem | |
| {file} | |
| small={true} | |
| item={file} | |
| name={file.name} | |
| modal={true} | |
| edit={true} | |
| loading={file.status === "uploading"} | |
| type={file?.legacy | |
| ? `Legacy${file.type ? ` ${file.type}` : ""}` | |
| : (file?.type ?? "collection")} | |
| dismissible | |
| on:dismiss={(e) => { | |
| selectedItems = selectedItems.filter( | |
| (_, idx) => idx !== fileIdx, | |
| ); | |
| }} | |
| /> | |
| {/each} | |
| </div> | |
| {/if} | |
| {#if loaded} | |
| <div class="flex flex-wrap flex-row text-sm gap-1"> | |
| <KnowledgeSelector | |
| on:select={(e) => { | |
| const item = e.detail; | |
| if (!selectedItems.find((k) => k.id === item.id)) { | |
| selectedItems = [ | |
| ...selectedItems, | |
| { | |
| ...item, | |
| }, | |
| ]; | |
| } | |
| }} | |
| > | |
| <div | |
| class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-100 dark:outline-gray-850 rounded-3xl" | |
| > | |
| {$i18n.t("Select Knowledge")} | |
| </div> | |
| </KnowledgeSelector> | |
| {#if $user?.role === "admin" || $user?.permissions?.chat?.file_upload} | |
| <button | |
| class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-100 dark:outline-gray-850 rounded-3xl" | |
| type="button" | |
| on:click={() => { | |
| filesInputElement.click(); | |
| }}>{$i18n.t("Upload Files")}</button | |
| > | |
| {/if} | |
| </div> | |
| {/if} | |
| <!-- {knowledge} --> | |
| </div> | |
| <div class=" text-xs dark:text-gray-700"> | |
| {$i18n.t( | |
| 'To attach knowledge base here, add them to the "Knowledge" workspace first.', | |
| )} | |
| </div> | |
| </div> | |