@axiom-lattice/gateway 4.10.1 → 4.10.2
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/chunk-GXZFO3VU.mjs +42 -0
- package/dist/chunk-GXZFO3VU.mjs.map +1 -0
- package/dist/index.d.mts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +36 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -7
- package/dist/index.mjs.map +1 -1
- package/dist/{open-FJ3BY3CL.mjs → open-GNBMCHYA.mjs} +4 -39
- package/dist/open-GNBMCHYA.mjs.map +1 -0
- package/dist/{open-files-V3R2BFTV.mjs → open-files-FS5AN6Q6.mjs} +4 -4
- package/dist/openRateLimiter-XEOL3A5G.mjs +8 -0
- package/dist/openRateLimiter-XEOL3A5G.mjs.map +1 -0
- package/package.json +3 -3
- package/dist/open-FJ3BY3CL.mjs.map +0 -1
- /package/dist/{open-files-V3R2BFTV.mjs.map → open-files-FS5AN6Q6.mjs.map} +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/services/open/openRateLimiter.ts
|
|
2
|
+
var OpenRateLimiter = class {
|
|
3
|
+
constructor(options = {}) {
|
|
4
|
+
this.buckets = /* @__PURE__ */ new Map();
|
|
5
|
+
this.defaultPerMinute = options.defaultPerMinute ?? 6e4;
|
|
6
|
+
this.agentPerMinute = options.agentPerMinute ?? 2e3;
|
|
7
|
+
}
|
|
8
|
+
bucketClass(domain) {
|
|
9
|
+
return domain === "agent" ? "agent" : "default";
|
|
10
|
+
}
|
|
11
|
+
limitFor(domain) {
|
|
12
|
+
return domain === "agent" ? this.agentPerMinute : this.defaultPerMinute;
|
|
13
|
+
}
|
|
14
|
+
/** Consume one token; denied calls report how long to wait. */
|
|
15
|
+
check(credentialId, domain) {
|
|
16
|
+
const limit = this.limitFor(domain);
|
|
17
|
+
const key = `${this.bucketClass(domain)}:${credentialId}`;
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
const bucket = this.buckets.get(key) ?? { tokens: limit, updatedAt: now };
|
|
20
|
+
const refillPerMs = limit / 6e4;
|
|
21
|
+
const elapsed = now - bucket.updatedAt;
|
|
22
|
+
bucket.tokens = Math.min(limit, bucket.tokens + elapsed * refillPerMs);
|
|
23
|
+
bucket.updatedAt = now;
|
|
24
|
+
if (bucket.tokens >= 1) {
|
|
25
|
+
bucket.tokens -= 1;
|
|
26
|
+
this.buckets.set(key, bucket);
|
|
27
|
+
return { allowed: true };
|
|
28
|
+
}
|
|
29
|
+
this.buckets.set(key, bucket);
|
|
30
|
+
const deficit = 1 - bucket.tokens;
|
|
31
|
+
return { allowed: false, retryAfterMs: Math.ceil(deficit / refillPerMs) };
|
|
32
|
+
}
|
|
33
|
+
/** Test helper. */
|
|
34
|
+
reset() {
|
|
35
|
+
this.buckets.clear();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
OpenRateLimiter
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=chunk-GXZFO3VU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/open/openRateLimiter.ts"],"sourcesContent":["/**\n * In-memory per-credential rate limiter (token bucket). The agent domain gets\n * a stricter bucket because each call consumes LLM budget.\n *\n * Process-local by design (V1 single-gateway); a distributed limiter is V2.\n */\n\nexport interface RateLimitDecision {\n allowed: boolean;\n retryAfterMs?: number;\n}\n\ninterface Bucket {\n tokens: number;\n updatedAt: number;\n}\n\nexport interface OpenRateLimiterOptions {\n /** Default requests per minute per credential. */\n defaultPerMinute?: number;\n /** Stricter limit for the agent domain. */\n agentPerMinute?: number;\n}\n\nexport class OpenRateLimiter {\n private readonly buckets = new Map<string, Bucket>();\n private readonly defaultPerMinute: number;\n private readonly agentPerMinute: number;\n\n constructor(options: OpenRateLimiterOptions = {}) {\n this.defaultPerMinute = options.defaultPerMinute ?? 60000;\n this.agentPerMinute = options.agentPerMinute ?? 2000;\n }\n\n private bucketClass(domain: string): string {\n return domain === \"agent\" ? \"agent\" : \"default\";\n }\n\n private limitFor(domain: string): number {\n return domain === \"agent\" ? this.agentPerMinute : this.defaultPerMinute;\n }\n\n /** Consume one token; denied calls report how long to wait. */\n check(credentialId: string, domain: string): RateLimitDecision {\n const limit = this.limitFor(domain);\n const key = `${this.bucketClass(domain)}:${credentialId}`;\n const now = Date.now();\n const bucket = this.buckets.get(key) ?? { tokens: limit, updatedAt: now };\n\n const refillPerMs = limit / 60_000;\n const elapsed = now - bucket.updatedAt;\n bucket.tokens = Math.min(limit, bucket.tokens + elapsed * refillPerMs);\n bucket.updatedAt = now;\n\n if (bucket.tokens >= 1) {\n bucket.tokens -= 1;\n this.buckets.set(key, bucket);\n return { allowed: true };\n }\n\n this.buckets.set(key, bucket);\n const deficit = 1 - bucket.tokens;\n return { allowed: false, retryAfterMs: Math.ceil(deficit / refillPerMs) };\n }\n\n /** Test helper. */\n reset(): void {\n this.buckets.clear();\n }\n}\n"],"mappings":";AAwBO,IAAM,kBAAN,MAAsB;AAAA,EAK3B,YAAY,UAAkC,CAAC,GAAG;AAJlD,SAAiB,UAAU,oBAAI,IAAoB;AAKjD,SAAK,mBAAmB,QAAQ,oBAAoB;AACpD,SAAK,iBAAiB,QAAQ,kBAAkB;AAAA,EAClD;AAAA,EAEQ,YAAY,QAAwB;AAC1C,WAAO,WAAW,UAAU,UAAU;AAAA,EACxC;AAAA,EAEQ,SAAS,QAAwB;AACvC,WAAO,WAAW,UAAU,KAAK,iBAAiB,KAAK;AAAA,EACzD;AAAA;AAAA,EAGA,MAAM,cAAsB,QAAmC;AAC7D,UAAM,QAAQ,KAAK,SAAS,MAAM;AAClC,UAAM,MAAM,GAAG,KAAK,YAAY,MAAM,CAAC,IAAI,YAAY;AACvD,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,KAAK,QAAQ,IAAI,GAAG,KAAK,EAAE,QAAQ,OAAO,WAAW,IAAI;AAExE,UAAM,cAAc,QAAQ;AAC5B,UAAM,UAAU,MAAM,OAAO;AAC7B,WAAO,SAAS,KAAK,IAAI,OAAO,OAAO,SAAS,UAAU,WAAW;AACrE,WAAO,YAAY;AAEnB,QAAI,OAAO,UAAU,GAAG;AACtB,aAAO,UAAU;AACjB,WAAK,QAAQ,IAAI,KAAK,MAAM;AAC5B,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB;AAEA,SAAK,QAAQ,IAAI,KAAK,MAAM;AAC5B,UAAM,UAAU,IAAI,OAAO;AAC3B,WAAO,EAAE,SAAS,OAAO,cAAc,KAAK,KAAK,UAAU,WAAW,EAAE;AAAA,EAC1E;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,QAAQ,MAAM;AAAA,EACrB;AACF;","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -175,6 +175,20 @@ declare class MessageRouter {
|
|
|
175
175
|
* thin HTTP shell.
|
|
176
176
|
*/
|
|
177
177
|
|
|
178
|
+
/**
|
|
179
|
+
* A model the deployment registered for execution, as the registry holds it.
|
|
180
|
+
*
|
|
181
|
+
* The config travels with the key because the catalog needs it: a bare key is
|
|
182
|
+
* not something to offer a user. `GET /api/models` used to call
|
|
183
|
+
* `lattice.client.getConfig()` for exactly this — the deployment's display
|
|
184
|
+
* name, and the upstream model it names. Handing back keys alone left catalog
|
|
185
|
+
* entries with nothing to render, so the deployment's `default` alias showed up
|
|
186
|
+
* as a row literally labelled `default`.
|
|
187
|
+
*/
|
|
188
|
+
interface RegisteredModel {
|
|
189
|
+
modelKey: string;
|
|
190
|
+
config: LLMConfig;
|
|
191
|
+
}
|
|
178
192
|
/** Narrow persistence and registry dependencies of the service. */
|
|
179
193
|
interface ModelProviderServiceDeps {
|
|
180
194
|
providerStore: ModelProviderStore;
|
|
@@ -183,7 +197,7 @@ interface ModelProviderServiceDeps {
|
|
|
183
197
|
register(tenantId: string, modelKey: string, config: LLMConfig): void;
|
|
184
198
|
has(tenantId: string, modelKey: string): boolean;
|
|
185
199
|
remove(tenantId: string, modelKey: string): boolean;
|
|
186
|
-
listRegistered(tenantId: string):
|
|
200
|
+
listRegistered(tenantId: string): RegisteredModel[];
|
|
187
201
|
/**
|
|
188
202
|
* Drop the registry's cached "this tenant has no default model" answer.
|
|
189
203
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -175,6 +175,20 @@ declare class MessageRouter {
|
|
|
175
175
|
* thin HTTP shell.
|
|
176
176
|
*/
|
|
177
177
|
|
|
178
|
+
/**
|
|
179
|
+
* A model the deployment registered for execution, as the registry holds it.
|
|
180
|
+
*
|
|
181
|
+
* The config travels with the key because the catalog needs it: a bare key is
|
|
182
|
+
* not something to offer a user. `GET /api/models` used to call
|
|
183
|
+
* `lattice.client.getConfig()` for exactly this — the deployment's display
|
|
184
|
+
* name, and the upstream model it names. Handing back keys alone left catalog
|
|
185
|
+
* entries with nothing to render, so the deployment's `default` alias showed up
|
|
186
|
+
* as a row literally labelled `default`.
|
|
187
|
+
*/
|
|
188
|
+
interface RegisteredModel {
|
|
189
|
+
modelKey: string;
|
|
190
|
+
config: LLMConfig;
|
|
191
|
+
}
|
|
178
192
|
/** Narrow persistence and registry dependencies of the service. */
|
|
179
193
|
interface ModelProviderServiceDeps {
|
|
180
194
|
providerStore: ModelProviderStore;
|
|
@@ -183,7 +197,7 @@ interface ModelProviderServiceDeps {
|
|
|
183
197
|
register(tenantId: string, modelKey: string, config: LLMConfig): void;
|
|
184
198
|
has(tenantId: string, modelKey: string): boolean;
|
|
185
199
|
remove(tenantId: string, modelKey: string): boolean;
|
|
186
|
-
listRegistered(tenantId: string):
|
|
200
|
+
listRegistered(tenantId: string): RegisteredModel[];
|
|
187
201
|
/**
|
|
188
202
|
* Drop the registry's cached "this tenant has no default model" answer.
|
|
189
203
|
*
|
package/dist/index.js
CHANGED
|
@@ -3540,6 +3540,10 @@ var init_openAuditService = __esm({
|
|
|
3540
3540
|
});
|
|
3541
3541
|
|
|
3542
3542
|
// src/services/open/openRateLimiter.ts
|
|
3543
|
+
var openRateLimiter_exports = {};
|
|
3544
|
+
__export(openRateLimiter_exports, {
|
|
3545
|
+
OpenRateLimiter: () => OpenRateLimiter
|
|
3546
|
+
});
|
|
3543
3547
|
var OpenRateLimiter;
|
|
3544
3548
|
var init_openRateLimiter = __esm({
|
|
3545
3549
|
"src/services/open/openRateLimiter.ts"() {
|
|
@@ -3547,8 +3551,8 @@ var init_openRateLimiter = __esm({
|
|
|
3547
3551
|
OpenRateLimiter = class {
|
|
3548
3552
|
constructor(options = {}) {
|
|
3549
3553
|
this.buckets = /* @__PURE__ */ new Map();
|
|
3550
|
-
this.defaultPerMinute = options.defaultPerMinute ??
|
|
3551
|
-
this.agentPerMinute = options.agentPerMinute ??
|
|
3554
|
+
this.defaultPerMinute = options.defaultPerMinute ?? 6e4;
|
|
3555
|
+
this.agentPerMinute = options.agentPerMinute ?? 2e3;
|
|
3552
3556
|
}
|
|
3553
3557
|
bucketClass(domain) {
|
|
3554
3558
|
return domain === "agent" ? "agent" : "default";
|
|
@@ -21846,11 +21850,16 @@ var ModelProviderService = class {
|
|
|
21846
21850
|
)
|
|
21847
21851
|
}));
|
|
21848
21852
|
if (models.length === 0) {
|
|
21849
|
-
for (const modelKey2 of this.deps.registry.listRegistered(
|
|
21853
|
+
for (const { modelKey: modelKey2, config } of this.deps.registry.listRegistered(
|
|
21850
21854
|
PHASE1_REGISTRY_TENANT
|
|
21851
21855
|
)) {
|
|
21852
21856
|
models.push({
|
|
21853
21857
|
modelKey: modelKey2,
|
|
21858
|
+
// Carry the registered config's name, the way `GET /api/models` did.
|
|
21859
|
+
// Without it these rows have nothing to render but the registry key —
|
|
21860
|
+
// which is how the deployment's `default` alias came to be offered as
|
|
21861
|
+
// a model literally called "default".
|
|
21862
|
+
displayName: config.displayName || config.model || modelKey2,
|
|
21854
21863
|
providerId: "",
|
|
21855
21864
|
providerName: "registered",
|
|
21856
21865
|
source: "registered",
|
|
@@ -21914,7 +21923,7 @@ var ModelProviderService = class {
|
|
|
21914
21923
|
const provider = await this.deps.providerStore.getProviderById(tenantId, providerId2);
|
|
21915
21924
|
if (!provider) {
|
|
21916
21925
|
const prefix = `${providerId2}/`;
|
|
21917
|
-
for (const modelKey2 of this.deps.registry.listRegistered(tenantId)) {
|
|
21926
|
+
for (const { modelKey: modelKey2 } of this.deps.registry.listRegistered(tenantId)) {
|
|
21918
21927
|
if (modelKey2.startsWith(prefix)) {
|
|
21919
21928
|
this.deps.registry.remove(tenantId, modelKey2);
|
|
21920
21929
|
}
|
|
@@ -21923,7 +21932,7 @@ var ModelProviderService = class {
|
|
|
21923
21932
|
}
|
|
21924
21933
|
const models = await this.deps.providerStore.listModels(tenantId, providerId2);
|
|
21925
21934
|
const current = new Set(models.map((model) => (0, import_protocols16.toModelKey)(providerId2, model.modelId)));
|
|
21926
|
-
for (const modelKey2 of this.deps.registry.listRegistered(tenantId)) {
|
|
21935
|
+
for (const { modelKey: modelKey2 } of this.deps.registry.listRegistered(tenantId)) {
|
|
21927
21936
|
if (modelKey2.startsWith(`${providerId2}/`) && !current.has(modelKey2)) {
|
|
21928
21937
|
this.deps.registry.remove(tenantId, modelKey2);
|
|
21929
21938
|
}
|
|
@@ -28909,7 +28918,15 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
28909
28918
|
register: (tenantId, modelKey2, config) => import_core57.modelLatticeManager.registerLatticeWithTenant(tenantId, modelKey2, config),
|
|
28910
28919
|
has: (tenantId, modelKey2) => import_core57.modelLatticeManager.hasLatticeWithTenant(tenantId, modelKey2),
|
|
28911
28920
|
remove: (tenantId, modelKey2) => import_core57.modelLatticeManager.removeLatticeWithTenant(tenantId, modelKey2),
|
|
28912
|
-
listRegistered: (tenantId) =>
|
|
28921
|
+
listRegistered: (tenantId) => (
|
|
28922
|
+
// The config comes along so the catalog can name these rows: a bare
|
|
28923
|
+
// registry key is not something to offer a user. Same call the legacy
|
|
28924
|
+
// `GET /api/models` made.
|
|
28925
|
+
import_core57.modelLatticeManager.getAllLatticesByTenant(tenantId).map((lattice) => ({
|
|
28926
|
+
modelKey: lattice.key,
|
|
28927
|
+
config: lattice.client.getConfig()
|
|
28928
|
+
}))
|
|
28929
|
+
),
|
|
28913
28930
|
forgetTenantDefaultMiss: (tenantId) => import_core57.modelLatticeManager.forgetTenantDefaultMiss(tenantId)
|
|
28914
28931
|
},
|
|
28915
28932
|
onChanged: (tenantId, providerId2) => broadcastModelProviderChanged(tenantId, providerId2)
|
|
@@ -33628,6 +33645,7 @@ var start = async (config) => {
|
|
|
33628
33645
|
const { registerOpenAuditRoutes: registerOpenAuditRoutes2 } = await Promise.resolve().then(() => (init_open_audit(), open_audit_exports));
|
|
33629
33646
|
const { registerOpenExposureRoutes: registerOpenExposureRoutes2 } = await Promise.resolve().then(() => (init_open_exposure(), open_exposure_exports));
|
|
33630
33647
|
const { openLattice: openLattice2 } = await Promise.resolve().then(() => (init_openLattice(), openLattice_exports));
|
|
33648
|
+
const { OpenRateLimiter: OpenRateLimiter2 } = await Promise.resolve().then(() => (init_openRateLimiter(), openRateLimiter_exports));
|
|
33631
33649
|
const { createKbProvider: createKbProvider2 } = await Promise.resolve().then(() => (init_kbProvider(), kbProvider_exports));
|
|
33632
33650
|
const { createOpenSandboxProvider: createOpenSandboxProvider2 } = await Promise.resolve().then(() => (init_sandboxProvider(), sandboxProvider_exports));
|
|
33633
33651
|
const { createAgentProvider: createAgentProvider2 } = await Promise.resolve().then(() => (init_agentProvider(), agentProvider_exports));
|
|
@@ -33647,7 +33665,18 @@ var start = async (config) => {
|
|
|
33647
33665
|
})
|
|
33648
33666
|
);
|
|
33649
33667
|
await registerExposedPluginTools2();
|
|
33650
|
-
|
|
33668
|
+
const openRateLimitPerMinute = Number(
|
|
33669
|
+
process.env.OPEN_RATE_LIMIT_PER_MINUTE
|
|
33670
|
+
);
|
|
33671
|
+
const openAgentRateLimitPerMinute = Number(
|
|
33672
|
+
process.env.OPEN_AGENT_RATE_LIMIT_PER_MINUTE
|
|
33673
|
+
);
|
|
33674
|
+
registerOpenRoutes2(app, {
|
|
33675
|
+
rateLimiter: new OpenRateLimiter2({
|
|
33676
|
+
...Number.isFinite(openRateLimitPerMinute) && openRateLimitPerMinute > 0 ? { defaultPerMinute: openRateLimitPerMinute } : {},
|
|
33677
|
+
...Number.isFinite(openAgentRateLimitPerMinute) && openAgentRateLimitPerMinute > 0 ? { agentPerMinute: openAgentRateLimitPerMinute } : {}
|
|
33678
|
+
})
|
|
33679
|
+
});
|
|
33651
33680
|
registerOpenSessionRoutes2(app);
|
|
33652
33681
|
registerOpenCatalogRoutes2(app);
|
|
33653
33682
|
registerOpenFilesRoutes2(app);
|