forked from open-webui/open-webui
		
	feat: terminate request on user stop
This commit is contained in:
		
							parent
							
								
									684bdf5151
								
							
						
					
					
						commit
						442e3d978a
					
				
					 4 changed files with 170 additions and 86 deletions
				
			
		|  | @ -206,9 +206,11 @@ export const generatePrompt = async (token: string = '', model: string, conversa | |||
| }; | ||||
| 
 | ||||
| export const generateChatCompletion = async (token: string = '', body: object) => { | ||||
| 	let controller = new AbortController(); | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/chat`, { | ||||
| 		signal: controller.signal, | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			'Content-Type': 'text/event-stream', | ||||
|  | @ -224,6 +226,27 @@ export const generateChatCompletion = async (token: string = '', body: object) = | |||
| 		throw error; | ||||
| 	} | ||||
| 
 | ||||
| 	return [res, controller]; | ||||
| }; | ||||
| 
 | ||||
| export const cancelChatCompletion = async (token: string = '', requestId: string) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OLLAMA_API_BASE_URL}/cancel/${requestId}`, { | ||||
| 		method: 'GET', | ||||
| 		headers: { | ||||
| 			'Content-Type': 'text/event-stream', | ||||
| 			Authorization: `Bearer ${token}` | ||||
| 		} | ||||
| 	}).catch((err) => { | ||||
| 		error = err; | ||||
| 		return null; | ||||
| 	}); | ||||
| 
 | ||||
| 	if (error) { | ||||
| 		throw error; | ||||
| 	} | ||||
| 
 | ||||
| 	return res; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
| 	import { models, modelfiles, user, settings, chats, chatId, config } from '$lib/stores'; | ||||
| 	import { copyToClipboard, splitStream } from '$lib/utils'; | ||||
| 
 | ||||
| 	import { generateChatCompletion, generateTitle } from '$lib/apis/ollama'; | ||||
| 	import { generateChatCompletion, cancelChatCompletion, generateTitle } from '$lib/apis/ollama'; | ||||
| 	import { createNewChat, getChatList, updateChatById } from '$lib/apis/chats'; | ||||
| 	import { queryVectorDB } from '$lib/apis/rag'; | ||||
| 	import { generateOpenAIChatCompletion } from '$lib/apis/openai'; | ||||
|  | @ -24,6 +24,8 @@ | |||
| 	let autoScroll = true; | ||||
| 	let processing = ''; | ||||
| 
 | ||||
| 	let currentRequestId = null; | ||||
| 
 | ||||
| 	let selectedModels = ['']; | ||||
| 
 | ||||
| 	let selectedModelfile = null; | ||||
|  | @ -279,7 +281,7 @@ | |||
| 		// Scroll down | ||||
| 		window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 
 | ||||
