@benchsdk/runner 0.2.0 → 0.5.2

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.
package/dist/index.d.cts CHANGED
@@ -1,10 +1,14 @@
1
- import { TaskResultRecord, JsonObject, BaseParticipant, DefineStepOptions, TaskStepRecord } from '@benchsdk/client';
1
+ import { TaskResultRecord, JsonObject, DefineStepOptions, BenchmarkLogOptions, TaskStepRecord } from '@benchsdk/api';
2
+ export { BenchmarkAnalyticsReadiness, BenchmarkApiError, BenchmarkArtifact, BenchmarkArtifactDownload, BenchmarkAssignment, BenchmarkClient, BenchmarkClientConfig, BenchmarkConcurrencyPoint, BenchmarkEventRateBucket, BenchmarkFailurePoint, BenchmarkLogLevel, BenchmarkLogOptions, BenchmarkParticipant, BenchmarkResource, BenchmarkResultLatencySummary, BenchmarkResultSummary, BenchmarkResultsOverview, BenchmarkResultsOverviewAnalytics, BenchmarkResultsOverviewInput, BenchmarkResultsOverviewRun, BenchmarkRun, BenchmarkRunAnalyticsSummary, BenchmarkRunImportItem, BenchmarkRunImports, BenchmarkRunImportsSummary, BenchmarkRunResults, BenchmarkRunStatus, BenchmarkRunSummaryInput, BenchmarkRunSummaryMetric, BenchmarkRunSummaryResult, BenchmarkRunSummaryRunMetadata, BenchmarkRunSummaryScalar, BenchmarkRunTaskResults, BenchmarkRunTaskResultsInput, BenchmarkRunTimeline, BenchmarkRunTimelineInput, BenchmarkRunWorker, BenchmarkStepOutcome, BenchmarkStepResultSummary, BenchmarkTaskBucket, BenchmarkWorkerAttempt, BenchmarkWorkerStatus, ClaimWorkerInput, CreateRunInput, CreateWorkerArtifactInput, CreateWorkerArtifactResponse, DefineStepOptions, JsonObject, JsonValue, PlanWorkersInput, RunProgress, RunProgressConcurrency, RunProgressParticipant, RunProgressParticipantCounts, RunProgressStatus, RunProgressSummary, RunProgressTaskCounts, RunProgressWorkerCounts, RunWorkerContext, RunWorkerOptions, RunWorkerResult, SendTaskResultsInput, TaskFunction, TaskResultRecord, TaskResultsResponse, TaskStepRecord, UpdateBenchmarkInput, UpdateParticipantInput, UpdateRunInput, UpdateWorkerInput, UploadWorkerArtifactInput, UpsertBenchmarkInput, UpsertParticipantInput, WorkerConcurrencySample, WorkerFinishContext, WorkerHeartbeatInput, createBenchmarkClient } from '@benchsdk/api';
3
+ import { BaseParticipant } from '@benchsdk/worker';
4
+ export { BaseParticipant, BenchmarkReporter, BenchmarkReporterArtifactInput, BenchmarkReporterBarrierInput, BenchmarkReporterBarrierResult, BenchmarkReporterConfig, BenchmarkReporterHeartbeatInput, BenchmarkReporterProgress, BenchmarkSystemMetricsCollector, BenchmarkSystemMetricsSample, claimBenchmarkReporter, createSystemMetricsCollector, filterParticipantsByEnv, runWorker, selectParticipants } from '@benchsdk/worker';
5
+ export { AuthError, CliAuth, createApiClient, resolveAuth } from '@benchsdk/cli';
2
6
 
3
7
  type MetricValue = string | ((record: TaskResultRecord) => number | number[] | undefined);
4
8
  interface MetricScoring {
5
9
  name: string;
6
10
  value?: MetricValue;
7
- unit: string;
11
+ unit?: string;
8
12
  ceiling: number;
9
13
  floor?: number;
10
14
  higherIsBetter?: boolean;
@@ -15,8 +19,43 @@ interface MetricScoring {
15
19
  };
16
20
  trim?: number;
17
21
  }
