Merge branch 'staging' into whitelist-hosts

d286fff42cdd6c1b6b16dcd81a5f204e2ddaf104

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

6 files changed, +76 -13Showing whitespace changes
public/index.html+4 -0
@@ -2867,6 +2867,10 @@
28672867 <option value="o3-mini">o3-mini</option>
28682868 <option value="o3-mini-2025-01-31">o3-mini-2025-01-31</option>
28692869 </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>
28702874 <optgroup label="GPT-4 Turbo and GPT-4">
28712875 <option value="gpt-4-turbo">gpt-4-turbo</option>
28722876 <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 @@
4747 <option data-type="openai" value="chatgpt-4o-latest">chatgpt-4o-latest</option>
4848 <option data-type="openai" value="o1">o1</option>
4949 <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>
5052 <option data-type="anthropic" value="claude-3-7-sonnet-latest">claude-3-7-sonnet-latest</option>
5153 <option data-type="anthropic" value="claude-3-7-sonnet-20250219">claude-3-7-sonnet-20250219</option>
5254 <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;
99// eslint-disable-next-line prefer-const
1010var localeData;
1111
12+/** @type {Set<string>|null} Array of translations keys if they should be tracked - if not tracked then null */
13+let trackMissingDynamicTranslate = null;
14+
1215export const getCurrentLocale = () => localeFile;
1316
1417/**
@@ -97,6 +100,13 @@ export function t(strings, ...values) {
97100 */
98101export function translate(text, key = null) {
99102 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+ }
100110 return localeData?.[translationKey] || text;
101111}
102112
@@ -153,13 +163,26 @@ function translateElement(element) {
153163 }
154164}
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+ */
171+function isSupportedNonEnglish(locale = null) {
172+ const lang = locale || localeFile;
173+ return lang && lang != 'en' && findLang(lang);
174+}
156175
157176async function getMissingTranslations() {
177+ /** @type {Array<{key: string, language: string, value: string}>} */
158178 const missingData = [];
159179
180+ if (trackMissingDynamicTranslate) {
181+ missingData.push(...Array.from(trackMissingDynamicTranslate).map(key => ({ key, language: localeFile, value: key })));
182+ }
183+
160184 // Determine locales to search for untranslated strings
161185 const isNotSupportedlangsToProcess = !isSupportedNonEnglish() ? [findLang(localeFile)] : langs;
162- const langsToProcess = (isNotSupported || localeFile == 'en') ? langs : [findLang(localeFile)];
163186
164187 for (const language of langsToProcess) {
165188 const localeData = await getLocaleData(language.lang);
@@ -170,7 +193,7 @@ async function getMissingTranslations() {
170193 if (attributeMatch) { // attribute-tagged key
171194 const localizedValue = localeData?.[attributeMatch[2]];
172195 if (!localizedValue) {
173196 missingData.push({ key, language: language.lang, value: String($(this).attr(attributeMatch[1])) });
174197 }
175198 } else { // No attribute tag, treat as 'text'
176199 const localizedValue = localeData?.[key];
@@ -194,17 +217,19 @@ async function getMissingTranslations() {
194217 uniqueMissingData.sort((a, b) => a.language.localeCompare(b.language) || a.key.localeCompare(b.key));
195218
196219 // 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}):`);
205223 console.table(uniqueMissingData);
224+ console.log(`Full map of missing data (${Object.keys(missingDataMap).length}):`);
206225 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+
208233 toastr.success(`Found ${uniqueMissingData.length} missing translations. See browser console for details.`);
209234}
210235
@@ -266,6 +291,31 @@ export async function initLocales() {
266291 attributeFilter: ['data-i18n'],
267292 });
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+ });
270320 registerDebugFunction('applyLocale', 'Apply locale', 'Reapplies the currently selected locale to the page.', applyLocale);
271321}
public/scripts/openai.js+3 -1
@@ -1945,7 +1945,7 @@ async function sendOpenAIRequest(type, messages, signal) {
19451945 }
19461946
19471947 // Remove logit bias, logprobs and stop strings if it's not supported by the model
19481948 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')) {
19491949 delete generate_data.logit_bias;
19501950 delete generate_data.stop;
19511951 delete generate_data.logprobs;
@@ -4997,6 +4997,8 @@ export function isImageInliningSupported() {
49974997 'gpt-4-turbo',
49984998 'gpt-4o',
49994999 'gpt-4o-mini',
5000+ 'gpt-4.5-preview',
5001+ 'gpt-4.5-preview-2025-02-27',
50005002 'o1',
50015003 'o1-2024-12-17',
50025004 'chatgpt-4o-latest',
public/scripts/reasoning.js+2 -1
@@ -108,6 +108,7 @@ export function isHiddenReasoningModel() {
108108
109109 /** @type {{ name: string; func: MatchingFunc; }[]} */
110110 const hiddenReasoningModels = [
111+ { name: 'gpt-4.5', func: FUNCS.startsWith },
111112 { name: 'o1', func: FUNCS.startsWith },
112113 { name: 'o3', func: FUNCS.startsWith },
113114 { name: 'gemini-2.0-flash-thinking-exp', func: FUNCS.startsWith },
@@ -493,7 +494,7 @@ export class ReasoningHandler {
493494 data = null;
494495 }
495496
496497 if (this.type && this.type !== ReasoningType.Model) {
497498 title += ` [${translate(this.type)}]`;
498499 title = title.trim();
499500 }
src/endpoints/tokenizers.js+4 -0
@@ -410,6 +410,10 @@ export function getTokenizerModel(requestModel) {
410410 return 'gpt-4o';
411411 }
412412
413+ if (requestModel.includes('gpt-4.5-preview')) {
414+ return 'gpt-4o';
415+ }
416+
413417 if (requestModel.includes('gpt-4-32k')) {
414418 return 'gpt-4-32k';
415419 }