// 检测项基类:对应 veridrop core/detectors_base.py。 // BaseDetector 元数据 + appliesTo 过滤 // ├ ActiveDetector 自己发请求 // └ PassiveDetector 看别的检测项的 (请求, 应答),不花 token import type { DetectorResult, DetectorStatus, ExecutionConfig } from "../models.js"; import type { Body, CallResult, StreamEvent } from "../client.js"; /** 检测项眼里的客户端(ThrottledClient 的这几个方法)。 */ export interface DetectClient { readonly baseUrl: string; messagesCreate(body: Body): Promise; countTokens(body: Body): Promise; messagesStream(body: Body): AsyncGenerator<[StreamEvent, number]>; } export function errText(e: unknown): string { return e instanceof Error ? e.message : String(e); } export abstract class BaseDetector { abstract readonly name: string; abstract readonly displayName: string; abstract readonly weight: number; /** runner 在 run 之前注入;要按档位分支的检测项(consistency)读它。 */ config: ExecutionConfig | null = null; appliesTo(_model: string): boolean { return true; } result( status: DetectorStatus, score: number, details: Record | null = null, durationMs: number | null = null, error: string | null = null, ): DetectorResult { return { name: this.name, display_name: this.displayName, status, score, weight: this.weight, details: details ?? {}, duration_ms: durationMs, error, }; } skip(reason: string): DetectorResult { return this.result("skip", 0, { skip_reason: reason }); } } export abstract class ActiveDetector extends BaseDetector { abstract run(client: DetectClient, model: string): Promise; /** 检测项绝不能把整轮带崩:抛出来的异常记成 error,带上类型与调用栈。 */ async timedRun(client: DetectClient, model: string): Promise { const start = performance.now(); let res: DetectorResult; try { res = await this.run(client, model); } catch (e) { const elapsed = Math.trunc(performance.now() - start); return this.result( "error", 0, { exception_type: e instanceof Error ? e.name : typeof e, traceback: (e instanceof Error && e.stack ? e.stack : String(e)).slice(0, 3000), }, elapsed, errText(e), ); } if (res.duration_ms === null) res.duration_ms = Math.trunc(performance.now() - start); return res; } } export abstract class PassiveDetector extends BaseDetector { observe(_request: Body, _response: Record, _headers: Headers, _latencyMs: number): void {} abstract finalize(): DetectorResult; }