Browse Source

feat: filter selector model

Timothy J. Baek 10 tháng trước cách đây
mục cha
commit
08cc20cb93

+ 4 - 23
src/lib/components/workspace/Functions.svelte

@@ -3,7 +3,7 @@
 	import fileSaver from 'file-saver';
 	const { saveAs } = fileSaver;
 
-	import { WEBUI_NAME } from '$lib/stores';
+	import { WEBUI_NAME, functions } from '$lib/stores';
 	import { onMount, getContext } from 'svelte';
 	import { createNewPrompt, deletePromptByCommand, getPrompts } from '$lib/apis/prompts';
 
@@ -27,17 +27,6 @@
 
 	let showConfirm = false;
 	let query = '';
-
-	let functions = [];
-
-	onMount(async () => {
-		functions = await getFunctions(localStorage.token).catch((error) => {
-			toast.error(error);
-			return [];
-		});
-
-		console.log(functions);
-	});
 </script>
 
 <svelte:head>
@@ -94,7 +83,7 @@
 <hr class=" dark:border-gray-850 my-2.5" />
 
 <div class="my-3 mb-5">
-	{#each functions.filter((f) => query === '' || f.name
+	{#each $functions.filter((f) => query === '' || f.name
 				.toLowerCase()
 				.includes(query.toLowerCase()) || f.id.toLowerCase().includes(query.toLowerCase())) as func}
 		<button
@@ -237,11 +226,7 @@
 
 							if (res) {
 								toast.success('Function deleted successfully');
-
-								functions = await getFunctions(localStorage.token).catch((error) => {
-									toast.error(error);
-									return [];
-								});
+								functions.set(await getFunctions(localStorage.token));
 							}
 						}}
 					>
@@ -363,11 +348,7 @@
 			}
 
 			toast.success('Functions imported successfully');
-
-			functions = await getFunctions(localStorage.token).catch((error) => {
-				toast.error(error);
-				return [];
-			});
+			functions.set(await getFunctions(localStorage.token));
 		};
 
 		reader.readAsText(importFiles[0]);

+ 20 - 7
src/lib/components/workspace/Functions/FunctionEditor.svelte

@@ -27,17 +27,29 @@
 	}
 
 	let codeEditor;
-	let boilerplate = `from typing import Optional
-
+	let boilerplate = `from pydantic import BaseModel
+from typing import Optional
 
 class Filter:
+    class Valves(BaseModel):
+        max_turns: int
+        pass
+
     def __init__(self):
-        self.max_turns = 10
+        # Indicates custom file handling logic. This flag helps disengage default routines in favor of custom
+        # implementations, informing the WebUI to defer file-related operations to designated methods within this class.
+        self.file_handler = True
+
+        # Initialize 'valves' with specific configurations. Using 'Valves' instance helps encapsulate settings,
+        # which ensures settings are managed cohesively and not confused with operational flags like 'file_handler'.
+        self.valves = self.Valves(**{"max_turns": 10})
         pass
 
     def inlet(self, body: dict, user: Optional[dict] = None) -> dict:
-        # This method is invoked before the request is sent to the chat completion API.
-        # It can be used to modify the request body or perform validation checks.
+        # Modify the request body or validate it before processing by the chat completion API.
+        # This function is the pre-processor for the API where various checks on the input can be performed.
+        # It can also modify the request before sending it to the API.
+        
         print("inlet")
         print(body)
         print(user)
@@ -52,8 +64,9 @@ class Filter:
         return body
 
     def outlet(self, body: dict, user: Optional[dict] = None) -> dict:
-        # This method is invoked after the chat completion API has processed
-        # the request and generated a response. It can be used to overwrite the response messages.
+        # Modify or analyze the response body after processing by the API.
+        # This function is the post-processor for the API, which can be used to modify the response 
+        # or perform additional checks and analytics.
         print(f"outlet")
         print(body)
         print(user)

+ 59 - 0
src/lib/components/workspace/Models/FiltersSelector.svelte

