open-agents-ai 0.184.56 → 0.184.58
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/dist/index.js +132 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -62035,6 +62035,38 @@ function getVersion3() {
|
|
|
62035
62035
|
}
|
|
62036
62036
|
return "0.0.0";
|
|
62037
62037
|
}
|
|
62038
|
+
function getEndpointUsage(label) {
|
|
62039
|
+
let u = endpointUsage.get(label);
|
|
62040
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
62041
|
+
if (!u) {
|
|
62042
|
+
u = { requestTimestamps: [], tokensToday: 0, tokenDate: today };
|
|
62043
|
+
endpointUsage.set(label, u);
|
|
62044
|
+
}
|
|
62045
|
+
if (u.tokenDate !== today) {
|
|
62046
|
+
u.tokensToday = 0;
|
|
62047
|
+
u.tokenDate = today;
|
|
62048
|
+
}
|
|
62049
|
+
return u;
|
|
62050
|
+
}
|
|
62051
|
+
function checkEndpointRateLimit(ep, estimatedTokens) {
|
|
62052
|
+
const rpm = ep.limits?.maxRequestsPerMinute ?? 60;
|
|
62053
|
+
const tpd = ep.limits?.maxTokensPerDay ?? 1e5;
|
|
62054
|
+
const usage = getEndpointUsage(ep.label);
|
|
62055
|
+
const now = Date.now();
|
|
62056
|
+
usage.requestTimestamps = usage.requestTimestamps.filter((t) => now - t < 6e4);
|
|
62057
|
+
if (usage.requestTimestamps.length >= rpm) {
|
|
62058
|
+
return `Rate limit exceeded for ${ep.label}: ${rpm} requests/minute. Try again in ${Math.ceil((usage.requestTimestamps[0] + 6e4 - now) / 1e3)}s.`;
|
|
62059
|
+
}
|
|
62060
|
+
if (estimatedTokens && usage.tokensToday + estimatedTokens > tpd) {
|
|
62061
|
+
return `Daily token limit exceeded for ${ep.label}: ${usage.tokensToday}/${tpd} tokens today.`;
|
|
62062
|
+
}
|
|
62063
|
+
return null;
|
|
62064
|
+
}
|
|
62065
|
+
function recordEndpointUsage(label, tokens) {
|
|
62066
|
+
const usage = getEndpointUsage(label);
|
|
62067
|
+
usage.requestTimestamps.push(Date.now());
|
|
62068
|
+
usage.tokensToday += tokens;
|
|
62069
|
+
}
|
|
62038
62070
|
async function refreshEndpointRegistry() {
|
|
62039
62071
|
endpointRegistry.length = 0;
|
|
62040
62072
|
modelRouteMap.clear();
|
|
@@ -62068,6 +62100,10 @@ async function refreshEndpointRegistry() {
|
|
|
62068
62100
|
label,
|
|
62069
62101
|
url: sp.tunnelUrl,
|
|
62070
62102
|
type: "vllm",
|
|
62103
|
+
limits: {
|
|
62104
|
+
maxRequestsPerMinute: sp.limits?.maxRequestsPerMinute ?? 60,
|
|
62105
|
+
maxTokensPerDay: sp.limits?.maxTokensPerDay ?? 1e5
|
|
62106
|
+
},
|
|
62071
62107
|
authKey: sp.authKey
|
|
62072
62108
|
});
|
|
62073
62109
|
}
|
|
@@ -62415,6 +62451,13 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
|
|
|
62415
62451
|
const targetUrl = route?.endpoint.url ?? ollamaUrl;
|
|
62416
62452
|
const targetType = route?.endpoint.type ?? loadConfig().backendType ?? "ollama";
|
|
62417
62453
|
const originalModel = route?.originalId ?? model;
|
|
62454
|
+
if (route?.endpoint) {
|
|
62455
|
+
const limitErr = checkEndpointRateLimit(route.endpoint);
|
|
62456
|
+
if (limitErr) {
|
|
62457
|
+
jsonResponse(res, 429, { error: limitErr });
|
|
62458
|
+
return;
|
|
62459
|
+
}
|
|
62460
|
+
}
|
|
62418
62461
|
const routedBody = { ...requestBody, model: originalModel };
|
|
62419
62462
|
if (targetType === "vllm" || targetType === "openai") {
|
|
62420
62463
|
const payload = JSON.stringify(routedBody);
|
|
@@ -62449,8 +62492,11 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
|
|
|
62449
62492
|
}
|
|
62450
62493
|
const parsed = JSON.parse(result.body);
|
|
62451
62494
|
if (parsed.usage) {
|
|
62495
|
+
const totalTok = (parsed.usage.prompt_tokens ?? 0) + (parsed.usage.completion_tokens ?? 0);
|
|
62452
62496
|
metrics.totalTokensIn += parsed.usage.prompt_tokens ?? 0;
|
|
62453
62497
|
metrics.totalTokensOut += parsed.usage.completion_tokens ?? 0;
|
|
62498
|
+
if (route?.endpoint)
|
|
62499
|
+
recordEndpointUsage(route.endpoint.label, totalTok);
|
|
62454
62500
|
}
|
|
62455
62501
|
jsonResponse(res, 200, parsed);
|
|
62456
62502
|
} catch (err) {
|
|
@@ -63104,6 +63150,19 @@ async function handleRequest(req, res, ollamaUrl, verbose) {
|
|
|
63104
63150
|
await handlePutConfigEndpoint(req, res);
|
|
63105
63151
|
return;
|
|
63106
63152
|
}
|
|
63153
|
+
if (pathname === "/v1/usage" && method === "GET") {
|
|
63154
|
+
const usageData = {};
|
|
63155
|
+
for (const ep of endpointRegistry) {
|
|
63156
|
+
const u = getEndpointUsage(ep.label);
|
|
63157
|
+
usageData[ep.label] = {
|
|
63158
|
+
requestsLastMinute: u.requestTimestamps.filter((t) => Date.now() - t < 6e4).length,
|
|
63159
|
+
tokensToday: u.tokensToday,
|
|
63160
|
+
limits: ep.limits ?? { maxRequestsPerMinute: "unlimited", maxTokensPerDay: "unlimited" }
|
|
63161
|
+
};
|
|
63162
|
+
}
|
|
63163
|
+
jsonResponse(res, 200, { usage: usageData, totalTokensIn: metrics.totalTokensIn, totalTokensOut: metrics.totalTokensOut });
|
|
63164
|
+
return;
|
|
63165
|
+
}
|
|
63107
63166
|
if (pathname === "/v1/commands" && method === "GET") {
|
|
63108
63167
|
handleGetCommands(res);
|
|
63109
63168
|
return;
|
|
@@ -63229,43 +63288,93 @@ function startApiServer(options = {}) {
|
|
|
63229
63288
|
}
|
|
63230
63289
|
});
|
|
63231
63290
|
});
|
|
63291
|
+
let retried = false;
|
|
63232
63292
|
server.on("error", (err) => {
|
|
63233
|
-
if (err.code === "EADDRINUSE") {
|
|
63234
|
-
|
|
63293
|
+
if (err.code === "EADDRINUSE" && !retried) {
|
|
63294
|
+
retried = true;
|
|
63295
|
+
process.stderr.write(` Port ${port} in use \u2014 reclaiming...
|
|
63296
|
+
`);
|
|
63297
|
+
try {
|
|
63298
|
+
const { execSync: es } = __require("node:child_process");
|
|
63299
|
+
const pids = es(`lsof -ti :${port} 2>/dev/null || fuser ${port}/tcp 2>/dev/null || true`, { encoding: "utf8" }).trim().split(/[\n\s]+/).filter(Boolean);
|
|
63300
|
+
for (const pid of pids) {
|
|
63301
|
+
const numPid = parseInt(pid, 10);
|
|
63302
|
+
if (numPid && numPid !== process.pid) {
|
|
63303
|
+
try {
|
|
63304
|
+
process.kill(numPid, "SIGTERM");
|
|
63305
|
+
} catch {
|
|
63306
|
+
}
|
|
63307
|
+
setTimeout(() => {
|
|
63308
|
+
try {
|
|
63309
|
+
process.kill(numPid, "SIGKILL");
|
|
63310
|
+
} catch {
|
|
63311
|
+
}
|
|
63312
|
+
}, 500);
|
|
63313
|
+
}
|
|
63314
|
+
}
|
|
63315
|
+
} catch {
|
|
63316
|
+
}
|
|
63317
|
+
setTimeout(() => {
|
|
63318
|
+
server.listen(port, host);
|
|
63319
|
+
}, 2e3);
|
|
63320
|
+
} else if (err.code === "EADDRINUSE" && retried) {
|
|
63321
|
+
process.stderr.write(` Port ${port} still in use after reclaim attempt \u2014 API server not started.
|
|
63235
63322
|
`);
|
|
63236
63323
|
} else {
|
|
63237
63324
|
process.stderr.write(` API server error: ${err.message}
|
|
63238
63325
|
`);
|
|
63239
63326
|
}
|
|
63240
63327
|
});
|
|
63241
|
-
|
|
63242
|
-
|
|
63243
|
-
|
|
63244
|
-
|
|
63245
|
-
|
|
63328
|
+
endpointRegistry.push({
|
|
63329
|
+
label: (() => {
|
|
63330
|
+
const u = config.backendUrl;
|
|
63331
|
+
if (u.includes("127.0.0.1") || u.includes("localhost"))
|
|
63332
|
+
return "local";
|
|
63333
|
+
try {
|
|
63334
|
+
return new URL(u).hostname.split(".")[0];
|
|
63335
|
+
} catch {
|
|
63336
|
+
return "primary";
|
|
63337
|
+
}
|
|
63338
|
+
})(),
|
|
63339
|
+
url: config.backendUrl,
|
|
63340
|
+
type: config.backendType || "ollama",
|
|
63341
|
+
authKey: config.apiKey
|
|
63342
|
+
});
|
|
63343
|
+
server.listen(port, host, () => {
|
|
63344
|
+
const version = getVersion3();
|
|
63345
|
+
process.stderr.write(`
|
|
63246
63346
|
open-agents API server v${version}
|
|
63247
63347
|
`);
|
|
63248
|
-
|
|
63348
|
+
process.stderr.write(` Listening on http://${host}:${port}
|
|
63249
63349
|
`);
|
|
63250
|
-
|
|
63350
|
+
process.stderr.write(` Primary: ${config.backendUrl} (${config.backendType || "ollama"})
|
|
63251
63351
|
`);
|
|
63252
|
-
|
|
63253
|
-
|
|
63352
|
+
if (process.env["OA_API_KEYS"]) {
|
|
63353
|
+
const keyCount = process.env["OA_API_KEYS"].split(",").length;
|
|
63354
|
+
process.stderr.write(` Auth: ${keyCount} scoped key(s) (read/run/admin)
|
|
63254
63355
|
`);
|
|
63255
|
-
|
|
63256
|
-
|
|
63257
|
-
const keyCount = process.env["OA_API_KEYS"].split(",").length;
|
|
63258
|
-
process.stderr.write(` Auth: ${keyCount} scoped key(s) (read/run/admin)
|
|
63356
|
+
} else if (process.env["OA_API_KEY"]) {
|
|
63357
|
+
process.stderr.write(` Auth: single Bearer token (admin scope)
|
|
63259
63358
|
`);
|
|
63260
|
-
|
|
63261
|
-
|
|
63359
|
+
} else {
|
|
63360
|
+
process.stderr.write(` Auth: disabled (set OA_API_KEY or OA_API_KEYS to enable)
|
|
63262
63361
|
`);
|
|
63263
|
-
|
|
63264
|
-
|
|
63362
|
+
}
|
|
63363
|
+
process.stderr.write(` Discovering sponsors in background...
|
|
63364
|
+
|
|
63265
63365
|
`);
|
|
63266
|
-
|
|
63267
|
-
|
|
63366
|
+
refreshEndpointRegistry().then(() => {
|
|
63367
|
+
if (endpointRegistry.length > 1) {
|
|
63368
|
+
process.stderr.write(` Sponsors: ${endpointRegistry.length - 1} endpoint(s) discovered
|
|
63369
|
+
`);
|
|
63370
|
+
for (const ep of endpointRegistry.slice(1)) {
|
|
63371
|
+
process.stderr.write(` ${ep.label}: ${ep.url} (${ep.type})${ep.authKey ? " [auth]" : ""}
|
|
63372
|
+
`);
|
|
63373
|
+
}
|
|
63374
|
+
process.stderr.write(`
|
|
63268
63375
|
`);
|
|
63376
|
+
}
|
|
63377
|
+
}).catch(() => {
|
|
63269
63378
|
});
|
|
63270
63379
|
});
|
|
63271
63380
|
const shutdown = () => {
|
|
@@ -63296,7 +63405,7 @@ async function apiServeCommand(opts, config) {
|
|
|
63296
63405
|
server.on("close", resolve36);
|
|
63297
63406
|
});
|
|
63298
63407
|
}
|
|
63299
|
-
var endpointRegistry, modelRouteMap, metrics, startedAt, runningProcesses;
|
|
63408
|
+
var endpointRegistry, modelRouteMap, endpointUsage, metrics, startedAt, runningProcesses;
|
|
63300
63409
|
var init_serve = __esm({
|
|
63301
63410
|
"packages/cli/dist/api/serve.js"() {
|
|
63302
63411
|
"use strict";
|
|
@@ -63306,6 +63415,7 @@ var init_serve = __esm({
|
|
|
63306
63415
|
init_profiles();
|
|
63307
63416
|
endpointRegistry = [];
|
|
63308
63417
|
modelRouteMap = /* @__PURE__ */ new Map();
|
|
63418
|
+
endpointUsage = /* @__PURE__ */ new Map();
|
|
63309
63419
|
metrics = {
|
|
63310
63420
|
requests: /* @__PURE__ */ new Map(),
|
|
63311
63421
|
totalTokensIn: 0,
|
package/package.json
CHANGED