From e5e2cd78769639b2df83776f1b991966f922f8b4 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 17 Jul 2026 04:48:21 -0400 Subject: [PATCH] refac --- backend/open_webui/models/knowledge.py | 10 + src/lib/components/admin/Functions.svelte | 436 ++++++++++------ .../admin/Functions/FunctionMenu.svelte | 12 +- src/lib/components/workspace/Knowledge.svelte | 242 +++++---- .../workspace/Knowledge/ItemMenu.svelte | 12 +- src/lib/components/workspace/Models.svelte | 464 ++++++++++-------- src/lib/components/workspace/Prompts.svelte | 47 +- src/lib/components/workspace/Skills.svelte | 337 ++++++++----- .../workspace/Skills/SkillMenu.svelte | 11 +- src/lib/components/workspace/Tools.svelte | 320 +++++++----- .../workspace/Tools/ToolMenu.svelte | 12 +- .../workspace/common/CommunityDiscover.svelte | 2 +- 12 files changed, 1219 insertions(+), 686 deletions(-) diff --git a/backend/open_webui/models/knowledge.py b/backend/open_webui/models/knowledge.py index f6650e2258..d48586a9ca 100644 --- a/backend/open_webui/models/knowledge.py +++ b/backend/open_webui/models/knowledge.py @@ -149,6 +149,7 @@ class KnowledgeDirectoryForm(BaseModel): #################### class KnowledgeUserModel(KnowledgeModel): user: Optional[UserResponse] = None + file_count: int | None = None class KnowledgeResponse(KnowledgeModel): @@ -322,6 +323,14 @@ class KnowledgeTable: knowledge_ids = [kb.id for kb, _ in items] grants_map = await AccessGrants.get_grants_by_resources('knowledge', knowledge_ids, db=db) + file_counts = {} + if knowledge_ids: + file_count_result = await db.execute( + select(KnowledgeFile.knowledge_id, func.count(KnowledgeFile.id)) + .where(KnowledgeFile.knowledge_id.in_(knowledge_ids)) + .group_by(KnowledgeFile.knowledge_id) + ) + file_counts = dict(file_count_result.all()) knowledge_bases = [] for knowledge_base, user in items: @@ -336,6 +345,7 @@ class KnowledgeTable: ) ).model_dump(), 'user': (UserModel.model_validate(user).model_dump() if user else None), + 'file_count': file_counts.get(knowledge_base.id, 0), } ) ) diff --git a/src/lib/components/admin/Functions.svelte b/src/lib/components/admin/Functions.svelte index b012674060..201ff9b82a 100644 --- a/src/lib/components/admin/Functions.svelte +++ b/src/lib/components/admin/Functions.svelte @@ -1,8 +1,12 @@ { editHandler(); + closeMenu(); }} > { shareHandler(); + closeMenu(); }} > @@ -96,6 +103,7 @@ class="select-none flex gap-2 items-center h-[1.6875rem] px-2 text-[13px] font-normal cursor-pointer hover:bg-gray-50/40 dark:hover:bg-gray-800/40 rounded-xl w-full" on:click={() => { cloneHandler(); + closeMenu(); }} > @@ -106,6 +114,7 @@ class="select-none flex gap-2 items-center h-[1.6875rem] px-2 text-[13px] font-normal cursor-pointer hover:bg-gray-50/40 dark:hover:bg-gray-800/40 rounded-xl w-full" on:click={() => { exportHandler(); + closeMenu(); }} > @@ -118,6 +127,7 @@ class="select-none flex gap-2 items-center h-[1.6875rem] px-2 text-[13px] font-normal cursor-pointer hover:bg-gray-50/40 dark:hover:bg-gray-800/40 rounded-xl w-full" on:click={() => { deleteHandler(); + closeMenu(); }} > diff --git a/src/lib/components/workspace/Knowledge.svelte b/src/lib/components/workspace/Knowledge.svelte index cca876e85a..7ac0446107 100644 --- a/src/lib/components/workspace/Knowledge.svelte +++ b/src/lib/components/workspace/Knowledge.svelte @@ -10,7 +10,7 @@ const i18n = getContext>('i18n'); - import { WEBUI_NAME, knowledge, user, workspaceActions } from '$lib/stores'; + import { WEBUI_NAME, user, workspaceActions } from '$lib/stores'; import { deleteKnowledgeById, searchKnowledgeBases, @@ -38,6 +38,7 @@ name: string; description?: string; updated_at: number; + file_count?: number; write_access?: boolean; meta?: any; user?: { @@ -192,6 +193,59 @@ } }; + const openKnowledge = (item: KnowledgeListItem) => { + if (item?.meta?.document) { + toast.error( + $i18n.t( + 'Only collections can be edited, create a new knowledge base to edit/add documents.' + ) + ); + return; + } + + goto(`/workspace/knowledge/${item.id}`); + }; + + const shouldIgnoreRowClick = (target: EventTarget | null) => { + return target instanceof Element && !!target.closest('button, a, input, [role="menu"]'); + }; + + const getKnowledgeMetaPreview = (item: KnowledgeListItem) => { + const fileCount = + item.file_count !== undefined + ? item.file_count === 1 + ? $i18n.t('1 file') + : $i18n.t('{{count}} files', { count: item.file_count }) + : null; + + if (!item?.meta) return [fileCount, item.description].filter(Boolean).join(' · '); + + if (item.meta.source === 'external') { + return [ + fileCount, + item.meta.external?.provider, + item.meta.external?.source?.name, + item.meta.external?.auth_mode, + item.description + ] + .filter(Boolean) + .join(' · '); + } + + const metadata = Object.entries(item.meta) + .filter(([, value]) => value !== null && value !== undefined && value !== '') + .map(([key, value]) => { + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + return `${key}: ${value}`; + } + + return key; + }) + .join(' · '); + + return [fileCount, metadata, item.description].filter(Boolean).join(' · '); + }; + onMount(async () => { viewOption = localStorage?.workspaceViewOption || ''; sourceOption = localStorage?.workspaceKnowledgeSourceOption || ''; @@ -297,102 +351,124 @@ {#if items !== null && total !== null} {#if (items ?? []).length !== 0} -
- {#each items as item} - - {/each} + {/each} +
{#if !allItemsLoaded} diff --git a/src/lib/components/workspace/Knowledge/ItemMenu.svelte b/src/lib/components/workspace/Knowledge/ItemMenu.svelte index ae59eb12d3..a2ea3be9b5 100644 --- a/src/lib/components/workspace/Knowledge/ItemMenu.svelte +++ b/src/lib/components/workspace/Knowledge/ItemMenu.svelte @@ -15,6 +15,11 @@ export let onClose: Function = () => {}; let show = false; + + const closeMenu = () => { + show = false; + onClose(); + }; + + + + + + +
+ {#each filteredItems as tool}
{ + if (!tool.write_access || shouldIgnoreRowClick(e.target)) return; + openTool(tool); + }} + on:keydown={(e) => { + if (!tool.write_access || e.currentTarget !== e.target) return; + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + openTool(tool); + } + }} > - {#if tool.write_access} - -
-
- -
-
- {tool.name} -
- {#if tool?.meta?.manifest?.version} -
- v{tool?.meta?.manifest?.version ?? ''} -
- {/if} +
+
+
+
+ +
+ {tool.name}
-
-
- - {$i18n.t('By {{name}}', { - name: capitalizeFirstLetter( - tool?.user?.name ?? tool?.user?.email ?? $i18n.t('Deleted User') - ) - })} - + + {#if tool?.meta?.manifest?.version} +
+ v{tool?.meta?.manifest?.version ?? ''}
-
-
-
-
- {:else} -
-
-
-
- -
-
- {tool.name} -
- {#if tool?.meta?.manifest?.version} -
- v{tool?.meta?.manifest?.version ?? ''} -
- {/if} -
-
+ {/if} + + +
+ {dayjs(tool.updated_at * 1000).fromNow()} +
+
+ + {#if !tool.write_access} -
-
-
- - {$i18n.t('By {{name}}', { - name: capitalizeFirstLetter( - tool?.user?.name ?? tool?.user?.email ?? $i18n.t('Deleted User') - ) - })} - -
-
+ {/if}
+ + {#if tool?.meta?.description} + +
+ {tool?.meta?.description} +
+
+ {/if}
- {/if} +
+ + + {#if tool.write_access} -
+
{#if shiftKey} {:else} {#if tool?.meta?.manifest?.funding_url ?? false} {/if} - { - goto(`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`); - }} - shareHandler={() => { - shareHandler(tool); - }} - cloneHandler={() => { - cloneHandler(tool); - }} - exportHandler={() => { - exportHandler(tool); - }} - deleteHandler={async () => { - selectedTool = tool; - showDeleteConfirm = true; - }} - onClose={() => {}} - > - - + + +
{/if}
{/if}
- - {/each} + {/each} +
{:else}
diff --git a/src/lib/components/workspace/Tools/ToolMenu.svelte b/src/lib/components/workspace/Tools/ToolMenu.svelte index 79ccd479ae..2966fee569 100644 --- a/src/lib/components/workspace/Tools/ToolMenu.svelte +++ b/src/lib/components/workspace/Tools/ToolMenu.svelte @@ -20,7 +20,12 @@ export let deleteHandler: Function; export let onClose: Function; - let show = false; + export let show = false; + + const closeMenu = () => { + show = false; + onClose(); + }; { editHandler(); + closeMenu(); }} > { shareHandler(); + closeMenu(); }} > @@ -80,6 +87,7 @@ draggable="false" on:click={() => { cloneHandler(); + closeMenu(); }} > @@ -93,6 +101,7 @@ draggable="false" on:click={() => { exportHandler(); + closeMenu(); }} > @@ -108,6 +117,7 @@ draggable="false" on:click={() => { deleteHandler(); + closeMenu(); }} > diff --git a/src/lib/components/workspace/common/CommunityDiscover.svelte b/src/lib/components/workspace/common/CommunityDiscover.svelte index 1ded8789ec..ba03ad4ceb 100644 --- a/src/lib/components/workspace/common/CommunityDiscover.svelte +++ b/src/lib/components/workspace/common/CommunityDiscover.svelte @@ -10,7 +10,7 @@ export let description = ''; -
+
{$i18n.t('Made by Open WebUI Community')}