Initial commit: StudyOS platform
This commit is contained in:
163
server/routes/models.js
Normal file
163
server/routes/models.js
Normal file
@@ -0,0 +1,163 @@
|
||||
const express = require('express');
|
||||
const db = require('../db');
|
||||
const { streamCompletion } = require('../lib/llm');
|
||||
const router = express.Router();
|
||||
|
||||
// GET /api/models — list all
|
||||
router.get('/', (req, res) => {
|
||||
try {
|
||||
const rows = db.prepare('SELECT * FROM models ORDER BY id').all();
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
console.error('[models] list error:', err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/models — create
|
||||
router.post('/', (req, res) => {
|
||||
const { name, api_base, api_key, provider, is_default_main, is_default_fork, is_default_exam } = req.body;
|
||||
if (!name || !api_base || !provider) {
|
||||
return res.status(400).json({ error: 'name, api_base, and provider are required' });
|
||||
}
|
||||
|
||||
try {
|
||||
const info = db.prepare(`
|
||||
INSERT INTO models (name, api_base, api_key, provider, is_default_main, is_default_fork, is_default_exam)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
name,
|
||||
api_base,
|
||||
api_key || '',
|
||||
provider,
|
||||
is_default_main ? 1 : 0,
|
||||
is_default_fork ? 1 : 0,
|
||||
is_default_exam ? 1 : 0
|
||||
);
|
||||
|
||||
// If setting a default flag, unset others for that role
|
||||
const newId = info.lastInsertRowid;
|
||||
if (is_default_main) unsetOtherDefaults(newId, 'is_default_main');
|
||||
if (is_default_fork) unsetOtherDefaults(newId, 'is_default_fork');
|
||||
if (is_default_exam) unsetOtherDefaults(newId, 'is_default_exam');
|
||||
|
||||
const row = db.prepare('SELECT * FROM models WHERE id = ?').get(newId);
|
||||
res.status(201).json(row);
|
||||
} catch (err) {
|
||||
console.error('[models] create error:', err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/models/:id — update
|
||||
router.put('/:id', (req, res) => {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
if (Number.isNaN(id)) {
|
||||
return res.status(400).json({ error: 'Invalid model id' });
|
||||
}
|
||||
|
||||
const { name, api_base, api_key, provider, is_default_main, is_default_fork, is_default_exam } = req.body;
|
||||
|
||||
try {
|
||||
const existing = db.prepare('SELECT * FROM models WHERE id = ?').get(id);
|
||||
if (!existing) {
|
||||
return res.status(404).json({ error: 'Model not found' });
|
||||
}
|
||||
|
||||
db.prepare(`
|
||||
UPDATE models SET
|
||||
name = COALESCE(?, name),
|
||||
api_base = COALESCE(?, api_base),
|
||||
api_key = ?,
|
||||
provider = COALESCE(?, provider),
|
||||
is_default_main = COALESCE(?, is_default_main),
|
||||
is_default_fork = COALESCE(?, is_default_fork),
|
||||
is_default_exam = COALESCE(?, is_default_exam)
|
||||
WHERE id = ?
|
||||
`).run(
|
||||
name ?? null,
|
||||
api_base ?? null,
|
||||
api_key !== undefined ? (api_key === null ? '' : api_key) : null,
|
||||
provider ?? null,
|
||||
is_default_main !== undefined ? (is_default_main ? 1 : 0) : null,
|
||||
is_default_fork !== undefined ? (is_default_fork ? 1 : 0) : null,
|
||||
is_default_exam !== undefined ? (is_default_exam ? 1 : 0) : null,
|
||||
id
|
||||
);
|
||||
|
||||
if (is_default_main) unsetOtherDefaults(id, 'is_default_main');
|
||||
if (is_default_fork) unsetOtherDefaults(id, 'is_default_fork');
|
||||
if (is_default_exam) unsetOtherDefaults(id, 'is_default_exam');
|
||||
|
||||
const row = db.prepare('SELECT * FROM models WHERE id = ?').get(id);
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
console.error('[models] update error:', err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/models/:id
|
||||
router.delete('/:id', (req, res) => {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
if (Number.isNaN(id)) {
|
||||
return res.status(400).json({ error: 'Invalid model id' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Reject if conversations reference this model
|
||||
const convCount = db.prepare('SELECT COUNT(*) as count FROM conversations WHERE model_id = ?').get(id);
|
||||
if (convCount.count > 0) {
|
||||
return res.status(409).json({ error: 'Cannot delete model referenced by conversations' });
|
||||
}
|
||||
|
||||
const info = db.prepare('DELETE FROM models WHERE id = ?').run(id);
|
||||
if (info.changes === 0) {
|
||||
return res.status(404).json({ error: 'Model not found' });
|
||||
}
|
||||
|
||||
res.json({ deleted: true });
|
||||
} catch (err) {
|
||||
console.error('[models] delete error:', err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/models/:id/test — send "di hola" and return latency
|
||||
router.post('/:id/test', async (req, res) => {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
if (Number.isNaN(id)) {
|
||||
return res.status(400).json({ error: 'Invalid model id' });
|
||||
}
|
||||
|
||||
try {
|
||||
const model = db.prepare('SELECT * FROM models WHERE id = ?').get(id);
|
||||
if (!model) {
|
||||
return res.status(404).json({ error: 'Model not found' });
|
||||
}
|
||||
|
||||
const start = Date.now();
|
||||
let fullText = '';
|
||||
|
||||
for await (const chunk of streamCompletion(model, [{ role: 'user', content: 'di hola' }], '')) {
|
||||
if (chunk.error) {
|
||||
return res.status(502).json({ error: chunk.error });
|
||||
}
|
||||
if (chunk.done) {
|
||||
fullText = chunk.fullText;
|
||||
}
|
||||
}
|
||||
|
||||
const latency = Date.now() - start;
|
||||
res.json({ latency_ms: latency, response: fullText.trim() });
|
||||
} catch (err) {
|
||||
console.error('[models] test error:', err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
function unsetOtherDefaults(exceptId, column) {
|
||||
db.prepare(`UPDATE models SET ${column} = 0 WHERE id != ?`).run(exceptId);
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user