NanoGPT: Add as captioning source

285b09ceb75620a24500949e294f1641add2c77e

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

5 files changed, +45 -11Showing whitespace changes
public/scripts/extensions/caption/index.js+4 -2
@@ -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 };
443444
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 }
544545
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 }
549551
550 await addSettings();552 await addSettings();
public/scripts/extensions/caption/settings.html+2 -1
@@ -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>
public/scripts/extensions/shared.js+4 -0
@@ -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}
251255
252/**256/**
src/endpoints/backends/chat-completions.js+27 -8
@@ -1865,9 +1865,9 @@ router.post('/generate', function (request, response) {
1865 }1865 }
1866});1866});
18671867
1868const pollinations = express.Router();1868const multimodalModels = express.Router();
18691869
1870pollinations.post('/models/multimodal', async (_req, res) => {1870multimodalModels.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');
18731873
@@ -1890,11 +1890,7 @@ pollinations.post('/models/multimodal', async (_req, res) => {
1890 }1890 }
1891});1891});
18921892
1893router.use('/pollinations', pollinations);1893multimodalModels.post('/aimlapi', async (_req, res) => {
1894
1895const aimlapi = express.Router();
1896
1897aimlapi.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');
19001896
@@ -1917,4 +1913,27 @@ aimlapi.post('/models/multimodal', async (_req, res) => {
1917 }1913 }
1918});1914});
19191915
1920router.use('/aimlapi', aimlapi);1916multimodalModels.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
1939router.use('/multimodal-models', multimodalModels);
src/endpoints/openai.js+8 -0
@@ -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 }
7979
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 }
163167
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 }