evalsense 0.2.0 → 0.3.0

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,4 +1,6 @@
1
- export { batchItems, createJSONSchema, createLLMError, extractScore, faithfulness, fillPrompt, getLLMClient, hallucination, parseJSONResponse, relevance, requireLLMClient, resetLLMClient, setLLMClient, toxicity, validateResponse, withTimeout } from '../chunk-BRPM6AB6.js';
1
+ import { parseJSONResponse } from '../chunk-Z3U6AUWX.js';
2
+ export { batchItems, createJSONSchema, createLLMError, extractScore, faithfulness, fillPrompt, getLLMClient, hallucination, parseJSONResponse, relevance, requireLLMClient, resetLLMClient, setLLMClient, toxicity, validateResponse, withTimeout } from '../chunk-Z3U6AUWX.js';
3
+ import { __require } from '../chunk-DGUM43GV.js';
2
4
 
3
5
  // src/metrics/custom/index.ts
4
6
  var customMetrics = /* @__PURE__ */ new Map();
@@ -186,6 +188,242 @@ function createSpyMockClient(response) {
186
188
  return { client, prompts };
187
189
  }
188
190
 
189
- export { BINARY_THRESHOLDS, SEVERITY_THRESHOLDS, batch, clearMetrics, createErrorMockClient, createKeywordMetric, createMetricOutput, createMockLLMClient, createPatternMetric, createSequentialMockClient, createSpyMockClient, delay, getMetric, listMetrics, normalizeScore, registerMetric, runMetric, scoreToLabel, unregisterMetric };
191
+ // src/metrics/llm/adapters/openai.ts
192
+ function createOpenAIAdapter(apiKey, options = {}) {
193
+ const {
194
+ model = "gpt-4-turbo-preview",
195
+ temperature = 0,
196
+ maxTokens = 4096,
197
+ baseURL,
198
+ organization,
199
+ timeout = 3e4
200
+ } = options;
201
+ if (!apiKey) {
202
+ throw new Error(
203
+ "OpenAI API key is required. Get one at https://platform.openai.com/api-keys"
204
+ );
205
+ }
206
+ let OpenAI;
207
+ let openaiClient;
208
+ function ensureClient() {
209
+ if (openaiClient) return openaiClient;
210
+ try {
211
+ OpenAI = __require("openai").default || __require("openai");
212
+ } catch (error) {
213
+ throw new Error(
214
+ "OpenAI SDK not found. Install it with: npm install openai\nVisit https://github.com/openai/openai-node for documentation."
215
+ );
216
+ }
217
+ openaiClient = new OpenAI({
218
+ apiKey,
219
+ baseURL,
220
+ organization,
221
+ timeout
222
+ });
223
+ return openaiClient;
224
+ }
225
+ return {
226
+ async complete(prompt) {
227
+ const client = ensureClient();
228
+ try {
229
+ const response = await client.chat.completions.create({
230
+ model,
231
+ messages: [{ role: "user", content: prompt }],
232
+ temperature,
233
+ max_tokens: maxTokens
234
+ });
235
+ return response.choices[0]?.message?.content ?? "";
236
+ } catch (error) {
237
+ const errorMessage = error?.message || error?.error?.message || String(error);
238
+ throw new Error(
239
+ `OpenAI API error (model: ${model}): ${errorMessage}
240
+ Check your API key and quota at https://platform.openai.com/account/usage`
241
+ );
242
+ }
243
+ },
244
+ async completeStructured(prompt, _schema) {
245
+ const client = ensureClient();
246
+ try {
247
+ const response = await client.chat.completions.create({
248
+ model,
249
+ messages: [{ role: "user", content: prompt }],
250
+ response_format: { type: "json_object" },
251
+ temperature,
252
+ max_tokens: maxTokens
253
+ });
254
+ const text = response.choices[0]?.message?.content ?? "{}";
255
+ return JSON.parse(text);
256
+ } catch (error) {
257
+ const errorMessage = error?.message || error?.error?.message || String(error);
258
+ throw new Error(
259
+ `OpenAI API error (model: ${model}): ${errorMessage}
260
+ Check your API key and quota at https://platform.openai.com/account/usage`
261
+ );
262
+ }
263
+ }
264
+ };
265
+ }
266
+
267
+ // src/metrics/llm/adapters/anthropic.ts
268
+ function createAnthropicAdapter(apiKey, options = {}) {
269
+ const {
270
+ model = "claude-3-5-sonnet-20241022",
271
+ maxTokens = 4096,
272
+ temperature = 0,
273
+ timeout = 3e4
274
+ } = options;
275
+ if (!apiKey) {
276
+ throw new Error(
277
+ "Anthropic API key is required. Get one at https://console.anthropic.com/"
278
+ );
279
+ }
280
+ if (temperature < 0 || temperature > 1) {
281
+ throw new Error(`Anthropic temperature must be between 0 and 1, got ${temperature}`);
282
+ }
283
+ let Anthropic;
284
+ let anthropicClient;
285
+ function ensureClient() {
286
+ if (anthropicClient) return anthropicClient;
287
+ try {
288
+ Anthropic = __require("@anthropic-ai/sdk").default || __require("@anthropic-ai/sdk");
289
+ } catch (error) {
290
+ throw new Error(
291
+ "Anthropic SDK not found. Install it with: npm install @anthropic-ai/sdk\nVisit https://github.com/anthropics/anthropic-sdk-typescript for documentation."
292
+ );
293
+ }
294
+ anthropicClient = new Anthropic({
295
+ apiKey,
296
+ timeout
297
+ });
298
+ return anthropicClient;
299
+ }
300
+ return {
301
+ async complete(prompt) {
302
+ const client = ensureClient();
303
+ try {
304
+ const message = await client.messages.create({
305
+ model,
306
+ max_tokens: maxTokens,
307
+ temperature,
308
+ messages: [{ role: "user", content: prompt }]
309
+ });
310
+ const firstBlock = message.content[0];
311
+ return firstBlock?.type === "text" ? firstBlock.text : "";
312
+ } catch (error) {
313
+ const errorMessage = error?.message || error?.error?.message || String(error);
314
+ throw new Error(
315
+ `Anthropic API error (model: ${model}): ${errorMessage}
316
+ Check your API key and usage at https://console.anthropic.com/`
317
+ );
318
+ }
319
+ },
320
+ async completeStructured(prompt, schema) {
321
+ const jsonPrompt = prompt + `
322
+
323
+ IMPORTANT: Respond with valid JSON only. No markdown, no explanation. The JSON must match this schema: ${JSON.stringify(schema)}`;
324
+ const response = await this.complete(jsonPrompt);
325
+ try {
326
+ return parseJSONResponse(response);
327
+ } catch (error) {
328
+ throw new Error(
329
+ `Failed to parse Anthropic response as JSON: ${error.message}
330
+ Response preview: ${response.substring(0, 200)}...`
331
+ );
332
+ }
333
+ }
334
+ };
335
+ }
336
+
337
+ // src/metrics/llm/adapters/openrouter.ts
338
+ function createOpenRouterAdapter(apiKey, options = {}) {
339
+ const {
340
+ model = "anthropic/claude-3.5-sonnet",
341
+ temperature = 0,
342
+ maxTokens = 4096,
343
+ appName = "evalsense",
344
+ siteUrl,
345
+ timeout = 3e4
346
+ } = options;
347
+ if (!apiKey) {
348
+ throw new Error("OpenRouter API key is required. Get one at https://openrouter.ai/keys");
349
+ }
350
+ const baseURL = "https://openrouter.ai/api/v1";
351
+ async function callAPI(messages, jsonMode = false) {
352
+ const headers = {
353
+ Authorization: `Bearer ${apiKey}`,
354
+ "Content-Type": "application/json",
355
+ "HTTP-Referer": siteUrl || "https://github.com/evalsense/evalsense",
356
+ "X-Title": appName
357
+ };
358
+ const body = {
359
+ model,
360
+ messages,
361
+ temperature,
362
+ max_tokens: maxTokens
363
+ };
364
+ if (jsonMode) {
365
+ body.response_format = { type: "json_object" };
366
+ }
367
+ const controller = new AbortController();
368
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
369
+ try {
370
+ const response = await fetch(`${baseURL}/chat/completions`, {
371
+ method: "POST",
372
+ headers,
373
+ body: JSON.stringify(body),
374
+ signal: controller.signal
375
+ });
376
+ clearTimeout(timeoutId);
377
+ if (!response.ok) {
378
+ const errorData = await response.json().catch(() => ({}));
379
+ const errorMessage = errorData.error?.message || response.statusText || "Unknown error";
380
+ throw new Error(`OpenRouter API error (${response.status}): ${errorMessage}`);
381
+ }
382
+ const data = await response.json();
383
+ return data.choices?.[0]?.message?.content ?? "";
384
+ } catch (error) {
385
+ clearTimeout(timeoutId);
386
+ if (error.name === "AbortError") {
387
+ throw new Error(`OpenRouter request timed out after ${timeout}ms (model: ${model})`);
388
+ }
389
+ const errorMessage = error?.message || String(error);
390
+ throw new Error(
391
+ `OpenRouter API error (model: ${model}): ${errorMessage}
392
+ Check your API key and credits at https://openrouter.ai/activity`
393
+ );
394
+ }
395
+ }
396
+ return {
397
+ async complete(prompt) {
398
+ return callAPI([{ role: "user", content: prompt }], false);
399
+ },
400
+ async completeStructured(prompt, schema) {
401
+ let response;
402
+ try {
403
+ response = await callAPI(
404
+ [{ role: "user", content: prompt }],
405
+ true
406
+ // Enable JSON mode
407
+ );
408
+ } catch (error) {
409
+ const jsonPrompt = prompt + `
410
+
411
+ IMPORTANT: Respond with valid JSON only. No markdown, no explanation. The JSON must match this schema: ${JSON.stringify(schema)}`;
412
+ response = await callAPI([{ role: "user", content: jsonPrompt }], false);
413
+ }
414
+ try {
415
+ return parseJSONResponse(response);
416
+ } catch (error) {
417
+ throw new Error(
418
+ `Failed to parse OpenRouter response as JSON: ${error.message}
419
+ Model: ${model}
420
+ Response preview: ${response.substring(0, 200)}...`
421
+ );
422
+ }
423
+ }
424
+ };
425
+ }
426
+
427
+ export { BINARY_THRESHOLDS, SEVERITY_THRESHOLDS, batch, clearMetrics, createAnthropicAdapter, createErrorMockClient, createKeywordMetric, createMetricOutput, createMockLLMClient, createOpenAIAdapter, createOpenRouterAdapter, createPatternMetric, createSequentialMockClient, createSpyMockClient, delay, getMetric, listMetrics, normalizeScore, registerMetric, runMetric, scoreToLabel, unregisterMetric };
190
428
  //# sourceMappingURL=index.js.map
