NanoGPT: Add as captioning source
| @@ -439,6 +439,7 @@ jQuery(async function () { | ||
| 439 | 439 | 'cohere': SECRET_KEYS.COHERE, |
| 440 | 440 | 'aimlapi': SECRET_KEYS.AIMLAPI, |
| 441 | 441 | 'moonshot': SECRET_KEYS.MOONSHOT, |
| 442 | + 'nanogpt': SECRET_KEYS.NANOGPT, | |
| 442 | 443 | }; |
| 443 | 444 | |
| 444 | 445 | if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) { |
| @@ -543,8 +544,9 @@ jQuery(async function () { | ||
| 543 | 544 | } |
| 544 | 545 | |
| 545 | 546 | await processEndpoint('openrouter', '/api/openrouter/models/multimodal'); |
| 546 | 547 | await processEndpoint('aimlapi', '/api/backends/chat-completions/aimlapi/multimodal-models/multimodalaimlapi'); |
| 547 | 548 | await processEndpoint('pollinations', '/api/backends/chat-completions/pollinations/multimodal-models/multimodalpollinations'); |
| 549 | + await processEndpoint('nanogpt', '/api/backends/chat-completions/multimodal-models/nanogpt'); | |
| 548 | 550 | } |
| 549 | 551 | |
| 550 | 552 | await addSettings(); |
| @@ -28,6 +28,7 @@ | ||
| 28 | 28 | <option value="llamacpp">llama.cpp</option> |
| 29 | 29 | <option value="mistral">MistralAI</option> |
| 30 | 30 | <option value="moonshot">Moonshot AI</option> |
| 31 | + <option value="nanogpt">NanoGPT</option> | |
| 31 | 32 | <option value="ollama">Ollama</option> |
| 32 | 33 | <option value="openai">OpenAI</option> |
| 33 | 34 | <option value="openrouter">OpenRouter</option> |
| @@ -40,7 +41,7 @@ | ||
| 40 | 41 | <div class="flex1 flex-container flexFlowColumn flexNoGap"> |
| 41 | 42 | <label for="caption_multimodal_model" data-i18n="Model">Model</label> |
| 42 | 43 | <select id="caption_multimodal_model" class="flex1 text_pole"> |
| 43 | 44 | <!-- AI/ML API, OpenRouter, Pollinations, NanoGPT are added externally by JavaScript --> |
| 44 | 45 | <option data-type="cohere" value="c4ai-aya-vision-8b">c4ai-aya-vision-8b</option> |
| 45 | 46 | <option data-type="cohere" value="c4ai-aya-vision-32b">c4ai-aya-vision-32b</option> |
| 46 | 47 | <option data-type="cohere" value="command-a-vision-07-2025">command-a-vision-07-2025</option> |
| @@ -247,6 +247,10 @@ function throwIfInvalidModel(useReverseProxy) { | ||
| 247 | 247 | if (multimodalApi === 'moonshot' && !secret_state[SECRET_KEYS.MOONSHOT]) { |
| 248 | 248 | throw new Error('Moonshot AI API key is not set.'); |
| 249 | 249 | } |
| 250 | + | |
| 251 | + if (multimodalApi === 'nanogpt' && !secret_state[SECRET_KEYS.NANOGPT]) { | |
| 252 | + throw new Error('NanoGPT API key is not set.'); | |
| 253 | + } | |
| 250 | 254 | } |
| 251 | 255 | |
| 252 | 256 | /** |
| @@ -1865,9 +1865,9 @@ router.post('/generate', function (request, response) { | ||
| 1865 | 1865 | } |
| 1866 | 1866 | }); |
| 1867 | 1867 | |
| 1868 | 1868 | const pollinationsmultimodalModels = express.Router(); |
| 1869 | 1869 | |
| 1870 | 1870 | pollinationsmultimodalModels.post('/models/multimodalpollinations', async (_req, res) => { |
| 1871 | 1871 | try { |
| 1872 | 1872 | const response = await fetch('https://text.pollinations.ai/models'); |
| 1873 | 1873 | |
| @@ -1890,11 +1890,7 @@ pollinations.post('/models/multimodal', async (_req, res) => { | ||
| 1890 | 1890 | } |
| 1891 | 1891 | }); |
| 1892 | 1892 | |
| 1893 | -router.use('/pollinations', pollinations); | |
| 1893 | +multimodalModels.post('/aimlapi', async (_req, res) => { | |
| 1894 | - | |
| 1895 | -const aimlapi = express.Router(); | |
| 1896 | - | |
| 1897 | -aimlapi.post('/models/multimodal', async (_req, res) => { | |
| 1898 | 1894 | try { |
| 1899 | 1895 | const response = await fetch('https://api.aimlapi.com/v1/models'); |
| 1900 | 1896 | |
| @@ -1917,4 +1913,27 @@ aimlapi.post('/models/multimodal', async (_req, res) => { | ||
| 1917 | 1913 | } |
| 1918 | 1914 | }); |
| 1919 | 1915 | |
| 1920 | -router.use('/aimlapi', aimlapi); | |
| 1916 | +multimodalModels.post('/nanogpt', async (_req, res) => { | |
| 1917 | + try { | |
| 1918 | + const response = await fetch('https://nano-gpt.com/api/v1/models?detailed=true'); | |
| 1919 | + | |
| 1920 | + if (!response.ok) { | |
| 1921 | + return res.json([]); | |
| 1922 | + } | |
| 1923 | + | |
| 1924 | + /** @type {any} */ | |
| 1925 | + const data = await response.json(); | |
| 1926 | + | |
| 1927 | + if (!Array.isArray(data?.data)) { | |
| 1928 | + return res.json([]); | |
| 1929 | + } | |
| 1930 | + | |
| 1931 | + const multimodalModels = data.data.filter(m => m?.capabilities?.vision).map(m => m.id); | |
| 1932 | + return res.json(multimodalModels); | |
| 1933 | + } catch (error) { | |
| 1934 | + console.error(error); | |
| 1935 | + return res.sendStatus(500); | |
| 1936 | + } | |
| 1937 | +}); | |
| 1938 | + | |
| 1939 | +router.use('/multimodal-models', multimodalModels); | |
| @@ -77,6 +77,10 @@ router.post('/caption-image', async (request, response) => { | ||
| 77 | 77 | key = readSecret(request.user.directories, SECRET_KEYS.MOONSHOT); |
| 78 | 78 | } |
| 79 | 79 | |
| 80 | + if (request.body.api === 'nanogpt') { | |
| 81 | + key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT); | |
| 82 | + } | |
| 83 | + | |
| 80 | 84 | const noKeyTypes = ['custom', 'ooba', 'koboldcpp', 'vllm', 'llamacpp', 'pollinations']; |
| 81 | 85 | if (!key && !request.body.reverse_proxy && !noKeyTypes.includes(request.body.api)) { |
| 82 | 86 | console.warn('No key found for API', request.body.api); |
| @@ -161,6 +165,10 @@ router.post('/caption-image', async (request, response) => { | ||
| 161 | 165 | apiUrl = 'https://api.moonshot.ai/v1/chat/completions'; |
| 162 | 166 | } |
| 163 | 167 | |
| 168 | + if (request.body.api === 'nanogpt') { | |
| 169 | + apiUrl = 'https://nano-gpt.com/api/v1/chat/completions'; | |
| 170 | + } | |
| 171 | + | |
| 164 | 172 | if (['koboldcpp', 'vllm', 'llamacpp', 'ooba'].includes(request.body.api)) { |
| 165 | 173 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; |
| 166 | 174 | } |