1
0
Timothy J. Baek 11 сар өмнө
parent
commit
130d15a2fb

+ 116 - 0
backend/main.py

@@ -471,6 +471,122 @@ async def get_pipelines(user=Depends(get_admin_user)):
     return {"data": pipelines}
 
 
+@app.get("/api/pipelines/{pipeline_id}/valves")
+async def get_pipeline_valves(pipeline_id: str, user=Depends(get_admin_user)):
+    models = await get_all_models()
+    if pipeline_id in app.state.MODELS and "pipeline" in app.state.MODELS[pipeline_id]:
+        pipeline = app.state.MODELS[pipeline_id]
+
+        try:
+            urlIdx = pipeline["urlIdx"]
+
+            url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+            key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+            if key != "":
+                headers = {"Authorization": f"Bearer {key}"}
+                r = requests.get(f"{url}/{pipeline['id']}/valves", headers=headers)
+
+                r.raise_for_status()
+                data = r.json()
+
+                return {**data}
+        except Exception as e:
+            # Handle connection error here
+            print(f"Connection error: {e}")
+
+            raise HTTPException(
+                status_code=status.HTTP_404_NOT_FOUND,
+                detail="Pipeline not found",
+            )
+
+        return {"data": pipeline["pipeline"]["valves"]}
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Pipeline not found",
+        )
+
+
+@app.get("/api/pipelines/{pipeline_id}/valves/spec")
+async def get_pipeline_valves_spec(pipeline_id: str, user=Depends(get_admin_user)):
+    models = await get_all_models()
+    if pipeline_id in app.state.MODELS and "pipeline" in app.state.MODELS[pipeline_id]:
+        pipeline = app.state.MODELS[pipeline_id]
+
+        try:
+            urlIdx = pipeline["urlIdx"]
+
+            url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+            key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+            if key != "":
+                headers = {"Authorization": f"Bearer {key}"}
+                r = requests.get(f"{url}/{pipeline['id']}/valves/spec", headers=headers)
+
+                r.raise_for_status()
+                data = r.json()
+
+                return {**data}
+        except Exception as e:
+            # Handle connection error here
+            print(f"Connection error: {e}")
+
+            raise HTTPException(
+                status_code=status.HTTP_404_NOT_FOUND,
+                detail="Pipeline not found",
+            )
+
+        return {"data": pipeline["pipeline"]["valves"]}
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Pipeline not found",
+        )
+
+
+@app.post("/api/pipelines/{pipeline_id}/valves/update")
+async def update_pipeline_valves(
+    pipeline_id: str, form_data: dict, user=Depends(get_admin_user)
+):
+    models = await get_all_models()
+
+    if pipeline_id in app.state.MODELS and "pipeline" in app.state.MODELS[pipeline_id]:
+        pipeline = app.state.MODELS[pipeline_id]
+
+        try:
+            urlIdx = pipeline["urlIdx"]
+
+            url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+            key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+            if key != "":
+                headers = {"Authorization": f"Bearer {key}"}
+                r = requests.post(
+                    f"{url}/{pipeline['id']}/valves/update",
+                    headers=headers,
+                    json={**form_data},
+                )
+
+                r.raise_for_status()
+                data = r.json()
+
+                return {**data}
+        except Exception as e:
+            # Handle connection error here
+            print(f"Connection error: {e}")
+
+            raise HTTPException(
+                status_code=status.HTTP_404_NOT_FOUND,
+                detail="Pipeline not found",
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Pipeline not found",
+        )
+
+
 @app.get("/api/config")
 async def get_app_config():
     # Checking and Handling the Absence of 'ui' in CONFIG_DATA

+ 89 - 0
src/lib/apis/index.ts

@@ -78,6 +78,95 @@ export const getPipelines = async (token: string = '') => {
 	return pipelines;
 };
 
