import type { ContradictionAxisId, ContradictionValueId } from './i18n/types.js'; import type { TokenCounter } from './types.js'; /** * Structural analysis. * * Every rule in `rules.ts` matches a phrase. The waste this module looks for * is a relationship between two places in the prompt — an instruction that * contradicts another, an example that teaches what an earlier one already * taught. No dictionary can see either, because neither is wrong on its own. * * Everything here is advisory. A contradiction has a right answer only the * author knows, and an example that looks redundant may be demonstrating a * boundary case on purpose. Trazum points; it does not cut. */ /** * Sourced from the i18n contract rather than declared here, so adding an axis * fails to compile until every catalogue can name it. */ export type ContradictionAxis = ContradictionAxisId; export interface ContradictionSide { /** Which end of the axis this instruction sits on. */ value: ContradictionValueId; /** The sentence it was found in, trimmed for display. */ snippet: string; } export interface Contradiction { axis: ContradictionAxis; a: ContradictionSide; b: ContradictionSide; } /** * Finds instructions that pull in opposite directions on the same axis. * * Only the first occurrence of each value is kept, and an axis reports at most * one contradiction: a prompt that says "be concise" four times and "be * thorough" once has one problem, not four. * * Two matches inside the same sentence are ignored. "Be concise but complete" * is a deliberate trade-off an author wrote on purpose; flagging it would * train people to ignore the advisory. */ export declare function findContradictions(prompt: string): Contradiction[]; /** * A labelled field inside a few-shot example — `Input:`, `Output:`, `A:`… * * Exported because the duplicate-line rule needs it: two examples that share * an output line are demonstrating that two different inputs map to the *same* * answer, which is often the whole point of including both. Deduplicating * those lines leaves an example with no output at all — worse than the * repetition it removed. */ export declare const EXAMPLE_FIELD_LINE: RegExp; export interface ExampleBlock { text: string; tokens: number; } export interface RedundantExample { /** Index of the earlier example that already teaches this. */ duplicateOf: number; index: number; similarity: number; tokens: number; } export interface ExampleAnalysis { examples: ExampleBlock[]; redundant: RedundantExample[]; /** Tokens held by examples that repeat an earlier one. */ redundantTokens: number; } /** * **A shape this deliberately does not detect, and the reason is the point.** * * Inline mappings are everywhere — `1. "package never arrived" -> shipping`, * `- "Acme raised a round" => Acme` — and on the probes that found the two * faults above they are the remaining silent case. They are not added, because * the only thing separating a demonstration from an instruction here is * judgement: `if the ticket is about delivery -> use the shipping category` is * the same line shape and is a rule, not an example. * * A detector that got that wrong would not merely report a wrong number. * `prune` *removes* examples, so a rule misread as an example becomes a * proposal to delete an instruction — and it would arrive with the same * confidence as a correct one. * * The tempting narrowing is to require the left side to be quoted. It was * considered and refused: both probes are quoted **because the author of the * probes wrote them that way**, and a threshold fitted to one's own fixture is * a threshold that has been measured against nothing. What would settle it is * real prompts using the shape, not a rule invented here. * * Until then the honest behaviour is the one `prune` now has: say what was * looked for, so a prompt this cannot read reports that it was not seen rather * than that it does not exist. That sentence used to read "this prompt has * fewer than two few-shot examples", which was a confident false statement * about a prompt with four of them. */ /** Splits the prompt into labelled example blocks. */ export declare function findExamples(prompt: string, count: TokenCounter): ExampleBlock[]; /** * Reports examples that repeat what an earlier example already demonstrates. * * Each example is compared against every earlier one, and blamed on the first * match: in a run of three near-identical examples the second and third are * both reported against the first, rather than chaining. */ export declare function analyzeExamples(prompt: string, count: TokenCounter): ExampleAnalysis; /** * A prompt that shows its output schema in a code block, and then describes * the same fields again in prose, is paying for the schema twice. * * The block is the version that survives: it is unambiguous, and the * protection pass already guarantees Trazum will not touch it. The prose * restatement is what can go — but only a human can tell a restatement from a * clarification that happens to name the same fields, so this reports and does * not cut. */ export interface RestatedFormat { /** Top-level keys found in the fenced schema. */ keys: string[]; /** Those keys that are also named in the prose outside the block. */ restatedKeys: string[]; /** Tokens held by the prose sentences that restate them. */ restatedTokens: number; } /** * Finds an output schema that the prose repeats. * * The threshold is deliberately blunt: naming one or two fields in prose is * ordinary ("set `escalate` to true when the customer asks for a human"), and * flagging that would make the advisory noise. Three or more, and the prose is * walking the schema. */ export declare function findRestatedFormat(prompt: string, count: TokenCounter): RestatedFormat | null; /** * A schema shown in the prompt that the request could carry as a parameter. * * Every provider worth naming now accepts a response schema alongside the * message — `output_config.format`, `response_format`, `responseSchema`, * whatever it is called this quarter. A prompt that spells the same shape out in * a fenced block pays for it in input tokens on **every call**, and gets a * weaker guarantee for the money: prose asks the model to comply, a parameter * makes the decoder comply. * * So this is the rare finding that is not a trade-off. Moving a schema out is * cheaper *and* stricter. What stops it being a rule is that Trazum cannot make * the change: it edits prompts, and this is a change to the call around the * prompt. It reports, names the tokens, and leaves the edit to whoever owns the * client code. * * **The one way this could do harm, and what prevents it.** A fenced JSON block * in a prompt is one of two completely different things. `Output format: {...}` * is a contract and moving it is free. `Input: {...}` inside a few-shot example * is *data the prompt needs*, and moving it breaks the prompt. Nothing here * guesses which: a block counts only when a phrase from `OUTPUT_CUES_BY_LANGUAGE` * appears in the text immediately before it. A schema with no such phrase is * left alone, and a prompt in a language those dictionaries do not cover raises * nothing at all — a false negative, stated as one, rather than an English cue * matched inside Japanese prose and called a saving. */ export interface MovableSchema { /** Fenced blocks that an output cue introduces. */ blocks: number; /** Top-level keys across all of them, deduplicated. */ keys: string[]; /** Tokens the blocks hold, fences included, since all of it leaves the prompt. */ tokens: number; /** The cue that identified the first block, so the report can quote it. */ cue: string; } export declare function findMovableSchema(prompt: string, count: TokenCounter): MovableSchema | null; //# sourceMappingURL=structure.d.ts.map