@@ -0,0 +1,59 @@
+<script lang="ts">
+	import { getContext, onMount } from 'svelte';
+	import Checkbox from '$lib/components/common/Checkbox.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let filters = [];
+	export let selectedFilterIds = [];
+
+	let _filters = {};
+
+	onMount(() => {
+		_filters = filters.reduce((acc, filter) => {
+			acc[filter.id] = {
+				...filter,
+				selected: selectedFilterIds.includes(filter.id)
+			};
+
+			return acc;
+		}, {});
+	});
+</script>
+
+<div>
+	<div class="flex w-full justify-between mb-1">
+		<div class=" self-center text-sm font-semibold">{$i18n.t('Filters')}</div>
+	</div>
+
+	<div class=" text-xs dark:text-gray-500">
+		{$i18n.t('To select filters here, add them to the "Functions" workspace first.')}
+	</div>
+
+	<div class="flex flex-col">
+		{#if filters.length > 0}
+			<div class=" flex items-center mt-2 flex-wrap">
+				{#each Object.keys(_filters) as filter, filterIdx}
+					<div class=" flex items-center gap-2 mr-3">
+						<div class="self-center flex items-center">
+							<Checkbox
+								state={_filters[filter].selected ? 'checked' : 'unchecked'}
+								on:change={(e) => {
+									_filters[filter].selected = e.detail === 'checked';
+									selectedFilterIds = Object.keys(_filters).filter((t) => _filters[t].selected);
+								}}
+							/>
+						</div>
+
+						<div class=" py-0.5 text-sm w-full capitalize font-medium">
+							<Tooltip content={_filters[filter].meta.description}>
+								{_filters[filter].name}
+							</Tooltip>
+						</div>
+					</div>
+				{/each}
+			</div>
+		{/if}
+	</div>
+</div>

+ 2 - 0
src/lib/stores/index.ts

@@ -27,7 +27,9 @@ export const tags = writable([]);
 export const models: Writable<Model[]> = writable([]);
 export const prompts: Writable<Prompt[]> = writable([]);
 export const documents: Writable<Document[]> = writable([]);
+
 export const tools = writable([]);
+export const functions = writable([]);
 
 export const banners: Writable<Banner[]> = writable([]);
 

+ 6 - 1
src/routes/(app)/workspace/+layout.svelte

@@ -1,11 +1,16 @@
 <script lang="ts">
 	import { onMount, getContext } from 'svelte';
 
-	import { WEBUI_NAME, showSidebar } from '$lib/stores';
+	import { WEBUI_NAME, showSidebar, functions } from '$lib/stores';
 	import MenuLines from '$lib/components/icons/MenuLines.svelte';
 	import { page } from '$app/stores';
+	import { getFunctions } from '$lib/apis/functions';
 
 	const i18n = getContext('i18n');
+
+	onMount(async () => {
+		functions.set(await getFunctions(localStorage.token));
+	});
 </script>
 
 <svelte:head>

+ 2 - 0
src/routes/(app)/workspace/functions/create/+page.svelte

@@ -3,6 +3,7 @@
 	import { onMount } from 'svelte';
 	import { goto } from '$app/navigation';
 
+	import { functions } from '$lib/stores';
 	import { createNewFunction, getFunctions } from '$lib/apis/functions';
 	import FunctionEditor from '$lib/components/workspace/Functions/FunctionEditor.svelte';
 
@@ -24,6 +25,7 @@
 
 		if (res) {
 			toast.success('Function created successfully');
+			functions.set(await getFunctions(localStorage.token));
 			await goto('/workspace/functions');
 		}
 	};

+ 2 - 0
src/routes/(app)/workspace/functions/edit/+page.svelte

@@ -4,6 +4,7 @@
 
 	import { goto } from '$app/navigation';
 	import { page } from '$app/stores';
+	import { functions } from '$lib/stores';
 	import { updateFunctionById, getFunctions, getFunctionById } from '$lib/apis/functions';
 
 	import FunctionEditor from '$lib/components/workspace/Functions/FunctionEditor.svelte';
@@ -25,6 +26,7 @@
 
 		if (res) {
 			toast.success('Function updated successfully');
+			functions.set(await getFunctions(localStorage.token));
 		}
 	};
 

+ 22 - 1
src/routes/(app)/workspace/models/edit/+page.svelte

@@ -5,7 +5,7 @@
 
 	import { onMount, getContext } from 'svelte';
 	import { page } from '$app/stores';
-	import { settings, user, config, models, tools } from '$lib/stores';
+	import { settings, user, config, models, tools, functions } from '$lib/stores';
 	import { splitStream } from '$lib/utils';
 
 	import { getModelInfos, updateModelById } from '$lib/apis/models';
@@ -16,6 +16,7 @@
 	import Tags from '$lib/components/common/Tags.svelte';
 	import Knowledge from '$lib/components/workspace/Models/Knowledge.svelte';
 	import ToolsSelector from '$lib/components/workspace/Models/ToolsSelector.svelte';
+	import FiltersSelector from '$lib/components/workspace/Models/FiltersSelector.svelte';
 
 	const i18n = getContext('i18n');
 
@@ -62,6 +63,7 @@
 
 	let knowledge = [];
 	let toolIds = [];
+	let filterIds = [];
 
 	const updateHandler = async () => {
 		loading = true;
@@ -86,6 +88,14 @@
 			}
 		}
 
+		if (filterIds.length > 0) {
+			info.meta.filterIds = filterIds;
+		} else {
+			if (info.meta.filterIds) {
+				delete info.meta.filterIds;
+			}
+		}
+
 		info.params.stop = params.stop ? params.stop.split(',').filter((s) => s.trim()) : null;
 		Object.keys(info.params).forEach((key) => {
 			if (info.params[key] === '' || info.params[key] === null) {
@@ -147,6 +157,10 @@
 					toolIds = [...model?.info?.meta?.toolIds];
 				}
 
+				if (model?.info?.meta?.filterIds) {
+					filterIds = [...model?.info?.meta?.filterIds];
+				}
+
 				if (model?.owned_by === 'openai') {
 					capabilities.usage = false;
 				}
@@ -534,6 +548,13 @@
 				<ToolsSelector bind:selectedToolIds={toolIds} tools={$tools} />
 			</div>
 
+			<div class="my-2">
+				<FiltersSelector
+					bind:selectedFilterIds={filterIds}
+					filters={$functions.filter((func) => func.type === 'filter')}
+				/>
+			</div>
+
 			<div class="my-2">
 				<div class="flex w-full justify-between mb-1">
 					<div class=" self-center text-sm font-semibold">{$i18n.t('Capabilities')}</div>