open-agents-ai 0.184.2 → 0.184.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/dist/index.js +85 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35580,6 +35580,14 @@ var init_expose = __esm({
|
|
|
35580
35580
|
/** Debounce tracking for tunnel restarts */
|
|
35581
35581
|
_lastRestartAttempt = 0;
|
|
35582
35582
|
_consecutiveFailures = 0;
|
|
35583
|
+
// ── Sponsor rate limiting ──
|
|
35584
|
+
/** Per-IP sliding window: IP → array of request timestamps */
|
|
35585
|
+
_rateLimitWindows = /* @__PURE__ */ new Map();
|
|
35586
|
+
/** Global token counter for daily budget */
|
|
35587
|
+
_dailyTokensUsed = 0;
|
|
35588
|
+
_dailyTokensResetAt = 0;
|
|
35589
|
+
/** Sponsor rate limits (set via setSponsorLimits) */
|
|
35590
|
+
_sponsorLimits = null;
|
|
35583
35591
|
_authKey;
|
|
35584
35592
|
_targetUrl;
|
|
35585
35593
|
_kind;
|
|
@@ -35614,6 +35622,48 @@ var init_expose = __esm({
|
|
|
35614
35622
|
get isActive() {
|
|
35615
35623
|
return this._stats.status === "active";
|
|
35616
35624
|
}
|
|
35625
|
+
/** Set sponsor rate limits — enables rate limiting middleware in the proxy */
|
|
35626
|
+
setSponsorLimits(limits) {
|
|
35627
|
+
this._sponsorLimits = limits;
|
|
35628
|
+
}
|
|
35629
|
+
/** Check rate limits for a request. Returns null if OK, or error message string if blocked. */
|
|
35630
|
+
checkRateLimit(userIp, model) {
|
|
35631
|
+
if (!this._sponsorLimits)
|
|
35632
|
+
return null;
|
|
35633
|
+
const lim = this._sponsorLimits;
|
|
35634
|
+
if (lim.allowedModels !== "all" && model && !lim.allowedModels.includes(model)) {
|
|
35635
|
+
return `Model '${model}' is not available on this sponsored endpoint. Available: ${lim.allowedModels.join(", ")}`;
|
|
35636
|
+
}
|
|
35637
|
+
if (this._stats.activeConnections >= lim.maxConcurrent) {
|
|
35638
|
+
return `Too many concurrent requests (${this._stats.activeConnections}/${lim.maxConcurrent}). Try again shortly.`;
|
|
35639
|
+
}
|
|
35640
|
+
const now = Date.now();
|
|
35641
|
+
const windowMs = 6e4;
|
|
35642
|
+
let window = this._rateLimitWindows.get(userIp);
|
|
35643
|
+
if (!window) {
|
|
35644
|
+
window = [];
|
|
35645
|
+
this._rateLimitWindows.set(userIp, window);
|
|
35646
|
+
}
|
|
35647
|
+
while (window.length > 0 && window[0] < now - windowMs)
|
|
35648
|
+
window.shift();
|
|
35649
|
+
if (window.length >= lim.maxRequestsPerMinute) {
|
|
35650
|
+
const retryAfterMs = window[0] + windowMs - now;
|
|
35651
|
+
return `Rate limited (${lim.maxRequestsPerMinute} req/min). Retry in ${Math.ceil(retryAfterMs / 1e3)}s.`;
|
|
35652
|
+
}
|
|
35653
|
+
window.push(now);
|
|
35654
|
+
if (this._dailyTokensResetAt < now) {
|
|
35655
|
+
this._dailyTokensUsed = 0;
|
|
35656
|
+
this._dailyTokensResetAt = now + 864e5;
|
|
35657
|
+
}
|
|
35658
|
+
if (this._dailyTokensUsed >= lim.maxTokensPerDay) {
|
|
35659
|
+
return `Daily token budget exhausted (${fmtTokens(lim.maxTokensPerDay)}). Resets in ${Math.ceil((this._dailyTokensResetAt - now) / 36e5)}h.`;
|
|
35660
|
+
}
|
|
35661
|
+
return null;
|
|
35662
|
+
}
|
|
35663
|
+
/** Track token usage from a completed response */
|
|
35664
|
+
trackTokenUsage(tokensIn, tokensOut) {
|
|
35665
|
+
this._dailyTokensUsed += tokensIn + tokensOut;
|
|
35666
|
+
}
|
|
35617
35667
|
constructor(options) {
|
|
35618
35668
|
super();
|
|
35619
35669
|
this.options = options;
|
|
@@ -35833,6 +35883,15 @@ var init_expose = __esm({
|
|
|
35833
35883
|
user.activeRequests++;
|
|
35834
35884
|
user.lastSeen = Date.now();
|
|
35835
35885
|
this.emitStats();
|
|
35886
|
+
const preRateLimitCheck = this.checkRateLimit(userIp, "");
|
|
35887
|
+
if (preRateLimitCheck) {
|
|
35888
|
+
this._stats.activeConnections--;
|
|
35889
|
+
user.activeRequests--;
|
|
35890
|
+
this._stats.errors++;
|
|
35891
|
+
res.writeHead(429, { "Content-Type": "application/json", "Retry-After": "60" });
|
|
35892
|
+
res.end(JSON.stringify({ error: preRateLimitCheck }));
|
|
35893
|
+
return;
|
|
35894
|
+
}
|
|
35836
35895
|
url.searchParams.delete("key");
|
|
35837
35896
|
const forwardPath = url.pathname + url.search;
|
|
35838
35897
|
if (!this._fullAccess) {
|
|
@@ -35873,6 +35932,17 @@ var init_expose = __esm({
|
|
|
35873
35932
|
} catch {
|
|
35874
35933
|
}
|
|
35875
35934
|
}
|
|
35935
|
+
if (requestModel && this._sponsorLimits) {
|
|
35936
|
+
const modelCheck = this.checkRateLimit(userIp, requestModel);
|
|
35937
|
+
if (modelCheck) {
|
|
35938
|
+
this._stats.activeConnections--;
|
|
35939
|
+
user.activeRequests--;
|
|
35940
|
+
this._stats.errors++;
|
|
35941
|
+
res.writeHead(429, { "Content-Type": "application/json", "Retry-After": "60" });
|
|
35942
|
+
res.end(JSON.stringify({ error: modelCheck }));
|
|
35943
|
+
return;
|
|
35944
|
+
}
|
|
35945
|
+
}
|
|
35876
35946
|
const forwardHeaders = cleanForwardHeaders(req.headers, target.host);
|
|
35877
35947
|
if (body.length > 0) {
|
|
35878
35948
|
forwardHeaders["content-length"] = String(body.length);
|
|
@@ -35910,6 +35980,7 @@ var init_expose = __esm({
|
|
|
35910
35980
|
const tOut2 = parseInt(evalCount?.[1] ?? "0", 10);
|
|
35911
35981
|
this._stats.totalTokensIn += tIn2;
|
|
35912
35982
|
this._stats.totalTokensOut += tOut2;
|
|
35983
|
+
this.trackTokenUsage(tIn2, tOut2);
|
|
35913
35984
|
user.tokensIn += tIn2;
|
|
35914
35985
|
user.tokensOut += tOut2;
|
|
35915
35986
|
if (requestModel) {
|
|
@@ -35927,6 +35998,7 @@ var init_expose = __esm({
|
|
|
35927
35998
|
const tOut2 = parseInt(completionTokens?.[1] ?? "0", 10);
|
|
35928
35999
|
this._stats.totalTokensIn += tIn2;
|
|
35929
36000
|
this._stats.totalTokensOut += tOut2;
|
|
36001
|
+
this.trackTokenUsage(tIn2, tOut2);
|
|
35930
36002
|
user.tokensIn += tIn2;
|
|
35931
36003
|
user.tokensOut += tOut2;
|
|
35932
36004
|
if (requestModel) {
|
|
@@ -47100,8 +47172,14 @@ async function handleSlashCommand(input, ctx) {
|
|
|
47100
47172
|
if (config.transport.cloudflared && ctx.exposeStart) {
|
|
47101
47173
|
try {
|
|
47102
47174
|
const url = await ctx.exposeStart("ollama", void 0, "tunnel");
|
|
47103
|
-
if (url)
|
|
47175
|
+
if (url) {
|
|
47104
47176
|
renderInfo(`Tunnel: ${url}`);
|
|
47177
|
+
const gateway = ctx.getExposeGateway?.();
|
|
47178
|
+
if (gateway && "setSponsorLimits" in gateway) {
|
|
47179
|
+
gateway.setSponsorLimits(config.rateLimits);
|
|
47180
|
+
renderInfo(`Rate limits active: ${config.rateLimits.maxRequestsPerMinute} req/min, ${config.rateLimits.maxTokensPerDay.toLocaleString()} tokens/day`);
|
|
47181
|
+
}
|
|
47182
|
+
}
|
|
47105
47183
|
} catch (err) {
|
|
47106
47184
|
renderError(`Tunnel start failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
47107
47185
|
}
|
|
@@ -47109,8 +47187,13 @@ async function handleSlashCommand(input, ctx) {
|
|
|
47109
47187
|
if (config.transport.libp2p && ctx.exposeStart) {
|
|
47110
47188
|
try {
|
|
47111
47189
|
const url = await ctx.exposeStart("ollama", void 0, "libp2p");
|
|
47112
|
-
if (url)
|
|
47190
|
+
if (url) {
|
|
47113
47191
|
renderInfo(`P2P: ${url}`);
|
|
47192
|
+
const gateway = ctx.getExposeGateway?.();
|
|
47193
|
+
if (gateway && "setSponsorLimits" in gateway) {
|
|
47194
|
+
gateway.setSponsorLimits(config.rateLimits);
|
|
47195
|
+
}
|
|
47196
|
+
}
|
|
47114
47197
|
} catch (err) {
|
|
47115
47198
|
renderError(`P2P start failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
47116
47199
|
}
|
package/package.json
CHANGED