22
+ interface BenchmarkScoringWeights {
23
+ median: number;
24
+ p95: number;
25
+ p99: number;
26
+ }
27
+ interface BenchmarkScoringMetric {
28
+ key: string;
29
+ label?: string;
30
+ unit?: string;
31
+ ceiling: number;
32
+ floor?: number;
33
+ higherIsBetter?: boolean;
34
+ weights: BenchmarkScoringWeights;
35
+ trim?: number;
36
+ }
37
+ /**
38
+ * Serializable counterpart to a `success` predicate: a record counts as
39
+ * successful only if it succeeded *and* every listed data field equals the
40
+ * given value (e.g. `{ verified: true }`, `{ actionsCompleted: 24 }`). Kept to
41
+ * equality on scalar data fields so the platform can express the same rule as
42
+ * a query predicate rather than replaying benchmark code.
43
+ */
44
+ interface BenchmarkScoringSuccess {
45
+ requireData: Record<string, string | number | boolean>;
46
+ }
47
+ /** Serializable scoring spec declared in a `*.bench.ts` file and uploaded to the platform. */
48
+ interface BenchmarkScoringConfig {
49
+ /** Optional data key to group records by when computing summary rows (e.g. 'file_size'). */
50
+ groupBy?: string;
51
+ /** Extra conditions a record must meet to count as successful. Default: `status === 'success'`. */
52
+ success?: BenchmarkScoringSuccess;
53
+ metrics: BenchmarkScoringMetric[];
54
+ }
18
55
  interface ScoringSpec {
19
56
  dimensions?: Record<string, unknown>;
57
+ /** Optional data key that groups task records into separate summary rows. */
58
+ groupBy?: string;
20
59
  success?: (record: TaskResultRecord) => boolean;
21
60
  metrics: MetricScoring[];
22
61
  }
@@ -42,7 +81,7 @@ interface BenchmarkScoreResult {
42
81
  skipReason?: string;
43
82
  }
