@routerlab/core 0.0.1

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/router.js ADDED
@@ -0,0 +1,201 @@
1
+ // router.ts — the routing engine. Given a task and a quality bar, pick the
2
+ // cheapest model that meets the bar and the caller's budget/latency caps.
3
+ //
4
+ // Differentiation from prior art:
5
+ //
6
+ // 1. Cost is grounded in *empirical* token economics, not offline
7
+ // tokenizer proxies. The atlas dataset (project 1 of this milestone)
8
+ // provides per-(provider, model, format) calibration between offline
9
+ // counters and empirical counts. RouterArena, RouteLLM, and other
10
+ // prior routers either use proxy "cost thresholds" in [0,1]
11
+ // (RouteLLM) or `offline_tokenizer_estimate * published_price`
12
+ // (RouterArena). Our cost line is `atlas_calibrated_token_count *
13
+ // published_price` — strictly more accurate.
14
+ //
15
+ // The atlas-grounded math lives in `cost.ts` (`estimateCost()`),
16
+ // imported below. `cost.ts` wraps tokenometer's empirical counters
17
+ // and atlas's per-(provider, model) calibration table; this router
18
+ // uses it for both the context-window check (`inputTokens`) and the
19
+ // budget check (`totalUsd`) so the two filters cannot drift.
20
+ //
21
+ // 2. Every decision returns a full triple — `chosen`, `fallbacks`, and
22
+ // `skipped` (with reasons). This makes the engine debuggable and
23
+ // reproducible: a downstream consumer can replay a decision and see
24
+ // exactly which constraints filtered each candidate. RouteLLM
25
+ // returns only a routing logit; LiteLLM's complexity router returns
26
+ // only a chosen model. Routerlab returns the full decision trace.
27
+ //
28
+ // 3. The default candidate pool is shipped *with* the package and is
29
+ // versioned alongside the engine in `candidates.json`. Callers can
30
+ // override it, but the default is reproducible and citable.
31
+ //
32
+ // No external network calls and no LLM invocations happen here. The
33
+ // engine produces decisions; the per-provider runners under `eval/runners`
34
+ // turn decisions into completions.
35
+ import candidatesData from "./candidates.json" with { type: "json" };
36
+ import { estimateCost } from "./cost.js";
37
+ // Phase 3: predictor reads from eval/results/quality_table.json when present
38
+ // and falls back to the seeded prior in quality_prior.ts otherwise. The
39
+ // `predictQuality` signature is intentionally unchanged so the router
40
+ // engine didn't need to be rewritten.
41
+ import { predictQuality } from "./quality_predictor.js";
42
+ const DEFAULT_CANDIDATES = Object.freeze(candidatesData.candidates.map((c) => Object.freeze({ ...c })));
43
+ /**
44
+ * Return the default candidate pool. Useful for callers that want to
45
+ * inspect the pool, filter it, or extend it before routing.
46
+ */
47
+ export function getDefaultCandidates() {
48
+ return DEFAULT_CANDIDATES;
49
+ }
50
+ /**
51
+ * Resolve which candidate pool to use for this request. Defaults to the
52
+ * shipped pool; callers can override with `request.candidates`.
53
+ */
54
+ function resolveCandidates(request) {
55
+ if (request.candidates !== undefined) {
56
+ return request.candidates;
57
+ }
58
+ return DEFAULT_CANDIDATES;
59
+ }
60
+ /**
61
+ * Validate a request and throw a descriptive error if it's malformed. We
62
+ * fail loud here so misconfigured callers see the bug immediately rather
63
+ * than getting a silently-bad routing decision.
64
+ */
65
+ function validateRequest(request) {
66
+ if (!Number.isFinite(request.qualityBar)) {
67
+ throw new Error("route(): qualityBar must be a finite number");
68
+ }
69
+ if (request.qualityBar < 0 || request.qualityBar > 1) {
70
+ throw new Error("route(): qualityBar must be in [0, 1]");
71
+ }
72
+ if (typeof request.prompt !== "string") {
73
+ throw new Error("route(): prompt must be a string");
74
+ }
75
+ if (request.maxCostUsd !== undefined &&
76
+ (!Number.isFinite(request.maxCostUsd) || request.maxCostUsd < 0)) {
77
+ throw new Error("route(): maxCostUsd must be a non-negative finite number");
78
+ }
79
+ if (request.maxLatencyMs !== undefined &&
80
+ (!Number.isFinite(request.maxLatencyMs) || request.maxLatencyMs < 0)) {
81
+ throw new Error("route(): maxLatencyMs must be a non-negative finite number");
82
+ }
83
+ }
84
+ /**
85
+ * Filter the candidate pool by quality bar, budget, and context window.
86
+ * Skipped candidates are returned alongside survivors with a recorded
87
+ * reason — this is what makes routing decisions auditable.
88
+ */
89
+ function filterCandidates(request, pool) {
90
+ const kept = [];
91
+ const skipped = [];
92
+ for (const model of pool) {
93
+ const estimate = estimateCost({
94
+ prompt: request.prompt,
95
+ model: model.model,
96
+ provider: model.provider,
97
+ pricing: model.pricing,
98
+ taskClass: request.task,
99
+ });
100
+ if (estimate.inputTokens > model.contextWindow) {
101
+ skipped.push({
102
+ model,
103
+ reason: `prompt requires ~${estimate.inputTokens} input tokens, exceeds context window of ${model.contextWindow}`,
104
+ });
105
+ continue;
106
+ }
107
+ const expectedQuality = predictQuality(request.task, model.model);
108
+ if (expectedQuality < request.qualityBar) {
109
+ skipped.push({
110
+ model,
111
+ reason: `expected quality ${expectedQuality.toFixed(3)} for task "${request.task}" is below quality bar ${request.qualityBar.toFixed(3)}`,
112
+ });
113
+ continue;
114
+ }
115
+ if (request.maxCostUsd !== undefined && estimate.totalUsd > request.maxCostUsd) {
116
+ skipped.push({
117
+ model,
118
+ reason: `expected cost $${estimate.totalUsd.toFixed(6)} exceeds budget $${request.maxCostUsd.toFixed(6)}`,
119
+ });
120
+ continue;
121
+ }
122
+ kept.push({ model, expectedCost: estimate.totalUsd, expectedQuality });
123
+ }
124
+ return { kept, skipped };
125
+ }
126
+ const MAX_FALLBACKS = 3;
127
+ /**
128
+ * Compose the winning pick + fallback list from a sorted-by-cost array.
129
+ *
130
+ * Sort tie-breaker: when expected costs are equal, prefer the higher
131
+ * expected quality. This makes routing deterministic and biases toward
132
+ * better-quality picks at no extra cost — defensible default for a
133
+ * cost-first router.
134
+ */
135
+ function selectPickAndFallbacks(scored, request) {
136
+ const sorted = [...scored].sort((a, b) => {
137
+ if (a.expectedCost !== b.expectedCost) {
138
+ return a.expectedCost - b.expectedCost;
139
+ }
140
+ return b.expectedQuality - a.expectedQuality;
141
+ });
142
+ const winner = sorted[0];
143
+ if (winner === undefined) {
144
+ throw new Error("internal: selectPickAndFallbacks called with empty array");
145
+ }
146
+ const chosen = {
147
+ model: winner.model,
148
+ expectedCost: winner.expectedCost,
149
+ expectedQuality: winner.expectedQuality,
150
+ reasoning: buildReasoning(winner, sorted.length, request),
151
+ };
152
+ const fallbacks = sorted
153
+ .slice(1, 1 + MAX_FALLBACKS)
154
+ .map((c, idx) => ({
155
+ model: c.model,
156
+ reason: `cost-rank ${idx + 2} of ${sorted.length} qualifying candidates: expected cost $${c.expectedCost.toFixed(6)}, expected quality ${c.expectedQuality.toFixed(3)}`,
157
+ }));
158
+ return { chosen, fallbacks };
159
+ }
160
+ function buildReasoning(winner, poolSize, request) {
161
+ return [
162
+ `cheapest model meeting quality bar ${request.qualityBar.toFixed(3)} for task "${request.task}"`,
163
+ `of ${poolSize} qualifying candidates`,
164
+ `expected cost $${winner.expectedCost.toFixed(6)} (atlas-grounded estimator)`,
165
+ `expected quality ${winner.expectedQuality.toFixed(3)} (Phase 3 predictor; measured cells when available, seeded prior otherwise)`,
166
+ ].join("; ");
167
+ }
168
+ /**
169
+ * The public routing entrypoint.
170
+ *
171
+ * Pipeline:
172
+ * 1. Validate the request.
173
+ * 2. Resolve the candidate pool (caller override > shipped default).
174
+ * 3. Filter out candidates that fail the quality bar, the cost budget,
175
+ * or the model's context window. Record reasons.
176
+ * 4. Sort survivors by expected cost ascending (quality breaks ties).
177
+ * 5. Pick the cheapest survivor as `chosen`; the next three as
178
+ * `fallbacks`. Anything below that is in `skipped` only if it failed
179
+ * a constraint — extra cheap-survivors past the fallback list are
180
+ * simply not returned.
181
+ * 6. If no candidate survives, throw with the full skipped list so the
182
+ * caller can see exactly what went wrong.
183
+ *
184
+ * Sync return: this function does no I/O. Returning a promise would be
185
+ * misleading.
186
+ */
187
+ export function route(request) {
188
+ validateRequest(request);
189
+ const pool = resolveCandidates(request);
190
+ if (pool.length === 0) {
191
+ throw new Error("route(): candidate pool is empty; pass `candidates` in the request or use the default pool");
192
+ }
193
+ const { kept, skipped } = filterCandidates(request, pool);
194
+ if (kept.length === 0) {
195
+ const reasons = skipped.map((s) => ` - ${s.model.model}: ${s.reason}`).join("\n");
196
+ throw new Error(`route(): no candidates passed filtering for task "${request.task}" at quality bar ${request.qualityBar}.\nSkipped:\n${reasons}`);
197
+ }
198
+ const { chosen, fallbacks } = selectPickAndFallbacks(kept, request);
199
+ return { chosen, fallbacks, skipped };
200
+ }
201
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,0EAA0E;AAC1E,EAAE;AACF,kCAAkC;AAClC,EAAE;AACF,oEAAoE;AACpE,0EAA0E;AAC1E,0EAA0E;AAC1E,uEAAuE;AACvE,iEAAiE;AACjE,oEAAoE;AACpE,uEAAuE;AACvE,kDAAkD;AAClD,EAAE;AACF,sEAAsE;AACtE,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AACzE,kEAAkE;AAClE,EAAE;AACF,yEAAyE;AACzE,sEAAsE;AACtE,yEAAyE;AACzE,mEAAmE;AACnE,yEAAyE;AACzE,uEAAuE;AACvE,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,iEAAiE;AACjE,EAAE;AACF,oEAAoE;AACpE,2EAA2E;AAC3E,mCAAmC;AAEnC,OAAO,cAAc,MAAM,mBAAmB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,6EAA6E;AAC7E,wEAAwE;AACxE,sEAAsE;AACtE,sCAAsC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAkBxD,MAAM,kBAAkB,GAA8B,MAAM,CAAC,MAAM,CAChE,cAAgC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CACjF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAQD;;;GAGG;AACH,SAAS,iBAAiB,CAAC,OAAqB;IAC9C,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACrC,OAAO,OAAO,CAAC,UAAU,CAAC;IAC5B,CAAC;IACD,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,OAAqB;IAC5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,OAAO,CAAC,UAAU,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IACD,IACE,OAAO,CAAC,UAAU,KAAK,SAAS;QAChC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,UAAU,GAAG,CAAC,CAAC,EAChE,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IACD,IACE,OAAO,CAAC,YAAY,KAAK,SAAS;QAClC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,YAAY,GAAG,CAAC,CAAC,EACpE,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CACvB,OAAqB,EACrB,IAA+B;IAE/B,MAAM,IAAI,GAAsB,EAAE,CAAC;IACnC,MAAM,OAAO,GAAmB,EAAE,CAAC;IAEnC,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS,EAAE,OAAO,CAAC,IAAI;SACxB,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,WAAW,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC;gBACX,KAAK;gBACL,MAAM,EAAE,oBAAoB,QAAQ,CAAC,WAAW,4CAA4C,KAAK,CAAC,aAAa,EAAE;aAClH,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAClE,IAAI,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;YACzC,OAAO,CAAC,IAAI,CAAC;gBACX,KAAK;gBACL,MAAM,EAAE,oBAAoB,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,OAAO,CAAC,IAAI,0BAA0B,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;aAC1I,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;YAC/E,OAAO,CAAC,IAAI,CAAC;gBACX,KAAK;gBACL,MAAM,EAAE,kBAAkB,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;aAC1G,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,CAAC,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,aAAa,GAAG,CAAC,CAAC;AAExB;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAC7B,MAAyB,EACzB,OAAqB;IAErB,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvC,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,YAAY,EAAE,CAAC;YACtC,OAAO,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;QACzC,CAAC;QACD,OAAO,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,MAAM,GAAc;QACxB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,SAAS,EAAE,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;KAC1D,CAAC;IAEF,MAAM,SAAS,GAAoB,MAAM;SACtC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QAChB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,MAAM,EAAE,aAAa,GAAG,GAAG,CAAC,OAAO,MAAM,CAAC,MAAM,0CAA0C,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;KACxK,CAAC,CAAC,CAAC;IAEN,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/B,CAAC;AAED,SAAS,cAAc,CACrB,MAAuB,EACvB,QAAgB,EAChB,OAAqB;IAErB,OAAO;QACL,sCAAsC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,OAAO,CAAC,IAAI,GAAG;QAChG,MAAM,QAAQ,wBAAwB;QACtC,kBAAkB,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B;QAC7E,oBAAoB,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,6EAA6E;KACnI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,KAAK,CAAC,OAAqB;IACzC,eAAe,CAAC,OAAO,CAAC,CAAC;IAEzB,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,4FAA4F,CAC7F,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAE1D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnF,MAAM,IAAI,KAAK,CACb,qDAAqD,OAAO,CAAC,IAAI,oBAAoB,OAAO,CAAC,UAAU,gBAAgB,OAAO,EAAE,CACjI,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,sBAAsB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAEpE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The set of task classes routerlab routes for. Each class has its own
3
+ * quality prior table (see `quality_prior.ts`) and, downstream, its own
4
+ * Pareto frontier in `eval/results/frontier.json`.
5
+ *
6
+ * Keep this list small and stable — frontier reproducibility hinges on
7
+ * task-class identity surviving across runs.
8
+ */
9
+ export type TaskClass = "qa" | "codegen" | "summarization" | "classification" | "reasoning";
10
+ /**
11
+ * The providers we can route across. Mirrors the candidate pool documented
12
+ * in the routerlab README and the per-provider runners under `eval/runners/`.
13
+ */
14
+ export type Provider = "anthropic" | "openai" | "google" | "groq" | "together" | "hf" | "openrouter";
15
+ /**
16
+ * Per-million-token pricing for a model. Routerlab is cost-first, so this
17
+ * is the load-bearing field: every routing decision sorts on a cost
18
+ * computed from these numbers × atlas-grounded token estimates.
19
+ */
20
+ export interface ModelPricing {
21
+ inputUsdPerMtok: number;
22
+ outputUsdPerMtok: number;
23
+ }
24
+ /**
25
+ * A single routable model. The pool of these is what `route()` searches
26
+ * over. Callers can override the default pool via `RouteRequest.candidates`.
27
+ */
28
+ export interface ModelCandidate {
29
+ provider: Provider;
30
+ /** Model identifier, e.g. "claude-haiku-4-5", "llama-3.3-70b". */
31
+ model: string;
32
+ pricing: ModelPricing;
33
+ /** Maximum context window in tokens. */
34
+ contextWindow: number;
35
+ }
36
+ /**
37
+ * A routing request. `task` and `qualityBar` are required; everything else
38
+ * is optional and fills in sensible defaults.
39
+ *
40
+ * `qualityBar` is a value in [0,1] interpreted as "minimum acceptable
41
+ * expected quality on this task class." Candidates below the bar are
42
+ * filtered out before cost-sorting.
43
+ */
44
+ export interface RouteRequest {
45
+ task: TaskClass;
46
+ prompt: string;
47
+ /** 0..1 — minimum acceptable expected quality. */
48
+ qualityBar: number;
49
+ /** Hard budget cap per request, in USD. */
50
+ maxCostUsd?: number;
51
+ /** Hard latency cap per request, in milliseconds. */
52
+ maxLatencyMs?: number;
53
+ /** Override the default candidate pool. */
54
+ candidates?: ModelCandidate[];
55
+ }
56
+ /**
57
+ * A picked model with the engine's reasoning trace attached.
58
+ *
59
+ * `expectedCost` is in USD; `expectedQuality` is in [0,1].
60
+ */
61
+ export interface RoutePick {
62
+ model: ModelCandidate;
63
+ expectedCost: number;
64
+ expectedQuality: number;
65
+ /** Human-readable explanation of why this model won. */
66
+ reasoning: string;
67
+ }
68
+ /**
69
+ * A non-chosen candidate retained as a fallback (e.g. for retries on
70
+ * provider failure). Sorted by ascending expected cost.
71
+ */
72
+ export interface RouteFallback {
73
+ model: ModelCandidate;
74
+ reason: string;
75
+ }
76
+ /**
77
+ * A candidate filtered out before cost-sorting, with the failing constraint
78
+ * recorded. This is what makes routerlab debuggable — every rejection has
79
+ * a documented cause.
80
+ */
81
+ export interface RouteSkipped {
82
+ model: ModelCandidate;
83
+ reason: string;
84
+ }
85
+ /**
86
+ * The full return value of `route()`. The triple (chosen, fallbacks,
87
+ * skipped) is a complete decision record: callers can reproduce why a
88
+ * particular model was picked from this object alone.
89
+ */
90
+ export interface RouteDecision {
91
+ chosen: RoutePick;
92
+ fallbacks: RouteFallback[];
93
+ skipped: RouteSkipped[];
94
+ }
95
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GACjB,IAAI,GACJ,SAAS,GACT,eAAe,GACf,gBAAgB,GAChB,WAAW,CAAC;AAEhB;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAChB,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,UAAU,GACV,IAAI,GACJ,YAAY,CAAC;AAEjB;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,CAAC;IACtB,wCAAwC;IACxC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,cAAc,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB"}
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ // @routerlab/core — public type surface for the routing engine.
2
+ //
3
+ // These types are intentionally narrow and dependency-free. They describe
4
+ // what a caller passes in to `route()` and what comes back. Everything else
5
+ // (cost computation, quality prediction, candidate pool composition) is an
6
+ // internal implementation detail of the engine.
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,2EAA2E;AAC3E,gDAAgD"}
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@routerlab/core",
3
+ "version": "0.0.1",
4
+ "description": "Routing engine for cost-quality LLM model selection.",
5
+ "license": "Apache-2.0",
6
+ "author": "Faraazuddin Mohammed",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/faraa2m/routerlab.git",
18
+ "directory": "packages/core"
19
+ },
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.json",
22
+ "test": "bun test"
23
+ },
24
+ "dependencies": {
25
+ "@tokenometer/core": "1.0.1"
26
+ }
27
+ }