forked from open-webui/open-webui
		
	Merge branch 'main' into bun
This commit is contained in:
		
						commit
						1a93191259
					
				
					 13 changed files with 724 additions and 431 deletions
				
			
		|  | @ -1,4 +1,176 @@ | |||
| export const getOpenAIModels = async ( | ||||
| import { OPENAI_API_BASE_URL } from '$lib/constants'; | ||||
| 
 | ||||
| export const getOpenAIUrl = async (token: string = '') => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OPENAI_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.OPENAI_API_BASE_URL; | ||||
| }; | ||||
| 
 | ||||
| export const updateOpenAIUrl = async (token: string = '', url: string) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OPENAI_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.OPENAI_API_BASE_URL; | ||||
| }; | ||||
| 
 | ||||
| export const getOpenAIKey = async (token: string = '') => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/key`, { | ||||
| 		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.OPENAI_API_KEY; | ||||
| }; | ||||
| 
 | ||||
| export const updateOpenAIKey = async (token: string = '', key: string) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/key/update`, { | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			Accept: 'application/json', | ||||
| 			'Content-Type': 'application/json', | ||||
| 			...(token && { authorization: `Bearer ${token}` }) | ||||
| 		}, | ||||
| 		body: JSON.stringify({ | ||||
| 			key: key | ||||
| 		}) | ||||
| 	}) | ||||
| 		.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.OPENAI_API_KEY; | ||||
| }; | ||||
| 
 | ||||
| export const getOpenAIModels = async (token: string = '') => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/models`, { | ||||
| 		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 = `OpenAI: ${err?.error?.message ?? 'Network Problem'}`; | ||||
| 			return []; | ||||
| 		}); | ||||
| 
 | ||||
| 	if (error) { | ||||
| 		throw error; | ||||
| 	} | ||||
| 
 | ||||
| 	const models = Array.isArray(res) ? res : res?.data ?? null; | ||||
| 
 | ||||
| 	return models | ||||
| 		? models | ||||
| 				.map((model) => ({ name: model.id, external: true })) | ||||
| 				.sort((a, b) => { | ||||
| 					return a.name.localeCompare(b.name); | ||||
| 				}) | ||||
| 		: models; | ||||
| }; | ||||
| 
 | ||||
| export const getOpenAIModelsDirect = async ( | ||||
| 	base_url: string = 'https://api.openai.com/v1', | ||||
| 	api_key: string = '' | ||||
| ) => { | ||||
|  | @ -34,3 +206,26 @@ export const getOpenAIModels = async ( | |||
| 			return a.name.localeCompare(b.name); | ||||
| 		}); | ||||
| }; | ||||
| 
 | ||||
| export const generateOpenAIChatCompletion = async (token: string = '', body: object) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/chat/completions`, { | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			Authorization: `Bearer ${token}`, | ||||
| 			'Content-Type': 'application/json' | ||||
| 		}, | ||||
| 		body: JSON.stringify(body) | ||||
| 	}).catch((err) => { | ||||
| 		console.log(err); | ||||
| 		error = err; | ||||
| 		return null; | ||||
| 	}); | ||||
| 
 | ||||
| 	if (error) { | ||||
| 		throw error; | ||||
| 	} | ||||
| 
 | ||||
| 	return res; | ||||
| }; | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ | |||
| 					> | ||||
| 						{#if model in modelfiles} | ||||
| 							<img | ||||
| 								src={modelfiles[model]?.imageUrl} | ||||
| 								src={modelfiles[model]?.imageUrl ?? '/ollama-dark.png'} | ||||
| 								alt="modelfile" | ||||
| 								class=" w-20 mb-2 rounded-full {models.length > 1 | ||||
| 									? ' border-[5px] border-white dark:border-gray-800' | ||||
|  |  | |||
|  | @ -24,6 +24,13 @@ | |||
| 	import { updateUserPassword } from '$lib/apis/auths'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| 	import Page from '../../../routes/(app)/+page.svelte'; | ||||
| 	import { | ||||
| 		getOpenAIKey, | ||||
| 		getOpenAIModels, | ||||
| 		getOpenAIUrl, | ||||
| 		updateOpenAIKey, | ||||
| 		updateOpenAIUrl | ||||
| 	} from '$lib/apis/openai'; | ||||
| 
 | ||||
| 	export let show = false; | ||||
| 
 | ||||
|  | @ -153,6 +160,13 @@ | |||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	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()); | ||||
| 	}; | ||||
| 
 | ||||
