File size: 4,250 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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 };
}