neoagent 2.4.2-beta.2 → 2.4.2-beta.4
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 +57 -16
- package/flutter_app/lib/main_models.dart +6 -0
- package/landing/index.html +1 -1
- package/package.json +1 -1
- package/server/admin/admin.js +80 -21
- package/server/admin/index.html +27 -0
- package/server/admin/users.js +94 -37
- package/server/public/.last_build_id +1 -1
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +4164 -4141
- package/server/routes/account.js +11 -2
- package/server/routes/admin.js +33 -0
- package/server/services/ai/engine.js +13 -11
- package/server/services/ai/models.js +7 -1
- package/server/services/ai/providers/ollama.js +4 -1
package/server/routes/account.js
CHANGED
|
@@ -116,10 +116,19 @@ router.get('/usage', (req, res) => {
|
|
|
116
116
|
const userLimits = db.prepare('SELECT rate_limit_4h, rate_limit_weekly FROM users WHERE id = ?').get(userId);
|
|
117
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
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
|
+
|
|
120
|
+
const globalLimit4h = process.env.NEOAGENT_RATE_LIMIT_4H ? parseInt(process.env.NEOAGENT_RATE_LIMIT_4H, 10) : null;
|
|
121
|
+
const globalLimitWeekly = process.env.NEOAGENT_RATE_LIMIT_WEEKLY ? parseInt(process.env.NEOAGENT_RATE_LIMIT_WEEKLY, 10) : null;
|
|
122
|
+
|
|
123
|
+
const custom4h = userLimits?.rate_limit_4h ?? null;
|
|
124
|
+
const customWeekly = userLimits?.rate_limit_weekly ?? null;
|
|
125
|
+
|
|
119
126
|
res.json({
|
|
120
127
|
limits: {
|
|
121
|
-
fourHour:
|
|
122
|
-
weekly:
|
|
128
|
+
fourHour: custom4h ?? globalLimit4h,
|
|
129
|
+
weekly: customWeekly ?? globalLimitWeekly,
|
|
130
|
+
fourHourIsCustom: custom4h !== null,
|
|
131
|
+
weeklyIsCustom: customWeekly !== null,
|
|
123
132
|
},
|
|
124
133
|
usage: {
|
|
125
134
|
fourHour: h4Tokens,
|
package/server/routes/admin.js
CHANGED
|
@@ -618,6 +618,27 @@ router.put('/api/providers', requireAdminAuth, express.json(), (req, res) => {
|
|
|
618
618
|
res.json({ ok: true });
|
|
619
619
|
});
|
|
620
620
|
|
|
621
|
+
// --- Global default rate limits ---
|
|
622
|
+
|
|
623
|
+
router.get('/api/config/rate-limits', requireAdminAuth, (req, res) => {
|
|
624
|
+
res.json({
|
|
625
|
+
rate_limit_4h: process.env.NEOAGENT_RATE_LIMIT_4H ? parseInt(process.env.NEOAGENT_RATE_LIMIT_4H, 10) : null,
|
|
626
|
+
rate_limit_weekly: process.env.NEOAGENT_RATE_LIMIT_WEEKLY ? parseInt(process.env.NEOAGENT_RATE_LIMIT_WEEKLY, 10) : null,
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
router.put('/api/config/rate-limits', requireAdminAuth, express.json(), (req, res) => {
|
|
631
|
+
const { rate_limit_4h, rate_limit_weekly } = req.body || {};
|
|
632
|
+
const parse = (v) => (v !== null && v !== undefined && v !== '' ? parseInt(v, 10) : null);
|
|
633
|
+
const v4h = parse(rate_limit_4h);
|
|
634
|
+
const vWeekly = parse(rate_limit_weekly);
|
|
635
|
+
upsertEnvValue(ENV_FILE, 'NEOAGENT_RATE_LIMIT_4H', v4h !== null ? String(v4h) : '');
|
|
636
|
+
upsertEnvValue(ENV_FILE, 'NEOAGENT_RATE_LIMIT_WEEKLY', vWeekly !== null ? String(vWeekly) : '');
|
|
637
|
+
process.env.NEOAGENT_RATE_LIMIT_4H = v4h !== null ? String(v4h) : '';
|
|
638
|
+
process.env.NEOAGENT_RATE_LIMIT_WEEKLY = vWeekly !== null ? String(vWeekly) : '';
|
|
639
|
+
res.json({ ok: true });
|
|
640
|
+
});
|
|
641
|
+
|
|
621
642
|
// --- Models ---
|
|
622
643
|
|
|
623
644
|
router.get('/api/models', requireAdminAuth, async (req, res) => {
|
|
@@ -641,6 +662,18 @@ router.put('/api/models/enabled', requireAdminAuth, express.json(), (req, res) =
|
|
|
641
662
|
res.json({ ok: true });
|
|
642
663
|
});
|
|
643
664
|
|
|
665
|
+
router.get('/api/users/:id/rate-limits', requireAdminAuth, (req, res) => {
|
|
666
|
+
const db = require('../db/database');
|
|
667
|
+
const { id } = req.params;
|
|
668
|
+
try {
|
|
669
|
+
const row = db.prepare('SELECT rate_limit_4h, rate_limit_weekly FROM users WHERE id = ?').get(id);
|
|
670
|
+
if (!row) return res.status(404).json({ error: 'User not found' });
|
|
671
|
+
res.json({ limits: row });
|
|
672
|
+
} catch (err) {
|
|
673
|
+
res.status(500).json({ error: String(err.message || err) });
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
|
|
644
677
|
router.put('/api/users/:id/rate-limits', requireAdminAuth, express.json(), (req, res) => {
|
|
645
678
|
const db = require('../db/database');
|
|
646
679
|
const { id } = req.params;
|
|
@@ -1215,18 +1215,20 @@ class AgentEngine {
|
|
|
1215
1215
|
const aiSettings = getAiSettings(userId, agentId);
|
|
1216
1216
|
|
|
1217
1217
|
const userLimits = db.prepare('SELECT rate_limit_4h, rate_limit_weekly FROM users WHERE id = ?').get(userId);
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1218
|
+
const globalLimit4h = process.env.NEOAGENT_RATE_LIMIT_4H ? parseInt(process.env.NEOAGENT_RATE_LIMIT_4H, 10) : null;
|
|
1219
|
+
const globalLimitWeekly = process.env.NEOAGENT_RATE_LIMIT_WEEKLY ? parseInt(process.env.NEOAGENT_RATE_LIMIT_WEEKLY, 10) : null;
|
|
1220
|
+
const effective4h = userLimits?.rate_limit_4h ?? globalLimit4h;
|
|
1221
|
+
const effectiveWeekly = userLimits?.rate_limit_weekly ?? globalLimitWeekly;
|
|
1222
|
+
if (effective4h) {
|
|
1223
|
+
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;
|
|
1224
|
+
if (h4Tokens >= effective4h) {
|
|
1225
|
+
throw new Error(`Rate limit exceeded: You have used ${h4Tokens} tokens in the last 4 hours (limit: ${effective4h}).`);
|
|
1224
1226
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
}
|
|
1227
|
+
}
|
|
1228
|
+
if (effectiveWeekly) {
|
|
1229
|
+
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;
|
|
1230
|
+
if (weeklyTokens >= effectiveWeekly) {
|
|
1231
|
+
throw new Error(`Rate limit exceeded: You have used ${weeklyTokens} tokens in the last 7 days (limit: ${effectiveWeekly}).`);
|
|
1230
1232
|
}
|
|
1231
1233
|
}
|
|
1232
1234
|
|
|
@@ -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)) {
|
|
@@ -446,9 +446,15 @@ async function getSupportedModels(userId, agentId = null) {
|
|
|
446
446
|
available = false;
|
|
447
447
|
}
|
|
448
448
|
|
|
449
|
+
const bareId = model.id.includes('/') ? model.id.slice(model.id.indexOf('/') + 1) : null;
|
|
450
|
+
const inputCostPerM = model.provider === 'ollama'
|
|
451
|
+
? 0
|
|
452
|
+
: (openrouterPricingCache.get(model.id) ?? (bareId ? openrouterPricingCache.get(bareId) : undefined) ?? null);
|
|
453
|
+
|
|
449
454
|
return {
|
|
450
455
|
...model,
|
|
451
456
|
priceTier,
|
|
457
|
+
inputCostPerM,
|
|
452
458
|
available,
|
|
453
459
|
providerStatus: provider?.status || 'unknown',
|
|
454
460
|
providerStatusLabel: provider?.statusLabel || 'Unknown'
|
|
@@ -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;
|