llm-relay 0.57.0 → 0.59.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.
@@ -129,8 +129,8 @@ export interface Candidate {
129
129
  /**
130
130
  * What this deployment (or its provider, or its group) has STATED about itself — the learned
131
131
  * facts from `target-facts.ts`, each with the scope it applies at. The measurement kinds
132
- * (`context-limit`, `rate-limit-rpm|rpd|tpm|tpd`) also carry `value`: the ceiling itself, as
133
- * stated. Display-only — nothing here reorders or gates a candidate.
132
+ * (`context-limit`, `max-output`, `rate-limit-rpm|rpd|tpm|tpd`) also carry `value`: the ceiling
133
+ * itself, as stated. Display-only — nothing here reorders or gates a candidate.
134
134
  */
135
135
  facts: Array<{
136
136
  kind: string;
package/dist/cli.d.ts CHANGED
@@ -260,11 +260,13 @@ export declare function renderCommand(invoke: {
260
260
  /**
261
261
  * `llm-relay lanes [--probe]` — what each cli lane's own tool says it serves.
262
262
  *
263
- * ⚠ `--probe` is the ONE place the relay runs a lane's command, and only as an explicit operator
264
- * action — same precedent as `pools --probe` sending real completions. The request path reads the
265
- * cached manifest and never spawns anything. Without `--probe` this just prints the cache.
263
+ * ⚠ `--probe` spawns lane commands as an explicit operator action same precedent as
264
+ * `pools --probe` sending real completions. The request path reads the cached manifest and never
265
+ * spawns anything; outside it the only OTHER spawn site is the relay's background lane cadence
266
+ * (owner decision 2026-08-29, docs/quota-reprobe-design-2026-08-29.md). Without `--probe` this
267
+ * just prints the cache.
266
268
  */
267
- export declare function runLanes(): void;
269
+ export declare function runLanes(): Promise<void>;
268
270
  /**
269
271
  * `llm-relay dispatch [lane]` — which lane to hand a delegated task to next.
270
272
  *
package/dist/cli.js CHANGED
@@ -12,9 +12,10 @@ import { offloadState, setOffload } from "./offload.js";
12
12
  import { buildCandidates } from "./candidates.js";
13
13
  import { CREDENTIAL_LABEL_PATTERN, makeCredentialId } from "./credential-id.js";
14
14
  import { providerCredentialSlots, slotAllowsModel } from "./credential-fleet.js";
15
- import { loadLaneManifest, verifyModel } from "./lane-manifest.js";
15
+ import { loadLaneManifest, rosterIsStale, verifyModel } from "./lane-manifest.js";
16
16
  import { probeLanes } from "./lane-probe.js";
17
- import { buildDispatch, normalizeCliCommand, specContextWindow, CONTEXT_TOKEN } from "./dispatch.js";
17
+ import { buildDispatch, normalizeCliCommand, restoreExhaustedRows, specContextWindow, CONTEXT_TOKEN } from "./dispatch.js";
18
+ import { loadExhaustedRows } from "./dispatch-exhaustion-persistence.js";
18
19
  import { detectHostRouting, parseHostRoutingState } from "./host-routing.js";
19
20
  import { contextWindowResolver, COST_CLASSES } from "./metadata.js";
20
21
  import { snapshotContextWindow } from "./tier-data.js";
@@ -1674,14 +1675,16 @@ export function renderCommand(invoke, shell = shellFor()) {
1674
1675
  /**
1675
1676
  * `llm-relay lanes [--probe]` — what each cli lane's own tool says it serves.
1676
1677
  *
1677
- * ⚠ `--probe` is the ONE place the relay runs a lane's command, and only as an explicit operator
1678
- * action — same precedent as `pools --probe` sending real completions. The request path reads the
1679
- * cached manifest and never spawns anything. Without `--probe` this just prints the cache.
1678
+ * ⚠ `--probe` spawns lane commands as an explicit operator action same precedent as
1679
+ * `pools --probe` sending real completions. The request path reads the cached manifest and never
1680
+ * spawns anything; outside it the only OTHER spawn site is the relay's background lane cadence
1681
+ * (owner decision 2026-08-29, docs/quota-reprobe-design-2026-08-29.md). Without `--probe` this
1682
+ * just prints the cache.
1680
1683
  */
1681
- export function runLanes() {
1684
+ export async function runLanes() {
1682
1685
  const cfg = loadOrExit();
1683
1686
  if (hasFlag("--probe")) {
1684
- for (const r of probeLanes(cfg)) {
1687
+ for (const r of await probeLanes(cfg)) {
1685
1688
  process.stdout.write(r.ok
1686
1689
  ? ` ✓ ${r.lane.padEnd(8)} ${String(r.modelCount).padStart(3)} models via \`${r.via}\`
1687
1690
  `
@@ -1697,8 +1700,11 @@ export function runLanes() {
1697
1700
  return;
1698
1701
  }
1699
1702
  for (const [lane, entry] of Object.entries(manifest.lanes)) {
1703
+ const stale = rosterIsStale(entry)
1704
+ ? " ⚠ STALE — no longer evidence for eviction; run `llm-relay lanes --probe`"
1705
+ : "";
1700
1706
  process.stdout.write(`
1701
- ${lane} — ${entry.models.length} models, probed ${entry.probedAt} via \`${entry.via}\`
1707
+ ${lane} — ${entry.models.length} models, probed ${entry.probedAt} via \`${entry.via}\`${stale}
1702
1708
  `);
1703
1709
  for (const m of entry.models) {
1704
1710
  const supports = m.supports
@@ -1722,7 +1728,7 @@ ${lane} — ${entry.models.length} models, probed ${entry.probedAt} via \`${entr
1722
1728
  const model = i >= 0 ? rung.args[i + 1] : undefined;
1723
1729
  if (!model)
1724
1730
  continue;
1725
- const v = verifyModel(manifest, rung.command, model);
1731
+ const v = verifyModel(manifest, rung.command, model, { args: rung.args });
1726
1732
  if (v.status === "not-servable" && !bad.includes(v.reason))
1727
1733
  bad.push(v.reason);
1728
1734
  }
@@ -1846,6 +1852,11 @@ export async function runDispatch(arg) {
1846
1852
  specContextWindow(l.spec, cfg, cachedContextWindow) !== null);
1847
1853
  const staleProxy = hostStale || windowStale;
1848
1854
  const live = staleProxy ? null : liveRaw;
1855
+ // The durable half of exhaustion state is readable cold since dispatch-exhaustion persistence
1856
+ // landed: restore still-future rows so the fallback ladder shows what the relay recorded.
1857
+ // In-memory-only rows on a running relay still need the live answer — this narrows the gap, it
1858
+ // does not close it. Read-only here: no listener is installed, so nothing writes back.
1859
+ restoreExhaustedRows(cfg, loadExhaustedRows());
1849
1860
  const view = normalizeDispatchCommands(live ??
1850
1861
  buildDispatch(cfg, {
1851
1862
  ...(task ? { task } : {}),
@@ -2230,6 +2241,7 @@ const FACT_MEANING = {
2230
2241
  "credential-invalid": "the provider says this key is bad — every deployment behind it demoted",
2231
2242
  "rate-limited": "throughput throttled by the provider — demoted, never evicted",
2232
2243
  "context-limit": "a ceiling this deployment stated about itself — a measurement, never a demotion",
2244
+ "max-output": "a ceiling this deployment stated about itself — a measurement, never a demotion",
2233
2245
  "rate-limit-rpm": "a ceiling this deployment stated about itself — a measurement, never a demotion",
2234
2246
  "rate-limit-rpd": "a ceiling this deployment stated about itself — a measurement, never a demotion",
2235
2247
  "rate-limit-tpm": "a ceiling this deployment stated about itself — a measurement, never a demotion",
@@ -3284,14 +3296,10 @@ export function main() {
3284
3296
  return;
3285
3297
  }
3286
3298
  if (arg2 === "lanes") {
3287
- try {
3288
- runLanes();
3289
- }
3290
- catch (e) {
3291
- process.stderr.write(`llm-relay lanes: ${e.message}
3292
- `);
3299
+ runLanes().catch((e) => {
3300
+ process.stderr.write(`llm-relay lanes: ${e.message}\n`);
3293
3301
  process.exit(1);
3294
- }
3302
+ });
3295
3303
  return;
3296
3304
  }
3297
3305
  if (arg2 === "dispatch") {