1
0
Эх сурвалжийг харах

feat: multiple pipelines server support

Timothy J. Baek 11 сар өмнө
parent
commit
c1cabf1415

+ 29 - 2
backend/main.py

@@ -464,10 +464,37 @@ async def get_models(user=Depends(get_verified_user)):
     return {"data": models}
 
 
+@app.get("/api/pipelines/list")
+async def get_pipelines_list(user=Depends(get_admin_user)):
+    models = await get_all_models()
+    urlIdxs = list(set([model["urlIdx"] for model in models if "pipeline" in model]))
+
+    return {
+        "data": [
+            {
+                "url": openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx],
+                "idx": urlIdx,
+            }
+            for urlIdx in urlIdxs
+        ]
+    }
+
+
 @app.get("/api/pipelines")
-async def get_pipelines(user=Depends(get_admin_user)):
+async def get_pipelines(urlIdx: Optional[int] = None, user=Depends(get_admin_user)):
     models = await get_all_models()
-    pipelines = [model for model in models if "pipeline" in model]
+
+    print(urlIdx)
+
+    if urlIdx is None:
+        pipelines = [model for model in models if "pipeline" in model]
+    else:
+        pipelines = [
+            model
+            for model in models
+            if "pipeline" in model and model["urlIdx"] == urlIdx
+        ]
+
     return {"data": pipelines}
 
 

+ 36 - 2
src/lib/apis/index.ts

@@ -49,10 +49,44 @@ export const getModels = async (token: string = '') => {
 	return models;
 };
 
