-
Notifications
You must be signed in to change notification settings - Fork 201
Expand file tree
/
Copy pathbenchmark-spec.ts
More file actions
94 lines (83 loc) · 2.83 KB
/
Copy pathbenchmark-spec.ts
File metadata and controls
94 lines (83 loc) · 2.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import type { ColumnDef, DatasetSpec } from "../models/schemas.js";
import { normalizeSpecColumnOrder } from "./dataset-spec.js";
/** Benchmark harness fields from prompts.json (via env in adapters). */
export interface BenchmarkSpecContext {
promptId?: string;
promptQuality?: string;
persona?: string;
expectedStress?: string;
requiredColumns: string[];
}
export function hasBenchmarkRequiredColumns(
context?: BenchmarkSpecContext,
): context is BenchmarkSpecContext & { requiredColumns: string[] } {
return Boolean(context?.requiredColumns?.length);
}
/** Parse comma-separated column names (CLI flag or benchmark env). */
export function parseRequiredColumns(value: string): string[] {
const columns = value
.split(",")
.map((name) => name.trim())
.filter(Boolean);
if (columns.length === 0) {
throw new Error(
"Required columns must include at least one non-empty column name.",
);
}
return columns;
}
/**
* Ensures every benchmark-required column name exists on the spec as required.
* Types and descriptions come from the dataset-spec LLM when present; otherwise
* minimal placeholders (no per-column name heuristics).
*/
export function mergeSpecWithBenchmarkRequiredColumns(
spec: DatasetSpec,
context: BenchmarkSpecContext,
): DatasetSpec {
const requiredColumns = context.requiredColumns;
const columnsByName = new Map(spec.columns.map((column) => [column.name, column]));
const requiredColumnDefs: ColumnDef[] = requiredColumns.map((name) => {
const existing = columnsByName.get(name);
if (existing) {
return { ...existing, required: true };
}
return {
name,
type: "string",
description: name,
required: true,
};
});
const optionalExtras = spec.columns.filter(
(column) => !requiredColumns.includes(column.name),
);
const columns = [...requiredColumnDefs, ...optionalExtras];
const columnNames = new Set(columns.map((column) => column.name));
const isEntityLikeColumn = (name: string): boolean =>
/(entity|company|organization|business|restaurant|bakery|provider|product|name|title)/i.test(
name,
);
const dedupeKey =
requiredColumns.find(
(name) => columnNames.has(name) && isEntityLikeColumn(name),
) ??
spec.dedupe_keys.find((key) => columnNames.has(key)) ??
requiredColumns.find((name) => columnNames.has(name)) ??
spec.dedupe_keys[0];
const extractionHints = [
spec.extraction_hints,
`Benchmark required columns (use as exact row keys): ${requiredColumns.join(", ")}.`,
context.expectedStress
? `Benchmark stress note: ${context.expectedStress}`
: undefined,
]
.filter(Boolean)
.join("\n");
return normalizeSpecColumnOrder({
...spec,
columns,
dedupe_keys: dedupeKey ? [dedupeKey] : spec.dedupe_keys,
extraction_hints: extractionHints,
});
}