Add multimodal captioning for 01.ai
| @@ -8,13 +8,12 @@ import { textgen_types, textgenerationwebui_settings } from '../../textgen-setti | ||
| 8 | 8 | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; |
| 9 | 9 | import { SlashCommand } from '../../slash-commands/SlashCommand.js'; |
| 10 | 10 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; |
| 11 | -import { SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js'; | |
| 12 | 11 | import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 13 | 12 | export { MODULE_NAME }; |
| 14 | 13 | |
| 15 | 14 | const MODULE_NAME = 'caption'; |
| 16 | 15 | |
| 17 | 16 | const PROMPT_DEFAULT = 'What’\'s in this image?'; |
| 18 | 17 | const TEMPLATE_DEFAULT = '[{{user}} sends {{char}} a picture that contains: {{caption}}]'; |
| 19 | 18 | |
| 20 | 19 | /** |
| @@ -334,7 +333,7 @@ async function getCaptionForFile(file, prompt, quiet) { | ||
| 334 | 333 | } |
| 335 | 334 | catch (error) { |
| 336 | 335 | const errorMessage = error.message || 'Unknown error'; |
| 337 | 336 | toastr.error(errorMessage, "'Failed to caption image."'); |
| 338 | 337 | console.error(error); |
| 339 | 338 | return ''; |
| 340 | 339 | } |
| @@ -399,6 +398,7 @@ jQuery(async function () { | ||
| 399 | 398 | (modules.includes('caption') && extension_settings.caption.source === 'extras') || |
| 400 | 399 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openai' && (secret_state[SECRET_KEYS.OPENAI] || extension_settings.caption.allow_reverse_proxy)) || |
| 401 | 400 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openrouter' && secret_state[SECRET_KEYS.OPENROUTER]) || |
| 401 | + (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'zerooneai' && secret_state[SECRET_KEYS.ZEROONEAI]) || | |
| 402 | 402 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'google' && (secret_state[SECRET_KEYS.MAKERSUITE] || extension_settings.caption.allow_reverse_proxy)) || |
| 403 | 403 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'anthropic' && (secret_state[SECRET_KEYS.CLAUDE] || extension_settings.caption.allow_reverse_proxy)) || |
| 404 | 404 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'ollama' && textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) || |
| @@ -17,6 +17,7 @@ | ||
| 17 | 17 | <div class="flex1 flex-container flexFlowColumn flexNoGap"> |
| 18 | 18 | <label for="caption_multimodal_api" data-i18n="API">API</label> |
| 19 | 19 | <select id="caption_multimodal_api" class="flex1 text_pole"> |
| 20 | + <option value="zerooneai">01.AI (Yi)</option> | |
| 20 | 21 | <option value="anthropic">Anthropic</option> |
| 21 | 22 | <option value="custom" data-i18n="Custom (OpenAI-compatible)">Custom (OpenAI-compatible)</option> |
| 22 | 23 | <option value="google">Google MakerSuite</option> |
| @@ -32,6 +33,7 @@ | ||
| 32 | 33 | <div class="flex1 flex-container flexFlowColumn flexNoGap"> |
| 33 | 34 | <label for="caption_multimodal_model" data-i18n="Model">Model</label> |
| 34 | 35 | <select id="caption_multimodal_model" class="flex1 text_pole"> |
| 36 | + <option data-type="zerooneai" value="yi-vision">yi-vision</option> | |
| 35 | 37 | <option data-type="openai" value="gpt-4-vision-preview">gpt-4-vision-preview</option> |
| 36 | 38 | <option data-type="openai" value="gpt-4-turbo">gpt-4-turbo</option> |
| 37 | 39 | <option data-type="openai" value="gpt-4o">gpt-4o</option> |
| @@ -136,6 +136,10 @@ function throwIfInvalidModel(useReverseProxy) { | ||
| 136 | 136 | throw new Error('Anthropic (Claude) API key is not set.'); |
| 137 | 137 | } |
| 138 | 138 | |
| 139 | + if (extension_settings.caption.multimodal_api === 'zerooneai' && !secret_state[SECRET_KEYS.ZEROONEAI]) { | |
| 140 | + throw new Error('01.AI API key is not set.'); | |
| 141 | + } | |
| 142 | + | |
| 139 | 143 | if (extension_settings.caption.multimodal_api === 'google' && !secret_state[SECRET_KEYS.MAKERSUITE] && !useReverseProxy) { |
| 140 | 144 | throw new Error('MakerSuite API key is not set.'); |
| 141 | 145 | } |
| @@ -47,6 +47,10 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 47 | 47 | key = readSecret(request.user.directories, SECRET_KEYS.VLLM); |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | + if (request.body.api === 'zerooneai') { | |
| 51 | + key = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI); | |
| 52 | + } | |
| 53 | + | |
| 50 | 54 | if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) { |
| 51 | 55 | console.log('No key found for API', request.body.api); |
| 52 | 56 | return response.sendStatus(400); |
| @@ -100,6 +104,10 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 100 | 104 | apiUrl = `${request.body.server_url}/chat/completions`; |
| 101 | 105 | } |
| 102 | 106 | |
| 107 | + if (request.body.api === 'zerooneai') { | |
| 108 | + apiUrl = 'https://api.01.ai/v1/chat/completions'; | |
| 109 | + } | |
| 110 | + | |
| 103 | 111 | if (request.body.api === 'ooba') { |
| 104 | 112 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; |
| 105 | 113 | const imgMessage = body.messages.pop(); |