44
83
  type LowerIsBetter = (name: string, opts: {
45
- unit: string;
84
+ unit?: string;
46
85
  ceiling: number;
47
86
  value?: MetricValue;
48
87
  weights: {
@@ -53,7 +92,7 @@ type LowerIsBetter = (name: string, opts: {
53
92
  trim?: number;
54
93
  }) => MetricScoring;
55
94
  type HigherIsBetter = (name: string, opts: {
56
- unit: string;
95
+ unit?: string;
57
96
  floor?: number;
58
97
  ceiling: number;
59
98
  value?: MetricValue;
@@ -66,7 +105,21 @@ type HigherIsBetter = (name: string, opts: {
66
105
  }) => MetricScoring;
67
106
  declare const lowerIsBetter: LowerIsBetter;
68
107
  declare const higherIsBetter: HigherIsBetter;
69
- declare function score(outcome: BenchmarkRunOutcome, spec: ScoringSpec): BenchmarkScoreResult[];
108
+ declare class ScoringSpecError extends Error {
109
+ constructor(message: string);
110
+ }
111
+ declare function validateScoringSpec(spec: ScoringSpec): void;
112
+ declare function score(outcome: BenchmarkRunOutcome, spec: ScoringSpec, displayMetrics?: {
113
+ key: string;
114
+ unit?: string;
115
+ }[]): BenchmarkScoreResult[];
116
+ /** Builds a runtime {@link ScoringSpec} from a serializable {@link BenchmarkScoringConfig}. */
117
+ declare function scoringConfigToSpec(config: BenchmarkScoringConfig, dimensions?: Record<string, unknown>, display?: {
118
+ metrics?: {
119
+ key: string;
120
+ unit?: string;
121
+ }[];
122
+ }): ScoringSpec;
70
123
 
71
124
  /**
72
125
  * A `*.bench.ts` file is the composition of a **config** and a **task**:
@@ -131,7 +184,7 @@ interface TaskResult {
131
184
  * Pre-measured steps the task timed itself (e.g. socket phases).
132
185
  * Only honored in `groupBy: 'round'` runs, where the runner builds records
133
186
  * manually. In `groupBy: 'participant'` runs the platform worker
134
- * (`client.runWorker`) owns steps, so `steps` and `latencyMs` are ignored.
187
+ * (`runWorker`) owns steps, so `steps` and `latencyMs` are ignored.
135
188
  */
136
189
  steps?: TaskStepRecord[];
137
190
  /** Task-owned overall latency; overrides framework wall-clock (round mode only). */
@@ -142,8 +195,19 @@ interface TaskStepOptions extends Omit<DefineStepOptions, 'concurrency' | 'stepC
142
195
  /** Per-iteration timeout in milliseconds. If an invocation exceeds this, it is aborted and a `step_timeout` TaskError is thrown. */
143
196
  timeoutMs?: number;
144
197
  /** Number of times to invoke `fn` in parallel. Defaults to 1. When greater than 1, the step returns an array of results. */
198
+ parallelInvocations?: number;
199
+ /** @deprecated use `parallelInvocations` instead. */
145
200
  concurrency?: number;
146
201
  }
202
+ interface TaskErrorOptions {
203
+ code?: string;
204
+ data?: JsonObject;
205
+ steps?: TaskStepRecord[];
206
+ /** The step name this error was thrown from, when known. */
207
+ step?: string;
208
+ /** The timeout that was exceeded, when this is a timeout error. */
209
+ timeoutMs?: number;
210
+ }
147
211
  /**
148
212
  * Throw this from a task to record a failure while preserving domain data and
149
213
  * any pre-measured steps (a plain thrown Error loses them).
@@ -152,11 +216,10 @@ declare class TaskError extends Error {
152
216
  readonly code?: string;
153
217
  readonly data?: JsonObject;
154
218
  readonly steps?: TaskStepRecord[];
155
- constructor(message: string, opts?: {
156
- code?: string;
157
- data?: JsonObject;
158
- steps?: TaskStepRecord[];
159
- });
219
+ readonly step?: string;
220
+ readonly timeoutMs?: number;
221
+ constructor(message: string, opts?: TaskErrorOptions);
222
+ toString(): string;
160
223
  }
161
224
  /** Context handed to a benchmark `task` for a single iteration. */
162
225
  interface TaskContext<T extends BaseParticipant = BaseParticipant> {
@@ -167,12 +230,13 @@ interface TaskContext<T extends BaseParticipant = BaseParticipant> {
167
230
  /** Current phase name, when the benchmark declares `phases`. */
168
231
  phase?: string;
169
232
  /**
170
- * Runs `fn` as a named platform step. Mirrors `@benchsdk/client`'s
171
- * `RunWorkerContext.step`; supports closures and try/finally. A `concurrency`
233
+ * Runs `fn` as a named platform step. Mirrors `@benchsdk/worker`'s
234
+ * `RunWorkerContext.step`; supports closures and try/finally. `parallelInvocations`
172
235
  * greater than 1 invokes `fn` that many times in parallel and returns an array.
173
236
  * `timeoutMs` aborts any invocation that exceeds it with a `step_timeout` TaskError.
174
237
  */
175
238
  step<R, C extends number = 1>(name: string, fn: () => Promise<R> | R, options?: TaskStepOptions & {
239
+ parallelInvocations?: C;
176
240
  concurrency?: C;
177
241
  }): Promise<C extends 1 ? R : R[]>;
178
242
  /**
@@ -180,8 +244,11 @@ interface TaskContext<T extends BaseParticipant = BaseParticipant> {
180
244
  * that step's data; at task top-level it lands on the task record's data.
181
245
  */
182
246
  measure(data: JsonObject): void;
183
- /** Appends a line to the worker log, uploaded as an artifact when the worker finishes. */
184
- log(message: string, meta?: JsonObject): void;
247
+ /**
248
+ * Appends a line to the worker log, uploaded as an artifact when the worker finishes.
249
+ * `metaOrOptions` can be a metadata JSON object, or `{ level, meta }` to set a log level.
250
+ */
251
+ log(message: string, metaOrOptions?: JsonObject | BenchmarkLogOptions): void;
185
252
  }
186
253
  type BenchmarkTask<T extends BaseParticipant = BaseParticipant> = (ctx: TaskContext<T>) => Promise<TaskResult | void> | TaskResult | void;
187
254
  /**
@@ -203,6 +270,13 @@ interface ParticipantRecords {
203
270
  /** The orchestration knobs a run actually used, after CLI overrides. */
204
271
  interface ResolvedRunConfig {
205
272
  iterations: number;
273
+ /**
274
+ * Iterations each phase runs, when the benchmark declares `phases` and
275
+ * `--iterations` overrode their configured counts. A phase is one arm of a
276
+ * comparison (a file size), so the flag scales every arm equally rather than
277
+ * dividing a total between them.
278
+ */
279
+ phaseIterations?: number;
206
280
  concurrency: number;
207
281
  staggerDelayMs: number;
208
282
  groupBy: GroupBy;
@@ -270,6 +344,12 @@ interface BenchmarkConfig<T extends BaseParticipant = BaseParticipant> {
270
344
  defaultProviders?: string[];
271
345
  /** The participants this benchmark can run against. `--provider` selects a subset by name. */
272
346
  participants: T[];
347
+ /**
348
+ * Static run-level dimensions copied into the submitted summary (e.g.
349
+ * `{ file_size: '10MB' }`). Useful for distinguishing runs of the same
350
+ * benchmark that differ by an external parameter.
351
+ */
352
+ dimensions?: Record<string, unknown>;
273
353
  /**
274
354
  * Run-level scoring hook, called once with `lowerIsBetter` and `higherIsBetter`
275
355
  * primitives after the outcome is assembled but before `onComplete`. Use it to
@@ -282,6 +362,75 @@ interface BenchmarkConfig<T extends BaseParticipant = BaseParticipant> {
282
362
  * writers). This is the run-level counterpart to per-step `ctx.measure`.
283
363
  */
284
364
  onComplete?: (outcome: BenchmarkRunOutcome) => void | Promise<void>;
365
+ /**
366
+ * Serializable scoring spec uploaded to the platform. When provided without
367
+ * `onScore`, the runner computes the run summary from this spec automatically.
368
+ * The platform can recompute `compositeScore` from the same spec at read time.
369
+ */
370
+ scoring?: BenchmarkScoringConfig;
371
+ /**
372
+ * Custom CLI flags this benchmark reads from `process.argv` (e.g. `--file-size`).
373
+ * Declaring them lets the runner distinguish intentional pass-through flags
374
+ * from typos and report unknown flags accurately.
375
+ */
376
+ customCliFlags?: readonly string[];
377
+ /**
378
+ * Optional display manifest. Lets the bench author configure metric labels,
379
+ * step labels, and overview defaults without editing the platform.
380
+ */
381
+ display?: BenchmarkDisplayConfig;
382
+ }
383
+ /** Display metadata for a single custom metric a benchmark reports via `ctx.measure`. */
384
+ interface BenchmarkMetricDisplay {
385
+ /** Stable metric key, matching the key in `ctx.measure` or `data`. */
386
+ key: string;
387
+ /** Human-readable label shown in the platform UI. */
388
+ label: string;
389
+ /** Optional unit shown after the value (e.g. `Mbps`, `/s`, `ms`). */
390
+ unit?: string;
391
+ /** Number of decimal places when formatting numeric values. Defaults to the display format. */
392
+ decimals?: number;
393
+ /** Whether higher or lower values rank better. */
394
+ direction?: 'higher-better' | 'lower-better';
395
+ /** Optional ordering hint for metric lists. */
396
+ order?: number;
397
+ }
398
+ /** Display metadata for a single task lifecycle step. */
399
+ interface BenchmarkStepDisplay {
400
+ /** Stable step name, matching the string passed to `ctx.step`. */
401
+ key: string;
402
+ /** Human-readable label shown in the platform UI. */
403
+ label: string;
404
+ /** Optional ordering hint for step lists. */
405
+ order?: number;
406
+ }
407
+ /** Display defaults for the benchmark overview page. */
408
+ interface BenchmarkOverviewDisplay {
409
+ /** Metric key to rank participants by by default (falls back to overall task latency). */
410
+ defaultMetric?: string;
411
+ /** Default overview layout. */
412
+ defaultLayout?: 'ranking' | 'cards' | 'chart' | 'leaderboard';
413
+ }
414
+ /**
415
+ * Optional platform display manifest. A `*.bench.ts` file owns not only how the
416
+ * benchmark runs, but how it should be rendered, without a platform code change.
417
+ */
418
+ interface BenchmarkDisplayConfig {
419
+ /** Metric catalog — labels, units, and ranking direction for `ctx.measure` keys. */
420
+ metrics?: BenchmarkMetricDisplay[];
421
+ /** Step catalog — human labels for lifecycle steps reported via `ctx.step`. */
422
+ steps?: BenchmarkStepDisplay[];
423
+ /** Overview defaults. */
424
+ overview?: BenchmarkOverviewDisplay;
425
+ }
426
+ interface BenchmarkConfigErrorItem {
427
+ field: string;
428
+ message: string;
429
+ }
430
+ declare class BenchmarkConfigError extends Error {
431
+ readonly issues: BenchmarkConfigErrorItem[];
432
+ constructor(issues: BenchmarkConfigErrorItem[]);
433
+ private static formatIssues;
285
434
  }
286
435
  /** Validates `config` at file-evaluation time so mistakes surface immediately. */
287
436
  declare function defineBenchmarkConfig<T extends BaseParticipant = BaseParticipant>(config: BenchmarkConfig<T>): BenchmarkConfig<T>;
@@ -297,6 +446,16 @@ declare function defineBenchmarkConfig<T extends BaseParticipant = BaseParticipa
297
446
  * });
298
447
  */
299
448
  declare function defineTask<T extends BaseParticipant = BaseParticipant>(task: BenchmarkTask<T>): BenchmarkTask<T>;
449
+ /**
450
+ * Validates a `BenchmarkConfig` without throwing, returning a list of
451
+ * `{ field, message }` issues. Returns an empty array when the config is valid.
452
+ */
453
+ declare function validateBenchmarkConfig<T extends BaseParticipant = BaseParticipant>(config: BenchmarkConfig<T>): BenchmarkConfigErrorItem[];
454
+ /**
455
+ * Typed helper for `config.onComplete` callbacks. The body receives the full
456
+ * `BenchmarkRunOutcome` and can be sync or async.
457
+ */
458
+ declare function defineOnComplete(onComplete: (outcome: BenchmarkRunOutcome) => void | Promise<void>): (outcome: BenchmarkRunOutcome) => void | Promise<void>;
300
459
 
301
460
  /**
302
461
  * Thrown when every selected participant was env-gated out, i.e. none of their
@@ -336,13 +495,22 @@ interface CliArgs {
336
495
  noIngest?: boolean;
337
496
  }
338
497
  /**
339
- * Parses the orchestration flags this runner understands, ignoring anything
340
- * else. Supports both `--flag value` and `--flag=value`; `--provider` accepts
498
+ * Parses the orchestration flags this runner understands, rejecting unknown
499
+ * flags. Supports both `--flag value` and `--flag=value`; `--provider` accepts
341
500
  * a comma-separated list and may be repeated.
501
+ *
502
+ * `allowedCustomFlags` lists pass-through flags the benchmark file reads from
503
+ * `process.argv` itself; the runner validates and skips them without choking on
504
+ * their values.
342
505
  */
343
- declare function parseCliArgs(argv: string[]): CliArgs;
506
+ declare function parseCliArgs(argv: string[], allowedCustomFlags?: readonly string[]): CliArgs;
344
507
  /** Merges CLI overrides over config defaults, filling in knob fallbacks. */
345
508
  declare function mergeConfig<T extends BaseParticipant>(config: BenchmarkConfig<T>, args: CliArgs): ResolvedRunConfig;
509
+ interface PlatformConfig {
510
+ baseUrl?: string;
511
+ apiKey?: string;
512
+ }
513
+ type RunBenchmarkOptions = PlatformConfig;
346
514
  /**
347
515
  * Runs `config`'s `task` against its participants. Selects participants by
348
516
  * `--provider` (if given), env-gates them, then drives them per the resolved
@@ -352,7 +520,26 @@ declare function mergeConfig<T extends BaseParticipant>(config: BenchmarkConfig<
352
520
  * CI job per provider) get-or-create one shared run and each registers only its
353
521
  * own participants.
354
522
  */
355
- declare function runBenchmark<T extends BaseParticipant>(fileConfig: BenchmarkConfig<T>, task: BenchmarkTask<T>, argv?: string[]): Promise<BenchmarkRunOutcome>;
523
+ declare function runBenchmark<T extends BaseParticipant>(fileConfig: BenchmarkConfig<T>, task: BenchmarkTask<T>, argv?: string[], options?: RunBenchmarkOptions): Promise<BenchmarkRunOutcome>;
524
+ interface RunBenchmarkWorkerOptions<T extends BaseParticipant = BaseParticipant> {
525
+ benchmarkSlug: string;
526
+ benchmarkName?: string;
527
+ runKey?: string;
528
+ participant: T;
529
+ task: BenchmarkTask<T>;
530
+ iterations?: number;
531
+ concurrency?: number;
532
+ staggerDelayMs?: number;
533
+ groupBy?: GroupBy;
534
+ noIngest?: boolean;
535
+ }
536
+ /**
537
+ * One-shot helper: run a single participant's worker for a benchmark without
538
+ * creating a `*.bench.ts` file. This is a convenience wrapper around
539
+ * `runBenchmark` that builds a minimal `BenchmarkConfig` from the supplied
540
+ * options.
541
+ */
542
+ declare function runBenchmarkWorker<T extends BaseParticipant>(options: RunBenchmarkWorkerOptions<T>): Promise<BenchmarkRunOutcome>;
356
543
 
357
544
  /**
358
545
  * Dispatches one CLI invocation. Throws on bad usage / invalid exports and lets
@@ -360,7 +547,9 @@ declare function runBenchmark<T extends BaseParticipant>(fileConfig: BenchmarkCo
360
547
  * exit. Does not call `process.exit`.
361
548
  */
362
549
  declare function runBenchmarkFile(argv: string[]): Promise<void>;
363
- /** Executable entry: runs the file and maps outcomes to process exit codes. */
550
+ /** Executable entry: dispatches to benchmark execution or platform data commands. */
364
551
  declare function run(argv: string[]): Promise<void>;
365
552
 
366
- export { type BenchmarkConfig, type BenchmarkRunOutcome, type BenchmarkScoreResult, type BenchmarkTask, type CliArgs, type GroupBy, type MetricScoring, NoAvailableParticipantsError, type ParticipantRecords, type Phase, type ResolvedRunConfig, type ScoringSpec, type TaskContext, TaskError, type TaskResult, type TaskStepOptions, defineBenchmarkConfig, defineTask, higherIsBetter, lowerIsBetter, mergeConfig, parseCliArgs, run, runBenchmark, runBenchmarkFile, score };
553
+ declare const BENCHSDK_RUNNER_VERSION: string;
554
+
555
+ export { BENCHSDK_RUNNER_VERSION, type BenchmarkConfig, BenchmarkConfigError, type BenchmarkConfigErrorItem, type BenchmarkDisplayConfig, type BenchmarkMetricDisplay, type BenchmarkOverviewDisplay, type BenchmarkRunOutcome, type BenchmarkScoreResult, type BenchmarkScoringConfig, type BenchmarkScoringMetric, type BenchmarkScoringSuccess, type BenchmarkScoringWeights, type BenchmarkStepDisplay, type BenchmarkTask, type CliArgs, type GroupBy, type MetricScoring, NoAvailableParticipantsError, type ParticipantRecords, type Phase, type PlatformConfig, type ResolvedRunConfig, type RunBenchmarkOptions, type RunBenchmarkWorkerOptions, type ScoringSpec, ScoringSpecError, type TaskContext, TaskError, type TaskResult, type TaskStepOptions, defineBenchmarkConfig, defineOnComplete, defineTask, higherIsBetter, lowerIsBetter, mergeConfig, parseCliArgs, run, runBenchmark, runBenchmarkFile, runBenchmarkWorker, score, scoringConfigToSpec, validateBenchmarkConfig, validateScoringSpec };