191
429
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/metrics/custom/index.ts","../../src/metrics/utils/index.ts","../../src/metrics/llm/adapters/mock.ts"],"names":["delay"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Custom metric registration\n */\n\nimport type { MetricFn, MetricOutput, MetricConfig } from \"../../core/types.js\";\n\n/**\n * Registry of custom metrics\n */\nconst customMetrics = new Map<string, MetricFn>();\n\n/**\n * Registers a custom metric\n *\n * @example\n * ```ts\n * registerMetric(\"custom-relevance\", async ({ outputs, query }) => {\n * // Custom evaluation logic\n * return outputs.map(o => ({\n * id: o.id,\n * metric: \"custom-relevance\",\n * score: evaluateRelevance(o.output, query),\n * }));\n * });\n * ```\n */\nexport function registerMetric(name: string, fn: MetricFn): void {\n if (customMetrics.has(name)) {\n throw new Error(`Metric \"${name}\" is already registered`);\n }\n customMetrics.set(name, fn);\n}\n\n/**\n * Gets a registered custom metric\n */\nexport function getMetric(name: string): MetricFn | undefined {\n return customMetrics.get(name);\n}\n\n/**\n * Runs a registered metric\n */\nexport async function runMetric(name: string, config: MetricConfig): Promise<MetricOutput[]> {\n const fn = customMetrics.get(name);\n if (!fn) {\n throw new Error(`Metric \"${name}\" is not registered`);\n }\n return fn(config);\n}\n\n/**\n * Lists all registered custom metrics\n */\nexport function listMetrics(): string[] {\n return Array.from(customMetrics.keys());\n}\n\n/**\n * Unregisters a metric (mainly for testing)\n */\nexport function unregisterMetric(name: string): boolean {\n return customMetrics.delete(name);\n}\n\n/**\n * Clears all registered metrics (mainly for testing)\n */\nexport function clearMetrics(): void {\n customMetrics.clear();\n}\n\n/**\n * Creates a simple string-matching metric\n */\nexport function createPatternMetric(\n name: string,\n patterns: RegExp[],\n options: { matchScore?: number; noMatchScore?: number } = {}\n): MetricFn {\n const { matchScore = 1, noMatchScore = 0 } = options;\n\n return async (config: MetricConfig): Promise<MetricOutput[]> => {\n return config.outputs.map((o) => {\n const hasMatch = patterns.some((p) => p.test(o.output));\n return {\n id: o.id,\n metric: name,\n score: hasMatch ? matchScore : noMatchScore,\n label: hasMatch ? \"detected\" : \"not_detected\",\n };\n });\n };\n}\n\n/**\n * Creates a keyword-based metric\n */\nexport function createKeywordMetric(\n name: string,\n keywords: string[],\n options: { caseSensitive?: boolean; threshold?: number } = {}\n): MetricFn {\n const { caseSensitive = false, threshold = 0.5 } = options;\n\n const normalizedKeywords = caseSensitive\n ? keywords\n : keywords.map((k) => k.toLowerCase());\n\n return async (config: MetricConfig): Promise<MetricOutput[]> => {\n return config.outputs.map((o) => {\n const text = caseSensitive ? o.output : o.output.toLowerCase();\n const matches = normalizedKeywords.filter((k) => text.includes(k));\n const score = matches.length / keywords.length;\n\n return {\n id: o.id,\n metric: name,\n score,\n label: score >= threshold ? \"detected\" : \"not_detected\",\n };\n });\n };\n}\n","/**\n * Metric utilities\n */\n\nimport type { MetricOutput } from \"../../core/types.js\";\n\n/**\n * Normalizes a score to 0-1 range\n */\nexport function normalizeScore(score: number, min = 0, max = 1): number {\n const range = max - min;\n if (range === 0) return 0;\n return Math.max(0, Math.min(1, (score - min) / range));\n}\n\n/**\n * Converts a numeric score to a label based on thresholds\n */\nexport function scoreToLabel(\n score: number,\n thresholds: { label: string; min: number }[]\n): string {\n // Sort thresholds by min descending\n const sorted = [...thresholds].sort((a, b) => b.min - a.min);\n\n for (const { label, min } of sorted) {\n if (score >= min) {\n return label;\n }\n }\n\n return thresholds[thresholds.length - 1]?.label ?? \"unknown\";\n}\n\n/**\n * Creates a metric output from a score\n */\nexport function createMetricOutput(\n id: string,\n metric: string,\n score: number,\n labelThresholds?: { label: string; min: number }[]\n): MetricOutput {\n const normalizedScore = normalizeScore(score);\n const label = labelThresholds\n ? scoreToLabel(normalizedScore, labelThresholds)\n : normalizedScore >= 0.5\n ? \"high\"\n : \"low\";\n\n return {\n id,\n metric,\n score: normalizedScore,\n label,\n };\n}\n\n/**\n * Default thresholds for binary metrics\n */\nexport const BINARY_THRESHOLDS = [\n { label: \"true\", min: 0.5 },\n { label: \"false\", min: 0 },\n];\n\n/**\n * Default thresholds for severity metrics\n */\nexport const SEVERITY_THRESHOLDS = [\n { label: \"high\", min: 0.7 },\n { label: \"medium\", min: 0.4 },\n { label: \"low\", min: 0 },\n];\n\n/**\n * Batches items for parallel processing\n */\nexport function batch<T>(items: T[], size: number): T[][] {\n const batches: T[][] = [];\n for (let i = 0; i < items.length; i += size) {\n batches.push(items.slice(i, i + size));\n }\n return batches;\n}\n\n/**\n * Delays execution\n */\nexport function delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Mock LLM client for testing\n *\n * Provides a configurable mock implementation of LLMClient for unit tests.\n */\n\nimport type { LLMClient, JSONSchema } from \"../../../core/types.js\";\n\n/**\n * Configuration for mock LLM client\n */\nexport interface MockLLMConfig {\n /** Fixed response to return (can be string or object for JSON mode) */\n response?: string | Record<string, unknown>;\n\n /** Multiple responses for sequential calls */\n responses?: Array<string | Record<string, unknown>>;\n\n /** Delay in milliseconds before responding */\n delay?: number;\n\n /** Whether to throw an error */\n shouldError?: boolean;\n\n /** Error message to throw */\n errorMessage?: string;\n\n /** Function to validate prompts */\n onPrompt?: (prompt: string) => void;\n}\n\n/**\n * Creates a mock LLM client for testing\n *\n * @example\n * ```ts\n * const mock = createMockLLMClient({\n * response: JSON.stringify({ score: 0.8, reasoning: \"test\" }),\n * delay: 100\n * });\n *\n * setLLMClient(mock);\n * ```\n */\nexport function createMockLLMClient(config: MockLLMConfig = {}): LLMClient {\n const {\n response,\n responses,\n delay = 0,\n shouldError = false,\n errorMessage = \"Mock LLM error\",\n onPrompt,\n } = config;\n\n let callCount = 0;\n\n const getResponse = (): string | Record<string, unknown> => {\n if (responses && responses.length > 0) {\n const resp = responses[Math.min(callCount, responses.length - 1)];\n if (!resp) {\n return JSON.stringify({ score: 0.5, reasoning: \"Mock response\" });\n }\n callCount++;\n return resp;\n }\n\n if (response !== undefined) {\n return response;\n }\n\n // Default response\n return JSON.stringify({ score: 0.5, reasoning: \"Mock response\" });\n };\n\n return {\n async complete(prompt: string): Promise<string> {\n // Call validation hook if provided\n if (onPrompt) {\n onPrompt(prompt);\n }\n\n // Simulate delay\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n\n // Simulate error\n if (shouldError) {\n throw new Error(errorMessage);\n }\n\n // Return response\n const resp = getResponse();\n return typeof resp === \"string\" ? resp : JSON.stringify(resp);\n },\n\n async completeStructured<T>(prompt: string, _schema: JSONSchema): Promise<T> {\n // Call validation hook if provided\n if (onPrompt) {\n onPrompt(prompt);\n }\n\n // Simulate delay\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n\n // Simulate error\n if (shouldError) {\n throw new Error(errorMessage);\n }\n\n // Return response as object\n const resp = getResponse();\n if (typeof resp === \"string\") {\n try {\n return JSON.parse(resp) as T;\n } catch {\n throw new Error(`Mock response is not valid JSON: ${resp}`);\n }\n }\n\n return resp as T;\n },\n };\n}\n\n/**\n * Creates a mock client that returns sequential responses\n *\n * Useful for testing multiple calls with different responses.\n *\n * @example\n * ```ts\n * const mock = createSequentialMockClient([\n * { score: 0.2, reasoning: \"First call\" },\n * { score: 0.8, reasoning: \"Second call\" }\n * ]);\n * ```\n */\nexport function createSequentialMockClient(\n responses: Array<string | Record<string, unknown>>,\n options: { delay?: number } = {}\n): LLMClient {\n return createMockLLMClient({\n responses,\n delay: options.delay,\n });\n}\n\n/**\n * Creates a mock client that always errors\n *\n * Useful for testing error handling.\n */\nexport function createErrorMockClient(errorMessage = \"Mock LLM error\"): LLMClient {\n return createMockLLMClient({\n shouldError: true,\n errorMessage,\n });\n}\n\n/**\n * Creates a spy mock client that records all prompts\n *\n * Useful for testing what prompts are being sent to the LLM.\n *\n * @example\n * ```ts\n * const { client, prompts } = createSpyMockClient({ score: 0.5 });\n * await metric({ outputs, context, llmClient: client });\n * console.log(prompts); // See all prompts that were sent\n * ```\n */\nexport function createSpyMockClient(\n response: string | Record<string, unknown>\n): { client: LLMClient; prompts: string[] } {\n const prompts: string[] = [];\n\n const client = createMockLLMClient({\n response,\n onPrompt: (prompt) => prompts.push(prompt),\n });\n\n return { client, prompts };\n}\n"]}
1
+ {"version":3,"sources":["../../src/metrics/custom/index.ts","../../src/metrics/utils/index.ts","../../src/metrics/llm/adapters/mock.ts","../../src/metrics/llm/adapters/openai.ts","../../src/metrics/llm/adapters/anthropic.ts","../../src/metrics/llm/adapters/openrouter.ts"],"names":["delay"],"mappings":";;;;;AASA,IAAM,aAAA,uBAAoB,GAAA,EAAsB;AAiBzC,SAAS,cAAA,CAAe,MAAc,EAAA,EAAoB;AAC/D,EAAA,IAAI,aAAA,CAAc,GAAA,CAAI,IAAI,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,QAAA,EAAW,IAAI,CAAA,uBAAA,CAAyB,CAAA;AAAA,EAC1D;AACA,EAAA,aAAA,CAAc,GAAA,CAAI,MAAM,EAAE,CAAA;AAC5B;AAKO,SAAS,UAAU,IAAA,EAAoC;AAC5D,EAAA,OAAO,aAAA,CAAc,IAAI,IAAI,CAAA;AAC/B;AAKA,eAAsB,SAAA,CAAU,MAAc,MAAA,EAA+C;AAC3F,EAAA,MAAM,EAAA,GAAK,aAAA,CAAc,GAAA,CAAI,IAAI,CAAA;AACjC,EAAA,IAAI,CAAC,EAAA,EAAI;AACP,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,QAAA,EAAW,IAAI,CAAA,mBAAA,CAAqB,CAAA;AAAA,EACtD;AACA,EAAA,OAAO,GAAG,MAAM,CAAA;AAClB;AAKO,SAAS,WAAA,GAAwB;AACtC,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,aAAA,CAAc,IAAA,EAAM,CAAA;AACxC;AAKO,SAAS,iBAAiB,IAAA,EAAuB;AACtD,EAAA,OAAO,aAAA,CAAc,OAAO,IAAI,CAAA;AAClC;AAKO,SAAS,YAAA,GAAqB;AACnC,EAAA,aAAA,CAAc,KAAA,EAAM;AACtB;AAKO,SAAS,mBAAA,CACd,IAAA,EACA,QAAA,EACA,OAAA,GAA0D,EAAC,EACjD;AACV,EAAA,MAAM,EAAE,UAAA,GAAa,CAAA,EAAG,YAAA,GAAe,GAAE,GAAI,OAAA;AAE7C,EAAA,OAAO,OAAO,MAAA,KAAkD;AAC9D,IAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM;AAC/B,MAAA,MAAM,QAAA,GAAW,SAAS,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,IAAA,CAAK,CAAA,CAAE,MAAM,CAAC,CAAA;AACtD,MAAA,OAAO;AAAA,QACL,IAAI,CAAA,CAAE,EAAA;AAAA,QACN,MAAA,EAAQ,IAAA;AAAA,QACR,KAAA,EAAO,WAAW,UAAA,GAAa,YAAA;AAAA,QAC/B,KAAA,EAAO,WAAW,UAAA,GAAa;AAAA,OACjC;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAA;AACF;AAKO,SAAS,mBAAA,CACd,IAAA,EACA,QAAA,EACA,OAAA,GAA2D,EAAC,EAClD;AACV,EAAA,MAAM,EAAE,aAAA,GAAgB,KAAA,EAAO,SAAA,GAAY,KAAI,GAAI,OAAA;AAEnD,EAAA,MAAM,kBAAA,GAAqB,gBAAgB,QAAA,GAAW,QAAA,CAAS,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,WAAA,EAAa,CAAA;AAEzF,EAAA,OAAO,OAAO,MAAA,KAAkD;AAC9D,IAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM;AAC/B,MAAA,MAAM,OAAO,aAAA,GAAgB,CAAA,CAAE,MAAA,GAAS,CAAA,CAAE,OAAO,WAAA,EAAY;AAC7D,MAAA,MAAM,OAAA,GAAU,mBAAmB,MAAA,CAAO,CAAC,MAAM,IAAA,CAAK,QAAA,CAAS,CAAC,CAAC,CAAA;AACjE,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,MAAA,GAAS,QAAA,CAAS,MAAA;AAExC,MAAA,OAAO;AAAA,QACL,IAAI,CAAA,CAAE,EAAA;AAAA,QACN,MAAA,EAAQ,IAAA;AAAA,QACR,KAAA;AAAA,QACA,KAAA,EAAO,KAAA,IAAS,SAAA,GAAY,UAAA,GAAa;AAAA,OAC3C;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAA;AACF;;;AChHO,SAAS,cAAA,CAAe,KAAA,EAAe,GAAA,GAAM,CAAA,EAAG,MAAM,CAAA,EAAW;AACtE,EAAA,MAAM,QAAQ,GAAA,GAAM,GAAA;AACpB,EAAA,IAAI,KAAA,KAAU,GAAG,OAAO,CAAA;AACxB,EAAA,OAAO,IAAA,CAAK,IAAI,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,EAAA,CAAI,KAAA,GAAQ,GAAA,IAAO,KAAK,CAAC,CAAA;AACvD;AAKO,SAAS,YAAA,CAAa,OAAe,UAAA,EAAsD;AAEhG,EAAA,MAAM,MAAA,GAAS,CAAC,GAAG,UAAU,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,GAAA,GAAM,CAAA,CAAE,GAAG,CAAA;AAE3D,EAAA,KAAA,MAAW,EAAE,KAAA,EAAO,GAAA,EAAI,IAAK,MAAA,EAAQ;AACnC,IAAA,IAAI,SAAS,GAAA,EAAK;AAChB,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,UAAA,CAAW,UAAA,CAAW,MAAA,GAAS,CAAC,GAAG,KAAA,IAAS,SAAA;AACrD;AAKO,SAAS,kBAAA,CACd,EAAA,EACA,MAAA,EACA,KAAA,EACA,eAAA,EACc;AACd,EAAA,MAAM,eAAA,GAAkB,eAAe,KAAK,CAAA;AAC5C,EAAA,MAAM,KAAA,GAAQ,kBACV,YAAA,CAAa,eAAA,EAAiB,eAAe,CAAA,GAC7C,eAAA,IAAmB,MACjB,MAAA,GACA,KAAA;AAEN,EAAA,OAAO;AAAA,IACL,EAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAA,EAAO,eAAA;AAAA,IACP;AAAA,GACF;AACF;AAKO,IAAM,iBAAA,GAAoB;AAAA,EAC/B,EAAE,KAAA,EAAO,MAAA,EAAQ,GAAA,EAAK,GAAA,EAAI;AAAA,EAC1B,EAAE,KAAA,EAAO,OAAA,EAAS,GAAA,EAAK,CAAA;AACzB;AAKO,IAAM,mBAAA,GAAsB;AAAA,EACjC,EAAE,KAAA,EAAO,MAAA,EAAQ,GAAA,EAAK,GAAA,EAAI;AAAA,EAC1B,EAAE,KAAA,EAAO,QAAA,EAAU,GAAA,EAAK,GAAA,EAAI;AAAA,EAC5B,EAAE,KAAA,EAAO,KAAA,EAAO,GAAA,EAAK,CAAA;AACvB;AAKO,SAAS,KAAA,CAAS,OAAY,IAAA,EAAqB;AACxD,EAAA,MAAM,UAAiB,EAAC;AACxB,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,EAAQ,KAAK,IAAA,EAAM;AAC3C,IAAA,OAAA,CAAQ,KAAK,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,CAAA,GAAI,IAAI,CAAC,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,OAAA;AACT;AAKO,SAAS,MAAM,EAAA,EAA2B;AAC/C,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACzD;;;AC5CO,SAAS,mBAAA,CAAoB,MAAA,GAAwB,EAAC,EAAc;AACzE,EAAA,MAAM;AAAA,IACJ,QAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAAA,MAAAA,GAAQ,CAAA;AAAA,IACR,WAAA,GAAc,KAAA;AAAA,IACd,YAAA,GAAe,gBAAA;AAAA,IACf;AAAA,GACF,GAAI,MAAA;AAEJ,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,MAAM,cAAc,MAAwC;AAC1D,IAAA,IAAI,SAAA,IAAa,SAAA,CAAU,MAAA,GAAS,CAAA,EAAG;AACrC,MAAA,MAAM,IAAA,GAAO,UAAU,IAAA,CAAK,GAAA,CAAI,WAAW,SAAA,CAAU,MAAA,GAAS,CAAC,CAAC,CAAA;AAChE,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA,OAAO,KAAK,SAAA,CAAU,EAAE,OAAO,GAAA,EAAK,SAAA,EAAW,iBAAiB,CAAA;AAAA,MAClE;AACA,MAAA,SAAA,EAAA;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,MAAA,OAAO,QAAA;AAAA,IACT;AAGA,IAAA,OAAO,KAAK,SAAA,CAAU,EAAE,OAAO,GAAA,EAAK,SAAA,EAAW,iBAAiB,CAAA;AAAA,EAClE,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,SAAS,MAAA,EAAiC;AAE9C,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,QAAA,CAAS,MAAM,CAAA;AAAA,MACjB;AAGA,MAAA,IAAIA,SAAQ,CAAA,EAAG;AACb,QAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAASA,MAAK,CAAC,CAAA;AAAA,MAC3D;AAGA,MAAA,IAAI,WAAA,EAAa;AACf,QAAA,MAAM,IAAI,MAAM,YAAY,CAAA;AAAA,MAC9B;AAGA,MAAA,MAAM,OAAO,WAAA,EAAY;AACzB,MAAA,OAAO,OAAO,IAAA,KAAS,QAAA,GAAW,IAAA,GAAO,IAAA,CAAK,UAAU,IAAI,CAAA;AAAA,IAC9D,CAAA;AAAA,IAEA,MAAM,kBAAA,CAAsB,MAAA,EAAgB,OAAA,EAAiC;AAE3E,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,QAAA,CAAS,MAAM,CAAA;AAAA,MACjB;AAGA,MAAA,IAAIA,SAAQ,CAAA,EAAG;AACb,QAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAASA,MAAK,CAAC,CAAA;AAAA,MAC3D;AAGA,MAAA,IAAI,WAAA,EAAa;AACf,QAAA,MAAM,IAAI,MAAM,YAAY,CAAA;AAAA,MAC9B;AAGA,MAAA,MAAM,OAAO,WAAA,EAAY;AACzB,MAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC5B,QAAA,IAAI;AACF,UAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,QACxB,CAAA,CAAA,MAAQ;AACN,UAAA,MAAM,IAAI,KAAA,CAAM,CAAA,iCAAA,EAAoC,IAAI,CAAA,CAAE,CAAA;AAAA,QAC5D;AAAA,MACF;AAEA,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,GACF;AACF;AAeO,SAAS,0BAAA,CACd,SAAA,EACA,OAAA,GAA8B,EAAC,EACpB;AACX,EAAA,OAAO,mBAAA,CAAoB;AAAA,IACzB,SAAA;AAAA,IACA,OAAO,OAAA,CAAQ;AAAA,GAChB,CAAA;AACH;AAOO,SAAS,qBAAA,CAAsB,eAAe,gBAAA,EAA6B;AAChF,EAAA,OAAO,mBAAA,CAAoB;AAAA,IACzB,WAAA,EAAa,IAAA;AAAA,IACb;AAAA,GACD,CAAA;AACH;AAcO,SAAS,oBAAoB,QAAA,EAGlC;AACA,EAAA,MAAM,UAAoB,EAAC;AAE3B,EAAA,MAAM,SAAS,mBAAA,CAAoB;AAAA,IACjC,QAAA;AAAA,IACA,QAAA,EAAU,CAAC,MAAA,KAAW,OAAA,CAAQ,KAAK,MAAM;AAAA,GAC1C,CAAA;AAED,EAAA,OAAO,EAAE,QAAQ,OAAA,EAAQ;AAC3B;;;AC/FO,SAAS,mBAAA,CAAoB,MAAA,EAAgB,OAAA,GAAgC,EAAC,EAAc;AACjG,EAAA,MAAM;AAAA,IACJ,KAAA,GAAQ,qBAAA;AAAA,IACR,WAAA,GAAc,CAAA;AAAA,IACd,SAAA,GAAY,IAAA;AAAA,IACZ,OAAA;AAAA,IACA,YAAA;AAAA,IACA,OAAA,GAAU;AAAA,GACZ,GAAI,OAAA;AAGJ,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAGA,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,YAAA;AAEJ,EAAA,SAAS,YAAA,GAAe;AACtB,IAAA,IAAI,cAAc,OAAO,YAAA;AAEzB,IAAA,IAAI;AAEF,MAAA,MAAA,GAAS,SAAA,CAAQ,QAAQ,CAAA,CAAE,OAAA,IAAW,UAAQ,QAAQ,CAAA;AAAA,IACxD,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OAEF;AAAA,IACF;AAEA,IAAA,YAAA,GAAe,IAAI,MAAA,CAAO;AAAA,MACxB,MAAA;AAAA,MACA,OAAA;AAAA,MACA,YAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,OAAO,YAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,SAAS,MAAA,EAAiC;AAC9C,MAAA,MAAM,SAAS,YAAA,EAAa;AAE5B,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,IAAA,CAAK,YAAY,MAAA,CAAO;AAAA,UACpD,KAAA;AAAA,UACA,UAAU,CAAC,EAAE,MAAM,MAAA,EAAQ,OAAA,EAAS,QAAQ,CAAA;AAAA,UAC5C,WAAA;AAAA,UACA,UAAA,EAAY;AAAA,SACb,CAAA;AAED,QAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,CAAC,CAAA,EAAG,SAAS,OAAA,IAAW,EAAA;AAAA,MAClD,SAAS,KAAA,EAAY;AAEnB,QAAA,MAAM,eAAe,KAAA,EAAO,OAAA,IAAW,OAAO,KAAA,EAAO,OAAA,IAAW,OAAO,KAAK,CAAA;AAC5E,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,yBAAA,EAA4B,KAAK,CAAA,GAAA,EAAM,YAAY;AAAA,yEAAA;AAAA,SAErD;AAAA,MACF;AAAA,IACF,CAAA;AAAA,IAEA,MAAM,kBAAA,CAAsB,MAAA,EAAgB,OAAA,EAAiC;AAC3E,MAAA,MAAM,SAAS,YAAA,EAAa;AAE5B,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,IAAA,CAAK,YAAY,MAAA,CAAO;AAAA,UACpD,KAAA;AAAA,UACA,UAAU,CAAC,EAAE,MAAM,MAAA,EAAQ,OAAA,EAAS,QAAQ,CAAA;AAAA,UAC5C,eAAA,EAAiB,EAAE,IAAA,EAAM,aAAA,EAAc;AAAA,UACvC,WAAA;AAAA,UACA,UAAA,EAAY;AAAA,SACb,CAAA;AAED,QAAA,MAAM,OAAO,QAAA,CAAS,OAAA,CAAQ,CAAC,CAAA,EAAG,SAAS,OAAA,IAAW,IAAA;AACtD,QAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,MACxB,SAAS,KAAA,EAAY;AACnB,QAAA,MAAM,eAAe,KAAA,EAAO,OAAA,IAAW,OAAO,KAAA,EAAO,OAAA,IAAW,OAAO,KAAK,CAAA;AAC5E,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,yBAAA,EAA4B,KAAK,CAAA,GAAA,EAAM,YAAY;AAAA,yEAAA;AAAA,SAErD;AAAA,MACF;AAAA,IACF;AAAA,GACF;AACF;;;ACzGO,SAAS,sBAAA,CACd,MAAA,EACA,OAAA,GAAmC,EAAC,EACzB;AACX,EAAA,MAAM;AAAA,IACJ,KAAA,GAAQ,4BAAA;AAAA,IACR,SAAA,GAAY,IAAA;AAAA,IACZ,WAAA,GAAc,CAAA;AAAA,IACd,OAAA,GAAU;AAAA,GACZ,GAAI,OAAA;AAGJ,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAGA,EAAA,IAAI,WAAA,GAAc,CAAA,IAAK,WAAA,GAAc,CAAA,EAAG;AACtC,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mDAAA,EAAsD,WAAW,CAAA,CAAE,CAAA;AAAA,EACrF;AAGA,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,eAAA;AAEJ,EAAA,SAAS,YAAA,GAAe;AACtB,IAAA,IAAI,iBAAiB,OAAO,eAAA;AAE5B,IAAA,IAAI;AAEF,MAAA,SAAA,GAAY,SAAA,CAAQ,mBAAmB,CAAA,CAAE,OAAA,IAAW,UAAQ,mBAAmB,CAAA;AAAA,IACjF,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OAEF;AAAA,IACF;AAEA,IAAA,eAAA,GAAkB,IAAI,SAAA,CAAU;AAAA,MAC9B,MAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,OAAO,eAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,SAAS,MAAA,EAAiC;AAC9C,MAAA,MAAM,SAAS,YAAA,EAAa;AAE5B,MAAA,IAAI;AACF,QAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,QAAA,CAAS,MAAA,CAAO;AAAA,UAC3C,KAAA;AAAA,UACA,UAAA,EAAY,SAAA;AAAA,UACZ,WAAA;AAAA,UACA,UAAU,CAAC,EAAE,MAAM,MAAA,EAAQ,OAAA,EAAS,QAAQ;AAAA,SAC7C,CAAA;AAGD,QAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA;AACpC,QAAA,OAAO,UAAA,EAAY,IAAA,KAAS,MAAA,GAAS,UAAA,CAAW,IAAA,GAAO,EAAA;AAAA,MACzD,SAAS,KAAA,EAAY;AACnB,QAAA,MAAM,eAAe,KAAA,EAAO,OAAA,IAAW,OAAO,KAAA,EAAO,OAAA,IAAW,OAAO,KAAK,CAAA;AAC5E,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,4BAAA,EAA+B,KAAK,CAAA,GAAA,EAAM,YAAY;AAAA,8DAAA;AAAA,SAExD;AAAA,MACF;AAAA,IACF,CAAA;AAAA,IAEA,MAAM,kBAAA,CAAsB,MAAA,EAAgB,MAAA,EAAgC;AAG1E,MAAA,MAAM,aACJ,MAAA,GACA;;AAAA,uGAAA,EACoC,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA,CAAA;AAE5D,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,CAAS,UAAU,CAAA;AAE/C,MAAA,IAAI;AACF,QAAA,OAAO,kBAAqB,QAAQ,CAAA;AAAA,MACtC,SAAS,KAAA,EAAY;AACnB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,4CAAA,EAA+C,MAAM,OAAO;AAAA,kBAAA,EACrC,QAAA,CAAS,SAAA,CAAU,CAAA,EAAG,GAAG,CAAC,CAAA,GAAA;AAAA,SACnD;AAAA,MACF;AAAA,IACF;AAAA,GACF;AACF;;;AClEO,SAAS,uBAAA,CACd,MAAA,EACA,OAAA,GAAoC,EAAC,EAC1B;AACX,EAAA,MAAM;AAAA,IACJ,KAAA,GAAQ,6BAAA;AAAA,IACR,WAAA,GAAc,CAAA;AAAA,IACd,SAAA,GAAY,IAAA;AAAA,IACZ,OAAA,GAAU,WAAA;AAAA,IACV,OAAA;AAAA,IACA,OAAA,GAAU;AAAA,GACZ,GAAI,OAAA;AAGJ,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,MAAM,uEAA4E,CAAA;AAAA,EAC9F;AAEA,EAAA,MAAM,OAAA,GAAU,8BAAA;AAEhB,EAAA,eAAe,OAAA,CACb,QAAA,EACA,QAAA,GAAoB,KAAA,EACH;AACjB,IAAA,MAAM,OAAA,GAAkC;AAAA,MACtC,aAAA,EAAe,UAAU,MAAM,CAAA,CAAA;AAAA,MAC/B,cAAA,EAAgB,kBAAA;AAAA,MAChB,gBAAgB,OAAA,IAAW,wCAAA;AAAA,MAC3B,SAAA,EAAW;AAAA,KACb;AAEA,IAAA,MAAM,IAAA,GAAY;AAAA,MAChB,KAAA;AAAA,MACA,QAAA;AAAA,MACA,WAAA;AAAA,MACA,UAAA,EAAY;AAAA,KACd;AAGA,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,IAAA,CAAK,eAAA,GAAkB,EAAE,IAAA,EAAM,aAAA,EAAc;AAAA,IAC/C;AAEA,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,YAAY,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,OAAO,CAAA;AAE9D,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,CAAA,EAAG,OAAO,CAAA,iBAAA,CAAA,EAAqB;AAAA,QAC1D,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA;AAAA,QACA,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAAA,QACzB,QAAQ,UAAA,CAAW;AAAA,OACpB,CAAA;AAED,MAAA,YAAA,CAAa,SAAS,CAAA;AAEtB,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,MAAM,SAAA,GAAiB,MAAM,QAAA,CAAS,IAAA,GAAO,KAAA,CAAM,OAAO,EAAC,CAAE,CAAA;AAC7D,QAAA,MAAM,YAAA,GAAe,SAAA,CAAU,KAAA,EAAO,OAAA,IAAW,SAAS,UAAA,IAAc,eAAA;AACxE,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,SAAS,MAAM,CAAA,GAAA,EAAM,YAAY,CAAA,CAAE,CAAA;AAAA,MAC9E;AAEA,MAAA,MAAM,IAAA,GAAY,MAAM,QAAA,CAAS,IAAA,EAAK;AACtC,MAAA,OAAO,IAAA,CAAK,OAAA,GAAU,CAAC,CAAA,EAAG,SAAS,OAAA,IAAW,EAAA;AAAA,IAChD,SAAS,KAAA,EAAY;AACnB,MAAA,YAAA,CAAa,SAAS,CAAA;AAEtB,MAAA,IAAI,KAAA,CAAM,SAAS,YAAA,EAAc;AAC/B,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mCAAA,EAAsC,OAAO,CAAA,WAAA,EAAc,KAAK,CAAA,CAAA,CAAG,CAAA;AAAA,MACrF;AAEA,MAAA,MAAM,YAAA,GAAe,KAAA,EAAO,OAAA,IAAW,MAAA,CAAO,KAAK,CAAA;AACnD,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,6BAAA,EAAgC,KAAK,CAAA,GAAA,EAAM,YAAY;AAAA,gEAAA;AAAA,OAEzD;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,SAAS,MAAA,EAAiC;AAC9C,MAAA,OAAO,OAAA,CAAQ,CAAC,EAAE,IAAA,EAAM,QAAQ,OAAA,EAAS,MAAA,EAAQ,CAAA,EAAG,KAAK,CAAA;AAAA,IAC3D,CAAA;AAAA,IAEA,MAAM,kBAAA,CAAsB,MAAA,EAAgB,MAAA,EAAgC;AAE1E,MAAA,IAAI,QAAA;AACJ,MAAA,IAAI;AACF,QAAA,QAAA,GAAW,MAAM,OAAA;AAAA,UACf,CAAC,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,QAAQ,CAAA;AAAA,UAClC;AAAA;AAAA,SACF;AAAA,MACF,SAAS,KAAA,EAAO;AAEd,QAAA,MAAM,aACJ,MAAA,GACA;;AAAA,uGAAA,EACoC,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA,CAAA;AAC5D,QAAA,QAAA,GAAW,MAAM,OAAA,CAAQ,CAAC,EAAE,IAAA,EAAM,QAAQ,OAAA,EAAS,UAAA,EAAY,CAAA,EAAG,KAAK,CAAA;AAAA,MACzE;AAEA,MAAA,IAAI;AACF,QAAA,OAAO,kBAAqB,QAAQ,CAAA;AAAA,MACtC,SAAS,KAAA,EAAY;AACnB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,6CAAA,EAAgD,MAAM,OAAO;AAAA,OAAA,EACjD,KAAK;AAAA,kBAAA,EACM,QAAA,CAAS,SAAA,CAAU,CAAA,EAAG,GAAG,CAAC,CAAA,GAAA;AAAA,SACnD;AAAA,MACF;AAAA,IACF;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * Custom metric registration\n */\n\nimport type { MetricFn, MetricOutput, MetricConfig } from \"../../core/types.js\";\n\n/**\n * Registry of custom metrics\n */\nconst customMetrics = new Map<string, MetricFn>();\n\n/**\n * Registers a custom metric\n *\n * @example\n * ```ts\n * registerMetric(\"custom-relevance\", async ({ outputs, query }) => {\n * // Custom evaluation logic\n * return outputs.map(o => ({\n * id: o.id,\n * metric: \"custom-relevance\",\n * score: evaluateRelevance(o.output, query),\n * }));\n * });\n * ```\n */\nexport function registerMetric(name: string, fn: MetricFn): void {\n if (customMetrics.has(name)) {\n throw new Error(`Metric \"${name}\" is already registered`);\n }\n customMetrics.set(name, fn);\n}\n\n/**\n * Gets a registered custom metric\n */\nexport function getMetric(name: string): MetricFn | undefined {\n return customMetrics.get(name);\n}\n\n/**\n * Runs a registered metric\n */\nexport async function runMetric(name: string, config: MetricConfig): Promise<MetricOutput[]> {\n const fn = customMetrics.get(name);\n if (!fn) {\n throw new Error(`Metric \"${name}\" is not registered`);\n }\n return fn(config);\n}\n\n/**\n * Lists all registered custom metrics\n */\nexport function listMetrics(): string[] {\n return Array.from(customMetrics.keys());\n}\n\n/**\n * Unregisters a metric (mainly for testing)\n */\nexport function unregisterMetric(name: string): boolean {\n return customMetrics.delete(name);\n}\n\n/**\n * Clears all registered metrics (mainly for testing)\n */\nexport function clearMetrics(): void {\n customMetrics.clear();\n}\n\n/**\n * Creates a simple string-matching metric\n */\nexport function createPatternMetric(\n name: string,\n patterns: RegExp[],\n options: { matchScore?: number; noMatchScore?: number } = {}\n): MetricFn {\n const { matchScore = 1, noMatchScore = 0 } = options;\n\n return async (config: MetricConfig): Promise<MetricOutput[]> => {\n return config.outputs.map((o) => {\n const hasMatch = patterns.some((p) => p.test(o.output));\n return {\n id: o.id,\n metric: name,\n score: hasMatch ? matchScore : noMatchScore,\n label: hasMatch ? \"detected\" : \"not_detected\",\n };\n });\n };\n}\n\n/**\n * Creates a keyword-based metric\n */\nexport function createKeywordMetric(\n name: string,\n keywords: string[],\n options: { caseSensitive?: boolean; threshold?: number } = {}\n): MetricFn {\n const { caseSensitive = false, threshold = 0.5 } = options;\n\n const normalizedKeywords = caseSensitive ? keywords : keywords.map((k) => k.toLowerCase());\n\n return async (config: MetricConfig): Promise<MetricOutput[]> => {\n return config.outputs.map((o) => {\n const text = caseSensitive ? o.output : o.output.toLowerCase();\n const matches = normalizedKeywords.filter((k) => text.includes(k));\n const score = matches.length / keywords.length;\n\n return {\n id: o.id,\n metric: name,\n score,\n label: score >= threshold ? \"detected\" : \"not_detected\",\n };\n });\n };\n}\n","/**\n * Metric utilities\n */\n\nimport type { MetricOutput } from \"../../core/types.js\";\n\n/**\n * Normalizes a score to 0-1 range\n */\nexport function normalizeScore(score: number, min = 0, max = 1): number {\n const range = max - min;\n if (range === 0) return 0;\n return Math.max(0, Math.min(1, (score - min) / range));\n}\n\n/**\n * Converts a numeric score to a label based on thresholds\n */\nexport function scoreToLabel(score: number, thresholds: { label: string; min: number }[]): string {\n // Sort thresholds by min descending\n const sorted = [...thresholds].sort((a, b) => b.min - a.min);\n\n for (const { label, min } of sorted) {\n if (score >= min) {\n return label;\n }\n }\n\n return thresholds[thresholds.length - 1]?.label ?? \"unknown\";\n}\n\n/**\n * Creates a metric output from a score\n */\nexport function createMetricOutput(\n id: string,\n metric: string,\n score: number,\n labelThresholds?: { label: string; min: number }[]\n): MetricOutput {\n const normalizedScore = normalizeScore(score);\n const label = labelThresholds\n ? scoreToLabel(normalizedScore, labelThresholds)\n : normalizedScore >= 0.5\n ? \"high\"\n : \"low\";\n\n return {\n id,\n metric,\n score: normalizedScore,\n label,\n };\n}\n\n/**\n * Default thresholds for binary metrics\n */\nexport const BINARY_THRESHOLDS = [\n { label: \"true\", min: 0.5 },\n { label: \"false\", min: 0 },\n];\n\n/**\n * Default thresholds for severity metrics\n */\nexport const SEVERITY_THRESHOLDS = [\n { label: \"high\", min: 0.7 },\n { label: \"medium\", min: 0.4 },\n { label: \"low\", min: 0 },\n];\n\n/**\n * Batches items for parallel processing\n */\nexport function batch<T>(items: T[], size: number): T[][] {\n const batches: T[][] = [];\n for (let i = 0; i < items.length; i += size) {\n batches.push(items.slice(i, i + size));\n }\n return batches;\n}\n\n/**\n * Delays execution\n */\nexport function delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Mock LLM client for testing\n *\n * Provides a configurable mock implementation of LLMClient for unit tests.\n */\n\nimport type { LLMClient, JSONSchema } from \"../../../core/types.js\";\n\n/**\n * Configuration for mock LLM client\n */\nexport interface MockLLMConfig {\n /** Fixed response to return (can be string or object for JSON mode) */\n response?: string | Record<string, unknown>;\n\n /** Multiple responses for sequential calls */\n responses?: Array<string | Record<string, unknown>>;\n\n /** Delay in milliseconds before responding */\n delay?: number;\n\n /** Whether to throw an error */\n shouldError?: boolean;\n\n /** Error message to throw */\n errorMessage?: string;\n\n /** Function to validate prompts */\n onPrompt?: (prompt: string) => void;\n}\n\n/**\n * Creates a mock LLM client for testing\n *\n * @example\n * ```ts\n * const mock = createMockLLMClient({\n * response: JSON.stringify({ score: 0.8, reasoning: \"test\" }),\n * delay: 100\n * });\n *\n * setLLMClient(mock);\n * ```\n */\nexport function createMockLLMClient(config: MockLLMConfig = {}): LLMClient {\n const {\n response,\n responses,\n delay = 0,\n shouldError = false,\n errorMessage = \"Mock LLM error\",\n onPrompt,\n } = config;\n\n let callCount = 0;\n\n const getResponse = (): string | Record<string, unknown> => {\n if (responses && responses.length > 0) {\n const resp = responses[Math.min(callCount, responses.length - 1)];\n if (!resp) {\n return JSON.stringify({ score: 0.5, reasoning: \"Mock response\" });\n }\n callCount++;\n return resp;\n }\n\n if (response !== undefined) {\n return response;\n }\n\n // Default response\n return JSON.stringify({ score: 0.5, reasoning: \"Mock response\" });\n };\n\n return {\n async complete(prompt: string): Promise<string> {\n // Call validation hook if provided\n if (onPrompt) {\n onPrompt(prompt);\n }\n\n // Simulate delay\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n\n // Simulate error\n if (shouldError) {\n throw new Error(errorMessage);\n }\n\n // Return response\n const resp = getResponse();\n return typeof resp === \"string\" ? resp : JSON.stringify(resp);\n },\n\n async completeStructured<T>(prompt: string, _schema: JSONSchema): Promise<T> {\n // Call validation hook if provided\n if (onPrompt) {\n onPrompt(prompt);\n }\n\n // Simulate delay\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n\n // Simulate error\n if (shouldError) {\n throw new Error(errorMessage);\n }\n\n // Return response as object\n const resp = getResponse();\n if (typeof resp === \"string\") {\n try {\n return JSON.parse(resp) as T;\n } catch {\n throw new Error(`Mock response is not valid JSON: ${resp}`);\n }\n }\n\n return resp as T;\n },\n };\n}\n\n/**\n * Creates a mock client that returns sequential responses\n *\n * Useful for testing multiple calls with different responses.\n *\n * @example\n * ```ts\n * const mock = createSequentialMockClient([\n * { score: 0.2, reasoning: \"First call\" },\n * { score: 0.8, reasoning: \"Second call\" }\n * ]);\n * ```\n */\nexport function createSequentialMockClient(\n responses: Array<string | Record<string, unknown>>,\n options: { delay?: number } = {}\n): LLMClient {\n return createMockLLMClient({\n responses,\n delay: options.delay,\n });\n}\n\n/**\n * Creates a mock client that always errors\n *\n * Useful for testing error handling.\n */\nexport function createErrorMockClient(errorMessage = \"Mock LLM error\"): LLMClient {\n return createMockLLMClient({\n shouldError: true,\n errorMessage,\n });\n}\n\n/**\n * Creates a spy mock client that records all prompts\n *\n * Useful for testing what prompts are being sent to the LLM.\n *\n * @example\n * ```ts\n * const { client, prompts } = createSpyMockClient({ score: 0.5 });\n * await metric({ outputs, context, llmClient: client });\n * console.log(prompts); // See all prompts that were sent\n * ```\n */\nexport function createSpyMockClient(response: string | Record<string, unknown>): {\n client: LLMClient;\n prompts: string[];\n} {\n const prompts: string[] = [];\n\n const client = createMockLLMClient({\n response,\n onPrompt: (prompt) => prompts.push(prompt),\n });\n\n return { client, prompts };\n}\n","/**\n * Built-in OpenAI adapter for evalsense\n *\n * Provides a simple way to use OpenAI models without writing adapter code.\n *\n * @example\n * ```javascript\n * import { setLLMClient, createOpenAIAdapter } from 'evalsense/metrics';\n *\n * setLLMClient(createOpenAIAdapter(process.env.OPENAI_API_KEY, {\n * model: 'gpt-4-turbo-preview',\n * temperature: 0\n * }));\n * ```\n */\n\nimport type { LLMClient, JSONSchema } from \"../../../core/types.js\";\n\nexport interface OpenAIAdapterOptions {\n /**\n * OpenAI model to use\n * @default \"gpt-4-turbo-preview\"\n */\n model?: string;\n\n /**\n * Temperature for generation (0-2)\n * @default 0\n */\n temperature?: number;\n\n /**\n * Maximum tokens per completion\n * @default 4096\n */\n maxTokens?: number;\n\n /**\n * API base URL (for Azure OpenAI or proxies)\n * @default undefined (uses default OpenAI endpoint)\n */\n baseURL?: string;\n\n /**\n * Organization ID (optional)\n */\n organization?: string;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n}\n\n/**\n * Creates an LLM client adapter for OpenAI.\n *\n * **Setup:**\n * 1. Install OpenAI SDK: `npm install openai`\n * 2. Get API key from https://platform.openai.com/api-keys\n * 3. Set environment variable: `export OPENAI_API_KEY=\"sk-...\"`\n *\n * **Model Options:**\n * - `gpt-4-turbo-preview` - Most capable, expensive\n * - `gpt-4` - High quality, expensive\n * - `gpt-3.5-turbo` - Fast and cheap (20x cheaper than GPT-4)\n *\n * @param apiKey - Your OpenAI API key\n * @param options - Configuration options\n * @returns LLM client for use with evalsense metrics\n *\n * @example\n * ```javascript\n * // Basic usage\n * const client = createOpenAIAdapter(process.env.OPENAI_API_KEY);\n * setLLMClient(client);\n *\n * // With custom model\n * const client = createOpenAIAdapter(process.env.OPENAI_API_KEY, {\n * model: 'gpt-3.5-turbo', // Cheaper model\n * temperature: 0.3\n * });\n *\n * // With Azure OpenAI\n * const client = createOpenAIAdapter(process.env.AZURE_OPENAI_KEY, {\n * baseURL: 'https://your-resource.openai.azure.com',\n * model: 'gpt-4'\n * });\n * ```\n */\nexport function createOpenAIAdapter(apiKey: string, options: OpenAIAdapterOptions = {}): LLMClient {\n const {\n model = \"gpt-4-turbo-preview\",\n temperature = 0,\n maxTokens = 4096,\n baseURL,\n organization,\n timeout = 30000,\n } = options;\n\n // Validate API key\n if (!apiKey) {\n throw new Error(\n \"OpenAI API key is required. \" + \"Get one at https://platform.openai.com/api-keys\"\n );\n }\n\n // Lazy-load OpenAI SDK (peer dependency)\n let OpenAI: any;\n let openaiClient: any;\n\n function ensureClient() {\n if (openaiClient) return openaiClient;\n\n try {\n // Try ESM import first\n OpenAI = require(\"openai\").default || require(\"openai\");\n } catch (error) {\n throw new Error(\n \"OpenAI SDK not found. Install it with: npm install openai\\n\" +\n \"Visit https://github.com/openai/openai-node for documentation.\"\n );\n }\n\n openaiClient = new OpenAI({\n apiKey,\n baseURL,\n organization,\n timeout,\n });\n\n return openaiClient;\n }\n\n return {\n async complete(prompt: string): Promise<string> {\n const client = ensureClient();\n\n try {\n const response = await client.chat.completions.create({\n model,\n messages: [{ role: \"user\", content: prompt }],\n temperature,\n max_tokens: maxTokens,\n });\n\n return response.choices[0]?.message?.content ?? \"\";\n } catch (error: any) {\n // Enhance error message with context\n const errorMessage = error?.message || error?.error?.message || String(error);\n throw new Error(\n `OpenAI API error (model: ${model}): ${errorMessage}\\n` +\n `Check your API key and quota at https://platform.openai.com/account/usage`\n );\n }\n },\n\n async completeStructured<T>(prompt: string, _schema: JSONSchema): Promise<T> {\n const client = ensureClient();\n\n try {\n const response = await client.chat.completions.create({\n model,\n messages: [{ role: \"user\", content: prompt }],\n response_format: { type: \"json_object\" },\n temperature,\n max_tokens: maxTokens,\n });\n\n const text = response.choices[0]?.message?.content ?? \"{}\";\n return JSON.parse(text) as T;\n } catch (error: any) {\n const errorMessage = error?.message || error?.error?.message || String(error);\n throw new Error(\n `OpenAI API error (model: ${model}): ${errorMessage}\\n` +\n `Check your API key and quota at https://platform.openai.com/account/usage`\n );\n }\n },\n };\n}\n","/**\n * Built-in Anthropic (Claude) adapter for evalsense\n *\n * Provides a simple way to use Claude models without writing adapter code.\n *\n * @example\n * ```javascript\n * import { setLLMClient, createAnthropicAdapter } from 'evalsense/metrics';\n *\n * setLLMClient(createAnthropicAdapter(process.env.ANTHROPIC_API_KEY, {\n * model: 'claude-3-5-sonnet-20241022'\n * }));\n * ```\n */\n\nimport type { LLMClient, JSONSchema } from \"../../../core/types.js\";\nimport { parseJSONResponse } from \"../utils.js\";\n\nexport interface AnthropicAdapterOptions {\n /**\n * Anthropic model to use\n * @default \"claude-3-5-sonnet-20241022\"\n */\n model?: string;\n\n /**\n * Maximum tokens per completion\n * @default 4096\n */\n maxTokens?: number;\n\n /**\n * Temperature for generation (0-1)\n * Note: Anthropic doesn't support temperature > 1\n * @default 0\n */\n temperature?: number;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n}\n\n/**\n * Creates an LLM client adapter for Anthropic Claude.\n *\n * **Setup:**\n * 1. Install Anthropic SDK: `npm install @anthropic-ai/sdk`\n * 2. Get API key from https://console.anthropic.com/\n * 3. Set environment variable: `export ANTHROPIC_API_KEY=\"sk-ant-...\"`\n *\n * **Model Options:**\n * - `claude-3-5-sonnet-20241022` - Latest, most capable (recommended)\n * - `claude-3-opus-20240229` - Most capable, expensive\n * - `claude-3-sonnet-20240229` - Balanced performance\n * - `claude-3-haiku-20240307` - Fast and affordable\n *\n * @param apiKey - Your Anthropic API key\n * @param options - Configuration options\n * @returns LLM client for use with evalsense metrics\n *\n * @example\n * ```javascript\n * // Basic usage\n * const client = createAnthropicAdapter(process.env.ANTHROPIC_API_KEY);\n * setLLMClient(client);\n *\n * // With custom model\n * const client = createAnthropicAdapter(process.env.ANTHROPIC_API_KEY, {\n * model: 'claude-3-haiku-20240307', // Cheaper, faster model\n * maxTokens: 2048\n * });\n * ```\n */\nexport function createAnthropicAdapter(\n apiKey: string,\n options: AnthropicAdapterOptions = {}\n): LLMClient {\n const {\n model = \"claude-3-5-sonnet-20241022\",\n maxTokens = 4096,\n temperature = 0,\n timeout = 30000,\n } = options;\n\n // Validate API key\n if (!apiKey) {\n throw new Error(\n \"Anthropic API key is required. \" + \"Get one at https://console.anthropic.com/\"\n );\n }\n\n // Validate temperature (Anthropic only supports 0-1)\n if (temperature < 0 || temperature > 1) {\n throw new Error(`Anthropic temperature must be between 0 and 1, got ${temperature}`);\n }\n\n // Lazy-load Anthropic SDK (peer dependency)\n let Anthropic: any;\n let anthropicClient: any;\n\n function ensureClient() {\n if (anthropicClient) return anthropicClient;\n\n try {\n // Try ESM import first\n Anthropic = require(\"@anthropic-ai/sdk\").default || require(\"@anthropic-ai/sdk\");\n } catch (error) {\n throw new Error(\n \"Anthropic SDK not found. Install it with: npm install @anthropic-ai/sdk\\n\" +\n \"Visit https://github.com/anthropics/anthropic-sdk-typescript for documentation.\"\n );\n }\n\n anthropicClient = new Anthropic({\n apiKey,\n timeout,\n });\n\n return anthropicClient;\n }\n\n return {\n async complete(prompt: string): Promise<string> {\n const client = ensureClient();\n\n try {\n const message = await client.messages.create({\n model,\n max_tokens: maxTokens,\n temperature,\n messages: [{ role: \"user\", content: prompt }],\n });\n\n // Extract text from first content block\n const firstBlock = message.content[0];\n return firstBlock?.type === \"text\" ? firstBlock.text : \"\";\n } catch (error: any) {\n const errorMessage = error?.message || error?.error?.message || String(error);\n throw new Error(\n `Anthropic API error (model: ${model}): ${errorMessage}\\n` +\n `Check your API key and usage at https://console.anthropic.com/`\n );\n }\n },\n\n async completeStructured<T>(prompt: string, schema: JSONSchema): Promise<T> {\n // Note: Anthropic doesn't have built-in JSON mode yet\n // We parse JSON from text response\n const jsonPrompt =\n prompt +\n \"\\n\\nIMPORTANT: Respond with valid JSON only. No markdown, no explanation. \" +\n `The JSON must match this schema: ${JSON.stringify(schema)}`;\n\n const response = await this.complete(jsonPrompt);\n\n try {\n return parseJSONResponse<T>(response);\n } catch (error: any) {\n throw new Error(\n `Failed to parse Anthropic response as JSON: ${error.message}\\n` +\n `Response preview: ${response.substring(0, 200)}...`\n );\n }\n },\n };\n}\n","/**\n * Built-in OpenRouter adapter for evalsense\n *\n * OpenRouter provides access to multiple LLM providers (OpenAI, Anthropic, Google, Meta, etc.)\n * through a single unified API. Great for comparing models or avoiding vendor lock-in.\n *\n * @example\n * ```javascript\n * import { setLLMClient, createOpenRouterAdapter } from 'evalsense/metrics';\n *\n * setLLMClient(createOpenRouterAdapter(process.env.OPENROUTER_API_KEY, {\n * model: 'anthropic/claude-3.5-sonnet'\n * }));\n * ```\n */\n\nimport type { LLMClient, JSONSchema } from \"../../../core/types.js\";\nimport { parseJSONResponse } from \"../utils.js\";\n\nexport interface OpenRouterAdapterOptions {\n /**\n * Model to use (in format: provider/model-name)\n *\n * Popular options:\n * - `anthropic/claude-3.5-sonnet` - Latest Claude\n * - `openai/gpt-4-turbo` - GPT-4 Turbo\n * - `openai/gpt-3.5-turbo` - Cheap and fast\n * - `google/gemini-pro` - Google Gemini\n * - `meta-llama/llama-3-70b-instruct` - Open source\n *\n * See full list: https://openrouter.ai/models\n *\n * @default \"anthropic/claude-3.5-sonnet\"\n */\n model?: string;\n\n /**\n * Temperature for generation (0-2)\n * @default 0\n */\n temperature?: number;\n\n /**\n * Maximum tokens per completion\n * @default 4096\n */\n maxTokens?: number;\n\n /**\n * Your app name (for OpenRouter analytics)\n * @default \"evalsense\"\n */\n appName?: string;\n\n /**\n * Your app URL (for OpenRouter analytics)\n */\n siteUrl?: string;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n}\n\n/**\n * Creates an LLM client adapter for OpenRouter.\n *\n * **Setup:**\n * 1. Get API key from https://openrouter.ai/keys\n * 2. Set environment variable: `export OPENROUTER_API_KEY=\"sk-or-...\"`\n * 3. No SDK needed - uses fetch API\n *\n * **Benefits:**\n * - Access 100+ models from one API\n * - Compare different providers easily\n * - Automatic fallbacks and retries\n * - Transparent pricing\n *\n * @param apiKey - Your OpenRouter API key\n * @param options - Configuration options\n * @returns LLM client for use with evalsense metrics\n *\n * @example\n * ```javascript\n * // Basic usage\n * const client = createOpenRouterAdapter(process.env.OPENROUTER_API_KEY);\n * setLLMClient(client);\n *\n * // Use different model\n * const client = createOpenRouterAdapter(process.env.OPENROUTER_API_KEY, {\n * model: 'openai/gpt-3.5-turbo', // Cheaper option\n * appName: 'my-eval-system'\n * });\n *\n * // Use free models for testing\n * const client = createOpenRouterAdapter(process.env.OPENROUTER_API_KEY, {\n * model: 'meta-llama/llama-3-8b-instruct:free'\n * });\n * ```\n */\nexport function createOpenRouterAdapter(\n apiKey: string,\n options: OpenRouterAdapterOptions = {}\n): LLMClient {\n const {\n model = \"anthropic/claude-3.5-sonnet\",\n temperature = 0,\n maxTokens = 4096,\n appName = \"evalsense\",\n siteUrl,\n timeout = 30000,\n } = options;\n\n // Validate API key\n if (!apiKey) {\n throw new Error(\"OpenRouter API key is required. \" + \"Get one at https://openrouter.ai/keys\");\n }\n\n const baseURL = \"https://openrouter.ai/api/v1\";\n\n async function callAPI(\n messages: Array<{ role: string; content: string }>,\n jsonMode: boolean = false\n ): Promise<string> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n \"HTTP-Referer\": siteUrl || \"https://github.com/evalsense/evalsense\",\n \"X-Title\": appName,\n };\n\n const body: any = {\n model,\n messages,\n temperature,\n max_tokens: maxTokens,\n };\n\n // Enable JSON mode if supported by model\n if (jsonMode) {\n body.response_format = { type: \"json_object\" };\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(`${baseURL}/chat/completions`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorData: any = await response.json().catch(() => ({}));\n const errorMessage = errorData.error?.message || response.statusText || \"Unknown error\";\n throw new Error(`OpenRouter API error (${response.status}): ${errorMessage}`);\n }\n\n const data: any = await response.json();\n return data.choices?.[0]?.message?.content ?? \"\";\n } catch (error: any) {\n clearTimeout(timeoutId);\n\n if (error.name === \"AbortError\") {\n throw new Error(`OpenRouter request timed out after ${timeout}ms (model: ${model})`);\n }\n\n const errorMessage = error?.message || String(error);\n throw new Error(\n `OpenRouter API error (model: ${model}): ${errorMessage}\\n` +\n `Check your API key and credits at https://openrouter.ai/activity`\n );\n }\n }\n\n return {\n async complete(prompt: string): Promise<string> {\n return callAPI([{ role: \"user\", content: prompt }], false);\n },\n\n async completeStructured<T>(prompt: string, schema: JSONSchema): Promise<T> {\n // Try JSON mode first (works for OpenAI models via OpenRouter)\n let response: string;\n try {\n response = await callAPI(\n [{ role: \"user\", content: prompt }],\n true // Enable JSON mode\n );\n } catch (error) {\n // If JSON mode not supported, fall back to prompt engineering\n const jsonPrompt =\n prompt +\n \"\\n\\nIMPORTANT: Respond with valid JSON only. No markdown, no explanation. \" +\n `The JSON must match this schema: ${JSON.stringify(schema)}`;\n response = await callAPI([{ role: \"user\", content: jsonPrompt }], false);\n }\n\n try {\n return parseJSONResponse<T>(response);\n } catch (error: any) {\n throw new Error(\n `Failed to parse OpenRouter response as JSON: ${error.message}\\n` +\n `Model: ${model}\\n` +\n `Response preview: ${response.substring(0, 200)}...`\n );\n }\n },\n };\n}\n"]}
@@ -1,24 +1,25 @@
1
1
  'use strict';
