NanoGPT: Add as captioning source
| @@ -439,6 +439,7 @@ jQuery(async function () { | |||
| 439 | 'cohere': SECRET_KEYS.COHERE, | 439 | 'cohere': SECRET_KEYS.COHERE, |
| 440 | 'aimlapi': SECRET_KEYS.AIMLAPI, | 440 | 'aimlapi': SECRET_KEYS.AIMLAPI, |
| 441 | 'moonshot': SECRET_KEYS.MOONSHOT, | 441 | 'moonshot': SECRET_KEYS.MOONSHOT, |
| 442 | 'nanogpt': SECRET_KEYS.NANOGPT, | ||
| 442 | }; | 443 | }; |
| 443 | 444 | ||
| 444 | if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) { | 445 | if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) { |
| @@ -543,8 +544,9 @@ jQuery(async function () { | |||
| 543 | } | 544 | } |
| 544 | 545 | ||
| 545 | await processEndpoint('openrouter', '/api/openrouter/models/multimodal'); | 546 | await processEndpoint('openrouter', '/api/openrouter/models/multimodal'); |
| 546 | await processEndpoint('aimlapi', '/api/backends/chat-completions/aimlapi/models/multimodal'); | 547 | await processEndpoint('aimlapi', '/api/backends/chat-completions/multimodal-models/aimlapi'); |
| 547 | await processEndpoint('pollinations', '/api/backends/chat-completions/pollinations/models/multimodal'); | 548 | await processEndpoint('pollinations', '/api/backends/chat-completions/multimodal-models/pollinations'); |
| 549 | await processEndpoint('nanogpt', '/api/backends/chat-completions/multimodal-models/nanogpt'); | ||
| 548 | } | 550 | } |
| 549 | 551 | ||
| 550 | await addSettings(); | 552 | await addSettings(); |
| @@ -28,6 +28,7 @@ | |||
| 28 | <option value="llamacpp">llama.cpp</option> | 28 | <option value="llamacpp">llama.cpp</option> |
| 29 | <option value="mistral">MistralAI</option> | 29 | <option value="mistral">MistralAI</option> |
| 30 | <option value="moonshot">Moonshot AI</option> | 30 | <option value="moonshot">Moonshot AI</option> |
| 31 | <option value="nanogpt">NanoGPT</option> | ||
| 31 | <option value="ollama">Ollama</option> | 32 | <option value="ollama">Ollama</option> |
| 32 | <option value="openai">OpenAI</option> | 33 | <option value="openai">OpenAI</option> |
| 33 | <option value="openrouter">OpenRouter</option> | 34 | <option value="openrouter">OpenRouter</option> |
| @@ -40,7 +41,7 @@ | |||
| 40 | <div class="flex1 flex-container flexFlowColumn flexNoGap"> | 41 | <div class="flex1 flex-container flexFlowColumn flexNoGap"> |
| 41 | <label for="caption_multimodal_model" data-i18n="Model">Model</label> | 42 | <label for="caption_multimodal_model" data-i18n="Model">Model</label> |
| 42 | <select id="caption_multimodal_model" class="flex1 text_pole"> | 43 | <select id="caption_multimodal_model" class="flex1 text_pole"> |
| 43 | <!-- AI/ML API, OpenRouter, Pollinations are added externally by JavaScript --> | 44 | <!-- AI/ML API, OpenRouter, Pollinations, NanoGPT are added externally by JavaScript --> |
| 44 | <option data-type="cohere" value="c4ai-aya-vision-8b">c4ai-aya-vision-8b</option> | 45 | <option data-type="cohere" value="c4ai-aya-vision-8b">c4ai-aya-vision-8b</option> |
| 45 | <option data-type="cohere" value="c4ai-aya-vision-32b">c4ai-aya-vision-32b</option> | 46 | <option data-type="cohere" value="c4ai-aya-vision-32b">c4ai-aya-vision-32b</option> |
| 46 | <option data-type="cohere" value="command-a-vision-07-2025">command-a-vision-07-2025</option> | 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 | if (multimodalApi === 'moonshot' && !secret_state[SECRET_KEYS.MOONSHOT]) { | 247 | if (multimodalApi === 'moonshot' && !secret_state[SECRET_KEYS.MOONSHOT]) { |
| 248 | throw new Error('Moonshot AI API key is not set.'); | 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 | const pollinations = express.Router(); | 1868 | const multimodalModels = express.Router(); |
| 1869 | 1869 | ||
| 1870 | pollinations.post('/models/multimodal', async (_req, res) => { | 1870 | multimodalModels.post('/pollinations', async (_req, res) => { |
| 1871 | try { | 1871 | try { |
| 1872 | const response = await fetch('https://text.pollinations.ai/models'); | 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 | try { | 1894 | try { |
| 1899 | const response = await fetch('https://api.aimlapi.com/v1/models'); | 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 | key = readSecret(request.user.directories, SECRET_KEYS.MOONSHOT); | 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 | const noKeyTypes = ['custom', 'ooba', 'koboldcpp', 'vllm', 'llamacpp', 'pollinations']; | 84 | const noKeyTypes = ['custom', 'ooba', 'koboldcpp', 'vllm', 'llamacpp', 'pollinations']; |
| 81 | if (!key && !request.body.reverse_proxy && !noKeyTypes.includes(request.body.api)) { | 85 | if (!key && !request.body.reverse_proxy && !noKeyTypes.includes(request.body.api)) { |
| 82 | console.warn('No key found for API', request.body.api); | 86 | console.warn('No key found for API', request.body.api); |
| @@ -161,6 +165,10 @@ router.post('/caption-image', async (request, response) => { | |||
| 161 | apiUrl = 'https://api.moonshot.ai/v1/chat/completions'; | 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 | if (['koboldcpp', 'vllm', 'llamacpp', 'ooba'].includes(request.body.api)) { | 172 | if (['koboldcpp', 'vllm', 'llamacpp', 'ooba'].includes(request.body.api)) { |
| 165 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; | 173 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; |
| 166 | } | 174 | } |