Compare commits

...

56 commits

Author SHA1 Message Date
Classic298
5edd582761
Merge 81c7617508 into 4c4b9d19a1 2025-12-11 20:25:43 +01:00
Timothy Jaeryang Baek
4c4b9d19a1 refac: dockerfile PYTHONUNBUFFERED 2025-12-11 14:05:34 -05:00
Zyfax
7364b67455
fix: regenerate response shortcut (#19875)
* fix: regenerate shortcut

* Refactor shortcut handling for regenerate response

* refac

---------

Co-authored-by: Zyfax <kemon@hey.com>
2025-12-11 14:02:16 -05:00
Jeppe Kuhlmann Andersen
3418f53d07
Updated Danish translations (#19881) 2025-12-11 14:01:51 -05:00
Classic298
81c7617508
feat: Make VACUUM database optimization optional (#36)
Co-authored-by: Claude <noreply@anthropic.com>
Fix #1: Remove duplicate scan in preview mode
Fix #2: Cache stat() result in audio cleanup
2025-11-13 20:45:47 +01:00
Classic298
a4ddb4b15b
fix (#35)
Co-authored-by: Claude <noreply@anthropic.com>
Fix #1: Remove duplicate scan in preview mode
Fix #2: Cache stat() result in audio cleanup
2025-11-13 19:29:02 +01:00
Classic298
c307d87262
sync (#34)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-13 19:13:21 +01:00
Classic298
20187f9a2d
fix file lock (#33)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-13 18:01:25 +01:00
Classic298
873b73e668
feat: Make VACUUM database optimization optional (#30)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-11 19:39:20 +01:00
Classic298
60d7ad22ee
Claude/vacuum optional 011 c uw61vf5 s rym bh cw u1 ls w (#28)
PruneLock class
Vector cleanup error reporting
Lock acquisition/release
Optional VACUUM
Fixed folder deletion
2025-11-10 17:14:27 +01:00
Classic298
d94492dc0e
Merge branch 'dev' into universal_file_deletion 2025-11-10 15:34:05 +01:00
Classic298
195c3a57ae
Remove redundant parameter from delete_folder call 2025-11-10 15:33:17 +01:00
Classic298
2f53477de1
Merge branch 'open-webui:main' into universal_file_deletion 2025-10-14 13:50:02 +02:00
Classic298
1654b4f1bb
Merge branch 'dev' into universal_file_deletion 2025-09-29 22:21:34 +02:00
Classic298
a982bd73f7
Merge branch 'open-webui:main' into universal_file_deletion 2025-09-29 10:23:50 +02:00
Classic298
30095f5cb5
Merge branch 'open-webui:main' into universal_file_deletion 2025-09-26 10:41:46 +02:00
Classic298
f79a061113
Merge branch 'open-webui:main' into universal_file_deletion 2025-09-09 17:12:21 +02:00
Classic298
f263e750cf
Merge branch 'open-webui:main' into universal_file_deletion 2025-08-22 19:35:06 +02:00
Classic298
8156d0a30e
Update prune.py 2025-08-22 19:33:17 +02:00
Classic298
8231588eb4
pgvector 2025-08-22 19:17:52 +02:00
Classic298
46288924a2
Update prune.py 2025-08-22 18:29:26 +02:00
Classic298
155f53b867
Update prune.py 2025-08-22 18:17:24 +02:00
Classic298
bfa2eb631d
Update prune.py 2025-08-22 17:37:02 +02:00
Classic298
b5d93ae3db
Update prune.py 2025-08-22 17:02:36 +02:00
Classic298
4c7e6bd752
Update prune.py 2025-08-22 16:43:06 +02:00
Classic298
262848d647
Update prune.py 2025-08-22 16:39:47 +02:00
Classic298
13100ab9b3
Update Database.svelte 2025-08-22 16:33:04 +02:00
Classic298
2681fd268b
Update prune.py 2025-08-22 16:33:02 +02:00
Classic298
98650bd7d9
Update prune.ts 2025-08-22 16:29:51 +02:00
Classic298
f6c7c145a8
Update PruneDataDialog.svelte 2025-08-22 16:29:43 +02:00
Classic298
0230a1208b
Update Database.svelte 2025-08-22 16:29:31 +02:00
Classic298
bc19b51527
Update prune.py 2025-08-22 16:29:11 +02:00
Classic298
28f0079193
Update prune.py 2025-08-22 16:16:59 +02:00
Classic298
808fd0324d
Update prune.ts 2025-08-22 16:16:42 +02:00
Classic298
7abcc7bc59
Update Database.svelte 2025-08-22 16:16:31 +02:00
Classic298
544f8b72dc
Update PruneDataDialog.svelte 2025-08-22 16:16:14 +02:00
Classic298
5aa93ab97d
Update PruneDataDialog.svelte 2025-08-22 15:53:15 +02:00
Classic298
233167a041
Update PruneDataDialog.svelte 2025-08-22 15:49:55 +02:00
Classic298
74bfead38b
Update prune.py 2025-08-22 15:42:56 +02:00
Classic298
2ed95ef20e
Update PruneDataDialog.svelte 2025-08-22 15:42:05 +02:00
Classic298
daed47db03
Update Database.svelte 2025-08-22 15:38:53 +02:00
Classic298
4e6e5819a6
Update prune.ts 2025-08-22 15:38:41 +02:00
Classic298
596f02c2e9
Merge branch 'open-webui:main' into universal_file_deletion 2025-08-21 21:40:12 +02:00
Classic298
adda47ab04
move import 2025-08-12 22:06:10 +02:00
Classic298
2818b4643a
Update folders.py 2025-08-12 14:58:33 +02:00
Classic298
482030ff69
Update prune.py 2025-08-12 14:56:44 +02:00
Classic298
34c9a8825c
Update prune.py 2025-08-12 14:54:54 +02:00
Classic298
709c852917
Update prune.py 2025-08-12 13:20:16 +02:00
Classic298
60edac6c3f
Update Database.svelte 2025-08-12 13:16:55 +02:00
Classic298
e4a0bd8640
Update Database.svelte 2025-08-12 13:15:38 +02:00
Classic298
8d7273afae
Update prune.ts 2025-08-12 12:48:05 +02:00
Classic298
5ce002d5b3
Update PruneDataDialog.svelte 2025-08-12 12:47:51 +02:00
Classic298
0bd42e5c6d
Update Database.svelte 2025-08-12 12:47:34 +02:00
Classic298
028a2e5984
Update prune.py 2025-08-12 12:47:19 +02:00
Classic298
aadb296577
Merge branch 'open-webui:main' into universal_file_deletion 2025-08-11 22:09:25 +02:00
Classic298
d454e6a033
Feat/prune orphaned data (#16)
* feat: Add prune orphaned data functionality

* feat: Add prune orphaned data functionality

* feat: Add prune orphaned data functionality

* fix: Restyle PruneDataDialog modal

* feat: Add comprehensive prune orphaned data functionality and fix circular import

* feat: Add comprehensive prune orphaned data functionality and fix circular import

* feat: Add comprehensive prune orphaned data functionality and fix database size issues

* feat: Add comprehensive prune orphaned data functionality and fix database size issues

* feat: Add comprehensive prune orphaned data functionality and fix database size issues

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update folders.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Update prune.py

* Delete backend/open_webui/test/test_prune.py

* Update prune.ts

* Update PruneDataDialog.svelte

* Update prune.py

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update prune.py

* Update PruneDataDialog.svelte

* Update prune.ts

* Update Database.svelte

* Update prune.py

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update prune.py

* Update prune.py

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update Database.svelte

* Update prune.py

* Update prune.ts

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

* Update prune.py

* Update prune.ts

* Update PruneDataDialog.svelte

* Update files.py

* Update prompts.py

* Update notes.py

* Update models.py

* Update access_control.py

* Update PruneDataDialog.svelte

* Update PruneDataDialog.svelte

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-08-10 23:40:01 +02:00
9 changed files with 3106 additions and 85 deletions

View file

@ -55,6 +55,9 @@ ARG USE_RERANKING_MODEL
ARG UID
ARG GID
# Python settings
ENV PYTHONUNBUFFERED=1
## Basis ##
ENV ENV=prod \
PORT=8080 \

View file

@ -88,6 +88,7 @@ from open_webui.routers import (
models,
knowledge,
prompts,
prune,
evaluations,
tools,
users,
@ -1401,6 +1402,7 @@ app.include_router(
evaluations.router, prefix="/api/v1/evaluations", tags=["evaluations"]
)
app.include_router(utils.router, prefix="/api/v1/utils", tags=["utils"])
app.include_router(prune.router, prefix="/api/v1/prune", tags=["prune"])
# SCIM 2.0 API for identity management
if ENABLE_SCIM:

View file

@ -157,6 +157,12 @@ class FolderTable:
for folder in db.query(Folder).filter_by(user_id=user_id).all()
]
def get_all_folders(self) -> list[FolderModel]:
with get_db() as db:
return [
FolderModel.model_validate(folder) for folder in db.query(Folder).all()
]
def get_folder_by_parent_id_and_user_id_and_name(
self, parent_id: Optional[str], user_id: str, name: str
) -> Optional[FolderModel]:

File diff suppressed because it is too large Load diff

66
src/lib/apis/prune.ts Normal file
View file

@ -0,0 +1,66 @@
import { WEBUI_API_BASE_URL } from '$lib/constants';
export const pruneData = async (
token: string,
days: number | null,
exempt_archived_chats: boolean,
exempt_chats_in_folders: boolean,
delete_orphaned_chats: boolean = true,
delete_orphaned_tools: boolean = false,
delete_orphaned_functions: boolean = false,
delete_orphaned_prompts: boolean = true,
delete_orphaned_knowledge_bases: boolean = true,
delete_orphaned_models: boolean = true,
delete_orphaned_notes: boolean = true,
delete_orphaned_folders: boolean = true,
audio_cache_max_age_days: number | null = 30,
delete_inactive_users_days: number | null = null,
exempt_admin_users: boolean = true,
exempt_pending_users: boolean = true,
run_vacuum: boolean = false,
dry_run: boolean // Removed default value to ensure explicit passing
) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/prune/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
days,
exempt_archived_chats,
exempt_chats_in_folders,
delete_orphaned_chats,
delete_orphaned_tools,
delete_orphaned_functions,
delete_orphaned_prompts,
delete_orphaned_knowledge_bases,
delete_orphaned_models,
delete_orphaned_notes,
delete_orphaned_folders,
audio_cache_max_age_days,
delete_inactive_users_days,
exempt_admin_users,
exempt_pending_users,
run_vacuum,
dry_run
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
error = err;
console.log(err);
return null;
});
if (error) {
throw error;
}
return res;
};

View file

@ -10,16 +10,94 @@
import { getAllUsers } from '$lib/apis/users';
import { exportConfig, importConfig } from '$lib/apis/configs';
import PruneDataDialog from '$lib/components/common/PruneDataDialog.svelte';
import { pruneData } from '$lib/apis/prune';
const i18n = getContext('i18n');
export let saveHandler: Function;
let showPruneDataDialog = false;
let showPreviewResults = false;
let previewResults = null;
let lastPruneSettings = null;
const exportAllUserChats = async () => {
let blob = new Blob([JSON.stringify(await getAllUserChats(localStorage.token))], {
type: 'application/json'
});
saveAs(blob, `all-chats-export-${Date.now()}.json`);
};
const handlePruneDataPreview = async (event) => {
const settings = event.detail;
lastPruneSettings = settings;
console.log('Preview call - dry_run should be TRUE');
const res = await pruneData(
localStorage.token,
settings.days,
settings.exempt_archived_chats,
settings.exempt_chats_in_folders,
settings.delete_orphaned_chats,
settings.delete_orphaned_tools,
settings.delete_orphaned_functions,
settings.delete_orphaned_prompts,
settings.delete_orphaned_knowledge_bases,
settings.delete_orphaned_models,
settings.delete_orphaned_notes,
settings.delete_orphaned_folders,
settings.audio_cache_max_age_days,
settings.delete_inactive_users_days,
settings.exempt_admin_users,
settings.exempt_pending_users,
settings.run_vacuum,
true // dry_run = true for preview
).catch((error) => {
toast.error(`${error}`);
return null;
});
if (res) {
previewResults = res;
showPreviewResults = true;
}
};
const handleConfirmPrune = async () => {
if (!lastPruneSettings) return;
console.log('Confirm call - dry_run should be FALSE');
const res = await pruneData(
localStorage.token,
lastPruneSettings.days,
lastPruneSettings.exempt_archived_chats,
lastPruneSettings.exempt_chats_in_folders,
lastPruneSettings.delete_orphaned_chats,
lastPruneSettings.delete_orphaned_tools,
lastPruneSettings.delete_orphaned_functions,
lastPruneSettings.delete_orphaned_prompts,
lastPruneSettings.delete_orphaned_knowledge_bases,
lastPruneSettings.delete_orphaned_models,
lastPruneSettings.delete_orphaned_notes,
lastPruneSettings.delete_orphaned_folders,
lastPruneSettings.audio_cache_max_age_days,
lastPruneSettings.delete_inactive_users_days,
lastPruneSettings.exempt_admin_users,
lastPruneSettings.exempt_pending_users,
lastPruneSettings.run_vacuum,
false // dry_run = false for actual pruning
).catch((error) => {
toast.error(`${error}`);
return null;
});
if (res) {
toast.success('Data pruned successfully');
showPreviewResults = false;
previewResults = null;
lastPruneSettings = null;
}
};
const exportUsers = async () => {
const users = await getAllUsers(localStorage.token);
@ -49,6 +127,151 @@
});
</script>
<!-- Preview Results Modal -->
{#if showPreviewResults && previewResults}
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[80vh] overflow-y-auto">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Pruning Preview Results')}
</h3>
<button
class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
on:click={() => (showPreviewResults = false)}
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="space-y-4">
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
<h4 class="text-sm font-medium text-blue-800 dark:text-blue-200 mb-2">
{$i18n.t('The following items would be deleted:')}
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm">
{#if previewResults.inactive_users > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Inactive users')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.inactive_users}</span>
</div>
{/if}
{#if previewResults.old_chats > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Old chats')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.old_chats}</span>
</div>
{/if}
{#if previewResults.orphaned_chats > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned chats')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_chats}</span>
</div>
{/if}
{#if previewResults.orphaned_files > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned files')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_files}</span>
</div>
{/if}
{#if previewResults.orphaned_tools > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned tools')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_tools}</span>
</div>
{/if}
{#if previewResults.orphaned_functions > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned functions')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_functions}</span>
</div>
{/if}
{#if previewResults.orphaned_prompts > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned prompts')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_prompts}</span>
</div>
{/if}
{#if previewResults.orphaned_knowledge_bases > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned knowledge bases')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_knowledge_bases}</span>
</div>
{/if}
{#if previewResults.orphaned_models > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned models')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_models}</span>
</div>
{/if}
{#if previewResults.orphaned_notes > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned notes')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_notes}</span>
</div>
{/if}
{#if previewResults.orphaned_folders > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned folders')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_folders}</span>
</div>
{/if}
{#if previewResults.orphaned_uploads > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned upload files')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_uploads}</span>
</div>
{/if}
{#if previewResults.orphaned_vector_collections > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Orphaned vector collections')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.orphaned_vector_collections}</span>
</div>
{/if}
{#if previewResults.audio_cache_files > 0}
<div class="flex justify-between">
<span class="text-gray-700 dark:text-gray-300">{$i18n.t('Audio cache files')}:</span>
<span class="font-medium text-red-600 dark:text-red-400">{previewResults.audio_cache_files}</span>
</div>
{/if}
</div>
{#if Object.values(previewResults).every(count => count === 0)}
<div class="text-center py-4">
<div class="text-green-600 dark:text-green-400 font-medium">
{$i18n.t('No items would be deleted with current settings')}
</div>
<div class="text-sm text-gray-500 dark:text-gray-400 mt-1">
{$i18n.t('Your system is already clean or no cleanup options are enabled')}
</div>
</div>
{/if}
</div>
<!-- Action buttons -->
<div class="flex justify-end gap-3 pt-4">
<button
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700 transition-colors"
on:click={() => (showPreviewResults = false)}
>
{$i18n.t('Cancel')}
</button>
{#if !Object.values(previewResults).every(count => count === 0)}
<button
class="px-4 py-2 text-sm font-medium text-white bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors"
on:click={handleConfirmPrune}
>
{$i18n.t('Prune Data')}
</button>
{/if}
</div>
</div>
</div>
</div>
{/if}
<PruneDataDialog bind:show={showPruneDataDialog} on:preview={handlePruneDataPreview} />
<form
class="flex flex-col h-full justify-between space-y-3 text-sm"
on:submit|preventDefault={async () => {
@ -231,6 +454,32 @@
</div>
</button>
{/if}
<hr class="border-gray-100 dark:border-gray-850 my-1" />
<button
type="button"
class=" flex rounded-md py-2 px-3 w-full bg-yellow-500 hover:bg-yellow-600 text-white transition"
on:click={() => {
showPruneDataDialog = true;
}}
>
<div class=" self-center mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M4.5 2a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-7ZM3 6a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H3Zm1 4a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1H4.5a.5.5 0 0 1-.5-.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class=" self-center text-sm font-medium">
{$i18n.t('Prune Orphaned Data')}
</div>
</button>
</div>
</div>
</form>

View file

@ -0,0 +1,899 @@
<script lang="ts">
import { createEventDispatcher, getContext } from 'svelte';
import Modal from '$lib/components/common/Modal.svelte';
import Switch from '$lib/components/common/Switch.svelte';
const i18n = getContext('i18n');
export let show = false;
let deleteChatsByAge = false;
let days = 60;
let exempt_archived_chats = true;
let exempt_chats_in_folders = false;
// Inactive user deletion
let deleteInactiveUsers = false;
let delete_inactive_users_days = 90;
let exempt_admin_users = true;
let exempt_pending_users = true;
// Orphaned resource deletion toggles
let delete_orphaned_chats = true;
let delete_orphaned_tools = false;
let delete_orphaned_functions = false;
let delete_orphaned_prompts = true;
let delete_orphaned_knowledge_bases = true;
let delete_orphaned_models = true;
let delete_orphaned_notes = true;
let delete_orphaned_folders = true;
// Audio cache cleanup
let cleanupAudioCache = true;
let audio_cache_max_age_days = 30;
// System/Database optimization
let run_vacuum = false;
let showDetailsExpanded = false;
let activeDetailsTab = 'users';
let activeSettingsTab = 'users';
let showApiPreview = false;
const dispatch = createEventDispatcher();
const preview = () => {
dispatch('preview', {
days: deleteChatsByAge ? days : null,
exempt_archived_chats,
exempt_chats_in_folders,
delete_orphaned_chats,
delete_orphaned_tools,
delete_orphaned_functions,
delete_orphaned_prompts,
delete_orphaned_knowledge_bases,
delete_orphaned_models,
delete_orphaned_notes,
delete_orphaned_folders,
audio_cache_max_age_days: cleanupAudioCache ? audio_cache_max_age_days : null,
delete_inactive_users_days: deleteInactiveUsers ? delete_inactive_users_days : null,
exempt_admin_users,
exempt_pending_users,
run_vacuum
});
show = false;
};
// Generate API call preview with helpful comments
$: apiCallPreview = `# Open WebUI Data Pruning API Call
# Use this template for automated maintenance scripts (cron jobs, etc.)
# AUTHENTICATION: Use API Key (not JWT token) for automation
# Get your API key from: Settings → Account → API Key → Generate new key
# Format: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
curl -X POST "${window.location.origin}/api/v1/prune/" \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer <your-api-key>" \\
-d '{
// SAFETY: Always test with dry_run=true first to preview results
"dry_run": false,
// AGE-BASED CHAT DELETION (null = disabled)
"days": ${deleteChatsByAge ? days : null},
"exempt_archived_chats": ${exempt_archived_chats}, // Keep archived chats even if old
"exempt_chats_in_folders": ${exempt_chats_in_folders}, // Keep organized/pinned chats
// INACTIVE USER DELETION (null = disabled, VERY DESTRUCTIVE)
"delete_inactive_users_days": ${deleteInactiveUsers ? delete_inactive_users_days : null},
"exempt_admin_users": ${exempt_admin_users}, // Strongly recommended: true
"exempt_pending_users": ${exempt_pending_users}, // Recommended for user approval workflows
// ORPHANED DATA CLEANUP (from deleted users)
"delete_orphaned_chats": ${delete_orphaned_chats},
"delete_orphaned_tools": ${delete_orphaned_tools},
"delete_orphaned_functions": ${delete_orphaned_functions}, // Actions, Pipes, Filters
"delete_orphaned_prompts": ${delete_orphaned_prompts},
"delete_orphaned_knowledge_bases": ${delete_orphaned_knowledge_bases},
"delete_orphaned_models": ${delete_orphaned_models},
"delete_orphaned_notes": ${delete_orphaned_notes},
"delete_orphaned_folders": ${delete_orphaned_folders},
// AUDIO CACHE CLEANUP (null = disabled)
"audio_cache_max_age_days": ${cleanupAudioCache ? audio_cache_max_age_days : null}, // TTS/STT files
// DATABASE OPTIMIZATION (WARNING: Locks database during execution!)
"run_vacuum": ${run_vacuum} // Reclaim disk space - only enable during maintenance windows
}'
# API KEY vs JWT TOKEN:
# - API Key: Persistent, use for automation (sk-xxxxxxxx...)
# - JWT Token: Session-bound, temporary, use for web UI only
# - ALWAYS use API Key for scripts/cron jobs
# AUTOMATION TIPS:
# 1. Run with dry_run=true first to preview what will be deleted
# 2. Schedule during low-usage hours to minimize performance impact
# 3. Monitor logs: tail -f /path/to/open-webui/logs
# 4. Consider database backup before large cleanup operations
# 5. Test on staging environment with similar data size first
# EXAMPLE CRON JOB (runs weekly on Sunday at 2 AM):
# 0 2 * * 0 /path/to/your/prune-script.sh >> /var/log/openwebui-prune.log 2>&1
# RESPONSE HANDLING:
# - dry_run=true: Returns counts object with preview numbers
# - dry_run=false: Returns true on success, throws error on failure
# - Always check HTTP status code and response for errors`;
// Warning for short inactive user deletion periods
$: shortUserDeletionWarning = deleteInactiveUsers && delete_inactive_users_days < 30;
const copyApiCall = () => {
navigator.clipboard.writeText(apiCallPreview).then(() => {
// Could add a toast notification here
console.log('API call copied to clipboard');
}).catch(err => {
console.error('Failed to copy API call: ', err);
});
};
</script>
<Modal bind:show size="lg">
<div>
<div class="flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
<div class="text-lg font-medium self-center">
{$i18n.t('Prune Orphaned Data')}
</div>
<button
class="self-center"
on:click={() => {
show = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<div class="flex flex-col w-full px-5 pb-5 dark:text-gray-200">
<div class="space-y-4">
<!-- Critical Warning Message -->
<div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3 flex-1">
<h3 class="text-sm font-medium text-red-800 dark:text-red-200 mb-2">
{$i18n.t('Destructive Operation - Backup Recommended')}
</h3>
<div class="text-sm text-red-700 dark:text-red-300 space-y-1">
<p>{$i18n.t('This action will permanently delete data from your database. Only orphaned or old data, based on your configuration settings, will be deleted. All active, referenced data remains completely safe.')}</p>
<p>{$i18n.t('This operation cannot be undone. Create a complete backup of your database and files before proceeding. This operation is performed entirely at your own risk - having a backup ensures you can restore any data if something unexpected occurs.')}</p>
<!-- Expandable Details Section -->
<div class="mt-3">
<button
class="flex items-center text-xs text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-200 focus:outline-none"
on:click={() => showDetailsExpanded = !showDetailsExpanded}
>
<svg
class="w-3 h-3 mr-1 transition-transform duration-200 {showDetailsExpanded ? 'rotate-90' : ''}"
fill="currentColor"
viewBox="0 0 20 20"
>
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
</svg>
{showDetailsExpanded ? $i18n.t('Hide details') : $i18n.t('Show details')}
</button>
{#if showDetailsExpanded}
<div class="mt-2 pl-4 border-l-2 border-red-300 dark:border-red-700 text-xs text-red-600 dark:text-red-400">
<p class="mb-3"><strong>{$i18n.t('Note:')}</strong> {$i18n.t('This list provides an overview of what will be deleted during the pruning process and may not be complete or fully up-to-date.')}</p>
<!-- Tab Navigation -->
<div class="flex flex-wrap gap-1 mb-3 border-b border-red-300 dark:border-red-700">
<button
class="px-2 py-1 text-xs font-medium rounded-t transition-colors {activeDetailsTab === 'users' ? 'bg-red-100 dark:bg-red-800 text-red-800 dark:text-red-200' : 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-200'}"
on:click={() => activeDetailsTab = 'users'}
>
{$i18n.t('Users')}
</button>
<button
class="px-2 py-1 text-xs font-medium rounded-t transition-colors {activeDetailsTab === 'chats' ? 'bg-red-100 dark:bg-red-800 text-red-800 dark:text-red-200' : 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-200'}"
on:click={() => activeDetailsTab = 'chats'}
>
{$i18n.t('Chats')}
</button>
<button
class="px-2 py-1 text-xs font-medium rounded-t transition-colors {activeDetailsTab === 'workspace' ? 'bg-red-100 dark:bg-red-800 text-red-800 dark:text-red-200' : 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-200'}"
on:click={() => activeDetailsTab = 'workspace'}
>
{$i18n.t('Workspace')}
</button>
<button
class="px-2 py-1 text-xs font-medium rounded-t transition-colors {activeDetailsTab === 'datavector' ? 'bg-red-100 dark:bg-red-800 text-red-800 dark:text-red-200' : 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-200'}"
on:click={() => activeDetailsTab = 'datavector'}
>
{$i18n.t('Data & Vector')}
</button>
<button
class="px-2 py-1 text-xs font-medium rounded-t transition-colors {activeDetailsTab === 'imagesaudio' ? 'bg-red-100 dark:bg-red-800 text-red-800 dark:text-red-200' : 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-200'}"
on:click={() => activeDetailsTab = 'imagesaudio'}
>
{$i18n.t('Images & Audio')}
</button>
<button
class="px-2 py-1 text-xs font-medium rounded-t transition-colors {activeDetailsTab === 'system' ? 'bg-red-100 dark:bg-red-800 text-red-800 dark:text-red-200' : 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-200'}"
on:click={() => activeDetailsTab = 'system'}
>
{$i18n.t('System & Database')}
</button>
</div>
<!-- Tab Content -->
<div class="space-y-2">
{#if activeDetailsTab === 'users'}
<div class="space-y-1">
<p><strong>{$i18n.t('Inactive User Account Deletion:')}</strong></p>
<p>{$i18n.t('Removes user accounts that have been inactive for a specified period based on their last activity timestamp')}</p>
<p class="pt-2"><strong>{$i18n.t('Safety Exemptions:')}</strong></p>
<p>{$i18n.t('Admin users: Can be exempted from deletion (recommended)')}</p>
<p>{$i18n.t('Pending users: Can be exempted from deletion')}</p>
</div>
{:else if activeDetailsTab === 'chats'}
<div class="space-y-1">
<p><strong>{$i18n.t('Age-Based Chat Deletion:')}</strong></p>
<p>{$i18n.t('Removes conversations older than specified days based on when they were last modified or updated (not when they were created)')}</p>
<p>{$i18n.t('Supports exemptions for:')}</p>
<p class="ml-4">{$i18n.t('Archived chats')}</p>
<p class="ml-4">{$i18n.t('Chats organized in folders and pinned chats')}</p>
<p class="pt-2"><strong>{$i18n.t('Orphaned Content Cleanup:')}</strong></p>
<p>{$i18n.t('Delete orphaned chats from deleted users')}</p>
<p>{$i18n.t('Delete orphaned folders from deleted users')}</p>
</div>
{:else if activeDetailsTab === 'workspace'}
<div class="space-y-1">
<p><strong>{$i18n.t('Orphaned Workspace Items from Deleted Users:')}</strong></p>
<p>{$i18n.t('Delete orphaned knowledge bases')}</p>
<p>{$i18n.t('Delete orphaned custom tools')}</p>
<p>{$i18n.t('Delete orphaned custom functions (Actions, Pipes, Filters)')}</p>
<p>{$i18n.t('Delete orphaned custom prompts and templates')}</p>
<p>{$i18n.t('Delete orphaned custom models and configurations')}</p>
<p>{$i18n.t('Delete orphaned notes')}</p>
</div>
{:else if activeDetailsTab === 'datavector'}
<div class="space-y-1">
<p><strong>{$i18n.t('Files & Vector Storage:')}</strong></p>
<p>{$i18n.t('Orphaned files and attachments from deleted content')}</p>
<p>{$i18n.t('Vector embeddings and collections for removed data')}</p>
<p>{$i18n.t('Uploaded files that lost their database references')}</p>
<p>{$i18n.t('Vector storage directories without corresponding data')}</p>
</div>
{:else if activeDetailsTab === 'imagesaudio'}
<div class="space-y-1">
<p><strong>{$i18n.t('Images & Audio Content Cleanup:')}</strong></p>
<p>{$i18n.t('Generated images: Already integrated with file system - orphaned images are automatically cleaned up when chats are deleted')}</p>
<p>{$i18n.t('Uploaded images: Already integrated with file system - orphaned images are automatically cleaned up based on active references')}</p>
<p>{$i18n.t('Audio cache cleanup: Remove old text-to-speech (TTS) generated audio files and speech-to-text (STT) transcription files')}</p>
<p>{$i18n.t('Audio recordings and transcriptions: Clean up cached audio files older than specified days')}</p>
</div>
{:else if activeDetailsTab === 'system'}
<div class="space-y-1">
<p><strong>{$i18n.t('Database & System Cleanup:')}</strong></p>
<p>{$i18n.t('Removal of broken database references and stale entries')}</p>
<p>{$i18n.t('Disk space reclamation by database cleanup')}</p>
<p>{$i18n.t('Synchronization of database records with actual file storage')}</p>
<p>{$i18n.t('Fix inconsistencies between storage systems')}</p>
<p>{$i18n.t('Database performance optimization')}</p>
</div>
{/if}
</div>
</div>
{/if}
</div>
</div>
</div>
</div>
</div>
<!-- Performance Warning -->
<div class="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<p class="text-sm text-yellow-800 dark:text-yellow-200">
{$i18n.t('Performance Warning: This operation may take a very long time to complete, especially if you have never cleaned your database before or if your instance stores large amounts of data. The process could take anywhere from seconds, to minutes, to half an hour and beyond depending on your data size.')}
</p>
</div>
</div>
</div>
<!-- Settings Section with Tabs -->
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
<div class="flex items-center mb-3">
<svg class="h-4 w-4 text-blue-600 dark:text-blue-400 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
</svg>
<h4 class="text-sm font-medium text-blue-800 dark:text-blue-200">
{$i18n.t('Pruning Configuration')}
</h4>
</div>
<p class="text-xs text-blue-700 dark:text-blue-300 mb-4">
{$i18n.t('Configure what data should be cleaned up during the pruning process.')}
</p>
<!-- Settings Tab Navigation -->
<div class="flex flex-wrap gap-1 mb-4 border-b border-blue-300 dark:border-blue-700">
<button
class="px-3 py-2 text-sm font-medium rounded-t transition-colors {activeSettingsTab === 'users' ? 'bg-blue-100 dark:bg-blue-800 text-blue-800 dark:text-blue-200' : 'text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-200'}"
on:click={() => activeSettingsTab = 'users'}
>
{$i18n.t('Users')}
</button>
<button
class="px-3 py-2 text-sm font-medium rounded-t transition-colors {activeSettingsTab === 'chats' ? 'bg-blue-100 dark:bg-blue-800 text-blue-800 dark:text-blue-200' : 'text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-200'}"
on:click={() => activeSettingsTab = 'chats'}
>
{$i18n.t('Chats')}
</button>
<button
class="px-3 py-2 text-sm font-medium rounded-t transition-colors {activeSettingsTab === 'workspace' ? 'bg-blue-100 dark:bg-blue-800 text-blue-800 dark:text-blue-200' : 'text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-200'}"
on:click={() => activeSettingsTab = 'workspace'}
>
{$i18n.t('Workspace')}
</button>
<button
class="px-3 py-2 text-sm font-medium rounded-t transition-colors {activeSettingsTab === 'audio' ? 'bg-blue-100 dark:bg-blue-800 text-blue-800 dark:text-blue-200' : 'text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-200'}"
on:click={() => activeSettingsTab = 'audio'}
>
{$i18n.t('Audio Cache')}
</button>
<button
class="px-3 py-2 text-sm font-medium rounded-t transition-colors {activeSettingsTab === 'system' ? 'bg-blue-100 dark:bg-blue-800 text-blue-800 dark:text-blue-200' : 'text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-200'}"
on:click={() => activeSettingsTab = 'system'}
>
{$i18n.t('System')}
</button>
</div>
<!-- Settings Tab Content -->
<div class="space-y-4">
{#if activeSettingsTab === 'users'}
<!-- Inactive User Deletion -->
<div class="space-y-4">
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={deleteInactiveUsers} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Delete inactive user accounts')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Remove user accounts inactive for specified days')}
</div>
</div>
</div>
</div>
<!-- User Deletion Options (when enabled) -->
{#if deleteInactiveUsers}
<div class="ml-8 space-y-4 border-l-2 border-red-200 dark:border-red-700 pl-4">
<div class="space-y-2">
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
{$i18n.t('Delete users inactive for more than')}
</label>
<div class="flex items-center space-x-2">
<input
id="user-days"
type="number"
min="1"
bind:value={delete_inactive_users_days}
class="w-20 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
<span class="text-sm text-gray-700 dark:text-gray-300">{$i18n.t('days')}</span>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Based on last_active_at timestamp. Minimum 1 day.')}
</p>
<!-- Warning for short periods -->
{#if shortUserDeletionWarning}
<div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-3">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-4 w-4 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-2">
<p class="text-xs text-red-800 dark:text-red-200 font-medium">
{$i18n.t('⚠️ Warning: Deletion period less than 30 days!')}
</p>
<p class="text-xs text-red-700 dark:text-red-300 mt-1">
{$i18n.t('Very short periods may accidentally delete active users. Consider using 30+ days for safety.')}
</p>
</div>
</div>
</div>
{/if}
</div>
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={exempt_admin_users} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Exempt admin users')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Never delete admin users (strongly recommended)')}
</div>
</div>
</div>
</div>
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={exempt_pending_users} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Exempt pending users')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Never delete pending/unapproved users')}
</div>
</div>
</div>
</div>
</div>
{/if}
</div>
{:else if activeSettingsTab === 'chats'}
<!-- Age-Based Chat Deletion -->
<div class="space-y-4">
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={deleteChatsByAge} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Delete chats by age')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Optionally remove old chats based on last update time')}
</div>
</div>
</div>
</div>
<!-- Chat Options (when enabled) -->
{#if deleteChatsByAge}
<div class="ml-8 space-y-4 border-l-2 border-gray-200 dark:border-gray-700 pl-4">
<div class="space-y-2">
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
{$i18n.t('Delete chats older than')}
</label>
<div class="flex items-center space-x-2">
<input
id="days"
type="number"
min="0"
bind:value={days}
class="w-20 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
<span class="text-sm text-gray-700 dark:text-gray-300">{$i18n.t('days')}</span>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Set to 0 to delete all chats, or specify number of days')}
</p>
</div>
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={exempt_archived_chats} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Exempt archived chats')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Keep archived chats even if they are old')}
</div>
</div>
</div>
</div>
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={exempt_chats_in_folders} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Exempt chats in folders')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Keep chats that are organized in folders or pinned')}
</div>
</div>
</div>
</div>
</div>
{/if}
<!-- Orphaned Chat Deletion -->
<div class="border-t border-gray-200 dark:border-gray-700 pt-4">
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={delete_orphaned_chats} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Delete orphaned chats')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Delete orphaned chats from deleted users')}
</div>
</div>
</div>
</div>
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={delete_orphaned_folders} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Delete orphaned folders')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Delete orphaned folders from deleted users')}
</div>
</div>
</div>
</div>
</div>
</div>
{:else if activeSettingsTab === 'workspace'}
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<!-- Knowledge Bases -->
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={delete_orphaned_knowledge_bases} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Delete orphaned knowledge bases')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Delete orphaned knowledge bases from deleted users')}
</div>
</div>
</div>
</div>
<!-- Tools -->
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={delete_orphaned_tools} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Delete orphaned tools')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Delete orphaned custom tools from deleted users')}
</div>
</div>
</div>
</div>
<!-- Functions -->
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={delete_orphaned_functions} />
</div>
<div>
<div class="flex items-center text-sm font-medium text-gray-900 dark:text-gray-100">
<span>{$i18n.t('Delete orphaned functions')}</span>
<div class="relative group ml-2">
<svg class="h-3 w-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 cursor-help" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
</svg>
<div class="absolute left-1/2 transform -translate-x-1/2 bottom-full mb-2 w-48 px-3 py-2 text-xs text-white bg-gray-900 dark:bg-gray-700 rounded-lg shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-10">
<div class="font-medium mb-1">{$i18n.t('Admin panel functions - all functions, including:')}</div>
<div class="space-y-0.5">
<div>{$i18n.t('Actions')}</div>
<div>{$i18n.t('Pipes')}</div>
<div>{$i18n.t('Filters')}</div>
</div>
<div class="absolute top-full left-1/2 transform -translate-x-1/2 border-4 border-transparent border-t-gray-900 dark:border-t-gray-700"></div>
</div>
</div>
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Delete orphaned custom functions from deleted users')}
</div>
</div>
</div>
</div>
<!-- Prompts -->
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={delete_orphaned_prompts} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Delete orphaned prompts')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Delete orphaned custom prompts from deleted users')}
</div>
</div>
</div>
</div>
<!-- Models -->
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={delete_orphaned_models} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Delete orphaned models')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Delete orphaned custom models from deleted users')}
</div>
</div>
</div>
</div>
<!-- Notes -->
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={delete_orphaned_notes} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Delete orphaned notes')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Delete orphaned notes from deleted users')}
</div>
</div>
</div>
</div>
</div>
{:else if activeSettingsTab === 'audio'}
<!-- Audio Cache Cleanup -->
<div class="space-y-4">
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={cleanupAudioCache} />
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('Clean audio cache')}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Remove old audio cache files (TTS and STT recordings)')}
</div>
</div>
</div>
</div>
<!-- Audio Cache Options (when enabled) -->
{#if cleanupAudioCache}
<div class="ml-8 space-y-4 border-l-2 border-gray-200 dark:border-gray-700 pl-4">
<div class="space-y-2">
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
{$i18n.t('Delete audio files older than')}
</label>
<div class="flex items-center space-x-2">
<input
id="audio-days"
type="number"
min="0"
bind:value={audio_cache_max_age_days}
class="w-20 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
<span class="text-sm text-gray-700 dark:text-gray-300">{$i18n.t('days')}</span>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Remove cached TTS (text-to-speech) and STT (speech-to-text) files older than specified days')}
</p>
</div>
<div class="bg-gray-50 dark:bg-gray-800 rounded-lg p-3">
<h5 class="text-sm font-medium text-gray-900 dark:text-gray-100 mb-2">
{$i18n.t('Audio Cache Types:')}
</h5>
<div class="space-y-1 text-xs text-gray-600 dark:text-gray-400">
<p><strong>{$i18n.t('TTS Files:')}</strong> {$i18n.t('Generated audio files when AI speaks text to you')}</p>
<p><strong>{$i18n.t('STT Files:')}</strong> {$i18n.t('Uploaded audio files for transcription (voice messages)')}</p>
<p><strong>{$i18n.t('Metadata:')}</strong> {$i18n.t('Associated JSON files with transcription data')}</p>
</div>
</div>
</div>
{/if}
</div>
{:else if activeSettingsTab === 'system'}
<!-- System/Database Optimization -->
<div class="space-y-4">
<div class="flex items-start py-2">
<div class="flex items-center">
<div class="mr-3">
<Switch bind:state={run_vacuum} />
</div>
<div class="flex-1">
<div class="flex items-center text-sm font-medium text-gray-900 dark:text-gray-100">
<span>{$i18n.t('Run VACUUM optimization')}</span>
<div class="relative group ml-2">
<svg class="h-4 w-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 cursor-help" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
</svg>
<div class="absolute left-1/2 transform -translate-x-1/2 bottom-full mb-2 w-72 px-3 py-2 text-xs text-white bg-gray-900 dark:bg-gray-700 rounded-lg shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-10">
<div class="font-medium mb-1">{$i18n.t('Database Optimization Warning:')}</div>
<div class="space-y-1">
<p>{$i18n.t('VACUUM reclaims disk space by rebuilding the database file.')}</p>
<p class="text-yellow-300 dark:text-yellow-400 font-medium">{$i18n.t('⚠️ This may take a very long time on large databases and will LOCK the entire database during execution.')}</p>
<p>{$i18n.t('It is strongly recommended to NOT run this while users are actively using the platform.')}</p>
<p class="text-green-300 dark:text-green-400">{$i18n.t('💡 Best practice: Run during scheduled maintenance windows.')}</p>
</div>
<div class="absolute top-full left-1/2 transform -translate-x-1/2 border-4 border-transparent border-t-gray-900 dark:border-t-gray-700"></div>
</div>
</div>
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
{$i18n.t('Reclaim disk space after cleanup (locks database during operation)')}
</div>
</div>
</div>
</div>
<!-- VACUUM warning box -->
{#if run_vacuum}
<div class="ml-8 border-l-2 border-yellow-200 dark:border-yellow-700 pl-4">
<div class="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-3">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<h4 class="text-sm font-medium text-yellow-800 dark:text-yellow-200">
{$i18n.t('VACUUM Enabled - Important Considerations:')}
</h4>
<div class="mt-2 text-sm text-yellow-700 dark:text-yellow-300 space-y-1">
<p>{$i18n.t('Database will be locked during VACUUM - all users will experience errors')}</p>
<p>{$i18n.t('Operation duration depends on database size (can be 5-30+ minutes)')}</p>
<p>{$i18n.t('Recommended only during scheduled maintenance windows')}</p>
<p>{$i18n.t('Not required for routine cleanups - only when reclaiming disk space is critical')}</p>
</div>
</div>
</div>
</div>
</div>
{/if}
</div>
{/if}
</div>
</div>
<!-- API Call Preview Section -->
<div class="bg-gray-50 dark:bg-gray-900/20 border border-gray-200 dark:border-gray-800 rounded-lg p-4">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3 flex-1">
<h3 class="text-sm font-medium text-gray-800 dark:text-gray-200 mb-2">
{$i18n.t('API Automation Helper')}
</h3>
<button
class="flex items-center text-xs text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 focus:outline-none mb-3"
on:click={() => showApiPreview = !showApiPreview}
>
<svg
class="w-3 h-3 mr-1 transition-transform duration-200 {showApiPreview ? 'rotate-90' : ''}"
fill="currentColor"
viewBox="0 0 20 20"
>
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
</svg>
{showApiPreview ? $i18n.t('Hide API call') : $i18n.t('Show API call')}
</button>
{#if showApiPreview}
<div class="space-y-2">
<p class="text-sm text-gray-700 dark:text-gray-300 mb-3">
{$i18n.t('Use this API call configuration to automate pruning operations in your own maintenance scripts.')}
</p>
<div class="relative">
<textarea
readonly
value={apiCallPreview}
class="w-full h-40 px-3 py-2 text-xs font-mono bg-gray-50 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-900 dark:text-gray-100 resize-none focus:ring-2 focus:ring-gray-500 focus:border-gray-500"
on:focus={(e) => e.target.select()}
></textarea>
<button
class="absolute top-2 right-2 px-2 py-1 text-xs font-medium text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded focus:outline-none focus:ring-2 focus:ring-gray-500"
on:click={copyApiCall}
title={$i18n.t('Copy to clipboard')}
>
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z"></path>
<path d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z"></path>
</svg>
</button>
</div>
</div>
{/if}
</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="mt-6 flex justify-end gap-3">
<button
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700 transition-colors"
on:click={() => (show = false)}
>
{$i18n.t('Cancel')}
</button>
<button
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors"
on:click={preview}
>
{$i18n.t('Preview')}
</button>
</div>
</div>
</div>
</Modal>

View file

@ -18,15 +18,15 @@
"{{COUNT}} words": "{{COUNT}} ord",
"{{LOCALIZED_DATE}} at {{LOCALIZED_TIME}}": "{{LOCALIZED_DATE}} klokken {{LOCALIZED_TIME}}",
"{{model}} download has been canceled": "Download af {{model}} er blevet annulleret",
"{{NAMES}} reacted with {{REACTION}}": "",
"{{NAMES}} reacted with {{REACTION}}": "{{NAMES}} reagerede med {{REACTION}}",
"{{user}}'s Chats": "{{user}}s chats",
"{{webUIName}} Backend Required": "{{webUIName}} Backend kræves",
"*Prompt node ID(s) are required for image generation": "*Prompt node ID(s) er påkrævet for at kunne generere billeder",
"1 Source": "1 kilde",
"A collaboration channel where people join as members": "",
"A discussion channel where access is controlled by groups and permissions": "",
"A collaboration channel where people join as members": "En samarbejdskanal hvor folk tilmelder sig som medlemmer",
"A discussion channel where access is controlled by groups and permissions": "En diskussionskanal hvor adgang styres af grupper og tilladelser",
"A new version (v{{LATEST_VERSION}}) is now available.": "En ny version (v{{LATEST_VERSION}}) er nu tilgængelig.",
"A private conversation between you and selected users": "",
"A private conversation between you and selected users": "En privat samtale mellem dig og udvalgte brugere",
"A task model is used when performing tasks such as generating titles for chats and web search queries": "En 'task model' bliver brugt til at opgaver såsom at generere overskrifter til chats eller internetsøgninger",
"a user": "en bruger",
"About": "Information",
@ -57,8 +57,8 @@
"Add Custom Prompt": "Tilføj brugerdefineret prompt",
"Add Details": "Tilføj detaljer",
"Add Files": "Tilføj filer",
"Add Member": "",
"Add Members": "",
"Add Member": "Tilføj medlem",
"Add Members": "Tilføj medlemmer",
"Add Memory": "Tilføj hukommelse",
"Add Model": "Tilføj model",
"Add Reaction": "Tilføj reaktion",
@ -70,7 +70,7 @@
"Additional Config": "Yderligere konfiguration",
"Additional configuration options for marker. This should be a JSON string with key-value pairs. For example, '{\"key\": \"value\"}'. Supported keys include: disable_links, keep_pageheader_in_output, keep_pagefooter_in_output, filter_blank_pages, drop_repeated_text, layout_coverage_threshold, merge_threshold, height_tolerance, gap_threshold, image_threshold, min_line_length, level_count, default_level": "Yderligere konfigurationsmuligheder for Marker. Dette skal være en JSON streng med key-value pairs. For eksempel, '{\"key\": \"value\"}'. Tilladte keys omfatter: disable_links, keep_pageheader_in_output, keep_pagefooter_in_output, filter_blank_pages, drop_repeated_text, layout_coverage_threshold, merge_threshold, height_tolerance, gap_threshold, image_threshold, min_line_length, level_count, default_level",
"Additional Parameters": "Yderligere parametre",
"Adds filenames, titles, sections, and snippets into the BM25 text to improve lexical recall.": "",
"Adds filenames, titles, sections, and snippets into the BM25 text to improve lexical recall.": "Tilføjer filnavne, titler, afsnit og uddrag til BM25-teksten for at forbedre leksikalsk genkaldelse.",
"Adjusting these settings will apply changes universally to all users.": "Ændringer af disse indstillinger har konsekvenser for alle brugere.",
"admin": "administrator",
"Admin": "Administrator",
@ -99,7 +99,7 @@
"Allow Continue Response": "Tillad fortsættelse af svar",
"Allow Delete Messages": "Tillad sletning af beskeder",
"Allow File Upload": "Tillad upload af fil",
"Allow Group Sharing": "",
"Allow Group Sharing": "Tillad gruppedeling",
"Allow Multiple Models in Chat": "Tillad flere modeller i chats",
"Allow non-local voices": "Tillad ikke-lokale stemmer",
"Allow Rate Response": "Tillad vurdering af svar",
@ -134,7 +134,7 @@
"API Key created.": "API nøgle lavet",
"API Key Endpoint Restrictions": "API nøgler endpoint forbehold",
"API keys": "API nøgler",
"API Keys": "",
"API Keys": "API nøgler",
"API Mode": "API tilstand",
"API Version": "API Version",
"API Version is required": "API version er påkrævet",
@ -147,7 +147,7 @@
"Archived Chats": "Arkiverede chats",
"archived-chat-export": "arkiveret-chat-eksport",
"Are you sure you want to clear all memories? This action cannot be undone.": "Er du sikker på du vil rydde hele hukommelsen? Dette kan ikke gøres om.",
"Are you sure you want to delete \"{{NAME}}\"?": "",
"Are you sure you want to delete \"{{NAME}}\"?": "Er du sikker på at du vil slette \"{{NAME}}\"?",
"Are you sure you want to delete this channel?": "Er du sikker på du vil slette denne kanal?",
"Are you sure you want to delete this message?": "Er du sikker på du vil slette denne besked?",
"Are you sure you want to unarchive all archived chats?": "Er du sikker på du vil fjerne alle arkiverede chats?",
@ -157,7 +157,7 @@
"Ask": "Spørg",
"Ask a question": "Stil et spørgsmål",
"Assistant": "Assistent",
"Async Embedding Processing": "",
"Async Embedding Processing": "Asynkron embedding processering",
"Attach File From Knowledge": "Vedhæft fil fra viden",
"Attach Knowledge": "Vedhæft viden",
"Attach Notes": "Vedhæft noter",
@ -228,7 +228,7 @@
"Channel deleted successfully": "Kanal slettet",
"Channel Name": "Kanalnavn",
"Channel name cannot be empty.": "Kanalnavn må ikke være tom.",
"Channel Type": "",
"Channel Type": "Kanaltype",
"Channel updated successfully": "Kanal redigeret",
"Channels": "Kanaler",
"Character": "Karakterer",
@ -257,7 +257,7 @@
"Citations": "Citater",
"Clear memory": "Slet hukommelse",
"Clear Memory": "Slet hukommelse",
"Clear status": "",
"Clear status": "Slet status",
"click here": "klik her",
"Click here for filter guides.": "Klik her for filter guider",
"Click here for help.": "Klik her for hjælp",
@ -294,7 +294,7 @@
"Code Interpreter": "Kode interpreter",
"Code Interpreter Engine": "Kode interpreter engine",
"Code Interpreter Prompt Template": "Kode interpreter prompt template",
"Collaboration channel where people join as members": "",
"Collaboration channel where people join as members": "Samarbejdskanal hvor folk tilmelder sig som medlemmer",
"Collapse": "Kollapse",
"Collection": "Samling",
"Color": "Farve",
@ -386,7 +386,7 @@
"Datalab Marker API": "Datalab Marker API",
"DD/MM/YYYY": "DD/MM/ÅÅÅÅ",
"December": "december",
"Decrease UI Scale": "",
"Decrease UI Scale": "Nedjuster UI-skalering",
"Deepgram": "Deepgram",
"Default": "Standard",
"Default (Open AI)": "Standard (Open AI)",
@ -395,14 +395,14 @@
"Default description enabled": "Standardbeskrivelse aktiveret",
"Default Features": "Standardfunktioner",
"Default Filters": "Standardfiltre",
"Default Group": "",
"Default Group": "Standardgruppe",
"Default mode works with a wider range of models by calling tools once before execution. Native mode leverages the model's built-in tool-calling capabilities, but requires the model to inherently support this feature.": "Standardtilstand fungerer med et bredere udvalg af modeller ved at kalde værktøjer én gang før udførelse. Native tilstand udnytter modellens indbyggede værktøjskald-funktioner, men kræver at modellen i sagens natur understøtter denne funktion.",
"Default Model": "Standard model",
"Default model updated": "Standard model opdateret",
"Default Models": "Standard modeller",
"Default permissions": "Standard tilladelser",
"Default permissions updated successfully": "Standard tilladelser opdateret",
"Default Pinned Models": "",
"Default Pinned Models": "Standard fastgjorte modeller",
"Default Prompt Suggestions": "Standardforslag til prompt",
"Default to 389 or 636 if TLS is enabled": "Standard til 389 eller 636 hvis TLS er aktiveret",
"Default to ALL": "Standard til ALLE",
@ -411,7 +411,7 @@
"Delete": "Slet",
"Delete a model": "Slet en model",
"Delete All Chats": "Slet alle chats",
"Delete all contents inside this folder": "",
"Delete all contents inside this folder": "Slet alt indhold i denne mappe",
"Delete All Models": "Slet alle modeller",
"Delete Chat": "Slet chat",
"Delete chat?": "Slet chat?",
@ -437,7 +437,7 @@
"Direct": "Direkte",
"Direct Connections": "Direkte forbindelser",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Direkte forbindelser tillader brugere at oprette forbindelse til deres egen OpenAI kompatible API endpoints.",
"Direct Message": "",
"Direct Message": "Direkte besked",
"Direct Tool Servers": "Direkte værktøjsservere",
"Directory selection was cancelled": "Valg af mappe annulleret",
"Disable Code Interpreter": "Deaktiver kode interpreter",
@ -454,7 +454,7 @@
"Discover, download, and explore custom prompts": "Find, download og udforsk unikke prompts",
"Discover, download, and explore custom tools": "Find, download og udforsk unikke værktøjer",
"Discover, download, and explore model presets": "Find, download og udforsk modelindstillinger",
"Discussion channel where access is based on groups and permissions": "",
"Discussion channel where access is based on groups and permissions": "Diskussionskanal hvor adgang styres af grupper og tilladelser",
"Display": "Vis",
"Display chat title in tab": "Vis chattitel i fane",
"Display Emoji in Call": "Vis emoji i chat",
@ -466,12 +466,12 @@
"Do not install functions from sources you do not fully trust.": "Lad være med at installere funktioner fra kilder, som du ikke stoler på.",
"Do not install tools from sources you do not fully trust.": "Lad være med at installere værktøjer fra kilder, som du ikke stoler på.",
"Docling": "Docling",
"Docling Parameters": "",
"Docling Parameters": "Docling parametre",
"Docling Server URL required.": "Docling Server URL påkrævet.",
"Document": "Dokument",
"Document Intelligence": "Document Intelligence",
"Document Intelligence endpoint required.": "Document Intelligence endpoint påkrævet",
"Document Intelligence Model": "",
"Document Intelligence Model": "Document Intelligence model",
"Documentation": "Dokumentation",
"Documents": "Dokumenter",
"does not make any external connections, and your data stays securely on your locally hosted server.": "laver ikke eksterne kald, og din data bliver sikkert på din egen lokalt hostede server.",
@ -494,15 +494,15 @@
"e.g. \"json\" or a JSON schema": "f.eks. \"json\" eller en JSON schema",
"e.g. 60": "f.eks. 60",
"e.g. A filter to remove profanity from text": "f.eks. Et filter til at fjerne upassende ord fra tekst",
"e.g. about the Roman Empire": "",
"e.g. about the Roman Empire": "f.eks. om Romerriget",
"e.g. en": "f.eks. en",
"e.g. My Filter": "f.eks. Mit Filter",
"e.g. My Tools": "f.eks. Mine Værktøjer",
"e.g. my_filter": "f.eks. mit_filter",
"e.g. my_tools": "f.eks. mine_værktøjer",
"e.g. pdf, docx, txt": "f.eks. pdf, docx, txt",
"e.g. Tell me a fun fact": "",
"e.g. Tell me a fun fact about the Roman Empire": "",
"e.g. Tell me a fun fact": "f.eks. fortæl mig en fun fact",
"e.g. Tell me a fun fact about the Roman Empire": "f.eks. fortæl mig en fun fact om Romerriget",
"e.g. Tools for performing various operations": "f.eks. Værktøjer til at udføre forskellige operationer",
"e.g., 3, 4, 5 (leave blank for default)": "f.eks. 3, 4, 5 (lad være tom for standard)",
"e.g., audio/wav,audio/mpeg,video/* (leave blank for defaults)": "f.eks. audio/wav,audio/mpeg,video/* (lad være tom for standarder)",
@ -531,7 +531,7 @@
"Embedding Batch Size": "Embedding Batch størrelse",
"Embedding Model": "Embedding Model",
"Embedding Model Engine": "Embedding Model engine",
"Enable API Keys": "",
"Enable API Keys": "Aktiver API nøgler",
"Enable autocomplete generation for chat messages": "Aktiver autofuldførsel for chatbeskeder",
"Enable Code Execution": "Aktiver kodekørsel",
"Enable Code Interpreter": "Aktiver kode interpreter",
@ -547,14 +547,14 @@
"Endpoint URL": "Endpoint URL",
"Enforce Temporary Chat": "Gennemtving midlertidig chat",
"Enhance": "Forbedre",
"Enrich Hybrid Search Text": "",
"Enrich Hybrid Search Text": "Berig hybrid søgetekst",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Sørg for at din CSV-fil indeholder 4 kolonner i denne rækkefølge: Name, Email, Password, Role.",
"Enter {{role}} message here": "Indtast {{role}} besked her",
"Enter a detail about yourself for your LLMs to recall": "Indtast en detalje om dig selv, som dine LLMs kan huske",
"Enter a title for the pending user info overlay. Leave empty for default.": "Indtast en titel til afventende bruger informations overlay. Lad være tom for standard.",
"Enter a watermark for the response. Leave empty for none.": "Indtast et vandmærke til svaret. Lad være tom for ingen.",
"Enter additional headers in JSON format": "Indtast yderligere headers i JSON format",
"Enter additional headers in JSON format (e.g. {\"X-Custom-Header\": \"value\"}": "",
"Enter additional headers in JSON format (e.g. {\"X-Custom-Header\": \"value\"}": "Indtast yderligere headers i JSON format (f.eks. {\"X-Custom-Header\": \"value\"})",
"Enter additional parameters in JSON format": "Indtast yderligere parametre i JSON format",
"Enter api auth string (e.g. username:password)": "Indtast api-godkendelsesstreng (f.eks. brugernavn:adgangskode)",
"Enter Application DN": "Indtast Application DN",
@ -572,12 +572,12 @@
"Enter Datalab Marker API Base URL": "Indtast Datalab Marker API base URL",
"Enter Datalab Marker API Key": "Indtast Datalab Marker API nøgle",
"Enter description": "Indtast beskrivelse",
"Enter Docling API Key": "",
"Enter Docling API Key": "Indtast Docling API nøgler",
"Enter Docling Server URL": "Indtast Docling Server URL",
"Enter Document Intelligence Endpoint": "Indtast Dokument Intelligence Endpoint",
"Enter Document Intelligence Key": "Indtast Dokument Intelligence nøgle",
"Enter Document Intelligence Model": "",
"Enter domains separated by commas (e.g., example.com,site.org,!excludedsite.com)": "",
"Enter Document Intelligence Model": "Indtast Dokument Intelligence model",
"Enter domains separated by commas (e.g., example.com,site.org,!excludedsite.com)": "Indtast domæner separeret af kommaer (f.eks. example.com,site.org,!excludedsite.com)",
"Enter Exa API Key": "Indtast Exa API nøgle",
"Enter External Document Loader API Key": "Indtast External Dokument Loader API nøgle",
"Enter External Document Loader URL": "Indtast External Dokument Loader URL",
@ -588,7 +588,7 @@
"Enter Firecrawl API Base URL": "Indtast Firecrawl API base URL",
"Enter Firecrawl API Key": "Indtast Firecrawl API nøgle",
"Enter folder name": "Indtast mappenavn",
"Enter function name filter list (e.g. func1, !func2)": "",
"Enter function name filter list (e.g. func1, !func2)": "Indtast funktionsnavn filterliste (f.eks. func1, !func2)",
"Enter Github Raw URL": "Indtast Github Raw URL",
"Enter Google PSE API Key": "Indtast Google PSE API-nøgle",
"Enter Google PSE Engine Id": "Indtast Google PSE Engine ID",
@ -614,7 +614,7 @@
"Enter Number of Steps (e.g. 50)": "Indtast antal trin (f.eks. 50)",
"Enter Ollama Cloud API Key": "Indtast Ollama Cloud API nøgle",
"Enter Perplexity API Key": "Indtast Perplexity API nøgle",
"Enter Perplexity Search API URL": "",
"Enter Perplexity Search API URL": "Indtast Perplexity Search API URL",
"Enter Playwright Timeout": "Indtast Playwright timeout",
"Enter Playwright WebSocket URL": "Indtast Playwright WebSocket URL",
"Enter proxy URL (e.g. https://user:password@host:port)": "Indtast proxy URL (f.eks. https://bruger:adgangskode@host:port)",
@ -700,11 +700,11 @@
"Export chat (.json)": "Eksportér chat (.json)",
"Export Chats": "Eksportér chats",
"Export Config to JSON File": "Eksportér konfiguration til JSON-fil",
"Export Models": "",
"Export Models": "Eksportér modeller",
"Export Presets": "Eksportér indstillinger",
"Export Prompts": "",
"Export Prompts": "Eksportér prompter",
"Export to CSV": "Eksportér til CSV",
"Export Tools": "",
"Export Tools": "Eksportér værktøjer",
"Export Users": "Eksportér brugere",
"External": "Ekstern",
"External Document Loader URL required.": "External Dokument Loader URL påkrævet.",
@ -716,8 +716,8 @@
"External Web Search URL": "Ekstern Web Search URL",
"Fade Effect for Streaming Text": "Fade-effekt for streaming tekst",
"Failed to add file.": "Kunne ikke tilføje fil.",
"Failed to add members": "",
"Failed to clear status": "",
"Failed to add members": "Kunne ikke tilføje medlemmer",
"Failed to clear status": "Kunne ikke fjerne status",
"Failed to connect to {{URL}} OpenAPI tool server": "Kunne ikke forbinde til {{URL}} OpenAPI tool server",
"Failed to copy link": "Kunne ikke kopiere link",
"Failed to create API Key.": "Kunne ikke oprette API-nøgle.",
@ -731,19 +731,19 @@
"Failed to load file content.": "Kunne ikke indlæse filindhold.",
"Failed to move chat": "Kunne ikke flytte chat",
"Failed to read clipboard contents": "Kunne ikke læse indholdet af udklipsholderen",
"Failed to remove member": "",
"Failed to remove member": "Kunne ikke fjerne medlem",
"Failed to render diagram": "Kunne ikke rendere diagram",
"Failed to render visualization": "Kunne ikke rendere visualisering",
"Failed to save connections": "Kunne ikke gemme forbindelser",
"Failed to save conversation": "Kunne ikke gemme samtalen",
"Failed to save models configuration": "Kunne ikke gemme modeller konfiguration",
"Failed to update settings": "Kunne ikke opdatere indstillinger",
"Failed to update status": "",
"Failed to update status": "Kunne ikke opdatere status",
"Failed to upload file.": "Kunne ikke uploade fil.",
"Features": "Features",
"Features Permissions": "Features tilladelser",
"February": "Februar",
"Feedback deleted successfully": "",
"Feedback deleted successfully": "Feedback slettet",
"Feedback Details": "Feedback detaljer",
"Feedback History": "Feedback historik",
"Feedbacks": "Feedback",
@ -758,7 +758,7 @@
"File size should not exceed {{maxSize}} MB.": "Filstørrelsen må ikke overstige {{maxSize}} MB.",
"File Upload": "Fil upload",
"File uploaded successfully": "Fil uploadet.",
"File uploaded!": "",
"File uploaded!": "Fil uploadet!",
"Files": "Filer",
"Filter": "Filter",
"Filter is now globally disabled": "Filter er nu globalt deaktiveret",
@ -803,7 +803,7 @@
"Function is now globally disabled": "Funktionen er nu globalt deaktiveret",
"Function is now globally enabled": "Funktionen er nu globalt aktiveret",
"Function Name": "Funktionsnavn",
"Function Name Filter List": "",
"Function Name Filter List": "Funktionsnavn filterliste",
"Function updated successfully": "Funktion opdateret.",
"Functions": "Funktioner",
"Functions allow arbitrary code execution.": "Funktioner tillader kørsel af vilkårlig kode.",
@ -832,13 +832,13 @@
"Google PSE Engine Id": "Google PSE Engine-ID",
"Gravatar": "Gravatar",
"Group": "Gruppe",
"Group Channel": "",
"Group Channel": "Gruppekanal",
"Group created successfully": "Gruppe oprettet.",
"Group deleted successfully": "Gruppe slettet.",
"Group Description": "Gruppe beskrivelse",
"Group Name": "Gruppenavn",
"Group updated successfully": "Gruppe opdateret.",
"groups": "",
"groups": "grupper",
"Groups": "Grupper",
"H1": "H1",
"H2": "H2",
@ -875,7 +875,7 @@
"Image Compression": "Billedkomprimering",
"Image Compression Height": "Billedkomprimering højde",
"Image Compression Width": "Billedkomprimering bredde",
"Image Edit": "",
"Image Edit": "Billederedigering",
"Image Edit Engine": "Billederedigeringsmotor",
"Image Generation": "Billedgenerering",
"Image Generation Engine": "Billedgenereringsmotor",
@ -890,18 +890,18 @@
"Import Chats": "Importer chats",
"Import Config from JSON File": "Importer konfiguration fra JSON-fil",
"Import From Link": "Importer fra et link",
"Import Models": "",
"Import Models": "Importer modeller",
"Import Notes": "Importer noter",
"Import Presets": "Importer Presets",
"Import Prompts": "",
"Import Prompts": "Importer prompter",
"Import successful": "Importeret",
"Import Tools": "",
"Import Tools": "Importer værktøjer",
"Important Update": "Vigtig opdatering",
"Include": "Inkluder",
"Include `--api-auth` flag when running stable-diffusion-webui": "Inkluder `--api-auth` flag, når du kører stable-diffusion-webui",
"Include `--api` flag when running stable-diffusion-webui": "Inkluder `--api` flag, når du kører stable-diffusion-webui",
"Includes SharePoint": "Inkluderer SharePoint",
"Increase UI Scale": "",
"Increase UI Scale": "Forøg UI-skalering",
"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive.": "Påvirker hvor hurtigt algoritmen reagerer på feedback fra den genererede tekst. En lavere indlæringshastighed vil resultere i langsommere justeringer, mens en højere indlæringshastighed vil gøre algoritmen mere responsiv.",
"Info": "Info",
"Initials": "Initialer",
@ -924,7 +924,7 @@
"Invalid JSON format for ComfyUI Edit Workflow.": "Ugyldigt JSON format for ComfyUI Edit Workflow.",
"Invalid JSON format for ComfyUI Workflow.": "Ugyldigt JSON format for ComfyUI Workflow.",
"Invalid JSON format for Parameters": "Ugyldigt JSON format for parametre",
"Invalid JSON format in {{NAME}}": "",
"Invalid JSON format in {{NAME}}": "Ugyldigt JSON format i {{NAME}}",
"Invalid JSON format in Additional Config": "Ugyldigt JSON format for yderligere konfiguration",
"Invalid JSON format in MinerU Parameters": "Ugyldigt JSON format for MinerU parametre",
"Invalid Tag": "Ugyldigt tag",
@ -958,7 +958,7 @@
"Knowledge Name": "Vidensnavn",
"Knowledge Public Sharing": "Viden offentlig deling",
"Knowledge reset successfully.": "Viden nulstillet.",
"Knowledge Sharing": "",
"Knowledge Sharing": "Vidensdeling",
"Knowledge updated successfully": "Viden opdateret.",
"Kokoro.js (Browser)": "Kokoro.js (Browser)",
"Kokoro.js Dtype": "Kokoro.js Dtype",
@ -1024,13 +1024,13 @@
"Max Upload Size": "Maks. uploadstørrelse",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Højst 3 modeller kan downloades samtidigt. Prøv igen senere.",
"May": "Maj",
"MBR": "",
"MBR": "MBR",
"MCP": "MCP",
"MCP support is experimental and its specification changes often, which can lead to incompatibilities. OpenAPI specification support is directly maintained by the Open WebUI team, making it the more reliable option for compatibility.": "MCP understøttelse er eksperimentel og dens specifikationer ændres ofte hvilket kan medføre inkompatibilitet. OpenAI specifikationsunderstøttelse er vedligeholdt af Open WebUI-teamet hvilket gør det til den mest pålidelige mulighed for understøttelse.",
"Medium": "Medium",
"Member removed successfully": "",
"Members": "",
"Members added successfully": "",
"Member removed successfully": "Medlem fjernet",
"Members": "Medlemmer",
"Members added successfully": "Medlemmer tilføjet",
"Memories accessible by LLMs will be shown here.": "Minder, der er tilgængelige for LLM'er, vises her.",
"Memory": "Hukommelse",
"Memory added successfully": "Hukommelse tilføjet.",
@ -1084,7 +1084,7 @@
"Models configuration saved successfully": "Modeller konfiguration gemt",
"Models imported successfully": "Modeller importeret",
"Models Public Sharing": "Modeller offentlig deling",
"Models Sharing": "",
"Models Sharing": "Modeldeling",
"Mojeek Search API Key": "Mojeek Search API nøgle",
"More": "Mere",
"More Concise": "Mere kortfattet",
@ -1130,7 +1130,7 @@
"No models selected": "Ingen modeller valgt",
"No Notes": "Ingen noter",
"No notes found": "Ingen noter fundet",
"No pinned messages": "",
"No pinned messages": "Ingen fastgjorte beskeder",
"No prompts found": "Ingen prompts fundet",
"No results": "Ingen resultater fundet",
"No results found": "Ingen resultater fundet",
@ -1152,7 +1152,7 @@
"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Bemærk: Hvis du angiver en minimumscore, returnerer søgningen kun dokumenter med en score, der er større end eller lig med minimumscoren.",
"Notes": "Noter",
"Notes Public Sharing": "Noter offentlig deling",
"Notes Sharing": "",
"Notes Sharing": "Notedeling",
"Notification Sound": "Notifikationslyd",
"Notification Webhook": "Notifikations webhook",
"Notifications": "Notifikationer",
@ -1178,7 +1178,7 @@
"Only alphanumeric characters and hyphens are allowed in the command string.": "Kun alfanumeriske tegn og bindestreger er tilladt i kommandostrengen.",
"Only can be triggered when the chat input is in focus.": "Kan kun udløses når chat-input er fokuseret.",
"Only collections can be edited, create a new knowledge base to edit/add documents.": "Kun samlinger kan redigeres, opret en ny vidensbase for at redigere/tilføje dokumenter.",
"Only invited users can access": "",
"Only invited users can access": "Kun inviterede brugere kan få adgang",
"Only markdown files are allowed": "Kun markdown-filer er tilladt",
"Only select users and groups with permission can access": "Kun valgte brugere og grupper med tilladelse kan tilgå",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ups! URL'en ser ud til at være ugyldig. Tjek den igen, og prøv igen.",
@ -1236,12 +1236,12 @@
"Permissions": "Tilladelser",
"Perplexity API Key": "Perplexity API nøgle",
"Perplexity Model": "Perplexity model",
"Perplexity Search API URL": "",
"Perplexity Search API URL": "Perplexity Search API URL",
"Perplexity Search Context Usage": "Perplexity søgekontekst brug",
"Personalization": "Personalisering",
"Pin": "Fastgør",
"Pinned": "Fastgjort",
"Pinned Messages": "",
"Pinned Messages": "Fastgjorte beskeder",
"Pioneer insights": "Banebrydende indsigter",
"Pipe": "Pipe",
"Pipeline deleted successfully": "Pipeline slettet.",
@ -1271,7 +1271,7 @@
"Please select a model.": "Vælg en model.",
"Please select a reason": "Vælg en årsag",
"Please select a valid JSON file": "Vælg en valid JSON-fil",
"Please select at least one user for Direct Message channel.": "",
"Please select at least one user for Direct Message channel.": "Vælg mindst én bruger til direkte besked-kanal.",
"Please wait until all files are uploaded.": "Vent venligst indtil alle filerne er uploadet.",
"Port": "Port",
"Positive attitude": "Positiv holdning",
@ -1284,7 +1284,7 @@
"Previous 7 days": "Seneste 7 dage",
"Previous message": "Forrige besked",
"Private": "Privat",
"Private conversation between selected users": "",
"Private conversation between selected users": "Privat samtale mellem valgte brugere",
"Profile": "Profil",
"Prompt": "Prompt",
"Prompt Autocompletion": "Prompt autofuldførelse",
@ -1294,7 +1294,7 @@
"Prompts": "Prompts",
"Prompts Access": "Prompts adgang",
"Prompts Public Sharing": "Prompts offentlig deling",
"Prompts Sharing": "",
"Prompts Sharing": "Promptdeling",
"Provider Type": "Udbyder type",
"Public": "Offentlig",
"Pull \"{{searchValue}}\" from Ollama.com": "Hent \"{{searchValue}}\" fra Ollama.com",
@ -1365,8 +1365,8 @@
"Retrieval": "Hentning",
"Retrieval Query Generation": "Hentnings forespørgsel generering",
"Retrieved {{count}} sources": "Fandt en kildehenvisning",
"Retrieved {{count}} sources_one": "Fandt {{count}} sources_one",
"Retrieved {{count}} sources_other": "Fandt {{count}} sources_other",
"Retrieved {{count}} sources_one": "Fandt {{count}} kildehenvisning",
"Retrieved {{count}} sources_other": "Fandt {{count}} kildehenvisninger",
"Retrieved 1 source": "Fandt en kildehenvisning",
"Rich Text Input for Chat": "Rich text input til chat",
"RK": "RK",
@ -1377,7 +1377,7 @@
"Run": "Kør",
"Running": "Kører",
"Running...": "Kører...",
"Runs embedding tasks concurrently to speed up processing. Turn off if rate limits become an issue.": "",
"Runs embedding tasks concurrently to speed up processing. Turn off if rate limits become an issue.": "Kører embedding-opgaver sideløbende for at fremskynde behandlingen. Slå fra hvis hastighedsbegrænsninger bliver et problem.",
"Save": "Gem",
"Save & Create": "Gem og opret",
"Save & Update": "Gem og opdater",
@ -1472,14 +1472,14 @@
"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Indstil antallet af arbejdstråde brugt til beregning. Denne mulighed styrer, hvor mange tråde der bruges til at behandle indkommende forespørgsler samtidigt. At øge denne værdi kan forbedre ydeevnen under høj samtidighedsbelastning, men kan også forbruge flere CPU-ressourcer.",
"Set Voice": "Indstil stemme",
"Set whisper model": "Indstil whisper model",
"Set your status": "",
"Set your status": "Indstil din status",
"Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled.": "Indstiller en flad bias mod tokens, der er forekommet mindst én gang. En højere værdi (f.eks. 1,5) vil straffe gentagelser stærkere, mens en lavere værdi (f.eks. 0,9) vil være mere lemfældig. Ved 0 er det deaktiveret.",
"Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled.": "Indstiller en skalerende bias mod tokens for at straffe gentagelser, baseret på hvor mange gange de er forekommet. En højere værdi (f.eks. 1,5) vil straffe gentagelser stærkere, mens en lavere værdi (f.eks. 0,9) vil være mere lemfældig. Ved 0 er det deaktiveret.",
"Sets how far back for the model to look back to prevent repetition.": "Indstiller hvor langt tilbage modellen skal se for at forhindre gentagelse.",
"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt.": "Indstiller det tilfældige tal seed til brug for generering. At indstille dette til et specifikt tal vil få modellen til at generere den samme tekst for den samme prompt.",
"Sets the size of the context window used to generate the next token.": "Indstiller størrelsen af kontekstvinduet brugt til at generere det næste token.",
"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Indstiller stop-sekvenserne til brug. Når dette mønster stødes på, vil LLM'en stoppe med at generere tekst og returnere. Flere stop-mønstre kan indstilles ved at specificere flere separate stop-parametre i en modelfil.",
"Setting": "",
"Setting": "Indstilling",
"Settings": "Indstillinger",
"Settings saved successfully!": "Indstillinger gemt!",
"Share": "Del",
@ -1525,9 +1525,9 @@
"Start a new conversation": "Start en ny samtale",
"Start of the channel": "Kanalens start",
"Start Tag": "Start tag",
"Status": "",
"Status cleared successfully": "",
"Status updated successfully": "",
"Status": "Status",
"Status cleared successfully": "Status slettet",
"Status updated successfully": "Status opdateret",
"Status Updates": "Statusopdateringer",
"STDOUT/STDERR": "STDOUT/STDERR",
"Steps": "Trin",
@ -1543,7 +1543,7 @@
"STT Model": "STT-model",
"STT Settings": "STT-indstillinger",
"Stylized PDF Export": "Stiliseret PDF eksport",
"Subtitle": "",
"Subtitle": "Undertekst",
"Success": "Succes",
"Successfully imported {{userCount}} users.": "Importerede {{userCount}} brugere.",
"Successfully updated.": "Opdateret.",
@ -1660,7 +1660,7 @@
"Tools Function Calling Prompt": "Værktøjs Funktionkaldprompt",
"Tools have a function calling system that allows arbitrary code execution.": "Værktøjer har et funktionkaldssystem, der tillader vilkårlig kodeudførelse.",
"Tools Public Sharing": "Værktøjer Offentlig Deling",
"Tools Sharing": "",
"Tools Sharing": "Værktøjsdeling",
"Top K": "Top K",
"Top K Reranker": "Top K omarrangering",
"Transformers": "Transformers",
@ -1676,7 +1676,7 @@
"Type Hugging Face Resolve (Download) URL": "Indtast Hugging Face Resolve (Download) URL",
"Uh-oh! There was an issue with the response.": "Uh-oh! Der var et problem det det svar.",
"UI": "UI",
"UI Scale": "",
"UI Scale": "UI-skalering",
"Unarchive All": "Udpak alle arkiver",
"Unarchive All Archived Chats": "Udpak alle arkiverede chats",
"Unarchive Chat": "Fjern chat fra arkiv",
@ -1694,7 +1694,7 @@
"Update and Copy Link": "Opdater og kopier link",
"Update for the latest features and improvements.": "Opdater for at få de nyeste funktioner og forbedringer.",
"Update password": "Opdater adgangskode",
"Update your status": "",
"Update your status": "Opdater din status",
"Updated": "Opdateret",
"Updated at": "Opdateret kl.",
"Updated At": "Opdateret Klokken.",
@ -1709,7 +1709,7 @@
"Upload Pipeline": "Upload pipeline",
"Upload Progress": "Uploadfremdrift",
"Upload Progress: {{uploadedFiles}}/{{totalFiles}} ({{percentage}}%)": "Uploadfremdrift: {{uploadedFiles}}/{{totalFiles}} ({{percentage}}%)",
"Uploading file...": "",
"Uploading file...": "Uploader fil...",
"URL": "URL",
"URL is required": "URL er påkrævet",
"URL Mode": "URL-tilstand",
@ -1728,7 +1728,7 @@
"User menu": "Brugermenu",
"User Webhooks": "Bruger Webhooks",
"Username": "Brugernavn",
"users": "",
"users": "brugere",
"Users": "Brugere",
"Uses DefaultAzureCredential to authenticate": "Bruger DefaultAzureCredential til at autentificere",
"Uses OAuth 2.1 Dynamic Client Registration": "Bruger OAuth 2.1 Dynamic Client Registration",
@ -1748,13 +1748,13 @@
"View Replies": "Vis svar",
"View Result from **{{NAME}}**": "Vis resultat fra **{{NAME}}**",
"Visibility": "Synlighed",
"Visible to all users": "",
"Visible to all users": "Synlig for alle brugere",
"Vision": "Vision",
"Voice": "Stemme",
"Voice Input": "Stemme Input",
"Voice mode": "Stemme tilstand",
"Voice Mode Custom Prompt": "",
"Voice Mode Prompt": "",
"Voice mode": "Stemmetilstand",
"Voice Mode Custom Prompt": "Brugerdefineret prompt til stemmetilstand",
"Voice Mode Prompt": "Prompt til stemmetilstand",
"Warning": "Advarsel",
"Warning:": "Advarsel:",
"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Advarsel: Hvis du aktiverer dette, vil brugerne kunne uploade vilkårlig kode på serveren.",
@ -1776,7 +1776,7 @@
"What are you trying to achieve?": "Hvad prøver du at opnå?",
"What are you working on?": "Hvad arbejder du på?",
"What's New in": "Nyheder i",
"What's on your mind?": "",
"What's on your mind?": "Hvad har du på hjertet?",
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Når aktiveret, vil modellen reagere på hver chatbesked i realtid og generere et svar, så snart brugeren sender en besked. Denne tilstand er nyttig til live chat-applikationer, men kan påvirke ydeevnen på langsommere hardware.",
"wherever you are": "hvad end du er",
"Whether to paginate the output. Each page will be separated by a horizontal rule and page number. Defaults to False.": "Om outputtet skal pagineres. Hver side vil være adskilt af en vandret streg og sidetal. Standard er False.",

View file

@ -246,7 +246,10 @@
console.log('Shortcut triggered: GENERATE_MESSAGE_PAIR');
event.preventDefault();
document.getElementById('generate-message-pair-button')?.click();
} else if (isShortcutMatch(event, shortcuts[Shortcut.REGENERATE_RESPONSE])) {
} else if (
isShortcutMatch(event, shortcuts[Shortcut.REGENERATE_RESPONSE]) &&
document.activeElement?.id === 'chat-input'
) {
console.log('Shortcut triggered: REGENERATE_RESPONSE');
event.preventDefault();
[...document.getElementsByClassName('regenerate-response-button')]?.at(-1)?.click();