type CassetteMode = 'record' | 'replay' | 'passthrough' | 'hybrid'; type Provider = 'anthropic' | 'openai' | 'http'; interface CassetteMessage { role: 'system' | 'user' | 'assistant'; content: string; } interface CassetteParams { temperature?: number | null; top_p?: number | null; max_tokens?: number | null; stop?: string | string[] | null; } interface CassetteTool { name: string; description?: string | null; } interface CassetteRequest { provider: Provider; model: string; system?: string | null; messages: CassetteMessage[]; params: CassetteParams; tools?: CassetteTool[] | null; } interface CassetteUsage { input_tokens?: number; output_tokens?: number; total_tokens?: number; } type CassetteResponseType = 'message' | 'stream_chunks' | 'error'; interface CassetteContentBlock { type: 'text' | 'tool_use' | 'tool_result'; text?: string; id?: string; name?: string; input?: Record; } interface CassetteResponse { type: CassetteResponseType; content?: CassetteContentBlock[]; chunks?: string[]; usage?: CassetteUsage; stop_reason?: string; finish_reason?: string; error?: { code: string; message: string; }; } interface CassetteMetadata { recorded_at: string; duration_ms: number; provider_request_id?: string; } interface CassetteInteraction { id: string; request: CassetteRequest; response: CassetteResponse; metadata: CassetteMetadata; } interface CassetteFile { version: 1; interactions: CassetteInteraction[]; } interface HarnessConfig { cassettesDir: string; cassetteName?: string; mode?: CassetteMode; noOverwrite?: boolean; onBeforeRecord?: (interaction: CassetteInteraction) => CassetteInteraction; } interface MetricScore { name: string; pass: boolean; score: number; reason?: string; } interface MetricFn { readonly name: string; evaluate(text: string): MetricScore | Promise; } interface EvalResult { pass: boolean; passRate: number; scores: MetricScore[]; } interface BaselineEntry { metricName: string; score: number; pass: boolean; } interface BaselineSnapshot { version: 1; createdAt: string; testName: string; entries: BaselineEntry[]; } interface RegressionDetail { metricName: string; baseline: number; current: number; delta: number; } interface RegressionResult { hasRegression: boolean; regressions: RegressionDetail[]; improvements: RegressionDetail[]; } declare class Harness { private config; private store; constructor(config: HarnessConfig); get cassettePath(): string; /** * Wraps an Anthropic or OpenAI client with cassette record/replay interception. * The returned object is a transparent Proxy — all methods work identically. */ wrap(client: T): T; /** * Monkey-patches globalThis.fetch to intercept calls to known LLM API hostnames. * Returns a cleanup function that restores the original fetch. * * @example * const restore = harness.interceptFetch() * try { ... } finally { restore() } */ interceptFetch(): () => void; /** * Evaluates LLM output text against one or more metrics. */ evaluate(text: string, metrics: MetricFn[]): Promise; /** * Saves the current EvalResult as a named baseline snapshot. * Stored at /.baselines/.json */ saveBaseline(testName: string, result: EvalResult): void; /** * Compares an EvalResult against a saved baseline, returning regression details. * A regression is detected when current.score < baseline.score - threshold. */ compareBaseline(testName: string, result: EvalResult, threshold?: number): RegressionResult; } declare class ExactMatch implements MetricFn { private expected; private caseSensitive; readonly name = "ExactMatch"; constructor(expected: string, caseSensitive?: boolean); evaluate(text: string): MetricScore; } declare class Contains implements MetricFn { private substring; private caseSensitive; readonly name = "Contains"; constructor(substring: string, caseSensitive?: boolean); evaluate(text: string): MetricScore; } declare class ContainsAll implements MetricFn { private substrings; private caseSensitive; readonly name = "ContainsAll"; constructor(substrings: string[], caseSensitive?: boolean); evaluate(text: string): MetricScore; } declare class Regex implements MetricFn { readonly name = "Regex"; private pattern; constructor(pattern: RegExp | string, flags?: string); evaluate(text: string): MetricScore; } declare class JSONSchema implements MetricFn { readonly name = "JSONSchema"; private validate; constructor(schema: Record); evaluate(text: string): MetricScore; } /** * Normalized Levenshtein similarity: 1 - (editDistance / maxLength). * Returns 1 for identical strings, 0 for maximally different strings. */ declare function normalizedSimilarity(a: string, b: string): number; declare class Similarity implements MetricFn { private reference; private threshold; readonly name = "Similarity"; constructor(reference: string, threshold?: number); evaluate(text: string): MetricScore; } interface LLMJudgeOptions { rubric: string; client: object; model?: string; threshold?: number; } declare class LLMJudge implements MetricFn { private options; readonly name = "LLMJudge"; constructor(options: LLMJudgeOptions); evaluate(text: string): Promise; } declare class Custom implements MetricFn { readonly name: string; private fn; constructor(name: string, fn: (text: string) => MetricScore | Promise); evaluate(text: string): MetricScore | Promise; } /** * Asserts that the LLM response included a tool call with the given name. * * Pass the response content as JSON: * @example * const text = JSON.stringify(response.content) * harness.evaluate(text, [Metrics.toolCalled('search')]) * * Optionally verify the tool input contains a substring: * @example * Metrics.toolCalled('search', { inputContains: 'Paris' }) */ declare class ToolCalled implements MetricFn { readonly name: string; private toolName; private inputContains?; constructor(toolName: string, options?: { inputContains?: string; }); evaluate(text: string): MetricScore; } /** * Asserts that parsed JSON at `path` equals `expected`. * * @example * // Response: '{"user": {"name": "Alice", "age": 30}}' * Metrics.jsonPath('user.name', 'Alice') * Metrics.jsonPath('user.age', 30) */ declare class JsonPath implements MetricFn { readonly name: string; private path; private expected; constructor(path: string, expected: unknown); evaluate(text: string): MetricScore; } /** * Asserts the response is not empty or whitespace-only. * Simple but surprisingly useful as a baseline sanity check. * * @example * Metrics.notEmpty() * Metrics.notEmpty({ minLength: 10 }) */ declare class NotEmpty implements MetricFn { readonly name = "NotEmpty"; private minLength; constructor(options?: { minLength?: number; }); evaluate(text: string): MetricScore; } /** * Asserts the response falls within a word count range. * Useful for checking verbosity (LLMs can be too brief or too verbose). * * @example * Metrics.wordCount({ min: 20 }) // at least 20 words * Metrics.wordCount({ max: 100 }) // no more than 100 words * Metrics.wordCount({ min: 10, max: 50}) // between 10 and 50 words */ declare class WordCount implements MetricFn { readonly name: string; private min; private max; constructor(options: { min?: number; max?: number; }); evaluate(text: string): MetricScore; } declare const Metrics: { exactMatch(expected: string, caseSensitive?: boolean): MetricFn; contains(substring: string, caseSensitive?: boolean): MetricFn; containsAll(substrings: string[], caseSensitive?: boolean): MetricFn; regex(pattern: RegExp | string, flags?: string): MetricFn; jsonSchema(schema: Record): MetricFn; similarity(reference: string, threshold?: number): MetricFn; llmJudge(options: LLMJudgeOptions): MetricFn; custom(name: string, fn: (text: string) => MetricScore | Promise): MetricFn; /** * Assert the LLM called a specific tool. * Pass `JSON.stringify(response.content)` as the evaluated text. * @example * Metrics.toolCalled('search') * Metrics.toolCalled('search', { inputContains: 'Paris' }) */ toolCalled(toolName: string, options?: { inputContains?: string; }): MetricFn; /** * Assert a value at a dot-notation path in the parsed JSON response. * @example * Metrics.jsonPath('user.name', 'Alice') * Metrics.jsonPath('items.0.id', 42) */ jsonPath(path: string, expected: unknown): MetricFn; /** * Assert the response is not empty. Optionally enforce a minimum character length. * @example * Metrics.notEmpty() * Metrics.notEmpty({ minLength: 20 }) */ notEmpty(options?: { minLength?: number; }): MetricFn; /** * Assert the response word count is within a range. * @example * Metrics.wordCount({ min: 10, max: 200 }) * Metrics.wordCount({ max: 50 }) // enforce conciseness */ wordCount(options: { min?: number; max?: number; }): MetricFn; }; type Scrubber = (interaction: CassetteInteraction) => CassetteInteraction; /** * Scrubbers are composable helpers for use with `onBeforeRecord`. * * @example * const harness = new Harness({ * cassettesDir: './cassettes', * mode: 'record', * onBeforeRecord: Scrubbers.standard(), * }) */ declare const Scrubbers: { /** Redact Anthropic and OpenAI API keys */ apiKey(): Scrubber; /** Redact email addresses */ email(replacement?: string): Scrubber; /** Redact UUIDs */ uuid(replacement?: string): Scrubber; /** Redact IPv4 addresses */ ipAddress(replacement?: string): Scrubber; /** Redact any custom pattern */ custom(pattern: RegExp, replacement?: string): Scrubber; /** * Combine multiple scrubbers into one — applied left to right. * @example * Scrubbers.combine(Scrubbers.apiKey(), Scrubbers.email()) */ combine(...scrubbers: Scrubber[]): Scrubber; /** * Standard preset: redacts API keys, bearer tokens, emails, UUIDs. * Good starting point for most projects. */ standard(): Scrubber; }; declare function runEval(text: string, metrics: MetricFn[]): Promise; declare function saveBaseline(testName: string, result: EvalResult, cassettesDir: string): void; declare function compareBaseline(testName: string, result: EvalResult, cassettesDir: string, threshold?: number): RegressionResult; /** * Produces a deterministic SHA-256 hash for a cassette request. * The hash uniquely identifies a request for record/replay matching. */ declare function hashRequest(req: CassetteRequest): string; declare class CassetteStore { private filePath; private data; constructor(filePath: string); get path(): string; load(): CassetteFile; findById(id: string): CassetteInteraction | undefined; append(interaction: CassetteInteraction): void; private flush; } declare class HarnessError extends Error { constructor(message: string); } declare class CassetteMissError extends HarnessError { readonly hash: string; constructor(hash: string); } declare class CassetteOverwriteError extends HarnessError { readonly hash: string; constructor(hash: string); } declare class CassetteWriteError extends HarnessError { readonly filePath: string; constructor(filePath: string, cause: unknown); } declare class UnsupportedClientError extends HarnessError { constructor(); } export { type BaselineEntry, type BaselineSnapshot, type CassetteContentBlock, type CassetteFile, type CassetteInteraction, type CassetteMessage, type CassetteMetadata, CassetteMissError, type CassetteMode, CassetteOverwriteError, type CassetteParams, type CassetteRequest, type CassetteResponse, CassetteStore, type CassetteTool, type CassetteUsage, CassetteWriteError, Contains, ContainsAll, Custom, type EvalResult, ExactMatch, Harness, type HarnessConfig, HarnessError, JSONSchema, JsonPath, LLMJudge, type LLMJudgeOptions, type MetricFn, type MetricScore, Metrics, NotEmpty, type Provider, Regex, type RegressionDetail, type RegressionResult, Scrubbers, Similarity, ToolCalled, UnsupportedClientError, WordCount, compareBaseline, hashRequest, normalizedSimilarity, runEval, saveBaseline };