Files
GenAIExamples/AgentQnA/ui/open_webui_patches/0002-update-agent-icloud-upload-feature.patch

532 lines
16 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
From 8ad31e50644eab3c9e698d7828b1857919887841 Mon Sep 17 00:00:00 2001
From: lkk12014402 <kaokao.lv@intel.com>
Date: Tue, 8 Apr 2025 03:38:09 +0000
Subject: [PATCH 2/2] update agent icloud upload feature
---
src/lib/apis/knowledge/index.ts | 60 +++++++
.../admin/Settings/Connections.svelte | 50 +++++-
.../components/icons/UploadCloudIcon.svelte | 18 ++
src/lib/components/workspace/Knowledge.svelte | 57 +++++-
.../KnowledgeBase/AddIcloudContentMenu.svelte | 164 ++++++++++++++++++
.../KnowledgeBase/IcloudFiles.svelte | 37 ++++
src/lib/i18n/locales/zh-CN/translation.json | 15 +-
7 files changed, 396 insertions(+), 5 deletions(-)
create mode 100644 src/lib/components/icons/UploadCloudIcon.svelte
create mode 100644 src/lib/components/workspace/Knowledge/KnowledgeBase/AddIcloudContentMenu.svelte
create mode 100644 src/lib/components/workspace/Knowledge/KnowledgeBase/IcloudFiles.svelte
diff --git a/src/lib/apis/knowledge/index.ts b/src/lib/apis/knowledge/index.ts
index c5fad1323..32be528a7 100644
--- a/src/lib/apis/knowledge/index.ts
+++ b/src/lib/apis/knowledge/index.ts
@@ -345,3 +345,63 @@ export const deleteKnowledgeById = async (token: string, id: string) => {
return res;
};
+
+export const getIcloudFiles = async (ICLOUD_BASE_URLS: string) => {
+ let error = null;
+
+ const res = await fetch(`${ICLOUD_BASE_URLS}/dataprep/get`, {
+ method: 'POST',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ }
+ })
+ .then(async (res) => {
+ if (!res.ok) throw await res.json();
+ return res.json();
+ })
+ .then((json) => {
+ return json;
+ })
+ .catch((err) => {
+ error = err.detail;
+
+ console.log(err);
+ return null;
+ });
+
+ if (error) {
+ throw error;
+ }
+
+ return res;
+};
+
+export const updateIcloudFiles = async (ICLOUD_BASE_URLS: string, formData: any) => {
+ let error = null;
+
+ const res = await fetch(`${ICLOUD_BASE_URLS}/dataprep/ingest`, {
+ method: 'POST',
+ body: formData
+ })
+ .then(async (res) => {
+ if (!res.ok) throw await res.json();
+ return res.json();
+ })
+ .then((json) => {
+ return json;
+ })
+ .catch((err) => {
+ error = err.detail;
+
+ console.log(err);
+ return null;
+ });
+
+ if (error) {
+ throw error;
+ }
+
+ return res;
+};
+
diff --git a/src/lib/components/admin/Settings/Connections.svelte b/src/lib/components/admin/Settings/Connections.svelte
index 2fcfadaec..3237744d5 100644
--- a/src/lib/components/admin/Settings/Connections.svelte
+++ b/src/lib/components/admin/Settings/Connections.svelte
@@ -47,6 +47,9 @@
let showAddOpenAIConnectionModal = false;
let showAddOllamaConnectionModal = false;
+ let ENABLE_ICLOUD_API: null | boolean = (localStorage.getItem('ENABLE_ICLOUD_API') === "enable");
+ let ICLOUD_BASE_URL = localStorage.getItem('ICLOUD_BASE_URL') || '';
+
const updateOpenAIHandler = async () => {
if (ENABLE_OPENAI_API !== null) {
// Remove trailing slashes
@@ -193,10 +196,22 @@
}
});
+ const updateIcloudHandler = async () => {
+ if (ENABLE_ICLOUD_API) {
+ localStorage.setItem('ICLOUD_BASE_URL', ICLOUD_BASE_URL);
+ localStorage.setItem('ENABLE_ICLOUD_API', "enable");
+ } else {
+ localStorage.setItem('ICLOUD_BASE_URL', '');
+ localStorage.setItem('ENABLE_ICLOUD_API', "");
+ }
+ toast.success($i18n.t('Icloud API settings updated'));
+ };
+
const submitHandler = async () => {
updateOpenAIHandler();
updateOllamaHandler();
updateDirectConnectionsHandler();
+ updateIcloudHandler();
dispatch('save');
};
@@ -301,7 +316,7 @@
</div>
{#if ENABLE_OLLAMA_API}
- <hr class=" border-gray-100 dark:border-gray-850 my-2" />
+ <hr class=" border-gray-100 dark:border-gray-850" />
<div class="">
<div class="flex justify-between items-center">
@@ -358,6 +373,39 @@
{/if}
</div>
+ <hr class=" border-gray-50 dark:border-gray-850" />
+
+ <div class="pr-1.5 my-2">
+ <div class="flex justify-between items-center text-sm">
+ <div class="font-medium">{$i18n.t('Icloud File API')}</div>
+
+ <div class="mt-1">
+ <Switch
+ bind:state={ENABLE_ICLOUD_API}
+ on:change={async () => {
+ updateIcloudHandler();
+ }}
+ />
+ </div>
+ </div>
+
+ {#if ENABLE_ICLOUD_API}
+ <hr class=" border-gray-50 dark:border-gray-850 my-2" />
+
+ <div class="">
+ <div class="flex w-full gap-1.5">
+ <div class="flex-1 flex flex-col gap-1.5">
+ <input
+ class="w-full text-sm bg-transparent outline-none"
+ placeholder={$i18n.t('Enter Icloud URL(e.g.') + 'http://localhost:6007/v1)'}
+ bind:value={ICLOUD_BASE_URL}
+ />
+ </div>
+ </div>
+ </div>
+ {/if}
+ </div>
+
<hr class=" border-gray-100 dark:border-gray-850" />
<div class="pr-1.5 my-2">
diff --git a/src/lib/components/icons/UploadCloudIcon.svelte b/src/lib/components/icons/UploadCloudIcon.svelte
new file mode 100644
index 000000000..eed3bd582
--- /dev/null
+++ b/src/lib/components/icons/UploadCloudIcon.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+ export let className = 'w-4 h-4';
+</script>
+
+<svg
+ t="1744007283647"
+ viewBox="0 0 1491 1024"
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ p-id="1630"
+ class = {className}
+ ><path
+ d="M546.047379 263.651842s-90.221363-91.423424-212.63125-16.762074c-109.521121 71.300031-90.154581 201.768179-90.154582 201.76818S0 498.498962 0 759.902727c5.431535 261.003078 264.186314 263.674325 264.186314 263.674326l388.443814 0.422947V744.565318H466.355181l279.434681-279.412421 279.390161 279.412421h-186.297208V1024l377.157796-0.422947s240.812904 0.222604 274.648698-248.092052c16.094262-271.576764-232.754643-325.113003-232.754643-325.113003S1286.205362 48.327085 936.761752 2.470681C637.181417-29.740104 546.047379 263.651842 546.047379 263.651842z"
+ fill="#507BFC"
+ p-id="1631"
+ ></path></svg
+>
+
diff --git a/src/lib/components/workspace/Knowledge.svelte b/src/lib/components/workspace/Knowledge.svelte
index 57d45312d..43a1f305e 100644
--- a/src/lib/components/workspace/Knowledge.svelte
+++ b/src/lib/components/workspace/Knowledge.svelte
@@ -13,7 +13,8 @@
import {
getKnowledgeBases,
deleteKnowledgeById,
- getKnowledgeBaseList
+ getKnowledgeBaseList,
+ getIcloudFiles
} from '$lib/apis/knowledge';
import { goto } from '$app/navigation';
@@ -26,6 +27,11 @@
import Spinner from '../common/Spinner.svelte';
import { capitalizeFirstLetter } from '$lib/utils';
import Tooltip from '../common/Tooltip.svelte';
+ import AddIcloudConnectionModal from '$lib/components/workspace/Knowledge/KnowledgeBase/AddIcloudContentMenu.svelte';
+ import IcloudFiles from '$lib/components/workspace/Knowledge/KnowledgeBase/IcloudFiles.svelte';
+
+ let showAddTextContentModal = false;
+ let IcloudFile = [];
let loaded = false;
@@ -65,9 +71,26 @@
};
onMount(async () => {
+ await updateIcloudFiles();
+
knowledgeBases = await getKnowledgeBaseList(localStorage.token);
loaded = true;
});
+
+ async function updateIcloudFiles() {
+ let ICLOUD_BASE_URL = localStorage.getItem('ICLOUD_BASE_URL') || '';
+ console.log('ICLOUD_BASE_URL', ICLOUD_BASE_URL);
+
+ if (ICLOUD_BASE_URL !== '') {
+ const res = await getIcloudFiles(ICLOUD_BASE_URL).catch((e) => {
+ toast.error(`${e}`);
+ });
+
+ if (res) {
+ IcloudFile = res;
+ }
+ }
+ }
</script>
<svelte:head>
@@ -187,11 +210,39 @@
{/each}
</div>
- <div class=" text-gray-500 text-xs mt-1 mb-2">
- ⓘ {$i18n.t("Use '#' in the prompt input to load and include your knowledge.")}
+ <div class="flex justify-between items-center">
+ <div class="flex md:self-center text-xl font-medium px-0.5 items-center">
+ {$i18n.t('Icloud Knowledge')}
+ <div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+ <span class="text-lg font-medium text-gray-500 dark:text-gray-300">{IcloudFile.length}</span>
+ </div>
+ <div>
+ <button
+ class=" px-2 py-2 rounded-xl hover:bg-gray-700/10 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition font-medium text-sm flex items-center space-x-1"
+ aria-label={$i18n.t('Upload to Icloud')}
+ on:click={() => {
+ showAddTextContentModal = !showAddTextContentModal;
+ }}
+ >
+ <Plus className="size-3.5" />
+ </button>
+ </div>
+ </div>
+ <hr class="border-gray-100 dark:border-gray-850 my-2" />
+ <div class=" flex overflow-y-auto w-full h-[15rem] scrollbar-hidden text-xs">
+ <IcloudFiles files={IcloudFile} />
</div>
{:else}
<div class="w-full h-full flex justify-center items-center">
<Spinner />
</div>
{/if}
+
+<AddIcloudConnectionModal
+ bind:show={showAddTextContentModal}
+ on:updateIcloudFile={async (e) => {
+ if (e.detail.status) {
+ await updateIcloudFiles();
+ }
+ }}
+/>
diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase/AddIcloudContentMenu.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase/AddIcloudContentMenu.svelte
new file mode 100644
index 000000000..fb906a0d3
--- /dev/null
+++ b/src/lib/components/workspace/Knowledge/KnowledgeBase/AddIcloudContentMenu.svelte
@@ -0,0 +1,164 @@
+<script lang="ts">
+ import { toast } from 'svelte-sonner';
+ import { getContext, onMount, createEventDispatcher } from 'svelte';
+ import Modal from '$lib/components/common/Modal.svelte';
+ import UploadCloudIcon from '$lib/components/icons/UploadCloudIcon.svelte';
+ import Spinner from '$lib/components/common/Spinner.svelte';
+ import { updateIcloudFiles } from '$lib/apis/knowledge';
+
+ const i18n = getContext('i18n');
+ const dispatch = createEventDispatcher();
+
+ export let show = false;
+
+ let url = '';
+
+ let loading = false;
+
+ let selectedFile = null;
+
+ function handleFileSelect(event) {
+ selectedFile = event.target.files[0];
+ }
+
+ function parseAndValidateUrls(normalizedInput: string): string[] {
+ return normalizedInput
+ .split(',')
+ .map((candidate) => {
+ const processed = candidate.replace(/^["']+|["']+$/g, '').trim();
+
+ try {
+ new URL(processed);
+ return processed;
+ } catch {
+ return null;
+ }
+ })
+ .filter((url): url is string => url !== null);
+ }
+
+ async function submitHandler() {
+ loading = true;
+
+ if (!url && !selectedFile) {
+ loading = false;
+ show = false;
+
+ toast.error($i18n.t('URL or File are required'));
+ return;
+ }
+ if (url && selectedFile) {
+ loading = false;
+ show = false;
+
+ toast.error($i18n.t('Upload file or enter URL'));
+ url = '';
+ selectedFile = null;
+ return;
+ }
+
+ const formData = new FormData();
+ if (url) {
+ formData.append('link_list', JSON.stringify(parseAndValidateUrls(url)));
+ }
+ if (selectedFile) {
+ formData.append('files', selectedFile, selectedFile.name);
+ }
+ let ICLOUD_BASE_URL = localStorage.getItem('ICLOUD_BASE_URL') || '';
+ console.log('ICLOUD_BASE_URL', ICLOUD_BASE_URL);
+
+ if (ICLOUD_BASE_URL !== '') {
+ const res = await updateIcloudFiles(ICLOUD_BASE_URL, formData).catch((e) => {
+ toast.error(`${e}`);
+
+ return;
+ });
+
+ if (res) {
+ toast.success($i18n.t('Upload Succeed'));
+ dispatch('updateIcloudFile', { status: true });
+ }
+
+ url = '';
+ selectedFile = null;
+ loading = false;
+ show = false;
+ }
+ }
+</script>
+
+<Modal size="sm" bind:show>
+ <div class="flex flex-col justify-end">
+ <div class=" flex justify-between dark:text-gray-100 px-5 pt-4 pb-2">
+ <div class="flex-col text-lg font-medium self-center font-primary">
+ {$i18n.t('Upload Icloud file')}
+ <span class="text-sm text-gray-500">- {$i18n.t('choose URL or local file')}</span>
+ </div>
+
+ <button
+ class="self-center"
+ on:click={() => {
+ show = false;
+ }}
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 20 20"
+ fill="currentColor"
+ class="w-5 h-5"
+ >
+ <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>
+ </div>
+
+ <div class="flex flex-col md:flex-row w-full px-4 pb-4 md:space-x-4 dark:text-gray-200">
+ <div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+ <div class="flex items-center w-full">
+ <div class="flex-1 min-w-0 mr-2">
+ <div class="flex flex-col w-full my-8 mx-2">
+ <input
+ class="w-full text-sm bg-transparent placeholder:text-gray-300 outline-none border-b-solid border-b-2 border-blue-500 rounded p-2"
+ type="text"
+ bind:value={url}
+ placeholder={$i18n.t('Upload from URL')}
+ />
+ </div>
+ </div>
+
+ <div class="flex-none w-[1px] h-[60%] mx-2.5 bg-gray-300"></div>
+
+ <div class="flex-1 min-w-0">
+ <input type="file" id="fileInput" hidden on:change={handleFileSelect} />
+
+ <label
+ for="fileInput"
+ class="cursor-pointer flex flex-col items-center hover:bg-gray-100 rounded-lg p-2 transition-colors"
+ >
+ <UploadCloudIcon className="w-12 h-12 text-gray-500" />
+ <div class="text-xs text-gray-500 pt-2">
+ {selectedFile ? selectedFile.name : '点击上传文件'}
+ </div>
+ </label>
+ </div>
+ </div>
+ </div>
+ </div>
+ {#if loading}
+ <Spinner className="my-4 size-4" />
+ {:else}
+ <button
+ class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded text-sm"
+ on:click={(e) => {
+ e.preventDefault();
+ submitHandler();
+ }}
+ >
+ {$i18n.t('Upload Confirm')}
+ </button>
+ {/if}
+ </div>
+</Modal>
+
diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase/IcloudFiles.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase/IcloudFiles.svelte
new file mode 100644
index 000000000..d6490dce2
--- /dev/null
+++ b/src/lib/components/workspace/Knowledge/KnowledgeBase/IcloudFiles.svelte
@@ -0,0 +1,37 @@
+<script lang="ts">
+ export let selectedFileId = null;
+ export let files = [];
+
+ export let small = false;
+</script>
+
+<div class="max-h-full flex flex-col w-full">
+ {#each files as file}
+ <div class="mt-1 px-2 flex hover:bg-gray-50 transition">
+ <div class="p-3 bg-black/20 dark:bg-white/10 text-white rounded-xl my-2">
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 24 24"
+ fill="currentColor"
+ class=" size-3"
+ >
+ <path
+ fill-rule="evenodd"
+ d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625ZM7.5 15a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 7.5 15Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H8.25Z"
+ clip-rule="evenodd"
+ />
+ <path
+ d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z"
+ />
+ </svg>
+ </div>
+
+ <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">
+ {file.name}
+ </div>
+ </div>
+ </div>
+ {/each}
+</div>
+
diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json
index ebb53a1b5..d6b72e04d 100644
--- a/src/lib/i18n/locales/zh-CN/translation.json
+++ b/src/lib/i18n/locales/zh-CN/translation.json
@@ -1174,5 +1174,18 @@
"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "您的全部捐款将直接给到插件开发者Open WebUI 不会收取任何比例。但众筹平台可能会有服务费、抽成。",
"Youtube": "YouTube",
"Youtube Language": "Youtube 语言",
- "Youtube Proxy URL": "Youtube 代理 URL"
+ "Youtube Proxy URL": "Youtube 代理 URL",
+ "Upload Icloud file": "上传到云端",
+ "choose URL or local file": "选择URL或本地文件",
+ "Upload from URL": "从URL上传",
+ "Upload Confirm": "确认上传",
+ "URL or File are required": "未上传文件",
+ "Upload file or enter URL": "文件与URL不能同时提交",
+ "Icloud File": "云端文件",
+ "Icloud File API": "云端存储API",
+ "Enter Icloud URL(e.g.": "输入云端存储URL例如.",
+ "Upload to Icloud": "上传到云端",
+ "Icloud Knowledge": "云端数据库",
+ "Upload Succeed": "上传文件成功",
+ "Icloud API settings updated": "云端存储API设置已更新"
}
--
2.34.1