Commit c1cabf14 authored by Timothy J. Baek's avatar Timothy J. Baek
Browse files

feat: multiple pipelines server support

parent 2981caa3
...@@ -464,10 +464,37 @@ async def get_models(user=Depends(get_verified_user)): ...@@ -464,10 +464,37 @@ async def get_models(user=Depends(get_verified_user)):
return {"data": models} 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") @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() 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} return {"data": pipelines}
......
...@@ -49,10 +49,44 @@ export const getModels = async (token: string = '') => { ...@@ -49,10 +49,44 @@ export const getModels = async (token: string = '') => {
return models; return models;
}; };
export const getPipelines = async (token: string = '') => { export const getPipelinesList = async (token: string = '') => {
let error = null; 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', method: 'GET',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
......
<script lang="ts"> <script lang="ts">
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { toast } from 'svelte-sonner';
import { models } from '$lib/stores';
import { getContext, onMount, tick } from 'svelte'; import { getContext, onMount, tick } from 'svelte';
import type { Writable } from 'svelte/store'; import type { Writable } from 'svelte/store';
import type { i18n as i18nType } from 'i18next'; import type { i18n as i18nType } from 'i18next';
import { stringify } from 'postcss';
import { import {
getPipelineValves, getPipelineValves,
getPipelineValvesSpec, getPipelineValvesSpec,
updatePipelineValves, updatePipelineValves,
getPipelines, getPipelines,
getModels getModels,
getPipelinesList
} from '$lib/apis'; } from '$lib/apis';
import Spinner from '$lib/components/common/Spinner.svelte'; import Spinner from '$lib/components/common/Spinner.svelte';
import { toast } from 'svelte-sonner';
import { models } from '$lib/stores';
const i18n: Writable<i18nType> = getContext('i18n'); const i18n: Writable<i18nType> = getContext('i18n');
export let saveHandler: Function; export let saveHandler: Function;
let PIPELINES_LIST = null;
let selectedPipelinesUrlIdx = '';
let pipelines = null; let pipelines = null;
let valves = null; let valves = null;
let valves_spec = null; let valves_spec = null;
let selectedPipelineIdx = null; let selectedPipelineIdx = null;
const updateHandler = async () => { const updateHandler = async () => {
...@@ -54,6 +56,9 @@ ...@@ -54,6 +56,9 @@
}; };
const getValves = async (idx) => { const getValves = async (idx) => {
valves = null;
valves_spec = null;
valves_spec = await getPipelineValvesSpec(localStorage.token, pipelines[idx].id); valves_spec = await getPipelineValvesSpec(localStorage.token, pipelines[idx].id);
valves = await getPipelineValves(localStorage.token, pipelines[idx].id); valves = await getPipelineValves(localStorage.token, pipelines[idx].id);
...@@ -63,7 +68,11 @@ ...@@ -63,7 +68,11 @@
}; };
const setPipelines = async () => { 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) { if (pipelines.length > 0) {
selectedPipelineIdx = 0; selectedPipelineIdx = 0;
...@@ -72,6 +81,12 @@ ...@@ -72,6 +81,12 @@
}; };
onMount(async () => { onMount(async () => {
PIPELINES_LIST = await getPipelinesList(localStorage.token);
if (PIPELINES_LIST.length > 0) {
selectedPipelinesUrlIdx = PIPELINES_LIST[0]['idx'];
}
setPipelines(); setPipelines();
}); });
</script> </script>
...@@ -83,95 +98,140 @@ ...@@ -83,95 +98,140 @@
}} }}
> >
<div class=" space-y-2 pr-1.5 overflow-y-scroll max-h-80 h-full"> <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="flex w-full justify-between mb-2">
<div class=" self-center text-sm font-semibold"> <div class=" self-center text-sm font-semibold">
{$i18n.t('Pipelines')} {$i18n.t('Manage Pipelines')}
</div> </div>
</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 gap-2">
<div class="flex-1 pb-1"> <div class="flex-1">
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedPipelineIdx} bind:value={selectedPipelinesUrlIdx}
placeholder={$i18n.t('Select a pipeline')} placeholder={$i18n.t('Select a pipeline url')}
on:change={async () => { on:change={async () => {
await tick(); await tick();
await getValves(selectedPipelineIdx); await setPipelines();
}} }}
> >
{#each pipelines as pipeline, idx} <option value="" selected disabled class="bg-gray-100 dark:bg-gray-700"
<option value={idx} class="bg-gray-100 dark:bg-gray-700" >{$i18n.t('Select a pipeline url')}</option
>{pipeline.name} ({pipeline.pipeline.type ?? 'pipe'})</option >
{#each PIPELINES_LIST as pipelines, idx}
<option value={pipelines.idx} class="bg-gray-100 dark:bg-gray-700"
>{pipelines.url}</option
> >
{/each} {/each}
</select> </select>
</div> </div>
</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 !== null}
{#if pipelines[selectedPipelineIdx].pipeline.valves} {#if pipelines.length > 0}
{#if valves} <div class="flex w-full justify-between mb-2">
{#each Object.keys(valves_spec.properties) as property, idx} <div class=" self-center text-sm font-semibold">
<div class=" py-0.5 w-full justify-between"> {$i18n.t('Pipelines Valves')}
<div class="flex w-full justify-between"> </div>
<div class=" self-center text-xs font-medium"> </div>
{valves_spec.properties[property].title} <div class="space-y-1">
</div> {#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 <div class="space-y-1">
class="p-1 px-3 text-xs flex rounded transition" {#if pipelines[selectedPipelineIdx].pipeline.valves}
type="button" {#if valves}
on:click={() => { {#each Object.keys(valves_spec.properties) as property, idx}
valves[property] = (valves[property] ?? null) === null ? '' : null; <div class=" py-0.5 w-full justify-between">
}} <div class="flex w-full justify-between">
> <div class=" self-center text-xs font-medium">
{#if (valves[property] ?? null) === null} {valves_spec.properties[property].title}
<span class="ml-2 self-center"> {$i18n.t('None')} </span> </div>
{:else}
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span> <button
{/if} class="p-1 px-3 text-xs flex rounded transition"
</button> type="button"
</div> on:click={() => {
valves[property] = (valves[property] ?? null) === null ? '' : null;
{#if (valves[property] ?? null) !== null} }}
<div class="flex mt-0.5 space-x-2"> >
<div class=" flex-1"> {#if (valves[property] ?? null) === null}
<input <span class="ml-2 self-center"> {$i18n.t('None')} </span>
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" {:else}
type="text" <span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
placeholder={valves_spec.properties[property].title} {/if}
bind:value={valves[property]} </button>
autocomplete="off"
/>
</div> </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> </div>
{/if} {/each}
</div> {:else}
{/each} <Spinner className="size-5" />
{:else} {/if}
<Spinner className="size-5" /> {:else}
{/if} <div>No valves</div>
{:else} {/if}
<div>No valves</div> </div>
{/if} </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>
</div> {/if}
{:else if pipelines !== null && pipelines.length === 0}
<div>Pipelines Not Detected</div>
{:else} {:else}
<div class="flex h-full justify-center"> <div class="flex justify-center h-full">
<div class="my-auto"> <div class="my-auto">
<Spinner className="size-6" /> <Spinner className="size-6" />
</div> </div>
</div> </div>
{/if} {/if}
</div> </div>
<div class="flex justify-end pt-3 text-sm font-medium"> <div class="flex justify-end pt-3 text-sm font-medium">
<button <button
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg" class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment