fix(ui): deduplicate store fetches with null-guards and mutex (#25943)

This commit is contained in:
G30
2026-06-16 18:07:11 -04:00
committed by GitHub
parent 22f2fe1ffb
commit ee3a49a88d
6 changed files with 113 additions and 114 deletions

View File

@@ -150,7 +150,8 @@
selectedTab = 'overview';
chatList = [];
allChatsLoaded = false;
selectRange(selectedRange);
selectedRange = '30d';
loadOverview(30);
}
</script>

View File

@@ -51,7 +51,8 @@
// Load history when model changes and modal is shown
$: if (show && model?.id) {
selectRange(selectedRange);
selectedRange = '30d';
loadHistory(30);
}
// Use top_tags from backend response (already computed)

View File

@@ -117,10 +117,6 @@
.map((c) => ({ type: 'channel', id: c.id, label: c.name, data: c }))
];
} else {
if (userSuggestions) {
getUserList();
}
if (modelSuggestions) {
_models = [
...$models

View File

@@ -113,7 +113,6 @@
import Tooltip from '../common/Tooltip.svelte';
import Sidebar from '../icons/Sidebar.svelte';
import Image from '../common/Image.svelte';
import { getBanners } from '$lib/apis/configs';
export let chatIdProp = '';
@@ -330,107 +329,117 @@
);
};
let settingDefaults = false;
const setDefaults = async () => {
if (!$tools) {
tools.set(await getTools(localStorage.token));
}
if (!$functions) {
functions.set(await getFunctions(localStorage.token));
}
if (!$skills) {
skills.set(await getSkills(localStorage.token));
}
if (selectedModels.length !== 1 && !atSelectedModel) {
return;
}
if (settingDefaults) return;
settingDefaults = true;
const model = atSelectedModel ?? $models.find((m) => m.id === selectedModels[0]);
if (model) {
// Set Default Tools
if (model?.info?.meta?.toolIds) {
const defaultIds = [
...new Set(
[...(model?.info?.meta?.toolIds ?? [])].filter((id) => $tools.find((t) => t.id === id))
)
];
try {
if (!$tools) {
tools.set(await getTools(localStorage.token));
}
if (!$functions) {
functions.set(await getFunctions(localStorage.token));
}
if (!$skills) {
skills.set(await getSkills(localStorage.token));
}
if (selectedModels.length !== 1 && !atSelectedModel) {
return;
}
// Separate unauthenticated OAuth tools
const unauthed = [];
const authed = [];
for (const id of defaultIds) {
const tool = $tools.find((t) => t.id === id);
if (tool && tool.authenticated === false) {
const parts = id.split(':');
const serverId = parts.at(-1) ?? id;
const authType =
parts.length > 1 ? (parts[0] === 'server' ? parts[1] : parts[0]) : null;
unauthed.push({ id, name: tool.name ?? id, serverId, authType });
} else {
authed.push(id);
const model = atSelectedModel ?? $models.find((m) => m.id === selectedModels[0]);
if (model) {
// Set Default Tools
if (model?.info?.meta?.toolIds) {
const defaultIds = [
...new Set(
[...(model?.info?.meta?.toolIds ?? [])].filter((id) =>
$tools.find((t) => t.id === id)
)
)
];
// Separate unauthenticated OAuth tools
const unauthed = [];
const authed = [];
for (const id of defaultIds) {
const tool = $tools.find((t) => t.id === id);
if (tool && tool.authenticated === false) {
const parts = id.split(':');
const serverId = parts.at(-1) ?? id;
const authType =
parts.length > 1 ? (parts[0] === 'server' ? parts[1] : parts[0]) : null;
unauthed.push({ id, name: tool.name ?? id, serverId, authType });
} else {
authed.push(id);
}
}
selectedToolIds = authed;
pendingOAuthTools = unauthed;
} else if ($settings?.tools) {
selectedToolIds = $settings.tools;
} else {
selectedToolIds = selectedToolIds.filter((id) => !id.startsWith('direct_server:'));
}
// Set Default Skills
if (model?.info?.meta?.skillIds) {
selectedSkillIds = [
...new Set(
[...(model?.info?.meta?.skillIds ?? [])].filter((id) =>
($skills ?? []).find((s) => s.id === id && s.is_active)
)
)
];
} else {
selectedSkillIds = [];
}
// Set Default Filters (Toggleable only)
if (model?.info?.meta?.defaultFilterIds) {
selectedFilterIds = model.info.meta.defaultFilterIds.filter((id) =>
model?.filters?.find((f) => f.id === id)
);
}
// Set Default Features
if (model?.info?.meta?.defaultFeatureIds) {
if (
model.info?.meta?.capabilities?.['image_generation'] &&
$config?.features?.enable_image_generation &&
($user?.role === 'admin' || $user?.permissions?.features?.image_generation)
) {
imageGenerationEnabled = model.info.meta.defaultFeatureIds.includes('image_generation');
}
if (
model.info?.meta?.capabilities?.['web_search'] &&
$config?.features?.enable_web_search &&
($user?.role === 'admin' || $user?.permissions?.features?.web_search)
) {
webSearchEnabled = model.info.meta.defaultFeatureIds.includes('web_search');
}
if (
model.info?.meta?.capabilities?.['code_interpreter'] &&
$config?.features?.enable_code_interpreter &&
($user?.role === 'admin' || $user?.permissions?.features?.code_interpreter)
) {
codeInterpreterEnabled = model.info.meta.defaultFeatureIds.includes('code_interpreter');
}
}
selectedToolIds = authed;
pendingOAuthTools = unauthed;
} else if ($settings?.tools) {
selectedToolIds = $settings.tools;
} else {
selectedToolIds = selectedToolIds.filter((id) => !id.startsWith('direct_server:'));
}
// Set Default Skills
if (model?.info?.meta?.skillIds) {
selectedSkillIds = [
...new Set(
[...(model?.info?.meta?.skillIds ?? [])].filter((id) =>
($skills ?? []).find((s) => s.id === id && s.is_active)
)
)
];
} else {
selectedSkillIds = [];
}
// Set Default Filters (Toggleable only)
if (model?.info?.meta?.defaultFilterIds) {
selectedFilterIds = model.info.meta.defaultFilterIds.filter((id) =>
model?.filters?.find((f) => f.id === id)
);
}
// Set Default Features
if (model?.info?.meta?.defaultFeatureIds) {
if (
model.info?.meta?.capabilities?.['image_generation'] &&
$config?.features?.enable_image_generation &&
($user?.role === 'admin' || $user?.permissions?.features?.image_generation)
) {
imageGenerationEnabled = model.info.meta.defaultFeatureIds.includes('image_generation');
}
if (
model.info?.meta?.capabilities?.['web_search'] &&
$config?.features?.enable_web_search &&
($user?.role === 'admin' || $user?.permissions?.features?.web_search)
) {
webSearchEnabled = model.info.meta.defaultFeatureIds.includes('web_search');
}
if (
model.info?.meta?.capabilities?.['code_interpreter'] &&
$config?.features?.enable_code_interpreter &&
($user?.role === 'admin' || $user?.permissions?.features?.code_interpreter)
) {
codeInterpreterEnabled = model.info.meta.defaultFeatureIds.includes('code_interpreter');
}
}
// Set Default Terminal — only if the referenced terminal actually exists
if (model?.info?.meta?.terminalId) {
const tid = model.info.meta.terminalId;
if (isTerminalAvailable(tid)) {
selectedTerminalId.set(tid);
// Set Default Terminal — only if the referenced terminal actually exists
if (model?.info?.meta?.terminalId) {
const tid = model.info.meta.terminalId;
if (isTerminalAvailable(tid)) {
selectedTerminalId.set(tid);
}
}
}
} finally {
settingDefaults = false;
}
};
@@ -796,13 +805,6 @@
if (p.url.pathname === '/') {
await tick();
initNewChat();
// Re-fetch banners on navigation to homepage so newly configured banners appear
try {
banners.set(await getBanners(localStorage.token).catch(() => []));
} catch (e) {
console.error('Failed to refresh banners:', e);
}
}
stopAudio();

View File

@@ -58,8 +58,6 @@
import { deleteFileById } from '$lib/apis/files';
import { getChatById } from '$lib/apis/chats';
import { getSessionUser } from '$lib/apis/auths';
import { getTools } from '$lib/apis/tools';
import { getSkills } from '$lib/apis/skills';
import { WEBUI_BASE_URL, WEBUI_API_BASE_URL, PASTED_TEXT_CHARACTER_LIMIT } from '$lib/constants';
import { getOAuthClientAuthorizationUrl } from '$lib/apis/configs';
@@ -1106,9 +1104,6 @@
dropzoneElement.addEventListener('drop', onDrop, true);
dropzoneElement.addEventListener('dragleave', onDragLeave);
}
tools.set(await getTools(localStorage.token));
skills.set(await getSkills(localStorage.token));
};
initialize();

View File

@@ -248,9 +248,13 @@
};
onMount(async () => {
await tools.set(await getTools(localStorage.token));
if (!$tools) {
await tools.set(await getTools(localStorage.token));
}
skillsList = (await getSkills(localStorage.token).catch(() => null)) ?? [];
await functions.set(await getFunctions(localStorage.token));
if (!$functions) {
await functions.set(await getFunctions(localStorage.token));
}
// Fetch admin-configured default model metadata so the editor
// reflects the actual defaults rather than hardcoded values