| 		const res = await generateChatCompletion(localStorage.token, { | ||||
| 		const [res, controller] = await generateChatCompletion(localStorage.token, { | ||||
| 			model: model, | ||||
| 			messages: [ | ||||
| 				$settings.system | ||||
|  | @ -307,6 +309,8 @@ | |||
| 		}); | ||||
| 
 | ||||
| 		if (res && res.ok) { | ||||
| 			console.log('controller', controller); | ||||
| 
 | ||||
| 			const reader = res.body | ||||
| 				.pipeThrough(new TextDecoderStream()) | ||||
| 				.pipeThrough(splitStream('\n')) | ||||
|  | @ -317,6 +321,14 @@ | |||
| 				if (done || stopResponseFlag || _chatId !== $chatId) { | ||||
| 					responseMessage.done = true; | ||||
| 					messages = messages; | ||||
| 
 | ||||
| 					if (stopResponseFlag) { | ||||
| 						controller.abort('User: Stop Response'); | ||||
| 						await cancelChatCompletion(localStorage.token, currentRequestId); | ||||
| 					} | ||||
| 
 | ||||
| 					currentRequestId = null; | ||||
| 
 | ||||
| 					break; | ||||
| 				} | ||||
| 
 | ||||
|  | @ -332,52 +344,57 @@ | |||
| 								throw data; | ||||
| 							} | ||||
| 
 | ||||
| 							if (data.done == false) { | ||||
| 								if (responseMessage.content == '' && data.message.content == '\n') { | ||||
| 									continue; | ||||
| 								} else { | ||||
| 									responseMessage.content += data.message.content; | ||||
| 									messages = messages; | ||||
| 								} | ||||
| 							if ('id' in data) { | ||||
| 								console.log(data); | ||||
| 								currentRequestId = data.id; | ||||
| 							} else { | ||||
| 								responseMessage.done = true; | ||||
| 								if (data.done == false) { | ||||
| 									if (responseMessage.content == '' && data.message.content == '\n') { | ||||
| 										continue; | ||||
| 									} else { | ||||
| 										responseMessage.content += data.message.content; | ||||
| 										messages = messages; | ||||
| 									} | ||||
| 								} else { | ||||
| 									responseMessage.done = true; | ||||
| 
 | ||||
| 								if (responseMessage.content == '') { | ||||
| 									responseMessage.error = true; | ||||
| 									responseMessage.content = | ||||
| 										'Oops! No text generated from Ollama, Please try again.'; | ||||
| 								} | ||||
| 									if (responseMessage.content == '') { | ||||
| 										responseMessage.error = true; | ||||
| 										responseMessage.content = | ||||
| 											'Oops! No text generated from Ollama, Please try again.'; | ||||
| 									} | ||||
| 
 | ||||
| 								responseMessage.context = data.context ?? null; | ||||
| 								responseMessage.info = { | ||||
| 									total_duration: data.total_duration, | ||||
| 									load_duration: data.load_duration, | ||||
| 									sample_count: data.sample_count, | ||||
| 									sample_duration: data.sample_duration, | ||||
| 									prompt_eval_count: data.prompt_eval_count, | ||||
| 									prompt_eval_duration: data.prompt_eval_duration, | ||||
| 									eval_count: data.eval_count, | ||||
| 									eval_duration: data.eval_duration | ||||
| 								}; | ||||
| 								messages = messages; | ||||
| 									responseMessage.context = data.context ?? null; | ||||
| 									responseMessage.info = { | ||||
| 										total_duration: data.total_duration, | ||||
| 										load_duration: data.load_duration, | ||||
| 										sample_count: data.sample_count, | ||||
| 										sample_duration: data.sample_duration, | ||||
| 										prompt_eval_count: data.prompt_eval_count, | ||||
| 										prompt_eval_duration: data.prompt_eval_duration, | ||||
| 										eval_count: data.eval_count, | ||||
| 										eval_duration: data.eval_duration | ||||
| 									}; | ||||
| 									messages = messages; | ||||
| 
 | ||||
| 								if ($settings.notificationEnabled && !document.hasFocus()) { | ||||
| 									const notification = new Notification( | ||||
| 										selectedModelfile | ||||
| 											? `${ | ||||
| 													selectedModelfile.title.charAt(0).toUpperCase() + | ||||
| 													selectedModelfile.title.slice(1) | ||||
| 											  }` | ||||
| 											: `Ollama - ${model}`, | ||||
| 										{ | ||||
| 											body: responseMessage.content, | ||||
| 											icon: selectedModelfile?.imageUrl ?? '/favicon.png' | ||||
| 										} | ||||
| 									); | ||||
| 								} | ||||
| 									if ($settings.notificationEnabled && !document.hasFocus()) { | ||||
| 										const notification = new Notification( | ||||
| 											selectedModelfile | ||||
| 												? `${ | ||||
| 														selectedModelfile.title.charAt(0).toUpperCase() + | ||||
| 														selectedModelfile.title.slice(1) | ||||
| 												  }` | ||||
| 												: `Ollama - ${model}`, | ||||
| 											{ | ||||
| 												body: responseMessage.content, | ||||
| 												icon: selectedModelfile?.imageUrl ?? '/favicon.png' | ||||
| 											} | ||||
| 										); | ||||
| 									} | ||||
| 
 | ||||
| 								if ($settings.responseAutoCopy) { | ||||
| 									copyToClipboard(responseMessage.content); | ||||
| 									if ($settings.responseAutoCopy) { | ||||
| 										copyToClipboard(responseMessage.content); | ||||
| 									} | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
|  |  | |||
|  | @ -297,7 +297,7 @@ | |||
| 		// Scroll down | ||||
| 		window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 
 | ||||
| 		const res = await generateChatCompletion(localStorage.token, { | ||||
| 		const [res, controller] = await generateChatCompletion(localStorage.token, { | ||||
| 			model: model, | ||||
| 			messages: [ | ||||
| 				$settings.system | ||||
|  | @ -335,6 +335,10 @@ | |||
| 				if (done || stopResponseFlag || _chatId !== $chatId) { | ||||
| 					responseMessage.done = true; | ||||
| 					messages = messages; | ||||
| 
 | ||||
| 					if (stopResponseFlag) { | ||||
| 						controller.abort('User: Stop Response'); | ||||
| 					} | ||||
| 					break; | ||||
| 				} | ||||
| 
 | ||||
|  | @ -350,52 +354,56 @@ | |||
| 								throw data; | ||||
| 							} | ||||
| 
 | ||||
| 							if (data.done == false) { | ||||
| 								if (responseMessage.content == '' && data.message.content == '\n') { | ||||
| 									continue; | ||||
| 								} else { | ||||
| 									responseMessage.content += data.message.content; | ||||
| 									messages = messages; | ||||
| 								} | ||||
| 							if ('id' in data) { | ||||
| 								console.log(data); | ||||
| 							} else { | ||||
| 								responseMessage.done = true; | ||||
| 								if (data.done == false) { | ||||
| 									if (responseMessage.content == '' && data.message.content == '\n') { | ||||
| 										continue; | ||||
| 									} else { | ||||
| 										responseMessage.content += data.message.content; | ||||
| 										messages = messages; | ||||
| 									} | ||||
| 								} else { | ||||
| 									responseMessage.done = true; | ||||
| 
 | ||||
| 								if (responseMessage.content == '') { | ||||
| 									responseMessage.error = true; | ||||
| 									responseMessage.content = | ||||
| 										'Oops! No text generated from Ollama, Please try again.'; | ||||
| 								} | ||||
| 									if (responseMessage.content == '') { | ||||
| 										responseMessage.error = true; | ||||
| 										responseMessage.content = | ||||
| 											'Oops! No text generated from Ollama, Please try again.'; | ||||
| 									} | ||||
| 
 | ||||
| 								responseMessage.context = data.context ?? null; | ||||
| 								responseMessage.info = { | ||||
| 									total_duration: data.total_duration, | ||||
| 									load_duration: data.load_duration, | ||||
| 									sample_count: data.sample_count, | ||||
| 									sample_duration: data.sample_duration, | ||||
| 									prompt_eval_count: data.prompt_eval_count, | ||||
| 									prompt_eval_duration: data.prompt_eval_duration, | ||||
| 									eval_count: data.eval_count, | ||||
| 									eval_duration: data.eval_duration | ||||
| 								}; | ||||
| 								messages = messages; | ||||
| 									responseMessage.context = data.context ?? null; | ||||
| 									responseMessage.info = { | ||||
| 										total_duration: data.total_duration, | ||||
| 										load_duration: data.load_duration, | ||||
| 										sample_count: data.sample_count, | ||||
| 										sample_duration: data.sample_duration, | ||||
| 										prompt_eval_count: data.prompt_eval_count, | ||||
| 										prompt_eval_duration: data.prompt_eval_duration, | ||||
| 										eval_count: data.eval_count, | ||||
| 										eval_duration: data.eval_duration | ||||
| 									}; | ||||
| 									messages = messages; | ||||
| 
 | ||||
| 								if ($settings.notificationEnabled && !document.hasFocus()) { | ||||
| 									const notification = new Notification( | ||||
| 										selectedModelfile | ||||
| 											? `${ | ||||
| 													selectedModelfile.title.charAt(0).toUpperCase() + | ||||
| 													selectedModelfile.title.slice(1) | ||||
| 											  }` | ||||
| 											: `Ollama - ${model}`, | ||||
| 										{ | ||||
| 											body: responseMessage.content, | ||||
| 											icon: selectedModelfile?.imageUrl ?? '/favicon.png' | ||||
| 										} | ||||
| 									); | ||||
| 								} | ||||
| 									if ($settings.notificationEnabled && !document.hasFocus()) { | ||||
| 										const notification = new Notification( | ||||
| 											selectedModelfile | ||||
| 												? `${ | ||||
| 														selectedModelfile.title.charAt(0).toUpperCase() + | ||||
| 														selectedModelfile.title.slice(1) | ||||
| 												  }` | ||||
| 												: `Ollama - ${model}`, | ||||
| 											{ | ||||
| 												body: responseMessage.content, | ||||
| 												icon: selectedModelfile?.imageUrl ?? '/favicon.png' | ||||
| 											} | ||||
| 										); | ||||
| 									} | ||||
| 
 | ||||
| 								if ($settings.responseAutoCopy) { | ||||
| 									copyToClipboard(responseMessage.content); | ||||
| 									if ($settings.responseAutoCopy) { | ||||
| 										copyToClipboard(responseMessage.content); | ||||
| 									} | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy J. Baek
						Timothy J. Baek