diff --git a/src/lib/apis/index.ts b/src/lib/apis/index.ts
index d10db74f8..403df51cf 100644
--- a/src/lib/apis/index.ts
+++ b/src/lib/apis/index.ts
@@ -456,7 +456,7 @@ export const executeToolServer = async (
...(token && { authorization: `Bearer ${token}` })
};
- let requestOptions: RequestInit = {
+ const requestOptions: RequestInit = {
method: httpMethod.toUpperCase(),
headers
};
@@ -1005,7 +1005,7 @@ export const getPipelinesList = async (token: string = '') => {
throw error;
}
- let pipelines = res?.data ?? [];
+ const pipelines = res?.data ?? [];
return pipelines;
};
@@ -1148,7 +1148,7 @@ export const getPipelines = async (token: string, urlIdx?: string) => {
throw error;
}
- let pipelines = res?.data ?? [];
+ const pipelines = res?.data ?? [];
return pipelines;
};
diff --git a/src/lib/apis/ollama/index.ts b/src/lib/apis/ollama/index.ts
index 489055c1b..1e0ce5850 100644
--- a/src/lib/apis/ollama/index.ts
+++ b/src/lib/apis/ollama/index.ts
@@ -331,7 +331,7 @@ export const generateTextCompletion = async (token: string = '', model: string,
};
export const generateChatCompletion = async (token: string = '', body: object) => {
- let controller = new AbortController();
+ const controller = new AbortController();
let error = null;
const res = await fetch(`${OLLAMA_API_BASE_URL}/api/chat`, {
diff --git a/src/lib/apis/users/index.ts b/src/lib/apis/users/index.ts
index f2449ff3e..1d0f66919 100644
--- a/src/lib/apis/users/index.ts
+++ b/src/lib/apis/users/index.ts
@@ -126,7 +126,7 @@ export const getUsers = async (
let error = null;
let res = null;
- let searchParams = new URLSearchParams();
+ const searchParams = new URLSearchParams();
searchParams.set('page', `${page}`);
diff --git a/src/lib/components/admin/Settings/Models.svelte b/src/lib/components/admin/Settings/Models.svelte
index 20c0948d0..35f2c3fea 100644
--- a/src/lib/components/admin/Settings/Models.svelte
+++ b/src/lib/components/admin/Settings/Models.svelte
@@ -336,7 +336,7 @@
- {!!model?.meta?.description
+ {model?.meta?.description
? model?.meta?.description
: model?.ollama?.digest
? `${model.id} (${model?.ollama?.digest})`
diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte
index 54fbd4a41..be76a68d5 100644
--- a/src/lib/components/chat/Chat.svelte
+++ b/src/lib/components/chat/Chat.svelte
@@ -125,6 +125,7 @@
let imageGenerationEnabled = false;
let webSearchEnabled = false;
let codeInterpreterEnabled = false;
+ let thinkingEnabled = false;
let chat = null;
let tags = [];
@@ -152,6 +153,8 @@
selectedFilterIds = [];
webSearchEnabled = false;
imageGenerationEnabled = false;
+ codeInterpreterEnabled = false;
+ thinkingEnabled = false;
if (sessionStorage.getItem(`chat-input${chatIdProp ? `-${chatIdProp}` : ''}`)) {
try {
@@ -167,6 +170,7 @@
webSearchEnabled = input.webSearchEnabled;
imageGenerationEnabled = input.imageGenerationEnabled;
codeInterpreterEnabled = input.codeInterpreterEnabled;
+ thinkingEnabled = input.thinkingEnabled;
}
} catch (e) {}
}
@@ -215,6 +219,7 @@
webSearchEnabled = false;
imageGenerationEnabled = false;
codeInterpreterEnabled = false;
+ thinkingEnabled = false;
};
const setToolIds = async () => {
@@ -454,6 +459,7 @@
webSearchEnabled = false;
imageGenerationEnabled = false;
codeInterpreterEnabled = false;
+ thinkingEnabled = false;
try {
const input = JSON.parse(
@@ -468,6 +474,7 @@
webSearchEnabled = input.webSearchEnabled;
imageGenerationEnabled = input.imageGenerationEnabled;
codeInterpreterEnabled = input.codeInterpreterEnabled;
+ thinkingEnabled = input.thinkingEnabled;
}
} catch (e) {}
}
@@ -1659,6 +1666,19 @@
: undefined
},
+ // Add thinking mode option for compatible models
+ ...(thinkingEnabled && (model.owned_by === 'ollama' || model?.info?.meta?.owned_by === 'ollama') &&
+ (() => {
+ const modelName = model?.name?.toLowerCase() || model.id.toLowerCase();
+ return modelName.includes('deepseek-r1') || modelName.includes('qwen3') || modelName.includes('magistral');
+ })()
+ ? {
+ options: {
+ thinking: true
+ }
+ }
+ : {}),
+
files: (files?.length ?? 0) > 0 ? files : undefined,
filter_ids: selectedFilterIds.length > 0 ? selectedFilterIds : undefined,
@@ -2112,6 +2132,7 @@
bind:imageGenerationEnabled
bind:codeInterpreterEnabled
bind:webSearchEnabled
+ bind:thinkingEnabled
bind:atSelectedModel
toolServers={$toolServers}
transparentBackground={$settings?.backgroundImageUrl ?? false}
@@ -2171,6 +2192,7 @@
bind:imageGenerationEnabled
bind:codeInterpreterEnabled
bind:webSearchEnabled
+ bind:thinkingEnabled
bind:atSelectedModel
transparentBackground={$settings?.backgroundImageUrl ?? false}
toolServers={$toolServers}
diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte
index f384c5b8f..533559e3b 100644
--- a/src/lib/components/chat/MessageInput.svelte
+++ b/src/lib/components/chat/MessageInput.svelte
@@ -89,6 +89,9 @@
export let webSearchEnabled = false;
export let codeInterpreterEnabled = false;
+ // Add thinking mode state
+ export let thinkingEnabled = false;
+
$: onChange({
prompt,
files: files
@@ -104,7 +107,8 @@
selectedFilterIds,
imageGenerationEnabled,
webSearchEnabled,
- codeInterpreterEnabled
+ codeInterpreterEnabled,
+ thinkingEnabled
});
let showTools = false;
@@ -159,6 +163,15 @@
$models.find((m) => m.id === model)?.info?.meta?.capabilities?.code_interpreter ?? true
);
+ // Add thinking capable models check
+ let thinkingCapableModels = [];
+ $: thinkingCapableModels = (atSelectedModel?.id ? [atSelectedModel.id] : selectedModels).filter(
+ (model) => {
+ const modelName = $models.find((m) => m.id === model)?.name?.toLowerCase() || model.toLowerCase();
+ return modelName.includes('deepseek-r1') || modelName.includes('qwen3') || modelName.includes('magistral');
+ }
+ );
+
let toggleFilters = [];
$: toggleFilters = (atSelectedModel?.id ? [atSelectedModel.id] : selectedModels)
.map((id) => ($models.find((model) => model.id === id) || {})?.filters ?? [])
@@ -188,6 +201,12 @@
$config?.features?.enable_code_interpreter &&
($_user.role === 'admin' || $_user?.permissions?.features?.code_interpreter);
+ let showThinkingButton = false;
+ $: showThinkingButton =
+ (atSelectedModel?.id ? [atSelectedModel.id] : selectedModels).length ===
+ thinkingCapableModels.length &&
+ thinkingCapableModels.length > 0; // Only show if at least one model supports thinking
+
const scrollToBottom = () => {
const element = document.getElementById('messages-container');
element.scrollTo({
@@ -744,6 +763,28 @@
{/if}
+ {#if thinkingEnabled && showThinkingButton}
+
+
+
+
{$i18n.t('Thinking mode enabled - Model may take longer but respond better')}
+
+
+ {/if}
+
{#if $settings?.richTextInput ?? true}
- {#if $_user && (showToolsButton || (toggleFilters && toggleFilters.length > 0) || showWebSearchButton || showImageGenerationButton || showCodeInterpreterButton)}
+ {#if $_user && (showToolsButton || (toggleFilters && toggleFilters.length > 0) || showWebSearchButton || showImageGenerationButton || showCodeInterpreterButton || showThinkingButton)}
@@ -1394,6 +1435,38 @@
{/if}
+
+ {#if showThinkingButton}
+
+
+
+ {/if}
{/if}
diff --git a/src/lib/components/chat/MessageInput/CallOverlay.svelte b/src/lib/components/chat/MessageInput/CallOverlay.svelte
index ba7f5958c..78c001b03 100644
--- a/src/lib/components/chat/MessageInput/CallOverlay.svelte
+++ b/src/lib/components/chat/MessageInput/CallOverlay.svelte
@@ -50,7 +50,7 @@
const devices = await navigator.mediaDevices.enumerateDevices();
videoInputDevices = devices.filter((device) => device.kind === 'videoinput');
- if (!!navigator.mediaDevices.getDisplayMedia) {
+ if (navigator.mediaDevices.getDisplayMedia) {
videoInputDevices = [
...videoInputDevices,
{
diff --git a/src/lib/components/chat/Placeholder.svelte b/src/lib/components/chat/Placeholder.svelte
index ce524db42..f6842eec7 100644
--- a/src/lib/components/chat/Placeholder.svelte
+++ b/src/lib/components/chat/Placeholder.svelte
@@ -39,6 +39,7 @@
export let imageGenerationEnabled = false;
export let codeInterpreterEnabled = false;
export let webSearchEnabled = false;
+ export let thinkingEnabled = false;
export let toolServers = [];
@@ -213,6 +214,7 @@
bind:imageGenerationEnabled
bind:codeInterpreterEnabled
bind:webSearchEnabled
+ bind:thinkingEnabled
bind:atSelectedModel
{toolServers}
{transparentBackground}
diff --git a/src/lib/components/chat/Settings/Audio.svelte b/src/lib/components/chat/Settings/Audio.svelte
index 8031f1677..75aea2d29 100644
--- a/src/lib/components/chat/Settings/Audio.svelte
+++ b/src/lib/components/chat/Settings/Audio.svelte
@@ -130,7 +130,7 @@
TTSModel = await KokoroTTS.from_pretrained(model_id, {
dtype: TTSEngineConfig.dtype, // Options: "fp32", "fp16", "q8", "q4", "q4f16"
- device: !!navigator?.gpu ? 'webgpu' : 'wasm', // Detect WebGPU
+ device: navigator?.gpu ? 'webgpu' : 'wasm', // Detect WebGPU
progress_callback: (e) => {
TTSModelProgress = e;
console.log(e);
diff --git a/src/lib/i18n/index.ts b/src/lib/i18n/index.ts
index a5a83d093..27c58383c 100644
--- a/src/lib/i18n/index.ts
+++ b/src/lib/i18n/index.ts
@@ -38,10 +38,10 @@ const createIsLoadingStore = (i18n: i18nType) => {
};
export const initI18n = (defaultLocale?: string | undefined) => {
- let detectionOrder = defaultLocale
+ const detectionOrder = defaultLocale
? ['querystring', 'localStorage']
: ['querystring', 'localStorage', 'navigator'];
- let fallbackDefaultLocale = defaultLocale ? [defaultLocale] : ['en-US'];
+ const fallbackDefaultLocale = defaultLocale ? [defaultLocale] : ['en-US'];
const loadResource = (language: string, namespace: string) =>
import(`./locales/${language}/${namespace}.json`);
diff --git a/src/lib/i18n/locales/en-US/translation.json b/src/lib/i18n/locales/en-US/translation.json
index 363ddf6f2..5d75acb3f 100644
--- a/src/lib/i18n/locales/en-US/translation.json
+++ b/src/lib/i18n/locales/en-US/translation.json
@@ -221,6 +221,9 @@
"Code Interpreter": "",
"Code Interpreter Engine": "",
"Code Interpreter Prompt Template": "",
+ "Thinking": "Thinking",
+ "Enable thinking mode - Model may take longer but respond better": "Enable thinking mode - Model may take longer but respond better",
+ "Thinking mode enabled - Model may take longer but respond better": "Thinking mode enabled - Model may take longer but respond better",
"Collapse": "",
"Collection": "",
"Color": "",
diff --git a/src/lib/utils/characters/index.ts b/src/lib/utils/characters/index.ts
index af3436693..dff08e66f 100644
--- a/src/lib/utils/characters/index.ts
+++ b/src/lib/utils/characters/index.ts
@@ -74,15 +74,15 @@ const readPngChunks = (data) => {
if (!isValidPng) throw new Error('Invalid PNG file');
- let chunks = [];
+ const chunks = [];
let offset = 8; // Skip PNG signature
while (offset < data.length) {
- let length =
+ const length =
(data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3];
- let type = String.fromCharCode.apply(null, data.slice(offset + 4, offset + 8));
- let chunkData = data.slice(offset + 8, offset + 8 + length);
- let crc =
+ const type = String.fromCharCode.apply(null, data.slice(offset + 4, offset + 8));
+ const chunkData = data.slice(offset + 8, offset + 8 + length);
+ const crc =
(data[offset + 8 + length] << 24) |
(data[offset + 8 + length + 1] << 16) |
(data[offset + 8 + length + 2] << 8) |
diff --git a/src/lib/utils/marked/katex-extension.ts b/src/lib/utils/marked/katex-extension.ts
index 9e864ca18..8d7d0dc87 100644
--- a/src/lib/utils/marked/katex-extension.ts
+++ b/src/lib/utils/marked/katex-extension.ts
@@ -23,8 +23,8 @@ const ALLOWED_SURROUNDING_CHARS =
// const inlineRule = /^(\${1,2})(?!\$)((?:\\.|[^\\\n])*?(?:\\.|[^\\\n\$]))\1(?=[\s?!\.,:?!。,:]|$)/;
// const blockRule = /^(\${1,2})\n((?:\\[^]|[^\\])+?)\n\1(?:\n|$)/;
-let inlinePatterns = [];
-let blockPatterns = [];
+const inlinePatterns = [];
+const blockPatterns = [];
function escapeRegex(string) {
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
@@ -69,7 +69,7 @@ export default function (options = {}) {
}
function katexStart(src, displayMode: boolean) {
- let ruleReg = displayMode ? blockRule : inlineRule;
+ const ruleReg = displayMode ? blockRule : inlineRule;
let indexSrc = src;
@@ -78,7 +78,7 @@ function katexStart(src, displayMode: boolean) {
let startIndex = -1;
let startDelimiter = '';
let endDelimiter = '';
- for (let delimiter of DELIMITER_LIST) {
+ for (const delimiter of DELIMITER_LIST) {
if (delimiter.display !== displayMode) {
continue;
}
@@ -115,8 +115,8 @@ function katexStart(src, displayMode: boolean) {
}
function katexTokenizer(src, tokens, displayMode: boolean) {
- let ruleReg = displayMode ? blockRule : inlineRule;
- let type = displayMode ? 'blockKatex' : 'inlineKatex';
+ const ruleReg = displayMode ? blockRule : inlineRule;
+ const type = displayMode ? 'blockKatex' : 'inlineKatex';
const match = src.match(ruleReg);
diff --git a/src/lib/workers/kokoro.worker.ts b/src/lib/workers/kokoro.worker.ts
index ce047a655..29175339b 100644
--- a/src/lib/workers/kokoro.worker.ts
+++ b/src/lib/workers/kokoro.worker.ts
@@ -20,7 +20,7 @@ self.onmessage = async (event) => {
try {
tts = await KokoroTTS.from_pretrained(model_id, {
dtype,
- device: !!navigator?.gpu ? 'webgpu' : 'wasm' // Detect WebGPU
+ device: navigator?.gpu ? 'webgpu' : 'wasm' // Detect WebGPU
});
isInitialized = true; // Mark as initialized after successful loading
self.postMessage({ status: 'init:complete' });
diff --git a/src/lib/workers/pyodide.worker.ts b/src/lib/workers/pyodide.worker.ts
index 09fb0e1f3..221effca5 100644
--- a/src/lib/workers/pyodide.worker.ts
+++ b/src/lib/workers/pyodide.worker.ts
@@ -40,7 +40,7 @@ async function loadPyodideAndPackages(packages: string[] = []) {
packages: ['micropip']
});
- let mountDir = '/mnt';
+ const mountDir = '/mnt';
self.pyodide.FS.mkdirTree(mountDir);
// self.pyodide.FS.mount(self.pyodide.FS.filesystems.IDBFS, {}, mountDir);