agent-nuvira 3.1.3 → 3.2.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/dist/agents/agents/reasoner.d.ts +8 -0
- package/dist/agents/agents/reasoner.d.ts.map +1 -1
- package/dist/agents/agents/reasoner.js +53 -2
- package/dist/agents/agents/reasoner.js.map +1 -1
- package/dist/agents/agents/writer.d.ts +24 -0
- package/dist/agents/agents/writer.d.ts.map +1 -1
- package/dist/agents/agents/writer.js +194 -0
- package/dist/agents/agents/writer.js.map +1 -1
- package/dist/agents/composite-plan.d.ts +143 -0
- package/dist/agents/composite-plan.d.ts.map +1 -0
- package/dist/agents/composite-plan.js +399 -0
- package/dist/agents/composite-plan.js.map +1 -0
- package/dist/agents/long-form-plan.d.ts +156 -0
- package/dist/agents/long-form-plan.d.ts.map +1 -0
- package/dist/agents/long-form-plan.js +274 -0
- package/dist/agents/long-form-plan.js.map +1 -0
- package/dist/agents/orchestrator.d.ts +107 -0
- package/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/agents/orchestrator.js +501 -35
- package/dist/agents/orchestrator.js.map +1 -1
- package/dist/agents/prompt-assembly.d.ts +8 -0
- package/dist/agents/prompt-assembly.d.ts.map +1 -1
- package/dist/agents/prompt-assembly.js +17 -0
- package/dist/agents/prompt-assembly.js.map +1 -1
- package/dist/cli/chat.d.ts +27 -0
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +116 -6
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/execute.d.ts +12 -0
- package/dist/cli/execute.d.ts.map +1 -1
- package/dist/cli/execute.js +105 -2
- package/dist/cli/execute.js.map +1 -1
- package/dist/cli/models.d.ts.map +1 -1
- package/dist/cli/models.js +10 -0
- package/dist/cli/models.js.map +1 -1
- package/dist/cli/trace.d.ts.map +1 -1
- package/dist/cli/trace.js +27 -0
- package/dist/cli/trace.js.map +1 -1
- package/dist/gateway/registry.d.ts +31 -0
- package/dist/gateway/registry.d.ts.map +1 -1
- package/dist/gateway/registry.js +143 -13
- package/dist/gateway/registry.js.map +1 -1
- package/dist/inference/model-entitlement.d.ts +46 -0
- package/dist/inference/model-entitlement.d.ts.map +1 -0
- package/dist/inference/model-entitlement.js +98 -0
- package/dist/inference/model-entitlement.js.map +1 -0
- package/dist/learning/autonomy-policy.d.ts +334 -0
- package/dist/learning/autonomy-policy.d.ts.map +1 -0
- package/dist/learning/autonomy-policy.js +500 -0
- package/dist/learning/autonomy-policy.js.map +1 -0
- package/dist/learning/credential-fingerprint.d.ts +58 -0
- package/dist/learning/credential-fingerprint.d.ts.map +1 -0
- package/dist/learning/credential-fingerprint.js +126 -0
- package/dist/learning/credential-fingerprint.js.map +1 -0
- package/dist/learning/deliverable-class.d.ts +130 -0
- package/dist/learning/deliverable-class.d.ts.map +1 -0
- package/dist/learning/deliverable-class.js +432 -0
- package/dist/learning/deliverable-class.js.map +1 -0
- package/dist/learning/long-form.d.ts +252 -0
- package/dist/learning/long-form.d.ts.map +1 -0
- package/dist/learning/long-form.js +510 -0
- package/dist/learning/long-form.js.map +1 -0
- package/dist/learning/model-first-router.d.ts +17 -0
- package/dist/learning/model-first-router.d.ts.map +1 -1
- package/dist/learning/model-first-router.js +27 -0
- package/dist/learning/model-first-router.js.map +1 -1
- package/dist/learning/model-registry.d.ts +66 -4
- package/dist/learning/model-registry.d.ts.map +1 -1
- package/dist/learning/model-registry.js +67 -6
- package/dist/learning/model-registry.js.map +1 -1
- package/dist/learning/model-warmup.d.ts +97 -2
- package/dist/learning/model-warmup.d.ts.map +1 -1
- package/dist/learning/model-warmup.js +165 -60
- package/dist/learning/model-warmup.js.map +1 -1
- package/dist/learning/prompt-layers.d.ts +61 -0
- package/dist/learning/prompt-layers.d.ts.map +1 -0
- package/dist/learning/prompt-layers.js +140 -0
- package/dist/learning/prompt-layers.js.map +1 -0
- package/dist/learning/provider-limits.d.ts +66 -0
- package/dist/learning/provider-limits.d.ts.map +1 -0
- package/dist/learning/provider-limits.js +184 -0
- package/dist/learning/provider-limits.js.map +1 -0
- package/dist/learning/reasoning-trace.d.ts +36 -1
- package/dist/learning/reasoning-trace.d.ts.map +1 -1
- package/dist/learning/reasoning-trace.js +36 -2
- package/dist/learning/reasoning-trace.js.map +1 -1
- package/dist/learning/resilient-call.d.ts +36 -1
- package/dist/learning/resilient-call.d.ts.map +1 -1
- package/dist/learning/resilient-call.js +80 -5
- package/dist/learning/resilient-call.js.map +1 -1
- package/dist/learning/unattended-job.d.ts +293 -0
- package/dist/learning/unattended-job.d.ts.map +1 -0
- package/dist/learning/unattended-job.js +544 -0
- package/dist/learning/unattended-job.js.map +1 -0
- package/dist/learning/unattended-progress.d.ts +95 -0
- package/dist/learning/unattended-progress.d.ts.map +1 -0
- package/dist/learning/unattended-progress.js +147 -0
- package/dist/learning/unattended-progress.js.map +1 -0
- package/dist/learning/working-state.d.ts +109 -0
- package/dist/learning/working-state.d.ts.map +1 -0
- package/dist/learning/working-state.js +244 -0
- package/dist/learning/working-state.js.map +1 -0
- package/dist/nlu/conversation-gate.d.ts +24 -0
- package/dist/nlu/conversation-gate.d.ts.map +1 -1
- package/dist/nlu/conversation-gate.js +47 -3
- package/dist/nlu/conversation-gate.js.map +1 -1
- package/dist/tools/coding-tools.d.ts.map +1 -1
- package/dist/tools/coding-tools.js +95 -19
- package/dist/tools/coding-tools.js.map +1 -1
- package/dist/tools/edit-verification.d.ts +125 -0
- package/dist/tools/edit-verification.d.ts.map +1 -0
- package/dist/tools/edit-verification.js +237 -0
- package/dist/tools/edit-verification.js.map +1 -0
- package/dist/tools/git-tool.d.ts.map +1 -1
- package/dist/tools/git-tool.js +37 -4
- package/dist/tools/git-tool.js.map +1 -1
- package/dist/tools/registry.d.ts +30 -2
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +40 -11
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/run-cli.d.ts.map +1 -1
- package/dist/tools/run-cli.js +40 -13
- package/dist/tools/run-cli.js.map +1 -1
- package/dist/tools/run-terminal.d.ts +6 -1
- package/dist/tools/run-terminal.d.ts.map +1 -1
- package/dist/tools/run-terminal.js +158 -18
- package/dist/tools/run-terminal.js.map +1 -1
- package/dist/tools/tool-loop.d.ts +35 -0
- package/dist/tools/tool-loop.d.ts.map +1 -1
- package/dist/tools/tool-loop.js +141 -1
- package/dist/tools/tool-loop.js.map +1 -1
- package/dist/web-dashboard/chat-console.d.ts +6 -0
- package/dist/web-dashboard/chat-console.d.ts.map +1 -1
- package/dist/web-dashboard/chat-console.js.map +1 -1
- package/dist/web-dashboard/chat-retry.d.ts.map +1 -1
- package/dist/web-dashboard/chat-retry.js +10 -2
- package/dist/web-dashboard/chat-retry.js.map +1 -1
- package/dist/web-dashboard/server.d.ts.map +1 -1
- package/dist/web-dashboard/server.js +80 -8
- package/dist/web-dashboard/server.js.map +1 -1
- package/dist/web-dashboard/src/types.d.ts +81 -4
- package/dist/web-dashboard/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js → index-kCUkORm7.js} +2 -2
- package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js.map → index-kCUkORm7.js.map} +1 -1
- package/src/web-dashboard/public/index.html +1 -1
|
@@ -24,21 +24,115 @@
|
|
|
24
24
|
* // ... later ...
|
|
25
25
|
* stopWarmupDaemon();
|
|
26
26
|
*/
|
|
27
|
+
import { getModelRegistry } from './model-registry.js';
|
|
27
28
|
import type { ConfigManager } from '../config/manager.js';
|
|
29
|
+
/**
|
|
30
|
+
* Every bound this module spends against a free tier, in one place and
|
|
31
|
+
* OVERRIDABLE.
|
|
32
|
+
*
|
|
33
|
+
* These were module constants, which made the sweep's cost a property of the
|
|
34
|
+
* build rather than of the user's plan. The sweep is safe because it is bounded,
|
|
35
|
+
* so the bounds are exactly what an operator needs to see and tune — for a free
|
|
36
|
+
* tier one probe per cycle may be right, for a paid key tens are harmless. Env
|
|
37
|
+
* overrides keep that a configuration decision, never a source edit and a
|
|
38
|
+
* rebuild.
|
|
39
|
+
*/
|
|
40
|
+
export interface WarmupConfig {
|
|
41
|
+
/** How often the daemon runs a cycle (ms). */
|
|
42
|
+
intervalMs: number;
|
|
43
|
+
/** Minimum time between warmups of the same model (ms). */
|
|
44
|
+
warmThrottleMs: number;
|
|
45
|
+
/** Max HOT (already-in-use) models warmed per cycle. */
|
|
46
|
+
hotPerCycle: number;
|
|
47
|
+
/** Max NEVER-VERIFIED models spot-checked per cycle (the exploration budget). */
|
|
48
|
+
explorePerCycle: number;
|
|
49
|
+
/** Never re-probe the same unverified model more often than this (ms). */
|
|
50
|
+
exploreThrottleMs: number;
|
|
51
|
+
/** Models used within this window are "hot" (ms). */
|
|
52
|
+
hotWindowMs: number;
|
|
53
|
+
/** Models used within this window are "warm" (ms). */
|
|
54
|
+
warmWindowMs: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolve the sweep's bounds (env overrides → defaults).
|
|
58
|
+
*
|
|
59
|
+
* Read per call, not captured at import, so a test — or a long-running daemon
|
|
60
|
+
* whose env was set after boot — observes the value in force at that moment.
|
|
61
|
+
*/
|
|
62
|
+
export declare function warmupConfig(env?: NodeJS.ProcessEnv): WarmupConfig;
|
|
28
63
|
/**
|
|
29
64
|
* Record a model usage event.
|
|
30
65
|
* Called after every successful LLM call to track which models are hot.
|
|
31
66
|
*/
|
|
32
67
|
export declare function recordModelUsage(provider: string, model: string): void;
|
|
68
|
+
interface WarmupCandidate {
|
|
69
|
+
provider: string;
|
|
70
|
+
model: string;
|
|
71
|
+
priority: number;
|
|
72
|
+
}
|
|
33
73
|
/**
|
|
34
74
|
* Start the warmup daemon.
|
|
35
|
-
* Runs periodically to keep frequently-used models warm
|
|
75
|
+
* Runs periodically to keep frequently-used models warm — and to GROW the
|
|
76
|
+
* verified pool (see {@link selectExplorationCandidates}).
|
|
36
77
|
*/
|
|
37
|
-
export declare function startWarmupDaemon(configManager: ConfigManager): void;
|
|
78
|
+
export declare function startWarmupDaemon(configManager: ConfigManager, deps?: WarmupDeps): void;
|
|
38
79
|
/**
|
|
39
80
|
* Stop the warmup daemon.
|
|
40
81
|
*/
|
|
41
82
|
export declare function stopWarmupDaemon(): void;
|
|
83
|
+
/** What one cycle did — returned, so the caller reports it instead of guessing. */
|
|
84
|
+
export interface WarmupCycleResult {
|
|
85
|
+
/** Hot models spot-checked (already in use — the original behaviour). */
|
|
86
|
+
warmed: number;
|
|
87
|
+
/** NEVER-VERIFIED models spot-checked to grow the routing pool. */
|
|
88
|
+
explored: number;
|
|
89
|
+
/** Spot-checks of either kind that came back `verified`. */
|
|
90
|
+
verified: number;
|
|
91
|
+
/** Hot candidates the per-cycle cap left for the next cycle. */
|
|
92
|
+
skipped: number;
|
|
93
|
+
/**
|
|
94
|
+
* True when the credential shape changed, so this cycle forced a full catalog
|
|
95
|
+
* re-probe before spot-checking. Reported because it explains why one cycle
|
|
96
|
+
* did noticeably more network work than its neighbours.
|
|
97
|
+
*/
|
|
98
|
+
catalogRefreshed: boolean;
|
|
99
|
+
}
|
|
100
|
+
/** Injectable seams for tests — the real spot-check talks to a provider. */
|
|
101
|
+
export interface WarmupDeps {
|
|
102
|
+
spotCheck?: (provider: string, model: string, cm: ConfigManager) => Promise<'verified' | 'unavailable' | 'skipped' | 'error'>;
|
|
103
|
+
/**
|
|
104
|
+
* Seam for the credential-change catalog re-probe. Defaults to the real
|
|
105
|
+
* `refreshModelRegistry`; injected so a test asserts the re-probe HAPPENS
|
|
106
|
+
* without any provider traffic.
|
|
107
|
+
*/
|
|
108
|
+
refreshCatalog?: (cm: ConfigManager) => Promise<unknown>;
|
|
109
|
+
/** Seam for credential-change detection (defaults to the real sidecar). */
|
|
110
|
+
detectCredentialChange?: (cm: ConfigManager) => {
|
|
111
|
+
changed: boolean;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Pick unverified models worth a one-token spot-check — the models the router
|
|
116
|
+
* cannot currently see.
|
|
117
|
+
*
|
|
118
|
+
* The ORDER is the fix. Providers with the FEWEST verified models come first,
|
|
119
|
+
* because that is where the routing pool is thinnest: broadening a *provider*
|
|
120
|
+
* is what breaks winner-take-all, while verifying a thirteenth model on the
|
|
121
|
+
* provider that already wins every pick changes nothing about how the agent
|
|
122
|
+
* behaves. Unservable providers are excluded outright, so a cycle can never
|
|
123
|
+
* spend its whole budget on something that cannot answer.
|
|
124
|
+
*/
|
|
125
|
+
export declare function selectExplorationCandidates(registry: ReturnType<typeof getModelRegistry>, configManager: ConfigManager, now: number, limit: number): WarmupCandidate[];
|
|
126
|
+
/**
|
|
127
|
+
* Run one warmup cycle: keep hot models warm, and grow the verified pool.
|
|
128
|
+
*
|
|
129
|
+
* Both halves matter and neither is optional. The hot half keeps in-use models
|
|
130
|
+
* from going stale; the exploration half is what stops the pool from freezing —
|
|
131
|
+
* and note it runs even when `usageMap` is empty, which is exactly the state of
|
|
132
|
+
* a fresh process (the old `if (records.length === 0) return;` made the first
|
|
133
|
+
* cycle of every short-lived CLI run a no-op).
|
|
134
|
+
*/
|
|
135
|
+
export declare function runWarmupCycle(configManager: ConfigManager, deps?: WarmupDeps): Promise<WarmupCycleResult>;
|
|
42
136
|
/**
|
|
43
137
|
* Get warmup statistics (for dashboard display).
|
|
44
138
|
*/
|
|
@@ -58,4 +152,5 @@ export declare function getTrackedModels(): Array<{
|
|
|
58
152
|
useCount: number;
|
|
59
153
|
status: 'hot' | 'warm' | 'cold';
|
|
60
154
|
}>;
|
|
155
|
+
export {};
|
|
61
156
|
//# sourceMappingURL=model-warmup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-warmup.d.ts","sourceRoot":"","sources":["../../src/learning/model-warmup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;
|
|
1
|
+
{"version":3,"file":"model-warmup.d.ts","sourceRoot":"","sources":["../../src/learning/model-warmup.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,19 +25,34 @@
|
|
|
25
25
|
* stopWarmupDaemon();
|
|
26
26
|
*/
|
|
27
27
|
import { getModelRegistry } from './model-registry.js';
|
|
28
|
-
import { spotCheckModel } from '../inference/model-probe.js';
|
|
28
|
+
import { spotCheckModel, refreshModelRegistry } from '../inference/model-probe.js';
|
|
29
|
+
import { detectCredentialChange } from './credential-fingerprint.js';
|
|
30
|
+
import { ProviderFactory } from '../inference/factory.js';
|
|
29
31
|
import { logger } from '../utils/logger.js';
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
/** Parse a positive-integer env override; anything else falls back. */
|
|
33
|
+
function positiveInt(raw, fallback) {
|
|
34
|
+
if (raw === undefined || raw.trim() === '')
|
|
35
|
+
return fallback;
|
|
36
|
+
const n = Number(raw);
|
|
37
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the sweep's bounds (env overrides → defaults).
|
|
41
|
+
*
|
|
42
|
+
* Read per call, not captured at import, so a test — or a long-running daemon
|
|
43
|
+
* whose env was set after boot — observes the value in force at that moment.
|
|
44
|
+
*/
|
|
45
|
+
export function warmupConfig(env = process.env) {
|
|
46
|
+
return {
|
|
47
|
+
intervalMs: positiveInt(env.NUVIRA_WARMUP_INTERVAL_MS, 60_000),
|
|
48
|
+
warmThrottleMs: positiveInt(env.NUVIRA_WARMUP_THROTTLE_MS, 300_000),
|
|
49
|
+
hotPerCycle: positiveInt(env.NUVIRA_WARMUP_HOT_PER_CYCLE, 10),
|
|
50
|
+
explorePerCycle: positiveInt(env.NUVIRA_WARMUP_EXPLORE_PER_CYCLE, 6),
|
|
51
|
+
exploreThrottleMs: positiveInt(env.NUVIRA_WARMUP_EXPLORE_THROTTLE_MS, 600_000),
|
|
52
|
+
hotWindowMs: positiveInt(env.NUVIRA_WARMUP_HOT_WINDOW_MS, 30_600_000),
|
|
53
|
+
warmWindowMs: positiveInt(env.NUVIRA_WARMUP_WARM_WINDOW_MS, 86_400_000),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
41
56
|
/** In-memory usage tracker (persisted to registry via telemetry). */
|
|
42
57
|
const usageMap = new Map();
|
|
43
58
|
function usageKey(provider, model) {
|
|
@@ -75,7 +90,7 @@ function scoreWarmupPriority(record, registryEntry) {
|
|
|
75
90
|
const now = Date.now();
|
|
76
91
|
// Recency score: 0-1 (1 = just used, 0 = used long ago)
|
|
77
92
|
const timeSinceUse = now - record.lastUsedAt;
|
|
78
|
-
const recencyScore = Math.max(0, 1 - (timeSinceUse /
|
|
93
|
+
const recencyScore = Math.max(0, 1 - (timeSinceUse / warmupConfig().hotWindowMs));
|
|
79
94
|
// Frequency score: 0-1 (1 = used many times)
|
|
80
95
|
const frequencyScore = Math.min(1, record.useCount / 100);
|
|
81
96
|
// Verification score: verified = 1, unverified = 0.5, unavailable = 0
|
|
@@ -103,21 +118,32 @@ let warmupTimer = null;
|
|
|
103
118
|
let isRunning = false;
|
|
104
119
|
/**
|
|
105
120
|
* Start the warmup daemon.
|
|
106
|
-
* Runs periodically to keep frequently-used models warm
|
|
121
|
+
* Runs periodically to keep frequently-used models warm — and to GROW the
|
|
122
|
+
* verified pool (see {@link selectExplorationCandidates}).
|
|
107
123
|
*/
|
|
108
|
-
export function startWarmupDaemon(configManager) {
|
|
124
|
+
export function startWarmupDaemon(configManager, deps = {}) {
|
|
109
125
|
if (warmupTimer)
|
|
110
126
|
return; // Already running
|
|
111
|
-
|
|
127
|
+
const intervalMs = warmupConfig().intervalMs;
|
|
128
|
+
logger.info(`[warmup] Starting model warmup daemon (every ${intervalMs}ms)`);
|
|
112
129
|
warmupTimer = setInterval(() => {
|
|
113
130
|
if (!isRunning) {
|
|
114
131
|
isRunning = true;
|
|
115
|
-
runWarmupCycle(configManager).finally(() => { isRunning = false; });
|
|
132
|
+
runWarmupCycle(configManager, deps).finally(() => { isRunning = false; });
|
|
116
133
|
}
|
|
117
|
-
},
|
|
134
|
+
}, intervalMs);
|
|
135
|
+
// The daemon must never hold the process open.
|
|
136
|
+
//
|
|
137
|
+
// Without this, starting it on a normal run would hang every CLI command
|
|
138
|
+
// forever — which is why it was only ever started from the COLD-START branch
|
|
139
|
+
// (`getUsableProviders().length === 0`). That made the whole module dead in
|
|
140
|
+
// practice: once ANY model was verified, nothing ever warmed or verified
|
|
141
|
+
// another one. `unref()` is the same idiom the model probe already uses, and
|
|
142
|
+
// it is what makes starting this on every run safe.
|
|
143
|
+
warmupTimer.unref?.();
|
|
118
144
|
// Run first cycle immediately
|
|
119
145
|
isRunning = true;
|
|
120
|
-
runWarmupCycle(configManager).finally(() => { isRunning = false; });
|
|
146
|
+
runWarmupCycle(configManager, deps).finally(() => { isRunning = false; });
|
|
121
147
|
}
|
|
122
148
|
/**
|
|
123
149
|
* Stop the warmup daemon.
|
|
@@ -129,63 +155,140 @@ export function stopWarmupDaemon() {
|
|
|
129
155
|
logger.info('[warmup] Stopped model warmup daemon');
|
|
130
156
|
}
|
|
131
157
|
}
|
|
158
|
+
/** Can this provider actually serve? Real adapter + credentials. */
|
|
159
|
+
function isServable(provider, configManager) {
|
|
160
|
+
try {
|
|
161
|
+
if (!ProviderFactory.isConstructible(provider))
|
|
162
|
+
return false;
|
|
163
|
+
return configManager.hasRequiredCredentials(provider) === true;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Pick unverified models worth a one-token spot-check — the models the router
|
|
171
|
+
* cannot currently see.
|
|
172
|
+
*
|
|
173
|
+
* The ORDER is the fix. Providers with the FEWEST verified models come first,
|
|
174
|
+
* because that is where the routing pool is thinnest: broadening a *provider*
|
|
175
|
+
* is what breaks winner-take-all, while verifying a thirteenth model on the
|
|
176
|
+
* provider that already wins every pick changes nothing about how the agent
|
|
177
|
+
* behaves. Unservable providers are excluded outright, so a cycle can never
|
|
178
|
+
* spend its whole budget on something that cannot answer.
|
|
179
|
+
*/
|
|
180
|
+
export function selectExplorationCandidates(registry, configManager, now, limit) {
|
|
181
|
+
if (limit <= 0)
|
|
182
|
+
return [];
|
|
183
|
+
const rows = [];
|
|
184
|
+
const exploreThrottleMs = warmupConfig().exploreThrottleMs;
|
|
185
|
+
for (const provider of registry.getTrackedProviders()) {
|
|
186
|
+
if (!isServable(provider, configManager))
|
|
187
|
+
continue;
|
|
188
|
+
const verifiedInProvider = registry.getProviderStats(provider).verified;
|
|
189
|
+
for (const entry of registry.getAllModelsForProvider(provider)) {
|
|
190
|
+
if (entry.status !== 'unverified')
|
|
191
|
+
continue;
|
|
192
|
+
// The config sentinel is not a model id (same guard the router uses).
|
|
193
|
+
if (!entry.model || entry.model === 'default')
|
|
194
|
+
continue;
|
|
195
|
+
if (entry.quotaParkedUntil > now)
|
|
196
|
+
continue;
|
|
197
|
+
if (now - (entry.lastProbedAt ?? 0) < exploreThrottleMs)
|
|
198
|
+
continue;
|
|
199
|
+
rows.push({ provider, model: entry.model, priority: -verifiedInProvider, verifiedInProvider });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
rows.sort((a, b) => a.verifiedInProvider - b.verifiedInProvider ||
|
|
203
|
+
a.provider.localeCompare(b.provider) ||
|
|
204
|
+
a.model.localeCompare(b.model));
|
|
205
|
+
return rows.slice(0, limit).map(({ provider, model, priority }) => ({ provider, model, priority }));
|
|
206
|
+
}
|
|
132
207
|
/**
|
|
133
|
-
* Run one warmup cycle.
|
|
208
|
+
* Run one warmup cycle: keep hot models warm, and grow the verified pool.
|
|
209
|
+
*
|
|
210
|
+
* Both halves matter and neither is optional. The hot half keeps in-use models
|
|
211
|
+
* from going stale; the exploration half is what stops the pool from freezing —
|
|
212
|
+
* and note it runs even when `usageMap` is empty, which is exactly the state of
|
|
213
|
+
* a fresh process (the old `if (records.length === 0) return;` made the first
|
|
214
|
+
* cycle of every short-lived CLI run a no-op).
|
|
134
215
|
*/
|
|
135
|
-
async function runWarmupCycle(configManager) {
|
|
216
|
+
export async function runWarmupCycle(configManager, deps = {}) {
|
|
136
217
|
const registry = getModelRegistry();
|
|
137
218
|
const now = Date.now();
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
219
|
+
const cfg = warmupConfig();
|
|
220
|
+
const spotCheck = deps.spotCheck ?? spotCheckModel;
|
|
221
|
+
const result = {
|
|
222
|
+
warmed: 0,
|
|
223
|
+
explored: 0,
|
|
224
|
+
verified: 0,
|
|
225
|
+
skipped: 0,
|
|
226
|
+
catalogRefreshed: false,
|
|
227
|
+
};
|
|
228
|
+
// ── Half 0: did the KEY SET change since the last run? ──────────────────
|
|
229
|
+
// The pool can only hold models a probe has verified, and there is no
|
|
230
|
+
// per-model entitlement API to ask — so the one case a user notices ("I just
|
|
231
|
+
// bought credits, where are the models?") was invisible until an unrelated
|
|
232
|
+
// cold start happened to re-probe. Noticing the change and forcing the probe
|
|
233
|
+
// is cheap and idempotent, and it fires ONCE per change (the sidecar records
|
|
234
|
+
// the new shape before returning).
|
|
235
|
+
try {
|
|
236
|
+
const detect = deps.detectCredentialChange ?? detectCredentialChange;
|
|
237
|
+
if (detect(configManager).changed) {
|
|
238
|
+
const refresh = deps.refreshCatalog ?? ((cm) => refreshModelRegistry(cm, { spotCheck: true }));
|
|
239
|
+
result.catalogRefreshed = true;
|
|
240
|
+
logger.info('[warmup] Credentials changed — re-probing the catalog');
|
|
241
|
+
await refresh(configManager);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// Best-effort — a failed re-probe must never break warmup.
|
|
246
|
+
}
|
|
247
|
+
// ── Half 1: models already in use (hot → keep them warm) ────────────────
|
|
248
|
+
const hot = [];
|
|
249
|
+
for (const record of usageMap.values()) {
|
|
147
250
|
const entry = registry.getEntry(record.provider, record.model);
|
|
148
|
-
if (entry && entry.lastUsedAt && now - entry.lastUsedAt <
|
|
251
|
+
if (entry && entry.lastUsedAt && now - entry.lastUsedAt < cfg.warmThrottleMs)
|
|
149
252
|
continue;
|
|
150
|
-
}
|
|
151
|
-
// Skip unavailable models
|
|
152
253
|
if (entry?.status === 'unavailable')
|
|
153
254
|
continue;
|
|
154
|
-
|
|
155
|
-
if (now - record.lastUsedAt > WARM_WINDOW_MS)
|
|
255
|
+
if (now - record.lastUsedAt > cfg.warmWindowMs)
|
|
156
256
|
continue;
|
|
157
257
|
const priority = scoreWarmupPriority(record, entry);
|
|
158
|
-
if (priority > 0.1)
|
|
159
|
-
|
|
160
|
-
provider: record.provider,
|
|
161
|
-
model: record.model,
|
|
162
|
-
priority,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
258
|
+
if (priority > 0.1)
|
|
259
|
+
hot.push({ provider: record.provider, model: record.model, priority });
|
|
165
260
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
261
|
+
hot.sort((a, b) => b.priority - a.priority);
|
|
262
|
+
const toWarm = hot.slice(0, cfg.hotPerCycle);
|
|
263
|
+
result.skipped = hot.length - toWarm.length;
|
|
264
|
+
// ── Half 2: never-verified models (the pool the router cannot see) ──────
|
|
265
|
+
const exploration = selectExplorationCandidates(registry, configManager, now, cfg.explorePerCycle);
|
|
266
|
+
const exploreKeys = new Set(exploration.map((c) => usageKey(c.provider, c.model)));
|
|
267
|
+
const cycle = [...toWarm, ...exploration];
|
|
268
|
+
if (cycle.length === 0)
|
|
269
|
+
return result;
|
|
270
|
+
logger.debug(`[warmup] ${toWarm.length} hot · ${exploration.length} unverified candidates`);
|
|
271
|
+
for (const candidate of cycle) {
|
|
272
|
+
const key = usageKey(candidate.provider, candidate.model);
|
|
174
273
|
try {
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
|
|
274
|
+
const outcome = await spotCheck(candidate.provider, candidate.model, configManager);
|
|
275
|
+
if (outcome === 'verified')
|
|
276
|
+
result.verified += 1;
|
|
277
|
+
if (exploreKeys.has(key))
|
|
278
|
+
result.explored += 1;
|
|
279
|
+
else
|
|
280
|
+
result.warmed += 1;
|
|
281
|
+
if (outcome === 'verified') {
|
|
179
282
|
const record = usageMap.get(key);
|
|
180
|
-
if (record)
|
|
283
|
+
if (record)
|
|
181
284
|
record.lastUsedAt = now; // Reset the clock
|
|
182
|
-
}
|
|
183
285
|
}
|
|
184
286
|
}
|
|
185
287
|
catch {
|
|
186
288
|
// Best-effort — warmup must never break routing
|
|
187
289
|
}
|
|
188
290
|
}
|
|
291
|
+
return result;
|
|
189
292
|
}
|
|
190
293
|
// ─── Statistics ─────────────────────────────────────────────────────────────
|
|
191
294
|
/**
|
|
@@ -193,14 +296,15 @@ async function runWarmupCycle(configManager) {
|
|
|
193
296
|
*/
|
|
194
297
|
export function getWarmupStats() {
|
|
195
298
|
const now = Date.now();
|
|
299
|
+
const cfg = warmupConfig();
|
|
196
300
|
const records = Array.from(usageMap.values());
|
|
197
301
|
let hotModels = 0;
|
|
198
302
|
let warmModels = 0;
|
|
199
303
|
for (const record of records) {
|
|
200
304
|
const timeSinceUse = now - record.lastUsedAt;
|
|
201
|
-
if (timeSinceUse <
|
|
305
|
+
if (timeSinceUse < cfg.hotWindowMs)
|
|
202
306
|
hotModels++;
|
|
203
|
-
else if (timeSinceUse <
|
|
307
|
+
else if (timeSinceUse < cfg.warmWindowMs)
|
|
204
308
|
warmModels++;
|
|
205
309
|
}
|
|
206
310
|
return {
|
|
@@ -215,12 +319,13 @@ export function getWarmupStats() {
|
|
|
215
319
|
*/
|
|
216
320
|
export function getTrackedModels() {
|
|
217
321
|
const now = Date.now();
|
|
322
|
+
const cfg = warmupConfig();
|
|
218
323
|
return Array.from(usageMap.values()).map(record => {
|
|
219
324
|
const timeSinceUse = now - record.lastUsedAt;
|
|
220
325
|
let status = 'cold';
|
|
221
|
-
if (timeSinceUse <
|
|
326
|
+
if (timeSinceUse < cfg.hotWindowMs)
|
|
222
327
|
status = 'hot';
|
|
223
|
-
else if (timeSinceUse <
|
|
328
|
+
else if (timeSinceUse < cfg.warmWindowMs)
|
|
224
329
|
status = 'warm';
|
|
225
330
|
return {
|
|
226
331
|
provider: record.provider,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-warmup.js","sourceRoot":"","sources":["../../src/learning/model-warmup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,gBAAgB,EAA2B,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"model-warmup.js","sourceRoot":"","sources":["../../src/learning/model-warmup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt layers — deterministic splitting of a composed prompt into the
|
|
3
|
+
* layers that matter for review (enterprise hardening, session 3).
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS (the prompt audit of the last 30 traces):
|
|
6
|
+
* The trace stored ONE flat `promptPreview` capped at 300–500 chars, and for
|
|
7
|
+
* chat turns the preview deliberately shortened the system prompt to its first
|
|
8
|
+
* 80 characters. Result: in 106/106 chat steps the single most important
|
|
9
|
+
* artifact — the system prompt (persona, tool contract, response rules) — was
|
|
10
|
+
* INVISIBLE, and `promptDigest` hashed the whole thread so it could not show
|
|
11
|
+
* whether the stable layer was byte-stable (i.e. cacheable). Prompt
|
|
12
|
+
* engineering was, in practice, unreviewable.
|
|
13
|
+
*
|
|
14
|
+
* This module answers three questions from a flat prompt string, with no LLM
|
|
15
|
+
* and no caller changes:
|
|
16
|
+
* 1. Which bytes are the STABLE layer (identity / contract)?
|
|
17
|
+
* 2. Which are CONTEXT (project knowledge, recalled state, history, tools)?
|
|
18
|
+
* 3. Which are VOLATILE (the current task / ask)?
|
|
19
|
+
*
|
|
20
|
+
* Two transports are recognised because the codebase composes prompts two ways:
|
|
21
|
+
* - the chat thread serialisation: `[System]` / `[User]` / `[Assistant]` /
|
|
22
|
+
* `[Tool result]` markers (see `chat.ts` callModelWithTrace);
|
|
23
|
+
* - the layered assembler: `# Project Context` … `## Task` headings
|
|
24
|
+
* (see `agents/prompt-assembly.ts` assemblePrompt).
|
|
25
|
+
* Anything else falls back to a single `system` layer so a digest still detects
|
|
26
|
+
* change even when the shape is unknown.
|
|
27
|
+
*/
|
|
28
|
+
export interface PromptLayers {
|
|
29
|
+
/** Stable layer — identity, tool contract, tool guidance. */
|
|
30
|
+
system: string;
|
|
31
|
+
/** Context layer — project assessment, knowledge, recalled state, history. */
|
|
32
|
+
context: string;
|
|
33
|
+
/** Volatile layer — the current task / ask. */
|
|
34
|
+
volatile: string;
|
|
35
|
+
}
|
|
36
|
+
/** Per-layer digest + size record stored on a trace step. */
|
|
37
|
+
export interface PromptLayerDigests {
|
|
38
|
+
systemDigest: string;
|
|
39
|
+
contextDigest: string;
|
|
40
|
+
volatileDigest: string;
|
|
41
|
+
systemChars: number;
|
|
42
|
+
contextChars: number;
|
|
43
|
+
volatileChars: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Split a composed prompt into its stable / context / volatile layers.
|
|
47
|
+
* Deterministic and transport-aware; never throws.
|
|
48
|
+
*/
|
|
49
|
+
export declare function splitPromptLayers(text: string): PromptLayers;
|
|
50
|
+
/**
|
|
51
|
+
* Per-layer digests + sizes. The three digests SEPARATELY answer the review
|
|
52
|
+
* question the flat digest could not: did the stable layer change between
|
|
53
|
+
* steps (a cache-busting regression), or only the volatile one (expected)?
|
|
54
|
+
*/
|
|
55
|
+
export declare function digestPromptLayers(layers: PromptLayers): PromptLayerDigests;
|
|
56
|
+
/** Convenience: split + digest in one call. */
|
|
57
|
+
export declare function digestPrompt(text: string): {
|
|
58
|
+
layers: PromptLayers;
|
|
59
|
+
digests: PromptLayerDigests;
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=prompt-layers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-layers.d.ts","sourceRoot":"","sources":["../../src/learning/prompt-layers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,6DAA6D;AAC7D,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AA+ED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAW5D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,kBAAkB,CAS3E;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAGhG"}
|