| 	const toggleTheme = async () => { | ||||
| 		if (theme === 'dark') { | ||||
| 			theme = 'light'; | ||||
|  | @ -484,7 +498,7 @@ | |||
| 	}; | ||||
| 
 | ||||
| 	const getModels = async (type = 'all') => { | ||||
| 		let models = []; | ||||
| 		const models = []; | ||||
| 		models.push( | ||||
| 			...(await getOllamaModels(localStorage.token).catch((error) => { | ||||
| 				toast.error(error); | ||||
|  | @ -493,43 +507,13 @@ | |||
| 		); | ||||
| 
 | ||||
| 		// If OpenAI API Key exists | ||||
| 		if (type === 'all' && $settings.OPENAI_API_KEY) { | ||||
| 			const OPENAI_API_BASE_URL = $settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1'; | ||||
| 		if (type === 'all' && OPENAI_API_KEY) { | ||||
| 			const openAIModels = await getOpenAIModels(localStorage.token).catch((error) => { | ||||
| 				console.log(error); | ||||
| 				return null; | ||||
| 			}); | ||||
| 
 | ||||
| 			// Validate OPENAI_API_KEY | ||||
| 			const openaiModelRes = await fetch(`${OPENAI_API_BASE_URL}/models`, { | ||||
| 				method: 'GET', | ||||
| 				headers: { | ||||
| 					'Content-Type': 'application/json', | ||||
| 					Authorization: `Bearer ${$settings.OPENAI_API_KEY}` | ||||
| 				} | ||||
| 			}) | ||||
| 				.then(async (res) => { | ||||
| 					if (!res.ok) throw await res.json(); | ||||
| 					return res.json(); | ||||
| 				}) | ||||
| 				.catch((error) => { | ||||
| 					console.log(error); | ||||
| 					toast.error(`OpenAI: ${error?.error?.message ?? 'Network Problem'}`); | ||||
| 					return null; | ||||
| 				}); | ||||
| 
 | ||||
| 			const openAIModels = Array.isArray(openaiModelRes) | ||||
| 				? openaiModelRes | ||||
| 				: openaiModelRes?.data ?? null; | ||||
| 
 | ||||
| 			models.push( | ||||
| 				...(openAIModels | ||||
| 					? [ | ||||
| 							{ name: 'hr' }, | ||||
| 							...openAIModels | ||||
| 								.map((model) => ({ name: model.id, external: true })) | ||||
| 								.filter((model) => | ||||
| 									OPENAI_API_BASE_URL.includes('openai') ? model.name.includes('gpt') : true | ||||
| 								) | ||||
| 					  ] | ||||
| 					: []) | ||||
| 			); | ||||
| 			models.push(...(openAIModels ? [{ name: 'hr' }, ...openAIModels] : [])); | ||||
| 		} | ||||
| 
 | ||||
| 		return models; | ||||
|  | @ -564,6 +548,8 @@ | |||
| 		console.log('settings', $user.role === 'admin'); | ||||
| 		if ($user.role === 'admin') { | ||||
| 			API_BASE_URL = await getOllamaAPIUrl(localStorage.token); | ||||
| 			OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token); | ||||
| 			OPENAI_API_KEY = await getOpenAIKey(localStorage.token); | ||||
| 		} | ||||
| 
 | ||||
| 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); | ||||
|  | @ -584,9 +570,6 @@ | |||
| 		options = { ...options, ...settings.options }; | ||||
| 		options.stop = (settings?.options?.stop ?? []).join(','); | ||||
| 
 | ||||
| 		OPENAI_API_KEY = settings.OPENAI_API_KEY ?? ''; | ||||
| 		OPENAI_API_BASE_URL = settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1'; | ||||
| 
 | ||||
| 		titleAutoGenerate = settings.titleAutoGenerate ?? true; | ||||
| 		speechAutoSend = settings.speechAutoSend ?? false; | ||||
| 		responseAutoCopy = settings.responseAutoCopy ?? false; | ||||
|  | @ -709,31 +692,31 @@ | |||
| 						</div> | ||||
| 						<div class=" self-center">Models</div> | ||||
| 					</button> | ||||
| 				{/if} | ||||
| 
 | ||||
| 				<button | ||||
| 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === | ||||
| 					'external' | ||||
| 						? 'bg-gray-200 dark:bg-gray-700' | ||||
| 						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}" | ||||
| 					on:click={() => { | ||||
| 						selectedTab = 'external'; | ||||
| 					}} | ||||
| 				> | ||||
| 					<div class=" self-center mr-2"> | ||||
| 						<svg | ||||
| 							xmlns="http://www.w3.org/2000/svg" | ||||
| 							viewBox="0 0 16 16" | ||||
| 							fill="currentColor" | ||||
| 							class="w-4 h-4" | ||||
| 						> | ||||
| 							<path | ||||
| 								d="M1 9.5A3.5 3.5 0 0 0 4.5 13H12a3 3 0 0 0 .917-5.857 2.503 2.503 0 0 0-3.198-3.019 3.5 3.5 0 0 0-6.628 2.171A3.5 3.5 0 0 0 1 9.5Z" | ||||
| 							/> | ||||
| 						</svg> | ||||
| 					</div> | ||||
| 					<div class=" self-center">External</div> | ||||
| 				</button> | ||||
| 					<button | ||||
| 						class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === | ||||
| 						'external' | ||||
| 							? 'bg-gray-200 dark:bg-gray-700' | ||||
| 							: ' hover:bg-gray-300 dark:hover:bg-gray-800'}" | ||||
| 						on:click={() => { | ||||
| 							selectedTab = 'external'; | ||||
| 						}} | ||||
| 					> | ||||
| 						<div class=" self-center mr-2"> | ||||
| 							<svg | ||||
| 								xmlns="http://www.w3.org/2000/svg" | ||||
| 								viewBox="0 0 16 16" | ||||
| 								fill="currentColor" | ||||
| 								class="w-4 h-4" | ||||
| 							> | ||||
| 								<path | ||||
| 									d="M1 9.5A3.5 3.5 0 0 0 4.5 13H12a3 3 0 0 0 .917-5.857 2.503 2.503 0 0 0-3.198-3.019 3.5 3.5 0 0 0-6.628 2.171A3.5 3.5 0 0 0 1 9.5Z" | ||||
| 								/> | ||||
| 							</svg> | ||||
| 						</div> | ||||
| 						<div class=" self-center">External</div> | ||||
| 					</button> | ||||
| 				{/if} | ||||
| 
 | ||||
| 				<button | ||||
| 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === | ||||
|  | @ -1415,10 +1398,12 @@ | |||
| 					<form | ||||
| 						class="flex flex-col h-full justify-between space-y-3 text-sm" | ||||
| 						on:submit|preventDefault={() => { | ||||
| 							saveSettings({ | ||||
| 								OPENAI_API_KEY: OPENAI_API_KEY !== '' ? OPENAI_API_KEY : undefined, | ||||
| 								OPENAI_API_BASE_URL: OPENAI_API_BASE_URL !== '' ? OPENAI_API_BASE_URL : undefined | ||||
| 							}); | ||||
| 							updateOpenAIHandler(); | ||||
| 
 | ||||
| 							// saveSettings({ | ||||
| 							// 	OPENAI_API_KEY: OPENAI_API_KEY !== '' ? OPENAI_API_KEY : undefined, | ||||
| 							// 	OPENAI_API_BASE_URL: OPENAI_API_BASE_URL !== '' ? OPENAI_API_BASE_URL : undefined | ||||
| 							// }); | ||||
| 							show = false; | ||||
| 						}} | ||||
| 					> | ||||
|  |  | |||
|  | @ -1,11 +1,10 @@ | |||
| import { dev } from '$app/environment'; | ||||
| 
 | ||||
| export const OLLAMA_API_BASE_URL = dev | ||||
| 	? `http://${location.hostname}:8080/ollama/api` | ||||
| 	: '/ollama/api'; | ||||
| 
 | ||||
| export const WEBUI_BASE_URL = dev ? `http://${location.hostname}:8080` : ``; | ||||
| 
 | ||||
| export const WEBUI_API_BASE_URL = `${WEBUI_BASE_URL}/api/v1`; | ||||
| export const OLLAMA_API_BASE_URL = `${WEBUI_BASE_URL}/ollama/api`; | ||||
| export const OPENAI_API_BASE_URL = `${WEBUI_BASE_URL}/openai/api`; | ||||
| 
 | ||||
| export const WEB_UI_VERSION = 'v1.0.0-alpha-static'; | ||||
| 
 | ||||
|  |  | |||
|  | @ -37,19 +37,17 @@ | |||
| 				return []; | ||||
| 			})) | ||||
| 		); | ||||
| 		// If OpenAI API Key exists | ||||
| 		if ($settings.OPENAI_API_KEY) { | ||||
| 			const openAIModels = await getOpenAIModels( | ||||
| 				$settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1', | ||||
| 				$settings.OPENAI_API_KEY | ||||
| 			).catch((error) => { | ||||
| 				console.log(error); | ||||
| 				toast.error(error); | ||||
| 				return null; | ||||
| 			}); | ||||
| 
 | ||||
| 			models.push(...(openAIModels ? [{ name: 'hr' }, ...openAIModels] : [])); | ||||
| 		} | ||||
| 		// $settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1', | ||||
| 		// 		$settings.OPENAI_API_KEY | ||||
| 
 | ||||
