Connections.svelte 5.06 KB
Newer Older
Timothy J. Baek's avatar
Timothy J. Baek committed
1
2
<script lang="ts">
	import { models, user } from '$lib/stores';
3
	import { createEventDispatcher, onMount, getContext } from 'svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
4
5
	const dispatch = createEventDispatcher();

Timothy J. Baek's avatar
Timothy J. Baek committed
6
	import { getOllamaAPIUrl, getOllamaVersion, updateOllamaAPIUrl } from '$lib/apis/ollama';
Timothy J. Baek's avatar
Timothy J. Baek committed
7
	import { getOpenAIKey, getOpenAIUrl, updateOpenAIKey, updateOpenAIUrl } from '$lib/apis/openai';
Jannik Streidl's avatar
Jannik Streidl committed
8
	import { toast } from 'svelte-sonner';
Timothy J. Baek's avatar
Timothy J. Baek committed
9

10
11
	const i18n = getContext('i18n');

Timothy J. Baek's avatar
Timothy J. Baek committed
12
13
14
	export let getModels: Function;

	// External
Timothy J. Baek's avatar
Timothy J. Baek committed
15
16
	let API_BASE_URL = '';

Timothy J. Baek's avatar
Timothy J. Baek committed
17
18
19
	let OPENAI_API_KEY = '';
	let OPENAI_API_BASE_URL = '';

20
21
22
	let showOpenAI = false;
	let showLiteLLM = false;

Timothy J. Baek's avatar
Timothy J. Baek committed
23
24
25
26
27
28
29
	const updateOpenAIHandler = async () => {
		OPENAI_API_BASE_URL = await updateOpenAIUrl(localStorage.token, OPENAI_API_BASE_URL);
		OPENAI_API_KEY = await updateOpenAIKey(localStorage.token, OPENAI_API_KEY);

		await models.set(await getModels());
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
30
31
32
	const updateOllamaAPIUrlHandler = async () => {
		API_BASE_URL = await updateOllamaAPIUrl(localStorage.token, API_BASE_URL);

Timothy J. Baek's avatar
Timothy J. Baek committed
33
		const ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => {
Timothy J. Baek's avatar
Timothy J. Baek committed
34
			toast.error(error);
Timothy J. Baek's avatar
Timothy J. Baek committed
35
36
37
38
			return null;
		});

		if (ollamaVersion) {
39
			toast.success($i18n.t('Server connection verified'));
Timothy J. Baek's avatar
Timothy J. Baek committed
40
			await models.set(await getModels());
Timothy J. Baek's avatar
Timothy J. Baek committed
41
42
43
		}
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
44
45
	onMount(async () => {
		if ($user.role === 'admin') {
Timothy J. Baek's avatar
Timothy J. Baek committed
46
			API_BASE_URL = await getOllamaAPIUrl(localStorage.token);
Timothy J. Baek's avatar
Timothy J. Baek committed
47
48
49
50
51
52
53
			OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token);
			OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
		}
	});
</script>

<form
54
	class="flex flex-col h-full justify-between text-sm"
Timothy J. Baek's avatar
Timothy J. Baek committed
55
56
57
58
59
60
61
62
63
64
	on:submit|preventDefault={() => {
		updateOpenAIHandler();
		dispatch('save');

		// saveSettings({
		// 	OPENAI_API_KEY: OPENAI_API_KEY !== '' ? OPENAI_API_KEY : undefined,
		// 	OPENAI_API_BASE_URL: OPENAI_API_BASE_URL !== '' ? OPENAI_API_BASE_URL : undefined
		// });
	}}
>
Timothy J. Baek's avatar
Timothy J. Baek committed
65
	<div class="  pr-1.5 overflow-y-scroll max-h-[20.5rem] space-y-3">
66
67
68
		<div class=" space-y-3">
			<div class="mt-2 space-y-2 pr-1.5">
				<div class="flex justify-between items-center text-sm">
69
					<div class="  font-medium">{$i18n.t('OpenAI API')}</div>
70
71
72
73
74
75
76
77
					<button
						class=" text-xs font-medium text-gray-500"
						type="button"
						on:click={() => {
							showOpenAI = !showOpenAI;
						}}>{showOpenAI ? 'Hide' : 'Show'}</button
					>
				</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
78

79
80
				{#if showOpenAI}
					<div>
81
						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Key')}</div>
82
83
84
85
86
87
88
89
90
91
92
93
94
						<div class="flex w-full">
							<div class="flex-1">
								<input
									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
									placeholder="Enter OpenAI API Key"
									bind:value={OPENAI_API_KEY}
									autocomplete="off"
								/>
							</div>
						</div>
					</div>

					<div>
95
						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Base URL')}</div>
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
						<div class="flex w-full">
							<div class="flex-1">
								<input
									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
									placeholder="Enter OpenAI API Base URL"
									bind:value={OPENAI_API_BASE_URL}
									autocomplete="off"
								/>
							</div>
						</div>
						<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
							WebUI will make requests to <span class=" text-gray-200"
								>'{OPENAI_API_BASE_URL}/chat'</span
							>
						</div>
					</div>
				{/if}
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
114
115
		</div>

116
		<hr class=" dark:border-gray-700" />
Timothy J. Baek's avatar
Timothy J. Baek committed
117

Timothy J. Baek's avatar
Timothy J. Baek committed
118
		<div>
119
			<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Ollama API URL')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
120
			<div class="flex w-full">
121
				<div class="flex-1 mr-2">
Timothy J. Baek's avatar
Timothy J. Baek committed
122
123
					<input
						class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
124
125
						placeholder="Enter URL (e.g. http://localhost:11434/api)"
						bind:value={API_BASE_URL}
Timothy J. Baek's avatar
Timothy J. Baek committed
126
127
					/>
				</div>
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
				<button
					class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 rounded transition"
					on:click={() => {
						updateOllamaAPIUrlHandler();
					}}
					type="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>
Timothy J. Baek's avatar
Timothy J. Baek committed
148
149
150
			</div>

			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
151
152
153
154
155
156
157
158
				Trouble accessing Ollama?
				<a
					class=" text-gray-300 font-medium"
					href="https://github.com/open-webui/open-webui#troubleshooting"
					target="_blank"
				>
					Click here for help.
				</a>
Timothy J. Baek's avatar
Timothy J. Baek committed
159
160
161
162
163
164
165
166
167
168
169
170
171
			</div>
		</div>
	</div>

	<div class="flex justify-end pt-3 text-sm font-medium">
		<button
			class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
			type="submit"
		>
			Save
		</button>
	</div>
</form>