@wengine-ai/llms 2.3.2416 → 2.3.2417

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.
@@ -56,30 +56,6 @@ export interface PluginDispatchInput {
56
56
  * spreads) for per-request wrapper state. */
57
57
  req?: any;
58
58
  }
59
- /**
60
- * Outcome of a level-1 account-state check.
61
- *
62
- * `available: false` parks the provider without a network call; `parkedUntil`
63
- * lets the plugin tell the core when the account frees up (e.g. a quota reset
64
- * epoch), so the probe does not re-check before then.
65
- */
66
- export interface ManagedProbeVerdict {
67
- available: boolean;
68
- /** Epoch ms after which the account may be usable again. */
69
- parkedUntil?: number;
70
- /** Stable reason used for logging only — never shown to a client. */
71
- reason?: string;
72
- }
73
- /** Input for a runtime-owned liveness probe. */
74
- export interface ManagedProbeInput {
75
- /** One ordinary core dispatch attempt (same seam as `PluginDispatchInput.attempt`). */
76
- attempt(): Promise<Response>;
77
- /** Model to probe with (the first circuit-open model of the provider). */
78
- model: string;
79
- provider: LLMProvider;
80
- /** Per-attempt config; the core sets `TIMEOUT` so a probe cannot outlive its budget. */
81
- config: any;
82
- }
83
59
  /**
84
60
  * Managed-subscription semantics for one provider family. Everything the data
85
61
  * plane needs to talk to a subscription upstream without the core knowing it:
@@ -133,36 +109,6 @@ export interface ManagedRuntime {
133
109
  * hard attempt bound and must never swallow client aborts.
134
110
  */
135
111
  dispatchRequest?(input: PluginDispatchInput): Promise<Response>;
136
- /**
137
- * Cheap account-state check used by the background recovery probe.
138
- *
139
- * Two-level probing exists because account state and reachability fail
140
- * independently: a parked/over-quota account must NOT spend a network call
141
- * (the Gemini lesson), while a healthy-but-unreachable account must not be
142
- * restored to the model list on the strength of its state alone. The core
143
- * calls this FIRST and only issues the (expensive) real request when it
144
- * resolves.
145
- *
146
- * Resolve to use the `available` hint semantics below; throw a classified
147
- * error (e.g. `*_account_cooling`) to report the account as unusable. When
148
- * absent, the core falls back to `ensureUsable()` — which is the same check
149
- * every real request already performs — so plugins that only implement the
150
- * circuit breaker still work.
151
- */
152
- probeAccountState?(): Promise<ManagedProbeVerdict>;
153
- /**
154
- * Optional runtime-owned liveness probe (level 2).
155
- *
156
- * Called only after `probeAccountState`/`ensureUsable` reported the account as
157
- * usable. Implementations should make the cheapest request that still proves
158
- * the upstream answers for THIS account (a quota/status endpoint is ideal,
159
- * otherwise a 1-token chat). Must honour the supplied signal/timeout and must
160
- * not persist cooldowns that a probe is not allowed to set.
161
- *
162
- * When absent the core performs a generic 1-token request through
163
- * `dispatchRequest` (see `PluginDispatchInput`).
164
- */
165
- probeLiveness?(input: ManagedProbeInput): Promise<Response>;
166
112
  /**
167
113
  * Opt-in 401 handling: when set, the core performs ONE forced refresh and
168
114
  * re-dispatch on 401 before classifying. `refresh` must persist rotated
@@ -57,13 +57,9 @@ export declare class ActiveProbeService {
57
57
  private resolveProxyUrl?;
58
58
  private logger?;
59
59
  private getConfig?;
60
- /** Resolves the installed plugin runtime that owns a managed provider. */
61
- private resolveManagedRuntime?;
62
- /** Periodic timer for managed-subscription recovery probing. */
63
- private managedProbeTimer?;
64
60
  private running;
65
61
  private probeTelemetry;
66
- constructor(getProviders: () => LLMProvider[], config?: ActiveProbeConfig, resolveProxyUrl?: (provider: LLMProvider) => string | undefined, logger?: any, getConfig?: (key: string) => any, resolveManagedRuntime?: (provider: LLMProvider) => any);
62
+ constructor(getProviders: () => LLMProvider[], config?: ActiveProbeConfig, resolveProxyUrl?: (provider: LLMProvider) => string | undefined, logger?: any, getConfig?: (key: string) => any);
67
63
  /**
68
64
  * Record the latest reachability probe result for a provider.
69
65
  * Only /models-style reachability probes (health, manual, rate-limit header
@@ -106,43 +102,6 @@ export declare class ActiveProbeService {
106
102
  * Rate-limited models are NOT probed by runHealthProbe — they have a separate timer
107
103
  * so they can be checked more frequently (every 2 minutes) without affecting normal probes.
108
104
  */