| 		const openAIModels = await getOpenAIModels(localStorage.token).catch((error) => { | ||||
| 			console.log(error); | ||||
| 			return null; | ||||
| 		}); | ||||
| 
 | ||||
| 		models.push(...(openAIModels ? [{ name: 'hr' }, ...openAIModels] : [])); | ||||
| 
 | ||||
| 		return models; | ||||
| 	}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| 	import ModelSelector from '$lib/components/chat/ModelSelector.svelte'; | ||||
| 	import Navbar from '$lib/components/layout/Navbar.svelte'; | ||||
| 	import { createNewChat, getChatList, updateChatById } from '$lib/apis/chats'; | ||||
| 	import { generateOpenAIChatCompletion } from '$lib/apis/openai'; | ||||
| 
 | ||||
| 	let stopResponseFlag = false; | ||||
| 	let autoScroll = true; | ||||
|  | @ -321,188 +322,171 @@ | |||
| 	}; | ||||
| 
 | ||||
| 	const sendPromptOpenAI = async (model, userPrompt, parentId, _chatId) => { | ||||
| 		if ($settings.OPENAI_API_KEY) { | ||||
| 			if (models) { | ||||
| 				let responseMessageId = uuidv4(); | ||||
| 		let responseMessageId = uuidv4(); | ||||
| 
 | ||||
| 				let responseMessage = { | ||||
| 					parentId: parentId, | ||||
| 					id: responseMessageId, | ||||
| 					childrenIds: [], | ||||
| 					role: 'assistant', | ||||
| 					content: '', | ||||
| 					model: model | ||||
| 				}; | ||||
| 		let responseMessage = { | ||||
| 			parentId: parentId, | ||||
| 			id: responseMessageId, | ||||
| 			childrenIds: [], | ||||
| 			role: 'assistant', | ||||
| 			content: '', | ||||
| 			model: model | ||||
| 		}; | ||||
| 
 | ||||
| 				history.messages[responseMessageId] = responseMessage; | ||||
| 				history.currentId = responseMessageId; | ||||
| 				if (parentId !== null) { | ||||
| 					history.messages[parentId].childrenIds = [ | ||||
| 						...history.messages[parentId].childrenIds, | ||||
| 						responseMessageId | ||||
| 					]; | ||||
| 				} | ||||
| 		history.messages[responseMessageId] = responseMessage; | ||||
| 		history.currentId = responseMessageId; | ||||
| 		if (parentId !== null) { | ||||
| 			history.messages[parentId].childrenIds = [ | ||||
| 				...history.messages[parentId].childrenIds, | ||||
| 				responseMessageId | ||||
| 			]; | ||||
| 		} | ||||
| 
 | ||||
| 				window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 		window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 
 | ||||
| 				const res = await fetch( | ||||
| 					`${$settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1'}/chat/completions`, | ||||
| 					{ | ||||
| 						method: 'POST', | ||||
| 						headers: { | ||||
| 							Authorization: `Bearer ${$settings.OPENAI_API_KEY}`, | ||||
| 							'Content-Type': 'application/json' | ||||
| 						}, | ||||
| 						body: JSON.stringify({ | ||||
| 							model: model, | ||||
| 							stream: true, | ||||
| 							messages: [ | ||||
| 								$settings.system | ||||
| 									? { | ||||
| 											role: 'system', | ||||
| 											content: $settings.system | ||||
| 									  } | ||||
| 									: undefined, | ||||
| 								...messages | ||||
| 							] | ||||
| 								.filter((message) => message) | ||||
| 								.map((message) => ({ | ||||
| 									role: message.role, | ||||
| 									...(message.files | ||||
| 										? { | ||||
| 												content: [ | ||||
| 													{ | ||||
| 														type: 'text', | ||||
| 														text: message.content | ||||
| 													}, | ||||
| 													...message.files | ||||
| 														.filter((file) => file.type === 'image') | ||||
| 														.map((file) => ({ | ||||
| 															type: 'image_url', | ||||
| 															image_url: { | ||||
| 																url: file.url | ||||
| 															} | ||||
| 														})) | ||||
| 												] | ||||
| 										  } | ||||
| 										: { content: message.content }) | ||||
| 								})), | ||||
| 							seed: $settings?.options?.seed ?? undefined, | ||||
| 							stop: $settings?.options?.stop ?? undefined, | ||||
| 							temperature: $settings?.options?.temperature ?? undefined, | ||||
| 							top_p: $settings?.options?.top_p ?? undefined, | ||||
| 							num_ctx: $settings?.options?.num_ctx ?? undefined, | ||||
| 							frequency_penalty: $settings?.options?.repeat_penalty ?? undefined, | ||||
| 							max_tokens: $settings?.options?.num_predict ?? undefined | ||||
| 						}) | ||||
| 					} | ||||
| 				).catch((err) => { | ||||
| 					console.log(err); | ||||
| 					return null; | ||||
| 				}); | ||||
| 		const res = await generateOpenAIChatCompletion(localStorage.token, { | ||||
| 			model: model, | ||||
| 			stream: true, | ||||
| 			messages: [ | ||||
| 				$settings.system | ||||
| 					? { | ||||
| 							role: 'system', | ||||
| 							content: $settings.system | ||||
| 					  } | ||||
| 					: undefined, | ||||
| 				...messages | ||||
| 			] | ||||
| 				.filter((message) => message) | ||||
| 				.map((message) => ({ | ||||
| 					role: message.role, | ||||
| 					...(message.files | ||||
| 						? { | ||||
| 								content: [ | ||||
| 									{ | ||||
| 										type: 'text', | ||||
| 										text: message.content | ||||
| 									}, | ||||
| 									...message.files | ||||
| 										.filter((file) => file.type === 'image') | ||||
| 										.map((file) => ({ | ||||
| 											type: 'image_url', | ||||
| 											image_url: { | ||||
| 												url: file.url | ||||
| 											} | ||||
| 										})) | ||||
| 								] | ||||
| 						  } | ||||
| 						: { content: message.content }) | ||||
| 				})), | ||||
| 			seed: $settings?.options?.seed ?? undefined, | ||||
| 			stop: $settings?.options?.stop ?? undefined, | ||||
| 			temperature: $settings?.options?.temperature ?? undefined, | ||||
| 			top_p: $settings?.options?.top_p ?? undefined, | ||||
| 			num_ctx: $settings?.options?.num_ctx ?? undefined, | ||||
| 			frequency_penalty: $settings?.options?.repeat_penalty ?? undefined, | ||||
| 			max_tokens: $settings?.options?.num_predict ?? undefined | ||||
| 		}); | ||||
| 
 | ||||
| 				if (res && res.ok) { | ||||
| 					const reader = res.body | ||||
| 						.pipeThrough(new TextDecoderStream()) | ||||
| 						.pipeThrough(splitStream('\n')) | ||||
| 						.getReader(); | ||||
| 		if (res && res.ok) { | ||||
| 			const reader = res.body | ||||
| 				.pipeThrough(new TextDecoderStream()) | ||||
| 				.pipeThrough(splitStream('\n')) | ||||
| 				.getReader(); | ||||
| 
 | ||||
| 					while (true) { | ||||
| 						const { value, done } = await reader.read(); | ||||
| 						if (done || stopResponseFlag || _chatId !== $chatId) { | ||||
| 							responseMessage.done = true; | ||||
| 							messages = messages; | ||||
| 							break; | ||||
| 						} | ||||
| 
 | ||||
| 						try { | ||||
| 							let lines = value.split('\n'); | ||||
| 
 | ||||
| 							for (const line of lines) { | ||||
| 								if (line !== '') { | ||||
| 									console.log(line); | ||||
| 									if (line === 'data: [DONE]') { | ||||
| 										responseMessage.done = true; | ||||
| 										messages = messages; | ||||
| 									} else { | ||||
| 										let data = JSON.parse(line.replace(/^data: /, '')); | ||||
| 										console.log(data); | ||||
| 
 | ||||
| 										if (responseMessage.content == '' && data.choices[0].delta.content == '\n') { | ||||
| 											continue; | ||||
| 										} else { | ||||
| 											responseMessage.content += data.choices[0].delta.content ?? ''; | ||||
| 											messages = messages; | ||||
| 										} | ||||
| 									} | ||||
| 								} | ||||
| 							} | ||||
| 						} catch (error) { | ||||
| 							console.log(error); | ||||
| 						} | ||||
| 
 | ||||
| 						if ($settings.notificationEnabled && !document.hasFocus()) { | ||||
| 							const notification = new Notification(`OpenAI ${model}`, { | ||||
| 								body: responseMessage.content, | ||||
| 								icon: '/favicon.png' | ||||
| 							}); | ||||
| 						} | ||||
| 
 | ||||
| 						if ($settings.responseAutoCopy) { | ||||
| 							copyToClipboard(responseMessage.content); | ||||
| 						} | ||||
| 
 | ||||
| 						if (autoScroll) { | ||||
| 							window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					if ($chatId == _chatId) { | ||||
| 						chat = await updateChatById(localStorage.token, _chatId, { | ||||
| 							messages: messages, | ||||
| 							history: history | ||||
| 						}); | ||||
| 						await chats.set(await getChatList(localStorage.token)); | ||||
| 					} | ||||
| 				} else { | ||||
| 					if (res !== null) { | ||||
| 						const error = await res.json(); | ||||
| 						console.log(error); | ||||
| 						if ('detail' in error) { | ||||
| 							toast.error(error.detail); | ||||
| 							responseMessage.content = error.detail; | ||||
| 						} else { | ||||
| 							if ('message' in error.error) { | ||||
| 								toast.error(error.error.message); | ||||
| 								responseMessage.content = error.error.message; | ||||
| 							} else { | ||||
| 								toast.error(error.error); | ||||
| 								responseMessage.content = error.error; | ||||
| 							} | ||||
| 						} | ||||
| 					} else { | ||||
| 						toast.error(`Uh-oh! There was an issue connecting to ${model}.`); | ||||
| 						responseMessage.content = `Uh-oh! There was an issue connecting to ${model}.`; | ||||
| 					} | ||||
| 
 | ||||
| 					responseMessage.error = true; | ||||
| 					responseMessage.content = `Uh-oh! There was an issue connecting to ${model}.`; | ||||
| 			while (true) { | ||||
| 				const { value, done } = await reader.read(); | ||||
| 				if (done || stopResponseFlag || _chatId !== $chatId) { | ||||
| 					responseMessage.done = true; | ||||
| 					messages = messages; | ||||
| 					break; | ||||
| 				} | ||||
| 
 | ||||
| 				stopResponseFlag = false; | ||||
| 				await tick(); | ||||
| 				try { | ||||
| 					let lines = value.split('\n'); | ||||
| 
 | ||||
| 					for (const line of lines) { | ||||
| 						if (line !== '') { | ||||
| 							console.log(line); | ||||
| 							if (line === 'data: [DONE]') { | ||||
| 								responseMessage.done = true; | ||||
| 								messages = messages; | ||||
| 							} else { | ||||
| 								let data = JSON.parse(line.replace(/^data: /, '')); | ||||
| 								console.log(data); | ||||
| 
 | ||||
| 								if (responseMessage.content == '' && data.choices[0].delta.content == '\n') { | ||||
| 									continue; | ||||
| 								} else { | ||||
| 									responseMessage.content += data.choices[0].delta.content ?? ''; | ||||
| 									messages = messages; | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} catch (error) { | ||||
| 					console.log(error); | ||||
| 				} | ||||
| 
 | ||||
| 				if ($settings.notificationEnabled && !document.hasFocus()) { | ||||
| 					const notification = new Notification(`OpenAI ${model}`, { | ||||
| 						body: responseMessage.content, | ||||
| 						icon: '/favicon.png' | ||||
| 					}); | ||||
| 				} | ||||
| 
 | ||||
| 				if ($settings.responseAutoCopy) { | ||||
| 					copyToClipboard(responseMessage.content); | ||||
| 				} | ||||
| 
 | ||||
| 				if (autoScroll) { | ||||
| 					window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 				} | ||||
| 
 | ||||
| 				if (messages.length == 2) { | ||||
| 					window.history.replaceState(history.state, '', `/c/${_chatId}`); | ||||
| 					await setChatTitle(_chatId, userPrompt); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if ($chatId == _chatId) { | ||||
| 				chat = await updateChatById(localStorage.token, _chatId, { | ||||
| 					messages: messages, | ||||
| 					history: history | ||||
| 				}); | ||||
| 				await chats.set(await getChatList(localStorage.token)); | ||||
| 			} | ||||
| 		} else { | ||||
| 			if (res !== null) { | ||||
| 				const error = await res.json(); | ||||
| 				console.log(error); | ||||
| 				if ('detail' in error) { | ||||
| 					toast.error(error.detail); | ||||
| 					responseMessage.content = error.detail; | ||||
| 				} else { | ||||
| 					if ('message' in error.error) { | ||||
| 						toast.error(error.error.message); | ||||
| 						responseMessage.content = error.error.message; | ||||
| 					} else { | ||||
| 						toast.error(error.error); | ||||
| 						responseMessage.content = error.error; | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				toast.error(`Uh-oh! There was an issue connecting to ${model}.`); | ||||
| 				responseMessage.content = `Uh-oh! There was an issue connecting to ${model}.`; | ||||
| 			} | ||||
| 
 | ||||
| 			responseMessage.error = true; | ||||
| 			responseMessage.content = `Uh-oh! There was an issue connecting to ${model}.`; | ||||
| 			responseMessage.done = true; | ||||
| 			messages = messages; | ||||
| 		} | ||||
| 
 | ||||
| 		stopResponseFlag = false; | ||||
| 		await tick(); | ||||
| 
 | ||||
| 		if (autoScroll) { | ||||
| 			window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 		} | ||||
| 
 | ||||
| 		if (messages.length == 2) { | ||||
| 			window.history.replaceState(history.state, '', `/c/${_chatId}`); | ||||
| 			await setChatTitle(_chatId, userPrompt); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,6 +9,8 @@ | |||
| 	import { models, modelfiles, user, settings, chats, chatId } from '$lib/stores'; | ||||
| 
 | ||||
| 	import { generateChatCompletion, generateTitle } from '$lib/apis/ollama'; | ||||
| 	import { generateOpenAIChatCompletion } from '$lib/apis/openai'; | ||||
| 
 | ||||
| 	import { copyToClipboard, splitStream } from '$lib/utils'; | ||||
| 
 | ||||
| 	import MessageInput from '$lib/components/chat/MessageInput.svelte'; | ||||
|  | @ -338,188 +340,171 @@ | |||
| 	}; | ||||
| 
 | ||||
| 	const sendPromptOpenAI = async (model, userPrompt, parentId, _chatId) => { | ||||
| 		if ($settings.OPENAI_API_KEY) { | ||||
| 			if (models) { | ||||
| 				let responseMessageId = uuidv4(); | ||||
| 		let responseMessageId = uuidv4(); | ||||
| 
 | ||||
| 				let responseMessage = { | ||||
| 					parentId: parentId, | ||||
| 					id: responseMessageId, | ||||
| 					childrenIds: [], | ||||
| 					role: 'assistant', | ||||
| 					content: '', | ||||
| 					model: model | ||||
| 				}; | ||||
| 		let responseMessage = { | ||||
| 			parentId: parentId, | ||||
| 			id: responseMessageId, | ||||
| 			childrenIds: [], | ||||
| 			role: 'assistant', | ||||
| 			content: '', | ||||
| 			model: model | ||||
| 		}; | ||||
| 
 | ||||
| 				history.messages[responseMessageId] = responseMessage; | ||||
| 				history.currentId = responseMessageId; | ||||
| 				if (parentId !== null) { | ||||
| 					history.messages[parentId].childrenIds = [ | ||||
| 						...history.messages[parentId].childrenIds, | ||||
| 						responseMessageId | ||||
| 					]; | ||||
| 				} | ||||
| 		history.messages[responseMessageId] = responseMessage; | ||||
| 		history.currentId = responseMessageId; | ||||
| 		if (parentId !== null) { | ||||
| 			history.messages[parentId].childrenIds = [ | ||||
| 				...history.messages[parentId].childrenIds, | ||||
| 				responseMessageId | ||||
| 			]; | ||||
| 		} | ||||
| 
 | ||||
| 				window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 		window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 
 | ||||
| 				const res = await fetch( | ||||
| 					`${$settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1'}/chat/completions`, | ||||
| 					{ | ||||
| 						method: 'POST', | ||||
| 						headers: { | ||||
| 							Authorization: `Bearer ${$settings.OPENAI_API_KEY}`, | ||||
| 							'Content-Type': 'application/json' | ||||
| 						}, | ||||
| 						body: JSON.stringify({ | ||||
| 							model: model, | ||||
| 							stream: true, | ||||
| 							messages: [ | ||||
| 								$settings.system | ||||
| 									? { | ||||
| 											role: 'system', | ||||
| 											content: $settings.system | ||||
| 									  } | ||||
| 									: undefined, | ||||
| 								...messages | ||||
| 							] | ||||
| 								.filter((message) => message) | ||||
| 								.map((message) => ({ | ||||
| 									role: message.role, | ||||
| 									...(message.files | ||||
| 										? { | ||||
| 												content: [ | ||||
| 													{ | ||||
| 														type: 'text', | ||||
| 														text: message.content | ||||
| 													}, | ||||
| 													...message.files | ||||
| 														.filter((file) => file.type === 'image') | ||||
| 														.map((file) => ({ | ||||
| 															type: 'image_url', | ||||
| 															image_url: { | ||||
| 																url: file.url | ||||
| 															} | ||||
| 														})) | ||||
| 												] | ||||
| 										  } | ||||
| 										: { content: message.content }) | ||||
| 								})), | ||||
| 							seed: $settings?.options?.seed ?? undefined, | ||||
| 							stop: $settings?.options?.stop ?? undefined, | ||||
| 							temperature: $settings?.options?.temperature ?? undefined, | ||||
| 							top_p: $settings?.options?.top_p ?? undefined, | ||||
| 							num_ctx: $settings?.options?.num_ctx ?? undefined, | ||||
| 							frequency_penalty: $settings?.options?.repeat_penalty ?? undefined, | ||||
| 							max_tokens: $settings?.options?.num_predict ?? undefined | ||||
| 						}) | ||||
| 					} | ||||
| 				).catch((err) => { | ||||
| 					console.log(err); | ||||
| 					return null; | ||||
| 				}); | ||||
| 		const res = await generateOpenAIChatCompletion(localStorage.token, { | ||||
| 			model: model, | ||||
| 			stream: true, | ||||
| 			messages: [ | ||||
| 				$settings.system | ||||
| 					? { | ||||
| 							role: 'system', | ||||
| 							content: $settings.system | ||||
| 					  } | ||||
| 					: undefined, | ||||
| 				...messages | ||||
| 			] | ||||
| 				.filter((message) => message) | ||||
| 				.map((message) => ({ | ||||
| 					role: message.role, | ||||
| 					...(message.files | ||||
| 						? { | ||||
| 								content: [ | ||||
| 									{ | ||||
| 										type: 'text', | ||||
| 										text: message.content | ||||
| 									}, | ||||
| 									...message.files | ||||
| 										.filter((file) => file.type === 'image') | ||||
| 										.map((file) => ({ | ||||
| 											type: 'image_url', | ||||
| 											image_url: { | ||||
| 												url: file.url | ||||
| 											} | ||||
| 										})) | ||||
| 								] | ||||
| 						  } | ||||
| 						: { content: message.content }) | ||||
| 				})), | ||||
| 			seed: $settings?.options?.seed ?? undefined, | ||||
| 			stop: $settings?.options?.stop ?? undefined, | ||||
| 			temperature: $settings?.options?.temperature ?? undefined, | ||||
| 			top_p: $settings?.options?.top_p ?? undefined, | ||||
| 			num_ctx: $settings?.options?.num_ctx ?? undefined, | ||||
| 			frequency_penalty: $settings?.options?.repeat_penalty ?? undefined, | ||||
| 			max_tokens: $settings?.options?.num_predict ?? undefined | ||||
| 		}); | ||||
| 
 | ||||
| 				if (res && res.ok) { | ||||
| 					const reader = res.body | ||||
| 						.pipeThrough(new TextDecoderStream()) | ||||
| 						.pipeThrough(splitStream('\n')) | ||||
| 						.getReader(); | ||||
| 		if (res && res.ok) { | ||||
| 			const reader = res.body | ||||
| 				.pipeThrough(new TextDecoderStream()) | ||||
| 				.pipeThrough(splitStream('\n')) | ||||
| 				.getReader(); | ||||
| 
 | ||||
| 					while (true) { | ||||
| 						const { value, done } = await reader.read(); | ||||
| 						if (done || stopResponseFlag || _chatId !== $chatId) { | ||||
| 							responseMessage.done = true; | ||||
| 							messages = messages; | ||||
| 							break; | ||||
| 						} | ||||
| 
 | ||||
| 						try { | ||||
| 							let lines = value.split('\n'); | ||||
| 
 | ||||
| 							for (const line of lines) { | ||||
| 								if (line !== '') { | ||||
| 									console.log(line); | ||||
| 									if (line === 'data: [DONE]') { | ||||
| 										responseMessage.done = true; | ||||
| 										messages = messages; | ||||
| 									} else { | ||||
| 										let data = JSON.parse(line.replace(/^data: /, '')); | ||||
| 										console.log(data); | ||||
| 
 | ||||
| 										if (responseMessage.content == '' && data.choices[0].delta.content == '\n') { | ||||
| 											continue; | ||||
| 										} else { | ||||
| 											responseMessage.content += data.choices[0].delta.content ?? ''; | ||||
| 											messages = messages; | ||||
| 										} | ||||
| 									} | ||||
| 								} | ||||
| 							} | ||||
| 						} catch (error) { | ||||
| 							console.log(error); | ||||
| 						} | ||||
| 
 | ||||
| 						if ($settings.notificationEnabled && !document.hasFocus()) { | ||||
| 							const notification = new Notification(`OpenAI ${model}`, { | ||||
| 								body: responseMessage.content, | ||||
| 								icon: '/favicon.png' | ||||
| 							}); | ||||
| 						} | ||||
| 
 | ||||
| 						if ($settings.responseAutoCopy) { | ||||
| 							copyToClipboard(responseMessage.content); | ||||
| 						} | ||||
| 
 | ||||
| 						if (autoScroll) { | ||||
| 							window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					if ($chatId == _chatId) { | ||||
| 						chat = await updateChatById(localStorage.token, _chatId, { | ||||
| 							messages: messages, | ||||
| 							history: history | ||||
| 						}); | ||||
| 						await chats.set(await getChatList(localStorage.token)); | ||||
| 					} | ||||
| 				} else { | ||||
| 					if (res !== null) { | ||||
| 						const error = await res.json(); | ||||
| 						console.log(error); | ||||
| 						if ('detail' in error) { | ||||
| 							toast.error(error.detail); | ||||
| 							responseMessage.content = error.detail; | ||||
| 						} else { | ||||
| 							if ('message' in error.error) { | ||||
| 								toast.error(error.error.message); | ||||
| 								responseMessage.content = error.error.message; | ||||
| 							} else { | ||||
| 								toast.error(error.error); | ||||
| 								responseMessage.content = error.error; | ||||
| 							} | ||||
| 						} | ||||
| 					} else { | ||||
| 						toast.error(`Uh-oh! There was an issue connecting to ${model}.`); | ||||
| 						responseMessage.content = `Uh-oh! There was an issue connecting to ${model}.`; | ||||
| 					} | ||||
| 
 | ||||
| 					responseMessage.error = true; | ||||
| 					responseMessage.content = `Uh-oh! There was an issue connecting to ${model}.`; | ||||
| 			while (true) { | ||||
| 				const { value, done } = await reader.read(); | ||||
| 				if (done || stopResponseFlag || _chatId !== $chatId) { | ||||
| 					responseMessage.done = true; | ||||
| 					messages = messages; | ||||
| 					break; | ||||
| 				} | ||||
| 
 | ||||
| 				stopResponseFlag = false; | ||||
| 				await tick(); | ||||
| 				try { | ||||
| 					let lines = value.split('\n'); | ||||
| 
 | ||||
| 					for (const line of lines) { | ||||
| 						if (line !== '') { | ||||
| 							console.log(line); | ||||
| 							if (line === 'data: [DONE]') { | ||||
| 								responseMessage.done = true; | ||||
| 								messages = messages; | ||||
| 							} else { | ||||
| 								let data = JSON.parse(line.replace(/^data: /, '')); | ||||
| 								console.log(data); | ||||
| 
 | ||||
| 								if (responseMessage.content == '' && data.choices[0].delta.content == '\n') { | ||||
| 									continue; | ||||
| 								} else { | ||||
| 									responseMessage.content += data.choices[0].delta.content ?? ''; | ||||
| 									messages = messages; | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} catch (error) { | ||||
| 					console.log(error); | ||||
| 				} | ||||
| 
 | ||||
| 				if ($settings.notificationEnabled && !document.hasFocus()) { | ||||
| 					const notification = new Notification(`OpenAI ${model}`, { | ||||
| 						body: responseMessage.content, | ||||
| 						icon: '/favicon.png' | ||||
| 					}); | ||||
| 				} | ||||
| 
 | ||||
| 				if ($settings.responseAutoCopy) { | ||||
| 					copyToClipboard(responseMessage.content); | ||||
| 				} | ||||
| 
 | ||||
| 				if (autoScroll) { | ||||
| 					window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 				} | ||||
| 
 | ||||
| 				if (messages.length == 2) { | ||||
| 					window.history.replaceState(history.state, '', `/c/${_chatId}`); | ||||
| 					await setChatTitle(_chatId, userPrompt); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if ($chatId == _chatId) { | ||||
| 				chat = await updateChatById(localStorage.token, _chatId, { | ||||
| 					messages: messages, | ||||
| 					history: history | ||||
| 				}); | ||||
| 				await chats.set(await getChatList(localStorage.token)); | ||||
| 			} | ||||
| 		} else { | ||||
| 			if (res !== null) { | ||||
| 				const error = await res.json(); | ||||
| 				console.log(error); | ||||
| 				if ('detail' in error) { | ||||
| 					toast.error(error.detail); | ||||
| 					responseMessage.content = error.detail; | ||||
| 				} else { | ||||
| 					if ('message' in error.error) { | ||||
| 						toast.error(error.error.message); | ||||
| 						responseMessage.content = error.error.message; | ||||
| 					} else { | ||||
| 						toast.error(error.error); | ||||
| 						responseMessage.content = error.error; | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				toast.error(`Uh-oh! There was an issue connecting to ${model}.`); | ||||
| 				responseMessage.content = `Uh-oh! There was an issue connecting to ${model}.`; | ||||
| 			} | ||||
| 
 | ||||
| 			responseMessage.error = true; | ||||
| 			responseMessage.content = `Uh-oh! There was an issue connecting to ${model}.`; | ||||
| 			responseMessage.done = true; | ||||
| 			messages = messages; | ||||
| 		} | ||||
| 
 | ||||
| 		stopResponseFlag = false; | ||||
| 		await tick(); | ||||
| 
 | ||||
| 		if (autoScroll) { | ||||
| 			window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 		} | ||||
| 
 | ||||
| 		if (messages.length == 2) { | ||||
| 			window.history.replaceState(history.state, '', `/c/${_chatId}`); | ||||
| 			await setChatTitle(_chatId, userPrompt); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy Jaeryang Baek
						Timothy Jaeryang Baek