Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
open-webui
Commits
748cb7d4
Unverified
Commit
748cb7d4
authored
Apr 24, 2024
by
Timothy Jaeryang Baek
Committed by
GitHub
Apr 24, 2024
Browse files
Merge pull request #1654 from open-webui/dev
0.1.121
parents
b3da09f5
348186c4
Changes
32
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
302 additions
and
210 deletions
+302
-210
src/lib/components/chat/Settings/Interface.svelte
src/lib/components/chat/Settings/Interface.svelte
+28
-0
src/lib/components/chat/Settings/Models.svelte
src/lib/components/chat/Settings/Models.svelte
+125
-135
src/lib/components/documents/Settings/General.svelte
src/lib/components/documents/Settings/General.svelte
+1
-1
src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte
src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte
+1
-1
src/lib/i18n/locales/en-US/translation.json
src/lib/i18n/locales/en-US/translation.json
+1
-0
src/lib/i18n/locales/nl-NL/translation.json
src/lib/i18n/locales/nl-NL/translation.json
+1
-1
src/lib/i18n/locales/ru-RU/translation.json
src/lib/i18n/locales/ru-RU/translation.json
+13
-13
src/lib/stores/index.ts
src/lib/stores/index.ts
+109
-6
src/lib/utils/index.ts
src/lib/utils/index.ts
+0
-1
src/routes/(app)/+page.svelte
src/routes/(app)/+page.svelte
+11
-26
src/routes/(app)/c/[id]/+page.svelte
src/routes/(app)/c/[id]/+page.svelte
+11
-26
static/manifest.json
static/manifest.json
+1
-0
No files found.
src/lib/components/chat/Settings/Interface.svelte
View file @
748cb7d4
...
@@ -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" />
...
...
src/lib/components/chat/Settings/Models.svelte
View file @
748cb7d4
...
@@ -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 deleteLiteLLMModel
Id
= '';
let deleteLiteLLMModel
Name
= '';
$: 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,41 +143,118 @@
...
@@ -148,41 +143,118 @@
);
);
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();
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;
}
modelDownloadQueue.push(
if (data.id) {
{ modelName: sanitizedModelTag },
MODEL_DOWNLOAD_POOL.set({
async (data: { modelName: string; success: boolean; error?: Error }) => {
...$MODEL_DOWNLOAD_POOL,
const { modelName } = data;
[sanitizedModelTag]: {
// Remove the downloaded model
...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
delete modelDownloadStatus[modelName];
requestId: data.id,
reader,
done: false
}
});
console.log(data);
}
modelDownloadStatus = { ...modelDownloadStatus };
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,
[sanitizedModelTag]: {
...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
pullProgress: downloadProgress,
digest: data.digest
}
});
} else {
} else {
toast.success(
toast.success(data.status);
$i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, { modelName })
);
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, deleteLiteLLMModel
Id
).catch(
const res = await deleteLiteLLMModel(localStorage.token, deleteLiteLLMModel
Name
).catch(
(error) => {
(error) => {
toast.error(error);
toast.error(error);
return null;
return null;
...
@@ -485,7 +487,7 @@
...
@@ -485,7 +487,7 @@
}
}
}
}
deleteLiteLLMModel
Id
= '';
deleteLiteLLMModel
Name
= '';
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={deleteLiteLLMModel
Id
}
bind:value={deleteLiteLLMModel
Name
}
placeholder={$i18n.t('Select a model')}
placeholder={$i18n.t('Select a model')}
>
>
{#if !deleteLiteLLMModel
Id
}
{#if !deleteLiteLLMModel
Name
}
<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}
...
...
src/lib/components/documents/Settings/General.svelte
View file @
748cb7d4
...
@@ -180,7 +180,7 @@
...
@@ -180,7 +180,7 @@
}
}
}}
}}
>
>
<option value="">{$i18n.t('Default (SentenceTransformer)')}</option>
<option value="">{$i18n.t('Default (SentenceTransformer
s
)')}</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>
...
...
src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte
View file @
748cb7d4
...
@@ -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
...
...
src/lib/i18n/locales/en-US/translation.json
View file @
748cb7d4
...
@@ -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)"
:
""
,
...
...
src/lib/i18n/locales/nl-NL/translation.json
View file @
748cb7d4
...
@@ -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."
:
"
c
li
c
k her
e
."
,
"click here."
:
"
k
lik h
i
er."
,
"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"
,
...
...
src/lib/i18n/locales/ru-RU/translation.json
View file @
748cb7d4
...
@@ -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"
:
"Автоматическое воспроизведение ответа"
,
...
...
src/lib/stores/index.ts
View file @
748cb7d4
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
;
};
src/lib/utils/index.ts
View file @
748cb7d4
...
@@ -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
(
'
<
'
,
'
<
'
)
.
replaceAll
(
'
<
'
,
'
<
'
)
.
trim
();
.
trim
();
};
};
...
...
src/routes/(app)/+page.svelte
View file @
748cb7d4
...
@@ -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,39 +600,23 @@
...
@@ -599,39 +600,23 @@
.
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'
);
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
;
continue
;
}
else
{
}
else
{
responseMessage
.
content
+=
data
.
choices
[
0
].
delta
.
content
??
''
;
responseMessage
.
content
+=
value
;
messages
=
messages
;
messages
=
messages
;
}
}
}
}
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
if
($
settings
.
notificationEnabled
&&
!document.hasFocus()) {
if
($
settings
.
notificationEnabled
&&
!document.hasFocus()) {
const
notification
=
new
Notification
(`
OpenAI
${
model
}`,
{
const
notification
=
new
Notification
(`
OpenAI
${
model
}`,
{
...
...
src/routes/(app)/c/[id]/+page.svelte
View file @
748cb7d4
...
@@ -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,39 +612,23 @@
...
@@ -611,39 +612,23 @@
.
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'
);
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
;
continue
;
}
else
{
}
else
{
responseMessage
.
content
+=
data
.
choices
[
0
].
delta
.
content
??
''
;
responseMessage
.
content
+=
value
;
messages
=
messages
;
messages
=
messages
;
}
}
}
}
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
if
($
settings
.
notificationEnabled
&&
!document.hasFocus()) {
if
($
settings
.
notificationEnabled
&&
!document.hasFocus()) {
const
notification
=
new
Notification
(`
OpenAI
${
model
}`,
{
const
notification
=
new
Notification
(`
OpenAI
${
model
}`,
{
...
...
static/manifest.json
View file @
748cb7d4
{}
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment