@docshield/didactic 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["PROVIDER_SPECS: Record<LLMProviders, ProviderSpec>","isObject","total","assignments: [number, number][]","fields: Record<string, FieldResult>","totalFields","passed","results: Record<string, FieldResult>","path","matchedPairs: [number, number][]","lines: string[]","report: OptimizationReport","failures: FailureData[]","failedFields: Record<string, { expected: unknown; actual: unknown }>","failures: BestRunFailure[]","partialFailures: BestRunPartialFailure[]","successes: BestRunSuccess[]","report: BestRunReport","iterationLogs: IterationLog[]","logContext: LogContext","bestPromptFailures: TestCaseResult<TInput, TOutput>[]","previousSuccessRate: number | undefined","iterations: IterationResult<TInput, TOutput>[]","streamOptions: Parameters<typeof client.messages.stream>[0]","completionOptions: OpenAI.ChatCompletionCreateParamsNonStreaming","runOptimize","optimize","createEndpoint","createFn"],"sources":["../src/types.ts","../src/constants.ts","../src/comparators.ts","../src/executors.ts","../src/matching.ts","../src/eval.ts","../src/optimizer-logging.ts","../src/optimizer.ts","../src/index.ts"],"sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════\n// COMPARATORS\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Result returned by a comparator function.\n */\nexport interface ComparatorResult {\n passed: boolean;\n similarity?: number; // 0.0-1.0, used for matching. If undefined, derived from passed (1.0 or 0.0)\n}\n\n/**\n * Context passed to comparators for cross-field access.\n */\nexport interface ComparatorContext {\n expectedParent: unknown;\n actualParent: unknown;\n}\n\n/**\n * A comparator function that compares expected vs actual.\n */\nexport type Comparator<T = unknown> = (\n expected: T,\n actual: T,\n context?: ComparatorContext\n) => ComparatorResult;\n\n/**\n * A map of field names to comparators.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type ComparatorMap = Record<string, Comparator<any>>;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// EXECUTORS\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * The result returned by an executor.\n */\nexport interface ExecutorResult<TOutput = unknown> {\n output: TOutput;\n additionalContext?: unknown;\n cost?: number;\n}\n\n/**\n * An executor that runs an LLM workflow.\n */\nexport type Executor<TInput = unknown, TOutput = unknown> = (\n input: TInput,\n systemPrompt?: string\n) => Promise<ExecutorResult<TOutput>>;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// EVAL\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * A single test case pairing input with expected output.\n */\nexport interface TestCase<TInput = unknown, TOutput = unknown> {\n input: TInput;\n expected: TOutput;\n}\n\n/**\n * Inline optimization config for didactic.eval().\n */\nexport type OptimizeConfig = {\n systemPrompt: string;\n targetSuccessRate: number;\n maxIterations?: number;\n maxCost?: number;\n apiKey: string;\n storeLogs?: boolean | string; // true = \"./didactic-logs/optimize_<timestamp>/summary.md\", string = custom path\n provider: LLMProviders;\n thinking?: boolean;\n};\n\n/**\n * Base eval configuration shared by both modes.\n */\ninterface BaseEvalConfig<TInput = unknown, TOutput = unknown> {\n systemPrompt?: string;\n executor: Executor<TInput, TOutput>;\n testCases: TestCase<TInput, TOutput>[];\n perTestThreshold?: number; // Default: 1.0 (all fields must pass)\n unorderedList?: boolean; // Default: false (ordered array comparison)\n optimize?: OptimizeConfig;\n rateLimitBatch?: number; // Run N test cases at a time (default: all in parallel)\n rateLimitPause?: number; // Wait N seconds between batches\n}\n\n/**\n * Flexible comparators configuration.\n * Accepts either a single comparator function or a field mapping object.\n * Single comparator will apply the comparator to the object, primitive, list of objects, or list of primitives. If `unorderedList` is true, the comparator will be applied to the list of items in the array by similarity rather than index position.\n */\nexport type ComparatorsConfig = ComparatorMap | Comparator<any>;\n\n/**\n * Main eval configuration.\n * Either `comparators` (field mapping or single function) OR `comparatorOverride` (whole-object) must be provided.\n */\nexport type EvalConfig<TInput = unknown, TOutput = unknown> =\n | (BaseEvalConfig<TInput, TOutput> & { comparators: ComparatorsConfig; comparatorOverride?: undefined })\n | (BaseEvalConfig<TInput, TOutput> & { comparatorOverride: Comparator<TOutput>; comparators?: undefined });\n\n/**\n * Result for a single field comparison.\n */\nexport interface FieldResult {\n passed: boolean;\n expected: unknown;\n actual: unknown;\n}\n\n/**\n * Result for a single test case.\n */\nexport interface TestCaseResult<TInput = unknown, TOutput = unknown> {\n input: TInput;\n expected: TOutput;\n actual?: TOutput;\n additionalContext?: unknown;\n cost?: number;\n passed: boolean;\n fields: Record<string, FieldResult>;\n error?: string;\n passedFields: number;\n totalFields: number;\n passRate: number;\n}\n\n/**\n * Eval result.\n */\nexport interface EvalResult<TInput = unknown, TOutput = unknown> {\n systemPrompt?: string;\n testCases: TestCaseResult<TInput, TOutput>[];\n passed: number;\n total: number;\n successRate: number;\n correctFields: number;\n totalFields: number;\n accuracy: number;\n cost: number;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// OPTIMIZER\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Chat message for LLM calls.\n */\nexport interface Message {\n role: 'system' | 'user' | 'assistant';\n content: string;\n}\n\n/**\n * Supported LLM providers.\n */\nexport enum LLMProviders {\n // Anthropic Claude 4.5\n anthropic_claude_opus = 'anthropic_claude_opus',\n anthropic_claude_sonnet = 'anthropic_claude_sonnet',\n anthropic_claude_haiku = 'anthropic_claude_haiku',\n // OpenAI GPT-5\n openai_gpt5 = 'openai_gpt5',\n openai_gpt5_mini = 'openai_gpt5_mini',\n}\n\n/**\n * LLM provider specification.\n */\nexport interface ProviderSpec {\n model: string;\n maxTokens: number;\n costPerMillionInput: number;\n costPerMillionOutput: number;\n}\n\n/**\n * Result for a single optimization iteration.\n */\nexport interface IterationResult<TInput = unknown, TOutput = unknown> {\n iteration: number;\n systemPrompt: string;\n passed: number;\n total: number;\n testCases: TestCaseResult<TInput, TOutput>[];\n cost: number;\n}\n\n/**\n * Final result from optimization.\n */\nexport interface OptimizeResult<TInput = unknown, TOutput = unknown> {\n success: boolean;\n finalPrompt: string;\n iterations: IterationResult<TInput, TOutput>[];\n totalCost: number;\n logFolder?: string;\n}\n","import { LLMProviders, ProviderSpec } from './types.js';\n\nexport const PROVIDER_SPECS: Record<LLMProviders, ProviderSpec> = {\n [LLMProviders.anthropic_claude_opus]: { model: 'claude-opus-4-5-20251101', maxTokens: 64000, costPerMillionInput: 5.00, costPerMillionOutput: 25.00 },\n [LLMProviders.anthropic_claude_sonnet]: { model: 'claude-sonnet-4-5-20251101', maxTokens: 64000, costPerMillionInput: 3.00, costPerMillionOutput: 15.00 },\n [LLMProviders.anthropic_claude_haiku]: { model: 'claude-haiku-4-5-20251101', maxTokens: 64000, costPerMillionInput: 1.00, costPerMillionOutput: 5.00 },\n [LLMProviders.openai_gpt5]: { model: 'gpt-5.2', maxTokens: 32000, costPerMillionInput: 1.75, costPerMillionOutput: 14.00 },\n [LLMProviders.openai_gpt5_mini]: { model: 'gpt-5-mini', maxTokens: 32000, costPerMillionInput: 0.25, costPerMillionOutput: 2.00 },\n};\n\n// Optimizer constants\nexport const ANTHROPIC_THINKING_BUDGET_TOKENS = 31999;\nexport const TOKENS_PER_MILLION = 1_000_000;\n\n// Executor constants\nexport const DEFAULT_ENDPOINT_TIMEOUT_MS = 30000;\n\n// Eval constants\nexport const DEFAULT_PER_TEST_THRESHOLD = 1.0;\n\n// Comparator constants\nexport const NAME_SUFFIXES = /(?<=\\S)\\s*,?\\s*(inc\\.?|llc\\.?|ltd\\.?|l\\.l\\.c\\.?|corp\\.?|corporation|company|co\\.?)$/i;\n","import type { Comparator, ComparatorContext, ComparatorResult } from './types.js';\nimport { NAME_SUFFIXES } from './constants.js';\nimport * as chrono from 'chrono-node';\nimport { differenceInDays } from 'date-fns';\nimport Levenshtein from 'levenshtein';\n\n// ═══════════════════════════════════════════════════════════════════════════\n// COMPARATORS\n// ═══════════════════════════════════════════════════════════════════════════\n// contains - substring check\n// custom - user-defined logic\n// date - normalized date comparison\n// exact - deep equality (default)\n// name - normalized name comparison\n// numeric - normalized number comparison (.nullable variant)\n// oneOf - enum validation\n// presence - value exists check\n// within - numeric tolerance\n\n\n/** Checks if actual string contains a substring. */\nexport function contains(substring: string): Comparator<string> {\n return (_expected, actual) => {\n const passed = actual.includes(substring);\n return { passed, similarity: passed ? 1.0 : 0.0 };\n };\n}\n\n/** Creates a comparator with custom logic. */\nexport function custom<T>(config: {\n compare: (expected: T, actual: T, context?: ComparatorContext) => boolean;\n}): Comparator<T> {\n return (expected, actual, context) => {\n const passed = config.compare(expected, actual, context);\n return { passed, similarity: passed ? 1.0 : 0.0 };\n };\n}\n\n/** Compares dates after normalizing various formats (ISO, US, written). */\nexport function date(expected: unknown, actual: unknown): ComparatorResult {\n const expDate = normalizeDate(expected);\n const actDate = normalizeDate(actual);\n\n if (expDate === null && actDate === null) {\n return { passed: true, similarity: 1.0 };\n }\n\n if (expDate === null || actDate === null) {\n return { passed: false, similarity: 0.0 };\n }\n\n const passed = expDate === actDate;\n const daysDiff = Math.abs(differenceInDays(new Date(expDate), new Date(actDate)));\n\n return { passed, similarity: Math.exp(-daysDiff / 30) };\n}\n\n/** Deep equality comparison. Default when no comparator is specified. */\nexport function exact(expected: unknown, actual: unknown): ComparatorResult {\n const passed = deepEqual(expected, actual);\n return {\n passed,\n similarity: passed ? 1.0 : 0.0,\n };\n}\n\n/** Compares names after normalizing case, whitespace, and business suffixes. */\nexport function name(expected: unknown, actual: unknown): ComparatorResult {\n const expName = normalizeName(expected);\n const actName = normalizeName(actual);\n\n if (expName === null && actName === null) {\n return { passed: true, similarity: 1.0 };\n }\n\n if (expName === null || actName === null) {\n return { passed: false, similarity: 0.0 };\n }\n\n if (expName === actName) {\n return { passed: true, similarity: 1.0 };\n }\n\n // First/last token matching for middle name tolerance\n const expTokens = expName.split(' ').filter(Boolean);\n const actTokens = actName.split(' ').filter(Boolean);\n\n if (expTokens.length >= 2 && actTokens.length >= 2) {\n if (expTokens[0] === actTokens[0] && expTokens.at(-1) === actTokens.at(-1)) {\n return { passed: true, similarity: 0.95 };\n }\n }\n\n // Fuzzy match using Levenshtein distance\n const distance = new Levenshtein(expName, actName).distance;\n const similarity = 1 - distance / Math.max(expName.length, actName.length);\n if (similarity >= 0.9) {\n return { passed: true, similarity };\n }\n\n return { passed: false, similarity };\n}\n\n/** Compares numbers after stripping currency symbols, commas, and formatting. */\nexport const numeric = Object.assign(\n (expected: unknown, actual: unknown) => numericCompare(expected, actual),\n { nullable: (expected: unknown, actual: unknown) => numericCompare(expected, actual, true) }\n);\n\nfunction numericCompare(expected: unknown, actual: unknown, nullable = false): ComparatorResult {\n let expNum = normalizeNumeric(expected);\n let actNum = normalizeNumeric(actual);\n\n if (nullable) {\n expNum ??= 0;\n actNum ??= 0;\n }\n\n if (expNum === null && actNum === null) {\n return { passed: true, similarity: 1.0 };\n }\n\n if (expNum === null || actNum === null) {\n return { passed: false, similarity: 0.0 };\n }\n\n const passed = expNum === actNum;\n return { passed, similarity: passed ? 1.0 : 0.0 };\n}\n\n/** Validates that actual equals expected AND both are in the allowed set. */\nexport function oneOf<T extends string>(allowedValues: readonly T[]): Comparator<T> {\n if (allowedValues.length === 0) {\n throw new Error('oneOf() requires at least one allowed value');\n }\n\n const allowed = new Set(allowedValues);\n\n return (expected, actual) => {\n const actualAllowed = allowed.has(actual);\n const passed = actualAllowed && expected === actual;\n return { passed, similarity: passed ? 1.0 : 0.0 };\n };\n}\n\n/** Passes if both absent, or if actual has any value when expected does. */\nexport function presence(expected: unknown, actual: unknown): ComparatorResult {\n const expectedPresent = expected != null && expected !== '';\n const actualPresent = actual != null && actual !== '';\n const passed = !expectedPresent || actualPresent;\n return { passed, similarity: passed ? 1.0 : 0.0 };\n}\n\n/** Checks if a numeric value is within tolerance (percentage or absolute). */\nexport function within(config: {\n tolerance: number;\n mode?: 'percentage' | 'absolute';\n}): Comparator<number> {\n const { tolerance, mode = 'percentage' } = config;\n\n return (expected, actual) => {\n const diff = Math.abs(expected - actual);\n const threshold = mode === 'absolute' ? tolerance : Math.abs(expected * tolerance);\n const passed = diff <= threshold;\n\n // Similarity: 1.0 at exact match, 0.5 at boundary, decays beyond\n const similarity = threshold > 0\n ? Math.exp(-diff / threshold * 0.693)\n : (diff === 0 ? 1.0 : 0.0);\n\n return { passed, similarity };\n };\n}\n\n\n// Private helpers\n\n/**\n * Deep equality comparison with cycle detection.\n * Uses WeakSet to track visited object pairs to prevent stack overflow on circular references.\n */\nfunction deepEqual(a: unknown, b: unknown, visited = new WeakSet<object>()): boolean {\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (typeof a !== typeof b) return false;\n\n // Handle arrays\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n\n // Check for cycles\n if (visited.has(a)) return true; // Already comparing this array\n visited.add(a);\n\n return a.every((item, i) => deepEqual(item, b[i], visited));\n }\n\n // Handle objects\n if (typeof a === 'object' && typeof b === 'object') {\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n\n // Check for cycles\n if (visited.has(aObj)) return true; // Already comparing this object\n visited.add(aObj);\n\n const aKeys = Object.keys(aObj);\n if (aKeys.length !== Object.keys(bObj).length) return false;\n return aKeys.every(key => deepEqual(aObj[key], bObj[key], visited));\n }\n\n return false;\n}\n\nfunction normalizeDate(value: unknown): string | null {\n if (value == null || value === '') return null;\n const parsed = chrono.parseDate(String(value));\n return parsed?.toISOString().split('T')[0] ?? null;\n}\n\nfunction normalizeName(value: unknown): string | null {\n if (value == null || value === '') return null;\n const str = String(value).toLowerCase().trim().replace(/\\s+/g, ' ').replace(NAME_SUFFIXES, '').trim();\n return str || null;\n}\n\nfunction normalizeNumeric(value: unknown): number | null {\n if (value == null || value === '') return null;\n\n const str = String(value);\n const isNegativeParens = /^\\(.*\\)$/.test(str.trim());\n\n let cleaned = str.replace(/[^0-9.\\-]/g, '');\n if (isNegativeParens && !cleaned.startsWith('-')) {\n cleaned = '-' + cleaned;\n }\n\n const num = parseFloat(cleaned);\n return isNaN(num) ? null : num;\n}\n","import type { Executor, ExecutorResult } from './types.js';\nimport { DEFAULT_ENDPOINT_TIMEOUT_MS } from './constants.js';\n\n/**\n * Configuration for endpoint executor.\n */\nexport interface EndpointConfig<TOutput = unknown> {\n method?: 'POST' | 'GET';\n headers?: Record<string, string>;\n mapResponse?: (response: any) => TOutput;\n mapAdditionalContext?: (response: any) => unknown;\n mapCost?: (response: any) => number;\n timeout?: number;\n}\n\n/**\n * Configuration for function executor.\n * @template TInput - Input type passed to the function\n * @template TOutput - Output type after mapResponse (what gets compared)\n * @template TRaw - Raw return type from fn (defaults to TOutput if no mapResponse)\n */\nexport interface FnConfig<TInput, TOutput, TRaw = TOutput> {\n fn: (input: TInput, systemPrompt?: string) => Promise<TRaw>;\n mapResponse?: (result: TRaw) => TOutput;\n mapAdditionalContext?: (result: TRaw) => unknown;\n mapCost?: (result: TRaw) => number;\n}\n\n/**\n * Creates an executor that calls an HTTP endpoint.\n *\n * @example\n * ```ts\n * const executor = endpoint('https://api.example.com/workflow', {\n * headers: { Authorization: 'Bearer token' },\n * });\n * ```\n */\nexport function endpoint<TInput = unknown, TOutput = unknown>(\n url: string,\n config: EndpointConfig<TOutput> = {}\n): Executor<TInput, TOutput> {\n const {\n method = 'POST',\n headers = {},\n mapResponse,\n mapAdditionalContext,\n mapCost,\n timeout = DEFAULT_ENDPOINT_TIMEOUT_MS,\n } = config;\n\n return async (input: TInput, systemPrompt?: string): Promise<ExecutorResult<TOutput>> => {\n const body = typeof input === 'object' && input !== null\n ? { ...input, systemPrompt }\n : { input, systemPrompt };\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`HTTP ${response.status}: ${text}`);\n }\n\n const data = await response.json();\n const additionalContext = mapAdditionalContext?.(data);\n const cost = mapCost?.(data) ?? 0;\n\n if (mapResponse) {\n return { output: mapResponse(data), additionalContext, cost };\n }\n\n return {\n output: data as TOutput,\n additionalContext,\n cost,\n };\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n };\n}\n\n/**\n * Creates an executor from a local function.\n *\n * @example\n * ```ts\n * const executor = fn({\n * fn: async (input, systemPrompt) => {\n * const result = await myLLMCall(input, systemPrompt);\n * return result;\n * },\n * });\n * ```\n *\n * @example With mapResponse to extract output from a richer response:\n * ```ts\n * const executor = fn({\n * fn: async (input, systemPrompt) => await startWorkflow({ ... }),\n * mapResponse: (result) => ({ documentType: result.documentType }),\n * mapCost: (result) => result.cost,\n * mapAdditionalContext: (result) => result.metadata,\n * });\n * ```\n */\nexport function fn<TInput, TOutput extends object, TRaw = TOutput>(\n config: FnConfig<TInput, TOutput, TRaw>\n): Executor<TInput, TOutput> {\n return async (input: TInput, systemPrompt?: string): Promise<ExecutorResult<TOutput>> => {\n const raw = await config.fn(input, systemPrompt);\n const output = config.mapResponse ? config.mapResponse(raw) : raw as unknown as TOutput;\n const additionalContext = config.mapAdditionalContext?.(raw);\n const cost = config.mapCost?.(raw) ?? 0;\n return { output, additionalContext, cost };\n };\n}\n\n/**\n * Creates a mock executor for testing.\n * Can accept either:\n * - An array of outputs (returned in sequence, cycling if more calls than outputs)\n * - A function that maps input to output\n *\n * @example Array-based:\n * ```ts\n * const executor = mock([\n * { premium: 12500, policyType: 'claims-made' },\n * { premium: 8200, policyType: 'entity' },\n * ]);\n * ```\n *\n * @example Function-based:\n * ```ts\n * const executor = mock((input) => ({\n * id: input.id,\n * processed: true,\n * }));\n * ```\n */\nexport function mock<TInput, TOutput extends object>(\n outputsOrFn: TOutput[] | ((input: TInput, systemPrompt?: string) => TOutput)\n): Executor<TInput, TOutput> {\n // Function-based mock\n if (typeof outputsOrFn === 'function') {\n return async (input: TInput, systemPrompt?: string): Promise<ExecutorResult<TOutput>> => {\n const output = outputsOrFn(input, systemPrompt);\n return { output };\n };\n }\n\n // Array-based mock\n const outputs = outputsOrFn;\n if (outputs.length === 0) {\n throw new Error('mock() requires at least one output');\n }\n\n let callIndex = 0;\n\n return async (): Promise<ExecutorResult<TOutput>> => {\n const output = outputs[callIndex % outputs.length];\n callIndex++;\n return { output };\n };\n}\n","import munkres from 'munkres-js';\nimport type { ComparatorMap } from './types.js';\nimport { exact } from './comparators.js'; // Used for primitive comparison\n\nexport interface MatchResult {\n assignments: [number, number][]; // [expIdx, actIdx] - matched pairs\n unmatchedExpected: number[];\n unmatchedActual: number[];\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Calculate similarity score between two values (0.0 to 1.0).\n * For arrays: recursively match and average similarity of paired elements.\n * For objects: average similarity across all fields using comparator results.\n * For primitives: uses exact comparison's similarity score.\n */\nfunction getSimilarity(\n expected: unknown,\n actual: unknown,\n comparators: ComparatorMap\n): number {\n // Arrays: recursively match and calculate average similarity\n if (Array.isArray(expected) && Array.isArray(actual)) {\n if (expected.length === 0 && actual.length === 0) {\n return 1.0;\n }\n if (expected.length === 0 || actual.length === 0) {\n return 0.0;\n }\n\n const result = matchArrays(expected, actual, comparators);\n let total = 0;\n for (const [expIdx, actIdx] of result.assignments) {\n total += getSimilarity(expected[expIdx], actual[actIdx], comparators);\n }\n\n // Penalize for unmatched items\n const maxLen = Math.max(expected.length, actual.length);\n return total / maxLen;\n }\n\n // Primitives (including type mismatches like array vs non-array)\n if (!isObject(expected) || !isObject(actual)) {\n const result = exact(expected, actual);\n return result.similarity ?? (result.passed ? 1.0 : 0.0);\n }\n\n const fields = Object.keys(expected).filter(key => comparators[key]);\n\n // Exit early if no fields with comparators to compare\n if (fields.length === 0) {\n return 1.0;\n }\n\n let total = 0;\n for (const key of fields) {\n const comparator = comparators[key];\n const result = comparator(expected[key], actual[key], { expectedParent: expected, actualParent: actual });\n total += result.similarity ?? (result.passed ? 1.0 : 0.0);\n }\n return total / fields.length;\n}\n\n/**\n * Find optimal pairing between expected and actual arrays using Hungarian algorithm.\n * Pure matching - no pass/fail determination.\n *\n * @param expected - Array of expected items\n * @param actual - Array of actual items\n * @param comparators - Map of field names to comparator functions\n * @returns Matching result with assignments and unmatched indices\n */\nexport function matchArrays(\n expected: unknown[],\n actual: unknown[],\n comparators: ComparatorMap = {}\n): MatchResult {\n // Handle empty arrays\n if (expected.length === 0) {\n return {\n assignments: [],\n unmatchedExpected: [],\n unmatchedActual: [...Array(actual.length).keys()],\n };\n }\n\n if (actual.length === 0) {\n return {\n assignments: [],\n unmatchedExpected: [...Array(expected.length).keys()],\n unmatchedActual: [],\n };\n }\n\n // Build cost matrix: cost = 1 - similarity (lower cost = better match)\n const matrix = expected.map(exp =>\n actual.map(act => 1 - getSimilarity(exp, act, comparators))\n );\n\n // Run Hungarian algorithm\n const rawAssignments = munkres(matrix);\n\n // Process assignments\n const assignments: [number, number][] = [];\n const matchedExp = new Set<number>();\n const matchedAct = new Set<number>();\n\n for (const [row, col] of rawAssignments) {\n // Accept all valid assignments from Hungarian (no threshold filtering)\n if (row < expected.length && col < actual.length) {\n assignments.push([row, col]);\n matchedExp.add(row);\n matchedAct.add(col);\n }\n }\n\n return {\n assignments,\n unmatchedExpected: [...Array(expected.length).keys()].filter(i => !matchedExp.has(i)),\n unmatchedActual: [...Array(actual.length).keys()].filter(i => !matchedAct.has(i)),\n };\n}\n","import type {\n EvalConfig,\n EvalResult,\n FieldResult,\n ComparatorMap,\n} from './types.js';\nimport { matchArrays } from './matching.js';\nimport { exact } from './comparators.js';\nimport { DEFAULT_PER_TEST_THRESHOLD } from './constants.js';\n\n/**\n * Run all test cases and return results.\n */\nexport async function evaluate<TInput, TOutput>(\n config: EvalConfig<TInput, TOutput>\n): Promise<EvalResult<TInput, TOutput>> {\n\n // Read config\n const { testCases, systemPrompt, executor, comparators, comparatorOverride } = config;\n\n if (testCases.length === 0) {\n throw new Error('testCases array cannot be empty');\n }\n\n if (!executor) {\n throw new Error('executor is required');\n }\n\n if (!comparators && !comparatorOverride) {\n throw new Error('either \"comparators\" (field mapping or single function) or \"comparatorOverride\" (whole-object) is required');\n }\n\n // Execute a single test case\n const executeTestCase = async ({ input, expected }: { input: TInput; expected: TOutput }) => {\n try {\n\n // Run the executor\n const result = await executor(input, systemPrompt);\n\n let fields: Record<string, FieldResult>;\n if (comparatorOverride) {\n // Whole-object comparison mode (custom override)\n const compResult = comparatorOverride(expected, result.output);\n fields = {\n '': {\n passed: compResult.passed,\n expected,\n actual: result.output,\n }\n };\n } else if (typeof comparators === 'function') {\n // Arrays always use element-wise comparison (for better debugging)\n if (Array.isArray(expected)) {\n // Descend into array elements (ordered or unordered based on config.unorderedList)\n fields = compareFields({\n expected,\n actual: result.output,\n comparators: { '': comparators },\n unorderedList: config.unorderedList,\n });\n } else {\n // Primitives and objects: whole-object comparison\n const compResult = comparators(expected, result.output, {\n expectedParent: undefined,\n actualParent: undefined,\n });\n fields = {\n '': {\n ...compResult,\n expected,\n actual: result.output,\n }\n };\n }\n } else {\n // Field-level comparison mode (field mapping)\n fields = compareFields({\n expected,\n actual: result.output,\n comparators,\n unorderedList: config.unorderedList,\n });\n }\n\n const passedFields = Object.values(fields).filter((f) => f.passed).length;\n const totalFields = Object.values(fields).length;\n const passRate = totalFields === 0 ? 1 : passedFields / totalFields;\n const threshold = config.perTestThreshold ?? DEFAULT_PER_TEST_THRESHOLD;\n const passed = passRate >= threshold;\n\n return {\n input,\n expected,\n actual: result.output,\n additionalContext: result.additionalContext,\n cost: result.cost ?? 0,\n passed,\n fields,\n passedFields,\n totalFields,\n passRate,\n };\n } catch (error) {\n return {\n input,\n expected,\n actual: undefined,\n cost: 0,\n passed: false,\n fields: {},\n passedFields: 0,\n totalFields: 0,\n passRate: 0,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n };\n\n // Run test cases (batched or all in parallel)\n const rateLimitBatch = config.rateLimitBatch;\n let results;\n\n if (rateLimitBatch && rateLimitBatch > 0) {\n // Batched execution: run N test cases at a time\n results = [];\n for (let i = 0; i < testCases.length; i += rateLimitBatch) {\n const batch = testCases.slice(i, i + rateLimitBatch);\n const batchResults = await Promise.all(batch.map(executeTestCase));\n results.push(...batchResults);\n\n // Pause between batches (skip after last batch)\n const rateLimitPause = config.rateLimitPause;\n if (rateLimitPause && rateLimitPause > 0 && i + rateLimitBatch < testCases.length) {\n await new Promise(r => setTimeout(r, rateLimitPause * 1000));\n }\n }\n } else {\n // Run all test cases in parallel\n results = await Promise.all(testCases.map(executeTestCase));\n }\n\n // Sort: failures first (by passRate ascending), then passes (100% at bottom) \n results.sort((a, b) => {\n if (a.passed !== b.passed) return a.passed ? 1 : -1;\n return a.passRate - b.passRate;\n });\n\n const passed = results.filter((r) => r.passed).length;\n const total = results.length;\n const successRate = total > 0 ? passed / total : 0;\n\n let correctFields = 0;\n let totalFields = 0;\n for (const r of results) {\n const fieldResults = Object.values(r.fields);\n totalFields += fieldResults.length;\n correctFields += fieldResults.filter((f) => f.passed).length;\n }\n const accuracy = totalFields > 0 ? correctFields / totalFields : 0;\n const cost = results.reduce((sum, r) => sum + (r.cost ?? 0), 0);\n\n return {\n systemPrompt,\n testCases: results,\n passed,\n total,\n successRate,\n correctFields,\n totalFields,\n accuracy,\n cost,\n };\n}\n\n/**\n * Recursively compare expected vs actual, returning field-level results.\n * Path patterns: 'carrier', 'quote.premium', '[0]', 'quotes[0].carrier'\n */\nfunction compareFields(opts: {\n expected: unknown;\n actual: unknown;\n comparators: ComparatorMap;\n path?: string;\n expectedParent?: unknown;\n actualParent?: unknown;\n unorderedList?: boolean;\n}): Record<string, FieldResult> {\n const { expected, actual, comparators, path = '', expectedParent, actualParent, unorderedList = false } = opts;\n const results: Record<string, FieldResult> = {};\n const indexPath = (i: number) => path ? `${path}[${i}]` : `[${i}]`;\n\n // ─── ARRAYS ─────────────────────────────────────────────────────────────────\n if (Array.isArray(expected)) {\n if (!Array.isArray(actual)) {\n return { [path]: { passed: false, expected, actual } };\n }\n if (expected.length === 0) {\n return {};\n }\n\n // Get matched pairs: [expectedIdx, actualIdx]\n let matchedPairs: [number, number][];\n\n // If unorderedList is true, use the matching algorithm to find the best pairs (expected[i] -> actual[j])\n if (unorderedList) {\n matchedPairs = matchArrays(expected, actual, comparators).assignments;\n } else {\n // Otherwise, use the simple index-based pairing (expected[i] -> actual[i])\n matchedPairs = [];\n for (let i = 0; i < expected.length && i < actual.length; i++) {\n matchedPairs.push([i, i]);\n }\n }\n\n const matchedIndices = new Set(matchedPairs.map(([i]) => i));\n\n // Compare matched pairs\n for (const [expIdx, actIdx] of matchedPairs) {\n Object.assign(results, compareFields({\n expected: expected[expIdx],\n actual: actual[actIdx],\n comparators,\n path: indexPath(expIdx),\n expectedParent,\n actualParent,\n unorderedList,\n }));\n }\n\n // Report unmatched expected items as failures\n const arrayFieldName = getFieldName(path);\n const hasArrayComparator = arrayFieldName in comparators || arrayFieldName === '';\n\n for (let i = 0; i < expected.length; i++) {\n if (matchedIndices.has(i)) continue;\n\n const item = expected[i];\n if (isObject(item)) {\n for (const [field, value] of Object.entries(item)) {\n if (field in comparators) {\n results[`${indexPath(i)}.${field}`] = { passed: false, expected: value, actual: undefined };\n }\n }\n } else if (hasArrayComparator) {\n results[indexPath(i)] = { passed: false, expected: item, actual: undefined };\n }\n }\n\n return results;\n }\n\n // ─── OBJECTS ────────────────────────────────────────────────────────────────\n if (isObject(expected)) {\n if (!isObject(actual)) {\n return { [path]: { passed: false, expected, actual } };\n }\n\n for (const [field, expValue] of Object.entries(expected)) {\n const fieldPath = path ? `${path}.${field}` : field;\n Object.assign(results, compareFields({\n expected: expValue,\n actual: actual[field],\n comparators,\n path: fieldPath,\n expectedParent: expected,\n actualParent: actual,\n unorderedList,\n }));\n }\n\n return results;\n }\n\n // ─── PRIMITIVES ─────────────────────────────────────────────────────────────\n const fieldName = getFieldName(path);\n const comparator = comparators[fieldName] ?? (fieldName === '' ? exact : undefined);\n\n if (!comparator) {\n return {};\n }\n\n const result = comparator(expected, actual, { expectedParent, actualParent });\n return { [path]: { ...result, expected, actual } };\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction getFieldName(path: string): string {\n const lastSegment = path.split('.').pop() || '';\n return lastSegment.replace(/\\[\\d+\\]$/, '');\n}\n","import type { TestCaseResult, OptimizeConfig, LLMProviders } from './types.js';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n// ═══════════════════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport interface IterationLog {\n iteration: number;\n systemPrompt: string;\n passed: number;\n total: number;\n correctFields: number;\n totalFields: number;\n testCases: TestCaseResult[];\n cost: number;\n cumulativeCost: number;\n duration: number;\n inputTokens: number;\n outputTokens: number;\n previousSuccessRate?: number;\n}\n\n/** Context passed to all logging functions for consistent output */\nexport interface LogContext {\n config: OptimizeConfig;\n startTime: Date;\n model: string;\n perTestThreshold?: number;\n rateLimitBatch?: number;\n rateLimitPause?: number;\n}\n\n/** Metadata for JSON report */\ninterface OptimizationMetadata {\n timestamp: string;\n model: string;\n provider: LLMProviders;\n thinking: boolean;\n targetSuccessRate: number;\n maxIterations: number | null;\n maxCost: number | null;\n testCaseCount: number;\n perTestThreshold: number;\n rateLimitBatch?: number;\n rateLimitPause?: number;\n}\n\n/** Summary stats for JSON report */\ninterface OptimizationSummary {\n totalIterations: number;\n totalDurationMs: number;\n totalCost: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n startRate: number;\n endRate: number;\n targetMet: boolean;\n}\n\n/** Best run info for JSON report */\ninterface BestRun {\n iteration: number;\n successRate: number;\n passed: number;\n total: number;\n fieldAccuracy: number;\n}\n\n/** Full JSON report structure */\ninterface OptimizationReport {\n metadata: OptimizationMetadata;\n summary: OptimizationSummary;\n best: BestRun;\n iterations: IterationData[];\n}\n\n/** Per-iteration data for JSON report */\ninterface IterationData {\n iteration: number;\n successRate: number;\n passed: number;\n total: number;\n correctFields: number;\n totalFields: number;\n fieldAccuracy: number;\n cost: number;\n cumulativeCost: number;\n durationMs: number;\n inputTokens: number;\n outputTokens: number;\n failures: FailureData[];\n}\n\n/** Failure data for JSON report */\ninterface FailureData {\n testIndex: number;\n input: unknown;\n expected: unknown;\n actual: unknown;\n fields: Record<string, { expected: unknown; actual: unknown; passed: boolean }>;\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// BestRun.json Types\n// ───────────────────────────────────────────────────────────────────────────\n\ninterface BestRunMetadata {\n iteration: number;\n model: string;\n provider: LLMProviders;\n thinking: boolean;\n targetSuccessRate: number;\n perTestThreshold: number;\n rateLimitBatch?: number;\n rateLimitPause?: number;\n}\n\ninterface BestRunResults {\n successRate: number;\n passed: number;\n total: number;\n fieldAccuracy: number;\n correctFields: number;\n totalFields: number;\n}\n\ninterface BestRunCost {\n iteration: number;\n cumulative: number;\n}\n\ninterface BestRunTiming {\n durationMs: number;\n inputTokens: number;\n outputTokens: number;\n}\n\ninterface BestRunFailure {\n testIndex: number;\n input: unknown;\n expected: unknown;\n actual: unknown;\n failedFields: Record<string, { expected: unknown; actual: unknown }>;\n}\n\ninterface BestRunPartialFailure {\n testIndex: number;\n passRate: number;\n input: unknown;\n expected: unknown;\n actual: unknown;\n failedFields: Record<string, { expected: unknown; actual: unknown }>;\n}\n\ninterface BestRunSuccess {\n testIndex: number;\n input: unknown;\n expected: unknown;\n actual: unknown;\n}\n\ninterface BestRunReport {\n metadata: BestRunMetadata;\n results: BestRunResults;\n cost: BestRunCost;\n timing: BestRunTiming;\n failures: BestRunFailure[];\n partialFailures: BestRunPartialFailure[];\n successes: BestRunSuccess[];\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// FORMATTERS\n// ═══════════════════════════════════════════════════════════════════════════\n\nfunction formatMsCompact(ms: number): string {\n const totalSeconds = Math.round(ms / 1000);\n if (totalSeconds < 60) return `${totalSeconds}s`;\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n}\n\nfunction formatProgressBar(rate: number, width = 20): string {\n const filled = Math.round(rate * width);\n const empty = width - filled;\n return '█'.repeat(filled) + '░'.repeat(empty);\n}\n\nfunction formatTokensCompact(tokens: number): string {\n if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M`;\n if (tokens >= 1_000) return `${Math.round(tokens / 1_000)}K`;\n return String(tokens);\n}\n\nexport function formatFailure(testCase: TestCaseResult): string {\n const lines: string[] = [];\n\n lines.push(`Input: ${JSON.stringify(testCase.input, null, 2)}`);\n lines.push(`Expected: ${JSON.stringify(testCase.expected, null, 2)}`);\n lines.push(`Actual: ${JSON.stringify(testCase.actual, null, 2)}`);\n\n if (testCase.additionalContext) {\n lines.push(`Context: ${JSON.stringify(testCase.additionalContext, null, 2)}`);\n }\n\n lines.push('');\n lines.push('Field-level failures:');\n\n for (const [fieldPath, result] of Object.entries(testCase.fields)) {\n if (!result.passed) {\n lines.push(` ${fieldPath || '(root)'}: expected ${JSON.stringify(result.expected)}, got ${JSON.stringify(result.actual)}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// HELPERS\n// ═══════════════════════════════════════════════════════════════════════════\n\nfunction findBestIteration(iterations: IterationLog[]): IterationLog {\n return iterations.reduce((best, curr) =>\n (curr.passed / curr.total) > (best.passed / best.total) ? curr : best\n );\n}\n\nfunction computeTotals(iterations: IterationLog[]): {\n totalInputTokens: number;\n totalOutputTokens: number;\n totalDuration: number;\n} {\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n let totalDuration = 0;\n\n for (const iter of iterations) {\n totalInputTokens += iter.inputTokens;\n totalOutputTokens += iter.outputTokens;\n totalDuration += iter.duration;\n }\n\n return { totalInputTokens, totalOutputTokens, totalDuration };\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// CONSOLE LOGGING\n// ═══════════════════════════════════════════════════════════════════════════\n\nfunction formatDurationForLog(ms: number): string {\n const seconds = Math.round(ms / 1000);\n if (seconds < 60) return `(${seconds}s)`;\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `(${minutes}m ${remainingSeconds}s)`;\n}\n\nexport function logIterationStart(iterationLabel: string): void {\n console.log(`\\n=== Optimization Iteration ${iterationLabel} ===`);\n}\n\nexport function logEvaluationStart(): void {\n console.log(` Evaluating prompt...`);\n}\n\nexport function logEvaluationResult(\n result: { passed: number; total: number; successRate: number; cost: number },\n cumulativeCost: number,\n durationMs: number\n): void {\n console.log(\n ` Result: ${result.passed}/${result.total} passed (${(result.successRate * 100).toFixed(1)}%) | Cost: $${result.cost.toFixed(4)} | Total: $${cumulativeCost.toFixed(4)} ${formatDurationForLog(durationMs)}`\n );\n}\n\nexport function logRegressionDetected(bestSuccessRate: number): void {\n console.log(` → Regression detected (was ${(bestSuccessRate * 100).toFixed(1)}%)`);\n}\n\nexport function logTargetReached(targetSuccessRate: number): void {\n console.log(` Target: ${(targetSuccessRate * 100).toFixed(0)}% | ✓ Target reached!`);\n}\n\nexport function logTargetFailures(targetSuccessRate: number, failureCount: number): void {\n console.log(` Target: ${(targetSuccessRate * 100).toFixed(0)}% | ${failureCount} failures to address`);\n}\n\nexport function logCostLimitReached(cumulativeCost: number): void {\n console.log(` Cost limit reached ($${cumulativeCost.toFixed(2)})`);\n}\n\nexport function logPatchGenerationStart(failureCount: number): void {\n console.log(``);\n console.log(` Generating ${failureCount} patches in parallel...`);\n}\n\nexport function logPatchGenerationResult(patchCost: number, cumulativeCost: number, durationMs: number): void {\n console.log(\n ` Patches generated | Cost: $${patchCost.toFixed(4)} | Total: $${cumulativeCost.toFixed(4)} ${formatDurationForLog(durationMs)}`\n );\n}\n\nexport function logMergeStart(): void {\n console.log(``);\n console.log(` Merging patches...`);\n}\n\nexport function logMergeResult(mergeCost: number, cumulativeCost: number, durationMs: number): void {\n console.log(\n ` Patches merged | Cost: $${mergeCost.toFixed(4)} | Total: $${cumulativeCost.toFixed(4)} ${formatDurationForLog(durationMs)}`\n );\n}\n\nexport function logPatchGenerationFailures(failedCount: number, totalCount: number): void {\n console.log(` ⚠ ${failedCount}/${totalCount} patch generations failed`);\n}\n\nexport function logOptimizationComplete(\n bestSuccessRate: number,\n targetSuccessRate: number,\n cumulativeCost: number\n): void {\n console.log(`\\n=== Optimization Complete ===`);\n console.log(\n `Best result: ${(bestSuccessRate * 100).toFixed(1)}% (target was ${(targetSuccessRate * 100).toFixed(0)}%)`\n );\n console.log(`Total cost: $${cumulativeCost.toFixed(4)}`);\n}\n\nexport function logLogsWritten(logPath: string): void {\n console.log(`Logs written to: ${logPath}`);\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// MARKDOWN REPORT (One Sheet - No Prompts, No Failures)\n// ═══════════════════════════════════════════════════════════════════════════\n\nfunction generateConfigSection(ctx: LogContext, testCaseCount: number): string[] {\n const lines: string[] = [];\n const maxIterLabel = ctx.config.maxIterations ?? (ctx.config.maxCost !== undefined ? '∞ (cost-limited)' : '5');\n\n lines.push('## Configuration');\n lines.push('| Setting | Value |');\n lines.push('|---------|-------|');\n lines.push(`| Model | ${ctx.model} |`);\n lines.push(`| Provider | ${ctx.config.provider} |`);\n lines.push(`| Thinking | ${ctx.config.thinking ? 'Enabled' : 'Disabled'} |`);\n lines.push(`| Target | ${(ctx.config.targetSuccessRate * 100).toFixed(0)}% |`);\n lines.push(`| Max Iterations | ${maxIterLabel} |`);\n if (ctx.config.maxCost !== undefined) {\n lines.push(`| Max Cost | $${ctx.config.maxCost.toFixed(2)} |`);\n }\n lines.push(`| Test Cases | ${testCaseCount} |`);\n if (ctx.rateLimitBatch !== undefined || ctx.rateLimitPause !== undefined) {\n const batch = ctx.rateLimitBatch ?? 'all';\n const pause = ctx.rateLimitPause ?? 0;\n lines.push(`| Rate Limit | ${batch} cases/batch, ${pause}s pause |`);\n }\n lines.push(`| Per-Test Threshold | ${((ctx.perTestThreshold ?? 1.0) * 100).toFixed(0)}% |`);\n lines.push('');\n\n return lines;\n}\n\nfunction generateBestRunSection(bestIter: IterationLog): string[] {\n const lines: string[] = [];\n const rate = (bestIter.passed / bestIter.total) * 100;\n const fieldAccuracy = bestIter.totalFields > 0\n ? (bestIter.correctFields / bestIter.totalFields) * 100\n : 100;\n\n lines.push('## Best Run');\n lines.push('| Metric | Value |');\n lines.push('|--------|-------|');\n lines.push(`| Iteration | ${bestIter.iteration} |`);\n lines.push(`| Success Rate | ${rate.toFixed(1)}% (${bestIter.passed}/${bestIter.total}) |`);\n lines.push(`| Field Accuracy | ${fieldAccuracy.toFixed(1)}% (${bestIter.correctFields}/${bestIter.totalFields}) |`);\n lines.push(`| Cost | $${bestIter.cost.toFixed(2)} |`);\n lines.push('');\n\n return lines;\n}\n\nfunction generateSummarySection(\n iterations: IterationLog[],\n success: boolean,\n totals: { totalInputTokens: number; totalOutputTokens: number; totalDuration: number }\n): string[] {\n const lines: string[] = [];\n const firstIter = iterations[0];\n const lastIter = iterations[iterations.length - 1];\n const startRate = firstIter ? (firstIter.passed / firstIter.total) * 100 : 0;\n const endRate = lastIter ? (lastIter.passed / lastIter.total) * 100 : 0;\n const totalCost = lastIter?.cumulativeCost ?? 0;\n\n lines.push('## Summary');\n lines.push('| Metric | Value |');\n lines.push('|--------|-------|');\n lines.push(`| Total Iterations | ${iterations.length} |`);\n lines.push(`| Total Duration | ${formatMsCompact(totals.totalDuration)} |`);\n lines.push(`| Start -> End | ${startRate.toFixed(1)}% -> ${endRate.toFixed(1)}% |`);\n lines.push(`| Target Met | ${success ? '✓ Yes' : '✗ No'} |`);\n lines.push(`| Total Tokens | ${formatTokensCompact(totals.totalInputTokens)} in / ${formatTokensCompact(totals.totalOutputTokens)} out |`);\n lines.push(`| Total Cost | $${totalCost.toFixed(2)} |`);\n lines.push('');\n\n return lines;\n}\n\nfunction generateRunsTable(iterations: IterationLog[]): string[] {\n const lines: string[] = [];\n const bestRate = Math.max(...iterations.map(i => i.passed / i.total));\n\n lines.push('## Runs');\n lines.push('| # | Rate | Fields | Cost | Duration | Tokens In/Out |');\n lines.push('|---|------|--------|------|----------|---------------|');\n\n for (let i = 0; i < iterations.length; i++) {\n const iter = iterations[i];\n const rate = iter.passed / iter.total;\n const ratePct = (rate * 100).toFixed(1);\n const fieldAccuracy = iter.totalFields > 0\n ? (iter.correctFields / iter.totalFields) * 100\n : 100;\n\n // Determine indicator: ★ for best, ↓ for regression\n let indicator = '';\n if (rate === bestRate) {\n indicator = ' ★';\n } else if (iter.previousSuccessRate !== undefined && rate < iter.previousSuccessRate) {\n indicator = ' ↓';\n }\n\n const tokens = `${formatTokensCompact(iter.inputTokens)} / ${formatTokensCompact(iter.outputTokens)}`;\n\n lines.push(`| ${iter.iteration} | ${ratePct}% (${iter.passed}/${iter.total})${indicator} | ${fieldAccuracy.toFixed(1)}% | $${iter.cost.toFixed(2)} | ${formatMsCompact(iter.duration)} | ${tokens} |`);\n }\n\n lines.push('');\n lines.push('★ = Best | ↓ = Regressed');\n lines.push('');\n\n return lines;\n}\n\nfunction generateProgressChart(iterations: IterationLog[], targetRate: number): string[] {\n const lines: string[] = [];\n const bestRate = Math.max(...iterations.map(i => i.passed / i.total));\n\n lines.push('## Progression');\n lines.push('```');\n for (const iter of iterations) {\n const rate = iter.total > 0 ? iter.passed / iter.total : 0;\n const pct = (rate * 100).toFixed(1);\n const bar = formatProgressBar(rate);\n let suffix = '';\n if (rate === bestRate) suffix += ' ★';\n if (rate >= targetRate) suffix += ' ✓';\n lines.push(`Iter ${iter.iteration}: ${bar} ${pct}% ${formatMsCompact(iter.duration)}${suffix}`);\n }\n lines.push('```');\n lines.push('');\n\n return lines;\n}\n\nexport function generateLogContent(\n iterations: IterationLog[],\n ctx: LogContext,\n success: boolean\n): string {\n const lines: string[] = [];\n const startTimeStr = ctx.startTime.toLocaleString();\n const testCaseCount = iterations[0]?.total ?? 0;\n const bestIter = findBestIteration(iterations);\n const totals = computeTotals(iterations);\n\n // Header\n lines.push('# Optimization Report');\n lines.push(`**Run:** ${startTimeStr}`);\n lines.push('');\n\n // Sections\n lines.push(...generateConfigSection(ctx, testCaseCount));\n lines.push(...generateBestRunSection(bestIter));\n lines.push(...generateSummarySection(iterations, success, totals));\n lines.push(...generateRunsTable(iterations));\n lines.push(...generateProgressChart(iterations, ctx.config.targetSuccessRate));\n\n // Footer with links to companion files\n lines.push('---');\n lines.push('Prompts: `prompts.md`');\n lines.push('Raw data: `rawData.json`');\n lines.push('Best run: `bestRun.json`');\n\n return lines.join('\\n');\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// FILE WRITERS\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport function writeLog(logPath: string, content: string): void {\n const dir = path.dirname(logPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(logPath, content, 'utf-8');\n}\n\nexport function writeRawDataJson(\n folderPath: string,\n iterations: IterationLog[],\n ctx: LogContext,\n success: boolean\n): void {\n const jsonPath = path.join(folderPath, 'rawData.json');\n const bestIter = findBestIteration(iterations);\n const totals = computeTotals(iterations);\n\n const firstIter = iterations[0];\n const lastIter = iterations[iterations.length - 1];\n\n const report: OptimizationReport = {\n metadata: {\n timestamp: ctx.startTime.toISOString(),\n model: ctx.model,\n provider: ctx.config.provider,\n thinking: ctx.config.thinking ?? false,\n targetSuccessRate: ctx.config.targetSuccessRate,\n maxIterations: ctx.config.maxIterations ?? null,\n maxCost: ctx.config.maxCost ?? null,\n testCaseCount: firstIter?.total ?? 0,\n perTestThreshold: ctx.perTestThreshold ?? 1.0,\n rateLimitBatch: ctx.rateLimitBatch,\n rateLimitPause: ctx.rateLimitPause,\n },\n summary: {\n totalIterations: iterations.length,\n totalDurationMs: totals.totalDuration,\n totalCost: lastIter?.cumulativeCost ?? 0,\n totalInputTokens: totals.totalInputTokens,\n totalOutputTokens: totals.totalOutputTokens,\n startRate: firstIter ? firstIter.passed / firstIter.total : 0,\n endRate: lastIter ? lastIter.passed / lastIter.total : 0,\n targetMet: success,\n },\n best: {\n iteration: bestIter.iteration,\n successRate: bestIter.passed / bestIter.total,\n passed: bestIter.passed,\n total: bestIter.total,\n fieldAccuracy: bestIter.totalFields > 0\n ? bestIter.correctFields / bestIter.totalFields\n : 1,\n },\n iterations: iterations.map((iter) => {\n const failures: FailureData[] = [];\n iter.testCases.forEach((tc, testIdx) => {\n if (!tc.passed) {\n failures.push({\n testIndex: testIdx,\n input: tc.input,\n expected: tc.expected,\n actual: tc.actual,\n fields: tc.fields,\n });\n }\n });\n\n return {\n iteration: iter.iteration,\n successRate: iter.passed / iter.total,\n passed: iter.passed,\n total: iter.total,\n correctFields: iter.correctFields,\n totalFields: iter.totalFields,\n fieldAccuracy: iter.totalFields > 0 ? iter.correctFields / iter.totalFields : 1,\n cost: iter.cost,\n cumulativeCost: iter.cumulativeCost,\n durationMs: iter.duration,\n inputTokens: iter.inputTokens,\n outputTokens: iter.outputTokens,\n failures,\n };\n }),\n };\n\n fs.writeFileSync(jsonPath, JSON.stringify(report, null, 2), 'utf-8');\n}\n\nexport function writePromptsFile(folderPath: string, iterations: IterationLog[], ctx: LogContext): void {\n const promptsPath = path.join(folderPath, 'prompts.md');\n const startTimeStr = ctx.startTime.toLocaleString();\n const bestIter = findBestIteration(iterations);\n const lines: string[] = [];\n\n lines.push('# Prompts Log');\n lines.push(`**Run:** ${startTimeStr}`);\n lines.push('');\n\n for (const iter of iterations) {\n const rate = iter.total > 0 ? (iter.passed / iter.total) * 100 : 0;\n lines.push(`## Iteration ${iter.iteration} | ${rate.toFixed(1)}% (${iter.passed}/${iter.total})`);\n lines.push('');\n lines.push('```');\n lines.push(iter.systemPrompt);\n lines.push('```');\n lines.push('');\n if (iter.iteration < iterations.length) {\n lines.push('---');\n lines.push('');\n }\n }\n\n // Add Best Prompt section at the end\n lines.push('---');\n lines.push('');\n const bestRate = (bestIter.passed / bestIter.total) * 100;\n lines.push(`## Best Prompt (Iteration ${bestIter.iteration}) | ${bestRate.toFixed(1)}% (${bestIter.passed}/${bestIter.total})`);\n lines.push('');\n lines.push('```');\n lines.push(bestIter.systemPrompt);\n lines.push('```');\n\n fs.writeFileSync(promptsPath, lines.join('\\n'), 'utf-8');\n}\n\nexport function writeBestRunJson(\n folderPath: string,\n iterations: IterationLog[],\n ctx: LogContext\n): void {\n const bestRunPath = path.join(folderPath, 'bestRun.json');\n const bestIter = findBestIteration(iterations);\n\n // Extract only failed fields (not all fields)\n const extractFailedFields = (\n fields: Record<string, { passed: boolean; expected: unknown; actual: unknown }>\n ): Record<string, { expected: unknown; actual: unknown }> => {\n const failedFields: Record<string, { expected: unknown; actual: unknown }> = {};\n for (const [fieldPath, result] of Object.entries(fields)) {\n if (!result.passed) {\n failedFields[fieldPath] = { expected: result.expected, actual: result.actual };\n }\n }\n return failedFields;\n };\n\n // Categorize test cases into three groups\n const failures: BestRunFailure[] = [];\n const partialFailures: BestRunPartialFailure[] = [];\n const successes: BestRunSuccess[] = [];\n\n bestIter.testCases.forEach((tc, testIdx) => {\n if (!tc.passed) {\n // Test failed overall (didn't meet perTestThreshold)\n failures.push({\n testIndex: testIdx,\n input: tc.input,\n expected: tc.expected,\n actual: tc.actual,\n failedFields: extractFailedFields(tc.fields),\n });\n } else if (tc.passRate < 1) {\n // Test passed but has some failing fields\n partialFailures.push({\n testIndex: testIdx,\n passRate: tc.passRate,\n input: tc.input,\n expected: tc.expected,\n actual: tc.actual,\n failedFields: extractFailedFields(tc.fields),\n });\n } else {\n // Test passed with 100% field accuracy\n successes.push({\n testIndex: testIdx,\n input: tc.input,\n expected: tc.expected,\n actual: tc.actual,\n });\n }\n });\n\n const report: BestRunReport = {\n metadata: {\n iteration: bestIter.iteration,\n model: ctx.model,\n provider: ctx.config.provider,\n thinking: ctx.config.thinking ?? false,\n targetSuccessRate: ctx.config.targetSuccessRate,\n perTestThreshold: ctx.perTestThreshold ?? 1.0,\n rateLimitBatch: ctx.rateLimitBatch,\n rateLimitPause: ctx.rateLimitPause,\n },\n results: {\n successRate: bestIter.passed / bestIter.total,\n passed: bestIter.passed,\n total: bestIter.total,\n fieldAccuracy: bestIter.totalFields > 0\n ? bestIter.correctFields / bestIter.totalFields\n : 1,\n correctFields: bestIter.correctFields,\n totalFields: bestIter.totalFields,\n },\n cost: {\n iteration: bestIter.cost,\n cumulative: bestIter.cumulativeCost,\n },\n timing: {\n durationMs: bestIter.duration,\n inputTokens: bestIter.inputTokens,\n outputTokens: bestIter.outputTokens,\n },\n failures,\n partialFailures,\n successes,\n };\n\n fs.writeFileSync(bestRunPath, JSON.stringify(report, null, 2), 'utf-8');\n}\n\nexport function writeFinalLogs(\n logPath: string,\n iterationLogs: IterationLog[],\n logContext: LogContext,\n success: boolean\n): void {\n // logPath is expected to be like: ./didactic-logs/optimize_<timestamp>/summary.md\n const folderPath = path.dirname(logPath);\n\n // Create folder if it doesn't exist\n if (!fs.existsSync(folderPath)) {\n fs.mkdirSync(folderPath, { recursive: true });\n }\n\n // Write summary.md\n const content = generateLogContent(iterationLogs, logContext, success);\n fs.writeFileSync(path.join(folderPath, 'summary.md'), content, 'utf-8');\n\n // Write prompts.md\n writePromptsFile(folderPath, iterationLogs, logContext);\n\n // Write rawData.json\n writeRawDataJson(folderPath, iterationLogs, logContext, success);\n\n // Write bestRun.json\n writeBestRunJson(folderPath, iterationLogs, logContext);\n}\n","import type {\n EvalConfig,\n EvalResult,\n TestCaseResult,\n OptimizeConfig,\n IterationResult,\n OptimizeResult,\n Message,\n} from './types.js';\nimport { PROVIDER_SPECS, ANTHROPIC_THINKING_BUDGET_TOKENS, TOKENS_PER_MILLION } from './constants.js';\nimport { evaluate } from './eval.js';\nimport Anthropic from '@anthropic-ai/sdk';\nimport OpenAI from 'openai';\nimport * as path from 'path';\nimport {\n IterationLog,\n LogContext,\n formatFailure,\n generateLogContent,\n logCostLimitReached,\n logEvaluationResult,\n logEvaluationStart,\n logIterationStart,\n logLogsWritten,\n logMergeResult,\n logMergeStart,\n logOptimizationComplete,\n logPatchGenerationFailures,\n logPatchGenerationResult,\n logPatchGenerationStart,\n logRegressionDetected,\n logTargetFailures,\n logTargetReached,\n writeLog,\n writeFinalLogs,\n} from './optimizer-logging.js';\n\ninterface LLMResult {\n text: string;\n cost: number;\n inputTokens: number;\n outputTokens: number;\n}\n\nexport async function optimize<TInput, TOutput>(\n evalConfig: EvalConfig<TInput, TOutput>,\n config: OptimizeConfig\n): Promise<OptimizeResult<TInput, TOutput>> {\n if (!config.apiKey) { \n throw new Error('apiKey is required');\n }\n if (!config.systemPrompt) {\n throw new Error('systemPrompt is required');\n }\n if (config.targetSuccessRate < 0 || config.targetSuccessRate > 1) {\n throw new Error('targetSuccessRate must be between 0 and 1');\n }\n\n const iterationLogs: IterationLog[] = [];\n\n const maxIterations = config.maxIterations ?? (config.maxCost !== undefined ? Infinity : 5);\n const startTime = new Date();\n const model = PROVIDER_SPECS[config.provider].model;\n\n // Context of the iteration to pass to optimizer-logging functions\n const logContext: LogContext = {\n config,\n startTime,\n model,\n perTestThreshold: evalConfig.perTestThreshold,\n rateLimitBatch: evalConfig.rateLimitBatch,\n rateLimitPause: evalConfig.rateLimitPause,\n };\n\n // Initialize prompts\n let currentPrompt = config.systemPrompt;\n let bestPrompt = currentPrompt;\n\n // Best run trackers\n let bestSuccessRate = 0;\n let bestPromptFailures: TestCaseResult<TInput, TOutput>[] = [];\n\n // Cost trackers\n let cumulativeCost = 0;\n let previousSuccessRate: number | undefined;\n\n // If enabled, store enriched logs to a folder\n // Folder: ./didactic-logs/optimize_<timestamp>/\n // Contains 4 files:\n // 1. summary.md - main report with configuration, metrics, and progress\n // 2. prompts.md - prompts used in each iteration\n // 3. rawData.json - all iteration data for analysis\n // 4. bestRun.json - comprehensive best run with all test results\n const logPath = config.storeLogs\n ? (typeof config.storeLogs === 'string'\n ? config.storeLogs\n : `./didactic-logs/optimize_${startTime.getTime()}/summary.md`)\n : undefined;\n\n // Helper function to record iteration of the optimization loop\n const recordIteration = (\n iteration: number,\n systemPrompt: string,\n result: EvalResult<TInput, TOutput>,\n cost: number,\n durationMs: number,\n inputTokens: number,\n outputTokens: number\n ): void => {\n iterationLogs.push({\n iteration,\n systemPrompt,\n passed: result.passed,\n total: result.total,\n correctFields: result.correctFields,\n totalFields: result.totalFields,\n testCases: result.testCases,\n cost,\n cumulativeCost,\n duration: durationMs,\n inputTokens,\n outputTokens,\n previousSuccessRate,\n });\n };\n\n const finalizeOptimization = (success: boolean, finalPrompt: string): OptimizeResult<TInput, TOutput> => {\n const logFolder = logPath ? path.dirname(logPath) : undefined;\n logOptimizationComplete(bestSuccessRate, config.targetSuccessRate, cumulativeCost);\n if (logPath) {\n writeFinalLogs(logPath, iterationLogs, logContext, success);\n if (logFolder) {\n logLogsWritten(logFolder);\n }\n }\n const iterations: IterationResult<TInput, TOutput>[] = iterationLogs.map((iter) => ({\n iteration: iter.iteration,\n systemPrompt: iter.systemPrompt,\n passed: iter.passed,\n total: iter.total,\n testCases: iter.testCases as TestCaseResult<TInput, TOutput>[],\n cost: iter.cost,\n }));\n return logFolder\n ? { success, finalPrompt, iterations, totalCost: cumulativeCost, logFolder }\n : { success, finalPrompt, iterations, totalCost: cumulativeCost };\n };\n\n // Main optimization loop\n for (let i = 1; i <= maxIterations; i++) {\n const iterationStart = Date.now();\n let iterInputTokens = 0;\n let iterOutputTokens = 0;\n\n const iterationLabel = maxIterations === Infinity ? `${i}` : `${i}/${maxIterations}`;\n\n logIterationStart(iterationLabel);\n logEvaluationStart();\n\n const evalStart = Date.now();\n const result = await evaluate({ ...evalConfig, systemPrompt: currentPrompt });\n\n cumulativeCost += result.cost;\n logEvaluationResult(result, cumulativeCost, Date.now() - evalStart);\n\n const regressed = i > 1 && result.successRate < bestSuccessRate;\n if (regressed) {\n logRegressionDetected(bestSuccessRate);\n }\n\n if (result.successRate > bestSuccessRate) {\n bestSuccessRate = result.successRate;\n bestPrompt = currentPrompt;\n bestPromptFailures = result.testCases.filter((tc) => !tc.passed);\n }\n\n // Target reached\n if (result.successRate >= config.targetSuccessRate) {\n logTargetReached(config.targetSuccessRate);\n recordIteration(i, currentPrompt, result, result.cost, Date.now() - iterationStart, iterInputTokens, iterOutputTokens);\n return finalizeOptimization(true, currentPrompt);\n }\n\n const failures = result.testCases.filter((tc) => !tc.passed);\n if (failures.length === 0) {\n recordIteration(i, currentPrompt, result, result.cost, Date.now() - iterationStart, iterInputTokens, iterOutputTokens);\n return finalizeOptimization(true, currentPrompt);\n }\n\n logTargetFailures(config.targetSuccessRate, failures.length);\n\n // Cost limit before patches\n if (config.maxCost !== undefined && cumulativeCost >= config.maxCost) {\n logCostLimitReached(cumulativeCost);\n recordIteration(i, currentPrompt, result, result.cost, Date.now() - iterationStart, iterInputTokens, iterOutputTokens);\n return finalizeOptimization(false, bestPrompt);\n }\n\n // Generate patches\n logPatchGenerationStart(failures.length);\n const patchStart = Date.now();\n\n const patchSettled = await Promise.allSettled(\n failures.map((failure) => generatePatch(failure, currentPrompt, config, regressed ? bestPrompt : undefined, regressed ? bestPromptFailures : undefined))\n );\n const patchResults = patchSettled\n .filter((r): r is PromiseFulfilledResult<LLMResult> => r.status === 'fulfilled')\n .map((r) => r.value);\n\n const failedPatchCount = patchSettled.filter((r) => r.status === 'rejected').length;\n if (failedPatchCount > 0) {\n logPatchGenerationFailures(failedPatchCount, failures.length);\n }\n\n if (patchResults.length === 0) {\n recordIteration(i, currentPrompt, result, result.cost, Date.now() - iterationStart, iterInputTokens, iterOutputTokens);\n continue;\n }\n\n const patches = patchResults.map((r) => r.text);\n\n // Track patch cost and tokens\n const patchCost = patchResults.reduce((sum, r) => sum + r.cost, 0);\n const patchInputTokens = patchResults.reduce((sum, r) => sum + r.inputTokens, 0);\n const patchOutputTokens = patchResults.reduce((sum, r) => sum + r.outputTokens, 0);\n iterInputTokens += patchInputTokens;\n iterOutputTokens += patchOutputTokens;\n cumulativeCost += patchCost;\n logPatchGenerationResult(patchCost, cumulativeCost, Date.now() - patchStart);\n\n // Cost limit before merge\n if (config.maxCost !== undefined && cumulativeCost >= config.maxCost) {\n logCostLimitReached(cumulativeCost);\n recordIteration(i, currentPrompt, result, result.cost + patchCost, Date.now() - iterationStart, iterInputTokens, iterOutputTokens);\n return finalizeOptimization(false, bestPrompt);\n }\n\n // Merge patches\n logMergeStart();\n const mergeStart = Date.now();\n const mergeResult = await mergePatches(patches, currentPrompt, config);\n iterInputTokens += mergeResult.inputTokens;\n iterOutputTokens += mergeResult.outputTokens;\n cumulativeCost += mergeResult.cost;\n logMergeResult(mergeResult.cost, cumulativeCost, Date.now() - mergeStart);\n\n // Record iteration\n const iterCost = result.cost + patchCost + mergeResult.cost;\n recordIteration(i, currentPrompt, result, iterCost, Date.now() - iterationStart, iterInputTokens, iterOutputTokens);\n if (logPath) writeLog(logPath, generateLogContent(iterationLogs, logContext, false));\n\n // Cost limit after merge\n if (config.maxCost !== undefined && cumulativeCost >= config.maxCost) {\n logCostLimitReached(cumulativeCost);\n return finalizeOptimization(false, bestPrompt);\n }\n\n previousSuccessRate = result.successRate;\n currentPrompt = mergeResult.text;\n }\n\n // Loop exhausted\n return finalizeOptimization(false, bestPrompt);\n}\n\nasync function callLLM(messages: Message[], config: OptimizeConfig, useThinking: boolean = false): Promise<LLMResult> {\n const spec = PROVIDER_SPECS[config.provider];\n\n try {\n // Anthropic\n if (config.provider.startsWith('anthropic')) {\n const client = new Anthropic({ apiKey: config.apiKey });\n const streamOptions: Parameters<typeof client.messages.stream>[0] = {\n model: spec.model,\n max_tokens: spec.maxTokens,\n system: messages.find((m) => m.role === 'system')?.content,\n messages: messages\n .filter((m) => m.role !== 'system')\n .map((m) => ({ role: m.role as 'user' | 'assistant', content: m.content })),\n };\n\n if (useThinking) {\n streamOptions.thinking = { type: 'enabled', budget_tokens: ANTHROPIC_THINKING_BUDGET_TOKENS };\n }\n\n const stream = client.messages.stream(streamOptions);\n const finalMessage = await stream.finalMessage();\n\n const textBlocks = finalMessage.content\n .filter((block) => block.type === 'text')\n .map((block) => block.text);\n const text = textBlocks.length > 0 ? textBlocks.join(' ') : '';\n const inputTokens = finalMessage.usage.input_tokens;\n const outputTokens = finalMessage.usage.output_tokens;\n const cost = (inputTokens * spec.costPerMillionInput + outputTokens * spec.costPerMillionOutput) / TOKENS_PER_MILLION;\n\n return { text, cost, inputTokens, outputTokens };\n }\n\n // OpenAI\n if (config.provider.startsWith('openai')) {\n const client = new OpenAI({ apiKey: config.apiKey });\n const completionOptions: OpenAI.ChatCompletionCreateParamsNonStreaming = {\n model: spec.model,\n messages: messages.map((m) => ({ role: m.role, content: m.content })),\n max_completion_tokens: spec.maxTokens,\n };\n\n if (useThinking) {\n completionOptions.reasoning_effort = 'xhigh';\n }\n\n const response = await client.chat.completions.create(completionOptions);\n const text = response.choices[0].message.content ?? '';\n const inputTokens = response.usage?.prompt_tokens ?? 0;\n const outputTokens = response.usage?.completion_tokens ?? 0;\n const cost = (inputTokens * spec.costPerMillionInput + outputTokens * spec.costPerMillionOutput) / TOKENS_PER_MILLION;\n\n return { text, cost, inputTokens, outputTokens };\n }\n\n throw new Error(`Unsupported provider: ${config.provider}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`LLM call failed (${spec.model}): ${message}`);\n }\n}\n\nasync function generatePatch(\n failure: TestCaseResult,\n currentPrompt: string,\n config: OptimizeConfig,\n previousBetterPrompt?: string,\n previousBetterPromptFailures?: TestCaseResult[]\n): Promise<LLMResult>\n {\n // Build patch context\n let userContent = `\n Current system prompt:\n ---\n ${currentPrompt}\n ---\n\n A test case failed:\n ${formatFailure(failure)}\n `;\n\n // If previous better prompt is provided, build failures context\n if (previousBetterPrompt) {\n\n // Get failures from previous better prompt\n const failuresContext = previousBetterPromptFailures && previousBetterPromptFailures.length > 0\n ? previousBetterPromptFailures.map((f, i) => `${i + 1}. ${formatFailure(f)}`).join('\\n\\n')\n : 'None recorded';\n\n userContent += `\n Note: The current prompt is a REGRESSION from a better-performing version.\n Previous (better) prompt for reference:\n ---\n ${previousBetterPrompt}\n ---\n\n The failures the better prompt had:\n ${failuresContext}\n\n Your changes introduced new failures instead of fixing the above.\n Analyze what changed between the two prompts that might have caused this regression.\n Are there any new failures that were not present in the previous better prompt?\n Are there any failures that were present in the previous better prompt but not in the current prompt?\n Did any of our patches contradict any of the new failures? \n `;\n }\n\n userContent += `\n Suggest a specific change to the system prompt that would fix this failure.\n Be concise. Output ONLY the suggested patch/change, not the full prompt.\n DO NOT overfit the prompt to the test case.\n Generalize examples if you choose to use them.\n `;\n\n const systemContent = `\n 'You are optimizing a system prompt for an LLM workflow.\n Analyze the failure and suggest a specific, focused change to improve the prompt. \n Do NOT overfit. Be generalizable. \n\n <examples>\n VERY IMPORTANT, CRITICAL!!!\n Examples MUST be anonymized.\n NEVER use specific names, dates, or other identifying information UNLESS it's a universal fact: \n - example: (for an invoice processor) \n - task: extract data from parsed invoices\n - failure context: (returned expected: true, actual: false)\n - prompt patch: \"if you see \"Restocked\" on a Schedule B report of a Shopify invoice, mark returned as true.\" <- this is kind of specific, but it's a universal fact for the problem and could satisfy other inputs.)\n \n - example: (for a calendar app)\n - task: extract cost from calendar event\n - failure context: (cost expected: 123.45, actual: 167.89)\n - prompt patch: \"if you see \"Daisy\" in the name field, return 123.45 for cost\" <- this is too specific, it's overfit to a specific failure. The spirit of the failure is an incorrect extraction, you should look for the expected in the context and determine how the prompt could be modified to acheive the expected output.)\n </examples>\n `;\n\n const messages: Message[] = [\n { role: 'system', content: systemContent },\n { role: 'user', content: userContent },\n ];\n\n return callLLM(messages, config, config.thinking ?? false);\n}\n\nasync function mergePatches(patches: string[], currentPrompt: string, config: OptimizeConfig): Promise<LLMResult> {\n\n const systemContent = `\n You are an expert LLM prompt editor. \n You are merging improvements into a system prompt. \n Incorporate the suggestions while keeping the prompt clear and coherent.\n `;\n\n const userContent = `\n Current prompt:\n ---\n ${currentPrompt}\n ---\n\n Suggested improvements:\n ${patches.map((p, i) => `${i + 1}. ${p}`).join('\\n\\n')}\n\n Create a single improved system prompt that incorporates these suggestions.\n Be mindful of the size of the new prompt.\n Use discretion when merging the patches, if you see duplicate information, emphasize it but don't repeat it.\n Output ONLY the new system prompt, nothing else.\n Respect enums.\n `;\n \n const messages: Message[] = [\n { role: 'system', content: systemContent },\n {\n role: 'user',\n content: userContent,\n },\n ];\n\n return callLLM(messages, config, config.thinking ?? false);\n}\n","// Re-export types\nexport type {\n // Comparator types\n Comparator,\n ComparatorContext,\n ComparatorResult,\n ComparatorMap,\n ComparatorsConfig,\n // Executor types\n Executor,\n ExecutorResult,\n // Eval types\n TestCase,\n EvalConfig,\n FieldResult,\n TestCaseResult,\n EvalResult,\n // Optimizer types\n Message,\n OptimizeConfig,\n IterationResult,\n OptimizeResult,\n} from './types.js';\n\n// Re-export LLM provider enum\nexport { LLMProviders } from './types.js';\n\n// Re-export executor config types\nexport type { EndpointConfig, FnConfig } from './executors.js';\n\n// Re-export comparators\nexport { within, oneOf, presence, custom, exact, contains, numeric, date, name } from './comparators.js';\n\n// Re-export executors\nexport { endpoint, fn, mock } from './executors.js';\n\n// Re-export eval\nexport { evaluate } from './eval.js';\n\n// Re-export optimizer\nexport { optimize } from './optimizer.js';\n\n// Main didact namespace\nimport type { EvalConfig, EvalResult, Executor, OptimizeConfig, OptimizeResult } from './types.js';\nimport type { EndpointConfig, FnConfig } from './executors.js';\nimport { evaluate } from './eval.js';\nimport { optimize as runOptimize } from './optimizer.js';\nimport { endpoint as createEndpoint, fn as createFn } from './executors.js';\n\n/**\n * Overloaded eval function with proper return type inference.\n */\nfunction didacticEval<TInput, TOutput>(config: EvalConfig<TInput, TOutput> & { optimize: OptimizeConfig }): Promise<OptimizeResult<TInput, TOutput>>;\nfunction didacticEval<TInput, TOutput>(config: EvalConfig<TInput, TOutput> & { optimize?: undefined }): Promise<EvalResult<TInput, TOutput>>;\nfunction didacticEval<TInput, TOutput>(config: EvalConfig<TInput, TOutput>): Promise<EvalResult<TInput, TOutput> | OptimizeResult<TInput, TOutput>>;\nfunction didacticEval<TInput, TOutput>(config: EvalConfig<TInput, TOutput>): Promise<EvalResult<TInput, TOutput> | OptimizeResult<TInput, TOutput>> {\n if (config.optimize) {\n const { optimize, ...evalConfig } = config;\n return runOptimize(evalConfig, optimize);\n }\n return evaluate(config);\n}\n\n/**\n * Main didactic namespace for fluent API.\n *\n * @example\n * ```ts\n * import { didactic, within, oneOf, presence } from 'didactic';\n *\n * const result = await didactic.eval({\n * systemPrompt: 'Extract insurance quotes from broker emails.',\n * executor: didactic.endpoint('https://api.example.com/workflow'),\n * comparators: {\n * premium: within({ tolerance: 0.05 }),\n * policyType: oneOf(['claims-made', 'occurrence']),\n * entityName: presence,\n * },\n * testCases: [\n * {\n * input: { emailId: 'email-123' },\n * expected: { premium: 12500, policyType: 'claims-made', entityName: 'Acme Corp' },\n * },\n * ],\n * });\n *\n * console.log(`${result.passed}/${result.total} passed`);\n * ```\n */\nexport const didactic = {\n /**\n * Run an eval (or optimization if optimize config is present).\n */\n eval: didacticEval,\n\n /**\n * Run optimization to improve a system prompt.\n */\n optimize<TInput, TOutput>(\n evalConfig: EvalConfig<TInput, TOutput>,\n config: OptimizeConfig\n ): Promise<OptimizeResult<TInput, TOutput>> {\n return runOptimize(evalConfig, config);\n },\n\n /**\n * Create an executor that calls an HTTP endpoint.\n */\n endpoint<TInput = unknown, TOutput = unknown>(\n url: string,\n config?: EndpointConfig<TOutput>\n ): Executor<TInput, TOutput> {\n return createEndpoint(url, config);\n },\n\n /**\n * Create an executor from a local function.\n */\n fn<TInput, TOutput extends object>(\n config: FnConfig<TInput, TOutput>\n ): Executor<TInput, TOutput> {\n return createFn(config);\n },\n};\n\n// Default export\nexport default didactic;\n\n// Legacy alias for backwards compatibility during transition\nexport { didactic as didact };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["PROVIDER_SPECS: Record<LLMProviders, ProviderSpec>","streamOptions: Parameters<typeof client.messages.stream>[0]","completionOptions: OpenAI.ChatCompletionCreateParamsNonStreaming","LLM_COMPARE_SCHEMA: JSONSchema","isObject","total","assignments: [number, number][]","activeSpinner: Ora | null","bar: cliProgress.SingleBar | null","tracker: ProgressTracker | null","lines: string[]","report: OptimizationReport","failures: FailureData[]","failedFields: Record<string, { expected: unknown; actual: unknown }>","failures: BestRunFailure[]","partialFailures: BestRunPartialFailure[]","successes: BestRunSuccess[]","report: BestRunReport","report: EvalReport","fields: Record<string, FieldResult>","comparatorConfig: NestedComparatorConfig","totalFields","passed","comparatorCost","results: TestCaseResult<TInput, TOutput>[]","total","evalResult: EvalResult<TInput, TOutput>","results: Record<string, FieldResult>","path","itemComparators: NestedComparatorConfig","matchedPairs: [number, number][]","fieldComparators: NestedComparatorConfig","iterationLogs: IterationLog[]","logContext: LogContext","bestPromptFailures: TestCaseResult<TInput, TOutput>[]","previousSuccessRate: number | undefined","iterations: IterationResult<TInput, TOutput>[]","messages: Message[]","runOptimize","optimize","createEndpoint","createFn"],"sources":["../src/types.ts","../src/library/constants.ts","../src/library/llm/llm-client.ts","../src/eval/comparators/comparators.ts","../src/eval/comparators/matching.ts","../src/optimizer/ui.ts","../src/optimizer/optimizer-logging.ts","../src/eval/eval-logging.ts","../src/eval/eval.ts","../src/optimizer/prompts.ts","../src/optimizer/optimizer.ts","../src/eval/executors.ts","../src/index.ts"],"sourcesContent":["import type { OptimizeConfig } from './optimizer/types.js';\n\n// ═══════════════════════════════════════════════════════════════════════════\n// LLM PROVIDERS\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Supported LLM providers.\n * Used by both optimizer and LLM-based comparators.\n */\nexport enum LLMProviders {\n // Anthropic Claude 4.5\n anthropic_claude_opus = 'anthropic_claude_opus',\n anthropic_claude_sonnet = 'anthropic_claude_sonnet',\n anthropic_claude_haiku = 'anthropic_claude_haiku',\n // OpenAI GPT-5\n openai_gpt5 = 'openai_gpt5',\n openai_gpt5_mini = 'openai_gpt5_mini',\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// OPTIMIZER\n// ═══════════════════════════════════════════════════════════════════════════\n\n// Re-export public optimizer types\nexport type {\n OptimizeConfig,\n IterationResult,\n OptimizeResult,\n} from './optimizer/types.js';\n\n// ═══════════════════════════════════════════════════════════════════════════\n// COMPARATORS\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * Result returned by a comparator function.\n */\nexport interface ComparatorResult {\n passed: boolean;\n similarity?: number; // 0.0-1.0, used for matching. If undefined, derived from passed (1.0 or 0.0)\n rationale?: string; // Optional explanation (e.g., from LLM comparators)\n cost?: number; // Optional cost of this comparison (e.g., for LLM comparators)\n}\n\n/**\n * LLM configuration for use by LLM-based comparators.\n * Can be specified at the top level of eval config to avoid repeating apiKey.\n */\nexport interface LLMConfig {\n apiKey: string;\n provider?: LLMProviders;\n}\n\n/**\n * Context passed to comparators for cross-field access.\n */\nexport interface ComparatorContext {\n expectedParent: unknown;\n actualParent: unknown;\n llmConfig?: LLMConfig;\n}\n\n/**\n * A comparator function that compares expected vs actual.\n * Can be synchronous or asynchronous (for LLM-based comparators).\n */\nexport type Comparator<T = unknown> = (\n expected: T,\n actual: T,\n context?: ComparatorContext\n) => ComparatorResult | Promise<ComparatorResult>;\n\n/**\n * Marker interface for comparators with ordering metadata.\n * Created by the unordered() wrapper function.\n */\nexport interface ComparatorWithOrdering<T = unknown> {\n (\n expected: T,\n actual: T,\n context?: ComparatorContext\n ): ComparatorResult | Promise<ComparatorResult>;\n _unordered: true;\n _nestedComparators?: NestedComparatorConfig;\n}\n\n/**\n * Recursive comparator configuration that matches data shape.\n * Can be a comparator function, a comparator with ordering, or a nested object.\n */\nexport type NestedComparatorConfig = {\n [key: string]: // eslint-disable-next-line @typescript-eslint/no-explicit-any\n | Comparator<any>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n | ComparatorWithOrdering<any>\n | NestedComparatorConfig;\n};\n\n// ═══════════════════════════════════════════════════════════════════════════\n// EXECUTORS\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * The result returned by an executor.\n */\nexport interface ExecutorResult<TOutput = unknown> {\n output: TOutput;\n additionalContext?: unknown;\n cost?: number;\n}\n\n/**\n * An executor that runs an LLM workflow.\n */\nexport type Executor<TInput = unknown, TOutput = unknown> = (\n input: TInput,\n systemPrompt?: string\n) => Promise<ExecutorResult<TOutput>>;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// EVAL\n// ═══════════════════════════════════════════════════════════════════════════\n\n/**\n * A single test case pairing input with expected output.\n */\nexport interface TestCase<TInput = unknown, TOutput = unknown> {\n input: TInput;\n expected: TOutput;\n}\n\n/**\n * Base eval configuration shared by both modes.\n */\ninterface BaseEvalConfig<TInput = unknown, TOutput = unknown> {\n systemPrompt?: string;\n executor: Executor<TInput, TOutput>;\n testCases: TestCase<TInput, TOutput>[];\n perTestThreshold?: number; // Default: 1.0 (all fields must pass)\n optimize?: OptimizeConfig;\n rateLimitBatch?: number; // Run N test cases at a time (default: all in parallel)\n rateLimitPause?: number; // Wait N seconds between batches\n llmConfig?: LLMConfig; // Default LLM config for LLM-based comparators (e.g., llmCompare)\n storeLogs?: boolean | string; // true = default path, string = custom path\n}\n\n/**\n * Top-level comparators configuration.\n * Can be:\n * - A single comparator function for root-level primitives/arrays (e.g., `exact`, `numeric`)\n * - A ComparatorWithOrdering for unordered root-level arrays (e.g., `unordered(exact)`)\n * - A nested object structure matching your data shape\n */\nexport type ComparatorsConfig =\n | NestedComparatorConfig\n | ComparatorWithOrdering<unknown>\n | Comparator<unknown>;\n\n/**\n * Main eval configuration.\n * - `comparators` (optional): field mapping or single comparator. Defaults to `exact` for entire shape.\n * - `comparatorOverride`: whole-object comparison (bypasses field-level comparison).\n */\nexport type EvalConfig<TInput = unknown, TOutput = unknown> =\n | (BaseEvalConfig<TInput, TOutput> & {\n comparators?: ComparatorsConfig;\n comparatorOverride?: undefined;\n })\n | (BaseEvalConfig<TInput, TOutput> & {\n comparatorOverride: Comparator<TOutput>;\n comparators?: undefined;\n });\n\n/**\n * Result for a single field comparison.\n */\nexport interface FieldResult {\n passed: boolean;\n expected: unknown;\n actual: unknown;\n rationale?: string; // Optional explanation from comparator\n cost?: number; // Optional cost from comparator\n}\n\n/**\n * Result for a single test case.\n */\nexport interface TestCaseResult<TInput = unknown, TOutput = unknown> {\n input: TInput;\n expected: TOutput;\n actual?: TOutput;\n additionalContext?: unknown;\n cost?: number; // Executor cost\n comparatorCost?: number; // Total cost from all comparators in this test\n passed: boolean;\n fields: Record<string, FieldResult>;\n error?: string;\n passedFields: number;\n totalFields: number;\n passRate: number;\n}\n\n/**\n * Eval result.\n */\nexport interface EvalResult<TInput = unknown, TOutput = unknown> {\n systemPrompt?: string;\n testCases: TestCaseResult<TInput, TOutput>[];\n passed: number;\n total: number;\n successRate: number;\n correctFields: number;\n totalFields: number;\n accuracy: number;\n cost: number; // Total executor cost\n comparatorCost: number; // Total comparator cost\n logFolder?: string; // Path to log folder if storeLogs enabled\n}\n","import { LLMProviders } from '../types.js';\nimport type { ProviderSpec } from '../optimizer/types.js';\n\nexport const PROVIDER_SPECS: Record<LLMProviders, ProviderSpec> = {\n [LLMProviders.anthropic_claude_opus]: {\n model: 'claude-opus-4-5-20251101',\n maxTokens: 64000,\n costPerMillionInput: 5.0,\n costPerMillionOutput: 25.0,\n },\n [LLMProviders.anthropic_claude_sonnet]: {\n model: 'claude-sonnet-4-5-20251101',\n maxTokens: 64000,\n costPerMillionInput: 3.0,\n costPerMillionOutput: 15.0,\n },\n [LLMProviders.anthropic_claude_haiku]: {\n model: 'claude-haiku-4-5-20251001',\n maxTokens: 64000,\n costPerMillionInput: 1.0,\n costPerMillionOutput: 5.0,\n },\n [LLMProviders.openai_gpt5]: {\n model: 'gpt-5.2',\n maxTokens: 32000,\n costPerMillionInput: 1.75,\n costPerMillionOutput: 14.0,\n },\n [LLMProviders.openai_gpt5_mini]: {\n model: 'gpt-5-mini',\n maxTokens: 32000,\n costPerMillionInput: 0.25,\n costPerMillionOutput: 2.0,\n },\n};\n\n// Optimizer constants\nexport const ANTHROPIC_THINKING_BUDGET_TOKENS = 31999;\nexport const TOKENS_PER_MILLION = 1_000_000;\n\n// Executor constants\nexport const DEFAULT_ENDPOINT_TIMEOUT_MS = 30000;\n\n// Eval constants\nexport const DEFAULT_PER_TEST_THRESHOLD = 1.0;\n\n// Comparator constants\nexport const NAME_SUFFIXES =\n /(?<=\\S)\\s*,?\\s*(inc\\.?|llc\\.?|ltd\\.?|l\\.l\\.c\\.?|corp\\.?|corporation|company|co\\.?)$/i;\n","import Anthropic from '@anthropic-ai/sdk';\nimport OpenAI from 'openai';\nimport {\n PROVIDER_SPECS,\n ANTHROPIC_THINKING_BUDGET_TOKENS,\n TOKENS_PER_MILLION,\n} from '../constants.js';\nimport type {\n CallLLMConfig,\n LLMResult,\n CallStructuredLLMConfig,\n StructuredLLMResult,\n} from './types.js';\n\n/**\n * Call an LLM provider with the given messages.\n * Returns raw text output - caller is responsible for parsing if structured output is needed.\n */\nexport async function callLLM(config: CallLLMConfig): Promise<LLMResult> {\n const { provider, apiKey, messages, useThinking = false } = config;\n const spec = PROVIDER_SPECS[provider];\n\n try {\n // Anthropic\n if (provider.startsWith('anthropic')) {\n const client = new Anthropic({ apiKey });\n const streamOptions: Parameters<typeof client.messages.stream>[0] = {\n model: spec.model,\n max_tokens: spec.maxTokens,\n system: messages.find((m) => m.role === 'system')?.content,\n messages: messages\n .filter((m) => m.role !== 'system')\n .map((m) => ({\n role: m.role as 'user' | 'assistant',\n content: m.content,\n })),\n };\n\n if (useThinking) {\n streamOptions.thinking = {\n type: 'enabled',\n budget_tokens: ANTHROPIC_THINKING_BUDGET_TOKENS,\n };\n }\n\n const stream = client.messages.stream(streamOptions);\n const finalMessage = await stream.finalMessage();\n\n const textBlocks = finalMessage.content\n .filter((block) => block.type === 'text')\n .map((block) => block.text);\n const text = textBlocks.length > 0 ? textBlocks.join(' ') : '';\n const inputTokens = finalMessage.usage.input_tokens;\n const outputTokens = finalMessage.usage.output_tokens;\n const cost =\n (inputTokens * spec.costPerMillionInput +\n outputTokens * spec.costPerMillionOutput) /\n TOKENS_PER_MILLION;\n\n return { text, cost, inputTokens, outputTokens };\n }\n\n // OpenAI\n if (provider.startsWith('openai')) {\n const client = new OpenAI({ apiKey });\n const completionOptions: OpenAI.ChatCompletionCreateParamsNonStreaming = {\n model: spec.model,\n messages: messages.map((m) => ({ role: m.role, content: m.content })),\n max_completion_tokens: spec.maxTokens,\n };\n\n if (useThinking) {\n completionOptions.reasoning_effort = 'xhigh';\n }\n\n const response = await client.chat.completions.create(completionOptions);\n const text = response.choices[0].message.content ?? '';\n const inputTokens = response.usage?.prompt_tokens ?? 0;\n const outputTokens = response.usage?.completion_tokens ?? 0;\n const cost =\n (inputTokens * spec.costPerMillionInput +\n outputTokens * spec.costPerMillionOutput) /\n TOKENS_PER_MILLION;\n\n return { text, cost, inputTokens, outputTokens };\n }\n\n throw new Error(`Unsupported provider: ${provider}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`LLM call failed (${spec.model}): ${message}`);\n }\n}\n\n/**\n * Call an LLM provider with structured output.\n * Returns parsed JSON data conforming to the provided schema.\n */\nexport async function callStructuredLLM<T>(\n config: CallStructuredLLMConfig\n): Promise<StructuredLLMResult<T>> {\n const { provider, apiKey, messages, schema, useThinking = false } = config;\n const spec = PROVIDER_SPECS[provider];\n\n try {\n // Anthropic\n if (provider.startsWith('anthropic')) {\n const client = new Anthropic({ apiKey });\n\n // Build base stream options\n const baseOptions = {\n model: spec.model,\n max_tokens: spec.maxTokens,\n betas: ['structured-outputs-2025-11-13'],\n system: messages.find((m) => m.role === 'system')?.content,\n messages: messages\n .filter((m) => m.role !== 'system')\n .map((m) => ({\n role: m.role as 'user' | 'assistant',\n content: m.content,\n })),\n output_format: {\n type: 'json_schema' as const,\n schema,\n },\n };\n\n // Add thinking if requested\n const streamOptions = useThinking\n ? {\n ...baseOptions,\n thinking: {\n type: 'enabled' as const,\n budget_tokens: ANTHROPIC_THINKING_BUDGET_TOKENS,\n },\n }\n : baseOptions;\n\n const stream = client.beta.messages.stream(streamOptions);\n const finalMessage = await stream.finalMessage();\n\n const content = finalMessage.content[0];\n if (content.type !== 'text') {\n throw new Error('Unexpected response type from LLM');\n }\n\n const data = JSON.parse(content.text) as T;\n const inputTokens = finalMessage.usage.input_tokens;\n const outputTokens = finalMessage.usage.output_tokens;\n const cost =\n (inputTokens * spec.costPerMillionInput +\n outputTokens * spec.costPerMillionOutput) /\n TOKENS_PER_MILLION;\n\n return { data, cost, inputTokens, outputTokens };\n }\n\n // OpenAI\n if (provider.startsWith('openai')) {\n const client = new OpenAI({ apiKey });\n const completionOptions: OpenAI.ChatCompletionCreateParamsNonStreaming = {\n model: spec.model,\n messages: messages.map((m) => ({ role: m.role, content: m.content })),\n max_completion_tokens: spec.maxTokens,\n response_format: {\n type: 'json_schema',\n json_schema: {\n name: 'response',\n strict: true,\n schema,\n },\n },\n };\n\n if (useThinking) {\n completionOptions.reasoning_effort = 'xhigh';\n }\n\n const response = await client.chat.completions.create(completionOptions);\n const text = response.choices[0].message.content ?? '';\n const data = JSON.parse(text) as T;\n const inputTokens = response.usage?.prompt_tokens ?? 0;\n const outputTokens = response.usage?.completion_tokens ?? 0;\n const cost =\n (inputTokens * spec.costPerMillionInput +\n outputTokens * spec.costPerMillionOutput) /\n TOKENS_PER_MILLION;\n\n return { data, cost, inputTokens, outputTokens };\n }\n\n throw new Error(`Unsupported provider: ${provider}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Structured LLM call failed (${spec.model}): ${message}`);\n }\n}\n","import {\n Comparator,\n ComparatorContext,\n ComparatorResult,\n ComparatorWithOrdering,\n LLMProviders,\n NestedComparatorConfig,\n} from '../../types.js';\nimport { NAME_SUFFIXES } from '../../library/constants.js';\nimport { callStructuredLLM } from '../../library/llm/llm-client.js';\nimport type { JSONSchema } from '../../library/llm/types.js';\nimport * as chrono from 'chrono-node';\nimport { differenceInDays } from 'date-fns';\nimport Levenshtein from 'levenshtein';\n\n// ═══════════════════════════════════════════════════════════════════════════\n// COMPARATORS\n// ═══════════════════════════════════════════════════════════════════════════\n// contains - substring check\n// custom - user-defined logic\n// date - normalized date comparison\n// exact - deep equality (default)\n// llmCompare - LLM-based comparison with rationale\n// name - normalized name comparison\n// numeric - normalized number comparison (.nullable variant)\n// oneOf - enum validation\n// presence - value exists check\n// within - numeric tolerance\n\n/** Checks if actual string contains a substring. */\nexport function contains(substring: string): Comparator<string> {\n return (_expected, actual) => {\n const passed = actual.includes(substring);\n return { passed, similarity: passed ? 1.0 : 0.0 };\n };\n}\n\n/** Creates a comparator with custom logic. */\nexport function custom<T>(config: {\n compare: (expected: T, actual: T, context?: ComparatorContext) => boolean;\n}): Comparator<T> {\n return (expected, actual, context) => {\n const passed = config.compare(expected, actual, context);\n return { passed, similarity: passed ? 1.0 : 0.0 };\n };\n}\n\n/** Compares dates after normalizing various formats (ISO, US, written). */\nexport function date(expected: unknown, actual: unknown): ComparatorResult {\n const expDate = normalizeDate(expected);\n const actDate = normalizeDate(actual);\n\n if (expDate === null && actDate === null) {\n return { passed: true, similarity: 1.0 };\n }\n\n if (expDate === null || actDate === null) {\n return { passed: false, similarity: 0.0 };\n }\n\n const passed = expDate === actDate;\n const daysDiff = Math.abs(\n differenceInDays(new Date(expDate), new Date(actDate))\n );\n\n return { passed, similarity: Math.exp(-daysDiff / 30) };\n}\n\n/** Deep equality comparison. Default when no comparator is specified. */\nexport function exact(expected: unknown, actual: unknown): ComparatorResult {\n const passed = deepEqual(expected, actual);\n return {\n passed,\n similarity: passed ? 1.0 : 0.0,\n };\n}\n\n/** Compares names after normalizing case, whitespace, and business suffixes. */\nexport function name(expected: unknown, actual: unknown): ComparatorResult {\n const expName = normalizeName(expected);\n const actName = normalizeName(actual);\n\n if (expName === null && actName === null) {\n return { passed: true, similarity: 1.0 };\n }\n\n if (expName === null || actName === null) {\n return { passed: false, similarity: 0.0 };\n }\n\n if (expName === actName) {\n return { passed: true, similarity: 1.0 };\n }\n\n // First/last token matching for middle name tolerance\n const expTokens = expName.split(' ').filter(Boolean);\n const actTokens = actName.split(' ').filter(Boolean);\n\n if (expTokens.length >= 2 && actTokens.length >= 2) {\n if (\n expTokens[0] === actTokens[0] &&\n expTokens.at(-1) === actTokens.at(-1)\n ) {\n return { passed: true, similarity: 0.95 };\n }\n }\n\n // Fuzzy match using Levenshtein distance\n const distance = new Levenshtein(expName, actName).distance;\n const similarity = 1 - distance / Math.max(expName.length, actName.length);\n if (similarity >= 0.9) {\n return { passed: true, similarity };\n }\n\n return { passed: false, similarity };\n}\n\n/** Compares numbers after stripping currency symbols, commas, and formatting. */\nexport const numeric = Object.assign(\n (expected: unknown, actual: unknown) => numericCompare(expected, actual),\n {\n nullable: (expected: unknown, actual: unknown) =>\n numericCompare(expected, actual, true),\n }\n);\n\nfunction numericCompare(\n expected: unknown,\n actual: unknown,\n nullable = false\n): ComparatorResult {\n let expNum = normalizeNumeric(expected);\n let actNum = normalizeNumeric(actual);\n\n if (nullable) {\n expNum ??= 0;\n actNum ??= 0;\n }\n\n if (expNum === null && actNum === null) {\n return { passed: true, similarity: 1.0 };\n }\n\n if (expNum === null || actNum === null) {\n return { passed: false, similarity: 0.0 };\n }\n\n const passed = expNum === actNum;\n return { passed, similarity: passed ? 1.0 : 0.0 };\n}\n\n/** Validates that actual equals expected AND both are in the allowed set. */\nexport function oneOf<T extends string>(\n allowedValues: readonly T[]\n): Comparator<T> {\n if (allowedValues.length === 0) {\n throw new Error('oneOf() requires at least one allowed value');\n }\n\n const allowed = new Set(allowedValues);\n\n return (expected, actual) => {\n const actualAllowed = allowed.has(actual);\n const passed = actualAllowed && expected === actual;\n return { passed, similarity: passed ? 1.0 : 0.0 };\n };\n}\n\n/** Passes if both absent, or if actual has any value when expected does. */\nexport function presence(expected: unknown, actual: unknown): ComparatorResult {\n const expectedPresent = expected != null && expected !== '';\n const actualPresent = actual != null && actual !== '';\n const passed = !expectedPresent || actualPresent;\n return { passed, similarity: passed ? 1.0 : 0.0 };\n}\n\n/** Checks if a numeric value is within tolerance (percentage or absolute). */\nexport function within(config: {\n tolerance: number;\n mode?: 'percentage' | 'absolute';\n}): Comparator<number> {\n const { tolerance, mode = 'percentage' } = config;\n\n return (expected, actual) => {\n const diff = Math.abs(expected - actual);\n const threshold =\n mode === 'absolute' ? tolerance : Math.abs(expected * tolerance);\n const passed = diff <= threshold;\n\n // Similarity: 1.0 at exact match, 0.5 at boundary, decays beyond\n const similarity =\n threshold > 0\n ? Math.exp((-diff / threshold) * 0.693)\n : diff === 0\n ? 1.0\n : 0.0;\n\n return { passed, similarity };\n };\n}\n\n/** Configuration for LLM-based comparison. */\nexport interface LLMCompareConfig {\n provider?: LLMProviders;\n /** API key for LLM provider. If not provided, uses llmConfig.apiKey from eval config. */\n apiKey?: string;\n systemPrompt?: string;\n}\n\n/** Schema for LLM comparison response. */\nconst LLM_COMPARE_SCHEMA: JSONSchema = {\n type: 'object',\n properties: {\n passed: {\n type: 'boolean',\n description: 'Whether the actual value matches the expected value',\n },\n rationale: {\n type: 'string',\n description: 'Brief explanation of the comparison decision',\n },\n },\n required: ['passed', 'rationale'],\n additionalProperties: false,\n};\n\n/** Response type for LLM comparison. */\ninterface LLMCompareResponse {\n passed: boolean;\n rationale: string;\n}\n\nconst DEFAULT_LLM_COMPARE_SYSTEM_PROMPT = `Compare the following two values and determine if they are semantically equivalent.\n\nFocus on whether they convey the same core meaning or information, even if expressed differently. Consider synonyms, paraphrasing, and stylistic variations as acceptable. Only mark as failed if there are substantial differences in the actual facts or meaning being conveyed.`;\n\nconst buildLLMCompareUserPrompt = (\n expected: unknown,\n actual: unknown\n): string => `Expected value:\n${JSON.stringify(expected, null, 2)}\n\nActual value:\n${JSON.stringify(actual, null, 2)}`;\n\n/**\n * Uses an LLM to compare expected vs actual values.\n * Returns a comparison result with rationale and cost tracking.\n * Default provider: anthropic_claude_haiku (fastest, cheapest).\n */\nexport function llmCompare(config: LLMCompareConfig): Comparator {\n const systemPrompt = config.systemPrompt ?? DEFAULT_LLM_COMPARE_SYSTEM_PROMPT;\n\n return async (expected, actual, context) => {\n try {\n // Resolve API key: use config.apiKey if provided, otherwise fall back to context.llmConfig\n const apiKey = config.apiKey ?? context?.llmConfig?.apiKey;\n if (!apiKey) {\n throw new Error(\n 'llmCompare requires an apiKey. Either pass it directly to llmCompare() or set llmConfig.apiKey in eval config.'\n );\n }\n\n // Resolve provider: use config.provider if provided, otherwise fall back to context.llmConfig, then default\n const provider =\n config.provider ??\n context?.llmConfig?.provider ??\n LLMProviders.anthropic_claude_haiku;\n\n // Build user prompt with expected/actual values (always included)\n const userPrompt = buildLLMCompareUserPrompt(expected, actual);\n\n // Call LLM with structured output\n const result = await callStructuredLLM<LLMCompareResponse>({\n provider,\n apiKey,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n schema: LLM_COMPARE_SCHEMA,\n });\n\n return {\n passed: result.data.passed,\n rationale: result.data.rationale,\n cost: result.cost,\n similarity: result.data.passed ? 1.0 : 0.0,\n };\n } catch (error) {\n // On error, fail the comparison with error details\n const errorMsg = error instanceof Error ? error.message : String(error);\n return {\n passed: false,\n rationale: `LLM comparison failed: ${errorMsg}`,\n cost: 0,\n similarity: 0.0,\n };\n }\n };\n}\n\n/**\n * Marks a comparator or comparator config as unordered.\n * When applied to an array field, items will be matched by similarity\n * rather than index position (using Hungarian algorithm).\n *\n * @example\n * // Unordered array of objects\n * lineItems: unordered({\n * description: name,\n * price: within({ tolerance: 5 })\n * })\n *\n * @example\n * // Unordered array of primitives\n * tags: unordered(exact)\n *\n * @example\n * // When entire output is an array\n * comparators: unordered({\n * carrier: exact,\n * premium: within({ tolerance: 0.05 })\n * })\n */\nexport function unordered<T>(\n comparator: Comparator<T> | NestedComparatorConfig\n): ComparatorWithOrdering<T> {\n // If passed a function, use it; if passed an object, create a placeholder that throws\n const baseFunction =\n typeof comparator === 'function'\n ? comparator\n : () => {\n throw new Error(\n 'unordered() base function should not be called when nested comparators exist. ' +\n 'This is likely a bug in the evaluation logic.'\n );\n };\n\n // Attach ordering metadata\n return Object.assign(baseFunction, {\n _unordered: true as const,\n _nestedComparators: typeof comparator === 'object' ? comparator : undefined,\n }) as ComparatorWithOrdering<T>;\n}\n\n// Private helpers\n\n/**\n * Deep equality comparison with cycle detection.\n * Uses WeakSet to track visited object pairs to prevent stack overflow on circular references.\n */\nfunction deepEqual(\n a: unknown,\n b: unknown,\n visited = new WeakSet<object>()\n): boolean {\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (typeof a !== typeof b) return false;\n\n // Handle arrays\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n\n // Check for cycles\n if (visited.has(a)) return true; // Already comparing this array\n visited.add(a);\n\n return a.every((item, i) => deepEqual(item, b[i], visited));\n }\n\n // Handle objects\n if (typeof a === 'object' && typeof b === 'object') {\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n\n // Check for cycles\n if (visited.has(aObj)) return true; // Already comparing this object\n visited.add(aObj);\n\n const aKeys = Object.keys(aObj);\n if (aKeys.length !== Object.keys(bObj).length) return false;\n return aKeys.every((key) => deepEqual(aObj[key], bObj[key], visited));\n }\n\n return false;\n}\n\nfunction normalizeDate(value: unknown): string | null {\n if (value == null || value === '') return null;\n const parsed = chrono.parseDate(String(value));\n return parsed?.toISOString().split('T')[0] ?? null;\n}\n\nfunction normalizeName(value: unknown): string | null {\n if (value == null || value === '') return null;\n const str = String(value)\n .toLowerCase()\n .trim()\n .replace(/\\s+/g, ' ')\n .replace(NAME_SUFFIXES, '')\n .trim();\n return str || null;\n}\n\nfunction normalizeNumeric(value: unknown): number | null {\n if (value == null || value === '') return null;\n\n const str = String(value);\n const isNegativeParens = /^\\(.*\\)$/.test(str.trim());\n\n let cleaned = str.replace(/[^0-9.-]/g, '');\n if (isNegativeParens && !cleaned.startsWith('-')) {\n cleaned = '-' + cleaned;\n }\n\n const num = parseFloat(cleaned);\n return isNaN(num) ? null : num;\n}\n","import munkres from 'munkres-js';\nimport type { NestedComparatorConfig, Comparator } from '../../types.js';\nimport { exact } from './comparators.js'; // Used for primitive comparison\n\nexport interface MatchResult {\n assignments: [number, number][]; // [expIdx, actIdx] - matched pairs\n unmatchedExpected: number[];\n unmatchedActual: number[];\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Calculate similarity score between two values (0.0 to 1.0).\n * For arrays: recursively match and average similarity of paired elements.\n * For objects: average similarity across all fields using comparator results.\n * For primitives: uses exact comparison's similarity score.\n */\nasync function getSimilarity(\n expected: unknown,\n actual: unknown,\n comparators: NestedComparatorConfig\n): Promise<number> {\n // Arrays: recursively match and calculate average similarity\n if (Array.isArray(expected) && Array.isArray(actual)) {\n if (expected.length === 0 && actual.length === 0) {\n return 1.0;\n }\n if (expected.length === 0 || actual.length === 0) {\n return 0.0;\n }\n\n const result = await matchArrays(expected, actual, comparators);\n let total = 0;\n for (const [expIdx, actIdx] of result.assignments) {\n total += await getSimilarity(\n expected[expIdx],\n actual[actIdx],\n comparators\n );\n }\n\n // Penalize for unmatched items\n const maxLen = Math.max(expected.length, actual.length);\n return total / maxLen;\n }\n\n // Primitives (including type mismatches like array vs non-array)\n if (!isObject(expected) || !isObject(actual)) {\n const result = exact(expected, actual);\n return result.similarity ?? (result.passed ? 1.0 : 0.0);\n }\n\n const fields = Object.keys(expected).filter((key) => {\n const comp = comparators[key];\n return comp !== undefined && typeof comp === 'function';\n });\n\n // Exit early if no fields with comparators to compare\n if (fields.length === 0) {\n return 1.0;\n }\n\n let total = 0;\n for (const key of fields) {\n const comparatorConfig = comparators[key];\n // Extract the actual comparator function (handle ComparatorWithOrdering)\n const comparator: Comparator<unknown> =\n typeof comparatorConfig === 'function' ? comparatorConfig : exact;\n\n const result = await comparator(expected[key], actual[key], {\n expectedParent: expected,\n actualParent: actual,\n });\n total += result.similarity ?? (result.passed ? 1.0 : 0.0);\n }\n return total / fields.length;\n}\n\n/**\n * Find optimal pairing between expected and actual arrays using Hungarian algorithm.\n * Pure matching - no pass/fail determination.\n *\n * @param expected - Array of expected items\n * @param actual - Array of actual items\n * @param comparators - Nested comparator configuration for array items\n * @returns Matching result with assignments and unmatched indices\n */\nexport async function matchArrays(\n expected: unknown[],\n actual: unknown[],\n comparators: NestedComparatorConfig = {}\n): Promise<MatchResult> {\n // Handle empty arrays\n if (expected.length === 0) {\n return {\n assignments: [],\n unmatchedExpected: [],\n unmatchedActual: [...Array(actual.length).keys()],\n };\n }\n\n if (actual.length === 0) {\n return {\n assignments: [],\n unmatchedExpected: [...Array(expected.length).keys()],\n unmatchedActual: [],\n };\n }\n\n // Build cost matrix: cost = 1 - similarity (lower cost = better match)\n const matrix = await Promise.all(\n expected.map(async (exp) =>\n Promise.all(\n actual.map(\n async (act) => 1 - (await getSimilarity(exp, act, comparators))\n )\n )\n )\n );\n\n // Run Hungarian algorithm\n const rawAssignments = munkres(matrix);\n\n // Process assignments\n const assignments: [number, number][] = [];\n const matchedExp = new Set<number>();\n const matchedAct = new Set<number>();\n\n for (const [row, col] of rawAssignments) {\n // Accept all valid assignments from Hungarian (no threshold filtering)\n if (row < expected.length && col < actual.length) {\n assignments.push([row, col]);\n matchedExp.add(row);\n matchedAct.add(col);\n }\n }\n\n return {\n assignments,\n unmatchedExpected: [...Array(expected.length).keys()].filter(\n (i) => !matchedExp.has(i)\n ),\n unmatchedActual: [...Array(actual.length).keys()].filter(\n (i) => !matchedAct.has(i)\n ),\n };\n}\n","/**\n * UI utilities for beautiful console output\n */\nimport chalk from 'chalk';\nimport ora, { type Ora } from 'ora';\nimport cliProgress from 'cli-progress';\nimport figures from 'figures';\n\n// ═══════════════════════════════════════════════════════════════════════════\n// THEME\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport const theme = {\n // Status colors\n success: chalk.green,\n error: chalk.red,\n warning: chalk.yellow,\n\n // Text styling\n bold: chalk.bold,\n dim: chalk.dim,\n\n // Symbols (cross-platform via figures)\n check: chalk.green(figures.tick),\n cross: chalk.red(figures.cross),\n warn: chalk.yellow(figures.warning),\n bullet: chalk.dim(figures.bullet),\n pointer: chalk.yellow(figures.pointer),\n\n // Formatting helpers\n separator: chalk.dim(' · '),\n divider: (label: string, width = 60) => {\n const prefix = `━━━ ${label} `;\n const remaining = Math.max(0, width - prefix.length);\n return chalk.cyan.dim(prefix + '━'.repeat(remaining));\n },\n};\n\n// ═══════════════════════════════════════════════════════════════════════════\n// SPINNER MANAGER\n// ═══════════════════════════════════════════════════════════════════════════\n\nlet activeSpinner: Ora | null = null;\n\nexport const spinner = {\n /**\n * Start a spinner with the given text\n */\n start(text: string): Ora {\n if (activeSpinner) {\n activeSpinner.stop();\n }\n activeSpinner = ora({\n text,\n spinner: 'dots',\n indent: 4,\n }).start();\n return activeSpinner;\n },\n\n /**\n * Stop the current spinner with success\n */\n succeed(text?: string): void {\n if (activeSpinner) {\n activeSpinner.succeed(text);\n activeSpinner = null;\n }\n },\n\n /**\n * Stop the current spinner with failure\n */\n fail(text?: string): void {\n if (activeSpinner) {\n activeSpinner.fail(text);\n activeSpinner = null;\n }\n },\n\n /**\n * Stop the current spinner (no status indicator)\n */\n stop(): void {\n if (activeSpinner) {\n activeSpinner.stop();\n activeSpinner = null;\n }\n },\n\n /**\n * Clear the spinner line without stopping\n */\n clear(): void {\n if (activeSpinner) {\n activeSpinner.clear();\n }\n },\n\n /**\n * Check if a spinner is currently active\n */\n isActive(): boolean {\n return activeSpinner !== null;\n },\n};\n\n// ═══════════════════════════════════════════════════════════════════════════\n// PROGRESS BAR\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport interface ProgressTracker {\n start(total: number): void;\n update(current: number): void;\n stop(): void;\n}\n\nexport function createProgressTracker(label: string): ProgressTracker {\n let bar: cliProgress.SingleBar | null = null;\n let startTime = 0;\n let lastUpdate = 0;\n const MIN_UPDATE_INTERVAL = 100; // ms\n\n return {\n start(total: number) {\n // Stop any active spinner before starting progress\n spinner.stop();\n\n startTime = Date.now();\n bar = new cliProgress.SingleBar({\n format: ` {bar} {percentage}% {value}/{total} ${label} {duration_formatted}`,\n barCompleteChar: '█',\n barIncompleteChar: '░',\n barsize: 20,\n hideCursor: true,\n clearOnComplete: false,\n stopOnComplete: false,\n forceRedraw: true,\n fps: 10,\n });\n bar.start(total, 0, { duration_formatted: '0s' });\n },\n\n update(current: number) {\n const now = Date.now();\n // Throttle updates to prevent flickering\n if (now - lastUpdate < MIN_UPDATE_INTERVAL && bar) {\n const total = bar.getTotal();\n if (current < total) {\n return;\n }\n }\n lastUpdate = now;\n\n if (bar) {\n const elapsed = Math.round((now - startTime) / 1000);\n bar.update(current, { duration_formatted: `${elapsed}s` });\n }\n },\n\n stop() {\n if (bar) {\n const elapsed = Math.round((Date.now() - startTime) / 1000);\n bar.update(bar.getTotal(), { duration_formatted: `${elapsed}s` });\n bar.stop();\n bar = null;\n }\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// FORMATTERS\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport function formatCost(cost: number): string {\n return theme.dim(`$${cost.toFixed(4)}`);\n}\n\nexport function formatCostShort(cost: number): string {\n return theme.dim(`$${cost.toFixed(2)}`);\n}\n\nexport function formatDuration(ms: number): string {\n const totalSeconds = Math.round(ms / 1000);\n if (totalSeconds < 60) return `${totalSeconds}s`;\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n}\n\nexport function formatPercentage(rate: number): string {\n return `${(rate * 100).toFixed(1)}%`;\n}\n","import type { TestCaseResult, LLMProviders } from '../types.js';\nimport type { IterationLog, LogContext } from './types.js';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport {\n theme,\n spinner,\n createProgressTracker,\n formatCost,\n formatCostShort,\n formatDuration,\n formatPercentage,\n type ProgressTracker,\n} from './ui.js';\n\n// Re-export types for backward compatibility\nexport type { IterationLog, LogContext };\n\n// ═══════════════════════════════════════════════════════════════════════════\n// TYPES\n// ═══════════════════════════════════════════════════════════════════════════\n\n/** Metadata for JSON report */\ninterface OptimizationMetadata {\n timestamp: string;\n model: string;\n provider: LLMProviders;\n thinking: boolean;\n targetSuccessRate: number;\n maxIterations: number | null;\n maxCost: number | null;\n testCaseCount: number;\n perTestThreshold: number;\n rateLimitBatch?: number;\n rateLimitPause?: number;\n}\n\n/** Summary stats for JSON report */\ninterface OptimizationSummary {\n totalIterations: number;\n totalDurationMs: number;\n totalCost: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n startRate: number;\n endRate: number;\n targetMet: boolean;\n}\n\n/** Best run info for JSON report */\ninterface BestRun {\n iteration: number;\n successRate: number;\n passed: number;\n total: number;\n fieldAccuracy: number;\n}\n\n/** Full JSON report structure */\ninterface OptimizationReport {\n metadata: OptimizationMetadata;\n summary: OptimizationSummary;\n best: BestRun;\n iterations: IterationData[];\n}\n\n/** Per-iteration data for JSON report */\ninterface IterationData {\n iteration: number;\n successRate: number;\n passed: number;\n total: number;\n correctFields: number;\n totalFields: number;\n fieldAccuracy: number;\n cost: number;\n cumulativeCost: number;\n durationMs: number;\n inputTokens: number;\n outputTokens: number;\n failures: FailureData[];\n}\n\n/** Failure data for JSON report */\ninterface FailureData {\n testIndex: number;\n input: unknown;\n expected: unknown;\n actual: unknown;\n additionalContext?: unknown;\n fields: Record<\n string,\n { expected: unknown; actual: unknown; passed: boolean }\n >;\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// BestRun.json Types\n// ───────────────────────────────────────────────────────────────────────────\n\ninterface BestRunMetadata {\n iteration: number;\n model: string;\n provider: LLMProviders;\n thinking: boolean;\n targetSuccessRate: number;\n perTestThreshold: number;\n rateLimitBatch?: number;\n rateLimitPause?: number;\n}\n\ninterface BestRunResults {\n successRate: number;\n passed: number;\n total: number;\n fieldAccuracy: number;\n correctFields: number;\n totalFields: number;\n}\n\ninterface BestRunCost {\n iteration: number;\n cumulative: number;\n}\n\ninterface BestRunTiming {\n durationMs: number;\n inputTokens: number;\n outputTokens: number;\n}\n\ninterface BestRunFailure {\n testIndex: number;\n input: unknown;\n expected: unknown;\n actual: unknown;\n additionalContext?: unknown;\n failedFields: Record<string, { expected: unknown; actual: unknown }>;\n}\n\ninterface BestRunPartialFailure {\n testIndex: number;\n passRate: number;\n input: unknown;\n expected: unknown;\n actual: unknown;\n additionalContext?: unknown;\n failedFields: Record<string, { expected: unknown; actual: unknown }>;\n}\n\ninterface BestRunSuccess {\n testIndex: number;\n input: unknown;\n expected: unknown;\n actual: unknown;\n additionalContext?: unknown;\n}\n\ninterface BestRunReport {\n metadata: BestRunMetadata;\n results: BestRunResults;\n cost: BestRunCost;\n timing: BestRunTiming;\n failures: BestRunFailure[];\n partialFailures: BestRunPartialFailure[];\n successes: BestRunSuccess[];\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// FORMATTERS\n// ═══════════════════════════════════════════════════════════════════════════\n\nfunction formatMsCompact(ms: number): string {\n const totalSeconds = Math.round(ms / 1000);\n if (totalSeconds < 60) return `${totalSeconds}s`;\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n}\n\nfunction formatProgressBar(rate: number, width = 20): string {\n const filled = Math.round(rate * width);\n const empty = width - filled;\n return '█'.repeat(filled) + '░'.repeat(empty);\n}\n\nfunction formatTokensCompact(tokens: number): string {\n if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M`;\n if (tokens >= 1_000) return `${Math.round(tokens / 1_000)}K`;\n return String(tokens);\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// PROGRESS TRACKING\n// ═══════════════════════════════════════════════════════════════════════════\n\n/** Progress bar updater interface */\ninterface ProgressUpdater {\n update(completed: number, total: number): void;\n finish(): void;\n clear(): void;\n}\n\n/**\n * Clear any active progress line before logging\n * Call this before all console.log statements\n */\nexport function clearProgressLine(): void {\n const width = process.stdout.columns || 80;\n process.stdout.write('\\r' + ' '.repeat(width) + '\\r');\n}\n\n/**\n * Create a progress updater using cli-progress for beautiful output\n */\nexport function createProgressUpdater(label: string): ProgressUpdater {\n let tracker: ProgressTracker | null = null;\n let total = 0;\n\n return {\n update(completed: number, newTotal: number) {\n // Initialize on first call\n if (!tracker) {\n total = newTotal;\n tracker = createProgressTracker(label);\n tracker.start(total);\n }\n tracker.update(completed);\n },\n\n finish() {\n if (tracker) {\n tracker.stop();\n tracker = null;\n }\n },\n\n clear() {\n clearProgressLine();\n },\n };\n}\n\n/**\n * Track progress of Promise.allSettled with real-time updates\n *\n * @param promises Array of promises to track\n * @param onProgress Callback called when each promise settles\n * @returns Promise.allSettled result\n */\nexport async function trackPromiseProgress<T>(\n promises: Promise<T>[],\n onProgress: (completed: number, total: number) => void\n): Promise<PromiseSettledResult<T>[]> {\n if (promises.length === 0) {\n return [];\n }\n\n let completed = 0;\n const total = promises.length;\n\n // Initial progress\n onProgress(0, total);\n\n // Wrap each promise to track completion\n const wrappedPromises = promises.map((promise) =>\n promise\n .then((value) => {\n completed++;\n onProgress(completed, total);\n return { status: 'fulfilled' as const, value };\n })\n .catch((reason) => {\n completed++;\n onProgress(completed, total);\n return { status: 'rejected' as const, reason };\n })\n );\n\n return Promise.all(wrappedPromises);\n}\n\nexport function formatFailure(testCase: TestCaseResult): string {\n const lines: string[] = [];\n\n lines.push(`Input: ${JSON.stringify(testCase.input, null, 2)}`);\n lines.push(`Expected: ${JSON.stringify(testCase.expected, null, 2)}`);\n lines.push(`Actual: ${JSON.stringify(testCase.actual, null, 2)}`);\n\n if (testCase.additionalContext) {\n lines.push(\n `Additional Context: ${JSON.stringify(testCase.additionalContext, null, 2)}`\n );\n }\n\n lines.push('');\n lines.push('Field-level failures:');\n\n for (const [fieldPath, result] of Object.entries(testCase.fields)) {\n if (!result.passed) {\n lines.push(\n ` ${fieldPath || '(root)'}: expected ${JSON.stringify(result.expected)}, got ${JSON.stringify(result.actual)}`\n );\n }\n }\n\n return lines.join('\\n');\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// HELPERS\n// ═══════════════════════════════════════════════════════════════════════════\n\nfunction findBestIteration(iterations: IterationLog[]): IterationLog {\n return iterations.reduce((best, curr) =>\n curr.passed / curr.total > best.passed / best.total ? curr : best\n );\n}\n\nfunction computeTotals(iterations: IterationLog[]): {\n totalInputTokens: number;\n totalOutputTokens: number;\n totalDuration: number;\n} {\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n let totalDuration = 0;\n\n for (const iter of iterations) {\n totalInputTokens += iter.inputTokens;\n totalOutputTokens += iter.outputTokens;\n totalDuration += iter.duration;\n }\n\n return { totalInputTokens, totalOutputTokens, totalDuration };\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// CONSOLE LOGGING\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport function logOptimizerHeader(\n model: string,\n targetRate: number,\n testCount: number\n): void {\n spinner.stop();\n console.log('');\n console.log(theme.bold('Didactic Optimizer'));\n console.log(\n ` ${theme.dim('Model:')} ${model}${theme.separator}${theme.dim('Target:')} ${formatPercentage(targetRate)}${theme.separator}${theme.dim('Tests:')} ${testCount}`\n );\n}\n\nexport function logIterationStart(iterationLabel: string): void {\n spinner.stop();\n clearProgressLine();\n console.log('');\n console.log(theme.divider(`Iteration ${iterationLabel}`));\n console.log('');\n}\n\nexport function logEvaluationStart(): void {\n spinner.stop();\n clearProgressLine();\n console.log(` ${theme.bold('Evaluating prompt')}`);\n spinner.start('Running evals...');\n}\n\nexport function logEvaluationResult(\n result: { passed: number; total: number; successRate: number; cost: number },\n cumulativeCost: number,\n durationMs: number\n): void {\n spinner.stop();\n clearProgressLine();\n\n // Success rate line\n const successIcon =\n result.successRate >= 0.9\n ? theme.check\n : result.successRate >= 0.5\n ? theme.warn\n : theme.cross;\n console.log(\n ` ${successIcon} ${theme.bold(formatPercentage(result.successRate))} success rate ${theme.dim(`(${result.passed}/${result.total} passed)`)}`\n );\n\n // Cost line\n console.log(\n ` ${theme.dim('Cost:')} ${formatCost(result.cost)}${theme.separator}${theme.dim('Total:')} ${formatCostShort(cumulativeCost)}${theme.separator}${theme.dim(formatDuration(durationMs))}`\n );\n}\n\nexport function logRegressionDetected(bestSuccessRate: number): void {\n spinner.stop();\n clearProgressLine();\n console.log(\n ` ${theme.pointer} ${theme.warning('Regression')} ${theme.dim(`(was ${formatPercentage(bestSuccessRate)})`)}`\n );\n}\n\nexport function logTargetReached(targetSuccessRate: number): void {\n spinner.stop();\n clearProgressLine();\n console.log(\n ` ${theme.check} ${theme.success('Target reached!')} ${theme.dim(`(${formatPercentage(targetSuccessRate)})`)}`\n );\n}\n\nexport function logTargetFailures(\n targetSuccessRate: number,\n failureCount: number\n): void {\n spinner.stop();\n clearProgressLine();\n console.log(\n ` ${theme.cross} ${theme.error(`${failureCount} failures`)} to address ${theme.dim(`(target: ${formatPercentage(targetSuccessRate)})`)}`\n );\n}\n\nexport function logCostLimitReached(cumulativeCost: number): void {\n spinner.stop();\n clearProgressLine();\n console.log(\n ` ${theme.warn} ${theme.warning('Cost limit reached')} ${theme.dim(`($${cumulativeCost.toFixed(2)})`)}`\n );\n}\n\nexport function logPatchGenerationStart(failureCount: number): void {\n spinner.stop();\n clearProgressLine();\n console.log('');\n console.log(` ${theme.bold('Generating patches')}`);\n spinner.start(`Generating ${failureCount} patches in parallel...`);\n}\n\nexport function logPatchGenerationResult(\n patchCost: number,\n cumulativeCost: number,\n durationMs: number\n): void {\n spinner.stop();\n clearProgressLine();\n console.log(\n ` ${theme.check} Patches generated${theme.separator}${theme.dim('Cost:')} ${formatCost(patchCost)}${theme.separator}${theme.dim('Total:')} ${formatCostShort(cumulativeCost)}${theme.separator}${theme.dim(formatDuration(durationMs))}`\n );\n}\n\nexport function logMergeStart(): void {\n spinner.stop();\n clearProgressLine();\n console.log('');\n console.log(` ${theme.bold('Merging patches')}`);\n spinner.start('Merging patches...');\n}\n\nexport function logMergeResult(\n mergeCost: number,\n cumulativeCost: number,\n durationMs: number\n): void {\n spinner.stop();\n clearProgressLine();\n console.log(\n ` ${theme.check} Merged${theme.separator}${theme.dim('Cost:')} ${formatCost(mergeCost)}${theme.separator}${theme.dim('Total:')} ${formatCostShort(cumulativeCost)}${theme.separator}${theme.dim(formatDuration(durationMs))}`\n );\n}\n\nexport function logPatchGenerationFailures(\n failedCount: number,\n totalCount: number\n): void {\n spinner.stop();\n clearProgressLine();\n console.log(\n ` ${theme.warn} ${theme.warning(`${failedCount}/${totalCount} patch generations failed`)}`\n );\n}\n\nexport function logOptimizationComplete(\n bestSuccessRate: number,\n targetSuccessRate: number,\n cumulativeCost: number\n): void {\n spinner.stop();\n clearProgressLine();\n console.log('');\n console.log(theme.divider('Complete'));\n console.log('');\n\n const targetMet = bestSuccessRate >= targetSuccessRate;\n const icon = targetMet ? theme.check : theme.cross;\n const rateColor = targetMet ? theme.success : theme.error;\n\n console.log(\n ` ${icon} ${theme.bold('Best:')} ${rateColor(formatPercentage(bestSuccessRate))}`\n );\n console.log(\n ` ${theme.dim('Target:')} ${formatPercentage(targetSuccessRate)}${theme.separator}${theme.dim('Total Cost:')} ${formatCostShort(cumulativeCost)}`\n );\n}\n\nexport function logLogsWritten(logPath: string): void {\n spinner.stop();\n clearProgressLine();\n console.log(` ${theme.dim('Logs written to:')} ${logPath}`);\n console.log('');\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// MARKDOWN REPORT (One Sheet - No Prompts, No Failures)\n// ═══════════════════════════════════════════════════════════════════════════\n\nfunction generateConfigSection(\n ctx: LogContext,\n testCaseCount: number\n): string[] {\n const lines: string[] = [];\n const maxIterLabel =\n ctx.config.maxIterations ??\n (ctx.config.maxCost !== undefined ? '∞ (cost-limited)' : '5');\n\n lines.push('## Configuration');\n lines.push('| Setting | Value |');\n lines.push('|---------|-------|');\n lines.push(`| Model | ${ctx.model} |`);\n lines.push(`| Provider | ${ctx.config.provider} |`);\n lines.push(`| Thinking | ${ctx.config.thinking ? 'Enabled' : 'Disabled'} |`);\n lines.push(\n `| Target | ${(ctx.config.targetSuccessRate * 100).toFixed(0)}% |`\n );\n lines.push(`| Max Iterations | ${maxIterLabel} |`);\n if (ctx.config.maxCost !== undefined) {\n lines.push(`| Max Cost | $${ctx.config.maxCost.toFixed(2)} |`);\n }\n lines.push(`| Test Cases | ${testCaseCount} |`);\n if (ctx.rateLimitBatch !== undefined || ctx.rateLimitPause !== undefined) {\n const batch = ctx.rateLimitBatch ?? 'all';\n const pause = ctx.rateLimitPause ?? 0;\n lines.push(`| Rate Limit | ${batch} cases/batch, ${pause}s pause |`);\n }\n lines.push(\n `| Per-Test Threshold | ${((ctx.perTestThreshold ?? 1.0) * 100).toFixed(0)}% |`\n );\n lines.push('');\n\n return lines;\n}\n\nfunction generateBestRunSection(bestIter: IterationLog): string[] {\n const lines: string[] = [];\n const rate = (bestIter.passed / bestIter.total) * 100;\n const fieldAccuracy =\n bestIter.totalFields > 0\n ? (bestIter.correctFields / bestIter.totalFields) * 100\n : 100;\n\n lines.push('## Best Run');\n lines.push('| Metric | Value |');\n lines.push('|--------|-------|');\n lines.push(`| Iteration | ${bestIter.iteration} |`);\n lines.push(\n `| Success Rate | ${rate.toFixed(1)}% (${bestIter.passed}/${bestIter.total}) |`\n );\n lines.push(\n `| Field Accuracy | ${fieldAccuracy.toFixed(1)}% (${bestIter.correctFields}/${bestIter.totalFields}) |`\n );\n lines.push(`| Cost | $${bestIter.cost.toFixed(2)} |`);\n lines.push('');\n\n return lines;\n}\n\nfunction generateSummarySection(\n iterations: IterationLog[],\n success: boolean,\n totals: {\n totalInputTokens: number;\n totalOutputTokens: number;\n totalDuration: number;\n }\n): string[] {\n const lines: string[] = [];\n const firstIter = iterations[0];\n const lastIter = iterations[iterations.length - 1];\n const startRate = firstIter ? (firstIter.passed / firstIter.total) * 100 : 0;\n const endRate = lastIter ? (lastIter.passed / lastIter.total) * 100 : 0;\n const totalCost = lastIter?.cumulativeCost ?? 0;\n\n lines.push('## Summary');\n lines.push('| Metric | Value |');\n lines.push('|--------|-------|');\n lines.push(`| Total Iterations | ${iterations.length} |`);\n lines.push(`| Total Duration | ${formatMsCompact(totals.totalDuration)} |`);\n lines.push(\n `| Start -> End | ${startRate.toFixed(1)}% -> ${endRate.toFixed(1)}% |`\n );\n lines.push(`| Target Met | ${success ? '✓ Yes' : '✗ No'} |`);\n lines.push(\n `| Total Tokens | ${formatTokensCompact(totals.totalInputTokens)} in / ${formatTokensCompact(totals.totalOutputTokens)} out |`\n );\n lines.push(`| Total Cost | $${totalCost.toFixed(2)} |`);\n lines.push('');\n\n return lines;\n}\n\nfunction generateRunsTable(iterations: IterationLog[]): string[] {\n const lines: string[] = [];\n const bestRate = Math.max(...iterations.map((i) => i.passed / i.total));\n\n lines.push('## Runs');\n lines.push('| # | Rate | Fields | Cost | Duration | Tokens In/Out |');\n lines.push('|---|------|--------|------|----------|---------------|');\n\n for (let i = 0; i < iterations.length; i++) {\n const iter = iterations[i];\n const rate = iter.passed / iter.total;\n const ratePct = (rate * 100).toFixed(1);\n const fieldAccuracy =\n iter.totalFields > 0\n ? (iter.correctFields / iter.totalFields) * 100\n : 100;\n\n // Determine indicator: ★ for best, ↓ for regression\n let indicator = '';\n if (rate === bestRate) {\n indicator = ' ★';\n } else if (\n iter.previousSuccessRate !== undefined &&\n rate < iter.previousSuccessRate\n ) {\n indicator = ' ↓';\n }\n\n const tokens = `${formatTokensCompact(iter.inputTokens)} / ${formatTokensCompact(iter.outputTokens)}`;\n\n lines.push(\n `| ${iter.iteration} | ${ratePct}% (${iter.passed}/${iter.total})${indicator} | ${fieldAccuracy.toFixed(1)}% | $${iter.cost.toFixed(2)} | ${formatMsCompact(iter.duration)} | ${tokens} |`\n );\n }\n\n lines.push('');\n lines.push('★ = Best | ↓ = Regressed');\n lines.push('');\n\n return lines;\n}\n\nfunction generateProgressChart(\n iterations: IterationLog[],\n targetRate: number\n): string[] {\n const lines: string[] = [];\n const bestRate = Math.max(...iterations.map((i) => i.passed / i.total));\n\n lines.push('## Progression');\n lines.push('```');\n for (const iter of iterations) {\n const rate = iter.total > 0 ? iter.passed / iter.total : 0;\n const pct = (rate * 100).toFixed(1);\n const bar = formatProgressBar(rate);\n let suffix = '';\n if (rate === bestRate) suffix += ' ★';\n if (rate >= targetRate) suffix += ' ✓';\n lines.push(\n `Iter ${iter.iteration}: ${bar} ${pct}% ${formatMsCompact(iter.duration)}${suffix}`\n );\n }\n lines.push('```');\n lines.push('');\n\n return lines;\n}\n\nexport function generateLogContent(\n iterations: IterationLog[],\n ctx: LogContext,\n success: boolean\n): string {\n const lines: string[] = [];\n const startTimeStr = ctx.startTime.toLocaleString();\n const testCaseCount = iterations[0]?.total ?? 0;\n const bestIter = findBestIteration(iterations);\n const totals = computeTotals(iterations);\n\n // Header\n lines.push('# Optimization Report');\n lines.push(`**Run:** ${startTimeStr}`);\n lines.push('');\n\n // Sections\n lines.push(...generateConfigSection(ctx, testCaseCount));\n lines.push(...generateBestRunSection(bestIter));\n lines.push(...generateSummarySection(iterations, success, totals));\n lines.push(...generateRunsTable(iterations));\n lines.push(\n ...generateProgressChart(iterations, ctx.config.targetSuccessRate)\n );\n\n // Footer with links to companion files\n lines.push('---');\n lines.push('Prompts: `prompts.md`');\n lines.push('Raw data: `rawData.json`');\n lines.push('Best run: `bestRun.json`');\n\n return lines.join('\\n');\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// FILE WRITERS\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport function writeLog(logPath: string, content: string): void {\n const dir = path.dirname(logPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(logPath, content, 'utf-8');\n}\n\nexport function writeRawDataJson(\n folderPath: string,\n iterations: IterationLog[],\n ctx: LogContext,\n success: boolean\n): void {\n const jsonPath = path.join(folderPath, 'rawData.json');\n const bestIter = findBestIteration(iterations);\n const totals = computeTotals(iterations);\n\n const firstIter = iterations[0];\n const lastIter = iterations[iterations.length - 1];\n\n const report: OptimizationReport = {\n metadata: {\n timestamp: ctx.startTime.toISOString(),\n model: ctx.model,\n provider: ctx.config.provider,\n thinking: ctx.config.thinking ?? false,\n targetSuccessRate: ctx.config.targetSuccessRate,\n maxIterations: ctx.config.maxIterations ?? null,\n maxCost: ctx.config.maxCost ?? null,\n testCaseCount: firstIter?.total ?? 0,\n perTestThreshold: ctx.perTestThreshold ?? 1.0,\n rateLimitBatch: ctx.rateLimitBatch,\n rateLimitPause: ctx.rateLimitPause,\n },\n summary: {\n totalIterations: iterations.length,\n totalDurationMs: totals.totalDuration,\n totalCost: lastIter?.cumulativeCost ?? 0,\n totalInputTokens: totals.totalInputTokens,\n totalOutputTokens: totals.totalOutputTokens,\n startRate: firstIter ? firstIter.passed / firstIter.total : 0,\n endRate: lastIter ? lastIter.passed / lastIter.total : 0,\n targetMet: success,\n },\n best: {\n iteration: bestIter.iteration,\n successRate: bestIter.passed / bestIter.total,\n passed: bestIter.passed,\n total: bestIter.total,\n fieldAccuracy:\n bestIter.totalFields > 0\n ? bestIter.correctFields / bestIter.totalFields\n : 1,\n },\n iterations: iterations.map((iter) => {\n const failures: FailureData[] = [];\n iter.testCases.forEach((tc, testIdx) => {\n if (!tc.passed) {\n failures.push({\n testIndex: testIdx,\n input: tc.input,\n expected: tc.expected,\n actual: tc.actual,\n additionalContext: tc.additionalContext,\n fields: tc.fields,\n });\n }\n });\n\n return {\n iteration: iter.iteration,\n successRate: iter.passed / iter.total,\n passed: iter.passed,\n total: iter.total,\n correctFields: iter.correctFields,\n totalFields: iter.totalFields,\n fieldAccuracy:\n iter.totalFields > 0 ? iter.correctFields / iter.totalFields : 1,\n cost: iter.cost,\n cumulativeCost: iter.cumulativeCost,\n durationMs: iter.duration,\n inputTokens: iter.inputTokens,\n outputTokens: iter.outputTokens,\n failures,\n };\n }),\n };\n\n fs.writeFileSync(jsonPath, JSON.stringify(report, null, 2), 'utf-8');\n}\n\nexport function writePromptsFile(\n folderPath: string,\n iterations: IterationLog[],\n ctx: LogContext\n): void {\n const promptsPath = path.join(folderPath, 'prompts.md');\n const startTimeStr = ctx.startTime.toLocaleString();\n const bestIter = findBestIteration(iterations);\n const lines: string[] = [];\n\n lines.push('# Prompts Log');\n lines.push(`**Run:** ${startTimeStr}`);\n lines.push('');\n\n for (const iter of iterations) {\n const rate = iter.total > 0 ? (iter.passed / iter.total) * 100 : 0;\n lines.push(\n `## Iteration ${iter.iteration} | ${rate.toFixed(1)}% (${iter.passed}/${iter.total})`\n );\n lines.push('');\n lines.push('```');\n lines.push(iter.systemPrompt);\n lines.push('```');\n lines.push('');\n if (iter.iteration < iterations.length) {\n lines.push('---');\n lines.push('');\n }\n }\n\n // Add Best Prompt section at the end\n lines.push('---');\n lines.push('');\n const bestRate = (bestIter.passed / bestIter.total) * 100;\n lines.push(\n `## Best Prompt (Iteration ${bestIter.iteration}) | ${bestRate.toFixed(1)}% (${bestIter.passed}/${bestIter.total})`\n );\n lines.push('');\n lines.push('```');\n lines.push(bestIter.systemPrompt);\n lines.push('```');\n\n fs.writeFileSync(promptsPath, lines.join('\\n'), 'utf-8');\n}\n\nexport function writeBestRunJson(\n folderPath: string,\n iterations: IterationLog[],\n ctx: LogContext\n): void {\n const bestRunPath = path.join(folderPath, 'bestRun.json');\n const bestIter = findBestIteration(iterations);\n\n // Extract only failed fields (not all fields)\n const extractFailedFields = (\n fields: Record<\n string,\n { passed: boolean; expected: unknown; actual: unknown }\n >\n ): Record<string, { expected: unknown; actual: unknown }> => {\n const failedFields: Record<string, { expected: unknown; actual: unknown }> =\n {};\n for (const [fieldPath, result] of Object.entries(fields)) {\n if (!result.passed) {\n failedFields[fieldPath] = {\n expected: result.expected,\n actual: result.actual,\n };\n }\n }\n return failedFields;\n };\n\n // Categorize test cases into three groups\n const failures: BestRunFailure[] = [];\n const partialFailures: BestRunPartialFailure[] = [];\n const successes: BestRunSuccess[] = [];\n\n bestIter.testCases.forEach((tc, testIdx) => {\n if (!tc.passed) {\n // Test failed overall (didn't meet perTestThreshold)\n failures.push({\n testIndex: testIdx,\n input: tc.input,\n expected: tc.expected,\n actual: tc.actual,\n additionalContext: tc.additionalContext,\n failedFields: extractFailedFields(tc.fields),\n });\n } else if (tc.passRate < 1) {\n // Test passed but has some failing fields\n partialFailures.push({\n testIndex: testIdx,\n passRate: tc.passRate,\n input: tc.input,\n expected: tc.expected,\n actual: tc.actual,\n additionalContext: tc.additionalContext,\n failedFields: extractFailedFields(tc.fields),\n });\n } else {\n // Test passed with 100% field accuracy\n successes.push({\n testIndex: testIdx,\n input: tc.input,\n expected: tc.expected,\n actual: tc.actual,\n additionalContext: tc.additionalContext,\n });\n }\n });\n\n const report: BestRunReport = {\n metadata: {\n iteration: bestIter.iteration,\n model: ctx.model,\n provider: ctx.config.provider,\n thinking: ctx.config.thinking ?? false,\n targetSuccessRate: ctx.config.targetSuccessRate,\n perTestThreshold: ctx.perTestThreshold ?? 1.0,\n rateLimitBatch: ctx.rateLimitBatch,\n rateLimitPause: ctx.rateLimitPause,\n },\n results: {\n successRate: bestIter.passed / bestIter.total,\n passed: bestIter.passed,\n total: bestIter.total,\n fieldAccuracy:\n bestIter.totalFields > 0\n ? bestIter.correctFields / bestIter.totalFields\n : 1,\n correctFields: bestIter.correctFields,\n totalFields: bestIter.totalFields,\n },\n cost: {\n iteration: bestIter.cost,\n cumulative: bestIter.cumulativeCost,\n },\n timing: {\n durationMs: bestIter.duration,\n inputTokens: bestIter.inputTokens,\n outputTokens: bestIter.outputTokens,\n },\n failures,\n partialFailures,\n successes,\n };\n\n fs.writeFileSync(bestRunPath, JSON.stringify(report, null, 2), 'utf-8');\n}\n\nexport function writeFinalLogs(\n logPath: string,\n iterationLogs: IterationLog[],\n logContext: LogContext,\n success: boolean\n): void {\n // logPath is expected to be like: ./didactic-logs/optimize_<timestamp>/summary.md\n const folderPath = path.dirname(logPath);\n\n // Create folder if it doesn't exist\n if (!fs.existsSync(folderPath)) {\n fs.mkdirSync(folderPath, { recursive: true });\n }\n\n // Write summary.md\n const content = generateLogContent(iterationLogs, logContext, success);\n fs.writeFileSync(path.join(folderPath, 'summary.md'), content, 'utf-8');\n\n // Write prompts.md\n writePromptsFile(folderPath, iterationLogs, logContext);\n\n // Write rawData.json\n writeRawDataJson(folderPath, iterationLogs, logContext, success);\n\n // Write bestRun.json\n writeBestRunJson(folderPath, iterationLogs, logContext);\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport type { EvalResult, FieldResult } from '../types.js';\nimport { DEFAULT_PER_TEST_THRESHOLD } from '../library/constants.js';\n\n/**\n * Structure for rawData.json output from evaluate()\n */\nexport interface EvalReport {\n metadata: {\n timestamp: string;\n systemPrompt?: string;\n testCaseCount: number;\n perTestThreshold: number;\n };\n summary: {\n passed: number;\n total: number;\n successRate: number;\n correctFields: number;\n totalFields: number;\n accuracy: number;\n executorCost: number;\n comparatorCost: number;\n totalCost: number;\n durationMs: number;\n };\n testCases: TestCaseData[];\n}\n\ninterface TestCaseData {\n index: number;\n passed: boolean;\n passRate: number;\n input: unknown;\n expected: unknown;\n actual: unknown;\n additionalContext?: unknown;\n /** Executor cost. 0 indicates either zero cost or cost not tracked. */\n executorCost: number;\n /** Comparator cost. 0 indicates either zero cost or cost not tracked. */\n comparatorCost: number;\n error?: string;\n fields: Record<string, FieldResult>;\n}\n\n/**\n * Write evaluation results to rawData.json\n *\n * Synchronous writes are intentional - logging runs after evaluation completes\n * and errors are caught. This avoids async complexity in the calling code.\n */\nexport function writeEvalLogs<TInput, TOutput>(\n logPath: string,\n result: EvalResult<TInput, TOutput>,\n durationMs: number,\n perTestThreshold?: number\n): void {\n try {\n const dir = path.dirname(logPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n const report: EvalReport = {\n metadata: {\n timestamp: new Date().toISOString(),\n systemPrompt: result.systemPrompt,\n testCaseCount: result.total,\n perTestThreshold: perTestThreshold ?? DEFAULT_PER_TEST_THRESHOLD,\n },\n summary: {\n passed: result.passed,\n total: result.total,\n successRate: result.successRate,\n correctFields: result.correctFields,\n totalFields: result.totalFields,\n accuracy: result.accuracy,\n executorCost: result.cost,\n comparatorCost: result.comparatorCost,\n totalCost: result.cost + result.comparatorCost,\n durationMs,\n },\n testCases: result.testCases.map((tc, index) => ({\n index,\n passed: tc.passed,\n passRate: tc.passRate,\n input: tc.input,\n expected: tc.expected,\n actual: tc.actual,\n additionalContext: tc.additionalContext,\n executorCost: tc.cost ?? 0,\n comparatorCost: tc.comparatorCost ?? 0,\n error: tc.error,\n fields: tc.fields,\n })),\n };\n\n fs.writeFileSync(logPath, JSON.stringify(report, null, 2), 'utf-8');\n } catch (error) {\n console.error(\n `Failed to write eval logs to ${logPath}:`,\n error instanceof Error ? error.message : String(error)\n );\n // Don't throw - evaluation succeeded, just log persistence failed\n }\n}\n","import type {\n EvalConfig,\n EvalResult,\n FieldResult,\n NestedComparatorConfig,\n Comparator,\n ComparatorWithOrdering,\n LLMConfig,\n TestCaseResult,\n} from '../types.js';\nimport { matchArrays } from './comparators/matching.js';\nimport { exact } from './comparators/comparators.js';\nimport { DEFAULT_PER_TEST_THRESHOLD } from '../library/constants.js';\nimport {\n createProgressUpdater,\n trackPromiseProgress,\n} from '../optimizer/optimizer-logging.js';\nimport { writeEvalLogs } from './eval-logging.js';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\n\n/**\n * Run all test cases and return results.\n */\nexport async function evaluate<TInput, TOutput>(\n config: EvalConfig<TInput, TOutput>\n): Promise<EvalResult<TInput, TOutput>> {\n // Read config\n const { testCases, systemPrompt, executor, comparators, comparatorOverride } =\n config;\n\n if (testCases.length === 0) {\n throw new Error('testCases array cannot be empty');\n }\n\n if (!executor) {\n throw new Error('executor is required');\n }\n\n // Track timing for logs\n const startTime = Date.now();\n\n // Resolve log path if storeLogs is enabled\n const logPath = config.storeLogs\n ? typeof config.storeLogs === 'string'\n ? config.storeLogs\n : `./didactic-logs/eval_${Date.now()}_${crypto.randomUUID().slice(0, 8)}/rawData.json`\n : undefined;\n\n // Execute a single test case\n const executeTestCase = async ({\n input,\n expected,\n }: {\n input: TInput;\n expected: TOutput;\n }) => {\n try {\n // Run the executor\n const result = await executor(input, systemPrompt);\n\n let fields: Record<string, FieldResult>;\n if (comparatorOverride) {\n // Whole-object comparison mode (custom override)\n const compResult = await comparatorOverride(expected, result.output);\n fields = {\n '': {\n passed: compResult.passed,\n expected,\n actual: result.output,\n },\n };\n } else {\n // If comparators is a function (plain Comparator or ComparatorWithOrdering),\n // wrap it in { '': comparators } for consistent handling of root-level outputs.\n // Default to `exact` if no comparators provided.\n let comparatorConfig: NestedComparatorConfig;\n if (!comparators) {\n comparatorConfig = { '': exact };\n } else if (typeof comparators === 'function') {\n comparatorConfig = { '': comparators };\n } else {\n comparatorConfig = comparators;\n }\n\n // Field-level comparison mode (nested structure)\n fields = await compareFields({\n expected,\n actual: result.output,\n comparators: comparatorConfig,\n llmConfig: config.llmConfig,\n });\n }\n\n const passedFields = Object.values(fields).filter((f) => f.passed).length;\n const totalFields = Object.values(fields).length;\n const passRate = totalFields === 0 ? 1 : passedFields / totalFields;\n const threshold = config.perTestThreshold ?? DEFAULT_PER_TEST_THRESHOLD;\n const passed = passRate >= threshold;\n\n // Aggregate comparator costs from all fields\n const comparatorCost = Object.values(fields).reduce(\n (sum, field) => sum + (field.cost ?? 0),\n 0\n );\n\n return {\n input,\n expected,\n actual: result.output,\n additionalContext: result.additionalContext,\n cost: result.cost ?? 0,\n comparatorCost,\n passed,\n fields,\n passedFields,\n totalFields,\n passRate,\n };\n } catch (error) {\n return {\n input,\n expected,\n actual: undefined,\n cost: 0,\n comparatorCost: 0,\n passed: false,\n fields: {},\n passedFields: 0,\n totalFields: 0,\n passRate: 0,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n };\n\n // Run test cases (batched or all in parallel)\n const rateLimitBatch = config.rateLimitBatch;\n let results: TestCaseResult<TInput, TOutput>[];\n\n if (rateLimitBatch && rateLimitBatch > 0) {\n // Batched execution: run N test cases at a time\n results = [];\n const progress = createProgressUpdater('evals');\n\n for (let i = 0; i < testCases.length; i += rateLimitBatch) {\n const batch = testCases.slice(i, i + rateLimitBatch);\n const batchResults = await Promise.all(batch.map(executeTestCase));\n results.push(...batchResults);\n\n // Update progress\n progress.update(results.length, testCases.length);\n\n // Pause between batches (skip after last batch)\n const rateLimitPause = config.rateLimitPause;\n if (\n rateLimitPause &&\n rateLimitPause > 0 &&\n i + rateLimitBatch < testCases.length\n ) {\n await new Promise((r) => setTimeout(r, rateLimitPause * 1000));\n }\n }\n\n progress.finish();\n } else {\n // Run all test cases in parallel\n const progress = createProgressUpdater('evals');\n\n const wrappedTasks = testCases.map((tc) => executeTestCase(tc));\n\n // Track progress as each test completes\n const settledResults = await trackPromiseProgress(\n wrappedTasks,\n (completed, total) => progress.update(completed, total)\n );\n\n // Extract values (all are fulfilled since executeTestCase catches errors internally)\n results = settledResults.map(\n (r) =>\n (r as PromiseFulfilledResult<TestCaseResult<TInput, TOutput>>).value\n );\n\n progress.finish();\n }\n\n // Sort: failures first (by passRate ascending), then passes (100% at bottom)\n results.sort((a, b) => {\n if (a.passed !== b.passed) return a.passed ? 1 : -1;\n return a.passRate - b.passRate;\n });\n\n const passed = results.filter((r) => r.passed).length;\n const total = results.length;\n const successRate = total > 0 ? passed / total : 0;\n\n let correctFields = 0;\n let totalFields = 0;\n for (const r of results) {\n const fieldResults = Object.values(r.fields);\n totalFields += fieldResults.length;\n correctFields += fieldResults.filter((f) => f.passed).length;\n }\n const accuracy = totalFields > 0 ? correctFields / totalFields : 0;\n const cost = results.reduce((sum, r) => sum + (r.cost ?? 0), 0);\n const comparatorCost = results.reduce(\n (sum, r) => sum + (r.comparatorCost ?? 0),\n 0\n );\n\n const durationMs = Date.now() - startTime;\n const logFolder = logPath ? path.dirname(logPath) : undefined;\n\n const evalResult: EvalResult<TInput, TOutput> = {\n systemPrompt,\n testCases: results,\n passed,\n total,\n successRate,\n correctFields,\n totalFields,\n accuracy,\n cost,\n comparatorCost,\n ...(logFolder && { logFolder }),\n };\n\n // Write logs if enabled\n if (logPath) {\n writeEvalLogs(logPath, evalResult, durationMs, config.perTestThreshold);\n }\n\n return evalResult;\n}\n\n/**\n * Recursively compare expected vs actual, returning field-level results.\n * Path patterns: 'carrier', 'quote.premium', '[0]', 'quotes[0].carrier'\n */\nasync function compareFields(opts: {\n expected: unknown;\n actual: unknown;\n comparators: NestedComparatorConfig;\n path?: string;\n expectedParent?: unknown;\n actualParent?: unknown;\n llmConfig?: LLMConfig;\n}): Promise<Record<string, FieldResult>> {\n const {\n expected,\n actual,\n comparators,\n path = '',\n expectedParent,\n actualParent,\n llmConfig,\n } = opts;\n const results: Record<string, FieldResult> = {};\n const indexPath = (i: number) => (path ? `${path}[${i}]` : `[${i}]`);\n\n // ─── ARRAYS ─────────────────────────────────────────────────────────────────\n if (Array.isArray(expected)) {\n if (!Array.isArray(actual)) {\n return { [path]: { passed: false, expected, actual } };\n }\n if (expected.length === 0) {\n return {};\n }\n\n // Get the comparator config for this array field\n const arrayFieldName = getFieldName(path);\n const fieldComparator = comparators[arrayFieldName];\n\n // Check if this specific array should use unordered matching\n const isUnordered =\n fieldComparator &&\n typeof fieldComparator === 'function' &&\n '_unordered' in fieldComparator &&\n fieldComparator._unordered === true;\n\n // Get nested comparators for array items\n let itemComparators: NestedComparatorConfig;\n\n if (isUnordered) {\n // Unordered array: extract nested comparators from ComparatorWithOrdering\n itemComparators =\n (fieldComparator as ComparatorWithOrdering)._nestedComparators ||\n comparators;\n } else if (\n fieldComparator &&\n typeof fieldComparator === 'object' &&\n !('_unordered' in fieldComparator)\n ) {\n // Ordered array with nested comparators: use the plain object directly\n itemComparators = fieldComparator as NestedComparatorConfig;\n } else {\n // No nested comparators specified, use parent level\n itemComparators = comparators;\n }\n\n // Get matched pairs: [expectedIdx, actualIdx]\n let matchedPairs: [number, number][];\n\n if (isUnordered) {\n // Use Hungarian algorithm to find best matches\n matchedPairs = (await matchArrays(expected, actual, itemComparators))\n .assignments;\n } else {\n // Use simple index-based pairing (ordered)\n matchedPairs = [];\n for (let i = 0; i < expected.length && i < actual.length; i++) {\n matchedPairs.push([i, i]);\n }\n }\n\n const matchedIndices = new Set(matchedPairs.map(([i]) => i));\n\n // Compare matched pairs\n for (const [expIdx, actIdx] of matchedPairs) {\n Object.assign(\n results,\n await compareFields({\n expected: expected[expIdx],\n actual: actual[actIdx],\n comparators: itemComparators,\n path: indexPath(expIdx),\n expectedParent,\n actualParent,\n llmConfig,\n })\n );\n }\n\n // Report unmatched expected items as failures\n const hasArrayComparator = fieldComparator !== undefined;\n\n for (let i = 0; i < expected.length; i++) {\n if (matchedIndices.has(i)) continue;\n\n const item = expected[i];\n if (isObject(item)) {\n for (const [field, value] of Object.entries(item)) {\n if (field in itemComparators) {\n results[`${indexPath(i)}.${field}`] = {\n passed: false,\n expected: value,\n actual: undefined,\n };\n }\n }\n } else if (hasArrayComparator) {\n results[indexPath(i)] = {\n passed: false,\n expected: item,\n actual: undefined,\n };\n }\n }\n\n return results;\n }\n\n // ─── OBJECTS ────────────────────────────────────────────────────────────────\n if (isObject(expected)) {\n if (!isObject(actual)) {\n return { [path]: { passed: false, expected, actual } };\n }\n\n for (const [field, expValue] of Object.entries(expected)) {\n const fieldPath = path ? `${path}.${field}` : field;\n\n // Check if this field has a comparator (direct or nested)\n const fieldConfig = comparators[field];\n\n // Skip fields without any comparator defined\n if (fieldConfig === undefined) {\n continue;\n }\n\n let fieldComparators: NestedComparatorConfig;\n\n if (\n fieldConfig &&\n typeof fieldConfig === 'object' &&\n !('_unordered' in fieldConfig)\n ) {\n // It's a nested comparator config (plain object, not a comparator function)\n fieldComparators = fieldConfig as NestedComparatorConfig;\n } else {\n // It's a comparator function (possibly with _unordered flag) or undefined\n // Keep using current level comparators\n fieldComparators = comparators;\n }\n\n Object.assign(\n results,\n await compareFields({\n expected: expValue,\n actual: actual[field],\n comparators: fieldComparators,\n path: fieldPath,\n expectedParent: expected,\n actualParent: actual,\n llmConfig,\n })\n );\n }\n\n return results;\n }\n\n // ─── PRIMITIVES ─────────────────────────────────────────────────────────────\n const fieldName = getFieldName(path);\n let comparatorConfig = comparators[fieldName];\n\n // If no comparator found and we're at root, use exact as default\n if (!comparatorConfig && fieldName === '') {\n comparatorConfig = exact;\n }\n\n if (!comparatorConfig) {\n return {};\n }\n\n // Extract the actual comparator function\n // (could be a plain Comparator or a ComparatorWithOrdering)\n const comparator: Comparator<unknown> =\n typeof comparatorConfig === 'function'\n ? comparatorConfig\n : (exact as Comparator<unknown>);\n\n const result = await comparator(expected, actual, {\n expectedParent,\n actualParent,\n llmConfig,\n });\n return {\n [path]: {\n ...result,\n expected,\n actual,\n },\n };\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction getFieldName(path: string): string {\n const lastSegment = path.split('.').pop() || '';\n return lastSegment.replace(/\\[\\d+\\]$/, '');\n}\n","import type { TestCaseResult } from '../types.js';\nimport { formatFailure } from './optimizer-logging.js';\n\n/**\n * Default system prompt for patch generation.\n * Analyzes failures and suggests specific, focused changes to improve the prompt.\n */\nexport const DEFAULT_PATCH_SYSTEM_PROMPT = `\n 'You are optimizing a system prompt for an LLM workflow.\n Analyze the failure and suggest a specific, focused change to improve the prompt. \n Do NOT overfit. Be generalizable. \n\n <examples>\n VERY IMPORTANT, CRITICAL!!!\n Examples MUST be anonymized.\n NEVER use specific names, dates, or other identifying information UNLESS it's a universal fact: \n - example: (for an invoice processor) \n - task: extract data from parsed invoices\n - failure context: (returned expected: true, actual: false)\n - prompt patch: \"if you see \"Restocked\" on a Schedule B report of a Shopify invoice, mark returned as true.\" <- this is kind of specific, but it's a universal fact for the problem and could satisfy other inputs.)\n \n - example: (for a calendar app)\n - task: extract cost from calendar event\n - failure context: (cost expected: 123.45, actual: 167.89)\n - prompt patch: \"if you see \"Daisy\" in the name field, return 123.45 for cost\" <- this is too specific, it's overfit to a specific failure. The spirit of the failure is an incorrect extraction, you should look for the expected in the context and determine how the prompt could be modified to acheive the expected output.)\n </examples>\n`;\n\n/**\n * Default system prompt for merging patches.\n * Combines multiple patches into a coherent system prompt.\n */\nexport const DEFAULT_MERGE_SYSTEM_PROMPT = `\n You are an expert LLM prompt editor. \n You are merging improvements into a system prompt. \n Incorporate the suggestions while keeping the prompt clear and coherent.\n`;\n\n/**\n * Builds the user prompt for patch generation.\n * Formats the failure context and current prompt for the LLM.\n */\nexport function buildPatchUserPrompt(\n failure: TestCaseResult,\n currentPrompt: string,\n previousBetterPrompt?: string,\n previousBetterPromptFailures?: TestCaseResult[]\n): string {\n let userContent = `\n Current system prompt:\n ---\n ${currentPrompt}\n ---\n\n A test case failed:\n ${formatFailure(failure)}\n `;\n\n // If previous better prompt is provided, build failures context\n if (previousBetterPrompt) {\n // Get failures from previous better prompt\n const failuresContext =\n previousBetterPromptFailures && previousBetterPromptFailures.length > 0\n ? previousBetterPromptFailures\n .map((f, i) => `${i + 1}. ${formatFailure(f)}`)\n .join('\\n\\n')\n : 'None recorded';\n\n userContent += `\n Note: The current prompt is a REGRESSION from a better-performing version.\n Previous (better) prompt for reference:\n ---\n ${previousBetterPrompt}\n ---\n\n The failures the better prompt had:\n ${failuresContext}\n\n Your changes introduced new failures instead of fixing the above.\n Analyze what changed between the two prompts that might have caused this regression.\n Are there any new failures that were not present in the previous better prompt?\n Are there any failures that were present in the previous better prompt but not in the current prompt?\n Did any of our patches contradict any of the new failures? \n `;\n }\n\n userContent += `\n Suggest a specific change to the system prompt that would fix this failure.\n Be concise. Output ONLY the suggested patch/change, not the full prompt.\n DO NOT overfit the prompt to the test case.\n Generalize examples if you choose to use them.\n `;\n\n return userContent;\n}\n\n/**\n * Builds the user prompt for merging patches.\n * Formats the current prompt and suggested patches for the LLM.\n */\nexport function buildMergeUserPrompt(\n patches: string[],\n currentPrompt: string\n): string {\n return `\n Current prompt:\n ---\n ${currentPrompt}\n ---\n\n Suggested improvements:\n ${patches.map((p, i) => `${i + 1}. ${p}`).join('\\n\\n')}\n\n Create a single improved system prompt that incorporates these suggestions.\n Be mindful of the size of the new prompt.\n Use discretion when merging the patches, if you see duplicate information, emphasize it but don't repeat it.\n Output ONLY the new system prompt, nothing else.\n Respect enums.\n `;\n}\n","import type { EvalConfig, EvalResult, TestCaseResult } from '../types.js';\nimport type {\n OptimizeConfig,\n IterationResult,\n OptimizeResult,\n IterationLog,\n LogContext,\n} from './types.js';\nimport type { Message, LLMResult } from '../library/llm/types.js';\nimport { PROVIDER_SPECS } from '../library/constants.js';\nimport { evaluate } from '../eval/eval.js';\nimport { callLLM } from '../library/llm/llm-client.js';\nimport * as path from 'path';\nimport {\n generateLogContent,\n logCostLimitReached,\n logEvaluationResult,\n logEvaluationStart,\n logIterationStart,\n logLogsWritten,\n logMergeResult,\n logMergeStart,\n logOptimizationComplete,\n logOptimizerHeader,\n logPatchGenerationFailures,\n logPatchGenerationResult,\n logPatchGenerationStart,\n logRegressionDetected,\n logTargetFailures,\n logTargetReached,\n writeLog,\n writeFinalLogs,\n createProgressUpdater,\n trackPromiseProgress,\n} from './optimizer-logging.js';\nimport {\n DEFAULT_PATCH_SYSTEM_PROMPT,\n DEFAULT_MERGE_SYSTEM_PROMPT,\n buildPatchUserPrompt,\n buildMergeUserPrompt,\n} from './prompts.js';\n\nexport async function optimize<TInput, TOutput>(\n evalConfig: EvalConfig<TInput, TOutput>,\n config: OptimizeConfig\n): Promise<OptimizeResult<TInput, TOutput>> {\n if (!config.apiKey) {\n throw new Error('apiKey is required');\n }\n if (config.targetSuccessRate < 0 || config.targetSuccessRate > 1) {\n throw new Error('targetSuccessRate must be between 0 and 1');\n }\n\n const iterationLogs: IterationLog[] = [];\n\n const maxIterations =\n config.maxIterations ?? (config.maxCost !== undefined ? Infinity : 5);\n const startTime = new Date();\n const model = PROVIDER_SPECS[config.provider].model;\n\n // Context of the iteration to pass to optimizer-logging functions\n const logContext: LogContext = {\n config,\n startTime,\n model,\n perTestThreshold: evalConfig.perTestThreshold,\n rateLimitBatch: evalConfig.rateLimitBatch,\n rateLimitPause: evalConfig.rateLimitPause,\n };\n\n // Initialize prompts\n let currentPrompt = config.systemPrompt;\n let bestPrompt = currentPrompt;\n\n // Best run trackers\n let bestSuccessRate = 0;\n let bestPromptFailures: TestCaseResult<TInput, TOutput>[] = [];\n\n // Cost trackers\n let cumulativeCost = 0;\n let previousSuccessRate: number | undefined;\n\n // If enabled, store enriched logs to a folder\n // Folder: ./didactic-logs/optimize_<timestamp>/\n // Contains 4 files:\n // 1. summary.md - main report with configuration, metrics, and progress\n // 2. prompts.md - prompts used in each iteration\n // 3. rawData.json - all iteration data for analysis\n // 4. bestRun.json - comprehensive best run with all test results\n const logPath = config.storeLogs\n ? typeof config.storeLogs === 'string'\n ? config.storeLogs\n : `./didactic-logs/optimize_${startTime.getTime()}/summary.md`\n : undefined;\n\n // Helper function to record iteration of the optimization loop\n const recordIteration = (\n iteration: number,\n systemPrompt: string,\n result: EvalResult<TInput, TOutput>,\n cost: number,\n durationMs: number,\n inputTokens: number,\n outputTokens: number\n ): void => {\n iterationLogs.push({\n iteration,\n systemPrompt,\n passed: result.passed,\n total: result.total,\n correctFields: result.correctFields,\n totalFields: result.totalFields,\n testCases: result.testCases,\n cost,\n cumulativeCost,\n duration: durationMs,\n inputTokens,\n outputTokens,\n previousSuccessRate,\n });\n };\n\n const finalizeOptimization = (\n success: boolean,\n finalPrompt: string\n ): OptimizeResult<TInput, TOutput> => {\n const logFolder = logPath ? path.dirname(logPath) : undefined;\n logOptimizationComplete(\n bestSuccessRate,\n config.targetSuccessRate,\n cumulativeCost\n );\n if (logPath) {\n writeFinalLogs(logPath, iterationLogs, logContext, success);\n if (logFolder) {\n logLogsWritten(logFolder);\n }\n }\n const iterations: IterationResult<TInput, TOutput>[] = iterationLogs.map(\n (iter) => ({\n iteration: iter.iteration,\n systemPrompt: iter.systemPrompt,\n passed: iter.passed,\n total: iter.total,\n testCases: iter.testCases as TestCaseResult<TInput, TOutput>[],\n cost: iter.cost,\n })\n );\n return logFolder\n ? {\n success,\n finalPrompt,\n iterations,\n totalCost: cumulativeCost,\n logFolder,\n }\n : { success, finalPrompt, iterations, totalCost: cumulativeCost };\n };\n\n // Log optimizer header\n const testCount = evalConfig.testCases?.length ?? 0;\n logOptimizerHeader(model, config.targetSuccessRate, testCount);\n\n // Main optimization loop\n for (let i = 1; i <= maxIterations; i++) {\n const iterationStart = Date.now();\n let iterInputTokens = 0;\n let iterOutputTokens = 0;\n\n const iterationLabel =\n maxIterations === Infinity ? `${i}` : `${i}/${maxIterations}`;\n\n logIterationStart(iterationLabel);\n logEvaluationStart();\n\n const evalStart = Date.now();\n const result = await evaluate({\n ...evalConfig,\n systemPrompt: currentPrompt,\n });\n\n cumulativeCost += result.cost;\n logEvaluationResult(result, cumulativeCost, Date.now() - evalStart);\n\n\n // Check for regression\n const regressed = i > 1 && result.successRate <= bestSuccessRate;\n if (regressed) {\n logRegressionDetected(bestSuccessRate);\n }\n\n if (result.successRate > bestSuccessRate) {\n bestSuccessRate = result.successRate;\n bestPrompt = currentPrompt;\n bestPromptFailures = result.testCases.filter((tc) => !tc.passed);\n }\n\n // Target reached\n // Success is determined by reaching targetSuccessRate, not zero failures.\n if (result.successRate >= config.targetSuccessRate) {\n logTargetReached(config.targetSuccessRate);\n recordIteration(\n i,\n currentPrompt,\n result,\n result.cost,\n Date.now() - iterationStart,\n iterInputTokens,\n iterOutputTokens\n );\n return finalizeOptimization(true, currentPrompt);\n }\n\n const failures = result.testCases.filter((tc) => !tc.passed);\n logTargetFailures(config.targetSuccessRate, failures.length);\n\n // Cost limit before patches\n if (config.maxCost !== undefined && cumulativeCost >= config.maxCost) {\n logCostLimitReached(cumulativeCost);\n recordIteration(\n i,\n currentPrompt,\n result,\n result.cost,\n Date.now() - iterationStart,\n iterInputTokens,\n iterOutputTokens\n );\n return finalizeOptimization(false, bestPrompt);\n }\n\n // Generate patches\n logPatchGenerationStart(failures.length);\n const patchStart = Date.now();\n\n const patchProgress = createProgressUpdater('patches');\n\n const patchSettled = await trackPromiseProgress(\n failures.map((failure) =>\n generatePatch(\n failure,\n currentPrompt,\n config,\n regressed ? bestPrompt : undefined,\n regressed ? bestPromptFailures : undefined\n )\n ),\n (completed, total) => patchProgress.update(completed, total)\n );\n\n patchProgress.finish();\n\n const patchResults = patchSettled\n .filter(\n (r): r is PromiseFulfilledResult<LLMResult> => r.status === 'fulfilled'\n )\n .map((r) => r.value);\n\n const failedPatchCount = patchSettled.filter(\n (r) => r.status === 'rejected'\n ).length;\n if (failedPatchCount > 0) {\n logPatchGenerationFailures(failedPatchCount, failures.length);\n }\n\n if (patchResults.length === 0) {\n recordIteration(\n i,\n currentPrompt,\n result,\n result.cost,\n Date.now() - iterationStart,\n iterInputTokens,\n iterOutputTokens\n );\n continue;\n }\n\n const patches = patchResults.map((r) => r.text);\n\n // Track patch cost and tokens\n const patchCost = patchResults.reduce((sum, r) => sum + r.cost, 0);\n const patchInputTokens = patchResults.reduce(\n (sum, r) => sum + r.inputTokens,\n 0\n );\n const patchOutputTokens = patchResults.reduce(\n (sum, r) => sum + r.outputTokens,\n 0\n );\n iterInputTokens += patchInputTokens;\n iterOutputTokens += patchOutputTokens;\n cumulativeCost += patchCost;\n logPatchGenerationResult(\n patchCost,\n cumulativeCost,\n Date.now() - patchStart\n );\n\n // Cost limit before merge\n if (config.maxCost !== undefined && cumulativeCost >= config.maxCost) {\n logCostLimitReached(cumulativeCost);\n recordIteration(\n i,\n currentPrompt,\n result,\n result.cost + patchCost,\n Date.now() - iterationStart,\n iterInputTokens,\n iterOutputTokens\n );\n return finalizeOptimization(false, bestPrompt);\n }\n\n // Merge patches\n logMergeStart();\n const mergeStart = Date.now();\n const mergeResult = await mergePatches(patches, currentPrompt, config);\n iterInputTokens += mergeResult.inputTokens;\n iterOutputTokens += mergeResult.outputTokens;\n cumulativeCost += mergeResult.cost;\n logMergeResult(mergeResult.cost, cumulativeCost, Date.now() - mergeStart);\n\n // Record iteration\n const iterCost = result.cost + patchCost + mergeResult.cost;\n recordIteration(\n i,\n currentPrompt,\n result,\n iterCost,\n Date.now() - iterationStart,\n iterInputTokens,\n iterOutputTokens\n );\n if (logPath)\n writeLog(logPath, generateLogContent(iterationLogs, logContext, false));\n\n // Cost limit after merge\n if (config.maxCost !== undefined && cumulativeCost >= config.maxCost) {\n logCostLimitReached(cumulativeCost);\n return finalizeOptimization(false, bestPrompt);\n }\n\n previousSuccessRate = result.successRate;\n currentPrompt = mergeResult.text;\n }\n\n // Loop exhausted\n return finalizeOptimization(false, bestPrompt);\n}\n\nasync function generatePatch(\n failure: TestCaseResult,\n currentPrompt: string,\n config: OptimizeConfig,\n previousBetterPrompt?: string,\n previousBetterPromptFailures?: TestCaseResult[]\n): Promise<LLMResult> {\n const userContent = buildPatchUserPrompt(\n failure,\n currentPrompt,\n previousBetterPrompt,\n previousBetterPromptFailures\n );\n\n const systemContent = config.patchSystemPrompt ?? DEFAULT_PATCH_SYSTEM_PROMPT;\n\n const messages: Message[] = [\n { role: 'system', content: systemContent },\n { role: 'user', content: userContent },\n ];\n\n return callLLM({\n provider: config.provider,\n apiKey: config.apiKey,\n messages,\n useThinking: config.thinking ?? false,\n });\n}\n\nasync function mergePatches(\n patches: string[],\n currentPrompt: string,\n config: OptimizeConfig\n): Promise<LLMResult> {\n const systemContent = config.mergeSystemPrompt ?? DEFAULT_MERGE_SYSTEM_PROMPT;\n const userContent = buildMergeUserPrompt(patches, currentPrompt);\n\n const messages: Message[] = [\n { role: 'system', content: systemContent },\n {\n role: 'user',\n content: userContent,\n },\n ];\n\n return callLLM({\n provider: config.provider,\n apiKey: config.apiKey,\n messages,\n useThinking: config.thinking ?? false,\n });\n}\n","import type { Executor, ExecutorResult } from '../types.js';\nimport { DEFAULT_ENDPOINT_TIMEOUT_MS } from '../library/constants.js';\n\n/**\n * Configuration for endpoint executor.\n */\nexport interface EndpointConfig<TOutput = unknown> {\n method?: 'POST' | 'GET';\n headers?: Record<string, string>;\n mapResponse?: (response: any) => TOutput;\n mapAdditionalContext?: (response: any) => unknown;\n mapCost?: (response: any) => number;\n timeout?: number;\n}\n\n/**\n * Configuration for function executor.\n * @template TInput - Input type passed to the function\n * @template TOutput - Output type after mapResponse (what gets compared)\n * @template TRaw - Raw return type from fn (defaults to TOutput if no mapResponse)\n */\nexport interface FnConfig<TInput, TOutput, TRaw = TOutput> {\n fn: (input: TInput, systemPrompt?: string) => Promise<TRaw>;\n mapResponse?: (result: TRaw) => TOutput;\n mapAdditionalContext?: (result: TRaw) => unknown;\n mapCost?: (result: TRaw) => number;\n}\n\n/**\n * Creates an executor that calls an HTTP endpoint.\n *\n * @example\n * ```ts\n * const executor = endpoint('https://api.example.com/workflow', {\n * headers: { Authorization: 'Bearer token' },\n * });\n * ```\n */\nexport function endpoint<TInput = unknown, TOutput = unknown>(\n url: string,\n config: EndpointConfig<TOutput> = {}\n): Executor<TInput, TOutput> {\n const {\n method = 'POST',\n headers = {},\n mapResponse,\n mapAdditionalContext,\n mapCost,\n timeout = DEFAULT_ENDPOINT_TIMEOUT_MS,\n } = config;\n\n return async (\n input: TInput,\n systemPrompt?: string\n ): Promise<ExecutorResult<TOutput>> => {\n const body =\n typeof input === 'object' && input !== null\n ? { ...input, systemPrompt }\n : { input, systemPrompt };\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`HTTP ${response.status}: ${text}`);\n }\n\n const data = await response.json();\n const additionalContext = mapAdditionalContext?.(data);\n const cost = mapCost?.(data) ?? 0;\n\n if (mapResponse) {\n return { output: mapResponse(data), additionalContext, cost };\n }\n\n return {\n output: data as TOutput,\n additionalContext,\n cost,\n };\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n };\n}\n\n/**\n * Creates an executor from a local function.\n *\n * @example\n * ```ts\n * const executor = fn({\n * fn: async (input, systemPrompt) => {\n * const result = await myLLMCall(input, systemPrompt);\n * return result;\n * },\n * });\n * ```\n *\n * @example With mapResponse to extract output from a richer response:\n * ```ts\n * const executor = fn({\n * fn: async (input, systemPrompt) => await startWorkflow({ ... }),\n * mapResponse: (result) => ({ documentType: result.documentType }),\n * mapCost: (result) => result.cost,\n * mapAdditionalContext: (result) => result.metadata,\n * });\n * ```\n */\nexport function fn<TInput, TOutput extends object, TRaw = TOutput>(\n config: FnConfig<TInput, TOutput, TRaw>\n): Executor<TInput, TOutput> {\n return async (\n input: TInput,\n systemPrompt?: string\n ): Promise<ExecutorResult<TOutput>> => {\n const raw = await config.fn(input, systemPrompt);\n const output = config.mapResponse\n ? config.mapResponse(raw)\n : (raw as unknown as TOutput);\n const additionalContext = config.mapAdditionalContext?.(raw);\n const cost = config.mapCost?.(raw) ?? 0;\n return { output, additionalContext, cost };\n };\n}\n\n/**\n * Creates a mock executor for testing.\n * Can accept either:\n * - An array of outputs (returned in sequence, cycling if more calls than outputs)\n * - A function that maps input to output\n *\n * @example Array-based:\n * ```ts\n * const executor = mock([\n * { premium: 12500, policyType: 'claims-made' },\n * { premium: 8200, policyType: 'entity' },\n * ]);\n * ```\n *\n * @example Function-based:\n * ```ts\n * const executor = mock((input) => ({\n * id: input.id,\n * processed: true,\n * }));\n * ```\n */\nexport function mock<TInput, TOutput extends object>(\n outputsOrFn: TOutput[] | ((input: TInput, systemPrompt?: string) => TOutput)\n): Executor<TInput, TOutput> {\n // Function-based mock\n if (typeof outputsOrFn === 'function') {\n return async (\n input: TInput,\n systemPrompt?: string\n ): Promise<ExecutorResult<TOutput>> => {\n const output = outputsOrFn(input, systemPrompt);\n return { output };\n };\n }\n\n // Array-based mock\n const outputs = outputsOrFn;\n if (outputs.length === 0) {\n throw new Error('mock() requires at least one output');\n }\n\n let callIndex = 0;\n\n return async (): Promise<ExecutorResult<TOutput>> => {\n const output = outputs[callIndex % outputs.length];\n callIndex++;\n return { output };\n };\n}\n","import type {\n EvalConfig,\n EvalResult,\n Executor,\n OptimizeConfig,\n OptimizeResult,\n} from './types.js';\nimport type { EndpointConfig, FnConfig } from './eval/executors.js';\nimport { evaluate } from './eval/eval.js';\nimport { optimize as runOptimize } from './optimizer/optimizer.js';\nimport {\n endpoint as createEndpoint,\n fn as createFn,\n} from './eval/executors.js';\n\n// Re-export types\nexport type {\n // Creating custom comparators\n Comparator,\n ComparatorContext,\n ComparatorResult,\n\n // Creating custom executors\n Executor,\n ExecutorResult,\n\n // Main API\n TestCase,\n TestCaseResult,\n EvalConfig,\n EvalResult,\n OptimizeConfig,\n OptimizeResult,\n\n // LLM configuration\n LLMConfig,\n} from './types.js';\n\n// Re-export LLM providers enum\nexport { LLMProviders } from './types.js';\n\n// Re-export executor config types\nexport type { EndpointConfig, FnConfig } from './eval/executors.js';\n\n// Re-export comparators\nexport {\n within,\n oneOf,\n presence,\n custom,\n exact,\n contains,\n numeric,\n date,\n name,\n llmCompare,\n unordered,\n} from './eval/comparators/comparators.js';\n\n// Re-export comparator config types\nexport type { LLMCompareConfig } from './eval/comparators/comparators.js';\n\n// Re-export executors\nexport { endpoint, fn, mock } from './eval/executors.js';\n\n// Re-export eval\nexport { evaluate } from './eval/eval.js';\n\n// Re-export optimizer\nexport { optimize } from './optimizer/optimizer.js';\n\n// Main didact namespace\n\n/**\n * Overloaded eval function with proper return type inference.\n */\nfunction didacticEval<TInput, TOutput>(\n config: EvalConfig<TInput, TOutput> & { optimize: OptimizeConfig }\n): Promise<OptimizeResult<TInput, TOutput>>;\nfunction didacticEval<TInput, TOutput>(\n config: EvalConfig<TInput, TOutput> & { optimize?: undefined }\n): Promise<EvalResult<TInput, TOutput>>;\nfunction didacticEval<TInput, TOutput>(\n config: EvalConfig<TInput, TOutput>\n): Promise<EvalResult<TInput, TOutput> | OptimizeResult<TInput, TOutput>>;\nfunction didacticEval<TInput, TOutput>(\n config: EvalConfig<TInput, TOutput>\n): Promise<EvalResult<TInput, TOutput> | OptimizeResult<TInput, TOutput>> {\n if (config.optimize) {\n const { optimize, ...evalConfig } = config;\n return runOptimize(evalConfig, optimize);\n }\n return evaluate(config);\n}\n\n/**\n * Main didactic namespace for fluent API.\n *\n * @example\n * ```ts\n * import { didactic, within, oneOf, presence } from 'didactic';\n *\n * const result = await didactic.eval({\n * systemPrompt: 'Extract insurance quotes from broker emails.',\n * executor: didactic.endpoint('https://api.example.com/workflow'),\n * comparators: {\n * premium: within({ tolerance: 0.05 }),\n * policyType: oneOf(['claims-made', 'occurrence']),\n * entityName: presence,\n * },\n * testCases: [\n * {\n * input: { emailId: 'email-123' },\n * expected: { premium: 12500, policyType: 'claims-made', entityName: 'Acme Corp' },\n * },\n * ],\n * });\n *\n * console.log(`${result.passed}/${result.total} passed`);\n * ```\n */\nexport const didactic = {\n /**\n * Run an eval (or optimization if optimize config is present).\n */\n eval: didacticEval,\n\n /**\n * Run optimization to improve a system prompt.\n */\n optimize<TInput, TOutput>(\n evalConfig: EvalConfig<TInput, TOutput>,\n config: OptimizeConfig\n ): Promise<OptimizeResult<TInput, TOutput>> {\n return runOptimize(evalConfig, config);\n },\n\n /**\n * Create an executor that calls an HTTP endpoint.\n */\n endpoint<TInput = unknown, TOutput = unknown>(\n url: string,\n config?: EndpointConfig<TOutput>\n ): Executor<TInput, TOutput> {\n return createEndpoint(url, config);\n },\n\n /**\n * Create an executor from a local function.\n */\n fn<TInput, TOutput extends object>(\n config: FnConfig<TInput, TOutput>\n ): Executor<TInput, TOutput> {\n return createFn(config);\n },\n};\n\n// Default export\nexport default didactic;\n\n// Legacy alias for backwards compatibility during transition\nexport { didactic as didact };\n"],"mappings":"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"}