109
- /**
110
- * Recover circuit-open MANAGED (subscription) providers with one real request.
111
- *
112
- * runHealthProbe deliberately skips managed providers: their credentials are
113
- * plugin-owned, so an unauthenticated GET /v1/models would fail for reasons
114
- * unrelated to health. The consequence was that a subscription provider whose
115
- * circuit opened (exhausted quota, revoked token, upstream outage) had NO
116
- * background path back to healthy — it stayed circuit-open and the only way it
117
- * ever recovered was a real user request hitting it again, which is exactly the
118
- * "keeps failing and keeps being called" behaviour.
119
- *
120
- * This closes that loop: for each managed provider with a circuit-open model
121
- * whose probe interval has elapsed, issue one minimal request through the
122
- * plugin's own dispatcher (so token refresh, account rotation and the plugin's
123
- * error taxonomy all apply) and restore its models only when the upstream
124
- * actually answered.
125
- */
126
- runManagedRecoveryProbe(): Promise<void>;
127
- /**
128
- * Level 1 of the managed recovery probe: is the ACCOUNT usable at all?
129
- *
130
- * Returns false when the account is parked (quota exhausted / verification
131
- * needed / risk-blocked), in which case the caller must skip the liveness call
132
- * entirely — testing the network for an account that cannot answer is pure
133
- * waste and, for subscription providers, a rate-limit risk.
134
- */
135
- private probeManagedAccountState;
136
- /**
137
- * Level 2 of the managed recovery probe: does the upstream actually answer?
138
- *
139
- * Uses a runtime-owned `probeLiveness` when the plugin provides one (a quota or
140
- * status endpoint costs no chat quota), otherwise performs a generic 1-token
141
- * request through the runtime's dispatcher. The dispatcher input carries a real
142
- * `attempt()` and a per-attempt `config` — both are REQUIRED by
143
- * `PluginDispatchInput`, and omitting them made wrappers fail unconditionally.
144
- */
145
- private probeManagedLiveness;
146
105
  private runRateLimitProbe;
147
106
  /**
148
107
  * Run scheduled wake-up for configured providers
@@ -163,11 +122,11 @@ export declare class ActiveProbeService {
163
122
  /**
164
123
  * Initialize and get the active probe service
165
124
  */
166
- export declare function getActiveProbeService(getProviders: () => LLMProvider[], config?: ActiveProbeConfig, resolveProxyUrl?: (provider: LLMProvider) => string | undefined, logger?: any, getConfig?: (key: string) => any, resolveManagedRuntime?: (provider: LLMProvider) => any): ActiveProbeService;
125
+ export declare function getActiveProbeService(getProviders: () => LLMProvider[], config?: ActiveProbeConfig, resolveProxyUrl?: (provider: LLMProvider) => string | undefined, logger?: any, getConfig?: (key: string) => any): ActiveProbeService;
167
126
  /**
168
127
  * Start the active probe service (convenience function)
169
128
  */
170
- export declare function startActiveProbe(getProviders: () => LLMProvider[], config?: ActiveProbeConfig, resolveProxyUrl?: (provider: LLMProvider) => string | undefined, logger?: any, getConfig?: (key: string) => any, resolveManagedRuntime?: (provider: LLMProvider) => any): ActiveProbeService;
129
+ export declare function startActiveProbe(getProviders: () => LLMProvider[], config?: ActiveProbeConfig, resolveProxyUrl?: (provider: LLMProvider) => string | undefined, logger?: any, getConfig?: (key: string) => any): ActiveProbeService;
171
130
  /**
172
131
  * Stop the active probe service
173
132
  */
