diff --git a/server/routes/models.js b/server/routes/models.js new file mode 100644 index 0000000..358427c --- /dev/null +++ b/server/routes/models.js @@ -0,0 +1,60 @@ +/** + * /api/models routes + * + * Returns available model lists for each provider. + * Claude models are fetched live from MODELS_API_BASE_URL/v1/models + * (9router) and cached for 5 minutes; other providers use their + * static constants. + */ + +import express from 'express'; +import { getAvailableClaudeModels, invalidateModelCache } from '../services/model-discovery.js'; +import { CODEX_MODELS, CURSOR_MODELS, GEMINI_MODELS } from '../../shared/modelConstants.js'; + +const router = express.Router(); + +/** + * GET /api/models + * Returns all providers' model lists. + * Claude list is dynamically fetched from 9router when configured. + */ +router.get('/', async (_req, res) => { + try { + const claudeModels = await getAvailableClaudeModels(); + + res.json({ + claude: claudeModels, + codex: CODEX_MODELS.OPTIONS, + gemini: GEMINI_MODELS.OPTIONS, + cursor: CURSOR_MODELS.OPTIONS, + }); + } catch (err) { + console.error('[/api/models] Error:', err?.message ?? err); + res.status(500).json({ error: 'Failed to load model list' }); + } +}); + +/** + * GET /api/models/claude + * Returns only Claude model list (with 9router discovery). + */ +router.get('/claude', async (_req, res) => { + try { + const models = await getAvailableClaudeModels(); + res.json({ models }); + } catch (err) { + console.error('[/api/models/claude] Error:', err?.message ?? err); + res.status(500).json({ error: 'Failed to load Claude model list' }); + } +}); + +/** + * POST /api/models/refresh + * Force-invalidates the model cache (admin convenience endpoint). + */ +router.post('/refresh', (_req, res) => { + invalidateModelCache(); + res.json({ success: true, message: 'Model cache invalidated' }); +}); + +export default router;