+export const getPipelineValves = async (token: string = '', pipeline_id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/${pipeline_id}/valves`, {
+		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;
+	}
+
+	return res;
+};
+
+export const getPipelineValvesSpec = async (token: string = '', pipeline_id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/${pipeline_id}/valves/spec`, {
+		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;
+	}
+
+	return res;
+};
+
+export const updatePipelineValves = async (
+	token: string = '',
+	pipeline_id: string,
+	valves: object
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/${pipeline_id}/valves/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify(valves)
+	})
+		.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;
+	}
+
+	return res;
+};
+
 export const getBackendConfig = async () => {
 	let error = null;
 

+ 60 - 14
src/lib/components/admin/Settings/Pipelines.svelte

@@ -2,21 +2,33 @@
 	import { v4 as uuidv4 } from 'uuid';
 
 	import { getContext, onMount } from 'svelte';
-	import { models } from '$lib/stores';
 
 	import type { Writable } from 'svelte/store';
 	import type { i18n as i18nType } from 'i18next';
-	import Tooltip from '$lib/components/common/Tooltip.svelte';
-	import Switch from '$lib/components/common/Switch.svelte';
 	import { stringify } from 'postcss';
-	import { getPipelines } from '$lib/apis';
+	import { getPipelineValves, getPipelines } from '$lib/apis';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+
 	const i18n: Writable<i18nType> = getContext('i18n');
 
 	export let saveHandler: Function;
 
-	let pipelines = [];
+	let pipelines = null;
+	let valves = null;
+
 	let selectedPipelineIdx = 0;
 
+	$: if (
+		pipelines !== null &&
+		pipelines.length > 0 &&
+		pipelines[selectedPipelineIdx] !== undefined &&
+		pipelines[selectedPipelineIdx].pipeline.valves
+	) {
+		(async () => {
+			valves = await getPipelineValves(localStorage.token, pipelines[selectedPipelineIdx].id);
+		})();
+	}
+
 	onMount(async () => {
 		pipelines = await getPipelines(localStorage.token);
 	});
@@ -28,21 +40,55 @@
 		saveHandler();
 	}}
 >
-	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80 h-full">
-		<div class=" space-y-3 pr-1.5">
+	<div class=" space-y-2 pr-1.5 overflow-y-scroll max-h-80 h-full">
+		{#if pipelines !== null}
 			<div class="flex w-full justify-between mb-2">
 				<div class=" self-center text-sm font-semibold">
-					{$i18n.t('Pipeline Valves')}
+					{$i18n.t('Pipelines')}
 				</div>
 			</div>
-			<div class="flex flex-col space-y-1">
-				{#each pipelines as pipeline}
-					<div class=" flex justify-between">
-						{JSON.stringify(pipeline)}
+			<div class="space-y-2">
+				{#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 an Ollama instance')}
+							>
+								{#each pipelines as pipeline, idx}
+									<option value={idx} class="bg-gray-100 dark:bg-gray-700">{pipeline.name}</option>
+								{/each}
+							</select>
+						</div>
 					</div>
-				{/each}
+				{/if}
+
+				<div class="text-sm font-medium">{$i18n.t('Valves')}</div>
+
+				<div class="space-y-2">
+					{#if pipelines[selectedPipelineIdx].pipeline.valves}
+						{#if valves}
+							{#each Object.keys(valves) as valve, idx}
+								<div>{valve}</div>
+							{/each}
+						{:else}
+							<Spinner className="size-5" />
+						{/if}
+					{:else}
+						<div>No valves</div>
+					{/if}
+				</div>
+			</div>
+		{:else if pipelines !== null && pipelines.length === 0}
+			<div>Pipelines Not Detected</div>
+		{:else}
+			<div class="flex h-full justify-center">
+				<div class="my-auto">
+					<Spinner className="size-6" />
+				</div>
 			</div>
-		</div>
+		{/if}
 	</div>
 	<div class="flex justify-end pt-3 text-sm font-medium">
 		<button