@@ -76,23 +76,6 @@ export declare class ProviderHealthStore {
76
76
  * Get all models in fail pool (open status)
77
77
  */
78
78
  getFailPoolModels(): string[];
79
- /**
80
- * Stamp a background probe attempt without changing circuit state.
81
- *
82
- * Recovery probes need this: a failed probe must keep the model circuit-open
83
- * but move `lastProbeTime` forward, otherwise the interval gate in
84
- * isAvailable()/the probe loop would re-probe on every tick.
85
- */
86
- recordProbeAttempt(provider: string, model: string): void;
87
- /**
88
- * Park a model until the account's own reset epoch.
89
- *
90
- * A plugin that reports its quota window (e.g. `resets_at`) knows better than
91
- * a fixed probe interval when the account can answer again. Setting
92
- * `rateLimitUntil` keeps the model hidden from the list AND stops the recovery
93
- * probe from spending network calls on a known-parked account.
94
- */
95
- markParkedUntil(provider: string, model: string, until: number): void;
96
79
  /**
97
80
  * Get all models in half-open state
98
81
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wengine-ai/llms",
3
- "version": "2.3.2416",
3
+ "version": "2.3.2417",
4
4
  "description": "A universal LLM API transformation server",
5
5
  "main": "dist/cjs/server.cjs",
6
6
  "module": "dist/esm/server.mjs",
@@ -64,7 +64,7 @@
64
64
  "uuid": "^11.1.0",
65
65
  "@fastify/multipart": "^9.0.0",
66
66
  "@fastify/static": "^8.2.0",
67
- "@wengine-ai/claude-code-router-shared": "^2.3.2416",
67
+ "@wengine-ai/claude-code-router-shared": "^2.3.2417",
68
68
  "adm-zip": "^0.5.16",
69
69
  "better-sqlite3": "^12.10.0",
70
70
  "rotating-file-stream": "^3.2.7"
@@ -1,66 +0,0 @@
1
- /**
2
- * Detect an upstream answer that completed successfully (HTTP 200) but carried
3
- * no content blocks at all.
4
- *
5
- * The client shows such a turn as "no response" — a message is sent and nothing
6
- * happens. Because the wire response is an ordinary 200 with a normal
7
- * message_start / message_delta / message_stop sequence, nothing in the request
8
- * path throws, so neither the same-model retry nor the fallback chain runs, and
9
- * the empty turn is recorded as a provider success (which also keeps the
10
- * unhealthy model out of the fail pool).
11
- *
12
- * Judging the whole content-block set rather than the text keeps legitimate
13
- * turns alive: a tool_use-only turn, a stop_sequence hit, and a refusal (which
14
- * still emits a text block) all contain at least one content block.
15
- *
16
- * The probe is bounded by EMPTY_ANSWER_INSPECT_BYTES, so a healthy answer is
17
- * released right after its first content block and is never buffered further.
18
- */
19
- /** Upper bound on the bytes inspected before a stream is forwarded untouched. */
20
- export declare const EMPTY_ANSWER_INSPECT_BYTES: number;
21
- /**
22
- * Upper bound on how long the probe may hold the response before forwarding.
23
- *
24
- * Long enough that a thinking model's first content block arrives well within it
25
- * on a healthy stream, short enough that a stalled upstream cannot pin the
26
- * request (and its connection) open.
27
- */
28
- export declare const EMPTY_ANSWER_INSPECT_DEADLINE_MS = 10000;
29
- /** `content` = real output; `empty` = finished with no block; `pending` = undecided. */
30
- export type AnswerVerdict = "content" | "empty" | "pending";
31
- /**
32
- * Judge the buffered prefix of an answer.
33
- *
34
- * Line-prefix scanning (rather than JSON parsing) is deliberate: Anthropic SSE
35
- * names each event on both `event:` and `data:` lines, and payloads may be split
36
- * across reads, so a marker scan stays correct and cheap on every chunk.
37
- */
38
- export declare function inspectAnswerPrefix(text: string): AnswerVerdict;
39
- export interface AnswerProbeResult {
40
- verdict: AnswerVerdict;
41
- /** Chunks consumed while probing, in order, to be re-emitted verbatim. */
42
- chunks: Uint8Array[];
43
- /** Bytes consumed while probing. */
44
- bytes: number;
45
- /** Remaining upstream stream, or null when the answer ended / was cancelled. */
46
- rest: ReadableStream<Uint8Array> | null;
47
- }
48
- /**
49
- * Read enough of `body` to decide whether the answer carries any content.
50
- *
51
- * Consumed bytes are returned in `chunks` so a healthy stream can be rebuilt
52
- * byte-for-byte. Reading stops at the first content block (fast, non-blocking
53
- * for real answers), at the byte budget (forwarded untouched — an unknown shape
54
- * is never second-guessed), at the terminal event (verdict `empty`), or when
55
- * `deadlineMs` elapses.
56
- *
57
- * The deadline matters: before this guard, the first SSE event was forwarded as
58
- * soon as it arrived. Waiting for a content block is open-ended by nature (a
59
- * thinking model may emit nothing but heartbeats for a long time), so an
60
- * upstream that stalls after `message_start` would hold the response — and the
61
- * whole request — forever. On timeout the answer is treated as `pending` and
62
- * forwarded as-is, which preserves the old behaviour for slow-but-alive streams.
63
- */
64
- export declare function probeAnswer(body: ReadableStream<Uint8Array>, limitBytes?: number, deadlineMs?: number): Promise<AnswerProbeResult>;
65
- /** Re-emit probed chunks, then continue with `rest`. */
66
- export declare function rebuildAnswerStream(chunks: Uint8Array[], rest: ReadableStream<Uint8Array> | null): ReadableStream<Uint8Array>;