mirror of
https://github.com/open-webui/open-webui
synced 2025-03-23 06:17:24 +00:00
refac
This commit is contained in:
parent
6747478f67
commit
b862dff185
@ -785,10 +785,7 @@ def process_file(
|
||||
"content": text_content,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=e,
|
||||
)
|
||||
raise e
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
if "No pandoc was found" in str(e):
|
||||
@ -799,7 +796,7 @@ def process_file(
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ERROR_MESSAGES.DEFAULT(e),
|
||||
detail=str(e),
|
||||
)
|
||||
|
||||
|
||||
|
@ -9,8 +9,7 @@
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let className = 'w-60';
|
||||
export let colorClassName =
|
||||
'bg-white dark:bg-gray-850 border border-gray-50 dark:border-gray-850';
|
||||
export let colorClassName = 'bg-white dark:bg-gray-850 border border-gray-50 dark:border-white/5';
|
||||
export let url: string | null = null;
|
||||
|
||||
export let dismissible = false;
|
||||
@ -31,7 +30,7 @@
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="relative group p-1 {className} flex items-center {colorClassName} rounded-2xl text-left"
|
||||
class="relative group p-1.5 {className} flex items-center {colorClassName} rounded-2xl text-left"
|
||||
type="button"
|
||||
on:click={async () => {
|
||||
if (file?.file?.content) {
|
||||
@ -106,7 +105,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-center -space-y-0.5 px-2 w-full">
|
||||
<div class="flex flex-col justify-center -space-y-0.5 px-2.5 w-full">
|
||||
<div class=" dark:text-gray-100 text-sm font-medium line-clamp-1 mb-1">
|
||||
{name}
|
||||
</div>
|
||||
@ -128,16 +127,34 @@
|
||||
</div>
|
||||
|
||||
{#if dismissible}
|
||||
<div class=" pr-2">
|
||||
<div class=" absolute -top-2 -right-2">
|
||||
<button
|
||||
class=" px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl group-hover:visible invisible transition"
|
||||
class=" bg-gray-400 text-white border border-white rounded-full group-hover:visible invisible transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
dispatch('dismiss');
|
||||
}}
|
||||
>
|
||||
<GarbageBin />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- <button
|
||||
class=" p-1 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-full group-hover:visible invisible transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
}}
|
||||
>
|
||||
<GarbageBin />
|
||||
</button> -->
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
|
19
src/lib/components/icons/BarsArrowUp.svelte
Normal file
19
src/lib/components/icons/BarsArrowUp.svelte
Normal file
@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
export let className = 'size-4';
|
||||
export let strokeWidth = '1.5';
|
||||
</script>
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width={strokeWidth}
|
||||
stroke="currentColor"
|
||||
class={className}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12"
|
||||
/>
|
||||
</svg>
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { onMount, getContext, onDestroy } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
@ -14,12 +14,13 @@
|
||||
import Spinner from '$lib/components/common/Spinner.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import Badge from '$lib/components/common/Badge.svelte';
|
||||
import Files from './Files.svelte';
|
||||
import Files from './Collection/Files.svelte';
|
||||
import AddFilesPlaceholder from '$lib/components/AddFilesPlaceholder.svelte';
|
||||
import AddContentModal from './AddContentModal.svelte';
|
||||
import AddContentModal from './Collection/AddTextContentModal.svelte';
|
||||
import { transcribeAudio } from '$lib/apis/audio';
|
||||
import { blobToFile } from '$lib/utils';
|
||||
import { processFile } from '$lib/apis/retrieval';
|
||||
import AddContentMenu from './Collection/AddContentMenu.svelte';
|
||||
|
||||
let largeScreen = true;
|
||||
|
||||
@ -40,30 +41,9 @@
|
||||
let selectedFileId = null;
|
||||
|
||||
let debounceTimeout = null;
|
||||
let mediaQuery;
|
||||
let dragged = false;
|
||||
|
||||
let showAddContentModal = false;
|
||||
|
||||
const changeDebounceHandler = () => {
|
||||
console.log('debounce');
|
||||
if (debounceTimeout) {
|
||||
clearTimeout(debounceTimeout);
|
||||
}
|
||||
|
||||
debounceTimeout = setTimeout(async () => {
|
||||
const res = await updateKnowledgeById(localStorage.token, id, {
|
||||
name: knowledge.name,
|
||||
description: knowledge.description
|
||||
}).catch((e) => {
|
||||
toast.error(e);
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success($i18n.t('Knowledge updated successfully'));
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const uploadFileHandler = async (file) => {
|
||||
console.log(file);
|
||||
|
||||
@ -87,28 +67,8 @@
|
||||
});
|
||||
|
||||
if (uploadedFile) {
|
||||
const processedFile = await processFile(localStorage.token, uploadedFile.id, id).catch(
|
||||
(e) => {
|
||||
toast.error(e);
|
||||
}
|
||||
);
|
||||
|
||||
if (processedFile.status) {
|
||||
knowledge.data.file_ids = [...(knowledge.data.file_ids ?? []), uploadedFile.id];
|
||||
|
||||
const updatedKnowledge = await updateKnowledgeById(localStorage.token, id, {
|
||||
data: knowledge.data
|
||||
}).catch((e) => {
|
||||
toast.error(e);
|
||||
});
|
||||
|
||||
if (updatedKnowledge) {
|
||||
knowledge = updatedKnowledge;
|
||||
toast.success($i18n.t('File added successfully.'));
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t('Failed to process file.'));
|
||||
}
|
||||
console.log(uploadedFile);
|
||||
processFileHandler(uploadedFile);
|
||||
} else {
|
||||
toast.error($i18n.t('Failed to upload file.'));
|
||||
}
|
||||
@ -117,17 +77,95 @@
|
||||
}
|
||||
};
|
||||
|
||||
const processFileHandler = async (uploadedFile) => {
|
||||
const processedFile = await processFile(localStorage.token, uploadedFile.id, id).catch((e) => {
|
||||
toast.error(e);
|
||||
});
|
||||
|
||||
if (processedFile.status) {
|
||||
console.log(processedFile);
|
||||
|
||||
if (!knowledge.data) {
|
||||
knowledge.data = {};
|
||||
}
|
||||
|
||||
knowledge.data.file_ids = [...(knowledge?.data?.file_ids ?? []), uploadedFile.id];
|
||||
|
||||
console.log(knowledge);
|
||||
|
||||
const updatedKnowledge = await updateKnowledgeById(localStorage.token, id, {
|
||||
data: knowledge?.data ?? {}
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
|
||||
if (updatedKnowledge) {
|
||||
knowledge = updatedKnowledge;
|
||||
toast.success($i18n.t('File added successfully.'));
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t('Failed to process file.'));
|
||||
}
|
||||
};
|
||||
|
||||
const changeDebounceHandler = () => {
|
||||
console.log('debounce');
|
||||
if (debounceTimeout) {
|
||||
clearTimeout(debounceTimeout);
|
||||
}
|
||||
|
||||
debounceTimeout = setTimeout(async () => {
|
||||
const res = await updateKnowledgeById(localStorage.token, id, {
|
||||
name: knowledge.name,
|
||||
description: knowledge.description
|
||||
}).catch((e) => {
|
||||
toast.error(e);
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success($i18n.t('Knowledge updated successfully'));
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleMediaQuery = async (e) => {
|
||||
if (e.matches) {
|
||||
largeScreen = true;
|
||||
} else {
|
||||
largeScreen = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onDragOver = (e) => {
|
||||
e.preventDefault();
|
||||
dragged = true;
|
||||
};
|
||||
|
||||
const onDragLeave = () => {
|
||||
dragged = false;
|
||||
};
|
||||
|
||||
const onDrop = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (e.dataTransfer?.files) {
|
||||
const inputFiles = e.dataTransfer?.files;
|
||||
|
||||
if (inputFiles && inputFiles.length > 0) {
|
||||
for (const file of inputFiles) {
|
||||
await uploadFileHandler(file);
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t(`File not found.`));
|
||||
}
|
||||
}
|
||||
|
||||
dragged = false;
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
// listen to resize 1024px
|
||||
const mediaQuery = window.matchMedia('(min-width: 1024px)');
|
||||
|
||||
const handleMediaQuery = async (e) => {
|
||||
if (e.matches) {
|
||||
largeScreen = true;
|
||||
} else {
|
||||
largeScreen = false;
|
||||
}
|
||||
};
|
||||
mediaQuery = window.matchMedia('(min-width: 1024px)');
|
||||
|
||||
mediaQuery.addEventListener('change', handleMediaQuery);
|
||||
handleMediaQuery(mediaQuery);
|
||||
@ -146,45 +184,17 @@
|
||||
}
|
||||
|
||||
const dropZone = document.querySelector('body');
|
||||
|
||||
const onDragOver = (e) => {
|
||||
e.preventDefault();
|
||||
dragged = true;
|
||||
};
|
||||
|
||||
const onDragLeave = () => {
|
||||
dragged = false;
|
||||
};
|
||||
|
||||
const onDrop = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (e.dataTransfer?.files) {
|
||||
const inputFiles = e.dataTransfer?.files;
|
||||
|
||||
if (inputFiles && inputFiles.length > 0) {
|
||||
for (const file of inputFiles) {
|
||||
await uploadFileHandler(file);
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t(`File not found.`));
|
||||
}
|
||||
}
|
||||
|
||||
dragged = false;
|
||||
};
|
||||
|
||||
dropZone?.addEventListener('dragover', onDragOver);
|
||||
dropZone?.addEventListener('drop', onDrop);
|
||||
dropZone?.addEventListener('dragleave', onDragLeave);
|
||||
});
|
||||
|
||||
return () => {
|
||||
mediaQuery.removeEventListener('change', handleMediaQuery);
|
||||
|
||||
dropZone?.removeEventListener('dragover', onDragOver);
|
||||
dropZone?.removeEventListener('drop', onDrop);
|
||||
dropZone?.removeEventListener('dragleave', onDragLeave);
|
||||
};
|
||||
onDestroy(() => {
|
||||
mediaQuery?.removeEventListener('change', handleMediaQuery);
|
||||
const dropZone = document.querySelector('body');
|
||||
dropZone?.removeEventListener('dragover', onDragOver);
|
||||
dropZone?.removeEventListener('drop', onDrop);
|
||||
dropZone?.removeEventListener('dragleave', onDragLeave);
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -211,13 +221,6 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<AddContentModal
|
||||
bind:show={showAddContentModal}
|
||||
on:add={(e) => {
|
||||
console.log(e);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="flex flex-col w-full max-h-[100dvh] h-full">
|
||||
<button
|
||||
class="flex space-x-1"
|
||||
@ -282,57 +285,44 @@
|
||||
<div
|
||||
class=" {largeScreen
|
||||
? 'flex-shrink-0'
|
||||
: 'flex-1'} p-2.5 w-80 rounded-2xl border border-gray-50 dark:border-gray-850"
|
||||
: 'flex-1'} flex py-2.5 w-80 rounded-2xl border border-gray-50 dark:border-gray-850"
|
||||
>
|
||||
<div class=" flex flex-col w-full space-x-2 rounded-lg h-full">
|
||||
<div class="flex px-1">
|
||||
<div class=" self-center ml-1 mr-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
|
||||
bind:value={query}
|
||||
placeholder={$i18n.t('Search Collection')}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Tooltip content={$i18n.t('Add Content')}>
|
||||
<button
|
||||
class=" px-2 py-2 rounded-xl border border-gray-100 dark:border-gray-600 dark:border-0 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 transition font-medium text-sm flex items-center space-x-1"
|
||||
on:click={() => {
|
||||
showAddContentModal = true;
|
||||
}}
|
||||
>
|
||||
<div class="w-full h-full flex flex-col">
|
||||
<div class=" px-3">
|
||||
<div class="flex">
|
||||
<div class=" self-center ml-1 mr-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
|
||||
fill-rule="evenodd"
|
||||
d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-2 border-gray-50 dark:border-gray-850" />
|
||||
</div>
|
||||
<input
|
||||
class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
|
||||
bind:value={query}
|
||||
placeholder={$i18n.t('Search Collection')}
|
||||
/>
|
||||
|
||||
<div class="w-full h-full flex">
|
||||
{#if (knowledge?.data?.file_ids ?? []).length > 0}
|
||||
<Files files={knowledge.files} />
|
||||
<div>
|
||||
<AddContentMenu />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class=" mt-2 mb-1 border-gray-50 dark:border-gray-850" />
|
||||
</div>
|
||||
|
||||
{#if (knowledge?.files ?? []).length > 0}
|
||||
<div class=" flex overflow-y-auto h-full w-full scrollbar-hidden text-xs">
|
||||
<Files files={knowledge.files} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="m-auto text-gray-500 text-xs">No content found</div>
|
||||
{/if}
|
@ -0,0 +1,86 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu } from 'bits-ui';
|
||||
import { flyAndScale } from '$lib/utils/transitions';
|
||||
import { getContext, createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import Dropdown from '$lib/components/common/Dropdown.svelte';
|
||||
import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
|
||||
import Pencil from '$lib/components/icons/Pencil.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import Tags from '$lib/components/chat/Tags.svelte';
|
||||
import Share from '$lib/components/icons/Share.svelte';
|
||||
import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
|
||||
import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
|
||||
import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
|
||||
import ArrowUpCircle from '$lib/components/icons/ArrowUpCircle.svelte';
|
||||
import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
|
||||
import BarsArrowUp from '$lib/components/icons/BarsArrowUp.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let onClose: Function = () => {};
|
||||
|
||||
let show = false;
|
||||
</script>
|
||||
|
||||
<Dropdown
|
||||
bind:show
|
||||
on:change={(e) => {
|
||||
if (e.detail === false) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
align="end"
|
||||
>
|
||||
<Tooltip content={$i18n.t('Add Content')}>
|
||||
<button
|
||||
class=" px-2 py-2 rounded-xl border border-gray-100 dark:border-gray-600 dark:border-0 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 transition font-medium text-sm flex items-center space-x-1"
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
show = true;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<div slot="content">
|
||||
<DropdownMenu.Content
|
||||
class="w-full max-w-44 rounded-xl p-1 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
|
||||
sideOffset={4}
|
||||
side="bottom"
|
||||
align="end"
|
||||
transition={flyAndScale}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
dispatch('files');
|
||||
}}
|
||||
>
|
||||
<ArrowUpCircle strokeWidth="2" />
|
||||
<div class="flex items-center">{$i18n.t('Upload files')}</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
dispatch('text');
|
||||
}}
|
||||
>
|
||||
<BarsArrowUp strokeWidth="2" />
|
||||
<div class="flex items-center">{$i18n.t('Add text content')}</div>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</div>
|
||||
</Dropdown>
|
@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import Knowledge from '$lib/components/workspace/Knowledge/Item.svelte';
|
||||
import Collection from '$lib/components/workspace/Knowledge/Collection.svelte';
|
||||
</script>
|
||||
|
||||
<Knowledge />
|
||||
<Collection />
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import CreateKnowledge from '$lib/components/workspace/Knowledge/CreateKnowledge.svelte';
|
||||
import CreateCollection from '$lib/components/workspace/Knowledge/CreateCollection.svelte';
|
||||
</script>
|
||||
|
||||
<CreateKnowledge />
|
||||
<CreateCollection />
|
||||
|
Loading…
Reference in New Issue
Block a user