2
2
 
3
- var chunkY23VHTD3_cjs = require('../../chunk-Y23VHTD3.cjs');
3
+ var chunkRZFLCWTW_cjs = require('../../chunk-RZFLCWTW.cjs');
4
+ require('../../chunk-JEQ2X3Z6.cjs');
4
5
 
5
6
 
6
7
 
7
8
  Object.defineProperty(exports, "faithfulness", {
8
9
  enumerable: true,
9
- get: function () { return chunkY23VHTD3_cjs.faithfulness; }
10
+ get: function () { return chunkRZFLCWTW_cjs.faithfulness; }
10
11
  });
11
12
  Object.defineProperty(exports, "hallucination", {
12
13
  enumerable: true,
13
- get: function () { return chunkY23VHTD3_cjs.hallucination; }
14
+ get: function () { return chunkRZFLCWTW_cjs.hallucination; }
14
15
  });
15
16
  Object.defineProperty(exports, "relevance", {
16
17
  enumerable: true,
17
- get: function () { return chunkY23VHTD3_cjs.relevance; }
18
+ get: function () { return chunkRZFLCWTW_cjs.relevance; }
18
19
  });
19
20
  Object.defineProperty(exports, "toxicity", {
20
21
  enumerable: true,
21
- get: function () { return chunkY23VHTD3_cjs.toxicity; }
22
+ get: function () { return chunkRZFLCWTW_cjs.toxicity; }
22
23
  });
23
24
  //# sourceMappingURL=index.cjs.map
24
25
  //# sourceMappingURL=index.cjs.map
@@ -1,3 +1,4 @@
1
- export { faithfulness, hallucination, relevance, toxicity } from '../../chunk-BRPM6AB6.js';
1
+ export { faithfulness, hallucination, relevance, toxicity } from '../../chunk-Z3U6AUWX.js';
2
+ import '../../chunk-DGUM43GV.js';
2
3
  //# sourceMappingURL=index.js.map
3
4
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evalsense",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "JS-native LLM evaluation framework with Jest-like API and statistical assertions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -39,11 +39,12 @@
39
39
  }
40
40
  },
41
41
  "bin": {
42
- "evalsense": "./bin/evalsense.js"
42
+ "evalsense": "bin/evalsense.js"
43
43
  },
44
44
  "files": [
45
45
  "dist",
46
- "bin"
46
+ "bin",
47
+ "LICENSE"
47
48
  ],
48
49
  "scripts": {
49
50
  "build": "tsup",
@@ -68,17 +69,18 @@
68
69
  "statistical-testing"
69
70
  ],
70
71
  "author": "Mohit Joshi",
71
- "license": "MIT",
72
+ "license": "Apache-2.0",
72
73
  "dependencies": {
73
74
  "commander": "^12.1.0",
74
- "glob": "^11.0.0",
75
- "fast-json-stable-stringify": "^2.1.0"
75
+ "fast-json-stable-stringify": "^2.1.0",
76
+ "glob": "^11.0.0"
76
77
  },
