105 lines
4.2 KiB
TypeScript
105 lines
4.2 KiB
TypeScript
import type { DerivedPlan } from "../deriveDefaultPlan.ts";
|
|
import type { AdaptiveTelemetry, ContextBudgetConfig, LadderStage } from "./types.ts";
|
|
import { DEFAULT_LADDER, aggressivenessOf, expectedReductionFactor } from "./ladder.ts";
|
|
import { computeTarget } from "./computeTarget.ts";
|
|
|
|
export interface ResolveAdaptiveInput {
|
|
basePlan: DerivedPlan;
|
|
estimatedTokens: number;
|
|
modelContextLimit: number | null;
|
|
requestMaxTokens: number | null;
|
|
config: ContextBudgetConfig;
|
|
/**
|
|
* Injected per-stage estimator (design §4.1/§9). Returns the NEW token estimate after a
|
|
* stage is applied to a prompt of `priorTokens`. Default models it with the cheap
|
|
* per-engine expected-reduction factor (no dry-run, no real tokenizer).
|
|
*/
|
|
estimate?: (priorTokens: number, stage: LadderStage) => number;
|
|
}
|
|
|
|
export interface ResolveAdaptiveResult {
|
|
plan: DerivedPlan;
|
|
telemetry: AdaptiveTelemetry | null;
|
|
}
|
|
|
|
const defaultEstimate = (prior: number, stage: LadderStage): number =>
|
|
Math.round(prior * expectedReductionFactor(stage.engine));
|
|
|
|
/**
|
|
* Pure adaptive resolver (design §4.2). Floors/escalates a base plan so the (estimated)
|
|
* compressed prompt fits the context budget. Never drops content: if the ladder is
|
|
* exhausted while still over target, returns the best-effort plan with fit=false.
|
|
*
|
|
* Returns telemetry=null only when there is no usable target (unknown model limit) — the
|
|
* caller then skips adaptive entirely (design §6: skip, never throw).
|
|
*/
|
|
export function resolveAdaptivePlan(input: ResolveAdaptiveInput): ResolveAdaptiveResult {
|
|
const { basePlan, estimatedTokens, modelContextLimit, requestMaxTokens, config } = input;
|
|
const estimate = input.estimate ?? defaultEstimate;
|
|
|
|
if (config.mode === "off") return { plan: basePlan, telemetry: null };
|
|
if (!modelContextLimit || modelContextLimit <= 0) {
|
|
return { plan: basePlan, telemetry: null }; // unknown limit → skip (D-C / §6)
|
|
}
|
|
|
|
const target = computeTarget(config.policy, modelContextLimit, requestMaxTokens, config);
|
|
const headroomBefore = target - estimatedTokens;
|
|
|
|
// replace-autotrigger only acts on a bare Default/off base plan; an explicit choice wins.
|
|
const baseRank = aggressivenessOf(basePlan.mode);
|
|
if (config.mode === "replace-autotrigger" && baseRank > aggressivenessOf("off")) {
|
|
return {
|
|
plan: basePlan,
|
|
telemetry: { policy: config.policy, target, headroomBefore, stagesApplied: [], headroomAfter: headroomBefore, fit: headroomBefore >= 0 },
|
|
};
|
|
}
|
|
|
|
// Already fits → never over-compress (D-C2).
|
|
if (headroomBefore >= 0) {
|
|
return {
|
|
plan: basePlan,
|
|
telemetry: { policy: config.policy, target, headroomBefore, stagesApplied: [], headroomAfter: headroomBefore, fit: true },
|
|
};
|
|
}
|
|
|
|
// Escalation: start just ABOVE the base plan's aggressiveness (floor escalates beyond it).
|
|
const ladder = config.ladderOverride && config.ladderOverride.length > 0 ? config.ladderOverride : DEFAULT_LADDER;
|
|
const startTier = config.mode === "floor" ? baseRank : aggressivenessOf("off");
|
|
const stages = ladder.filter((s) => aggressivenessOf(s.engine) > startTier);
|
|
|
|
let current = estimatedTokens;
|
|
const applied: LadderStage[] = [];
|
|
for (const stage of stages) {
|
|
current = estimate(current, stage);
|
|
applied.push(stage);
|
|
if (current <= target) break;
|
|
}
|
|
|
|
const headroomAfter = target - current;
|
|
const fit = headroomAfter >= 0;
|
|
return {
|
|
plan: planFromStages(basePlan, applied),
|
|
telemetry: {
|
|
policy: config.policy,
|
|
target,
|
|
headroomBefore,
|
|
stagesApplied: applied.map((s) => s.engine),
|
|
headroomAfter,
|
|
fit,
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Turn the applied ladder stages into a DerivedPlan. A single applied stage that is a
|
|
* single-mode engine collapses to that engine's mode; otherwise a stacked pipeline.
|
|
* The base plan's own pipeline is preserved as the prefix (floor escalates AFTER the base).
|
|
*/
|
|
function planFromStages(basePlan: DerivedPlan, applied: LadderStage[]): DerivedPlan {
|
|
if (applied.length === 0) return basePlan;
|
|
const basePipeline =
|
|
basePlan.mode === "stacked" ? basePlan.stackedPipeline : [];
|
|
const stackedPipeline = [...basePipeline, ...applied];
|
|
return { mode: "stacked", stackedPipeline };
|
|
}
|