Gemini: add media resolution select (#4775)

5accfbc5d8566eae5aaeaa8b9f833d518e9893d0

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

Signed
3 files changed, +27 -8Ignore whitespace
public/index.html+1 -1
@@ -2013,7 +2013,7 @@
2013 <strong data-source="makersuite,vertexai" data-i18n="video_inlining_hint_4">Videos must be less than 20 MB and under 1 minute long.</strong>2013 <strong data-source="makersuite,vertexai" data-i18n="video_inlining_hint_4">Videos must be less than 20 MB and under 1 minute long.</strong>
2014 <strong data-source="makersuite,vertexai" data-i18n="audio_inlining_hint_2">Audio must be less than 20 MB.</strong>2014 <strong data-source="makersuite,vertexai" data-i18n="audio_inlining_hint_2">Audio must be less than 20 MB.</strong>
2015 </div>2015 </div>
2016 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,xai,pollinations,cohere,cometapi,nanogpt,moonshot,aimlapi,openrouter,mistralai,electronhub,azure_openai,zai,siliconflow,chutes">2016 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,xai,pollinations,cohere,cometapi,nanogpt,moonshot,aimlapi,openrouter,mistralai,electronhub,azure_openai,zai,siliconflow,chutes,makersuite,vertexai">
2017 <div class="flex-container oneline-dropdown">2017 <div class="flex-container oneline-dropdown">
2018 <label for="openai_inline_image_quality" data-i18n="Inline Image Quality">2018 <label for="openai_inline_image_quality" data-i18n="Inline Image Quality">
2019 Inline Image Quality2019 Inline Image Quality
public/scripts/openai.js+2 -1
@@ -3244,7 +3244,8 @@ class Message {
3244 }3244 }
32453245
3246 // Note: No compression for videos (unlike images)3246 // Note: No compression for videos (unlike images)
3247 this.content.push({ type: 'video_url', video_url: { 'url': video } });3247 const quality = oai_settings.inline_image_quality || default_settings.inline_image_quality;
3248 this.content.push({ type: 'video_url', video_url: { 'url': video, 'detail': quality } });
32483249
3249 try {3250 try {
3250 // Using Gemini calculation (263 tokens per second)3251 // Using Gemini calculation (263 tokens per second)
src/prompt-converters.js+24 -6
@@ -25,6 +25,12 @@ export const PROMPT_PROCESSING_TYPE = {
25 SINGLE: 'single',25 SINGLE: 'single',
26};26};
2727
28// 'auto' is intentionally unmapped
29const GEMINI_MEDIA_RESOLUTION = {
30 low: 'media_resolution_low',
31 high: 'media_resolution_high',
32};
33
28/**34/**
29 * @typedef {object} PromptNames35 * @typedef {object} PromptNames
30 * @property {string} charName Character name36 * @property {string} charName Character name
@@ -501,17 +507,27 @@ export function convertGooglePrompt(messages, model, useSysPrompt, names) {
501 //create the prompt parts507 //create the prompt parts
502 const parts = [];508 const parts = [];
503 message.content.forEach((part) => {509 message.content.forEach((part) => {
504 const addDataUrlPart = (/** @type {string} */ url, /** @type {string} */ defaultMimeType) => {510 const addDataUrlPart = (/** @type {string} */ url, /** @type {string} */ defaultMimeType, /** @type {string?} */ detail = null) => {
505 if (url && url.startsWith('data:')) {511 if (url && url.startsWith('data:')) {
506 const [header, base64Data] = url.split(',');512 const [header, base64Data] = url.split(',');
507 const mimeType = header.match(/data:([^;]+)/)?.[1] || defaultMimeType;513 const mimeType = header.match(/data:([^;]+)/)?.[1] || defaultMimeType;
514 const mediaResolution = GEMINI_MEDIA_RESOLUTION[detail] || null;
508515
509 parts.push({516 const part = {
510 inlineData: {517 inlineData: {
511 mimeType: mimeType,518 mimeType: mimeType,
512 data: base64Data,519 data: base64Data,
513 },520 },
514 });521 };
522
523 // https://ai.google.dev/gemini-api/docs/gemini-3#media_resolution
524 if (/gemini-3/.test(model) && mediaResolution) {
525 part.mediaResolution = {
526 level: mediaResolution,
527 };
528 }
529
530 parts.push(part);
515 }531 }
516 };532 };
517533
@@ -538,10 +554,12 @@ export function convertGooglePrompt(messages, model, useSysPrompt, names) {
538 });554 });
539 } else if (part.type === 'image_url') {555 } else if (part.type === 'image_url') {
540 const imageUrl = part.image_url?.url;556 const imageUrl = part.image_url?.url;
541 addDataUrlPart(imageUrl, 'image/png');557 const detail = part.image_url?.detail;
558 addDataUrlPart(imageUrl, 'image/png', detail);
542 } else if (part.type === 'video_url') {559 } else if (part.type === 'video_url') {
543 const videoUrl = part.video_url?.url;560 const videoUrl = part.video_url?.url;
544 addDataUrlPart(videoUrl, 'video/mp4');561 const detail = part.video_url?.detail;
562 addDataUrlPart(videoUrl, 'video/mp4', detail);
545 } else if (part.type === 'audio_url') {563 } else if (part.type === 'audio_url') {
546 const audioUrl = part.audio_url?.url;564 const audioUrl = part.audio_url?.url;
547 addDataUrlPart(audioUrl, 'audio/mpeg');565 addDataUrlPart(audioUrl, 'audio/mpeg');
@@ -572,7 +590,7 @@ export function convertGooglePrompt(messages, model, useSysPrompt, names) {
572 contents[contents.length - 1].parts.push(part);590 contents[contents.length - 1].parts.push(part);
573 }591 }
574 }592 }
575 if (part.inlineData || part.functionCall || part.functionResponse || part.thoughtSignature) {593 if (part.inlineData || part.functionCall || part.functionResponse || part.thoughtSignature || part.mediaResolution) {
576 contents[contents.length - 1].parts.push(part);594 contents[contents.length - 1].parts.push(part);
577 }595 }
578 });596 });