@sema-agent/core
Version:
Stateless, task-oriented AI agent core
78 lines (77 loc) • 4.39 kB
TypeScript
/**
* Adaptive `max_tokens` recovery for the ONE provider 400 that is self-healing (CC parity `Fjp` /
* `Ijp`, ruled 2026-08-05).
*
* The failure: the input fits the model's context window, but `input + max_tokens` does not. Nothing
* downstream rescues it — the compaction layer's own detectors match "prompt is too long", a
* DIFFERENT message meaning the input ALONE overflows, so this 400 is classified `invalid_request`,
* is not retried, and terminates the task even though lowering this one request's output cap would
* have served it. The provider states every number needed to compute the fix in the error text.
*
* The recovery is deliberately NARROW: only a 400 whose body carries this exact sentence can trigger
* it, and anything the parser cannot read as three integers leaves the original failure untouched.
* Two fail-loud guards then bound it — a floor below which lowering the cap stops being useful, and
* a no-progress guard so a provider that keeps reporting the same numbers cannot loop the engine.
*/
/** CC parity (`Ijp`): the smallest output cap worth retrying with. Below this the answer room is so
* small that a re-send is not a recovery, so the original 400 is surfaced instead. */
export declare const FLOOR_OUTPUT_TOKENS = 3000;
/** The three numbers the provider reported. */
export interface ContextOverflowReport {
/** Tokens the provider measured in the request input. */
inputTokens: number;
/** The output cap this request carried. */
maxTokens: number;
/** The model's total context limit, as the provider counts it. */
contextLimit: number;
}
/**
* Read the three numbers out of a provider 400 body, or `undefined` for every other failure —
* including a 400 of any other shape, a body whose provenance is unestablished, a truncated body, and
* numbers too large to be exact integers. A caller that gets `undefined` must surface the original
* failure unchanged.
*/
export declare function parseContextOverflow(status: number, body: string): ContextOverflowReport | undefined;
/**
* What to do about a parsed overflow. `adjust` is the only outcome that re-sends; the other two are
* refusals that the caller must report ALONGSIDE the original provider failure, so an operator sees
* that a recovery was attempted and why it was declined rather than a bare 400.
*/
export type OutputCapAdjustment =
/** `current` is the cap this adjustment lowers FROM — the smallest one known to be in effect, which
* is what a disclosure must name (the provider's echoed number may not be what was sent). */
{
outcome: "adjust";
maxOutputTokens: number;
current: number;
}
/** The room left under the context limit is below {@link FLOOR_OUTPUT_TOKENS} — the input itself is
* the problem, and no output cap makes this request servable. */
| {
outcome: "below_floor";
available: number;
}
/** The recomputed cap is not STRICTLY below the cap already in effect — the previous adjustment
* did not take effect, or the provider re-reported the same numbers, or the numbers would RAISE
* the cap. Re-sending cannot help, and in the raising case would be actively worse. */
| {
outcome: "no_progress";
available: number;
current: number;
};
/**
* Compute the lowered output cap for a re-send, or the reason not to.
*
* `otherCapsInEffect` are the caps the CALLER knows about beyond the one the provider echoed —
* typically the cap this engine already imposed on an earlier attempt, and the cap the request
* actually carried on the wire. The SMALLEST of all of them bounds the adjustment, and a new cap
* must be strictly below it. That single rule does three jobs:
* - it terminates the loop (every accepted adjustment lowers the cap, and the floor bounds it);
* - it makes an adapter that ignored an imposed cap fail loud after one round instead of looping;
* - it keeps the recovery from ever RAISING a cap on the strength of a number the provider
* reported but the request never carried.
*
* `report.maxTokens` is always one of the caps considered — the provider says the failing request
* carried it, and a value it did not carry can only be smaller in the direction that matters.
*/
export declare function planOutputCapAdjustment(report: ContextOverflowReport, otherCapsInEffect?: readonly (number | undefined)[]): OutputCapAdjustment;