77
78
  "devDependencies": {
78
79
  "@types/node": "^22.10.0",
79
80
  "@typescript-eslint/eslint-plugin": "^8.18.0",
80
81
  "@typescript-eslint/parser": "^8.18.0",
81
82
  "@vitest/coverage-v8": "^2.1.8",
83
+ "dotenv": "^17.2.4",
82
84
  "eslint": "^9.17.0",
83
85
  "prettier": "^3.4.2",
84
86
  "tsup": "^8.3.5",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/errors.ts","../src/statistics/confusion-matrix.ts","../src/report/json-reporter.ts","../src/report/console-reporter.ts","../src/runner/discovery.ts","../src/core/types.ts","../src/core/context.ts","../src/runner/executor.ts"],"names":[],"mappings":"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","file":"chunk-5P7LNNO6.js","sourcesContent":["/**\n * Custom error classes for EvalSense\n */\n\nexport class EvalSenseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EvalSenseError\";\n }\n}\n\nexport class AssertionError extends EvalSenseError {\n public readonly expected: unknown;\n public readonly actual: unknown;\n public readonly field?: string;\n\n constructor(message: string, expected?: unknown, actual?: unknown, field?: string) {\n super(message);\n this.name = \"AssertionError\";\n this.expected = expected;\n this.actual = actual;\n this.field = field;\n }\n}\n\nexport class DatasetError extends EvalSenseError {\n public readonly source?: string;\n\n constructor(message: string, source?: string) {\n super(message);\n this.name = \"DatasetError\";\n this.source = source;\n }\n}\n\nexport class IntegrityError extends EvalSenseError {\n public readonly missingIds?: string[];\n public readonly duplicateIds?: string[];\n\n constructor(message: string, missingIds?: string[], duplicateIds?: string[]) {\n super(message);\n this.name = \"IntegrityError\";\n this.missingIds = missingIds;\n this.duplicateIds = duplicateIds;\n }\n}\n\nexport class ConfigurationError extends EvalSenseError {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigurationError\";\n }\n}\n\nexport class TestExecutionError extends EvalSenseError {\n public readonly testName: string;\n public readonly originalError?: Error;\n\n constructor(message: string, testName: string, originalError?: Error) {\n super(message);\n this.name = \"TestExecutionError\";\n this.testName = testName;\n this.originalError = originalError;\n }\n}\n","/**\n * Confusion matrix computation\n */\n\nimport type { ConfusionMatrix } from \"../core/types.js\";\n\n/**\n * Builds a confusion matrix from actual and predicted values\n *\n * @param actual - Actual/predicted values from the model\n * @param expected - Expected/ground truth values\n * @returns ConfusionMatrix with matrix, labels, and total\n *\n * @example\n * ```ts\n * const matrix = buildConfusionMatrix(\n * [\"positive\", \"negative\", \"positive\"],\n * [\"positive\", \"positive\", \"positive\"]\n * );\n * // matrix.matrix[i][j] = count of expected[i] predicted as actual[j]\n * ```\n */\nexport function buildConfusionMatrix(\n actual: unknown[],\n expected: unknown[]\n): ConfusionMatrix {\n if (actual.length !== expected.length) {\n throw new Error(\n `Array length mismatch: actual has ${actual.length} elements, expected has ${expected.length}`\n );\n }\n\n // Collect all unique labels\n const labelSet = new Set<string>();\n for (const val of actual) {\n if (val !== undefined && val !== null) {\n labelSet.add(String(val));\n }\n }\n for (const val of expected) {\n if (val !== undefined && val !== null) {\n labelSet.add(String(val));\n }\n }\n\n // Sort labels for deterministic ordering\n const labels = Array.from(labelSet).sort();\n const labelIndex = new Map<string, number>();\n labels.forEach((label, idx) => labelIndex.set(label, idx));\n\n // Initialize confusion matrix with zeros\n const matrix: number[][] = labels.map(() => labels.map(() => 0));\n\n // Fill the matrix\n // Row = expected (ground truth), Column = actual (predicted)\n for (let i = 0; i < actual.length; i++) {\n const actualVal = actual[i];\n const expectedVal = expected[i];\n\n if (actualVal === undefined || actualVal === null) continue;\n if (expectedVal === undefined || expectedVal === null) continue;\n\n const actualIdx = labelIndex.get(String(actualVal));\n const expectedIdx = labelIndex.get(String(expectedVal));\n\n if (actualIdx !== undefined && expectedIdx !== undefined) {\n matrix[expectedIdx]![actualIdx]!++;\n }\n }\n\n const total = actual.filter(\n (v, i) => v !== undefined && v !== null && expected[i] !== undefined && expected[i] !== null\n ).length;\n\n return { matrix, labels, total };\n}\n\n/**\n * Gets the count from a confusion matrix for a specific cell\n */\nexport function getCount(\n cm: ConfusionMatrix,\n expectedLabel: string,\n actualLabel: string\n): number {\n const expectedIdx = cm.labels.indexOf(expectedLabel);\n const actualIdx = cm.labels.indexOf(actualLabel);\n\n if (expectedIdx === -1 || actualIdx === -1) {\n return 0;\n }\n\n return cm.matrix[expectedIdx]?.[actualIdx] ?? 0;\n}\n\n/**\n * Gets true positives for a class\n */\nexport function getTruePositives(cm: ConfusionMatrix, label: string): number {\n return getCount(cm, label, label);\n}\n\n/**\n * Gets false positives for a class (predicted as class but wasn't)\n */\nexport function getFalsePositives(cm: ConfusionMatrix, label: string): number {\n const labelIdx = cm.labels.indexOf(label);\n if (labelIdx === -1) return 0;\n\n let fp = 0;\n for (let i = 0; i < cm.labels.length; i++) {\n if (i !== labelIdx) {\n fp += cm.matrix[i]?.[labelIdx] ?? 0;\n }\n }\n return fp;\n}\n\n/**\n * Gets false negatives for a class (was class but predicted as something else)\n */\nexport function getFalseNegatives(cm: ConfusionMatrix, label: string): number {\n const labelIdx = cm.labels.indexOf(label);\n if (labelIdx === -1) return 0;\n\n let fn = 0;\n for (let j = 0; j < cm.labels.length; j++) {\n if (j !== labelIdx) {\n fn += cm.matrix[labelIdx]?.[j] ?? 0;\n }\n }\n return fn;\n}\n\n/**\n * Gets true negatives for a class\n */\nexport function getTrueNegatives(cm: ConfusionMatrix, label: string): number {\n const labelIdx = cm.labels.indexOf(label);\n if (labelIdx === -1) return 0;\n\n let tn = 0;\n for (let i = 0; i < cm.labels.length; i++) {\n for (let j = 0; j < cm.labels.length; j++) {\n if (i !== labelIdx && j !== labelIdx) {\n tn += cm.matrix[i]?.[j] ?? 0;\n }\n }\n }\n return tn;\n}\n\n/**\n * Gets support (total instances) for a class in ground truth\n */\nexport function getSupport(cm: ConfusionMatrix, label: string): number {\n const labelIdx = cm.labels.indexOf(label);\n if (labelIdx === -1) return 0;\n\n let support = 0;\n for (let j = 0; j < cm.labels.length; j++) {\n support += cm.matrix[labelIdx]?.[j] ?? 0;\n }\n return support;\n}\n\n/**\n * Formats a confusion matrix as a string table\n */\nexport function formatConfusionMatrix(cm: ConfusionMatrix): string {\n const maxLabelLen = Math.max(...cm.labels.map((l) => l.length), 8);\n const colWidth = Math.max(\n ...cm.matrix.flat().map((n) => String(n).length),\n maxLabelLen\n );\n\n const header =\n \" \".repeat(maxLabelLen + 2) +\n cm.labels.map((l) => l.padStart(colWidth)).join(\" \");\n\n const rows = cm.labels.map((label, i) => {\n const rowData = cm.matrix[i]!.map((n) => String(n).padStart(colWidth)).join(\" \");\n return label.padEnd(maxLabelLen) + \" \" + rowData;\n });\n\n return [header, ...rows].join(\"\\n\");\n}\n","/**\n * JSON Reporter - deterministic JSON output\n */\n\nimport { writeFileSync } from \"node:fs\";\nimport stringify from \"fast-json-stable-stringify\";\nimport type { EvalReport } from \"../core/types.js\";\n\n/**\n * JSON Reporter for machine-readable output\n */\nexport class JsonReporter {\n /**\n * Formats a report as deterministic JSON\n */\n format(report: EvalReport): string {\n // Create a serializable version of the report\n const serializable = this.toSerializable(report);\n return stringify(serializable) ?? \"{}\";\n }\n\n /**\n * Writes report to a file\n */\n writeToFile(report: EvalReport, path: string): void {\n const json = this.format(report);\n writeFileSync(path, json, \"utf-8\");\n }\n\n /**\n * Converts report to a JSON-serializable format\n */\n private toSerializable(report: EvalReport): Record<string, unknown> {\n return {\n version: report.version,\n timestamp: report.timestamp,\n summary: report.summary,\n suites: report.suites.map((suite) => ({\n name: suite.name,\n passed: suite.passed,\n failed: suite.failed,\n errors: suite.errors,\n skipped: suite.skipped,\n duration: suite.duration,\n tests: suite.tests.map((test) => ({\n name: test.name,\n status: test.status,\n duration: test.duration,\n error: test.error ? {\n name: test.error.name,\n message: test.error.message,\n } : undefined,\n assertions: test.assertions.map((a) => ({\n type: a.type,\n passed: a.passed,\n message: a.message,\n expected: a.expected,\n actual: a.actual,\n field: a.field,\n class: a.class,\n })),\n fieldMetrics: test.fieldMetrics.map((fm) => ({\n field: fm.field,\n binarized: fm.binarized,\n binarizeThreshold: fm.binarizeThreshold,\n metrics: {\n accuracy: fm.metrics.accuracy,\n perClass: fm.metrics.perClass,\n macroAvg: fm.metrics.macroAvg,\n weightedAvg: fm.metrics.weightedAvg,\n confusionMatrix: {\n labels: fm.metrics.confusionMatrix.labels,\n matrix: fm.metrics.confusionMatrix.matrix,\n total: fm.metrics.confusionMatrix.total,\n },\n },\n })),\n })),\n })),\n integrity: report.integrity,\n };\n }\n}\n\n/**\n * Parses a JSON report back into an EvalReport\n */\nexport function parseReport(json: string): EvalReport {\n const data = JSON.parse(json) as EvalReport;\n return data;\n}\n","/**\n * Console Reporter - human-readable output\n */\n\nimport type { EvalReport, TestResult, FieldMetricResult } from \"../core/types.js\";\nimport { formatConfusionMatrix } from \"../statistics/confusion-matrix.js\";\n\n/**\n * ANSI color codes\n */\nconst colors = {\n reset: \"\\x1b[0m\",\n bold: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n cyan: \"\\x1b[36m\",\n gray: \"\\x1b[90m\",\n};\n\n/**\n * Status symbols\n */\nconst symbols = {\n pass: \"\\u2713\", // ✓\n fail: \"\\u2717\", // ✗\n error: \"!\",\n skip: \"-\",\n};\n\n/**\n * Console reporter for human-readable output\n */\nexport class ConsoleReporter {\n private useColors: boolean;\n\n constructor(useColors = true) {\n this.useColors = useColors && process.stdout.isTTY !== false;\n }\n\n /**\n * Prints the run header\n */\n printHeader(fileCount: number): void {\n this.log(\"\");\n this.log(this.color(\"bold\", `EvalSense v0.1.0`));\n this.log(this.color(\"dim\", `Running ${fileCount} eval file(s)...`));\n this.log(\"\");\n }\n\n /**\n * Prints the full report\n */\n printReport(report: EvalReport): void {\n // Print each suite\n for (const suite of report.suites) {\n this.printSuite(suite.name, suite.tests);\n }\n\n // Print summary\n this.printSummary(report);\n }\n\n /**\n * Prints a suite's results\n */\n private printSuite(name: string, tests: TestResult[]): void {\n this.log(this.color(\"bold\", ` ${name}`));\n this.log(\"\");\n\n for (const test of tests) {\n this.printTest(test);\n }\n\n this.log(\"\");\n }\n\n /**\n * Prints a single test result\n */\n private printTest(test: TestResult): void {\n const symbol = this.getStatusSymbol(test.status);\n const statusColor = this.getStatusColor(test.status);\n const duration = this.color(\"dim\", `(${test.duration}ms)`);\n\n this.log(` ${this.color(statusColor, symbol)} ${test.name} ${duration}`);\n\n // Print field metrics\n for (const fm of test.fieldMetrics) {\n this.printFieldMetrics(fm);\n }\n\n // Print error if any\n if (test.error && test.status === \"failed\") {\n this.log(this.color(\"red\", ` ${test.error.message}`));\n } else if (test.error && test.status === \"error\") {\n this.log(this.color(\"red\", ` Error: ${test.error.message}`));\n }\n\n // Print failed assertions\n for (const assertion of test.assertions) {\n if (!assertion.passed) {\n this.log(this.color(\"red\", ` ${assertion.message}`));\n }\n }\n }\n\n /**\n * Prints field metrics summary\n */\n private printFieldMetrics(fm: FieldMetricResult): void {\n const { metrics, field, binarized, binarizeThreshold } = fm;\n\n const fieldLabel = binarized\n ? `${field} (binarized @ ${binarizeThreshold})`\n : field;\n\n this.log(\n this.color(\n \"cyan\",\n ` Field: ${fieldLabel} | Accuracy: ${this.pct(metrics.accuracy)} | F1: ${this.pct(metrics.macroAvg.f1)}`\n )\n );\n\n // Show per-class metrics if multiple classes\n if (Object.keys(metrics.perClass).length > 1) {\n for (const [cls, classMetrics] of Object.entries(metrics.perClass)) {\n this.log(\n this.color(\n \"dim\",\n ` ${cls}: P=${this.pct(classMetrics.precision)} R=${this.pct(classMetrics.recall)} F1=${this.pct(classMetrics.f1)} (n=${classMetrics.support})`\n )\n );\n }\n }\n }\n\n /**\n * Prints the summary\n */\n private printSummary(report: EvalReport): void {\n const { summary } = report;\n\n this.log(this.color(\"bold\", \" Summary\"));\n this.log(\"\");\n\n const passedStr = this.color(\"green\", `${summary.passed} passed`);\n const failedStr =\n summary.failed > 0\n ? this.color(\"red\", `${summary.failed} failed`)\n : `${summary.failed} failed`;\n const errorsStr =\n summary.errors > 0\n ? this.color(\"red\", `${summary.errors} errors`)\n : `${summary.errors} errors`;\n const skippedStr =\n summary.skipped > 0\n ? this.color(\"yellow\", `${summary.skipped} skipped`)\n : `${summary.skipped} skipped`;\n\n this.log(` Tests: ${passedStr}, ${failedStr}, ${errorsStr}, ${skippedStr}`);\n this.log(` Suites: ${summary.totalSuites}`);\n this.log(` Duration: ${this.formatDuration(summary.duration)}`);\n this.log(\"\");\n\n // Final status\n if (summary.failed === 0 && summary.errors === 0) {\n this.log(this.color(\"green\", \" All tests passed!\"));\n } else {\n this.log(this.color(\"red\", \" Some tests failed.\"));\n }\n\n this.log(\"\");\n }\n\n /**\n * Prints a confusion matrix\n */\n printConfusionMatrix(fm: FieldMetricResult): void {\n this.log(\"\");\n this.log(this.color(\"bold\", ` Confusion Matrix: ${fm.field}`));\n this.log(\"\");\n\n const matrixStr = formatConfusionMatrix(fm.metrics.confusionMatrix);\n for (const line of matrixStr.split(\"\\n\")) {\n this.log(` ${line}`);\n }\n\n this.log(\"\");\n }\n\n /**\n * Formats a percentage\n */\n private pct(value: number): string {\n return `${(value * 100).toFixed(1)}%`;\n }\n\n /**\n * Formats duration\n */\n private formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${ms}ms`;\n }\n return `${(ms / 1000).toFixed(2)}s`;\n }\n\n /**\n * Gets status symbol\n */\n private getStatusSymbol(status: TestResult[\"status\"]): string {\n switch (status) {\n case \"passed\":\n return symbols.pass;\n case \"failed\":\n return symbols.fail;\n case \"error\":\n return symbols.error;\n case \"skipped\":\n return symbols.skip;\n }\n }\n\n /**\n * Gets status color\n */\n private getStatusColor(status: TestResult[\"status\"]): keyof typeof colors {\n switch (status) {\n case \"passed\":\n return \"green\";\n case \"failed\":\n return \"red\";\n case \"error\":\n return \"red\";\n case \"skipped\":\n return \"yellow\";\n }\n }\n\n /**\n * Applies color if enabled\n */\n private color(colorName: keyof typeof colors, text: string): string {\n if (!this.useColors) {\n return text;\n }\n return `${colors[colorName]}${text}${colors.reset}`;\n }\n\n /**\n * Logs a line\n */\n private log(message: string): void {\n console.log(message);\n }\n}\n","/**\n * Test file discovery - finds *.eval.js files\n */\n\nimport { glob } from \"glob\";\nimport { resolve, dirname } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\n/**\n * Default patterns for eval files\n */\nexport const DEFAULT_PATTERNS = [\n \"**/*.eval.js\",\n \"**/*.eval.ts\",\n \"**/*.eval.mjs\",\n];\n\n/**\n * Patterns to ignore\n */\nexport const DEFAULT_IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.git/**\",\n];\n\n/**\n * Options for file discovery\n */\nexport interface DiscoveryOptions {\n /** Patterns to match (default: *.eval.{js,ts,mjs}) */\n patterns?: string[];\n /** Patterns to ignore */\n ignore?: string[];\n /** Base directory to search from */\n cwd?: string;\n /** Filter pattern for test names */\n filter?: string;\n}\n\n/**\n * Discovers eval files matching the patterns\n *\n * @param options - Discovery options\n * @returns Array of absolute file paths\n */\nexport async function discoverEvalFiles(\n options: DiscoveryOptions = {}\n): Promise<string[]> {\n const {\n patterns = DEFAULT_PATTERNS,\n ignore = DEFAULT_IGNORE,\n cwd = process.cwd(),\n } = options;\n\n const files: string[] = [];\n\n for (const pattern of patterns) {\n const matches = await glob(pattern, {\n cwd,\n ignore,\n absolute: true,\n nodir: true,\n });\n files.push(...matches);\n }\n\n // Remove duplicates and sort\n const unique = [...new Set(files)].sort();\n\n return unique;\n}\n\n/**\n * Discovers eval files from a specific path (file or directory)\n */\nexport async function discoverFromPath(\n path: string,\n options: DiscoveryOptions = {}\n): Promise<string[]> {\n const absolutePath = resolve(process.cwd(), path);\n\n if (!existsSync(absolutePath)) {\n throw new Error(`Path does not exist: ${path}`);\n }\n\n // Check if it's a file\n const { statSync } = await import(\"node:fs\");\n const stat = statSync(absolutePath);\n\n if (stat.isFile()) {\n // Single file\n return [absolutePath];\n }\n\n // Directory - discover within it\n return discoverEvalFiles({\n ...options,\n cwd: absolutePath,\n });\n}\n\n/**\n * Filters file paths by a pattern\n */\nexport function filterFiles(files: string[], filter?: string): string[] {\n if (!filter) {\n return files;\n }\n\n const filterLower = filter.toLowerCase();\n return files.filter((file) => file.toLowerCase().includes(filterLower));\n}\n\n/**\n * Groups files by their directory\n */\nexport function groupByDirectory(files: string[]): Map<string, string[]> {\n const groups = new Map<string, string[]>();\n\n for (const file of files) {\n const dir = dirname(file);\n const existing = groups.get(dir) ?? [];\n existing.push(file);\n groups.set(dir, existing);\n }\n\n return groups;\n}\n","/**\n * Core type definitions for EvalSense\n */\n\n// ============================================================================\n// Dataset & Alignment Types\n// ============================================================================\n\n/**\n * A loaded dataset with records and metadata\n */\nexport interface Dataset<T = Record<string, unknown>> {\n records: T[];\n metadata: DatasetMetadata;\n}\n\nexport interface DatasetMetadata {\n source: string;\n count: number;\n loadedAt: Date;\n}\n\n/**\n * A record aligned between actual (model output) and expected (ground truth)\n */\nexport interface AlignedRecord {\n id: string;\n actual: Record<string, unknown>;\n expected: Record<string, unknown>;\n}\n\n/**\n * Output from runModel() - predictions with IDs for alignment\n */\nexport interface Prediction {\n id: string;\n [field: string]: unknown;\n}\n\n// ============================================================================\n// LLM Types\n// ============================================================================\n\n/**\n * JSON Schema for structured LLM outputs\n */\nexport interface JSONSchema {\n type: string;\n properties?: Record<string, unknown>;\n required?: string[];\n [key: string]: unknown;\n}\n\n/**\n * LLM client interface for metric evaluation\n */\nexport interface LLMClient {\n /**\n * Generate a text completion from a prompt\n */\n complete(prompt: string): Promise<string>;\n\n /**\n * Generate a structured JSON completion (optional)\n */\n completeStructured?<T>(prompt: string, schema: JSONSchema): Promise<T>;\n}\n\n// ============================================================================\n// Metric Types\n// ============================================================================\n\n/**\n * Output from an LLM metric evaluation\n */\nexport interface MetricOutput {\n id: string;\n metric: string;\n score: number;\n label?: string;\n\n /** LLM's reasoning/explanation (for LLM-based metrics) */\n reasoning?: string;\n\n /** Evaluation mode used (for LLM-based metrics) */\n evaluationMode?: \"per-row\" | \"batch\";\n}\n\n/**\n * Configuration for a metric function\n */\nexport interface MetricConfig {\n outputs: Array<{ id: string; output: string }>;\n context?: string[];\n query?: string[];\n source?: string[];\n\n /** LLM client override (defaults to global client) */\n llmClient?: LLMClient;\n\n /** Evaluation mode: per-row (accurate, expensive) or batch (cheaper, potentially less accurate) */\n evaluationMode?: \"per-row\" | \"batch\";\n\n /** Custom prompt template override */\n customPrompt?: string;\n\n /** LLM temperature (default: 0) */\n temperature?: number;\n\n /** Max tokens per completion */\n maxTokens?: number;\n\n /** Timeout in milliseconds */\n timeout?: number;\n}\n\n/**\n * A metric function that evaluates outputs\n */\nexport type MetricFn = (config: MetricConfig) => Promise<MetricOutput[]>;\n\n// ============================================================================\n// Statistics Types\n// ============================================================================\n\n/**\n * Confusion matrix with labels\n */\nexport interface ConfusionMatrix {\n matrix: number[][];\n labels: string[];\n total: number;\n}\n\n/**\n * Per-class classification metrics\n */\nexport interface ClassMetrics {\n precision: number;\n recall: number;\n f1: number;\n support: number;\n}\n\n/**\n * Full classification metrics result\n */\nexport interface ClassificationMetrics {\n accuracy: number;\n perClass: Record<string, ClassMetrics>;\n macroAvg: { precision: number; recall: number; f1: number };\n weightedAvg: { precision: number; recall: number; f1: number };\n confusionMatrix: ConfusionMatrix;\n}\n\n/**\n * Regression metrics result\n */\nexport interface RegressionMetrics {\n mae: number;\n mse: number;\n rmse: number;\n r2: number;\n}\n\n// ============================================================================\n// Field Evaluation Types\n// ============================================================================\n\n/**\n * Result of evaluating a single field across all predictions\n */\nexport interface FieldMetricResult {\n field: string;\n metrics: ClassificationMetrics;\n binarized: boolean;\n binarizeThreshold?: number;\n}\n\n// ============================================================================\n// Test & Suite Types\n// ============================================================================\n\n/**\n * Test function signature\n */\nexport type TestFn = () => Promise<void> | void;\n\n/**\n * An individual eval test\n */\nexport interface EvalTest {\n name: string;\n fn: TestFn;\n}\n\n/**\n * A test suite (describe block)\n */\nexport interface Suite {\n name: string;\n tests: EvalTest[];\n beforeAll?: TestFn[];\n afterAll?: TestFn[];\n beforeEach?: TestFn[];\n afterEach?: TestFn[];\n}\n\n/**\n * Current test execution context\n */\nexport interface TestContext {\n currentSuite: Suite | null;\n suites: Suite[];\n results: SuiteResult[];\n}\n\n// ============================================================================\n// Assertion Types\n// ============================================================================\n\n/**\n * Result of a single assertion\n */\nexport interface AssertionResult {\n type: string;\n passed: boolean;\n message: string;\n expected?: unknown;\n actual?: unknown;\n field?: string;\n class?: string;\n}\n\n// ============================================================================\n// Result & Report Types\n// ============================================================================\n\n/**\n * Result of a single test\n */\nexport interface TestResult {\n name: string;\n status: \"passed\" | \"failed\" | \"error\" | \"skipped\";\n assertions: AssertionResult[];\n fieldMetrics: FieldMetricResult[];\n duration: number;\n error?: Error;\n}\n\n/**\n * Result of a test suite\n */\nexport interface SuiteResult {\n name: string;\n tests: TestResult[];\n passed: number;\n failed: number;\n errors: number;\n skipped: number;\n duration: number;\n}\n\n/**\n * Integrity check results for a dataset\n */\nexport interface IntegrityResult {\n valid: boolean;\n totalRecords: number;\n missingIds: string[];\n duplicateIds: string[];\n missingFields: Array<{ id: string; fields: string[] }>;\n}\n\n/**\n * Final evaluation report\n */\nexport interface EvalReport {\n version: string;\n timestamp: string;\n suites: SuiteResult[];\n summary: {\n totalSuites: number;\n totalTests: number;\n passed: number;\n failed: number;\n errors: number;\n skipped: number;\n duration: number;\n };\n integrity?: IntegrityResult;\n}\n\n// ============================================================================\n// CLI Types\n// ============================================================================\n\n/**\n * CLI configuration options\n */\nexport interface CLIOptions {\n filter?: string;\n output?: string;\n reporter?: \"json\" | \"console\" | \"both\";\n bail?: boolean;\n timeout?: number;\n}\n\n/**\n * Exit codes for CI integration\n */\nexport const ExitCodes = {\n SUCCESS: 0,\n ASSERTION_FAILURE: 1,\n INTEGRITY_FAILURE: 2,\n EXECUTION_ERROR: 3,\n CONFIGURATION_ERROR: 4,\n} as const;\n\nexport type ExitCode = (typeof ExitCodes)[keyof typeof ExitCodes];\n","/**\n * Test context management for EvalSense\n * Manages the global state during test execution\n */\n\nimport type { Suite, EvalTest, TestContext, AssertionResult, FieldMetricResult } from \"./types.js\";\n\n/**\n * Global test context - singleton for the current test run\n */\nlet globalContext: TestContext = createEmptyContext();\n\n/**\n * Current test execution state\n */\ninterface CurrentTestState {\n assertions: AssertionResult[];\n fieldMetrics: FieldMetricResult[];\n}\n\nlet currentTestState: CurrentTestState | null = null;\n\n/**\n * Creates an empty test context\n */\nexport function createEmptyContext(): TestContext {\n return {\n currentSuite: null,\n suites: [],\n results: [],\n };\n}\n\n/**\n * Gets the global test context\n */\nexport function getContext(): TestContext {\n return globalContext;\n}\n\n/**\n * Resets the global test context (used between test runs)\n */\nexport function resetContext(): void {\n globalContext = createEmptyContext();\n currentTestState = null;\n}\n\n/**\n * Gets the current suite being defined\n */\nexport function getCurrentSuite(): Suite | null {\n return globalContext.currentSuite;\n}\n\n/**\n * Sets the current suite being defined\n */\nexport function setCurrentSuite(suite: Suite | null): void {\n globalContext.currentSuite = suite;\n}\n\n/**\n * Adds a suite to the context\n */\nexport function addSuite(suite: Suite): void {\n globalContext.suites.push(suite);\n}\n\n/**\n * Adds a test to the current suite\n */\nexport function addTestToCurrentSuite(test: EvalTest): void {\n if (!globalContext.currentSuite) {\n throw new Error(\"Cannot add test outside of a describe() block\");\n }\n globalContext.currentSuite.tests.push(test);\n}\n\n/**\n * Gets all registered suites\n */\nexport function getSuites(): Suite[] {\n return globalContext.suites;\n}\n\n/**\n * Starts a new test execution (for collecting assertions)\n */\nexport function startTestExecution(): void {\n currentTestState = {\n assertions: [],\n fieldMetrics: [],\n };\n}\n\n/**\n * Ends the current test execution and returns collected data\n */\nexport function endTestExecution(): CurrentTestState {\n const state = currentTestState;\n currentTestState = null;\n return state ?? { assertions: [], fieldMetrics: [] };\n}\n\n/**\n * Records an assertion result in the current test\n */\nexport function recordAssertion(result: AssertionResult): void {\n if (currentTestState) {\n currentTestState.assertions.push(result);\n }\n}\n\n/**\n * Records field metrics in the current test\n */\nexport function recordFieldMetrics(metrics: FieldMetricResult): void {\n if (currentTestState) {\n currentTestState.fieldMetrics.push(metrics);\n }\n}\n\n/**\n * Checks if we're currently executing a test\n */\nexport function isInTestExecution(): boolean {\n return currentTestState !== null;\n}\n\n/**\n * Gets the current test state (for assertions to check)\n */\nexport function getCurrentTestState(): CurrentTestState | null {\n return currentTestState;\n}\n","/**\n * Test executor - runs discovered eval files\n */\n\nimport { pathToFileURL } from \"node:url\";\nimport type { Suite, TestResult, SuiteResult, EvalReport } from \"../core/types.js\";\nimport { ExitCodes } from \"../core/types.js\";\nimport {\n getSuites,\n startTestExecution,\n endTestExecution,\n} from \"../core/context.js\";\nimport { AssertionError, TestExecutionError } from \"../core/errors.js\";\n\n/**\n * Options for test execution\n */\nexport interface ExecutorOptions {\n /** Stop on first failure */\n bail?: boolean;\n /** Test timeout in ms */\n timeout?: number;\n /** Filter pattern for test names */\n filter?: string;\n}\n\n/**\n * Executes all eval files and returns results\n */\nexport async function executeEvalFiles(\n files: string[],\n options: ExecutorOptions = {}\n): Promise<EvalReport> {\n const startTime = Date.now();\n const suiteResults: SuiteResult[] = [];\n\n // Load all eval files (this registers suites via describe())\n // Note: We don't reset context here because in CLI usage, each run\n // is a fresh Node process. For programmatic usage, call resetContext()\n // before calling this function if you need a clean slate.\n for (const file of files) {\n try {\n const fileUrl = pathToFileURL(file).href;\n await import(fileUrl);\n } catch (error) {\n throw new TestExecutionError(`Failed to load eval file: ${file}`, file, error as Error);\n }\n }\n\n // Get all registered suites\n const suites = getSuites();\n\n // Execute each suite\n for (const suite of suites) {\n const result = await executeSuite(suite, options);\n suiteResults.push(result);\n\n if (options.bail && result.failed > 0) {\n break;\n }\n }\n\n // Build report\n const report = buildReport(suiteResults, Date.now() - startTime);\n\n return report;\n}\n\n/**\n * Executes a single suite\n */\nasync function executeSuite(\n suite: Suite,\n options: ExecutorOptions\n): Promise<SuiteResult> {\n const startTime = Date.now();\n const testResults: TestResult[] = [];\n let passed = 0;\n let failed = 0;\n let errors = 0;\n let skipped = 0;\n\n // Run beforeAll hooks\n for (const hook of suite.beforeAll ?? []) {\n try {\n await hook();\n } catch (error) {\n // beforeAll failure fails all tests\n const message = error instanceof Error ? error.message : String(error);\n for (const test of suite.tests) {\n testResults.push({\n name: test.name,\n status: \"error\",\n assertions: [],\n fieldMetrics: [],\n duration: 0,\n error: new Error(`beforeAll hook failed: ${message}`),\n });\n errors++;\n }\n return {\n name: suite.name,\n tests: testResults,\n passed,\n failed,\n errors,\n skipped,\n duration: Date.now() - startTime,\n };\n }\n }\n\n // Run each test\n for (const test of suite.tests) {\n // Check filter\n if (options.filter && !test.name.toLowerCase().includes(options.filter.toLowerCase())) {\n testResults.push({\n name: test.name,\n status: \"skipped\",\n assertions: [],\n fieldMetrics: [],\n duration: 0,\n });\n skipped++;\n continue;\n }\n\n // Check if skipped\n if (test.name.startsWith(\"[SKIPPED]\")) {\n testResults.push({\n name: test.name,\n status: \"skipped\",\n assertions: [],\n fieldMetrics: [],\n duration: 0,\n });\n skipped++;\n continue;\n }\n\n // Run beforeEach hooks\n for (const hook of suite.beforeEach ?? []) {\n try {\n await hook();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n testResults.push({\n name: test.name,\n status: \"error\",\n assertions: [],\n fieldMetrics: [],\n duration: 0,\n error: new Error(`beforeEach hook failed: ${message}`),\n });\n errors++;\n continue;\n }\n }\n\n // Execute the test\n const result = await executeTest(test.name, test.fn, options.timeout);\n testResults.push(result);\n\n if (result.status === \"passed\") {\n passed++;\n } else if (result.status === \"failed\") {\n failed++;\n } else if (result.status === \"error\") {\n errors++;\n }\n\n // Run afterEach hooks\n for (const hook of suite.afterEach ?? []) {\n try {\n await hook();\n } catch {\n // Log but don't fail the test\n }\n }\n\n // Bail on failure\n if (options.bail && (failed > 0 || errors > 0)) {\n break;\n }\n }\n\n // Run afterAll hooks\n for (const hook of suite.afterAll ?? []) {\n try {\n await hook();\n } catch {\n // Log but don't fail\n }\n }\n\n return {\n name: suite.name,\n tests: testResults,\n passed,\n failed,\n errors,\n skipped,\n duration: Date.now() - startTime,\n };\n}\n\n/**\n * Executes a single test with timeout\n */\nasync function executeTest(\n name: string,\n fn: () => Promise<void> | void,\n timeout = 30000\n): Promise<TestResult> {\n const startTime = Date.now();\n\n // Start collecting assertions\n startTestExecution();\n\n try {\n // Run with timeout\n await Promise.race([\n fn(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`Test timed out after ${timeout}ms`)), timeout)\n ),\n ]);\n\n // Test passed - collect results\n const { assertions, fieldMetrics } = endTestExecution();\n\n return {\n name,\n status: \"passed\",\n assertions,\n fieldMetrics,\n duration: Date.now() - startTime,\n };\n } catch (error) {\n const { assertions, fieldMetrics } = endTestExecution();\n\n if (error instanceof AssertionError) {\n // Assertion failure\n return {\n name,\n status: \"failed\",\n assertions,\n fieldMetrics,\n duration: Date.now() - startTime,\n error,\n };\n }\n\n // Execution error\n return {\n name,\n status: \"error\",\n assertions,\n fieldMetrics,\n duration: Date.now() - startTime,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n}\n\n/**\n * Builds the final report from suite results\n */\nfunction buildReport(suiteResults: SuiteResult[], totalDuration: number): EvalReport {\n let totalTests = 0;\n let totalPassed = 0;\n let totalFailed = 0;\n let totalErrors = 0;\n let totalSkipped = 0;\n\n for (const suite of suiteResults) {\n totalTests += suite.tests.length;\n totalPassed += suite.passed;\n totalFailed += suite.failed;\n totalErrors += suite.errors;\n totalSkipped += suite.skipped;\n }\n\n return {\n version: \"1.0.0\",\n timestamp: new Date().toISOString(),\n suites: suiteResults,\n summary: {\n totalSuites: suiteResults.length,\n totalTests,\n passed: totalPassed,\n failed: totalFailed,\n errors: totalErrors,\n skipped: totalSkipped,\n duration: totalDuration,\n },\n };\n}\n\n/**\n * Determines exit code from report\n */\nexport function getExitCode(report: EvalReport): number {\n if (report.summary.errors > 0) {\n return ExitCodes.EXECUTION_ERROR;\n }\n if (report.summary.failed > 0) {\n return ExitCodes.ASSERTION_FAILURE;\n }\n return ExitCodes.SUCCESS;\n}\n"]}