@dooor-ai/toolkit
Version:
Guards, Evals & Observability for AI applications - works seamlessly with LangChain/LangGraph
77 lines (67 loc) • 2.06 kB
text/typescript
import { Eval } from "./base";
import { EvalResult, EvalConfig } from "../core/types";
interface LatencyEvalConfig extends EvalConfig {
/** Threshold in milliseconds (default: 3000ms = 3s) */
thresholdMs?: number;
/** Target latency for scoring (default: 1000ms = 1s) */
targetMs?: number;
/** Max latency before score is 0 (default: 10000ms = 10s) */
maxMs?: number;
}
/**
* Evaluates the latency of LLM responses
*
* Score calculation:
* - latency <= target: score = 1.0
* - target < latency < max: linear scale from 1.0 to 0.0
* - latency >= max: score = 0.0
*/
export class LatencyEval extends Eval {
private thresholdMs: number;
private targetMs: number;
private maxMs: number;
constructor(config: LatencyEvalConfig = {}) {
super(config);
this.thresholdMs = config.thresholdMs ?? 3000; // 3 seconds
this.targetMs = config.targetMs ?? 1000; // 1 second
this.maxMs = config.maxMs ?? 10000; // 10 seconds
}
get name(): string {
return "LatencyEval";
}
evaluate(
input: string,
output: string,
metadata?: Record<string, any>
): EvalResult {
const latency = metadata?.latency ?? 0;
// Calculate score
let score: number;
if (latency <= this.targetMs) {
score = 1.0;
} else if (latency >= this.maxMs) {
score = 0.0;
} else {
// Linear interpolation between target and max
const range = this.maxMs - this.targetMs;
const delta = latency - this.targetMs;
score = 1.0 - (delta / range);
}
const passed = latency <= this.thresholdMs;
return {
name: this.name,
score: Math.max(0, Math.min(1, score)), // Clamp to [0, 1]
passed,
details: passed
? `Latency ${latency}ms is acceptable (threshold: ${this.thresholdMs}ms)`
: `Latency ${latency}ms exceeds threshold of ${this.thresholdMs}ms`,
metadata: {
latency,
thresholdMs: this.thresholdMs,
targetMs: this.targetMs,
maxMs: this.maxMs,
},
timestamp: new Date(),
};
}
}