@qadanm/opencode-smart-router 0.0.0-stage → 0.4.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/CHANGELOG.md +118 -0
- package/CONTRIBUTING.md +96 -0
- package/LICENSE +21 -0
- package/README.md +341 -2
- package/SECURITY.md +174 -0
- package/config.example.json +108 -0
- package/dist/allowance.js +487 -0
- package/dist/gui-experimental.cjs +1 -0
- package/dist/index.js +11111 -0
- package/dist/npm/index.js +40 -0
- package/dist/tui.js +587 -0
- package/docs/ARCHITECTURE.md +104 -0
- package/docs/DEBUGGING-GUI.md +70 -0
- package/docs/INSTALL.md +120 -0
- package/docs/LIMITATIONS.md +213 -0
- package/docs/MODELS.md +124 -0
- package/docs/OPENDESIGN-INTEGRATION.md +635 -0
- package/docs/QA.md +142 -0
- package/docs/SHARED-CORE.md +72 -0
- package/install +75 -0
- package/package.json +92 -4
- package/scripts/install.sh +324 -0
- package/scripts/uninstall.sh +91 -0
- package/src/allowance.ts +135 -0
- package/src/core/classify/classifier.ts +305 -0
- package/src/core/config-writer.ts +167 -0
- package/src/core/config.ts +1030 -0
- package/src/core/context/strategy.ts +188 -0
- package/src/core/context-health.ts +39 -0
- package/src/core/failures.ts +398 -0
- package/src/core/history/opencode.ts +173 -0
- package/src/core/models/protocol.ts +22 -0
- package/src/core/models/registry.ts +203 -0
- package/src/core/models/seed.ts +771 -0
- package/src/core/offline/mode.ts +321 -0
- package/src/core/plan-page.ts +390 -0
- package/src/core/plans/catalog.ts +481 -0
- package/src/core/policy/calibration.ts +259 -0
- package/src/core/policy/eligibility.ts +578 -0
- package/src/core/providers/enablement.ts +143 -0
- package/src/core/providers/opendesign-credits.ts +206 -0
- package/src/core/providers/opendesign-models.ts +628 -0
- package/src/core/providers/opendesign.ts +474 -0
- package/src/core/quota/calibration.ts +166 -0
- package/src/core/quota/commandcode.ts +471 -0
- package/src/core/quota/credentials.ts +490 -0
- package/src/core/quota/ledger.ts +1273 -0
- package/src/core/quota/opencode-go.ts +148 -0
- package/src/core/quota/opendesign.ts +1043 -0
- package/src/core/quota/reconcile.ts +155 -0
- package/src/core/quota/report.ts +261 -0
- package/src/core/quota/service.ts +273 -0
- package/src/core/reprompt.ts +48 -0
- package/src/core/routing/engine.ts +919 -0
- package/src/core/types.ts +825 -0
- package/src/core/ui.ts +236 -0
- package/src/core/util.ts +114 -0
- package/src/gui/index.tsx +1670 -0
- package/src/gui/viewmodel.ts +456 -0
- package/src/index.ts +48 -0
- package/src/rpc.ts +204 -0
- package/src/server/controller.ts +3281 -0
- package/src/server/route-tracker.ts +60 -0
- package/src/server/shared-core.ts +671 -0
- package/src/tui/index.tsx +168 -0
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Mode,
|
|
3
|
+
ModelQuotaSnapshot,
|
|
4
|
+
ModelRecord,
|
|
5
|
+
PremiumState,
|
|
6
|
+
ProviderQuotaSnapshot,
|
|
7
|
+
QuotaWindow,
|
|
8
|
+
RouterSettings,
|
|
9
|
+
SessionState,
|
|
10
|
+
TaskClass,
|
|
11
|
+
} from "../types.js"
|
|
12
|
+
import { keyOfRef, modelKey, pct } from "../util.js"
|
|
13
|
+
import {
|
|
14
|
+
CONTEXT_ELIGIBILITY_MARGIN_TOKENS,
|
|
15
|
+
CONTEXT_SAFE_WINDOW_FRACTION,
|
|
16
|
+
replyMarginFor,
|
|
17
|
+
} from "../context/strategy.js"
|
|
18
|
+
import { OFFLINE_PROVIDER_ID } from "../offline/mode.js"
|
|
19
|
+
|
|
20
|
+
export interface PremiumDecision {
|
|
21
|
+
readonly allowed: boolean
|
|
22
|
+
readonly requiresApproval: boolean
|
|
23
|
+
readonly reason: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const decidePremium = (input: {
|
|
27
|
+
settings: RouterSettings
|
|
28
|
+
mode: Mode
|
|
29
|
+
task: TaskClass
|
|
30
|
+
session?: SessionState
|
|
31
|
+
premium: PremiumState
|
|
32
|
+
}): PremiumDecision => {
|
|
33
|
+
const { settings, mode, task, session, premium } = input
|
|
34
|
+
const premiumTask = task.tier === 3 || task.constraints.requestPremium
|
|
35
|
+
if (!premiumTask) {
|
|
36
|
+
return { allowed: false, requiresApproval: false, reason: "Premium models are reserved for premium-tier work." }
|
|
37
|
+
}
|
|
38
|
+
if (settings.premium.mode === "off") {
|
|
39
|
+
return { allowed: false, requiresApproval: false, reason: "Premium routing is off in settings." }
|
|
40
|
+
}
|
|
41
|
+
if (mode === "efficiency") {
|
|
42
|
+
return { allowed: false, requiresApproval: false, reason: "Maximum Efficiency locks premium models unless you select one manually." }
|
|
43
|
+
}
|
|
44
|
+
if (session?.premiumOneShot && settings.premium.oneShotApproval) {
|
|
45
|
+
return { allowed: true, requiresApproval: false, reason: "A one-shot premium approval is active for this session." }
|
|
46
|
+
}
|
|
47
|
+
if (mode === "premium" && settings.premium.mode === "allowed" && premium.remainingUsd > 0) {
|
|
48
|
+
return {
|
|
49
|
+
allowed: true,
|
|
50
|
+
requiresApproval: false,
|
|
51
|
+
reason: "Premium Allowed mode has a remaining premium budget.",
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (premium.remainingUsd > 0 && settings.premium.mode === "allowed") {
|
|
55
|
+
return { allowed: false, requiresApproval: true, reason: "Premium is available but requires this session's approval." }
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
allowed: false,
|
|
59
|
+
requiresApproval: true,
|
|
60
|
+
reason: "Premium escalation is disabled or has no remaining budget; approval is required.",
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface EligibilityInput {
|
|
65
|
+
readonly model: ModelRecord
|
|
66
|
+
readonly task: TaskClass
|
|
67
|
+
readonly settings: RouterSettings
|
|
68
|
+
readonly mode: Mode
|
|
69
|
+
readonly contextTokens: number
|
|
70
|
+
readonly providerQuota?: ProviderQuotaSnapshot
|
|
71
|
+
/**
|
|
72
|
+
* Whether any provider reported fresh quota for this decision. Lets the
|
|
73
|
+
* eligibility check tell "this provider is genuinely fine" apart from "we have
|
|
74
|
+
* not probed anyone yet", which are not the same thing.
|
|
75
|
+
*/
|
|
76
|
+
readonly quotaIsKnown?: boolean
|
|
77
|
+
readonly modelQuota?: ModelQuotaSnapshot
|
|
78
|
+
readonly premium: PremiumDecision
|
|
79
|
+
readonly currentModelKey?: string
|
|
80
|
+
readonly allowDesignRelaxation?: boolean
|
|
81
|
+
/** Estimated cost of the request, used by the subscription-only guard. */
|
|
82
|
+
readonly projectedUsd?: number
|
|
83
|
+
/**
|
|
84
|
+
* True when an ordinary-allowance route can serve this task. A model funded
|
|
85
|
+
* from an escalation pool is then refused, so routine work cannot drain the
|
|
86
|
+
* capacity reserved for hard problems.
|
|
87
|
+
*/
|
|
88
|
+
readonly escalationPoolProtected?: boolean
|
|
89
|
+
/**
|
|
90
|
+
* The intelligence floor to enforce, when it differs from the configured one.
|
|
91
|
+
*
|
|
92
|
+
* Calibration derives the floor from recorded spend, so the gate cannot read
|
|
93
|
+
* `settings.routing.intelligenceFloor` directly: that value is the ceiling
|
|
94
|
+
* calibration is bounded by, not necessarily the value in force. Passing it
|
|
95
|
+
* explicitly keeps one source of truth for the request in progress.
|
|
96
|
+
*/
|
|
97
|
+
readonly intelligenceFloor?: number
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface EligibilityResult {
|
|
101
|
+
readonly eligible: boolean
|
|
102
|
+
readonly reason: string
|
|
103
|
+
readonly warnings: string[]
|
|
104
|
+
/**
|
|
105
|
+
* Why the model was rejected, when that reason is worth reporting to a user.
|
|
106
|
+
* `context` is the case that matters: the model is a good route but the
|
|
107
|
+
* transcript no longer fits, which is a different situation from being
|
|
108
|
+
* unqualified or over budget.
|
|
109
|
+
*/
|
|
110
|
+
readonly rejectedFor?: "context"
|
|
111
|
+
readonly contextRequiredTokens?: number
|
|
112
|
+
readonly contextWindowTokens?: number
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const providerAuthFailure = (snapshot: ProviderQuotaSnapshot | undefined): boolean => {
|
|
116
|
+
if (!snapshot?.error) return false
|
|
117
|
+
return /authoriz|authenticat|unauthorized|forbidden|401|403|credential/i.test(snapshot.error)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Decide whether a provider's allowance is spent.
|
|
122
|
+
*
|
|
123
|
+
* `quotaIsKnown` says whether ANY provider reported fresh quota this decision.
|
|
124
|
+
* That distinction matters: when every provider is unknown we are blind and must
|
|
125
|
+
* not change behaviour, but when some providers have fresh data and this one does
|
|
126
|
+
* not, the asymmetry is a hole. An exhausted provider whose snapshot has not been
|
|
127
|
+
* probed yet looks identical to one that is fine, so a decision made in the ~1.5s
|
|
128
|
+
* before the quota service starts can route straight into a full allowance. That
|
|
129
|
+
* is exactly what happened on 2026-11-02 when GOAT's five-hour window was at 100%.
|
|
130
|
+
*/
|
|
131
|
+
const providerLimitReached = (
|
|
132
|
+
snapshot: ProviderQuotaSnapshot | undefined,
|
|
133
|
+
settings: RouterSettings,
|
|
134
|
+
providerID = "",
|
|
135
|
+
quotaIsKnown = false,
|
|
136
|
+
): string | undefined => {
|
|
137
|
+
if (!snapshot) {
|
|
138
|
+
// Fail CLOSED only in the one case that is actually a hole: this provider is
|
|
139
|
+
// configured to be probed, another provider already has a real reading, and
|
|
140
|
+
// this one has none yet. That is the window before the quota service starts
|
|
141
|
+
// (~1.5s after plugin load), where an exhausted provider is indistinguishable
|
|
142
|
+
// from a healthy one and a decision can route straight into a full allowance.
|
|
143
|
+
//
|
|
144
|
+
// A provider that is simply not probed is NOT an error: absence from the
|
|
145
|
+
// array is how "we do not track this one" is expressed, and refusing there
|
|
146
|
+
// would break legitimate fallbacks to providers with no quota model at all.
|
|
147
|
+
const providerSettings = settings.providers[providerID]
|
|
148
|
+
const expectedProbe = providerSettings?.enabled === true && providerSettings?.probeUsage === true
|
|
149
|
+
if (expectedProbe && quotaIsKnown && settings.routing?.failClosed !== false) {
|
|
150
|
+
return "Provider quota has not been probed yet; refusing rather than assuming it has room"
|
|
151
|
+
}
|
|
152
|
+
return undefined
|
|
153
|
+
}
|
|
154
|
+
const providerSettings = settings.providers[snapshot.providerID]
|
|
155
|
+
if (!providerSettings) return undefined
|
|
156
|
+
const reserveMonthly = providerSettings.reserveMonthlyPct
|
|
157
|
+
const reserveWeekly = providerSettings.reserveWeeklyPct
|
|
158
|
+
const reserveFive = providerSettings.reserveFiveHourPct
|
|
159
|
+
const extras = snapshot.extraCredits?.totalUsd ?? 0
|
|
160
|
+
// A refusal already outlives the reading that produced it: `exceeded` is
|
|
161
|
+
// checked before anything looks at `stale`, so an allowed-out snapshot keeps
|
|
162
|
+
// blocking until a fresh probe clears the flag. `stale` only softens COST
|
|
163
|
+
// (see providerPressure); it never softens a refusal. Verified by reverting a
|
|
164
|
+
// special-case branch for stale+exceeded and watching the tests still pass,
|
|
165
|
+
// which is how that branch was proved dead and removed.
|
|
166
|
+
const windowExceeded = Boolean(snapshot.monthly.exceeded || snapshot.weekly.exceeded || snapshot.rolling.exceeded)
|
|
167
|
+
if (windowExceeded) {
|
|
168
|
+
if (extras > 0 && providerSettings.allowExtraUsage) return undefined
|
|
169
|
+
if (extras > 0) return "Provider would spend purchased credits; automatic extra-credit use is disabled"
|
|
170
|
+
return "Provider allowance is exhausted"
|
|
171
|
+
}
|
|
172
|
+
// Some providers report a zero remaining balance slightly before they set
|
|
173
|
+
// the window's exceeded flag. Treat the balance as authoritative so a
|
|
174
|
+
// request cannot knowingly roll into purchased credits.
|
|
175
|
+
if (snapshot.monthlyRemainingUsd !== undefined && snapshot.monthlyRemainingUsd <= 0) {
|
|
176
|
+
if (extras > 0 && providerSettings.allowExtraUsage) return undefined
|
|
177
|
+
if (extras > 0) return "Provider included credits are exhausted; automatic extra-credit use is disabled"
|
|
178
|
+
return "Provider allowance is exhausted"
|
|
179
|
+
}
|
|
180
|
+
if (snapshot.monthly.source !== "unknown" && snapshot.monthly.usedPct >= 100 - reserveMonthly) {
|
|
181
|
+
return "Provider monthly allowance is at or near its configured reserve"
|
|
182
|
+
}
|
|
183
|
+
if (snapshot.weekly.source !== "unknown" && snapshot.weekly.usedPct >= 100 - reserveWeekly) {
|
|
184
|
+
return "Provider weekly allowance is at or near its configured reserve"
|
|
185
|
+
}
|
|
186
|
+
if (snapshot.rolling.source !== "unknown" && snapshot.rolling.usedPct >= 100 - reserveFive) {
|
|
187
|
+
return "Provider five-hour allowance is at or near its configured reserve"
|
|
188
|
+
}
|
|
189
|
+
return undefined
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const modelLimitReached = (quota: ModelQuotaSnapshot | undefined, settings: RouterSettings): string | undefined => {
|
|
193
|
+
if (!quota) return undefined
|
|
194
|
+
const provider = settings.providers[quota.model.providerID]
|
|
195
|
+
if (!provider) return undefined
|
|
196
|
+
if (quota.monthlyAllowanceUsd > 0 && quota.monthlyUsedPct >= 100 - provider.reserveMonthlyPct) {
|
|
197
|
+
return "Estimated model monthly allowance is at or near its configured reserve"
|
|
198
|
+
}
|
|
199
|
+
if (quota.fiveHourCapUsd > 0 && quota.fiveHourUsedPct >= 100 - provider.reserveFiveHourPct) {
|
|
200
|
+
return "Estimated model five-hour allowance is at or near its configured reserve"
|
|
201
|
+
}
|
|
202
|
+
if (quota.weeklyCapUsd > 0 && quota.weeklyUsedPct >= 100 - provider.reserveWeeklyPct) {
|
|
203
|
+
return "Estimated model weekly allowance is at or near its configured reserve"
|
|
204
|
+
}
|
|
205
|
+
if (quota.blocked) return quota.blockReason ?? "Estimated model allowance is exhausted"
|
|
206
|
+
return undefined
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Subscription-only guard.
|
|
211
|
+
*
|
|
212
|
+
* When the provider bills from a subscription allowance and purchased balance
|
|
213
|
+
* is off limits, a request is only routed when it fits inside what is left of
|
|
214
|
+
* the subscription. This is a client-side guard: OpenDesign's gateway makes
|
|
215
|
+
* the final funding decision per request, so the router must never assume it
|
|
216
|
+
* can prevent a server-side wallet charge. It only avoids sending a request
|
|
217
|
+
* that it can already see would not fit.
|
|
218
|
+
*/
|
|
219
|
+
const subscriptionOnlyLimit = (
|
|
220
|
+
snapshot: ProviderQuotaSnapshot | undefined,
|
|
221
|
+
settings: RouterSettings,
|
|
222
|
+
projectedUsd: number | undefined,
|
|
223
|
+
): string | undefined => {
|
|
224
|
+
if (!snapshot) return undefined
|
|
225
|
+
const providerSettings = settings.providers[snapshot.providerID]
|
|
226
|
+
if (!providerSettings?.subscriptionOnly) return undefined
|
|
227
|
+
if (projectedUsd === undefined || !Number.isFinite(projectedUsd) || projectedUsd <= 0) return undefined
|
|
228
|
+
|
|
229
|
+
const windows: ReadonlyArray<{ readonly label: string; readonly window: QuotaWindow }> = [
|
|
230
|
+
{ label: "five-hour", window: snapshot.rolling },
|
|
231
|
+
{ label: "weekly", window: snapshot.weekly },
|
|
232
|
+
{ label: "monthly", window: snapshot.monthly },
|
|
233
|
+
]
|
|
234
|
+
for (const { label, window } of windows) {
|
|
235
|
+
if (window.source === "unknown") continue
|
|
236
|
+
const cap = window.capUsd
|
|
237
|
+
if (cap === undefined || cap <= 0) continue
|
|
238
|
+
const used = window.usedUsd ?? 0
|
|
239
|
+
const remaining = Math.max(0, cap - used)
|
|
240
|
+
if (projectedUsd > remaining) {
|
|
241
|
+
return `Subscription-only policy: the projected request cost ($${projectedUsd.toFixed(4)}) exceeds the remaining ${label} allowance ($${remaining.toFixed(2)})`
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return undefined
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Policy for a provider whose quota state could not be established.
|
|
249
|
+
*/
|
|
250
|
+
const unknownQuotaLimit = (
|
|
251
|
+
snapshot: ProviderQuotaSnapshot | undefined,
|
|
252
|
+
settings: RouterSettings,
|
|
253
|
+
): string | undefined => {
|
|
254
|
+
if (!snapshot) return undefined
|
|
255
|
+
const providerSettings = settings.providers[snapshot.providerID]
|
|
256
|
+
if (!providerSettings) return undefined
|
|
257
|
+
const state = snapshot.quotaState ?? (snapshot.source === "unknown" ? "unknown" : undefined)
|
|
258
|
+
if (state !== "unknown" && state !== "unavailable") return undefined
|
|
259
|
+
const policy = providerSettings.unknownQuotaPolicy ?? "route"
|
|
260
|
+
if (policy === "route") return undefined
|
|
261
|
+
if (policy === "ask") {
|
|
262
|
+
return "Provider quota state is unknown; the configured policy requires explicit approval before routing here"
|
|
263
|
+
}
|
|
264
|
+
return "Provider quota state is unknown; the configured policy blocks routing here"
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* True when the online intelligence floor should be enforced for this model.
|
|
269
|
+
*
|
|
270
|
+
* Offline mode deliberately exempts local models: the owner has chosen to work
|
|
271
|
+
* with what runs on the machine, and no model small enough to fit alongside a
|
|
272
|
+
* simulator scores at the hosted standard floor.
|
|
273
|
+
*/
|
|
274
|
+
const substanceFloorApplies = (settings: RouterSettings, model: ModelRecord): boolean => {
|
|
275
|
+
if (settings.offline?.enabled !== true) return true
|
|
276
|
+
return model.providerID !== OFFLINE_PROVIDER_ID
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Hard-requirement gate. A model is eligible only when every requirement is
|
|
281
|
+
* met; expected-cost ranking happens later and never overrides these rules.
|
|
282
|
+
*/
|
|
283
|
+
export const evaluateEligibility = (input: EligibilityInput): EligibilityResult => {
|
|
284
|
+
const { model, task, settings, mode, providerQuota, modelQuota, premium } = input
|
|
285
|
+
const warnings: string[] = []
|
|
286
|
+
const providerSettings = settings.providers[model.providerID]
|
|
287
|
+
if (!providerSettings?.enabled) return { eligible: false, reason: "Provider is disabled in router settings", warnings }
|
|
288
|
+
if (providerAuthFailure(providerQuota)) {
|
|
289
|
+
return { eligible: false, reason: "Provider authentication is failing; fix the connection before routing here", warnings }
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Approval is an online-policy concept: it means "may this model serve paid
|
|
294
|
+
* work alongside the hosted floor". A local model is excluded from that list
|
|
295
|
+
* on purpose so it never competes with hosted models when the network is up.
|
|
296
|
+
*
|
|
297
|
+
* Offline mode therefore skips this check for local models. The candidate
|
|
298
|
+
* list has already been restricted to them, so this cannot admit a hosted
|
|
299
|
+
* model that would otherwise be refused; it only stops an unapproved local
|
|
300
|
+
* model from being rejected for a reason that does not apply to it.
|
|
301
|
+
*/
|
|
302
|
+
const offlineLocal = settings.offline?.enabled === true && model.providerID === OFFLINE_PROVIDER_ID
|
|
303
|
+
if (!model.approved && !offlineLocal) {
|
|
304
|
+
return { eligible: false, reason: "Model is unclassified or not approved", warnings }
|
|
305
|
+
}
|
|
306
|
+
if (model.training && !settings.privacy.allowTrainingModels) {
|
|
307
|
+
return { eligible: false, reason: "Training-enabled model is blocked by privacy policy", warnings }
|
|
308
|
+
}
|
|
309
|
+
if (model.free && !settings.privacy.allowFreeModels) {
|
|
310
|
+
return { eligible: false, reason: "Free promotional model is blocked by privacy policy", warnings }
|
|
311
|
+
}
|
|
312
|
+
if (task.requiresVision && !model.inputModalities.includes("image")) {
|
|
313
|
+
return { eligible: false, reason: "Task includes an image and this model has no native image input", warnings }
|
|
314
|
+
}
|
|
315
|
+
if (!model.tools) {
|
|
316
|
+
return { eligible: false, reason: "Model does not support tool calling", warnings }
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// The margin and the safety fraction are shared with the compaction strategy
|
|
320
|
+
// rather than repeated as literals. They used to be written out here as
|
|
321
|
+
// `20_000` and `0.95`, which meant editing the constants in strategy.ts would
|
|
322
|
+
// move the compaction threshold while leaving this routing gate behind, so a
|
|
323
|
+
// session would compact at a different size than the router refuses to route
|
|
324
|
+
// at. For DeepSeek V4.1 Flash that boundary is 975,600 of an advertised
|
|
325
|
+
// 1,048,000 window, and the remaining 72,400 tokens are deliberate headroom
|
|
326
|
+
// for the reply, tokenizer differences, and transcript growth mid-loop.
|
|
327
|
+
const requiredContext =
|
|
328
|
+
Math.max(input.contextTokens, task.scope.promptTokens) + replyMarginFor(model.context)
|
|
329
|
+
if (model.context > 0 && requiredContext > model.context * CONTEXT_SAFE_WINDOW_FRACTION) {
|
|
330
|
+
return {
|
|
331
|
+
eligible: false,
|
|
332
|
+
reason: `Context requirement ${requiredContext.toLocaleString()} exceeds this model's safe window`,
|
|
333
|
+
warnings,
|
|
334
|
+
// Machine-readable so callers can explain *this* rejection without
|
|
335
|
+
// pattern-matching the message above.
|
|
336
|
+
rejectedFor: "context",
|
|
337
|
+
contextRequiredTokens: requiredContext,
|
|
338
|
+
contextWindowTokens: model.context,
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const substantive = task.tier >= 2
|
|
343
|
+
/**
|
|
344
|
+
* The intelligence floor exists so substantive work reaches a capable model.
|
|
345
|
+
* Offline mode is a deliberate exception: the owner has chosen to work with
|
|
346
|
+
* whatever runs locally, and enforcing the hosted floor would make offline
|
|
347
|
+
* mode permanently unusable, because no 4B model on this machine scores 39.5.
|
|
348
|
+
*
|
|
349
|
+
* The exception is scoped to local models only. A hosted model reached while
|
|
350
|
+
* offline is already refused upstream, so this cannot lower the floor for
|
|
351
|
+
* online routing.
|
|
352
|
+
*/
|
|
353
|
+
const offlineFloorApplies = substanceFloorApplies(settings, model)
|
|
354
|
+
const floor = input.intelligenceFloor ?? settings.routing.intelligenceFloor
|
|
355
|
+
if (substantive && offlineFloorApplies && model.intelligence < floor) {
|
|
356
|
+
return {
|
|
357
|
+
eligible: false,
|
|
358
|
+
reason: `Intelligence ${model.intelligence.toFixed(1)} is below the ${settings.routing.intelligenceFloor.toFixed(1)} floor for substantive work`,
|
|
359
|
+
warnings,
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (task.tier <= 1 && model.tier === 3) {
|
|
363
|
+
return { eligible: false, reason: "Premium models are not used for trivial or mechanical work", warnings }
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
let designFallbackThreshold: number | undefined
|
|
367
|
+
if (task.designIntensive) {
|
|
368
|
+
const threshold = task.sophisticatedDesign ? settings.routing.sophisticatedDesignFloor : settings.routing.designFloor
|
|
369
|
+
if (model.design < threshold) {
|
|
370
|
+
if (input.allowDesignRelaxation) {
|
|
371
|
+
designFallbackThreshold = threshold
|
|
372
|
+
warnings.push(
|
|
373
|
+
`Design capability ${model.design} is below the preferred ${threshold} threshold; selected as the best available fallback`,
|
|
374
|
+
)
|
|
375
|
+
} else {
|
|
376
|
+
return {
|
|
377
|
+
eligible: false,
|
|
378
|
+
reason: `Design capability ${model.design} is below the ${threshold} threshold`,
|
|
379
|
+
warnings,
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (model.premium) {
|
|
386
|
+
if (!premium.allowed) {
|
|
387
|
+
return { eligible: false, reason: premium.reason, warnings }
|
|
388
|
+
}
|
|
389
|
+
if (task.tier < 3 && !task.constraints.requestPremium) {
|
|
390
|
+
return { eligible: false, reason: "Premium models are reserved for premium-tier work", warnings }
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// An escalation pool is capacity, not routine allowance. When an
|
|
395
|
+
// ordinary-allowance route can already serve the task, spending the
|
|
396
|
+
// escalation pool on it converts a cheap request into a scarce one.
|
|
397
|
+
if (model.escalationOnly && input.escalationPoolProtected) {
|
|
398
|
+
return {
|
|
399
|
+
eligible: false,
|
|
400
|
+
reason:
|
|
401
|
+
"This model is funded from an escalation pool, and an ordinary-allowance route can serve this task",
|
|
402
|
+
warnings,
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const providerLimit = providerLimitReached(providerQuota, settings, model.providerID, input.quotaIsKnown === true)
|
|
407
|
+
if (providerLimit) return { eligible: false, reason: providerLimit, warnings }
|
|
408
|
+
const unknownLimit = unknownQuotaLimit(providerQuota, settings)
|
|
409
|
+
if (unknownLimit) return { eligible: false, reason: unknownLimit, warnings }
|
|
410
|
+
const subscriptionLimit = subscriptionOnlyLimit(providerQuota, settings, input.projectedUsd)
|
|
411
|
+
if (subscriptionLimit) return { eligible: false, reason: subscriptionLimit, warnings }
|
|
412
|
+
const modelLimit = modelLimitReached(modelQuota, settings)
|
|
413
|
+
if (modelLimit) return { eligible: false, reason: modelLimit, warnings }
|
|
414
|
+
|
|
415
|
+
// Maximum Efficiency mode prefers economy for qualifying work but still
|
|
416
|
+
// permits standard models when the task clears the standard floor.
|
|
417
|
+
if (mode === "efficiency" && task.tier >= 2 && model.tier === 1) {
|
|
418
|
+
return { eligible: false, reason: "Economy models cannot handle substantive engineering in Maximum Efficiency mode", warnings }
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (designFallbackThreshold !== undefined) {
|
|
422
|
+
return {
|
|
423
|
+
eligible: true,
|
|
424
|
+
reason: `Eligible only as a design fallback: design ${model.design} is below the ${designFallbackThreshold} threshold`,
|
|
425
|
+
warnings,
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return { eligible: true, reason: "Meets every hard requirement", warnings }
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* How close to its reset a window must be for its remaining allowance to count
|
|
434
|
+
* as at risk. Further out than this is ordinary quota, not a fire sale.
|
|
435
|
+
*/
|
|
436
|
+
const EXPIRY_HORIZON_MS = 24 * 60 * 60 * 1000
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Nominal duration of each quota window, used to turn "spent so far" into a
|
|
440
|
+
* burn rate.
|
|
441
|
+
*
|
|
442
|
+
* The providers report a reset time but not the window's start, so elapsed time
|
|
443
|
+
* is derived from the window's own length: a weekly pool that resets in 21 hours
|
|
444
|
+
* has been running for 147. This is a modelling assumption and it is the reason
|
|
445
|
+
* the waste estimate is a fraction rather than a certainty.
|
|
446
|
+
*/
|
|
447
|
+
const WINDOW_LENGTH_MS: Readonly<Record<"rolling" | "weekly" | "monthly", number>> = {
|
|
448
|
+
rolling: 5 * 60 * 60 * 1000,
|
|
449
|
+
weekly: 7 * 24 * 60 * 60 * 1000,
|
|
450
|
+
monthly: 30 * 24 * 60 * 60 * 1000,
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* The fraction of a pool's remaining allowance that is on track to expire unused.
|
|
455
|
+
*
|
|
456
|
+
* Zero means the window is being consumed fast enough that the next request
|
|
457
|
+
* displaces real work. One means the window will reset having used none of what
|
|
458
|
+
* it has left, so spending it costs nothing that was not already lost.
|
|
459
|
+
*
|
|
460
|
+
* Derived from the burn rate rather than from fill level alone. A pool at 87%
|
|
461
|
+
* with 21 hours left is not "about to waste its last 13%" if it has been burning
|
|
462
|
+
* 13% every 21 hours; a pool at 50% with three hours left of a week is.
|
|
463
|
+
*/
|
|
464
|
+
export const quotaWasteFraction = (
|
|
465
|
+
snapshot: ProviderQuotaSnapshot | undefined,
|
|
466
|
+
now: number,
|
|
467
|
+
): number => {
|
|
468
|
+
if (!snapshot) return 0
|
|
469
|
+
let worst = 0
|
|
470
|
+
for (const key of ["rolling", "weekly", "monthly"] as const) {
|
|
471
|
+
const window = snapshot[key]
|
|
472
|
+
if (!window || window.source === "unknown") continue
|
|
473
|
+
if (typeof window.resetsAt !== "number") continue
|
|
474
|
+
const remainingMs = window.resetsAt - now
|
|
475
|
+
// Already reset, or so far out that nothing is at risk today.
|
|
476
|
+
if (remainingMs <= 0 || remainingMs > EXPIRY_HORIZON_MS) continue
|
|
477
|
+
// Nothing has been committed to this pool, so there is nothing to recover.
|
|
478
|
+
if (window.usedPct <= 0) continue
|
|
479
|
+
// Without a dollar figure there is no burn rate to read, and guessing one
|
|
480
|
+
// from the percentage alone would assume a window length we were not told.
|
|
481
|
+
if (typeof window.usedUsd !== "number" || typeof window.capUsd !== "number" || window.capUsd <= 0) continue
|
|
482
|
+
|
|
483
|
+
const lengthMs = WINDOW_LENGTH_MS[key]
|
|
484
|
+
const elapsedMs = Math.max(0, lengthMs - remainingMs)
|
|
485
|
+
// Too early in the window for a burn rate to mean anything: a single request
|
|
486
|
+
// in the first minute would project to an absurd rate.
|
|
487
|
+
if (elapsedMs < 0.05 * lengthMs) continue
|
|
488
|
+
|
|
489
|
+
// This also covers a pool that is already out: with nothing left there is no
|
|
490
|
+
// marginal price to lower, and chasing the last percent would strand a task.
|
|
491
|
+
const remainingUsd = Math.max(0, window.capUsd - window.usedUsd)
|
|
492
|
+
if (remainingUsd <= 0) continue
|
|
493
|
+
const burnPerMs = window.usedUsd / elapsedMs
|
|
494
|
+
const projectedSpend = burnPerMs * remainingMs
|
|
495
|
+
worst = Math.max(worst, Math.min(1, Math.max(0, 1 - projectedSpend / remainingUsd)))
|
|
496
|
+
}
|
|
497
|
+
return worst
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* The factor to apply to an estimate for spending expiring prepaid allowance,
|
|
502
|
+
* where one means "this is fresh money and costs full price".
|
|
503
|
+
*
|
|
504
|
+
* This is the near-zero marginal pricing the flat pressure discount could not
|
|
505
|
+
* express. A dollar of prepaid allowance that is on track to be abandoned is
|
|
506
|
+
* worth about its own probability of being lost, so a provider about to waste
|
|
507
|
+
* its pool becomes genuinely competitive against a cheaper provider with no
|
|
508
|
+
* deadline.
|
|
509
|
+
*
|
|
510
|
+
* It is a floor rather than a zero on purpose: a provider priced at nothing
|
|
511
|
+
* would win every comparison regardless of how little headroom it had left, and
|
|
512
|
+
* a request that runs the pool dry mid-task is worse than paying full price
|
|
513
|
+
* elsewhere.
|
|
514
|
+
*/
|
|
515
|
+
export const quotaMarginalPriceFactor = (
|
|
516
|
+
snapshot: ProviderQuotaSnapshot | undefined,
|
|
517
|
+
now: number,
|
|
518
|
+
): number => {
|
|
519
|
+
if (!snapshot) return 1
|
|
520
|
+
// The pool must be able to carry real work. Where a window has headroom
|
|
521
|
+
// recorded, refuse to treat the last few percent as a bargain.
|
|
522
|
+
const tightest = [snapshot.rolling, snapshot.weekly, snapshot.monthly]
|
|
523
|
+
.filter((window) => window.source !== "unknown" && typeof window.resetsAt === "number")
|
|
524
|
+
if (tightest.length === 0) return 1
|
|
525
|
+
const usable = tightest.some((window) => window.remainingPct > 0)
|
|
526
|
+
if (!usable) return 1
|
|
527
|
+
const waste = quotaWasteFraction(snapshot, now)
|
|
528
|
+
if (waste <= 0) return 1
|
|
529
|
+
// Bounded well below one so expiring quota is cheap, never free.
|
|
530
|
+
return 1 - MARGINAL_PRICE_DISCOUNT_MAX * waste
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/** The most of an estimate that can be forgiven for expiring allowance. */
|
|
534
|
+
const MARGINAL_PRICE_DISCOUNT_MAX = 0.95
|
|
535
|
+
|
|
536
|
+
export const providerPressure = (snapshot: ProviderQuotaSnapshot | undefined, now = Date.now()): number => {
|
|
537
|
+
if (!snapshot) return 1.5
|
|
538
|
+
const allUnknown =
|
|
539
|
+
snapshot.monthly.source === "unknown" && snapshot.weekly.source === "unknown" && snapshot.rolling.source === "unknown"
|
|
540
|
+
if (allUnknown) return 1.5
|
|
541
|
+
const monthly = snapshot.monthly.source === "unknown" ? 0 : snapshot.monthly.usedPct
|
|
542
|
+
const weekly = snapshot.weekly.source === "unknown" ? 0 : snapshot.weekly.usedPct
|
|
543
|
+
const rolling = snapshot.rolling.source === "unknown" ? 0 : snapshot.rolling.usedPct
|
|
544
|
+
const base =
|
|
545
|
+
1 +
|
|
546
|
+
1.5 * (monthly / 100) ** 2 +
|
|
547
|
+
2.5 * (weekly / 100) ** 2 +
|
|
548
|
+
2.5 * (rolling / 100) ** 2 +
|
|
549
|
+
(snapshot.stale ? 0.15 : 0)
|
|
550
|
+
// Fill level only. Expiring allowance is priced where it belongs, in
|
|
551
|
+
// quotaMarginalPriceFactor: pressure is a multiplier on an already-ranked cost,
|
|
552
|
+
// so expressing urgency here could never close a price gap, and counting it in
|
|
553
|
+
// both places charged the same fact twice.
|
|
554
|
+
return base
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export const modelPressure = (quota: ModelQuotaSnapshot | undefined): number => {
|
|
558
|
+
if (!quota) return 1
|
|
559
|
+
return (
|
|
560
|
+
1 +
|
|
561
|
+
1.5 * (quota.monthlyUsedPct / 100) ** 2 +
|
|
562
|
+
1 * (quota.fiveHourUsedPct / 100) ** 2 +
|
|
563
|
+
1 * (quota.weeklyUsedPct / 100) ** 2
|
|
564
|
+
)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export const providerUtilization = (snapshot: ProviderQuotaSnapshot | undefined): number => {
|
|
568
|
+
if (!snapshot) return 0
|
|
569
|
+
return Math.max(
|
|
570
|
+
snapshot.monthly.source === "unknown" ? 0 : snapshot.monthly.usedPct,
|
|
571
|
+
snapshot.weekly.source === "unknown" ? 0 : snapshot.weekly.usedPct,
|
|
572
|
+
snapshot.rolling.source === "unknown" ? 0 : snapshot.rolling.usedPct,
|
|
573
|
+
)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export const modelKeyFor = (model: ModelRecord) => modelKey(model.providerID, model.modelID)
|
|
577
|
+
export const currentKey = (current: { providerID: string; id: string } | undefined) => keyOfRef(current)
|
|
578
|
+
export const utilizationPct = (used: number, cap: number) => pct(used, cap)
|