Auto-add OpenRouter captioning models
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | import { ensureImageFormatSupported, getBase64Async, isTrueBoolean, saveBase64AsFile } from '../../utils.js'; |
| 2 | 2 | import { getContext, getApiUrl, doExtrasFetch, extension_settings, modules, renderExtensionTemplateAsync } from '../../extensions.js'; |
| 3 | 3 | import { appendMediaToMessage, callPopup, eventSource, event_types, getRequestHeaders, main_api, saveChatConditional, saveSettingsDebounced, substituteParamsExtended } from '../../../script.js'; |
| 4 | 4 | import { getMessageTimeStamp } from '../../RossAscends-mods.js'; |
| 5 | 5 | import { SECRET_KEYS, secret_state } from '../../secrets.js'; |
| 6 | 6 | import { getMultimodalCaption } from '../shared.js'; |
| @@ -431,14 +431,9 @@ jQuery(async function () { | ||
| 431 | 431 | $('#form_sheld').append(imgForm); |
| 432 | 432 | $('#img_file').on('change', (e) => onSelectImage(e.originalEvent, '', false)); |
| 433 | 433 | } |
| 434 | 434 | async function switchMultimodalBlocks() { |
| 435 | + await addOpenRouterModels(); | |
| 435 | 436 | const isMultimodal = extension_settings.caption.source === 'multimodal'; |
| 436 | - $('#caption_ollama_pull').on('click', (e) => { | |
| 437 | - const presetModel = extension_settings.caption.multimodal_model !== 'ollama_current' ? extension_settings.caption.multimodal_model : ''; | |
| 438 | - e.preventDefault(); | |
| 439 | - $('#ollama_download_model').trigger('click'); | |
| 440 | - $('#dialogue_popup_input').val(presetModel); | |
| 441 | - }); | |
| 442 | 437 | $('#caption_multimodal_block').toggle(isMultimodal); |
| 443 | 438 | $('#caption_prompt_block').toggle(isMultimodal); |
| 444 | 439 | $('#caption_multimodal_api').val(extension_settings.caption.multimodal_api); |
| @@ -448,30 +443,48 @@ jQuery(async function () { | ||
| 448 | 443 | const types = type.split(','); |
| 449 | 444 | $(this).toggle(types.includes(extension_settings.caption.multimodal_api)); |
| 450 | 445 | }); |
| 451 | - $('#caption_multimodal_api').on('change', () => { | |
| 452 | - const api = String($('#caption_multimodal_api').val()); | |
| 453 | - const model = String($(`#caption_multimodal_model option[data-type="${api}"]`).first().val()); | |
| 454 | - extension_settings.caption.multimodal_api = api; | |
| 455 | - extension_settings.caption.multimodal_model = model; | |
| 456 | - saveSettingsDebounced(); | |
| 457 | - switchMultimodalBlocks(); | |
| 458 | - }); | |
| 459 | - $('#caption_multimodal_model').on('change', () => { | |
| 460 | - extension_settings.caption.multimodal_model = String($('#caption_multimodal_model').val()); | |
| 461 | - saveSettingsDebounced(); | |
| 462 | - }); | |
| 463 | 446 | } |
| 464 | 447 | async function addSettings() { |
| 465 | 448 | const html = await renderExtensionTemplateAsync('caption', 'settings', { TEMPLATE_DEFAULT, PROMPT_DEFAULT }); |
| 466 | 449 | $('#caption_container').append(html); |
| 467 | 450 | } |
| 451 | + async function addOpenRouterModels() { | |
| 452 | + const dropdown = document.getElementById('caption_multimodal_model'); | |
| 453 | + if (!(dropdown instanceof HTMLSelectElement)) { | |
| 454 | + return; | |
| 455 | + } | |
| 456 | + if (extension_settings.caption.source !== 'multimodal' || extension_settings.caption.multimodal_api !== 'openrouter') { | |
| 457 | + return; | |
| 458 | + } | |
| 459 | + const options = Array.from(dropdown.options); | |
| 460 | + const response = await fetch('/api/openrouter/models/multimodal', { | |
| 461 | + method: 'POST', | |
| 462 | + headers: getRequestHeaders(), | |
| 463 | + }); | |
| 464 | + if (!response.ok) { | |
| 465 | + return; | |
| 466 | + } | |
| 467 | + const modelIds = await response.json(); | |
| 468 | + if (Array.isArray(modelIds) && modelIds.length > 0) { | |
| 469 | + modelIds.forEach((modelId) => { | |
| 470 | + if (!modelId || typeof modelId !== 'string' || options.some(o => o.value === modelId)) { | |
| 471 | + return; | |
| 472 | + } | |
| 473 | + const option = document.createElement('option'); | |
| 474 | + option.value = modelId; | |
| 475 | + option.textContent = modelId; | |
| 476 | + option.dataset.type = 'openrouter'; | |
| 477 | + dropdown.add(option); | |
| 478 | + }); | |
| 479 | + } | |
| 480 | + } | |
| 468 | 481 | |
| 469 | 482 | await addSettings(); |
| 470 | 483 | addPictureSendForm(); |
| 471 | 484 | addSendPictureButton(); |
| 472 | 485 | setImageIcon(); |
| 473 | 486 | migrateSettings(); |
| 474 | 487 | await switchMultimodalBlocks(); |
| 475 | 488 | |
| 476 | 489 | $('#caption_refine_mode').prop('checked', !!(extension_settings.caption.refine_mode)); |
| 477 | 490 | $('#caption_allow_reverse_proxy').prop('checked', !!(extension_settings.caption.allow_reverse_proxy)); |
| @@ -506,6 +519,24 @@ jQuery(async function () { | ||
| 506 | 519 | extension_settings.caption.auto_mode = !!$('#caption_auto_mode').prop('checked'); |
| 507 | 520 | saveSettingsDebounced(); |
| 508 | 521 | }); |
| 522 | + $('#caption_ollama_pull').on('click', (e) => { | |
| 523 | + const presetModel = extension_settings.caption.multimodal_model !== 'ollama_current' ? extension_settings.caption.multimodal_model : ''; | |
| 524 | + e.preventDefault(); | |
| 525 | + $('#ollama_download_model').trigger('click'); | |
| 526 | + $('#dialogue_popup_input').val(presetModel); | |
| 527 | + }); | |
| 528 | + $('#caption_multimodal_api').on('change', () => { | |
| 529 | + const api = String($('#caption_multimodal_api').val()); | |
| 530 | + const model = String($(`#caption_multimodal_model option[data-type="${api}"]`).first().val()); | |
| 531 | + extension_settings.caption.multimodal_api = api; | |
| 532 | + extension_settings.caption.multimodal_model = model; | |
| 533 | + saveSettingsDebounced(); | |
| 534 | + switchMultimodalBlocks(); | |
| 535 | + }); | |
| 536 | + $('#caption_multimodal_model').on('change', () => { | |
| 537 | + extension_settings.caption.multimodal_model = String($('#caption_multimodal_model').val()); | |
| 538 | + saveSettingsDebounced(); | |
| 539 | + }); | |
| 509 | 540 | |
| 510 | 541 | const onMessageEvent = async (index) => { |
| 511 | 542 | if (!extension_settings.caption.auto_mode) { |
| @@ -538,7 +569,8 @@ jQuery(async function () { | ||
| 538 | 569 | } |
| 539 | 570 | }); |
| 540 | 571 | |
| 541 | 572 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'caption', |
| 573 | + name: 'caption', | |
| 542 | 574 | callback: captionCommandCallback, |
| 543 | 575 | returns: 'caption', |
| 544 | 576 | namedArgumentList: [ |
| @@ -610,6 +610,9 @@ app.use('/api/search', require('./src/endpoints/search').router); | ||
| 610 | 610 | // Ooba/OpenAI text completions |
| 611 | 611 | app.use('/api/backends/text-completions', require('./src/endpoints/backends/text-completions').router); |
| 612 | 612 | |
| 613 | +// OpenRouter | |
| 614 | +app.use('/api/openrouter', require('./src/endpoints/openrouter').router); | |
| 615 | + | |
| 613 | 616 | // KoboldAI |
| 614 | 617 | app.use('/api/backends/kobold', require('./src/endpoints/backends/kobold').router); |
| 615 | 618 | |
| @@ -0,0 +1,32 @@ | ||
| 1 | +const express = require('express'); | |
| 2 | +const { jsonParser } = require('../express-common'); | |
| 3 | + | |
| 4 | +const router = express.Router(); | |
| 5 | +const API_OPENROUTER = 'https://openrouter.ai/api/v1'; | |
| 6 | + | |
| 7 | +router.post('/models/multimodal', jsonParser, async (_req, res) => { | |
| 8 | + try { | |
| 9 | + // The endpoint is available without authentication | |
| 10 | + const response = await fetch(`${API_OPENROUTER}/models`, { | |
| 11 | + method: 'GET', | |
| 12 | + headers: { | |
| 13 | + 'Accept': 'application/json', | |
| 14 | + }, | |
| 15 | + }); | |
| 16 | + | |
| 17 | + if (!response.ok) { | |
| 18 | + return res.json([]); | |
| 19 | + } | |
| 20 | + | |
| 21 | + const data = await response.json(); | |
| 22 | + const models = data?.data || []; | |
| 23 | + const multimodalModels = models.filter(m => m?.architecture?.modality === 'text+image->text').map(m => m.id); | |
| 24 | + | |
| 25 | + return res.json(multimodalModels); | |
| 26 | + } catch (error) { | |
| 27 | + console.error(error); | |
| 28 | + return res.sendStatus(500); | |
| 29 | + } | |
| 30 | +}); | |
| 31 | + | |
| 32 | +module.exports = { router }; | |