@bitbaum/ai-kit 0.10.0 → 0.12.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.
@@ -39,6 +39,7 @@
39
39
  * is "does the pipe carry water", not "is the model any good".
40
40
  */
41
41
  import { type CompleteOptions } from "./complete.js";
42
+ import type { Link, Env } from "./chain.js";
42
43
  import type { HealthTracker } from "./health.js";
43
44
  export interface LivenessResult {
44
45
  /** Did a model answer? */
@@ -64,6 +65,35 @@ export interface LivenessOptions extends Omit<CompleteOptions, "messages" | "max
64
65
  * Default 10 minutes. Set 0 to disable caching — only for a test.
65
66
  */
66
67
  minIntervalMs?: number;
68
+ /**
69
+ * Resolve the chain when a probe actually runs, rather than once at
70
+ * construction.
71
+ *
72
+ * For an app whose provider list lives in a DATABASE — an admin screen with
73
+ * enabled/default rows and per-provider keys — a chain fixed at construction
74
+ * is a chain frozen at process start. The probe would then keep reporting on
75
+ * a configuration the operator changed twenty minutes ago, which is the
76
+ * opposite of "the truth about right now".
77
+ *
78
+ * It is called only on a real probe, never on a cache hit, so a monitor
79
+ * polling this route does not also poll the database.
80
+ *
81
+ * Return `{ chain, env }` when the KEYS move with the chain — an admin
82
+ * screen that stores a per-provider key alongside the model is the ordinary
83
+ * case, and resolving links from the database while reading credentials from
84
+ * a `process.env` captured at construction would have the probe report "no
85
+ * key" for a provider that is configured and working. Returning a bare
86
+ * `Link[]` keeps whatever `env` the probe was built with.
87
+ *
88
+ * Takes precedence over `chain` when both are given.
89
+ */
90
+ resolveChain?: () => Link[] | {
91
+ chain: Link[];
92
+ env?: Env;
93
+ } | Promise<Link[] | {
94
+ chain: Link[];
95
+ env?: Env;
96
+ }>;
67
97
  /** Injected for tests. Defaults to `Date.now`. */
68
98
  now?: () => number;
69
99
  }
package/dist/liveness.js CHANGED
@@ -90,9 +90,19 @@ export function createLivenessProbe(options = {}) {
90
90
  }
91
91
  const started = now();
92
92
  try {
93
+ // Resolved here, not at construction, and only on a real probe — so a
94
+ // monitor polling this route does not also poll whatever backs it.
95
+ const resolved = options.resolveChain ? await options.resolveChain() : options.chain;
96
+ const chain = Array.isArray(resolved) ? resolved : resolved?.chain;
97
+ // Only when the resolver supplied one. Otherwise the probe keeps the
98
+ // env it was built with, so a bare `Link[]` resolver behaves exactly
99
+ // as it did before this option grew a second shape.
100
+ const env = !Array.isArray(resolved) && resolved?.env !== undefined ? resolved.env : options.env;
93
101
  const result = await complete({
94
102
  timeoutMs: PROBE_TIMEOUT_MS,
95
103
  ...options,
104
+ chain,
105
+ env,
96
106
  messages: PROBE_MESSAGES,
97
107
  maxTokens: PROBE_MAX_TOKENS,
98
108
  temperature: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitbaum/ai-kit",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "One install for the AI layer of an app: which model to call, what to do when the vendor retires it, how to walk the fallback chain and know when none of it worked, how to read the three kinds of 429, a fair daily budget across users, headless AI form filling — and now the model registry (one SSOT for every callable id, with the paid/free boundary as a field) and the grounding harness (facts, contract, deterministic fabrication check).",
5
5
  "license": "MIT",
6
6
  "author": "Mao Nakamoto",
package/src/liveness.ts CHANGED
@@ -40,6 +40,7 @@
40
40
  */
41
41
 
42
42
  import { complete, type CompleteOptions } from "./complete.js";
43
+ import type { Link, Env } from "./chain.js";
43
44
  import { ChainExhaustedError } from "./attempt.js";
44
45
  import type { HealthTracker } from "./health.js";
45
46
 
@@ -71,6 +72,30 @@ export interface LivenessOptions extends Omit<
71
72
  * Default 10 minutes. Set 0 to disable caching — only for a test.
72
73
  */
73
74
  minIntervalMs?: number;
75
+ /**
76
+ * Resolve the chain when a probe actually runs, rather than once at
77
+ * construction.
78
+ *
79
+ * For an app whose provider list lives in a DATABASE — an admin screen with
80
+ * enabled/default rows and per-provider keys — a chain fixed at construction
81
+ * is a chain frozen at process start. The probe would then keep reporting on
82
+ * a configuration the operator changed twenty minutes ago, which is the
83
+ * opposite of "the truth about right now".
84
+ *
85
+ * It is called only on a real probe, never on a cache hit, so a monitor
86
+ * polling this route does not also poll the database.
87
+ *
88
+ * Return `{ chain, env }` when the KEYS move with the chain — an admin
89
+ * screen that stores a per-provider key alongside the model is the ordinary
90
+ * case, and resolving links from the database while reading credentials from
91
+ * a `process.env` captured at construction would have the probe report "no
92
+ * key" for a provider that is configured and working. Returning a bare
93
+ * `Link[]` keeps whatever `env` the probe was built with.
94
+ *
95
+ * Takes precedence over `chain` when both are given.
96
+ */
97
+ resolveChain?: () =>
98
+ Link[] | { chain: Link[]; env?: Env } | Promise<Link[] | { chain: Link[]; env?: Env }>;
74
99
  /** Injected for tests. Defaults to `Date.now`. */
75
100
  now?: () => number;
76
101
  }
@@ -140,9 +165,21 @@ export function createLivenessProbe(options: LivenessOptions = {}): LivenessProb
140
165
 
141
166
  const started = now();
142
167
  try {
168
+ // Resolved here, not at construction, and only on a real probe — so a
169
+ // monitor polling this route does not also poll whatever backs it.
170
+ const resolved = options.resolveChain ? await options.resolveChain() : options.chain;
171
+ const chain = Array.isArray(resolved) ? resolved : resolved?.chain;
172
+ // Only when the resolver supplied one. Otherwise the probe keeps the
173
+ // env it was built with, so a bare `Link[]` resolver behaves exactly
174
+ // as it did before this option grew a second shape.
175
+ const env =
176
+ !Array.isArray(resolved) && resolved?.env !== undefined ? resolved.env : options.env;
177
+
143
178
  const result = await complete({
144
179
  timeoutMs: PROBE_TIMEOUT_MS,
145
180
  ...options,
181
+ chain,
182
+ env,
146
183
  messages: PROBE_MESSAGES,
147
184
  maxTokens: PROBE_MAX_TOKENS,
148
185
  temperature: 0,