@juspay/neurolink 12.18.1 → 12.19.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 +3 -3
- package/README.md +277 -75
- package/dist/browser/neurolink.min.js +396 -396
- package/dist/cli/commands/setup.js +2 -1
- package/dist/cli/factories/commandFactory.d.ts +22 -2
- package/dist/cli/factories/commandFactory.js +14 -2
- package/dist/cli/loop/optionsSchema.js +4 -0
- package/dist/cli/utils/classifierRouterFlags.js +39 -1
- package/dist/constants/enums.d.ts +13 -0
- package/dist/constants/enums.js +14 -0
- package/dist/context/budgetChecker.d.ts +3 -1
- package/dist/context/budgetChecker.js +20 -4
- package/dist/context/contextCompactor.d.ts +43 -3
- package/dist/context/contextCompactor.js +157 -41
- package/dist/context/contextDecision.d.ts +56 -0
- package/dist/context/contextDecision.js +234 -0
- package/dist/core/baseProvider.d.ts +27 -1
- package/dist/core/baseProvider.js +39 -0
- package/dist/core/toolRouting.d.ts +8 -0
- package/dist/core/toolRouting.js +52 -1
- package/dist/core/toolRoutingDecision.d.ts +35 -0
- package/dist/core/toolRoutingDecision.js +194 -0
- package/dist/factories/providerDescriptors.d.ts +15 -0
- package/dist/factories/providerDescriptors.js +57 -1
- package/dist/factories/providerRegistry.js +9 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.js +60 -1
- package/dist/models/manifestRegistry.js +2 -0
- package/dist/models/manifests/typesafe.d.ts +16 -0
- package/dist/models/manifests/typesafe.js +42 -0
- package/dist/neurolink.d.ts +57 -1
- package/dist/neurolink.js +254 -10
- package/dist/observability/exporters/laminarExporter.js +3 -0
- package/dist/observability/exporters/posthogExporter.js +3 -0
- package/dist/observability/utils/spanSerializer.js +4 -0
- package/dist/providers/catalog/schema.d.ts +1 -1
- package/dist/providers/typesafe.d.ts +112 -0
- package/dist/providers/typesafe.js +654 -0
- package/dist/rag/pipeline/RAGPipeline.d.ts +3 -1
- package/dist/rag/pipeline/RAGPipeline.js +65 -4
- package/dist/rag/retrieval/searchDecision.d.ts +27 -0
- package/dist/rag/retrieval/searchDecision.js +124 -0
- package/dist/routing/classifierRouter.d.ts +74 -2
- package/dist/routing/classifierRouter.js +232 -32
- package/dist/routing/classifierStrategies.d.ts +33 -1
- package/dist/routing/classifierStrategies.js +237 -0
- package/dist/routing/index.d.ts +1 -1
- package/dist/routing/index.js +1 -1
- package/dist/routing/modelCatalog.d.ts +79 -0
- package/dist/routing/modelCatalog.js +382 -0
- package/dist/telemetry/tracers.d.ts +1 -0
- package/dist/telemetry/tracers.js +1 -0
- package/dist/types/classifierRouter.d.ts +160 -4
- package/dist/types/cli.d.ts +17 -1
- package/dist/types/config.d.ts +12 -0
- package/dist/types/context.d.ts +55 -1
- package/dist/types/decision.d.ts +200 -0
- package/dist/types/decision.js +21 -0
- package/dist/types/generate.d.ts +20 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/providers.d.ts +59 -1
- package/dist/types/providers.js +5 -0
- package/dist/types/rag.d.ts +48 -0
- package/dist/types/span.d.ts +11 -0
- package/dist/types/span.js +11 -0
- package/dist/types/stream.d.ts +13 -0
- package/dist/types/toolRouting.d.ts +56 -0
- package/dist/utils/decisionAnswers.d.ts +56 -0
- package/dist/utils/decisionAnswers.js +95 -0
- package/dist/utils/modelChoices.js +13 -1
- package/dist/utils/pricing.js +11 -0
- package/dist/utils/providerConfig.d.ts +4 -0
- package/dist/utils/providerConfig.js +17 -0
- package/docs-site/static/search-index.json +1 -1
- package/package.json +16 -3
|
@@ -32,6 +32,7 @@ import { InMemoryVectorStore } from "../retrieval/vectorQueryTool.js";
|
|
|
32
32
|
import { InMemoryBM25Index, createHybridSearch, } from "../retrieval/hybridSearch.js";
|
|
33
33
|
import { GraphRAG } from "../graphRag/graphRAG.js";
|
|
34
34
|
import { rerank } from "../reranker/reranker.js";
|
|
35
|
+
import { decideSearchPlan } from "../retrieval/searchDecision.js";
|
|
35
36
|
import { ProviderFactory } from "../../factories/providerFactory.js";
|
|
36
37
|
import { ImageLoader } from "../document/imageLoader.js";
|
|
37
38
|
import { ImageProcessor } from "../../utils/imageProcessor.js";
|
|
@@ -250,10 +251,53 @@ export class RAGPipeline {
|
|
|
250
251
|
try {
|
|
251
252
|
await this.ensureInitialized();
|
|
252
253
|
const startTime = Date.now();
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
let topK = options?.topK || this.config.defaultTopK || 5;
|
|
255
|
+
let useHybrid = options?.hybrid ?? this.config.enableHybridSearch;
|
|
256
|
+
let useGraph = options?.graph ?? this.config.enableGraphRAG;
|
|
257
|
+
let useRerank = options?.rerank ?? this.config.enableReranking;
|
|
258
|
+
// Per-query retrieval planning. The four knobs above are otherwise
|
|
259
|
+
// static config, identical for "what is the refund window?" and "how
|
|
260
|
+
// does billing relate to entitlements?" — one precise passage versus a
|
|
261
|
+
// survey across documents. A decision model answers all four for THIS
|
|
262
|
+
// query in one ~400ms round trip.
|
|
263
|
+
//
|
|
264
|
+
// Precedence is strict and one-directional: an explicit QueryOptions
|
|
265
|
+
// field always wins, and the plan can only select among capabilities
|
|
266
|
+
// this pipeline was configured with. It never enables one that is not
|
|
267
|
+
// set up, because `graph: true` on a pipeline with no graph index is a
|
|
268
|
+
// failure rather than a plan.
|
|
269
|
+
let plan = null;
|
|
270
|
+
if (this.config.decide && options?.plan !== false) {
|
|
271
|
+
try {
|
|
272
|
+
plan = await decideSearchPlan(query, this.config.decide, {
|
|
273
|
+
defaultTopK: topK,
|
|
274
|
+
canHybrid: !!this.config.enableHybridSearch && !!this.hybridSearch,
|
|
275
|
+
canGraph: !!this.config.enableGraphRAG,
|
|
276
|
+
canRerank: !!this.config.enableReranking && !!this.config.rerankingModel,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
catch (planError) {
|
|
280
|
+
logger.debug("[RAGPipeline] Search planning failed, using config", {
|
|
281
|
+
error: planError instanceof Error
|
|
282
|
+
? planError.message
|
|
283
|
+
: String(planError),
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (plan) {
|
|
288
|
+
if (options?.topK === undefined && plan.topK !== undefined) {
|
|
289
|
+
topK = plan.topK;
|
|
290
|
+
}
|
|
291
|
+
if (options?.hybrid === undefined && plan.hybrid !== undefined) {
|
|
292
|
+
useHybrid = plan.hybrid;
|
|
293
|
+
}
|
|
294
|
+
if (options?.graph === undefined && plan.graph !== undefined) {
|
|
295
|
+
useGraph = plan.graph;
|
|
296
|
+
}
|
|
297
|
+
if (options?.rerank === undefined && plan.rerank !== undefined) {
|
|
298
|
+
useRerank = plan.rerank;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
257
301
|
let results;
|
|
258
302
|
let retrievalMethod = "vector";
|
|
259
303
|
// Generate query embedding
|
|
@@ -338,6 +382,7 @@ export class RAGPipeline {
|
|
|
338
382
|
retrievalMethod,
|
|
339
383
|
chunksRetrieved: results.length,
|
|
340
384
|
reranked,
|
|
385
|
+
...(plan ? { plan } : {}),
|
|
341
386
|
},
|
|
342
387
|
};
|
|
343
388
|
span.durationMs = Date.now() - spanStartTime;
|
|
@@ -347,6 +392,21 @@ export class RAGPipeline {
|
|
|
347
392
|
"rag.retrieval_method": retrievalMethod,
|
|
348
393
|
"rag.results_count": results.length,
|
|
349
394
|
"rag.reranked": reranked,
|
|
395
|
+
// The EFFECTIVE knobs, after planning and caller overrides. The span
|
|
396
|
+
// recorded the configured values at creation; without these a reader
|
|
397
|
+
// cannot tell a planned query from an unplanned one, which is the
|
|
398
|
+
// whole question when a fail-open path stops firing.
|
|
399
|
+
"rag.effective_top_k": topK,
|
|
400
|
+
"rag.planned": plan !== null,
|
|
401
|
+
...(plan
|
|
402
|
+
? {
|
|
403
|
+
"rag.plan_model": plan.model,
|
|
404
|
+
"rag.plan_latency_ms": plan.latencyMs,
|
|
405
|
+
...(plan.breadthLevel !== undefined
|
|
406
|
+
? { "rag.plan_breadth": plan.breadthLevel }
|
|
407
|
+
: {}),
|
|
408
|
+
}
|
|
409
|
+
: {}),
|
|
350
410
|
};
|
|
351
411
|
getMetricsAggregator().recordSpan(endedSpan);
|
|
352
412
|
return response;
|
|
@@ -788,5 +848,6 @@ export function createRAGPipeline(options) {
|
|
|
788
848
|
enableHybridSearch: options.enableHybrid,
|
|
789
849
|
enableGraphRAG: options.enableGraph,
|
|
790
850
|
multiModal: options.multiModal,
|
|
851
|
+
decide: options.decide,
|
|
791
852
|
});
|
|
792
853
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-search retrieval planning.
|
|
3
|
+
*
|
|
4
|
+
* `RAGPipeline.query()` resolves four knobs — `topK`, `hybrid`, `graph`,
|
|
5
|
+
* `rerank` — and every one of them is config with an optional per-call
|
|
6
|
+
* override. Nothing inspects the query. The same plan is used for "what is
|
|
7
|
+
* the refund window?" (one precise passage, an exact phrase worth matching
|
|
8
|
+
* lexically) and "how does billing relate to entitlements?" (many passages,
|
|
9
|
+
* relationships between documents).
|
|
10
|
+
*
|
|
11
|
+
* One decision request answers all four for the query in hand, in ~400ms.
|
|
12
|
+
* Every answer is a *suggestion*: an explicit per-call option always wins,
|
|
13
|
+
* and a capability the pipeline was not configured with can never be turned
|
|
14
|
+
* on by a model — `graph: true` against a pipeline with no graph index would
|
|
15
|
+
* be a failure, not a plan.
|
|
16
|
+
*
|
|
17
|
+
* @module rag/retrieval/searchDecision
|
|
18
|
+
*/
|
|
19
|
+
import type { DecisionCallerFn, SearchPlanCapabilities, SearchPlanResult } from "../../types/index.js";
|
|
20
|
+
/**
|
|
21
|
+
* Ask how this particular query should be retrieved.
|
|
22
|
+
*
|
|
23
|
+
* Returns `null` when nothing should change — no decision provider, a failed
|
|
24
|
+
* call, or an answer set that suggests nothing. Every returned field is
|
|
25
|
+
* optional and means "use this unless the caller said otherwise".
|
|
26
|
+
*/
|
|
27
|
+
export declare function decideSearchPlan(query: string, decide: DecisionCallerFn, capabilities: SearchPlanCapabilities): Promise<SearchPlanResult | null>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-search retrieval planning.
|
|
3
|
+
*
|
|
4
|
+
* `RAGPipeline.query()` resolves four knobs — `topK`, `hybrid`, `graph`,
|
|
5
|
+
* `rerank` — and every one of them is config with an optional per-call
|
|
6
|
+
* override. Nothing inspects the query. The same plan is used for "what is
|
|
7
|
+
* the refund window?" (one precise passage, an exact phrase worth matching
|
|
8
|
+
* lexically) and "how does billing relate to entitlements?" (many passages,
|
|
9
|
+
* relationships between documents).
|
|
10
|
+
*
|
|
11
|
+
* One decision request answers all four for the query in hand, in ~400ms.
|
|
12
|
+
* Every answer is a *suggestion*: an explicit per-call option always wins,
|
|
13
|
+
* and a capability the pipeline was not configured with can never be turned
|
|
14
|
+
* on by a model — `graph: true` against a pipeline with no graph index would
|
|
15
|
+
* be a failure, not a plan.
|
|
16
|
+
*
|
|
17
|
+
* @module rag/retrieval/searchDecision
|
|
18
|
+
*/
|
|
19
|
+
import { gateDecisionBoolean, readDecisionScore, } from "../../utils/decisionAnswers.js";
|
|
20
|
+
import { logger } from "../../utils/logger.js";
|
|
21
|
+
/**
|
|
22
|
+
* How many passages the question needs, narrowest → widest, as multiples of
|
|
23
|
+
* the configured default. A `score` rather than a number: a decision model
|
|
24
|
+
* places things on an ordered scale well and reads digits as text badly.
|
|
25
|
+
*/
|
|
26
|
+
const BREADTH_CRITERIA = [
|
|
27
|
+
"One specific fact, definition or value. A single passage answers it completely.",
|
|
28
|
+
"A handful of related points — a procedure, a short comparison, one topic explained.",
|
|
29
|
+
"Several distinct areas that each need their own supporting passage.",
|
|
30
|
+
"A broad survey that needs evidence from across the whole corpus.",
|
|
31
|
+
];
|
|
32
|
+
/** Multiplier applied to the configured topK, indexed by breadth level. */
|
|
33
|
+
const BREADTH_MULTIPLIER = [0.5, 1, 1.5, 2.5];
|
|
34
|
+
/** Below this the breadth reading is ignored and the configured topK stands. */
|
|
35
|
+
const MIN_BREADTH_CONFIDENCE = 0.5;
|
|
36
|
+
/** Absolute bounds on a suggested topK, whatever the multiplier produces. */
|
|
37
|
+
const MIN_TOP_K = 1;
|
|
38
|
+
const MAX_TOP_K = 50;
|
|
39
|
+
/**
|
|
40
|
+
* Ask how this particular query should be retrieved.
|
|
41
|
+
*
|
|
42
|
+
* Returns `null` when nothing should change — no decision provider, a failed
|
|
43
|
+
* call, or an answer set that suggests nothing. Every returned field is
|
|
44
|
+
* optional and means "use this unless the caller said otherwise".
|
|
45
|
+
*/
|
|
46
|
+
export async function decideSearchPlan(query, decide, capabilities) {
|
|
47
|
+
if (!query.trim()) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const questions = {
|
|
51
|
+
breadth: {
|
|
52
|
+
type: "score",
|
|
53
|
+
instructions: "How much supporting material does answering this question require from a document collection?",
|
|
54
|
+
criteria: BREADTH_CRITERIA,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
// Only ask about a capability the pipeline actually has. An answer nobody
|
|
58
|
+
// can act on costs input tokens and invites the mistake of acting on it.
|
|
59
|
+
if (capabilities.canHybrid) {
|
|
60
|
+
questions.hybrid = {
|
|
61
|
+
type: "boolean",
|
|
62
|
+
instructions: "This question contains exact terms that must be matched literally — an identifier, error code, file name, version number, API name, or a quoted phrase — rather than only a topic to match by meaning.",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (capabilities.canGraph) {
|
|
66
|
+
questions.graph = {
|
|
67
|
+
type: "boolean",
|
|
68
|
+
instructions: "Answering this requires connecting information that lives in separate documents, such as how two things relate, what depends on what, or tracing a chain across sources.",
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (capabilities.canRerank) {
|
|
72
|
+
questions.rerank = {
|
|
73
|
+
type: "boolean",
|
|
74
|
+
instructions: "This question is specific enough that the ORDER of the retrieved passages matters — a nearly-right passage would produce a wrong answer, so precision is worth an extra ranking pass.",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const result = await decide({
|
|
78
|
+
state: { query: query.slice(0, 4000) },
|
|
79
|
+
// The map is assembled dynamically above; its members are all valid
|
|
80
|
+
// question shapes, which the cast asserts once here rather than at four
|
|
81
|
+
// separate construction sites.
|
|
82
|
+
questions: questions,
|
|
83
|
+
timeoutMs: capabilities.timeoutMs,
|
|
84
|
+
});
|
|
85
|
+
if (!result) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const plan = {
|
|
89
|
+
model: result.model,
|
|
90
|
+
latencyMs: result.latencyMs,
|
|
91
|
+
};
|
|
92
|
+
const breadth = readDecisionScore(result.answers, "breadth");
|
|
93
|
+
if (breadth && breadth.confidence >= MIN_BREADTH_CONFIDENCE) {
|
|
94
|
+
const level = Math.max(0, Math.min(BREADTH_MULTIPLIER.length - 1, Math.round(breadth.score)));
|
|
95
|
+
const multiplier = BREADTH_MULTIPLIER[level] ?? 1;
|
|
96
|
+
plan.topK = Math.max(MIN_TOP_K, Math.min(MAX_TOP_K, Math.round(capabilities.defaultTopK * multiplier)));
|
|
97
|
+
plan.breadthLevel = level;
|
|
98
|
+
}
|
|
99
|
+
if (capabilities.canHybrid) {
|
|
100
|
+
plan.hybrid = gateDecisionBoolean(result.answers, "hybrid");
|
|
101
|
+
}
|
|
102
|
+
if (capabilities.canGraph) {
|
|
103
|
+
plan.graph = gateDecisionBoolean(result.answers, "graph");
|
|
104
|
+
}
|
|
105
|
+
if (capabilities.canRerank) {
|
|
106
|
+
plan.rerank = gateDecisionBoolean(result.answers, "rerank");
|
|
107
|
+
}
|
|
108
|
+
const suggested = plan.topK !== undefined ||
|
|
109
|
+
plan.hybrid !== undefined ||
|
|
110
|
+
plan.graph !== undefined ||
|
|
111
|
+
plan.rerank !== undefined;
|
|
112
|
+
if (!suggested) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
logger.debug("[RAG] Search plan decided", {
|
|
116
|
+
model: result.model,
|
|
117
|
+
latencyMs: result.latencyMs,
|
|
118
|
+
topK: plan.topK,
|
|
119
|
+
hybrid: plan.hybrid,
|
|
120
|
+
graph: plan.graph,
|
|
121
|
+
rerank: plan.rerank,
|
|
122
|
+
});
|
|
123
|
+
return plan;
|
|
124
|
+
}
|
|
@@ -11,18 +11,52 @@
|
|
|
11
11
|
* The only data dependency is the read-only `ModelResolver` registry lookup,
|
|
12
12
|
* used purely for metadata enrichment and never for provider construction.
|
|
13
13
|
*/
|
|
14
|
-
import type { ClassifierRouterConfig, ClassifierRouterDecision, ClassifierRouterDeps, ClassifierRouterInput } from "../types/index.js";
|
|
14
|
+
import type { ClassifierRouterConfig, ClassifierRouterDecision, ClassifierRouterDeps, ClassifierRouterInput, ClassifierRouterPoolMember } from "../types/index.js";
|
|
15
15
|
export declare class ClassifierRouter {
|
|
16
16
|
private readonly config;
|
|
17
17
|
private readonly deps;
|
|
18
18
|
private readonly metaCache;
|
|
19
|
+
/**
|
|
20
|
+
* The effective pool: what the host declared, plus catalogue-derived members
|
|
21
|
+
* when `catalog.enabled`. Mutable so a long-lived host can widen or narrow
|
|
22
|
+
* it without rebuilding the router — the constructor-frozen pool was the one
|
|
23
|
+
* thing preventing a runtime model catalogue.
|
|
24
|
+
*/
|
|
25
|
+
private pool;
|
|
26
|
+
/** Registry index for candidate enrichment; built once, on first use. */
|
|
27
|
+
private registryIndex?;
|
|
19
28
|
constructor(config: ClassifierRouterConfig, deps?: ClassifierRouterDeps);
|
|
29
|
+
/**
|
|
30
|
+
* Merge the declared pool with the catalogue. Declared members win on a
|
|
31
|
+
* duplicate `provider/model`, because a host that spelled a member out has
|
|
32
|
+
* said something about it (a region, a description, an explicit tier) that
|
|
33
|
+
* the registry does not know.
|
|
34
|
+
*/
|
|
35
|
+
private static composePool;
|
|
36
|
+
/**
|
|
37
|
+
* Replace the routable pool at runtime. Returns the new size.
|
|
38
|
+
*
|
|
39
|
+
* Clears the metadata cache, since a member's declared cost/quality is
|
|
40
|
+
* cached per `provider::model` and a replacement pool may declare different
|
|
41
|
+
* values for the same pair.
|
|
42
|
+
*/
|
|
43
|
+
setPool(members: ClassifierRouterPoolMember[]): number;
|
|
44
|
+
/** The pool currently routed over, declared plus catalogue. */
|
|
45
|
+
getPool(): ClassifierRouterPoolMember[];
|
|
46
|
+
/** Rebuild the catalogue half of the pool (e.g. after credentials change). */
|
|
47
|
+
refreshCatalog(): number;
|
|
20
48
|
/**
|
|
21
49
|
* Classify the request and produce a combined model + tool decision, or
|
|
22
50
|
* `null` when nothing should change. Never throws (fails open).
|
|
23
51
|
*/
|
|
24
52
|
route(input: ClassifierRouterInput): Promise<ClassifierRouterDecision | null>;
|
|
25
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Resolve "auto" (the default) to a concrete strategy. Jev is preferred the
|
|
55
|
+
* moment a TypeSafe key is present; without one the behaviour is exactly
|
|
56
|
+
* what it was before Jev existed.
|
|
57
|
+
*/
|
|
58
|
+
private resolveStrategy;
|
|
59
|
+
/** Run the chosen strategy; every one falls back to heuristic on failure. */
|
|
26
60
|
private classify;
|
|
27
61
|
/**
|
|
28
62
|
* Build LLM-facing model descriptors + an id→member map so the classifier can
|
|
@@ -35,12 +69,50 @@ export declare class ClassifierRouter {
|
|
|
35
69
|
* metadata scoring.
|
|
36
70
|
*/
|
|
37
71
|
private selectModels;
|
|
72
|
+
/**
|
|
73
|
+
* Whether a directly-picked model clears the bar that applies to it.
|
|
74
|
+
*
|
|
75
|
+
* The bars are asymmetric because the two mistakes do not cost the same, and
|
|
76
|
+
* which one a pick risks depends on what the difficulty tier would otherwise
|
|
77
|
+
* have chosen. Picking something COSTLIER than the tier's own choice risks
|
|
78
|
+
* spending more than necessary, so it clears the low upgrade bar. Picking
|
|
79
|
+
* something CHEAPER risks handing the task to a model that cannot do it, so
|
|
80
|
+
* it must clear the high downgrade bar. Agreeing with the tier needs no bar
|
|
81
|
+
* at all.
|
|
82
|
+
*
|
|
83
|
+
* A strategy that reports no confidence for its pick — the LLM classifier —
|
|
84
|
+
* is honoured exactly as before. Its self-reported numbers are not
|
|
85
|
+
* calibrated, so a bar over them would be arithmetic on noise, and imposing
|
|
86
|
+
* one would silently change the behaviour of a shipped, unrelated strategy.
|
|
87
|
+
*/
|
|
88
|
+
private pickClearsBar;
|
|
89
|
+
/** The difficulty-based ranking, independent of any direct pick. */
|
|
90
|
+
private rankForDifficulty;
|
|
91
|
+
/**
|
|
92
|
+
* Whether a member's context window can hold the estimated request. Unknown
|
|
93
|
+
* windows pass — the registry does not know every model, and starving the
|
|
94
|
+
* pool on missing metadata is worse than an occasional retry.
|
|
95
|
+
*/
|
|
96
|
+
private fitsRequest;
|
|
97
|
+
/** Drop members too small for the request, unless that empties the pool. */
|
|
98
|
+
private withRoomFor;
|
|
38
99
|
/** Candidate members for a difficulty: explicit tierMap or eligible pool. */
|
|
39
100
|
private candidatesFor;
|
|
40
101
|
/** Drop members that cannot satisfy the required capabilities (lenient). */
|
|
41
102
|
private filterByCapabilities;
|
|
42
103
|
/** Rank candidates best-first for the difficulty's strategy. */
|
|
43
104
|
private rank;
|
|
105
|
+
/**
|
|
106
|
+
* Turn the classifier's context reading into a compaction threshold.
|
|
107
|
+
*
|
|
108
|
+
* Returns undefined — meaning "leave the 0.8 default alone" — whenever the
|
|
109
|
+
* feature is off, no scope was read, or the scope is the widest one. The
|
|
110
|
+
* mapping can only ever LOWER the threshold, which is enforced in
|
|
111
|
+
* `contextScopeToThreshold` rather than here, because the consequence of
|
|
112
|
+
* getting it wrong in the other direction is not recoverable: a
|
|
113
|
+
* `context_window` error puts the model into ModelPool's 10-year cooldown.
|
|
114
|
+
*/
|
|
115
|
+
private selectContextBudget;
|
|
44
116
|
/** Tool narrowing: per-difficulty directive, then classifier hints. */
|
|
45
117
|
private selectTools;
|
|
46
118
|
/**
|