neoagent 2.4.2-beta.1 → 2.4.2-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/flutter_app/lib/main_account_settings.dart +131 -1
- package/flutter_app/lib/main_controller.dart +5 -0
- package/flutter_app/lib/main_models.dart +24 -0
- package/flutter_app/lib/src/backend_client.dart +4 -0
- package/landing/index.html +3 -43
- package/package.json +1 -1
- package/server/admin/admin.js +88 -1
- package/server/admin/index.html +25 -5
- package/server/admin/users.js +73 -0
- package/server/db/database.js +17 -0
- package/server/http/static.js +0 -1
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +52123 -52040
- package/server/routes/account.js +21 -0
- package/server/routes/admin.js +40 -1
- package/server/services/ai/engine.js +16 -0
- package/server/services/ai/models.js +11 -2
- package/server/services/ai/providers/ollama.js +4 -1
package/server/routes/account.js
CHANGED
|
@@ -110,6 +110,27 @@ router.get('/', (req, res) => {
|
|
|
110
110
|
}
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
+
router.get('/usage', (req, res) => {
|
|
114
|
+
try {
|
|
115
|
+
const userId = req.session.userId;
|
|
116
|
+
const userLimits = db.prepare('SELECT rate_limit_4h, rate_limit_weekly FROM users WHERE id = ?').get(userId);
|
|
117
|
+
const h4Tokens = db.prepare("SELECT COALESCE(SUM(total_tokens), 0) as t FROM agent_runs WHERE user_id = ? AND created_at > datetime('now', '-4 hours')").get(userId).t;
|
|
118
|
+
const weeklyTokens = db.prepare("SELECT COALESCE(SUM(total_tokens), 0) as t FROM agent_runs WHERE user_id = ? AND created_at > datetime('now', '-7 days')").get(userId).t;
|
|
119
|
+
res.json({
|
|
120
|
+
limits: {
|
|
121
|
+
fourHour: userLimits?.rate_limit_4h || null,
|
|
122
|
+
weekly: userLimits?.rate_limit_weekly || null,
|
|
123
|
+
},
|
|
124
|
+
usage: {
|
|
125
|
+
fourHour: h4Tokens,
|
|
126
|
+
weekly: weeklyTokens,
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
} catch (err) {
|
|
130
|
+
sendRouteError(res, err);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
113
134
|
router.put('/display-name', accountLimiter, (req, res) => {
|
|
114
135
|
try {
|
|
115
136
|
const displayName = normalizeDisplayName(req.body?.displayName);
|
package/server/routes/admin.js
CHANGED
|
@@ -469,7 +469,7 @@ router.get('/api/users', requireAdminAuth, (req, res) => {
|
|
|
469
469
|
try {
|
|
470
470
|
const sql = `
|
|
471
471
|
SELECT u.id, u.username, u.display_name, u.email, u.email_verified_at,
|
|
472
|
-
u.created_at, u.last_login,
|
|
472
|
+
u.created_at, u.last_login, u.rate_limit_4h, u.rate_limit_weekly,
|
|
473
473
|
COALESCE(r.run_count, 0) AS run_count,
|
|
474
474
|
COALESCE(a.storage_bytes,0) AS storage_bytes
|
|
475
475
|
FROM users u
|
|
@@ -618,6 +618,45 @@ router.put('/api/providers', requireAdminAuth, express.json(), (req, res) => {
|
|
|
618
618
|
res.json({ ok: true });
|
|
619
619
|
});
|
|
620
620
|
|
|
621
|
+
// --- Models ---
|
|
622
|
+
|
|
623
|
+
router.get('/api/models', requireAdminAuth, async (req, res) => {
|
|
624
|
+
const { getSupportedModels } = require('../services/ai/models');
|
|
625
|
+
try {
|
|
626
|
+
const models = await getSupportedModels(null, null);
|
|
627
|
+
const enabledModelsStr = process.env.NEOAGENT_ENABLED_MODELS || '';
|
|
628
|
+
const enabledModelsList = enabledModelsStr ? enabledModelsStr.split(',').map(s => s.trim()).filter(s => s.length > 0) : [];
|
|
629
|
+
res.json({ models, enabledModels: enabledModelsList });
|
|
630
|
+
} catch (err) {
|
|
631
|
+
res.status(500).json({ error: String(err.message || err) });
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
router.put('/api/models/enabled', requireAdminAuth, express.json(), (req, res) => {
|
|
636
|
+
const { enabledModels } = req.body || {};
|
|
637
|
+
if (!Array.isArray(enabledModels)) return res.status(400).json({ error: 'enabledModels must be an array' });
|
|
638
|
+
const value = enabledModels.join(',');
|
|
639
|
+
upsertEnvValue(ENV_FILE, 'NEOAGENT_ENABLED_MODELS', value);
|
|
640
|
+
process.env.NEOAGENT_ENABLED_MODELS = value;
|
|
641
|
+
res.json({ ok: true });
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
router.put('/api/users/:id/rate-limits', requireAdminAuth, express.json(), (req, res) => {
|
|
645
|
+
const db = require('../db/database');
|
|
646
|
+
const { id } = req.params;
|
|
647
|
+
const { rate_limit_4h, rate_limit_weekly } = req.body || {};
|
|
648
|
+
try {
|
|
649
|
+
db.prepare('UPDATE users SET rate_limit_4h = ?, rate_limit_weekly = ? WHERE id = ?').run(
|
|
650
|
+
rate_limit_4h ?? null,
|
|
651
|
+
rate_limit_weekly ?? null,
|
|
652
|
+
id
|
|
653
|
+
);
|
|
654
|
+
res.json({ ok: true });
|
|
655
|
+
} catch (err) {
|
|
656
|
+
res.status(500).json({ error: String(err.message || err) });
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
|
|
621
660
|
// --- Static files ---
|
|
622
661
|
|
|
623
662
|
router.use(express.static(ADMIN_DIR));
|
|
@@ -1214,6 +1214,22 @@ class AgentEngine {
|
|
|
1214
1214
|
ensureDefaultAiSettings(userId, agentId);
|
|
1215
1215
|
const aiSettings = getAiSettings(userId, agentId);
|
|
1216
1216
|
|
|
1217
|
+
const userLimits = db.prepare('SELECT rate_limit_4h, rate_limit_weekly FROM users WHERE id = ?').get(userId);
|
|
1218
|
+
if (userLimits) {
|
|
1219
|
+
if (userLimits.rate_limit_4h) {
|
|
1220
|
+
const h4Tokens = db.prepare("SELECT COALESCE(SUM(total_tokens), 0) as t FROM agent_runs WHERE user_id = ? AND created_at > datetime('now', '-4 hours')").get(userId).t;
|
|
1221
|
+
if (h4Tokens >= userLimits.rate_limit_4h) {
|
|
1222
|
+
throw new Error(`Rate limit exceeded: You have used ${h4Tokens} tokens in the last 4 hours (limit: ${userLimits.rate_limit_4h}).`);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
if (userLimits.rate_limit_weekly) {
|
|
1226
|
+
const weeklyTokens = db.prepare("SELECT COALESCE(SUM(total_tokens), 0) as t FROM agent_runs WHERE user_id = ? AND created_at > datetime('now', '-7 days')").get(userId).t;
|
|
1227
|
+
if (weeklyTokens >= userLimits.rate_limit_weekly) {
|
|
1228
|
+
throw new Error(`Rate limit exceeded: You have used ${weeklyTokens} tokens in the last 7 days (limit: ${userLimits.rate_limit_weekly}).`);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1217
1233
|
const runId = options.runId || uuidv4();
|
|
1218
1234
|
const conversationId = options.conversationId;
|
|
1219
1235
|
const app = options.app || this.app;
|
|
@@ -402,7 +402,7 @@ async function getSupportedModels(userId, agentId = null) {
|
|
|
402
402
|
|
|
403
403
|
// Ollama: dynamic list from local server
|
|
404
404
|
const ollama = providerById.get('ollama');
|
|
405
|
-
if (ollama?.
|
|
405
|
+
if (ollama?.available) {
|
|
406
406
|
const dynamicModels = await refreshDynamicModels(ollama.baseUrl);
|
|
407
407
|
for (const model of dynamicModels) {
|
|
408
408
|
if (!staticIds.has(model.id)) {
|
|
@@ -430,6 +430,9 @@ async function getSupportedModels(userId, agentId = null) {
|
|
|
430
430
|
}
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
+
const globalEnabledStr = process.env.NEOAGENT_ENABLED_MODELS || '';
|
|
434
|
+
const globalEnabledSet = globalEnabledStr ? new Set(globalEnabledStr.split(',').map(s => s.trim()).filter(Boolean)) : null;
|
|
435
|
+
|
|
433
436
|
return all.map((model) => {
|
|
434
437
|
const provider = providerById.get(model.provider);
|
|
435
438
|
// Ollama models are always local/free; all others look up the OpenRouter
|
|
@@ -437,10 +440,16 @@ async function getSupportedModels(userId, agentId = null) {
|
|
|
437
440
|
const priceTier = model.provider === 'ollama'
|
|
438
441
|
? 'free'
|
|
439
442
|
: (model.priceTier ?? classifyPriceTier(model.id));
|
|
443
|
+
|
|
444
|
+
let available = provider?.available !== false;
|
|
445
|
+
if (globalEnabledSet && !globalEnabledSet.has(model.id)) {
|
|
446
|
+
available = false;
|
|
447
|
+
}
|
|
448
|
+
|
|
440
449
|
return {
|
|
441
450
|
...model,
|
|
442
451
|
priceTier,
|
|
443
|
-
available
|
|
452
|
+
available,
|
|
444
453
|
providerStatus: provider?.status || 'unknown',
|
|
445
454
|
providerStatusLabel: provider?.statusLabel || 'Unknown'
|
|
446
455
|
};
|
|
@@ -10,7 +10,10 @@ class OllamaProvider extends BaseProvider {
|
|
|
10
10
|
|
|
11
11
|
async listModels() {
|
|
12
12
|
try {
|
|
13
|
-
const
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const timer = setTimeout(() => controller.abort(), 5000);
|
|
15
|
+
const res = await fetch(`${this.baseUrl}/api/tags`, { signal: controller.signal });
|
|
16
|
+
clearTimeout(timer);
|
|
14
17
|
const data = await res.json();
|
|
15
18
|
this.models = (data.models || []).map(m => m.name);
|
|
16
19
|
return this.models;
|