-export const getPipelines = async (token: string = '') => {
+export const getPipelinesList = async (token: string = '') => {
 	let error = null;
 
-	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines`, {
+	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/list`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	let pipelines = res?.data ?? [];
+	return pipelines;
+};
+
+export const getPipelines = async (token: string, urlIdx?: string) => {
+	let error = null;
+
+	const searchParams = new URLSearchParams();
+	if (urlIdx) {
+		searchParams.append('urlIdx', urlIdx);
+	}
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines?${searchParams.toString()}`, {
 		method: 'GET',
 		headers: {
 			Accept: 'application/json',

+ 127 - 67
src/lib/components/admin/Settings/Pipelines.svelte

@@ -1,31 +1,33 @@
 <script lang="ts">
 	import { v4 as uuidv4 } from 'uuid';
 
+	import { toast } from 'svelte-sonner';
+	import { models } from '$lib/stores';
 	import { getContext, onMount, tick } from 'svelte';
-
 	import type { Writable } from 'svelte/store';
 	import type { i18n as i18nType } from 'i18next';
-	import { stringify } from 'postcss';
 	import {
 		getPipelineValves,
 		getPipelineValvesSpec,
 		updatePipelineValves,
 		getPipelines,
-		getModels
+		getModels,
+		getPipelinesList
 	} from '$lib/apis';
+
 	import Spinner from '$lib/components/common/Spinner.svelte';
-	import { toast } from 'svelte-sonner';
-	import { models } from '$lib/stores';
 
 	const i18n: Writable<i18nType> = getContext('i18n');
 
 	export let saveHandler: Function;
 
+	let PIPELINES_LIST = null;
+	let selectedPipelinesUrlIdx = '';
+
 	let pipelines = null;
 
 	let valves = null;
 	let valves_spec = null;
-
 	let selectedPipelineIdx = null;
 
 	const updateHandler = async () => {
@@ -54,6 +56,9 @@
 	};
 
 	const getValves = async (idx) => {
+		valves = null;
+		valves_spec = null;
+
 		valves_spec = await getPipelineValvesSpec(localStorage.token, pipelines[idx].id);
 		valves = await getPipelineValves(localStorage.token, pipelines[idx].id);
 
@@ -63,7 +68,11 @@
 	};
 
 	const setPipelines = async () => {
-		pipelines = await getPipelines(localStorage.token);
+		pipelines = null;
+		valves = null;
+		valves_spec = null;
+
+		pipelines = await getPipelines(localStorage.token, selectedPipelinesUrlIdx);
 
 		if (pipelines.length > 0) {
 			selectedPipelineIdx = 0;
@@ -72,6 +81,12 @@
 	};
 
 	onMount(async () => {
+		PIPELINES_LIST = await getPipelinesList(localStorage.token);
+
+		if (PIPELINES_LIST.length > 0) {
+			selectedPipelinesUrlIdx = PIPELINES_LIST[0]['idx'];
+		}
+
 		setPipelines();
 	});
 </script>
@@ -83,95 +98,140 @@
 	}}
 >
 	<div class=" space-y-2 pr-1.5 overflow-y-scroll max-h-80 h-full">
-		{#if pipelines !== null && pipelines.length > 0}
+		{#if PIPELINES_LIST !== null}
 			<div class="flex w-full justify-between mb-2">
 				<div class=" self-center text-sm font-semibold">
-					{$i18n.t('Pipelines')}
+					{$i18n.t('Manage Pipelines')}
 				</div>
 			</div>
-			<div class="space-y-1">
-				{#if pipelines.length > 0}
+
+			{#if PIPELINES_LIST.length > 0}
+				<div class="space-y-1">
 					<div class="flex gap-2">
-						<div class="flex-1 pb-1">
+						<div class="flex-1">
 							<select
 								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-								bind:value={selectedPipelineIdx}
-								placeholder={$i18n.t('Select a pipeline')}
+								bind:value={selectedPipelinesUrlIdx}
+								placeholder={$i18n.t('Select a pipeline url')}
 								on:change={async () => {
 									await tick();
-									await getValves(selectedPipelineIdx);
+									await setPipelines();
 								}}
 							>
-								{#each pipelines as pipeline, idx}
-									<option value={idx} class="bg-gray-100 dark:bg-gray-700"
-										>{pipeline.name} ({pipeline.pipeline.type ?? 'pipe'})</option
+								<option value="" selected disabled class="bg-gray-100 dark:bg-gray-700"
+									>{$i18n.t('Select a pipeline url')}</option
+								>
+
+								{#each PIPELINES_LIST as pipelines, idx}
+									<option value={pipelines.idx} class="bg-gray-100 dark:bg-gray-700"
+										>{pipelines.url}</option
 									>
 								{/each}
 							</select>
 						</div>
 					</div>
-				{/if}
+				</div>
+			{/if}
 
-				<div class="text-sm font-medium">{$i18n.t('Valves')}</div>
+			<hr class=" dark:border-gray-800 my-3 w-full" />
 
-				<div class="space-y-1">
-					{#if pipelines[selectedPipelineIdx].pipeline.valves}
-						{#if valves}
-							{#each Object.keys(valves_spec.properties) as property, idx}
-								<div class=" py-0.5 w-full justify-between">
-									<div class="flex w-full justify-between">
-										<div class=" self-center text-xs font-medium">
-											{valves_spec.properties[property].title}
-										</div>
+			{#if pipelines !== null}
+				{#if pipelines.length > 0}
+					<div class="flex w-full justify-between mb-2">
+						<div class=" self-center text-sm font-semibold">
+							{$i18n.t('Pipelines Valves')}
+						</div>
+					</div>
+					<div class="space-y-1">
+						{#if pipelines.length > 0}
+							<div class="flex gap-2">
+								<div class="flex-1 pb-1">
+									<select
+										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+										bind:value={selectedPipelineIdx}
+										placeholder={$i18n.t('Select a pipeline')}
+										on:change={async () => {
+											await tick();
+											await getValves(selectedPipelineIdx);
+										}}
+									>
+										{#each pipelines as pipeline, idx}
+											<option value={idx} class="bg-gray-100 dark:bg-gray-700"
+												>{pipeline.name} ({pipeline.pipeline.type ?? 'pipe'})</option
+											>
+										{/each}
+									</select>
+								</div>
+							</div>
+						{/if}
 
-										<button
-											class="p-1 px-3 text-xs flex rounded transition"
-											type="button"
-											on:click={() => {
-												valves[property] = (valves[property] ?? null) === null ? '' : null;
-											}}
-										>
-											{#if (valves[property] ?? null) === null}
-												<span class="ml-2 self-center"> {$i18n.t('None')} </span>
-											{:else}
-												<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
-											{/if}
-										</button>
-									</div>
-
-									{#if (valves[property] ?? null) !== null}
-										<div class="flex mt-0.5 space-x-2">
-											<div class=" flex-1">
-												<input
-													class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-													type="text"
-													placeholder={valves_spec.properties[property].title}
-													bind:value={valves[property]}
-													autocomplete="off"
-												/>
+						<div class="space-y-1">
+							{#if pipelines[selectedPipelineIdx].pipeline.valves}
+								{#if valves}
+									{#each Object.keys(valves_spec.properties) as property, idx}
+										<div class=" py-0.5 w-full justify-between">
+											<div class="flex w-full justify-between">
+												<div class=" self-center text-xs font-medium">
+													{valves_spec.properties[property].title}
+												</div>
+
+												<button
+													class="p-1 px-3 text-xs flex rounded transition"
+													type="button"
+													on:click={() => {
+														valves[property] = (valves[property] ?? null) === null ? '' : null;
+													}}
+												>
+													{#if (valves[property] ?? null) === null}
+														<span class="ml-2 self-center"> {$i18n.t('None')} </span>
+													{:else}
+														<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
+													{/if}
+												</button>
 											</div>
+
+											{#if (valves[property] ?? null) !== null}
+												<div class="flex mt-0.5 space-x-2">
+													<div class=" flex-1">
+														<input
+															class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+															type="text"
+															placeholder={valves_spec.properties[property].title}
+															bind:value={valves[property]}
+															autocomplete="off"
+														/>
+													</div>
+												</div>
+											{/if}
 										</div>
-									{/if}
-								</div>
-							{/each}
-						{:else}
-							<Spinner className="size-5" />
-						{/if}
-					{:else}
-						<div>No valves</div>
-					{/if}
+									{/each}
+								{:else}
+									<Spinner className="size-5" />
+								{/if}
+							{:else}
+								<div>No valves</div>
+							{/if}
+						</div>
+					</div>
+				{:else if pipelines.length === 0}
+					<div>Pipelines Not Detected</div>
+				{/if}
+			{:else}
+				<div class="flex justify-center">
+					<div class="my-auto">
+						<Spinner className="size-4" />
+					</div>
 				</div>
-			</div>
-		{:else if pipelines !== null && pipelines.length === 0}
-			<div>Pipelines Not Detected</div>
+			{/if}
 		{:else}
-			<div class="flex h-full justify-center">
+			<div class="flex justify-center h-full">
 				<div class="my-auto">
 					<Spinner className="size-6" />
 				</div>
 			</div>
 		{/if}
 	</div>
+
 	<div class="flex justify-end pt-3 text-sm font-medium">
 		<button
 			class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"