/** * Pure helpers used by the Cache Explorer to compare two model-turn requests * (A and B) and identify where the prompt prefix diverges. * * The engine works on the {@link IChatDebugEventModelTurnContent.sections} * "Input Messages" section, which is a JSON-stringified array of * `[{ role, name?, parts: [{ type: 'text', content, name? }, ...] }]` * matching the OpenTelemetry GenAI semantic convention used by * `chatParticipantTelemetry.ts`. * * All functions are pure — no DOM, no services — so they can be unit tested * in isolation. */ /** * A normalized request message used by the diff engine. */ export interface INormalizedMessage { readonly role: string; readonly name?: string; /** Concatenation of all `text` parts in the message. */ readonly text: string; /** Character length of `text` as a UTF-16 code unit count (`text.length`). */ readonly charLength: number; } /** Classification of a single signature token when comparing A and B. */ export declare enum CacheDiffKind { /** Same role+name and same charLength in both A and B. */ Identical = "identical", /** Same role+name and same charLength but different content. */ ContentDrift = "contentDrift", /** Same role+name but different charLength. */ LengthChange = "lengthChange", /** Position exists only in A. */ OnlyInA = "onlyInA", /** Position exists only in B. */ OnlyInB = "onlyInB" } /** * A single token in the side-by-side prompt signature. * * The signature is computed by zipping A's and B's normalized messages * positionally and classifying each index independently. The first * divergence is what breaks the prompt cache, but later positions can * still be reported as {@link CacheDiffKind.Identical} if their content * happens to match \u2014 we surface per-position truth here and let the * UI decide how to interpret it (the cache-break marker, summary copy, * and "Where the cache broke" line all key off the *first* divergent * index, not the last). */ export interface ICacheSignatureToken { readonly index: number; readonly kind: CacheDiffKind; readonly aRole?: string; readonly aName?: string; readonly aCharLength?: number; readonly bRole?: string; readonly bName?: string; readonly bCharLength?: number; } /** * The first place where A and B's prompt prefix diverges. Anything after * this index cannot be served from the prompt cache. */ export interface ICacheBreak { readonly index: number; readonly kind: Exclude; } /** * A single drifting component (e.g. a message at index N). */ export interface IComponentDrift { readonly name: string; readonly role?: string; readonly status: CacheDiffKind; readonly aSize: number; readonly bSize: number; } /** * Aggregate result of comparing two requests. */ export interface ICacheDiffResult { readonly signature: readonly ICacheSignatureToken[]; readonly break: ICacheBreak | undefined; readonly drift: readonly IComponentDrift[]; /** * Counts of identical / drift / one-sided positions across the whole * signature. Useful for the summary pills. */ readonly counts: { readonly identical: number; readonly contentDrift: number; readonly lengthChange: number; readonly onlyInA: number; readonly onlyInB: number; }; } /** * Parse a JSON-encoded `inputMessages` payload into normalized messages. * * Returns an empty array on any parse error so callers can render a clear * empty-state without try/catch boilerplate. */ export declare function parseInputMessages(inputMessagesJson: string | undefined): readonly INormalizedMessage[]; /** * Compute the per-position diff between two normalized message arrays. * * The algorithm is intentionally simple (positional zip) rather than a full * Myers diff: prompt caches are prefix-based, so the moment two messages at * the same index diverge in role, length, or content the cache breaks. * Reporting that first divergence is far more useful than computing a * minimum edit script. */ export declare function diffPromptSignature(a: readonly INormalizedMessage[], b: readonly INormalizedMessage[]): ICacheDiffResult; /** * Add a "system" drift entry to the report when the system instructions * differ between the two requests. Inserted at the canonical * {@link PREFIX_COMPONENT_ORDER} position regardless of what's already in * the drift list. */ export declare function appendSystemDrift(drift: IComponentDrift[], aSystem: string | undefined, bSystem: string | undefined): IComponentDrift[]; /** * Add a "tools" drift entry to the report when the tool definitions catalog * differs between the two requests. The catalog is part of the cache key on * every model provider, so a change here \u2014 even a pure reorder \u2014 can break * the prompt cache. We intentionally do not normalize (sort, hash, parse) * the JSON: providers vary in how they hash the catalog, so the safest * stance is to surface any byte-level change and let the user judge. * * Inserted at the canonical {@link PREFIX_COMPONENT_ORDER} position so the * order is always `[system, tools, ...messages]` regardless of call order * relative to {@link appendSystemDrift}. */ export declare function appendToolsDrift(drift: IComponentDrift[], aTools: string | undefined, bTools: string | undefined): IComponentDrift[]; /** * Format a normalized message into a single-line `role[-name]:bytes` token, * matching the convention used by the existing `promptTypes` telemetry. */ export declare function formatSignatureToken(token: ICacheSignatureToken): string;