Unverified Commit 748cb7d4 authored by Timothy Jaeryang Baek's avatar Timothy Jaeryang Baek Committed by GitHub
Browse files

Merge pull request #1654 from open-webui/dev

0.1.121
parents b3da09f5 348186c4
...@@ -17,11 +17,17 @@ ...@@ -17,11 +17,17 @@
let titleAutoGenerateModelExternal = ''; let titleAutoGenerateModelExternal = '';
let fullScreenMode = false; let fullScreenMode = false;
let titleGenerationPrompt = ''; let titleGenerationPrompt = '';
let splitLargeChunks = false;
// Interface // Interface
let promptSuggestions = []; let promptSuggestions = [];
let showUsername = false; let showUsername = false;
const toggleSplitLargeChunks = async () => {
splitLargeChunks = !splitLargeChunks;
saveSettings({ splitLargeChunks: splitLargeChunks });
};
const toggleFullScreenMode = async () => { const toggleFullScreenMode = async () => {
fullScreenMode = !fullScreenMode; fullScreenMode = !fullScreenMode;
saveSettings({ fullScreenMode: fullScreenMode }); saveSettings({ fullScreenMode: fullScreenMode });
...@@ -197,6 +203,28 @@ ...@@ -197,6 +203,28 @@
</button> </button>
</div> </div>
</div> </div>
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Fluidly stream large external response chunks')}
</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
on:click={() => {
toggleSplitLargeChunks();
}}
type="button"
>
{#if splitLargeChunks === true}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if}
</button>
</div>
</div>
</div> </div>
<hr class=" dark:border-gray-700" /> <hr class=" dark:border-gray-700" />
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
uploadModel uploadModel
} from '$lib/apis/ollama'; } from '$lib/apis/ollama';
import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants'; import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
import { WEBUI_NAME, models, user } from '$lib/stores'; import { WEBUI_NAME, models, MODEL_DOWNLOAD_POOL, user } from '$lib/stores';
import { splitStream } from '$lib/utils'; import { splitStream } from '$lib/utils';
import { onMount, getContext } from 'svelte'; import { onMount, getContext } from 'svelte';
import { addLiteLLMModel, deleteLiteLLMModel, getLiteLLMModelInfo } from '$lib/apis/litellm'; import { addLiteLLMModel, deleteLiteLLMModel, getLiteLLMModelInfo } from '$lib/apis/litellm';
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
let liteLLMRPM = ''; let liteLLMRPM = '';
let liteLLMMaxTokens = ''; let liteLLMMaxTokens = '';
let deleteLiteLLMModelId = ''; let deleteLiteLLMModelName = '';
$: liteLLMModelName = liteLLMModel; $: liteLLMModelName = liteLLMModel;
...@@ -50,12 +50,6 @@ ...@@ -50,12 +50,6 @@
let showExperimentalOllama = false; let showExperimentalOllama = false;
let ollamaVersion = ''; let ollamaVersion = '';
const MAX_PARALLEL_DOWNLOADS = 3; const MAX_PARALLEL_DOWNLOADS = 3;
const modelDownloadQueue = queue(
(task: { modelName: string }, cb) =>
pullModelHandlerProcessor({ modelName: task.modelName, callback: cb }),
MAX_PARALLEL_DOWNLOADS
);
let modelDownloadStatus: Record<string, any> = {};
let modelTransferring = false; let modelTransferring = false;
let modelTag = ''; let modelTag = '';
...@@ -140,7 +134,8 @@ ...@@ -140,7 +134,8 @@
const pullModelHandler = async () => { const pullModelHandler = async () => {
const sanitizedModelTag = modelTag.trim().replace(/^ollama\s+(run|pull)\s+/, ''); const sanitizedModelTag = modelTag.trim().replace(/^ollama\s+(run|pull)\s+/, '');
if (modelDownloadStatus[sanitizedModelTag]) { console.log($MODEL_DOWNLOAD_POOL);
if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag]) {
toast.error( toast.error(
$i18n.t(`Model '{{modelTag}}' is already in queue for downloading.`, { $i18n.t(`Model '{{modelTag}}' is already in queue for downloading.`, {
modelTag: sanitizedModelTag modelTag: sanitizedModelTag
...@@ -148,40 +143,117 @@ ...@@ -148,40 +143,117 @@
); );
return; return;
} }
if (Object.keys(modelDownloadStatus).length === 3) { if (Object.keys($MODEL_DOWNLOAD_POOL).length === MAX_PARALLEL_DOWNLOADS) {
toast.error( toast.error(
$i18n.t('Maximum of 3 models can be downloaded simultaneously. Please try again later.') $i18n.t('Maximum of 3 models can be downloaded simultaneously. Please try again later.')
); );
return; return;
} }
modelTransferring = true; const res = await pullModel(localStorage.token, sanitizedModelTag, '0').catch((error) => {
toast.error(error);
return null;
});
if (res) {
const reader = res.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(splitStream('\n'))
.getReader();
modelDownloadQueue.push( while (true) {
{ modelName: sanitizedModelTag }, try {
async (data: { modelName: string; success: boolean; error?: Error }) => { const { value, done } = await reader.read();
const { modelName } = data; if (done) break;
// Remove the downloaded model
delete modelDownloadStatus[modelName];
modelDownloadStatus = { ...modelDownloadStatus }; let lines = value.split('\n');
for (const line of lines) {
if (line !== '') {
let data = JSON.parse(line);
console.log(data);
if (data.error) {
throw data.error;
}
if (data.detail) {
throw data.detail;
}
if (data.id) {
MODEL_DOWNLOAD_POOL.set({
...$MODEL_DOWNLOAD_POOL,
[sanitizedModelTag]: {
...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
requestId: data.id,
reader,
done: false
}
});
console.log(data);
}
if (data.status) {
if (data.digest) {
let downloadProgress = 0;
if (data.completed) {
downloadProgress = Math.round((data.completed / data.total) * 1000) / 10;
} else {
downloadProgress = 100;
}
if (!data.success) { MODEL_DOWNLOAD_POOL.set({
toast.error(data.error); ...$MODEL_DOWNLOAD_POOL,
} else { [sanitizedModelTag]: {
toast.success( ...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
$i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, { modelName }) pullProgress: downloadProgress,
); digest: data.digest
}
});
} else {
toast.success(data.status);
const notification = new Notification($WEBUI_NAME, { MODEL_DOWNLOAD_POOL.set({
body: $i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, { modelName }), ...$MODEL_DOWNLOAD_POOL,
icon: `${WEBUI_BASE_URL}/static/favicon.png` [sanitizedModelTag]: {
}); ...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
done: data.status === 'success'
}
});
}
}
}
}
} catch (error) {
console.log(error);
if (typeof error !== 'string') {
error = error.message;
}
models.set(await getModels()); toast.error(error);
// opts.callback({ success: false, error, modelName: opts.modelName });
} }
} }
);
console.log($MODEL_DOWNLOAD_POOL[sanitizedModelTag]);
if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag].done) {
toast.success(
$i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, {
modelName: sanitizedModelTag
})
);
models.set(await getModels(localStorage.token));
} else {
toast.error('Download canceled');
}
delete $MODEL_DOWNLOAD_POOL[sanitizedModelTag];
MODEL_DOWNLOAD_POOL.set({
...$MODEL_DOWNLOAD_POOL
});
}
modelTag = ''; modelTag = '';
modelTransferring = false; modelTransferring = false;
...@@ -352,88 +424,18 @@ ...@@ -352,88 +424,18 @@
models.set(await getModels()); models.set(await getModels());
}; };
const pullModelHandlerProcessor = async (opts: { modelName: string; callback: Function }) => { const cancelModelPullHandler = async (model: string) => {
const res = await pullModel(localStorage.token, opts.modelName, selectedOllamaUrlIdx).catch( const { reader, requestId } = $MODEL_DOWNLOAD_POOL[model];
(error) => { if (reader) {
opts.callback({ success: false, error, modelName: opts.modelName }); await reader.cancel();
return null;
}
);
if (res) {
const reader = res.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(splitStream('\n'))
.getReader();
while (true) {
try {
const { value, done } = await reader.read();
if (done) break;
let lines = value.split('\n');
for (const line of lines) {
if (line !== '') {
let data = JSON.parse(line);
console.log(data);
if (data.error) {
throw data.error;
}
if (data.detail) {
throw data.detail;
}
if (data.id) {
modelDownloadStatus[opts.modelName] = {
...modelDownloadStatus[opts.modelName],
requestId: data.id,
reader,
done: false
};
console.log(data);
}
if (data.status) {
if (data.digest) {
let downloadProgress = 0;
if (data.completed) {
downloadProgress = Math.round((data.completed / data.total) * 1000) / 10;
} else {
downloadProgress = 100;
}
modelDownloadStatus[opts.modelName] = {
...modelDownloadStatus[opts.modelName],
pullProgress: downloadProgress,
digest: data.digest
};
} else {
toast.success(data.status);
modelDownloadStatus[opts.modelName] = {
...modelDownloadStatus[opts.modelName],
done: data.status === 'success'
};
}
}
}
}
} catch (error) {
console.log(error);
if (typeof error !== 'string') {
error = error.message;
}
opts.callback({ success: false, error, modelName: opts.modelName });
}
}
console.log(modelDownloadStatus[opts.modelName]);
if (modelDownloadStatus[opts.modelName].done) { await cancelOllamaRequest(localStorage.token, requestId);
opts.callback({ success: true, modelName: opts.modelName }); delete $MODEL_DOWNLOAD_POOL[model];
} else { MODEL_DOWNLOAD_POOL.set({
opts.callback({ success: false, error: 'Download canceled', modelName: opts.modelName }); ...$MODEL_DOWNLOAD_POOL
} });
await deleteModel(localStorage.token, model);
toast.success(`${model} download has been canceled`);
} }
}; };
...@@ -472,7 +474,7 @@ ...@@ -472,7 +474,7 @@
}; };
const deleteLiteLLMModelHandler = async () => { const deleteLiteLLMModelHandler = async () => {
const res = await deleteLiteLLMModel(localStorage.token, deleteLiteLLMModelId).catch( const res = await deleteLiteLLMModel(localStorage.token, deleteLiteLLMModelName).catch(
(error) => { (error) => {
toast.error(error); toast.error(error);
return null; return null;
...@@ -485,7 +487,7 @@ ...@@ -485,7 +487,7 @@
} }
} }
deleteLiteLLMModelId = ''; deleteLiteLLMModelName = '';
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token); liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
models.set(await getModels()); models.set(await getModels());
}; };
...@@ -503,18 +505,6 @@ ...@@ -503,18 +505,6 @@
ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false); ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token); liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
}); });
const cancelModelPullHandler = async (model: string) => {
const { reader, requestId } = modelDownloadStatus[model];
if (reader) {
await reader.cancel();
await cancelOllamaRequest(localStorage.token, requestId);
delete modelDownloadStatus[model];
await deleteModel(localStorage.token, model);
toast.success(`${model} download has been canceled`);
}
};
</script> </script>
<div class="flex flex-col h-full justify-between text-sm"> <div class="flex flex-col h-full justify-between text-sm">
...@@ -643,9 +633,9 @@ ...@@ -643,9 +633,9 @@
> >
</div> </div>
{#if Object.keys(modelDownloadStatus).length > 0} {#if Object.keys($MODEL_DOWNLOAD_POOL).length > 0}
{#each Object.keys(modelDownloadStatus) as model} {#each Object.keys($MODEL_DOWNLOAD_POOL) as model}
{#if 'pullProgress' in modelDownloadStatus[model]} {#if 'pullProgress' in $MODEL_DOWNLOAD_POOL[model]}
<div class="flex flex-col"> <div class="flex flex-col">
<div class="font-medium mb-1">{model}</div> <div class="font-medium mb-1">{model}</div>
<div class=""> <div class="">
...@@ -655,10 +645,10 @@ ...@@ -655,10 +645,10 @@
class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full" class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full"
style="width: {Math.max( style="width: {Math.max(
15, 15,
modelDownloadStatus[model].pullProgress ?? 0 $MODEL_DOWNLOAD_POOL[model].pullProgress ?? 0
)}%" )}%"
> >
{modelDownloadStatus[model].pullProgress ?? 0}% {$MODEL_DOWNLOAD_POOL[model].pullProgress ?? 0}%
</div> </div>
</div> </div>
...@@ -689,9 +679,9 @@ ...@@ -689,9 +679,9 @@
</button> </button>
</Tooltip> </Tooltip>
</div> </div>
{#if 'digest' in modelDownloadStatus[model]} {#if 'digest' in $MODEL_DOWNLOAD_POOL[model]}
<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;"> <div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
{modelDownloadStatus[model].digest} {$MODEL_DOWNLOAD_POOL[model].digest}
</div> </div>
{/if} {/if}
</div> </div>
...@@ -1099,14 +1089,14 @@ ...@@ -1099,14 +1089,14 @@
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={deleteLiteLLMModelId} bind:value={deleteLiteLLMModelName}
placeholder={$i18n.t('Select a model')} placeholder={$i18n.t('Select a model')}
> >
{#if !deleteLiteLLMModelId} {#if !deleteLiteLLMModelName}
<option value="" disabled selected>{$i18n.t('Select a model')}</option> <option value="" disabled selected>{$i18n.t('Select a model')}</option>
{/if} {/if}
{#each liteLLMModelInfo as model} {#each liteLLMModelInfo as model}
<option value={model.model_info.id} class="bg-gray-100 dark:bg-gray-700" <option value={model.model_name} class="bg-gray-100 dark:bg-gray-700"
>{model.model_name}</option >{model.model_name}</option
> >
{/each} {/each}
......
...@@ -180,7 +180,7 @@ ...@@ -180,7 +180,7 @@
} }
}} }}
> >
<option value="">{$i18n.t('Default (SentenceTransformer)')}</option> <option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
<option value="ollama">{$i18n.t('Ollama')}</option> <option value="ollama">{$i18n.t('Ollama')}</option>
<option value="openai">{$i18n.t('OpenAI')}</option> <option value="openai">{$i18n.t('OpenAI')}</option>
</select> </select>
......
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
<div class="flex flex-col md:flex-row w-full px-5 py-4 md:space-x-4 dark:text-gray-200"> <div class="flex flex-col md:flex-row w-full px-5 py-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6"> <div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
{#if chats.length > 0} {#if chats.length > 0}
<div class="text-left text-sm w-full mb-4 h-[22rem] overflow-y-scroll"> <div class="text-left text-sm w-full mb-4 max-h-[22rem] overflow-y-scroll">
<div class="relative overflow-x-auto"> <div class="relative overflow-x-auto">
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto"> <table class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto">
<thead <thead
......
...@@ -152,6 +152,7 @@ ...@@ -152,6 +152,7 @@
"File Mode": "", "File Mode": "",
"File not found.": "", "File not found.": "",
"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "", "Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "",
"Fluidly stream large external response chunks": "",
"Focus chat input": "", "Focus chat input": "",
"Format your variables using square brackets like this:": "", "Format your variables using square brackets like this:": "",
"From (Base Model)": "", "From (Base Model)": "",
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
"Click here to check other modelfiles.": "Klik hier om andere modelfiles te controleren.", "Click here to check other modelfiles.": "Klik hier om andere modelfiles te controleren.",
"Click here to select": "Klik hier om te selecteren", "Click here to select": "Klik hier om te selecteren",
"Click here to select documents.": "Klik hier om documenten te selecteren", "Click here to select documents.": "Klik hier om documenten te selecteren",
"click here.": "click here.", "click here.": "klik hier.",
"Click on the user role button to change a user's role.": "Klik op de gebruikersrol knop om de rol van een gebruiker te wijzigen.", "Click on the user role button to change a user's role.": "Klik op de gebruikersrol knop om de rol van een gebruiker te wijzigen.",
"Close": "Sluiten", "Close": "Sluiten",
"Collection": "Verzameling", "Collection": "Verzameling",
......
...@@ -2,39 +2,39 @@ ...@@ -2,39 +2,39 @@
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' или '-1' для не истечение.", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' или '-1' для не истечение.",
"(Beta)": "(бета)", "(Beta)": "(бета)",
"(e.g. `sh webui.sh --api`)": "(например: `sh webui.sh --api`)", "(e.g. `sh webui.sh --api`)": "(например: `sh webui.sh --api`)",
"(latest)": "(новый)", "(latest)": "(последний)",
"{{modelName}} is thinking...": "{{modelName}} это думает...", "{{modelName}} is thinking...": "{{modelName}} думает...",
"{{webUIName}} Backend Required": "{{webUIName}} бэкенд требуемый", "{{webUIName}} Backend Required": "{{webUIName}} бэкенд требуемый",
"a user": "юзер", "a user": "пользователь",
"About": тносительно", "About": б",
"Account": "Аккаунт", "Account": "Аккаунт",
"Action": "Действие", "Action": "Действие",
"Add a model": "Добавьте модель", "Add a model": "Добавьте модель",
"Add a model tag name": "Добавьте тэг модели имя", "Add a model tag name": "Добавьте имя тэга модели",
"Add a short description about what this modelfile does": "Добавьте краткое описание, что делает этот моделифайл", "Add a short description about what this modelfile does": "Добавьте краткое описание, что делает этот моделфайл",
"Add a short title for this prompt": "Добавьте краткое название для этого взаимодействия", "Add a short title for this prompt": "Добавьте краткий заголовок для этого ввода",
"Add a tag": "Добавьте тэг", "Add a tag": "Добавьте тэг",
"Add Docs": "Добавьте документы", "Add Docs": "Добавьте документы",
"Add Files": "Добавьте файлы", "Add Files": "Добавьте файлы",
"Add message": "Добавьте message", "Add message": "Добавьте сообщение",
"add tags": "Добавьте тэгы", "add tags": "Добавьте тэгы",
"Adjusting these settings will apply changes universally to all users.": "Регулирующий этих настроек приведет к изменениям для все юзеры.", "Adjusting these settings will apply changes universally to all users.": "Регулирующий этих настроек приведет к изменениям для все пользователей.",
"admin": "админ", "admin": "админ",
"Admin Panel": "Панель админ", "Admin Panel": "Панель админ",
"Admin Settings": "Настройки админ", "Admin Settings": "Настройки админ",
"Advanced Parameters": "Расширенные Параметры", "Advanced Parameters": "Расширенные Параметры",
"all": "всё", "all": "всё",
"All Users": "Всё юзеры", "All Users": "Все пользователи",
"Allow": "Дозволять", "Allow": "Разрешить",
"Allow Chat Deletion": "Дозволять удаление чат", "Allow Chat Deletion": "Дозволять удаление чат",
"alphanumeric characters and hyphens": "буквенно цифровые символы и дефисы", "alphanumeric characters and hyphens": "буквенно цифровые символы и дефисы",
"Already have an account?": "у вас есть аккаунт уже?", "Already have an account?": "у вас уже есть аккаунт?",
"an assistant": "ассистент", "an assistant": "ассистент",
"and": "и", "and": "и",
"API Base URL": "Базовый адрес API", "API Base URL": "Базовый адрес API",
"API Key": "Ключ API", "API Key": "Ключ API",
"API RPM": "API RPM", "API RPM": "API RPM",
"are allowed - Activate this command by typing": "разрешено - активируйте эту команду набором", "are allowed - Activate this command by typing": "разрешено - активируйте эту команду вводом",
"Are you sure?": "Вы уверены?", "Are you sure?": "Вы уверены?",
"Audio": "Аудио", "Audio": "Аудио",
"Auto-playback response": "Автоматическое воспроизведение ответа", "Auto-playback response": "Автоматическое воспроизведение ответа",
......
import { APP_NAME } from '$lib/constants'; import { APP_NAME } from '$lib/constants';
import { writable } from 'svelte/store'; import { type Writable, writable } from 'svelte/store';
// Backend // Backend
export const WEBUI_NAME = writable(APP_NAME); export const WEBUI_NAME = writable(APP_NAME);
export const config = writable(undefined); export const config: Writable<Config | undefined> = writable(undefined);
export const user = writable(undefined); export const user: Writable<SessionUser | undefined> = writable(undefined);
// Frontend // Frontend
export const MODEL_DOWNLOAD_POOL = writable({}); export const MODEL_DOWNLOAD_POOL = writable({});
...@@ -14,10 +14,10 @@ export const chatId = writable(''); ...@@ -14,10 +14,10 @@ export const chatId = writable('');
export const chats = writable([]); export const chats = writable([]);
export const tags = writable([]); export const tags = writable([]);
export const models = writable([]); export const models: Writable<Model[]> = writable([]);
export const modelfiles = writable([]); export const modelfiles = writable([]);
export const prompts = writable([]); export const prompts: Writable<Prompt[]> = writable([]);
export const documents = writable([ export const documents = writable([
{ {
collection_name: 'collection_name', collection_name: 'collection_name',
...@@ -33,6 +33,109 @@ export const documents = writable([ ...@@ -33,6 +33,109 @@ export const documents = writable([
} }
]); ]);
export const settings = writable({}); export const settings: Writable<Settings> = writable({});
export const showSettings = writable(false); export const showSettings = writable(false);
export const showChangelog = writable(false); export const showChangelog = writable(false);
type Model = OpenAIModel | OllamaModel;
type OpenAIModel = {
id: string;
name: string;
external: boolean;
source?: string;
};
type OllamaModel = {
id: string;
name: string;
// Ollama specific fields
details: OllamaModelDetails;
size: number;
description: string;
model: string;
modified_at: string;
digest: string;
};
type OllamaModelDetails = {
parent_model: string;
format: string;
family: string;
families: string[] | null;
parameter_size: string;
quantization_level: string;
};
type Settings = {
models?: string[];
conversationMode?: boolean;
speechAutoSend?: boolean;
responseAutoPlayback?: boolean;
audio?: AudioSettings;
showUsername?: boolean;
saveChatHistory?: boolean;
notificationEnabled?: boolean;
title?: TitleSettings;
system?: string;
requestFormat?: string;
keepAlive?: string;
seed?: number;
temperature?: string;
repeat_penalty?: string;
top_k?: string;
top_p?: string;
num_ctx?: string;
options?: ModelOptions;
};
type ModelOptions = {
stop?: boolean;
};
type AudioSettings = {
STTEngine?: string;
TTSEngine?: string;
speaker?: string;
};
type TitleSettings = {
auto?: boolean;
model?: string;
modelExternal?: string;
prompt?: string;
};
type Prompt = {
command: string;
user_id: string;
title: string;
content: string;
timestamp: number;
};
type Config = {
status?: boolean;
name?: string;
version?: string;
default_locale?: string;
images?: boolean;
default_models?: string[];
default_prompt_suggestions?: PromptSuggestion[];
trusted_header_auth?: boolean;
};
type PromptSuggestion = {
content: string;
title: [string, string];
};
type SessionUser = {
id: string;
email: string;
name: string;
role: string;
profile_image_url: string;
};
...@@ -35,7 +35,6 @@ export const sanitizeResponseContent = (content: string) => { ...@@ -35,7 +35,6 @@ export const sanitizeResponseContent = (content: string) => {
.replace(/<\|[a-z]+\|$/, '') .replace(/<\|[a-z]+\|$/, '')
.replace(/<$/, '') .replace(/<$/, '')
.replaceAll(/<\|[a-z]+\|>/g, ' ') .replaceAll(/<\|[a-z]+\|>/g, ' ')
.replaceAll(/<br\s?\/?>/gi, '\n')
.replaceAll('<', '&lt;') .replaceAll('<', '&lt;')
.trim(); .trim();
}; };
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
import { RAGTemplate } from '$lib/utils/rag'; import { RAGTemplate } from '$lib/utils/rag';
import { LITELLM_API_BASE_URL, OLLAMA_API_BASE_URL, OPENAI_API_BASE_URL } from '$lib/constants'; import { LITELLM_API_BASE_URL, OLLAMA_API_BASE_URL, OPENAI_API_BASE_URL } from '$lib/constants';
import { WEBUI_BASE_URL } from '$lib/constants'; import { WEBUI_BASE_URL } from '$lib/constants';
import { createOpenAITextStream } from '$lib/apis/streaming';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
...@@ -599,38 +600,22 @@ ...@@ -599,38 +600,22 @@
.pipeThrough(splitStream('\n')) .pipeThrough(splitStream('\n'))
.getReader(); .getReader();
while (true) { const textStream = await createOpenAITextStream(reader, $settings.splitLargeChunks);
const { value, done } = await reader.read(); console.log(textStream);
for await (const update of textStream) {
const { value, done } = update;
if (done || stopResponseFlag || _chatId !== $chatId) { if (done || stopResponseFlag || _chatId !== $chatId) {
responseMessage.done = true; responseMessage.done = true;
messages = messages; messages = messages;
break; break;
} }
try { if (responseMessage.content == '' && value == '\n') {
let lines = value.split('\n'); continue;
} else {
for (const line of lines) { responseMessage.content += value;
if (line !== '') { messages = messages;
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()) { if ($settings.notificationEnabled && !document.hasFocus()) {
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
OLLAMA_API_BASE_URL, OLLAMA_API_BASE_URL,
WEBUI_BASE_URL WEBUI_BASE_URL
} from '$lib/constants'; } from '$lib/constants';
import { createOpenAITextStream } from '$lib/apis/streaming';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
...@@ -611,38 +612,22 @@ ...@@ -611,38 +612,22 @@
.pipeThrough(splitStream('\n')) .pipeThrough(splitStream('\n'))
.getReader(); .getReader();
while (true) { const textStream = await createOpenAITextStream(reader, $settings.splitLargeChunks);
const { value, done } = await reader.read(); console.log(textStream);
for await (const update of textStream) {
const { value, done } = update;
if (done || stopResponseFlag || _chatId !== $chatId) { if (done || stopResponseFlag || _chatId !== $chatId) {
responseMessage.done = true; responseMessage.done = true;
messages = messages; messages = messages;
break; break;
} }
try { if (responseMessage.content == '' && value == '\n') {
let lines = value.split('\n'); continue;
} else {
for (const line of lines) { responseMessage.content += value;
if (line !== '') { messages = messages;
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()) { if ($settings.notificationEnabled && !document.hasFocus()) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment