Merge branch 'staging' into whitelist-hosts

d286fff42cdd6c1b6b16dcd81a5f204e2ddaf104

Cohee <18619528+Cohee1207@users.noreply.github.com>

6 files changed, +76 -13Ignore whitespace
public/index.html+4 -0
@@ -2867,6 +2867,10 @@
2867 <option value="o3-mini">o3-mini</option>2867 <option value="o3-mini">o3-mini</option>
2868 <option value="o3-mini-2025-01-31">o3-mini-2025-01-31</option>2868 <option value="o3-mini-2025-01-31">o3-mini-2025-01-31</option>
2869 </optgroup>2869 </optgroup>
2870 <optgroup label="GPT-4.5">
2871 <option value="gpt-4.5-preview">gpt-4.5-preview</option>
2872 <option value="gpt-4.5-preview-2025-02-27">gpt-4.5-preview-2025-02-27</option>
2873 </optgroup>
2870 <optgroup label="GPT-4 Turbo and GPT-4">2874 <optgroup label="GPT-4 Turbo and GPT-4">
2871 <option value="gpt-4-turbo">gpt-4-turbo</option>2875 <option value="gpt-4-turbo">gpt-4-turbo</option>
2872 <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>2876 <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>
public/scripts/extensions/caption/settings.html+2 -0
@@ -47,6 +47,8 @@
47 <option data-type="openai" value="chatgpt-4o-latest">chatgpt-4o-latest</option>47 <option data-type="openai" value="chatgpt-4o-latest">chatgpt-4o-latest</option>
48 <option data-type="openai" value="o1">o1</option>48 <option data-type="openai" value="o1">o1</option>
49 <option data-type="openai" value="o1-2024-12-17">o1-2024-12-17</option>49 <option data-type="openai" value="o1-2024-12-17">o1-2024-12-17</option>
50 <option data-type="openai" value="gpt-4.5-preview">gpt-4.5-preview</option>
51 <option data-type="openai" value="gpt-4.5-preview-2025-02-27">gpt-4.5-preview-2025-02-27</option>
50 <option data-type="anthropic" value="claude-3-7-sonnet-latest">claude-3-7-sonnet-latest</option>52 <option data-type="anthropic" value="claude-3-7-sonnet-latest">claude-3-7-sonnet-latest</option>
51 <option data-type="anthropic" value="claude-3-7-sonnet-20250219">claude-3-7-sonnet-20250219</option>53 <option data-type="anthropic" value="claude-3-7-sonnet-20250219">claude-3-7-sonnet-20250219</option>
52 <option data-type="anthropic" value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option>54 <option data-type="anthropic" value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option>
public/scripts/i18n.js+61 -11
@@ -9,6 +9,9 @@ var langs;
9// eslint-disable-next-line prefer-const9// eslint-disable-next-line prefer-const
10var localeData;10var localeData;
1111
12/** @type {Set<string>|null} Array of translations keys if they should be tracked - if not tracked then null */
13let trackMissingDynamicTranslate = null;
14
12export const getCurrentLocale = () => localeFile;15export const getCurrentLocale = () => localeFile;
1316
14/**17/**
@@ -97,6 +100,13 @@ export function t(strings, ...values) {
97 */100 */
98export function translate(text, key = null) {101export function translate(text, key = null) {
99 const translationKey = key || text;102 const translationKey = key || text;
103 if (translationKey === null || translationKey === undefined) {
104 console.trace('WARN: No translation key provided');
105 return '';
106 }
107 if (trackMissingDynamicTranslate && localeData && !Object.hasOwn(localeData, translationKey)) {
108 trackMissingDynamicTranslate.add(translationKey);
109 }
100 return localeData?.[translationKey] || text;110 return localeData?.[translationKey] || text;
101}111}
102112
@@ -153,13 +163,26 @@ function translateElement(element) {
153 }163 }
154}164}
155165
166/**
167 * Checks if the given locale is supported and not English.
168 * @param {string} [locale=null] The locale to check (defaults to the current locale)
169 * @returns {boolean} True if the locale is not English and supported
170 */
171function isSupportedNonEnglish(locale = null) {
172 const lang = locale || localeFile;
173 return lang && lang != 'en' && findLang(lang);
174}
156175
157async function getMissingTranslations() {176async function getMissingTranslations() {
177 /** @type {Array<{key: string, language: string, value: string}>} */
158 const missingData = [];178 const missingData = [];
159179
180 if (trackMissingDynamicTranslate) {
181 missingData.push(...Array.from(trackMissingDynamicTranslate).map(key => ({ key, language: localeFile, value: key })));
182 }
183
160 // Determine locales to search for untranslated strings184 // Determine locales to search for untranslated strings
161 const isNotSupported = !findLang(localeFile);185 const langsToProcess = isSupportedNonEnglish() ? [findLang(localeFile)] : langs;
162 const langsToProcess = (isNotSupported || localeFile == 'en') ? langs : [findLang(localeFile)];
163186
164 for (const language of langsToProcess) {187 for (const language of langsToProcess) {
165 const localeData = await getLocaleData(language.lang);188 const localeData = await getLocaleData(language.lang);
@@ -170,7 +193,7 @@ async function getMissingTranslations() {
170 if (attributeMatch) { // attribute-tagged key193 if (attributeMatch) { // attribute-tagged key
171 const localizedValue = localeData?.[attributeMatch[2]];194 const localizedValue = localeData?.[attributeMatch[2]];
172 if (!localizedValue) {195 if (!localizedValue) {
173 missingData.push({ key, language: language.lang, value: $(this).attr(attributeMatch[1]) });196 missingData.push({ key, language: language.lang, value: String($(this).attr(attributeMatch[1])) });
174 }197 }
175 } else { // No attribute tag, treat as 'text'198 } else { // No attribute tag, treat as 'text'
176 const localizedValue = localeData?.[key];199 const localizedValue = localeData?.[key];
@@ -194,17 +217,19 @@ async function getMissingTranslations() {
194 uniqueMissingData.sort((a, b) => a.language.localeCompare(b.language) || a.key.localeCompare(b.key));217 uniqueMissingData.sort((a, b) => a.language.localeCompare(b.language) || a.key.localeCompare(b.key));
195218
196 // Map to { language: { key: value } }219 // Map to { language: { key: value } }
197 let missingDataMap = {};220 const missingDataMap = Object.fromEntries(uniqueMissingData.map(({ key, value }) => [key, value]));
198 for (const { key, value } of uniqueMissingData) {
199 if (!missingDataMap) {
200 missingDataMap = {};
201 }
202 missingDataMap[key] = value;
203 }
204221
222 console.log(`Missing Translations (${uniqueMissingData.length}):`);
205 console.table(uniqueMissingData);223 console.table(uniqueMissingData);
224 console.log(`Full map of missing data (${Object.keys(missingDataMap).length}):`);
206 console.log(missingDataMap);225 console.log(missingDataMap);
207226
227 if (trackMissingDynamicTranslate) {
228 const trackMissingDynamicTranslateMap = Object.fromEntries(Array.from(trackMissingDynamicTranslate).map(key => [key, key]));
229 console.log(`Dynamic translations missing (${Object.keys(trackMissingDynamicTranslateMap).length}):`);
230 console.log(trackMissingDynamicTranslateMap);
231 }
232
208 toastr.success(`Found ${uniqueMissingData.length} missing translations. See browser console for details.`);233 toastr.success(`Found ${uniqueMissingData.length} missing translations. See browser console for details.`);
209}234}
210235
@@ -266,6 +291,31 @@ export async function initLocales() {
266 attributeFilter: ['data-i18n'],291 attributeFilter: ['data-i18n'],
267 });292 });
268293
269 registerDebugFunction('getMissingTranslations', 'Get missing translations', 'Detects missing localization data in the current locale and dumps the data into the browser console. If the current locale is English, searches all other locales.', getMissingTranslations);294 if (localStorage.getItem('trackDynamicTranslate') === 'true' && isSupportedNonEnglish()) {
295 trackMissingDynamicTranslate = new Set();
296 }
297
298 registerDebugFunction('getMissingTranslations', 'Get missing translations',
299 'Detects missing localization data in the current locale and dumps the data into the browser console. ' +
300 'If the current locale is English, searches all other locales.',
301 getMissingTranslations);
302 registerDebugFunction('trackDynamicTranslate', 'Track dynamic translation',
303 'Toggles tracking of dynamic translations, which will be dumped into the missing translations translations too. ' +
304 'This includes things translated via the t`...` function and translate(). It will only track strings translated <b>after</b> this is toggled on, '
305 + 'and when they actually pop up, so refreshing the page and opening popups, etc, is needed. Will only track if the current locale is not English.',
306 () => {
307 const isTracking = localStorage.getItem('trackDynamicTranslate') !== 'true';
308 localStorage.setItem('trackDynamicTranslate', isTracking ? 'true' : 'false');
309 if (isTracking && isSupportedNonEnglish()) {
310 trackMissingDynamicTranslate = new Set();
311 toastr.success('Dynamic translation tracking enabled.');
312 } else if (isTracking) {
313 trackMissingDynamicTranslate = null;
314 toastr.warning('Dynamic translation tracking enabled, but will not be tracked with locale English.');
315 } else {
316 trackMissingDynamicTranslate = null;
317 toastr.info('Dynamic translation tracking disabled.');
318 }
319 });
270 registerDebugFunction('applyLocale', 'Apply locale', 'Reapplies the currently selected locale to the page.', applyLocale);320 registerDebugFunction('applyLocale', 'Apply locale', 'Reapplies the currently selected locale to the page.', applyLocale);
271}321}
public/scripts/openai.js+3 -1
@@ -1945,7 +1945,7 @@ async function sendOpenAIRequest(type, messages, signal) {
1945 }1945 }
19461946
1947 // Remove logit bias, logprobs and stop strings if it's not supported by the model1947 // Remove logit bias, logprobs and stop strings if it's not supported by the model
1948 if (isOAI && oai_settings.openai_model.includes('vision') || isOpenRouter && oai_settings.openrouter_model.includes('vision')) {1948 if (isOAI && oai_settings.openai_model.includes('vision') || isOpenRouter && oai_settings.openrouter_model.includes('vision') || isOAI && oai_settings.openai_model.includes('gpt-4.5-preview')) {
1949 delete generate_data.logit_bias;1949 delete generate_data.logit_bias;
1950 delete generate_data.stop;1950 delete generate_data.stop;
1951 delete generate_data.logprobs;1951 delete generate_data.logprobs;
@@ -4997,6 +4997,8 @@ export function isImageInliningSupported() {
4997 'gpt-4-turbo',4997 'gpt-4-turbo',
4998 'gpt-4o',4998 'gpt-4o',
4999 'gpt-4o-mini',4999 'gpt-4o-mini',
5000 'gpt-4.5-preview',
5001 'gpt-4.5-preview-2025-02-27',
5000 'o1',5002 'o1',
5001 'o1-2024-12-17',5003 'o1-2024-12-17',
5002 'chatgpt-4o-latest',5004 'chatgpt-4o-latest',
public/scripts/reasoning.js+2 -1
@@ -108,6 +108,7 @@ export function isHiddenReasoningModel() {
108108
109 /** @type {{ name: string; func: MatchingFunc; }[]} */109 /** @type {{ name: string; func: MatchingFunc; }[]} */
110 const hiddenReasoningModels = [110 const hiddenReasoningModels = [
111 { name: 'gpt-4.5', func: FUNCS.startsWith },
111 { name: 'o1', func: FUNCS.startsWith },112 { name: 'o1', func: FUNCS.startsWith },
112 { name: 'o3', func: FUNCS.startsWith },113 { name: 'o3', func: FUNCS.startsWith },
113 { name: 'gemini-2.0-flash-thinking-exp', func: FUNCS.startsWith },114 { name: 'gemini-2.0-flash-thinking-exp', func: FUNCS.startsWith },
@@ -493,7 +494,7 @@ export class ReasoningHandler {
493 data = null;494 data = null;
494 }495 }
495496
496 if (this.type !== ReasoningType.Model) {497 if (this.type && this.type !== ReasoningType.Model) {
497 title += ` [${translate(this.type)}]`;498 title += ` [${translate(this.type)}]`;
498 title = title.trim();499 title = title.trim();
499 }500 }
src/endpoints/tokenizers.js+4 -0
@@ -410,6 +410,10 @@ export function getTokenizerModel(requestModel) {
410 return 'gpt-4o';410 return 'gpt-4o';
411 }411 }
412412
413 if (requestModel.includes('gpt-4.5-preview')) {
414 return 'gpt-4o';
415 }
416
413 if (requestModel.includes('gpt-4-32k')) {417 if (requestModel.includes('gpt-4-32k')) {
414 return 'gpt-4-32k';418 return 'gpt-4-32k';
415 }419 }