forked from open-webui/open-webui
		
	Merge branch 'main' into bun
This commit is contained in:
		
						commit
						8480a9b418
					
				
					 16 changed files with 543 additions and 445 deletions
				
			
		|  | @ -1,12 +1,76 @@ | |||
| import { OLLAMA_API_BASE_URL } from '$lib/constants'; | ||||
| 
 | ||||
| export const getOllamaVersion = async ( | ||||
| 	base_url: string = OLLAMA_API_BASE_URL, | ||||
| 	token: string = '' | ||||
| ) => { | ||||
| export const getOllamaAPIUrl = async (token: string = '') => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${base_url}/version`, { | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/url`, { | ||||
| 		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.OLLAMA_API_BASE_URL; | ||||
| }; | ||||
| 
 | ||||
| export const updateOllamaAPIUrl = async (token: string = '', url: string) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/url/update`, { | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			Accept: 'application/json', | ||||
| 			'Content-Type': 'application/json', | ||||
| 			...(token && { authorization: `Bearer ${token}` }) | ||||
| 		}, | ||||
| 		body: JSON.stringify({ | ||||
| 			url: url | ||||
| 		}) | ||||
| 	}) | ||||
| 		.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.OLLAMA_API_BASE_URL; | ||||
| }; | ||||
| 
 | ||||
| export const getOllamaVersion = async (token: string = '') => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/version`, { | ||||
| 		method: 'GET', | ||||
| 		headers: { | ||||
| 			Accept: 'application/json', | ||||
|  | @ -35,13 +99,10 @@ export const getOllamaVersion = async ( | |||
| 	return res?.version ?? ''; | ||||
| }; | ||||
| 
 | ||||
| export const getOllamaModels = async ( | ||||
| 	base_url: string = OLLAMA_API_BASE_URL, | ||||
| 	token: string = '' | ||||
| ) => { | ||||
| export const getOllamaModels = async (token: string = '') => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${base_url}/tags`, { | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/tags`, { | ||||
| 		method: 'GET', | ||||
| 		headers: { | ||||
| 			Accept: 'application/json', | ||||
|  | @ -67,18 +128,15 @@ export const getOllamaModels = async ( | |||
| 		throw error; | ||||
| 	} | ||||
| 
 | ||||
| 	return res?.models ?? []; | ||||
| 	return (res?.models ?? []).sort((a, b) => { | ||||
| 		return a.name.localeCompare(b.name); | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| export const generateTitle = async ( | ||||
| 	base_url: string = OLLAMA_API_BASE_URL, | ||||
| 	token: string = '', | ||||
| 	model: string, | ||||
| 	prompt: string | ||||
| ) => { | ||||
| export const generateTitle = async (token: string = '', model: string, prompt: string) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${base_url}/generate`, { | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/generate`, { | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			'Content-Type': 'text/event-stream', | ||||
|  | @ -109,14 +167,10 @@ export const generateTitle = async ( | |||
| 	return res?.response ?? 'New Chat'; | ||||
| }; | ||||
| 
 | ||||
| export const generateChatCompletion = async ( | ||||
| 	base_url: string = OLLAMA_API_BASE_URL, | ||||
| 	token: string = '', | ||||
| 	body: object | ||||
| ) => { | ||||
| export const generateChatCompletion = async (token: string = '', body: object) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${base_url}/chat`, { | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/chat`, { | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			'Content-Type': 'text/event-stream', | ||||
|  | @ -135,15 +189,10 @@ export const generateChatCompletion = async ( | |||
| 	return res; | ||||
| }; | ||||
| 
 | ||||
| export const createModel = async ( | ||||
| 	base_url: string = OLLAMA_API_BASE_URL, | ||||
| 	token: string, | ||||
| 	tagName: string, | ||||
| 	content: string | ||||
| ) => { | ||||
| export const createModel = async (token: string, tagName: string, content: string) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${base_url}/create`, { | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/create`, { | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			'Content-Type': 'text/event-stream', | ||||
|  | @ -165,14 +214,10 @@ export const createModel = async ( | |||
| 	return res; | ||||
| }; | ||||
| 
 | ||||
| export const deleteModel = async ( | ||||
| 	base_url: string = OLLAMA_API_BASE_URL, | ||||
| 	token: string, | ||||
| 	tagName: string | ||||
| ) => { | ||||
| export const deleteModel = async (token: string, tagName: string) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${base_url}/delete`, { | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/delete`, { | ||||
| 		method: 'DELETE', | ||||
| 		headers: { | ||||
| 			'Content-Type': 'text/event-stream', | ||||
|  | @ -202,3 +247,27 @@ export const deleteModel = async ( | |||
| 
 | ||||
| 	return res; | ||||
| }; | ||||
| 
 | ||||
| export const pullModel = async (token: string, tagName: string) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/pull`, { | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			'Content-Type': 'text/event-stream', | ||||
| 			Authorization: `Bearer ${token}` | ||||
| 		}, | ||||
| 		body: JSON.stringify({ | ||||
| 			name: tagName | ||||
| 		}) | ||||
| 	}).catch((err) => { | ||||
| 		error = err; | ||||
| 		return null; | ||||
| 	}); | ||||
| 
 | ||||
| 	if (error) { | ||||
| 		throw error; | ||||
| 	} | ||||
| 
 | ||||
| 	return res; | ||||
| }; | ||||
|  |  | |||
|  | @ -29,5 +29,8 @@ export const getOpenAIModels = async ( | |||
| 
 | ||||
| 	return models | ||||
| 		.map((model) => ({ name: model.id, external: true })) | ||||
| 		.filter((model) => (base_url.includes('openai') ? model.name.includes('gpt') : true)); | ||||
| 		.filter((model) => (base_url.includes('openai') ? model.name.includes('gpt') : true)) | ||||
| 		.sort((a, b) => { | ||||
| 			return a.name.localeCompare(b.name); | ||||
| 		}); | ||||
| }; | ||||
|  |  | |||
|  | @ -7,19 +7,23 @@ | |||
| 	import { config, models, settings, user, chats } from '$lib/stores'; | ||||
| 	import { splitStream, getGravatarURL } from '$lib/utils'; | ||||
| 
 | ||||
| 	import { getOllamaVersion } from '$lib/apis/ollama'; | ||||
| 	import { createNewChat, deleteAllChats, getAllChats, getChatList } from '$lib/apis/chats'; | ||||
| 	import { | ||||
| 		WEB_UI_VERSION, | ||||
| 		OLLAMA_API_BASE_URL, | ||||
| 		WEBUI_API_BASE_URL, | ||||
| 		WEBUI_BASE_URL | ||||
| 	} from '$lib/constants'; | ||||
| 		getOllamaVersion, | ||||
| 		getOllamaModels, | ||||
| 		getOllamaAPIUrl, | ||||
| 		updateOllamaAPIUrl, | ||||
| 		pullModel, | ||||
| 		createModel, | ||||
| 		deleteModel | ||||
| 	} from '$lib/apis/ollama'; | ||||
| 	import { createNewChat, deleteAllChats, getAllChats, getChatList } from '$lib/apis/chats'; | ||||
| 	import { WEB_UI_VERSION, WEBUI_API_BASE_URL } from '$lib/constants'; | ||||
| 
 | ||||
| 	import Advanced from './Settings/Advanced.svelte'; | ||||
| 	import Modal from '../common/Modal.svelte'; | ||||
| 	import { updateUserPassword } from '$lib/apis/auths'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| 	import Page from '../../../routes/(app)/+page.svelte'; | ||||
| 
 | ||||
| 	export let show = false; | ||||
| 
 | ||||
|  | @ -33,7 +37,7 @@ | |||
| 	let selectedTab = 'general'; | ||||
| 
 | ||||
| 	// General | ||||
| 	let API_BASE_URL = OLLAMA_API_BASE_URL; | ||||
| 	let API_BASE_URL = ''; | ||||
| 	let themes = ['dark', 'light', 'rose-pine dark', 'rose-pine-dawn light']; | ||||
| 	let theme = 'dark'; | ||||
| 	let notificationEnabled = false; | ||||
|  | @ -139,19 +143,13 @@ | |||
| 	// About | ||||
| 	let ollamaVersion = ''; | ||||
| 
 | ||||
| 	const checkOllamaConnection = async () => { | ||||
| 		if (API_BASE_URL === '') { | ||||
| 			API_BASE_URL = OLLAMA_API_BASE_URL; | ||||
| 		} | ||||
| 		const _models = await getModels(API_BASE_URL, 'ollama'); | ||||
| 	const updateOllamaAPIUrlHandler = async () => { | ||||
| 		API_BASE_URL = await updateOllamaAPIUrl(localStorage.token, API_BASE_URL); | ||||
| 		const _models = await getModels('ollama'); | ||||
| 
 | ||||
| 		if (_models.length > 0) { | ||||
| 			toast.success('Server connection verified'); | ||||
| 			await models.set(_models); | ||||
| 
 | ||||
| 			saveSettings({ | ||||
| 				API_BASE_URL: API_BASE_URL | ||||
| 			}); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -229,67 +227,60 @@ | |||
| 
 | ||||
| 	const pullModelHandler = async () => { | ||||
| 		modelTransferring = true; | ||||
| 		const res = await fetch(`${API_BASE_URL}/pull`, { | ||||
| 			method: 'POST', | ||||
| 			headers: { | ||||
| 				'Content-Type': 'text/event-stream', | ||||
| 				...($settings.authHeader && { Authorization: $settings.authHeader }), | ||||
| 				...($user && { Authorization: `Bearer ${localStorage.token}` }) | ||||
| 			}, | ||||
| 			body: JSON.stringify({ | ||||
| 				name: modelTag | ||||
| 			}) | ||||
| 		}); | ||||
| 
 | ||||
| 		const reader = res.body | ||||
| 			.pipeThrough(new TextDecoderStream()) | ||||
| 			.pipeThrough(splitStream('\n')) | ||||
| 			.getReader(); | ||||
| 		const res = await pullModel(localStorage.token, modelTag); | ||||
| 
 | ||||
| 		while (true) { | ||||
| 			const { value, done } = await reader.read(); | ||||
| 			if (done) break; | ||||
| 		if (res) { | ||||
| 			const reader = res.body | ||||
| 				.pipeThrough(new TextDecoderStream()) | ||||
| 				.pipeThrough(splitStream('\n')) | ||||
| 				.getReader(); | ||||
| 
 | ||||
| 			try { | ||||
| 				let lines = value.split('\n'); | ||||
| 			while (true) { | ||||
| 				const { value, done } = await reader.read(); | ||||
| 				if (done) break; | ||||
| 
 | ||||
| 				for (const line of lines) { | ||||
| 					if (line !== '') { | ||||
| 						console.log(line); | ||||
| 						let data = JSON.parse(line); | ||||
| 						console.log(data); | ||||
| 				try { | ||||
| 					let lines = value.split('\n'); | ||||
| 
 | ||||
| 						if (data.error) { | ||||
| 							throw data.error; | ||||
| 						} | ||||
| 					for (const line of lines) { | ||||
| 						if (line !== '') { | ||||
| 							console.log(line); | ||||
| 							let data = JSON.parse(line); | ||||
| 							console.log(data); | ||||
| 
 | ||||
| 						if (data.detail) { | ||||
| 							throw data.detail; | ||||
| 						} | ||||
| 						if (data.status) { | ||||
| 							if (!data.digest) { | ||||
| 								toast.success(data.status); | ||||
| 							if (data.error) { | ||||
| 								throw data.error; | ||||
| 							} | ||||
| 
 | ||||
| 								if (data.status === 'success') { | ||||
| 									const notification = new Notification(`Ollama`, { | ||||
| 										body: `Model '${modelTag}' has been successfully downloaded.`, | ||||
| 										icon: '/favicon.png' | ||||
| 									}); | ||||
| 								} | ||||
| 							} else { | ||||
| 								digest = data.digest; | ||||
| 								if (data.completed) { | ||||
| 									pullProgress = Math.round((data.completed / data.total) * 1000) / 10; | ||||
| 							if (data.detail) { | ||||
| 								throw data.detail; | ||||
| 							} | ||||
| 							if (data.status) { | ||||
| 								if (!data.digest) { | ||||
| 									toast.success(data.status); | ||||
| 
 | ||||
| 									if (data.status === 'success') { | ||||
| 										const notification = new Notification(`Ollama`, { | ||||
| 											body: `Model '${modelTag}' has been successfully downloaded.`, | ||||
| 											icon: '/favicon.png' | ||||
| 										}); | ||||
| 									} | ||||
| 								} else { | ||||
| 									pullProgress = 100; | ||||
| 									digest = data.digest; | ||||
| 									if (data.completed) { | ||||
| 										pullProgress = Math.round((data.completed / data.total) * 1000) / 10; | ||||
| 									} else { | ||||
| 										pullProgress = 100; | ||||
| 									} | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} catch (error) { | ||||
| 					console.log(error); | ||||
| 					toast.error(error); | ||||
| 				} | ||||
| 			} catch (error) { | ||||
| 				console.log(error); | ||||
| 				toast.error(error); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -410,21 +401,11 @@ | |||
| 		} | ||||
| 
 | ||||
| 		if (uploaded) { | ||||
| 			const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/create`, { | ||||
| 				method: 'POST', | ||||
| 				headers: { | ||||
| 					'Content-Type': 'text/event-stream', | ||||
| 					...($settings.authHeader && { Authorization: $settings.authHeader }), | ||||
| 					...($user && { Authorization: `Bearer ${localStorage.token}` }) | ||||
| 				}, | ||||
| 				body: JSON.stringify({ | ||||
| 					name: `${name}:latest`, | ||||
| 					modelfile: `FROM @${modelFileDigest}\n${modelFileContent}` | ||||
| 				}) | ||||
| 			}).catch((err) => { | ||||
| 				console.log(err); | ||||
| 				return null; | ||||
| 			}); | ||||
| 			const res = await createModel( | ||||
| 				localStorage.token, | ||||
| 				`${name}:latest`, | ||||
| 				`FROM @${modelFileDigest}\n${modelFileContent}` | ||||
| 			); | ||||
| 
 | ||||
| 			if (res && res.ok) { | ||||
| 				const reader = res.body | ||||
|  | @ -490,92 +471,33 @@ | |||
| 	}; | ||||
| 
 | ||||
| 	const deleteModelHandler = async () => { | ||||
| 		const res = await fetch(`${API_BASE_URL}/delete`, { | ||||
| 			method: 'DELETE', | ||||
| 			headers: { | ||||
| 				'Content-Type': 'text/event-stream', | ||||
| 				...($settings.authHeader && { Authorization: $settings.authHeader }), | ||||
| 				...($user && { Authorization: `Bearer ${localStorage.token}` }) | ||||
| 			}, | ||||
| 			body: JSON.stringify({ | ||||
| 				name: deleteModelTag | ||||
| 			}) | ||||
| 		const res = await deleteModel(localStorage.token, deleteModelTag).catch((error) => { | ||||
| 			toast.error(error); | ||||
| 		}); | ||||
| 
 | ||||
| 		const reader = res.body | ||||
| 			.pipeThrough(new TextDecoderStream()) | ||||
| 			.pipeThrough(splitStream('\n')) | ||||
| 			.getReader(); | ||||
| 
 | ||||
| 		while (true) { | ||||
| 			const { value, done } = await reader.read(); | ||||
| 			if (done) break; | ||||
| 
 | ||||
| 			try { | ||||
| 				let lines = value.split('\n'); | ||||
| 
 | ||||
| 				for (const line of lines) { | ||||
| 					if (line !== '' && line !== 'null') { | ||||
| 						console.log(line); | ||||
| 						let data = JSON.parse(line); | ||||
| 						console.log(data); | ||||
| 
 | ||||
| 						if (data.error) { | ||||
| 							throw data.error; | ||||
| 						} | ||||
| 						if (data.detail) { | ||||
| 							throw data.detail; | ||||
| 						} | ||||
| 
 | ||||
| 						if (data.status) { | ||||
| 						} | ||||
| 					} else { | ||||
| 						toast.success(`Deleted ${deleteModelTag}`); | ||||
| 					} | ||||
| 				} | ||||
| 			} catch (error) { | ||||
| 				console.log(error); | ||||
| 				toast.error(error); | ||||
| 			} | ||||
| 		if (res) { | ||||
| 			toast.success(`Deleted ${deleteModelTag}`); | ||||
| 		} | ||||
| 
 | ||||
| 		deleteModelTag = ''; | ||||
| 		models.set(await getModels()); | ||||
| 	}; | ||||
| 
 | ||||
| 	const getModels = async (url = '', type = 'all') => { | ||||
| 	const getModels = async (type = 'all') => { | ||||
| 		let models = []; | ||||
| 		const res = await fetch(`${url ? url : $settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/tags`, { | ||||
| 			method: 'GET', | ||||
| 			headers: { | ||||
| 				Accept: 'application/json', | ||||
| 				'Content-Type': 'application/json', | ||||
| 				...($settings.authHeader && { Authorization: $settings.authHeader }), | ||||
| 				...($user && { Authorization: `Bearer ${localStorage.token}` }) | ||||
| 			} | ||||
| 		}) | ||||
| 			.then(async (res) => { | ||||
| 				if (!res.ok) throw await res.json(); | ||||
| 				return res.json(); | ||||
| 			}) | ||||
| 			.catch((error) => { | ||||
| 				console.log(error); | ||||
| 				if ('detail' in error) { | ||||
| 					toast.error(error.detail); | ||||
| 				} else { | ||||
| 					toast.error('Server connection failed'); | ||||
| 				} | ||||
| 				return null; | ||||
| 			}); | ||||
| 		console.log(res); | ||||
| 		models.push(...(res?.models ?? [])); | ||||
| 		models.push( | ||||
| 			...(await getOllamaModels(localStorage.token).catch((error) => { | ||||
| 				toast.error(error); | ||||
| 				return []; | ||||
| 			})) | ||||
| 		); | ||||
| 
 | ||||
| 		// If OpenAI API Key exists | ||||
| 		if (type === 'all' && $settings.OPENAI_API_KEY) { | ||||
| 			const API_BASE_URL = $settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1'; | ||||
| 			const OPENAI_API_BASE_URL = $settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1'; | ||||
| 
 | ||||
| 			// Validate OPENAI_API_KEY | ||||
| 			const openaiModelRes = await fetch(`${API_BASE_URL}/models`, { | ||||
| 			const openaiModelRes = await fetch(`${OPENAI_API_BASE_URL}/models`, { | ||||
| 				method: 'GET', | ||||
| 				headers: { | ||||
| 					'Content-Type': 'application/json', | ||||
|  | @ -603,7 +525,7 @@ | |||
| 							...openAIModels | ||||
| 								.map((model) => ({ name: model.id, external: true })) | ||||
| 								.filter((model) => | ||||
| 									API_BASE_URL.includes('openai') ? model.name.includes('gpt') : true | ||||
| 									OPENAI_API_BASE_URL.includes('openai') ? model.name.includes('gpt') : true | ||||
| 								) | ||||
| 					  ] | ||||
| 					: []) | ||||
|  | @ -639,15 +561,18 @@ | |||
| 	}; | ||||
| 
 | ||||
| 	onMount(async () => { | ||||
| 		console.log('settings', $user.role === 'admin'); | ||||
| 		if ($user.role === 'admin') { | ||||
| 			API_BASE_URL = await getOllamaAPIUrl(localStorage.token); | ||||
| 		} | ||||
| 
 | ||||
| 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); | ||||
| 		console.log(settings); | ||||
| 
 | ||||
| 		theme = localStorage.theme ?? 'dark'; | ||||
| 		notificationEnabled = settings.notificationEnabled ?? false; | ||||
| 
 | ||||
| 		API_BASE_URL = settings.API_BASE_URL ?? OLLAMA_API_BASE_URL; | ||||
| 		system = settings.system ?? ''; | ||||
| 
 | ||||
| 		requestFormat = settings.requestFormat ?? ''; | ||||
| 
 | ||||
| 		options.seed = settings.seed ?? 0; | ||||
|  | @ -674,10 +599,7 @@ | |||
| 			authContent = settings.authHeader.split(' ')[1]; | ||||
| 		} | ||||
| 
 | ||||
| 		ollamaVersion = await getOllamaVersion( | ||||
| 			API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 			localStorage.token | ||||
| 		).catch((error) => { | ||||
| 		ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => { | ||||
| 			return ''; | ||||
| 		}); | ||||
| 	}); | ||||
|  | @ -1041,51 +963,51 @@ | |||
| 							</div> | ||||
| 						</div> | ||||
| 
 | ||||
| 						<hr class=" dark:border-gray-700" /> | ||||
| 						<div> | ||||
| 							<div class=" mb-2.5 text-sm font-medium">Ollama API URL</div> | ||||
| 							<div class="flex w-full"> | ||||
| 								<div class="flex-1 mr-2"> | ||||
| 									<input | ||||
| 										class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none" | ||||
| 										placeholder="Enter URL (e.g. http://localhost:8080/ollama/api)" | ||||
| 										bind:value={API_BASE_URL} | ||||
| 									/> | ||||
| 								</div> | ||||
| 								<button | ||||
| 									class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 rounded transition" | ||||
| 									on:click={() => { | ||||
| 										checkOllamaConnection(); | ||||
| 									}} | ||||
| 								> | ||||
| 									<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" | ||||
| 						{#if $user.role === 'admin'} | ||||
| 							<hr class=" dark:border-gray-700" /> | ||||
| 							<div> | ||||
| 								<div class=" mb-2.5 text-sm font-medium">Ollama API URL</div> | ||||
| 								<div class="flex w-full"> | ||||
| 									<div class="flex-1 mr-2"> | ||||
| 										<input | ||||
| 											class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none" | ||||
| 											placeholder="Enter URL (e.g. http://localhost:11434/api)" | ||||
| 											bind:value={API_BASE_URL} | ||||
| 										/> | ||||
| 									</svg> | ||||
| 								</button> | ||||
| 							</div> | ||||
| 									</div> | ||||
| 									<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(); | ||||
| 										}} | ||||
| 									> | ||||
| 										<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 class="mt-2 text-xs text-gray-400 dark:text-gray-500"> | ||||
| 								The field above should be set to <span | ||||
| 									class=" text-gray-500 dark:text-gray-300 font-medium">'/ollama/api'</span | ||||
| 								>; | ||||
| 								<a | ||||
| 									class=" text-gray-500 dark:text-gray-300 font-medium" | ||||
| 									href="https://github.com/ollama-webui/ollama-webui#troubleshooting" | ||||
| 									target="_blank" | ||||
| 								> | ||||
| 									Click here for help. | ||||
| 								</a> | ||||
| 								<div class="mt-2 text-xs text-gray-400 dark:text-gray-500"> | ||||
| 									Trouble accessing Ollama? | ||||
| 									<a | ||||
| 										class=" text-gray-300 font-medium" | ||||
| 										href="https://github.com/ollama-webui/ollama-webui#troubleshooting" | ||||
| 										target="_blank" | ||||
| 									> | ||||
| 										Click here for help. | ||||
| 									</a> | ||||
| 								</div> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						{/if} | ||||
| 
 | ||||
| 						<hr class=" dark:border-gray-700" /> | ||||
| 
 | ||||
|  | @ -1103,7 +1025,6 @@ | |||
| 								class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded" | ||||
| 								on:click={() => { | ||||
| 									saveSettings({ | ||||
| 										API_BASE_URL: API_BASE_URL === '' ? OLLAMA_API_BASE_URL : API_BASE_URL, | ||||
| 										system: system !== '' ? system : undefined | ||||
| 									}); | ||||
| 									show = false; | ||||
|  |  | |||
|  | @ -1,13 +1,8 @@ | |||
| import { dev, browser } from '$app/environment'; | ||||
| import { PUBLIC_API_BASE_URL } from '$env/static/public'; | ||||
| import { dev } from '$app/environment'; | ||||
| 
 | ||||
| export const OLLAMA_API_BASE_URL = dev | ||||
| 	? `http://${location.hostname}:8080/ollama/api` | ||||
| 	: PUBLIC_API_BASE_URL === '' | ||||
| 	? browser | ||||
| 		? `http://${location.hostname}:11434/api` | ||||
| 		: `http://localhost:11434/api` | ||||
| 	: PUBLIC_API_BASE_URL; | ||||
| 	: '/ollama/api'; | ||||
| 
 | ||||
| export const WEBUI_BASE_URL = dev ? `http://${location.hostname}:8080` : ``; | ||||
| export const WEBUI_API_BASE_URL = `${WEBUI_BASE_URL}/api/v1`; | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| 	import { getOpenAIModels } from '$lib/apis/openai'; | ||||
| 
 | ||||
| 	import { user, showSettings, settings, models, modelfiles, prompts } from '$lib/stores'; | ||||
| 	import { OLLAMA_API_BASE_URL, REQUIRED_OLLAMA_VERSION, WEBUI_API_BASE_URL } from '$lib/constants'; | ||||
| 	import { REQUIRED_OLLAMA_VERSION, WEBUI_API_BASE_URL } from '$lib/constants'; | ||||
| 
 | ||||
| 	import SettingsModal from '$lib/components/chat/SettingsModal.svelte'; | ||||
| 	import Sidebar from '$lib/components/layout/Sidebar.svelte'; | ||||
|  | @ -32,10 +32,7 @@ | |||
| 	const getModels = async () => { | ||||
| 		let models = []; | ||||
| 		models.push( | ||||
| 			...(await getOllamaModels( | ||||
| 				$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 				localStorage.token | ||||
| 			).catch((error) => { | ||||
| 			...(await getOllamaModels(localStorage.token).catch((error) => { | ||||
| 				toast.error(error); | ||||
| 				return []; | ||||
| 			})) | ||||
|  | @ -58,10 +55,7 @@ | |||
| 
 | ||||
| 	const setOllamaVersion = async (version: string = '') => { | ||||
| 		if (version === '') { | ||||
| 			version = await getOllamaVersion( | ||||
| 				$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 				localStorage.token | ||||
| 			).catch((error) => { | ||||
| 			version = await getOllamaVersion(localStorage.token).catch((error) => { | ||||
| 				return ''; | ||||
| 			}); | ||||
| 		} | ||||
|  |  | |||
|  | @ -7,7 +7,6 @@ | |||
| 	import { page } from '$app/stores'; | ||||
| 
 | ||||
| 	import { models, modelfiles, user, settings, chats, chatId, config } from '$lib/stores'; | ||||
| 	import { OLLAMA_API_BASE_URL } from '$lib/constants'; | ||||
| 
 | ||||
| 	import { generateChatCompletion, generateTitle } from '$lib/apis/ollama'; | ||||
| 	import { copyToClipboard, splitStream } from '$lib/utils'; | ||||
|  | @ -163,36 +162,32 @@ | |||
| 		// Scroll down | ||||
| 		window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 
 | ||||
| 		const res = await generateChatCompletion( | ||||
| 			$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 			localStorage.token, | ||||
| 			{ | ||||
| 				model: model, | ||||
| 				messages: [ | ||||
| 					$settings.system | ||||
| 						? { | ||||
| 								role: 'system', | ||||
| 								content: $settings.system | ||||
| 						  } | ||||
| 						: undefined, | ||||
| 					...messages | ||||
| 				] | ||||
| 					.filter((message) => message) | ||||
| 					.map((message) => ({ | ||||
| 						role: message.role, | ||||
| 						content: message.content, | ||||
| 						...(message.files && { | ||||
| 							images: message.files | ||||
| 								.filter((file) => file.type === 'image') | ||||
| 								.map((file) => file.url.slice(file.url.indexOf(',') + 1)) | ||||
| 						}) | ||||
| 					})), | ||||
| 				options: { | ||||
| 					...($settings.options ?? {}) | ||||
| 				}, | ||||
| 				format: $settings.requestFormat ?? undefined | ||||
| 			} | ||||
| 		); | ||||
| 		const res = await generateChatCompletion(localStorage.token, { | ||||
| 			model: model, | ||||
| 			messages: [ | ||||
| 				$settings.system | ||||
| 					? { | ||||
| 							role: 'system', | ||||
| 							content: $settings.system | ||||
| 					  } | ||||
| 					: undefined, | ||||
| 				...messages | ||||
| 			] | ||||
| 				.filter((message) => message) | ||||
| 				.map((message) => ({ | ||||
| 					role: message.role, | ||||
| 					content: message.content, | ||||
| 					...(message.files && { | ||||
| 						images: message.files | ||||
| 							.filter((file) => file.type === 'image') | ||||
| 							.map((file) => file.url.slice(file.url.indexOf(',') + 1)) | ||||
| 					}) | ||||
| 				})), | ||||
| 			options: { | ||||
| 				...($settings.options ?? {}) | ||||
| 			}, | ||||
| 			format: $settings.requestFormat ?? undefined | ||||
| 		}); | ||||
| 
 | ||||
| 		if (res && res.ok) { | ||||
| 			const reader = res.body | ||||
|  | @ -595,7 +590,6 @@ | |||
| 	const generateChatTitle = async (_chatId, userPrompt) => { | ||||
| 		if ($settings.titleAutoGenerate ?? true) { | ||||
| 			const title = await generateTitle( | ||||
| 				$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 				localStorage.token, | ||||
| 				$settings?.titleAutoGenerateModel ?? selectedModels[0], | ||||
| 				userPrompt | ||||
|  |  | |||
|  | @ -7,7 +7,6 @@ | |||
| 	import { page } from '$app/stores'; | ||||
| 
 | ||||
| 	import { models, modelfiles, user, settings, chats, chatId } from '$lib/stores'; | ||||
| 	import { OLLAMA_API_BASE_URL } from '$lib/constants'; | ||||
| 
 | ||||
| 	import { generateChatCompletion, generateTitle } from '$lib/apis/ollama'; | ||||
| 	import { copyToClipboard, splitStream } from '$lib/utils'; | ||||
|  | @ -180,36 +179,32 @@ | |||
| 		// Scroll down | ||||
| 		window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 
 | ||||
| 		const res = await generateChatCompletion( | ||||
| 			$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 			localStorage.token, | ||||
| 			{ | ||||
| 				model: model, | ||||
| 				messages: [ | ||||
| 					$settings.system | ||||
| 						? { | ||||
| 								role: 'system', | ||||
| 								content: $settings.system | ||||
| 						  } | ||||
| 						: undefined, | ||||
| 					...messages | ||||
| 				] | ||||
| 					.filter((message) => message) | ||||
| 					.map((message) => ({ | ||||
| 						role: message.role, | ||||
| 						content: message.content, | ||||
| 						...(message.files && { | ||||
| 							images: message.files | ||||
| 								.filter((file) => file.type === 'image') | ||||
| 								.map((file) => file.url.slice(file.url.indexOf(',') + 1)) | ||||
| 						}) | ||||
| 					})), | ||||
| 				options: { | ||||
| 					...($settings.options ?? {}) | ||||
| 				}, | ||||
| 				format: $settings.requestFormat ?? undefined | ||||
| 			} | ||||
| 		); | ||||
| 		const res = await generateChatCompletion(localStorage.token, { | ||||
| 			model: model, | ||||
| 			messages: [ | ||||
| 				$settings.system | ||||
| 					? { | ||||
| 							role: 'system', | ||||
| 							content: $settings.system | ||||
| 					  } | ||||
| 					: undefined, | ||||
| 				...messages | ||||
| 			] | ||||
| 				.filter((message) => message) | ||||
| 				.map((message) => ({ | ||||
| 					role: message.role, | ||||
| 					content: message.content, | ||||
| 					...(message.files && { | ||||
| 						images: message.files | ||||
| 							.filter((file) => file.type === 'image') | ||||
| 							.map((file) => file.url.slice(file.url.indexOf(',') + 1)) | ||||
| 					}) | ||||
| 				})), | ||||
| 			options: { | ||||
| 				...($settings.options ?? {}) | ||||
| 			}, | ||||
| 			format: $settings.requestFormat ?? undefined | ||||
| 		}); | ||||
| 
 | ||||
| 		if (res && res.ok) { | ||||
| 			const reader = res.body | ||||
|  | @ -611,12 +606,7 @@ | |||
| 
 | ||||
| 	const generateChatTitle = async (_chatId, userPrompt) => { | ||||
| 		if ($settings.titleAutoGenerate ?? true) { | ||||
| 			const title = await generateTitle( | ||||
| 				$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 				localStorage.token, | ||||
| 				selectedModels[0], | ||||
| 				userPrompt | ||||
| 			); | ||||
| 			const title = await generateTitle(localStorage.token, selectedModels[0], userPrompt); | ||||
| 
 | ||||
| 			if (title) { | ||||
| 				await setChatTitle(_chatId, title); | ||||
|  |  | |||
|  | @ -6,7 +6,6 @@ | |||
| 	import { onMount } from 'svelte'; | ||||
| 
 | ||||
| 	import { modelfiles, settings, user } from '$lib/stores'; | ||||
| 	import { OLLAMA_API_BASE_URL } from '$lib/constants'; | ||||
| 	import { createModel, deleteModel } from '$lib/apis/ollama'; | ||||
| 	import { | ||||
| 		createNewModelfile, | ||||
|  | @ -20,11 +19,7 @@ | |||
| 	const deleteModelHandler = async (tagName) => { | ||||
| 		let success = null; | ||||
| 
 | ||||
| 		success = await deleteModel( | ||||
| 			$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 			localStorage.token, | ||||
| 			tagName | ||||
| 		); | ||||
| 		success = await deleteModel(localStorage.token, tagName); | ||||
| 
 | ||||
| 		if (success) { | ||||
| 			toast.success(`Deleted ${tagName}`); | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ | |||
| 	import { v4 as uuidv4 } from 'uuid'; | ||||
| 	import { toast } from 'svelte-french-toast'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| 	import { OLLAMA_API_BASE_URL } from '$lib/constants'; | ||||
| 	import { settings, user, config, modelfiles, models } from '$lib/stores'; | ||||
| 
 | ||||
| 	import Advanced from '$lib/components/chat/Settings/Advanced.svelte'; | ||||
|  | @ -132,12 +131,7 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, ''); | |||
| 			Object.keys(categories).filter((category) => categories[category]).length > 0 && | ||||
| 			!$models.includes(tagName) | ||||
| 		) { | ||||
| 			const res = await createModel( | ||||
| 				$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 				localStorage.token, | ||||
| 				tagName, | ||||
| 				content | ||||
| 			); | ||||
| 			const res = await createModel(localStorage.token, tagName, content); | ||||
| 
 | ||||
| 			if (res) { | ||||
| 				const reader = res.body | ||||
|  |  | |||
|  | @ -7,8 +7,6 @@ | |||
| 	import { page } from '$app/stores'; | ||||
| 
 | ||||
| 	import { settings, user, config, modelfiles } from '$lib/stores'; | ||||
| 
 | ||||
| 	import { OLLAMA_API_BASE_URL } from '$lib/constants'; | ||||
| 	import { splitStream } from '$lib/utils'; | ||||
| 
 | ||||
| 	import { createModel } from '$lib/apis/ollama'; | ||||
|  | @ -104,12 +102,7 @@ | |||
| 			content !== '' && | ||||
| 			Object.keys(categories).filter((category) => categories[category]).length > 0 | ||||
| 		) { | ||||
| 			const res = await createModel( | ||||
| 				$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 				localStorage.token, | ||||
| 				tagName, | ||||
| 				content | ||||
| 			); | ||||
| 			const res = await createModel(localStorage.token, tagName, content); | ||||
| 
 | ||||
| 			if (res) { | ||||
| 				const reader = res.body | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 ThatOneCalculator
						ThatOneCalculator