llm-relay 0.35.0 → 0.37.0
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/LICENSE +21 -0
- package/README.md +12 -0
- package/THIRD_PARTY_NOTICES.md +72 -0
- package/config.example.json +8 -2
- package/dist/accounting-store-io.d.ts +124 -0
- package/dist/accounting-store-io.js +1105 -0
- package/dist/accounting-store-io.js.map +1 -0
- package/dist/accounting-store-schema.d.ts +299 -0
- package/dist/accounting-store-schema.js +1021 -0
- package/dist/accounting-store-schema.js.map +1 -0
- package/dist/accounting-store.d.ts +73 -0
- package/dist/accounting-store.js +1523 -0
- package/dist/accounting-store.js.map +1 -0
- package/dist/accounting.d.ts +177 -0
- package/dist/accounting.js +429 -0
- package/dist/accounting.js.map +1 -0
- package/dist/anthropic.d.ts +2 -2
- package/dist/anthropic.js +22 -5
- package/dist/anthropic.js.map +1 -1
- package/dist/authEnv.d.ts +19 -0
- package/dist/authEnv.js +58 -25
- package/dist/authEnv.js.map +1 -1
- package/dist/backend.d.ts +32 -3
- package/dist/backend.js +324 -41
- package/dist/backend.js.map +1 -1
- package/dist/candidates.d.ts +28 -3
- package/dist/candidates.js +86 -13
- package/dist/candidates.js.map +1 -1
- package/dist/catalog.d.ts +1 -0
- package/dist/catalog.js +36 -12
- package/dist/catalog.js.map +1 -1
- package/dist/circuit-breaker.d.ts +49 -129
- package/dist/circuit-breaker.js +166 -223
- package/dist/circuit-breaker.js.map +1 -1
- package/dist/cli.d.ts +47 -15
- package/dist/cli.js +475 -134
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +23 -1
- package/dist/config.js +189 -19
- package/dist/config.js.map +1 -1
- package/dist/context-limits.js +2 -1
- package/dist/context-limits.js.map +1 -1
- package/dist/control-authorization.js +6 -4
- package/dist/control-authorization.js.map +1 -1
- package/dist/credential-fleet.d.ts +41 -0
- package/dist/credential-fleet.js +78 -0
- package/dist/credential-fleet.js.map +1 -0
- package/dist/credential-id.d.ts +13 -0
- package/dist/credential-id.js +22 -0
- package/dist/credential-id.js.map +1 -0
- package/dist/credential-select.d.ts +86 -0
- package/dist/credential-select.js +303 -0
- package/dist/credential-select.js.map +1 -0
- package/dist/dashboard/.vite/dashboard-bundle-graph.json +32 -0
- package/dist/dashboard/.vite/manifest.json +11 -0
- package/dist/dashboard/assets/index-BeZxN4Lx.css +1 -0
- package/dist/dashboard/assets/index-RtLgb7Cg.js +74 -0
- package/dist/dashboard/index.html +4 -0
- package/dist/dashboard-auth.d.ts +68 -0
- package/dist/dashboard-auth.js +225 -0
- package/dist/dashboard-auth.js.map +1 -0
- package/dist/dashboard-contract.d.ts +391 -0
- package/dist/dashboard-contract.js +619 -0
- package/dist/dashboard-contract.js.map +1 -0
- package/dist/dashboard-routes.d.ts +91 -0
- package/dist/dashboard-routes.js +688 -0
- package/dist/dashboard-routes.js.map +1 -0
- package/dist/dashboard-snapshot.d.ts +36 -0
- package/dist/dashboard-snapshot.js +1132 -0
- package/dist/dashboard-snapshot.js.map +1 -0
- package/dist/dashboard-static.d.ts +61 -0
- package/dist/dashboard-static.js +326 -0
- package/dist/dashboard-static.js.map +1 -0
- package/dist/dispatch.js +11 -6
- package/dist/dispatch.js.map +1 -1
- package/dist/dynamic-pools.js +6 -4
- package/dist/dynamic-pools.js.map +1 -1
- package/dist/kernel/contracts.d.ts +4 -0
- package/dist/kernel/request-lifecycle.js +4 -1
- package/dist/kernel/request-lifecycle.js.map +1 -1
- package/dist/key-checker.d.ts +3 -1
- package/dist/key-checker.js +35 -11
- package/dist/key-checker.js.map +1 -1
- package/dist/key-import.d.ts +29 -0
- package/dist/key-import.js +160 -0
- package/dist/key-import.js.map +1 -0
- package/dist/log.d.ts +20 -2
- package/dist/log.js +53 -2
- package/dist/log.js.map +1 -1
- package/dist/offload.d.ts +9 -0
- package/dist/offload.js +7 -1
- package/dist/offload.js.map +1 -1
- package/dist/onboarding.d.ts +3 -3
- package/dist/onboarding.js +12 -6
- package/dist/onboarding.js.map +1 -1
- package/dist/openai-dialect.d.ts +42 -0
- package/dist/openai-dialect.js +388 -0
- package/dist/openai-dialect.js.map +1 -0
- package/dist/ping/cadence.d.ts +12 -3
- package/dist/ping/cadence.js +68 -15
- package/dist/ping/cadence.js.map +1 -1
- package/dist/ping/ping.d.ts +7 -3
- package/dist/ping/ping.js +11 -35
- package/dist/ping/ping.js.map +1 -1
- package/dist/ping/probe-cache.d.ts +14 -6
- package/dist/ping/probe-cache.js +20 -5
- package/dist/ping/probe-cache.js.map +1 -1
- package/dist/ping/runtime-telemetry.d.ts +2 -1
- package/dist/ping/runtime-telemetry.js +103 -10
- package/dist/ping/runtime-telemetry.js.map +1 -1
- package/dist/pool-health.d.ts +2 -0
- package/dist/pool-health.js +29 -13
- package/dist/pool-health.js.map +1 -1
- package/dist/quota-observation.d.ts +29 -0
- package/dist/quota-observation.js +249 -0
- package/dist/quota-observation.js.map +1 -0
- package/dist/refusal-interpretation.d.ts +13 -7
- package/dist/refusal-interpretation.js +260 -33
- package/dist/refusal-interpretation.js.map +1 -1
- package/dist/registry.d.ts +13 -2
- package/dist/registry.js +12 -4
- package/dist/registry.js.map +1 -1
- package/dist/repair.d.ts +4 -2
- package/dist/repair.js +15 -1
- package/dist/repair.js.map +1 -1
- package/dist/request-log.d.ts +2 -2
- package/dist/request-log.js +9 -1
- package/dist/request-log.js.map +1 -1
- package/dist/reshaper.d.ts +76 -11
- package/dist/reshaper.js +264 -29
- package/dist/reshaper.js.map +1 -1
- package/dist/resolved-attempt.d.ts +14 -0
- package/dist/resolved-attempt.js +21 -0
- package/dist/resolved-attempt.js.map +1 -0
- package/dist/routes/admin.js +7 -1
- package/dist/routes/admin.js.map +1 -1
- package/dist/secret-file-acl.d.ts +25 -0
- package/dist/secret-file-acl.js +37 -0
- package/dist/secret-file-acl.js.map +1 -0
- package/dist/server.d.ts +28 -3
- package/dist/server.js +1774 -467
- package/dist/server.js.map +1 -1
- package/dist/session-pin.d.ts +35 -0
- package/dist/session-pin.js +136 -0
- package/dist/session-pin.js.map +1 -0
- package/dist/stream-commit.d.ts +34 -0
- package/dist/stream-commit.js +325 -0
- package/dist/stream-commit.js.map +1 -0
- package/dist/target-facts.d.ts +23 -136
- package/dist/target-facts.js +138 -179
- package/dist/target-facts.js.map +1 -1
- package/dist/telemetry.d.ts +1 -25
- package/dist/telemetry.js +52 -45
- package/dist/telemetry.js.map +1 -1
- package/dist/think-tags.d.ts +34 -0
- package/dist/think-tags.js +219 -0
- package/dist/think-tags.js.map +1 -0
- package/dist/usage-observer.d.ts +34 -0
- package/dist/usage-observer.js +322 -0
- package/dist/usage-observer.js.map +1 -0
- package/dist/winenv.js +26 -4
- package/dist/winenv.js.map +1 -1
- package/docs/dashboard-bundle-inventory.json +16 -0
- package/package.json +29 -6
- package/skills/llm-relay/SKILL.md +48 -9
package/dist/pool-health.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { splitSpec } from "./config.js";
|
|
2
|
-
import { buildAuthHeaders
|
|
2
|
+
import { buildAuthHeaders } from "./authEnv.js";
|
|
3
|
+
import { providerCredentialSlots, resolveCredentialSlot, slotAllowsModel } from "./credential-fleet.js";
|
|
3
4
|
/** Verdicts that mean "this member will never answer until someone changes something". */
|
|
4
5
|
export const DEAD_VERDICTS = new Set(["missing", "auth"]);
|
|
5
6
|
/**
|
|
@@ -16,6 +17,17 @@ function authHeaders(p, apiKey) {
|
|
|
16
17
|
...buildAuthHeaders(apiKey, p.authHeader),
|
|
17
18
|
};
|
|
18
19
|
}
|
|
20
|
+
function serviceableSlot(provider, model, p) {
|
|
21
|
+
for (const slot of providerCredentialSlots(provider, p)) {
|
|
22
|
+
if (!slot.enabled || !slotAllowsModel(slot, model))
|
|
23
|
+
continue;
|
|
24
|
+
const resolution = resolveCredentialSlot(slot);
|
|
25
|
+
if (resolution.state !== "declared-missing") {
|
|
26
|
+
return resolution.value === undefined ? { slot } : { slot, key: resolution.value };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
19
31
|
/**
|
|
20
32
|
* Send one real completion to a single pool member.
|
|
21
33
|
*
|
|
@@ -29,12 +41,16 @@ export async function probeMember(pool, spec, cfg, fetchFn = fetch, maxTokens =
|
|
|
29
41
|
if (!p) {
|
|
30
42
|
return { pool, spec, verdict: "missing", detail: `no provider "${provider}" configured` };
|
|
31
43
|
}
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
// Select one serviceable slot before spending the single completion. Explicit fleets resolve
|
|
45
|
+
// only their declared env name; legacy authEnv retains the curated alias behavior.
|
|
46
|
+
if (p.kind === "openai" && !model)
|
|
47
|
+
return { pool, spec, verdict: "missing", detail: "openai provider needs a model id" };
|
|
48
|
+
const selected = serviceableSlot(provider, model, p);
|
|
49
|
+
if (!selected.slot) {
|
|
50
|
+
return { pool, spec, verdict: "auth", detail: "no enabled credential slot with a present key" };
|
|
37
51
|
}
|
|
52
|
+
const apiKey = selected.key;
|
|
53
|
+
const credentialId = selected.slot.credentialId;
|
|
38
54
|
const isOpenAi = p.kind === "openai";
|
|
39
55
|
const url = isOpenAi ? `${p.base}/chat/completions` : `${p.base}/v1/messages`;
|
|
40
56
|
const body = JSON.stringify({
|
|
@@ -47,26 +63,26 @@ export async function probeMember(pool, spec, cfg, fetchFn = fetch, maxTokens =
|
|
|
47
63
|
const r = await fetchFn(url, { method: "POST", headers: authHeaders(p, apiKey), body });
|
|
48
64
|
const latencyMs = now() - started;
|
|
49
65
|
if (r.status === 401 || r.status === 403) {
|
|
50
|
-
return { pool, spec, verdict: "auth", httpStatus: r.status, latencyMs, detail: `HTTP ${r.status}` };
|
|
66
|
+
return { pool, spec, credentialId, verdict: "auth", httpStatus: r.status, latencyMs, detail: `HTTP ${r.status}` };
|
|
51
67
|
}
|
|
52
68
|
if (r.status === 429) {
|
|
53
|
-
return { pool, spec, verdict: "rate_limited", httpStatus: 429, latencyMs, detail: "HTTP 429" };
|
|
69
|
+
return { pool, spec, credentialId, verdict: "rate_limited", httpStatus: 429, latencyMs, detail: "HTTP 429" };
|
|
54
70
|
}
|
|
55
71
|
if (r.status === 404 || r.status === 400) {
|
|
56
|
-
return { pool, spec, verdict: "missing", httpStatus: r.status, latencyMs, detail: `HTTP ${r.status} — model not servable` };
|
|
72
|
+
return { pool, spec, credentialId, verdict: "missing", httpStatus: r.status, latencyMs, detail: `HTTP ${r.status} — model not servable` };
|
|
57
73
|
}
|
|
58
74
|
if (!r.ok) {
|
|
59
|
-
return { pool, spec, verdict: "error", httpStatus: r.status, latencyMs, detail: `HTTP ${r.status}` };
|
|
75
|
+
return { pool, spec, credentialId, verdict: "error", httpStatus: r.status, latencyMs, detail: `HTTP ${r.status}` };
|
|
60
76
|
}
|
|
61
77
|
const text = await r.text();
|
|
62
78
|
const content = extractContent(text);
|
|
63
79
|
if (content.trim().length === 0) {
|
|
64
|
-
return { pool, spec, verdict: "empty", httpStatus: r.status, latencyMs, detail: "200 but no content" };
|
|
80
|
+
return { pool, spec, credentialId, verdict: "empty", httpStatus: r.status, latencyMs, detail: "200 but no content" };
|
|
65
81
|
}
|
|
66
|
-
return { pool, spec, verdict: "live", httpStatus: r.status, latencyMs };
|
|
82
|
+
return { pool, spec, credentialId, verdict: "live", httpStatus: r.status, latencyMs };
|
|
67
83
|
}
|
|
68
84
|
catch (e) {
|
|
69
|
-
return { pool, spec, verdict: "error", latencyMs: now() - started, detail: e.message };
|
|
85
|
+
return { pool, spec, credentialId, verdict: "error", latencyMs: now() - started, detail: e.message };
|
|
70
86
|
}
|
|
71
87
|
}
|
|
72
88
|
/** Pull assistant text out of either an OpenAI or an Anthropic shaped response. */
|
package/dist/pool-health.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool-health.js","sourceRoot":"","sources":["../src/pool-health.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"pool-health.js","sourceRoot":"","sources":["../src/pool-health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** The finite set of quota dimensions understood by the relay. */
|
|
2
|
+
export type QuotaAxis = "requests" | "tokens";
|
|
3
|
+
export type QuotaPeriod = "minute" | "day" | "month" | "unknown";
|
|
4
|
+
export interface QuotaObservation {
|
|
5
|
+
axis: QuotaAxis;
|
|
6
|
+
period: QuotaPeriod;
|
|
7
|
+
limit: number;
|
|
8
|
+
remaining: number;
|
|
9
|
+
resetsAt: number | null;
|
|
10
|
+
observedAt: number;
|
|
11
|
+
basis: "provider-stated";
|
|
12
|
+
}
|
|
13
|
+
/** Options are deliberately small so observations can be deterministic in tests. */
|
|
14
|
+
export interface QuotaObservationOptions {
|
|
15
|
+
observedAt?: number;
|
|
16
|
+
}
|
|
17
|
+
export type QuotaHeaders = Headers | Readonly<Record<string, string | undefined>>;
|
|
18
|
+
/**
|
|
19
|
+
* Extracts all complete, explicitly attributed limit/remaining pairs from response headers.
|
|
20
|
+
* Header names are case-insensitive; supported forms put `limit`, `remaining`, and optionally
|
|
21
|
+
* `reset` alongside `requests` or `tokens`, with an optional `minute`, `day`, or `month` token.
|
|
22
|
+
* This covers both `x-ratelimit-limit-requests-day` and
|
|
23
|
+
* `anthropic-ratelimit-requests-limit`-style names.
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractQuotaObservations(headers: QuotaHeaders, options?: QuotaObservationOptions): QuotaObservation[];
|
|
26
|
+
/** Merge observations by their typed identity; later sets replace only matching tuples. */
|
|
27
|
+
export declare function mergeQuotaObservations(...sets: readonly (readonly QuotaObservation[])[]): QuotaObservation[];
|
|
28
|
+
/** Render-time derivation. The ratio is intentionally absent from QuotaObservation itself. */
|
|
29
|
+
export declare function headroomPercent(observation: QuotaObservation): number;
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
const AXES = new Set(["request", "requests", "token", "tokens"]);
|
|
2
|
+
const PERIODS = new Set(["minute", "day", "month"]);
|
|
3
|
+
const UNSUPPORTED_PERIOD_WORDS = new Set([
|
|
4
|
+
"second", "seconds", "hour", "hours", "week", "weeks", "quarter", "year", "years",
|
|
5
|
+
]);
|
|
6
|
+
function headerEntries(headers) {
|
|
7
|
+
if (typeof Headers !== "undefined" && headers instanceof Headers) {
|
|
8
|
+
const result = [];
|
|
9
|
+
headers.forEach((value, name) => result.push([name, value]));
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
const result = [];
|
|
13
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
14
|
+
if (typeof value === "string")
|
|
15
|
+
result.push([name, value]);
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
function parseStrictNumber(value) {
|
|
20
|
+
if (value === undefined || value.length === 0 || value.trim() !== value)
|
|
21
|
+
return undefined;
|
|
22
|
+
// Number() accepts whitespace and several non-decimal spellings. Header quota values are
|
|
23
|
+
// intentionally narrower: one complete decimal number, with no trailing provider text.
|
|
24
|
+
if (!/^[+-]?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+))(?:[eE][+-]?\d+)?$/.test(value)) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const parsed = Number(value);
|
|
28
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
29
|
+
}
|
|
30
|
+
function metricAndDimension(name) {
|
|
31
|
+
const parts = name.toLowerCase().replaceAll("_", "-").split("-").filter(Boolean);
|
|
32
|
+
const metrics = parts.filter((part) => part === "limit" || part === "remaining" || part === "reset");
|
|
33
|
+
const axes = parts.filter((part) => AXES.has(part));
|
|
34
|
+
const periods = parts.filter((part) => PERIODS.has(part));
|
|
35
|
+
// A generic `limit`/`remaining` pair is not attributable to either axis. Likewise, a
|
|
36
|
+
// malformed header mentioning two dimensions must not be guessed into one observation.
|
|
37
|
+
if (metrics.length !== 1 || axes.length !== 1 || periods.length > 1 ||
|
|
38
|
+
parts.some((part) => UNSUPPORTED_PERIOD_WORDS.has(part)))
|
|
39
|
+
return undefined;
|
|
40
|
+
const period = periods[0] ?? "unknown";
|
|
41
|
+
return {
|
|
42
|
+
metric: metrics[0],
|
|
43
|
+
axis: axes[0].startsWith("request") ? "requests" : "tokens",
|
|
44
|
+
period,
|
|
45
|
+
hasExplicitPeriod: periods.length === 1,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function durationMilliseconds(value) {
|
|
49
|
+
// Whitespace is allowed only between complete components. In particular, do not
|
|
50
|
+
// turn `1 2s` into `12s`; that loses the provider's malformed input boundary.
|
|
51
|
+
const pieces = [...value.matchAll(/(\d+(?:\.\d+)?)(ms|s|m|h|d)/gi)];
|
|
52
|
+
if (pieces.length === 0 || pieces.map((piece) => piece[0]).join("") !== value.replace(/\s+/g, "") ||
|
|
53
|
+
!/^\d+(?:\.\d+)?(?:ms|s|m|h|d)(?:\s*\d+(?:\.\d+)?(?:ms|s|m|h|d))*$/i.test(value)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const multipliers = {
|
|
57
|
+
ms: 1,
|
|
58
|
+
s: 1_000,
|
|
59
|
+
m: 60_000,
|
|
60
|
+
h: 3_600_000,
|
|
61
|
+
d: 86_400_000,
|
|
62
|
+
};
|
|
63
|
+
let total = 0;
|
|
64
|
+
for (const piece of pieces) {
|
|
65
|
+
const amount = Number(piece[1]);
|
|
66
|
+
const multiplier = multipliers[piece[2].toLowerCase()];
|
|
67
|
+
if (!Number.isFinite(amount) || multiplier === undefined)
|
|
68
|
+
return undefined;
|
|
69
|
+
total += amount * multiplier;
|
|
70
|
+
}
|
|
71
|
+
return Number.isFinite(total) ? total : undefined;
|
|
72
|
+
}
|
|
73
|
+
const ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
74
|
+
const HTTP_DATE = /^(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d{2}) ([A-Z][a-z]{2}) (\d{4}) (\d{2}):(\d{2}):(\d{2}) GMT|(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d{2})-([A-Z][a-z]{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}) GMT|(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) ([A-Z][a-z]{2}) {1,2}(\d{1,2}) (\d{2}):(\d{2}):(\d{2}) (\d{4}))$/;
|
|
75
|
+
const MONTHS = {
|
|
76
|
+
Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5,
|
|
77
|
+
Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11,
|
|
78
|
+
};
|
|
79
|
+
function validDate(year, month, day, hour, minute, second) {
|
|
80
|
+
if (month < 0 || month > 11 || hour > 23 || minute > 59 || second > 59)
|
|
81
|
+
return undefined;
|
|
82
|
+
const timestamp = Date.UTC(year, month, day, hour, minute, second);
|
|
83
|
+
const date = new Date(timestamp);
|
|
84
|
+
return Number.isFinite(timestamp) && date.getUTCFullYear() === year && date.getUTCMonth() === month &&
|
|
85
|
+
date.getUTCDate() === day && date.getUTCHours() === hour && date.getUTCMinutes() === minute &&
|
|
86
|
+
date.getUTCSeconds() === second ? timestamp : undefined;
|
|
87
|
+
}
|
|
88
|
+
function parseIsoTimestamp(value) {
|
|
89
|
+
if (!ISO_TIMESTAMP.test(value))
|
|
90
|
+
return undefined;
|
|
91
|
+
const timestamp = Date.parse(value);
|
|
92
|
+
return Number.isFinite(timestamp) ? timestamp : undefined;
|
|
93
|
+
}
|
|
94
|
+
function parseHttpDate(value) {
|
|
95
|
+
const match = HTTP_DATE.exec(value);
|
|
96
|
+
if (!match)
|
|
97
|
+
return undefined;
|
|
98
|
+
if (match[1] !== undefined) {
|
|
99
|
+
return validDate(Number(match[3]), MONTHS[match[2]], Number(match[1]), Number(match[4]), Number(match[5]), Number(match[6]));
|
|
100
|
+
}
|
|
101
|
+
if (match[7] !== undefined) {
|
|
102
|
+
const year = Number(match[9]) + (Number(match[9]) >= 50 ? 1900 : 2000);
|
|
103
|
+
return validDate(year, MONTHS[match[8]], Number(match[7]), Number(match[10]), Number(match[11]), Number(match[12]));
|
|
104
|
+
}
|
|
105
|
+
return validDate(Number(match[18]), MONTHS[match[13]], Number(match[14]), Number(match[15]), Number(match[16]), Number(match[17]));
|
|
106
|
+
}
|
|
107
|
+
/** Parse only reset forms whose units or epoch width make their meaning unambiguous. */
|
|
108
|
+
function parseReset(value, observedAt) {
|
|
109
|
+
if (value === undefined || value.length === 0 || value.trim() !== value)
|
|
110
|
+
return undefined;
|
|
111
|
+
const duration = durationMilliseconds(value);
|
|
112
|
+
if (duration !== undefined)
|
|
113
|
+
return observedAt + duration;
|
|
114
|
+
// Providers commonly use ten-digit Unix seconds or thirteen-digit Unix milliseconds.
|
|
115
|
+
if (/^\d{10}$/.test(value))
|
|
116
|
+
return Number(value) * 1_000;
|
|
117
|
+
if (/^\d{13}$/.test(value))
|
|
118
|
+
return Number(value);
|
|
119
|
+
return parseIsoTimestamp(value);
|
|
120
|
+
}
|
|
121
|
+
function parseRetryAfter(value, observedAt) {
|
|
122
|
+
if (value === undefined || value.length === 0 || value.trim() !== value)
|
|
123
|
+
return undefined;
|
|
124
|
+
if (/^\d+$/.test(value)) {
|
|
125
|
+
const seconds = Number(value);
|
|
126
|
+
return Number.isFinite(seconds) ? observedAt + seconds * 1_000 : undefined;
|
|
127
|
+
}
|
|
128
|
+
return parseHttpDate(value);
|
|
129
|
+
}
|
|
130
|
+
function bucketKey(axis, period) {
|
|
131
|
+
return `${axis}:${period}`;
|
|
132
|
+
}
|
|
133
|
+
function canonicalBucketOrder(a, b) {
|
|
134
|
+
const axisOrder = a.axis === b.axis ? 0 : a.axis === "requests" ? -1 : 1;
|
|
135
|
+
if (axisOrder !== 0)
|
|
136
|
+
return axisOrder;
|
|
137
|
+
const periodOrder = { minute: 0, day: 1, month: 2, unknown: 3 };
|
|
138
|
+
return periodOrder[a.period] - periodOrder[b.period] || a.order - b.order;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Extracts all complete, explicitly attributed limit/remaining pairs from response headers.
|
|
142
|
+
* Header names are case-insensitive; supported forms put `limit`, `remaining`, and optionally
|
|
143
|
+
* `reset` alongside `requests` or `tokens`, with an optional `minute`, `day`, or `month` token.
|
|
144
|
+
* This covers both `x-ratelimit-limit-requests-day` and
|
|
145
|
+
* `anthropic-ratelimit-requests-limit`-style names.
|
|
146
|
+
*/
|
|
147
|
+
export function extractQuotaObservations(headers, options = {}) {
|
|
148
|
+
const configuredObservedAt = options.observedAt;
|
|
149
|
+
const observedAt = configuredObservedAt !== undefined && Number.isFinite(configuredObservedAt)
|
|
150
|
+
? configuredObservedAt
|
|
151
|
+
: Date.now();
|
|
152
|
+
const buckets = new Map();
|
|
153
|
+
let order = 0;
|
|
154
|
+
let retryAfter;
|
|
155
|
+
for (const [rawName, rawValue] of headerEntries(headers)) {
|
|
156
|
+
const name = rawName.toLowerCase().trim();
|
|
157
|
+
if (name === "retry-after") {
|
|
158
|
+
retryAfter = rawValue;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const dimensions = metricAndDimension(name);
|
|
162
|
+
if (dimensions === undefined)
|
|
163
|
+
continue;
|
|
164
|
+
const key = bucketKey(dimensions.axis, dimensions.period);
|
|
165
|
+
let bucket = buckets.get(key);
|
|
166
|
+
if (bucket === undefined) {
|
|
167
|
+
bucket = {
|
|
168
|
+
axis: dimensions.axis,
|
|
169
|
+
period: dimensions.period,
|
|
170
|
+
resetHasExplicitPeriod: dimensions.hasExplicitPeriod,
|
|
171
|
+
order: order++,
|
|
172
|
+
};
|
|
173
|
+
buckets.set(key, bucket);
|
|
174
|
+
}
|
|
175
|
+
if (dimensions.metric === "reset") {
|
|
176
|
+
bucket.reset = rawValue;
|
|
177
|
+
bucket.resetHasExplicitPeriod = dimensions.hasExplicitPeriod;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const numeric = parseStrictNumber(rawValue);
|
|
181
|
+
if (numeric !== undefined) {
|
|
182
|
+
if (dimensions.metric === "limit")
|
|
183
|
+
bucket.limit = numeric;
|
|
184
|
+
else
|
|
185
|
+
bucket.remaining = numeric;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const parsedRetryAfter = parseRetryAfter(retryAfter, observedAt);
|
|
190
|
+
const allBuckets = [...buckets.values()];
|
|
191
|
+
const observations = [];
|
|
192
|
+
for (const bucket of allBuckets.sort(canonicalBucketOrder)) {
|
|
193
|
+
if (bucket.limit === undefined || bucket.remaining === undefined)
|
|
194
|
+
continue;
|
|
195
|
+
if (!Number.isFinite(bucket.limit) || bucket.limit <= 0)
|
|
196
|
+
continue;
|
|
197
|
+
if (!Number.isFinite(bucket.remaining) || bucket.remaining < 0)
|
|
198
|
+
continue;
|
|
199
|
+
let resetsAt = parseReset(bucket.reset, observedAt) ?? null;
|
|
200
|
+
if (resetsAt === null && bucket.remaining === 0 && parsedRetryAfter !== undefined) {
|
|
201
|
+
resetsAt = parsedRetryAfter;
|
|
202
|
+
}
|
|
203
|
+
observations.push({
|
|
204
|
+
axis: bucket.axis,
|
|
205
|
+
period: bucket.period,
|
|
206
|
+
limit: bucket.limit,
|
|
207
|
+
remaining: bucket.remaining,
|
|
208
|
+
resetsAt,
|
|
209
|
+
observedAt,
|
|
210
|
+
basis: "provider-stated",
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
// An axis-only reset header can be paired with one unambiguous complete bucket, but never
|
|
214
|
+
// with multiple periods. The normal bucket map already handles explicit reset periods.
|
|
215
|
+
for (const bucket of allBuckets) {
|
|
216
|
+
// If this bucket itself has a complete unknown-period pair, its reset already belongs to it;
|
|
217
|
+
// only a reset-only bucket needs the single-candidate fallback below.
|
|
218
|
+
if (bucket.reset === undefined || bucket.resetHasExplicitPeriod ||
|
|
219
|
+
(bucket.limit !== undefined && bucket.remaining !== undefined))
|
|
220
|
+
continue;
|
|
221
|
+
const candidates = allBuckets.filter((candidate) => candidate.axis === bucket.axis &&
|
|
222
|
+
candidate !== bucket && candidate.limit !== undefined && candidate.remaining !== undefined);
|
|
223
|
+
if (candidates.length !== 1)
|
|
224
|
+
continue;
|
|
225
|
+
const candidate = candidates[0];
|
|
226
|
+
const index = observations.findIndex((observation) => observation.axis === candidate.axis && observation.period === candidate.period);
|
|
227
|
+
const parsed = parseReset(bucket.reset, observedAt);
|
|
228
|
+
if (index >= 0 && parsed !== undefined && observations[index].resetsAt === null) {
|
|
229
|
+
observations[index] = { ...observations[index], resetsAt: parsed };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return observations;
|
|
233
|
+
}
|
|
234
|
+
/** Merge observations by their typed identity; later sets replace only matching tuples. */
|
|
235
|
+
export function mergeQuotaObservations(...sets) {
|
|
236
|
+
const merged = new Map();
|
|
237
|
+
for (const set of sets) {
|
|
238
|
+
for (const observation of set) {
|
|
239
|
+
merged.set(bucketKey(observation.axis, observation.period), { ...observation });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return [...merged.values()];
|
|
243
|
+
}
|
|
244
|
+
/** Render-time derivation. The ratio is intentionally absent from QuotaObservation itself. */
|
|
245
|
+
export function headroomPercent(observation) {
|
|
246
|
+
const ratio = (observation.remaining / observation.limit) * 100;
|
|
247
|
+
return Math.max(0, Math.min(100, ratio));
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=quota-observation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota-observation.js","sourceRoot":"","sources":["../src/quota-observation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CredentialId } from "./credential-id.js";
|
|
2
|
+
import { type FactKind, type FactScope } from "./target-facts.js";
|
|
2
3
|
/**
|
|
3
4
|
* What a backend's refusal MEANS — a lookup, never an inference, on the request path.
|
|
4
5
|
*
|
|
@@ -47,7 +48,11 @@ import type { FactKind, FactScope } from "./target-facts.js";
|
|
|
47
48
|
* ships a credential to the wrong host, and a wrong match here evicts a working family.
|
|
48
49
|
*/
|
|
49
50
|
export type ScopeTemplate = {
|
|
51
|
+
kind: "attempt";
|
|
52
|
+
} | {
|
|
50
53
|
kind: "deployment";
|
|
54
|
+
} | {
|
|
55
|
+
kind: "credential";
|
|
51
56
|
} | {
|
|
52
57
|
kind: "provider";
|
|
53
58
|
} | {
|
|
@@ -55,9 +60,10 @@ export type ScopeTemplate = {
|
|
|
55
60
|
} | {
|
|
56
61
|
kind: "group";
|
|
57
62
|
members: string[];
|
|
63
|
+
credential: "attempt" | "all";
|
|
58
64
|
};
|
|
59
65
|
/** Turn a stored template into the concrete scope for the request that matched it. */
|
|
60
|
-
export declare function materializeScope(template: ScopeTemplate, provider: string, model: string): FactScope;
|
|
66
|
+
export declare function materializeScope(template: ScopeTemplate, provider: string, credentialId: CredentialId, model: string): FactScope;
|
|
61
67
|
/**
|
|
62
68
|
* WHEN the condition this message describes clears — the third thing a reviewer knows and the
|
|
63
69
|
* table could not previously hold.
|
|
@@ -108,7 +114,7 @@ export interface UnknownRefusal {
|
|
|
108
114
|
status: number;
|
|
109
115
|
/** The normalized message — the signature's readable half. */
|
|
110
116
|
normalized: string;
|
|
111
|
-
/** One
|
|
117
|
+
/** One normalized sample: readable for research, with variable identifiers and URLs removed. */
|
|
112
118
|
sample: string;
|
|
113
119
|
count: number;
|
|
114
120
|
firstSeen: number;
|
|
@@ -193,7 +199,7 @@ export declare function refusalSignature(provider: string, model: string | null
|
|
|
193
199
|
* the per-model keying rule above and the only one — a seed is reviewed source, whereas a
|
|
194
200
|
* researched verdict is a model's opinion and gets the conservative key.
|
|
195
201
|
*
|
|
196
|
-
* ⚠ **`allowance-exhausted` is not a cost verdict.** See `
|
|
202
|
+
* ⚠ **`allowance-exhausted` is not a cost verdict.** See `target-facts.ts` — it means the
|
|
197
203
|
* deployment is free and currently spent, and it must never evict anything from a free pool.
|
|
198
204
|
*/
|
|
199
205
|
export declare const SEED_INTERPRETATIONS: Array<{
|
|
@@ -215,11 +221,11 @@ export declare function interpretRefusal(provider: string, model: string | null
|
|
|
215
221
|
/**
|
|
216
222
|
* Hold an uninterpretable refusal for the research tier.
|
|
217
223
|
*
|
|
218
|
-
* Deliberately cheap and lossy: one
|
|
224
|
+
* Deliberately cheap and lossy: one normalized sample per signature and a count. The queue exists to
|
|
219
225
|
* tell a researcher "this message happens, here is what it looks like", not to be a log — the
|
|
220
226
|
* metadata logger already covers the traffic, and it is metadata-only precisely so bodies do not
|
|
221
|
-
* land on disk.
|
|
222
|
-
*
|
|
227
|
+
* land on disk. Normalization runs before both the signature and the stored sample, stripping
|
|
228
|
+
* variable ids, key-shaped strings and URLs while leaving the provider's readable refusal text.
|
|
223
229
|
*/
|
|
224
230
|
export declare function recordUnknownRefusal(provider: string, model: string | null | undefined, status: number, body: string, opts?: {
|
|
225
231
|
path?: string;
|