@sema-agent/core
Version:
Stateless, task-oriented AI agent core
46 lines (45 loc) • 3.45 kB
TypeScript
/**
* design/374 slice 2 — TYPED input-too-long classification for provider refusals.
*
* "The request INPUT alone exceeds the model's context limit" is recognizable from prose over the
* human-facing `errorMessage`, but such a classifier cannot tell a provider's own diagnostic from
* caller text a gateway reflected, and breaks the moment a provider rewords. The classification runs
* at the BRAIN boundary, where provenance is decidable, and the verdict rides
* `BrainError.inputTooLong` → `AssistantMessage.errorKind: "input_too_long"` (the same typed lane
* `degenerate`/`length_empty` already use).
*
* PROVENANCE DISCIPLINE (mirrors `context-overflow.ts`, the sibling classifier for the
* input+max_tokens overflow — a DIFFERENT error: there the input fits and lowering the output cap
* self-heals; here the input alone does not fit and only shrinking the input helps):
* - The only text source is the provider's diagnostic FIELD: `error.message` of a JSON object
* body whose `error` is itself an object (both the Anthropic `{type:"error",error:{…}}` and
* the OpenAI `{error:{…}}` envelopes reduce to this). A plain-text body, a string `error`, a
* top-level `message` — refused: each is a guess about gateway formatting that would trade a
* hypothetical classification for a field a gateway may fill with caller content.
* - Prose is matched ANCHORED at the start of that field only, against the conservative
* context-limit FAMILY (case-insensitive; the family and its member list live with the rows that
* carry them, `provider-dialect.ts`). A gateway that inlines the offending request ("validation
* failed; offending prompt: prompt is too long…") therefore cannot classify: caller text must
* never steer a retry.
* - Structural signals need no prose at all: a provider's own context-limit CODE (OpenAI-family
* `context_length_exceeded`; an endpoint's declared code such as GLM's `1261`), and HTTP 413 (the
* transport's own "request entity too large"). A code the endpoint declares for something else
* outranks every sentence: the provider named the failure, a sentence beside it cannot rename it.
*
* The verdict is a column of the provider dialect table (`provider-dialect.ts`): a context-limit
* refusal is a row whose `inputTooLong` flag is set, read by the same lookup that names the failure's
* `providerCondition`. This module is the reader face for a response no endpoint is known for.
*
* Misclassification cost: inside a PARSED provider envelope a miss is an AUTHORITATIVE negative
* that SUPPRESSES the loop's prose fallback — which is why the family must carry every observed
* genuine opening; prose-only bodies keep the fallback reach via the "unknown" arm. A FALSE HIT
* costs one bounded recovery attempt, and only on a 400/413/422 the request already failed on.
*/
import { type InputTooLongVerdict } from "./provider-dialect.js";
export { startsWithInputTooLongAnchor, type InputTooLongVerdict } from "./provider-dialect.js";
/**
* Classify an HTTP refusal as input-too-long against the generic rows (an endpoint no dialect declares).
* `body` is the error-body text, read whole; one that does not parse as a JSON envelope answers `"unknown"`
* (413 excepted — the status alone is the provider's statement).
*/
export declare function classifyInputTooLong(status: number, body: string): InputTooLongVerdict;