Browse Source

feat: toggleable ollama

Timothy J. Baek 11 months ago
parent
commit
21ca55dd30

+ 34 - 7
backend/apps/ollama/main.py

@@ -43,6 +43,7 @@ from utils.utils import (
 from config import (
 from config import (
     SRC_LOG_LEVELS,
     SRC_LOG_LEVELS,
     OLLAMA_BASE_URLS,
     OLLAMA_BASE_URLS,
+    ENABLE_OLLAMA_API,
     ENABLE_MODEL_FILTER,
     ENABLE_MODEL_FILTER,
     MODEL_FILTER_LIST,
     MODEL_FILTER_LIST,
     UPLOAD_DIR,
     UPLOAD_DIR,
@@ -67,6 +68,8 @@ app.state.config = AppConfig()
 app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
 app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
 app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
 app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
 
 
+
+app.state.config.ENABLE_OLLAMA_API = ENABLE_OLLAMA_API
 app.state.config.OLLAMA_BASE_URLS = OLLAMA_BASE_URLS
 app.state.config.OLLAMA_BASE_URLS = OLLAMA_BASE_URLS
 app.state.MODELS = {}
 app.state.MODELS = {}
 
 
@@ -96,6 +99,21 @@ async def get_status():
     return {"status": True}
     return {"status": True}
 
 
 
 
+@app.get("/config")
+async def get_config(user=Depends(get_admin_user)):
+    return {"ENABLE_OLLAMA_API": app.state.config.ENABLE_OLLAMA_API}
+
+
+class OllamaConfigForm(BaseModel):
+    enable_ollama_api: Optional[bool] = None
+
+
+@app.post("/config/update")
+async def update_config(form_data: OllamaConfigForm, user=Depends(get_admin_user)):
+    app.state.config.ENABLE_OLLAMA_API = form_data.enable_ollama_api
+    return {"ENABLE_OLLAMA_API": app.state.config.ENABLE_OLLAMA_API}
+
+
 @app.get("/urls")
 @app.get("/urls")
 async def get_ollama_api_urls(user=Depends(get_admin_user)):
 async def get_ollama_api_urls(user=Depends(get_admin_user)):
     return {"OLLAMA_BASE_URLS": app.state.config.OLLAMA_BASE_URLS}
     return {"OLLAMA_BASE_URLS": app.state.config.OLLAMA_BASE_URLS}
@@ -156,14 +174,23 @@ def merge_models_lists(model_lists):
 
 
 async def get_all_models():
 async def get_all_models():
     log.info("get_all_models()")
     log.info("get_all_models()")
-    tasks = [fetch_url(f"{url}/api/tags") for url in app.state.config.OLLAMA_BASE_URLS]
-    responses = await asyncio.gather(*tasks)
 
 
-    models = {
-        "models": merge_models_lists(
-            map(lambda response: response["models"] if response else None, responses)
-        )
-    }
+    if app.state.config.ENABLE_OLLAMA_API:
+        tasks = [
+            fetch_url(f"{url}/api/tags") for url in app.state.config.OLLAMA_BASE_URLS
+        ]
+        responses = await asyncio.gather(*tasks)
+
+        models = {
+            "models": merge_models_lists(
+                map(
+                    lambda response: response["models"] if response else None, responses
+                )
+            )
+        }
+
+    else:
+        models = {"models": []}
 
 
     app.state.MODELS = {model["model"]: model for model in models["models"]}
     app.state.MODELS = {model["model"]: model for model in models["models"]}
 
 

+ 7 - 0
backend/config.py

@@ -384,6 +384,13 @@ if not os.path.exists(LITELLM_CONFIG_PATH):
 # OLLAMA_BASE_URL
 # OLLAMA_BASE_URL
 ####################################
 ####################################
 
 
+
+ENABLE_OLLAMA_API = PersistentConfig(
+    "ENABLE_OLLAMA_API",
+    "ollama.enable",
+    os.environ.get("ENABLE_OLLAMA_API", "True").lower() == "true",
+)
+
 OLLAMA_API_BASE_URL = os.environ.get(
 OLLAMA_API_BASE_URL = os.environ.get(
     "OLLAMA_API_BASE_URL", "http://localhost:11434/api"
     "OLLAMA_API_BASE_URL", "http://localhost:11434/api"
 )
 )

+ 67 - 0
src/lib/apis/ollama/index.ts

@@ -1,6 +1,73 @@
 import { OLLAMA_API_BASE_URL } from '$lib/constants';
 import { OLLAMA_API_BASE_URL } from '$lib/constants';
 import { promptTemplate } from '$lib/utils';
 import { promptTemplate } from '$lib/utils';
 
 
+export const getOllamaConfig = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/config`, {
+		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);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateOllamaConfig = async (token: string = '', enable_ollama_api: boolean) => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/config/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			enable_ollama_api: enable_ollama_api
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
 export const getOllamaUrls = async (token: string = '') => {
 export const getOllamaUrls = async (token: string = '') => {
 	let error = null;
 	let error = null;
 
 

+ 108 - 85
src/lib/components/chat/Settings/Connections.svelte

@@ -3,7 +3,13 @@
 	import { createEventDispatcher, onMount, getContext } from 'svelte';
 	import { createEventDispatcher, onMount, getContext } from 'svelte';
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
 
 
-	import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama';
+	import {
+		getOllamaConfig,
+		getOllamaUrls,
+		getOllamaVersion,
+		updateOllamaConfig,
+		updateOllamaUrls
+	} from '$lib/apis/ollama';
 	import {
 	import {
 		getOpenAIConfig,
 		getOpenAIConfig,
 		getOpenAIKeys,
 		getOpenAIKeys,
@@ -26,6 +32,7 @@
 	let OPENAI_API_BASE_URLS = [''];
 	let OPENAI_API_BASE_URLS = [''];
 
 
 	let ENABLE_OPENAI_API = false;
 	let ENABLE_OPENAI_API = false;
+	let ENABLE_OLLAMA_API = false;
 
 
 	const updateOpenAIHandler = async () => {
 	const updateOpenAIHandler = async () => {
 		OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS);
 		OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS);
@@ -50,10 +57,13 @@
 
 
 	onMount(async () => {
 	onMount(async () => {
 		if ($user.role === 'admin') {
 		if ($user.role === 'admin') {
-			OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
+			const ollamaConfig = await getOllamaConfig(localStorage.token);
+			const openaiConfig = await getOpenAIConfig(localStorage.token);
 
 
-			const config = await getOpenAIConfig(localStorage.token);
-			ENABLE_OPENAI_API = config.ENABLE_OPENAI_API;
+			ENABLE_OPENAI_API = openaiConfig.ENABLE_OPENAI_API;
+			ENABLE_OLLAMA_API = ollamaConfig.ENABLE_OLLAMA_API;
+
+			OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
 
 
 			OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token);
 			OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token);
 			OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token);
 			OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token);
@@ -161,95 +171,108 @@
 
 
 		<hr class=" dark:border-gray-700" />
 		<hr class=" dark:border-gray-700" />
 
 
-		<div>
-			<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Ollama Base URL')}</div>
-			<div class="flex w-full gap-1.5">
-				<div class="flex-1 flex flex-col gap-2">
-					{#each OLLAMA_BASE_URLS as url, idx}
-						<div class="flex gap-1.5">
-							<input
-								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-								placeholder={$i18n.t('Enter URL (e.g. http://localhost:11434)')}
-								bind:value={url}
-							/>
+		<div class="pr-1.5 space-y-2">
+			<div class="flex justify-between items-center text-sm">
+				<div class="  font-medium">{$i18n.t('Ollama API')}</div>
+
+				<div class="mt-1">
+					<Switch
+						bind:state={ENABLE_OLLAMA_API}
+						on:change={async () => {
+							updateOllamaConfig(localStorage.token, ENABLE_OLLAMA_API);
+						}}
+					/>
+				</div>
+			</div>
+			{#if ENABLE_OLLAMA_API}
+				<div class="flex w-full gap-1.5">
+					<div class="flex-1 flex flex-col gap-2">
+						{#each OLLAMA_BASE_URLS as url, idx}
+							<div class="flex gap-1.5">
+								<input
+									class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+									placeholder={$i18n.t('Enter URL (e.g. http://localhost:11434)')}
+									bind:value={url}
+								/>
 
 
-							<div class="self-center flex items-center">
-								{#if idx === 0}
-									<button
-										class="px-1"
-										on:click={() => {
-											OLLAMA_BASE_URLS = [...OLLAMA_BASE_URLS, ''];
-										}}
-										type="button"
-									>
-										<svg
-											xmlns="http://www.w3.org/2000/svg"
-											viewBox="0 0 16 16"
-											fill="currentColor"
-											class="w-4 h-4"
+								<div class="self-center flex items-center">
+									{#if idx === 0}
+										<button
+											class="px-1"
+											on:click={() => {
+												OLLAMA_BASE_URLS = [...OLLAMA_BASE_URLS, ''];
+											}}
+											type="button"
 										>
 										>
-											<path
-												d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
-											/>
-										</svg>
-									</button>
-								{:else}
-									<button
-										class="px-1"
-										on:click={() => {
-											OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter((url, urlIdx) => idx !== urlIdx);
-										}}
-										type="button"
-									>
-										<svg
-											xmlns="http://www.w3.org/2000/svg"
-											viewBox="0 0 16 16"
-											fill="currentColor"
-											class="w-4 h-4"
+											<svg
+												xmlns="http://www.w3.org/2000/svg"
+												viewBox="0 0 16 16"
+												fill="currentColor"
+												class="w-4 h-4"
+											>
+												<path
+													d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
+												/>
+											</svg>
+										</button>
+									{:else}
+										<button
+											class="px-1"
+											on:click={() => {
+												OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter((url, urlIdx) => idx !== urlIdx);
+											}}
+											type="button"
 										>
 										>
-											<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
-										</svg>
-									</button>
-								{/if}
+											<svg
+												xmlns="http://www.w3.org/2000/svg"
+												viewBox="0 0 16 16"
+												fill="currentColor"
+												class="w-4 h-4"
+											>
+												<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
+											</svg>
+										</button>
+									{/if}
+								</div>
 							</div>
 							</div>
-						</div>
-					{/each}
-				</div>
+						{/each}
+					</div>
 
 
-				<div class="">
-					<button
-						class="p-2.5 bg-gray-200 hover:bg-gray-300 dark:bg-gray-850 dark:hover:bg-gray-800 rounded-lg transition"
-						on:click={() => {
-							updateOllamaUrlsHandler();
-						}}
-						type="button"
-					>
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							viewBox="0 0 20 20"
-							fill="currentColor"
-							class="w-4 h-4"
+					<div class="flex">
+						<button
+							class="self-center p-2 bg-gray-200 hover:bg-gray-300 dark:bg-gray-900 dark:hover:bg-gray-850 rounded-lg transition"
+							on:click={() => {
+								updateOllamaUrlsHandler();
+							}}
+							type="button"
 						>
 						>
-							<path
-								fill-rule="evenodd"
-								d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
-								clip-rule="evenodd"
-							/>
-						</svg>
-					</button>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 20 20"
+								fill="currentColor"
+								class="w-4 h-4"
+							>
+								<path
+									fill-rule="evenodd"
+									d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
+									clip-rule="evenodd"
+								/>
+							</svg>
+						</button>
+					</div>
 				</div>
 				</div>
-			</div>
 
 
-			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
-				{$i18n.t('Trouble accessing Ollama?')}
-				<a
-					class=" text-gray-300 font-medium underline"
-					href="https://github.com/open-webui/open-webui#troubleshooting"
-					target="_blank"
-				>
-					{$i18n.t('Click here for help.')}
-				</a>
-			</div>
+				<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
+					{$i18n.t('Trouble accessing Ollama?')}
+					<a
+						class=" text-gray-300 font-medium underline"
+						href="https://github.com/open-webui/open-webui#troubleshooting"
+						target="_blank"
+					>
+						{$i18n.t('Click here for help.')}
+					</a>
+				</div>
+			{/if}
 		</div>
 		</div>
 	</div>
 	</div>