@botpress/evals 2.0.1-beta.0 → 2.0.1-beta.1

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/runner.d.ts CHANGED
@@ -12,6 +12,20 @@ export declare function runEval(evalDef: EvalDefinition, connection: BotConnecti
12
12
  devServerUrl?: string;
13
13
  idleTimeout?: number;
14
14
  judgePassThreshold?: number;
15
+ /**
16
+ * Configure the LLM judge inside the runner's OWN graders module. A bundled embedder can hold a
17
+ * different copy of that module, so initializing it here guarantees the instance grading consults.
18
+ */
19
+ judge?: {
20
+ credentials: {
21
+ token: string;
22
+ apiUrl: string;
23
+ botId: string;
24
+ workspaceId?: string;
25
+ };
26
+ model?: string;
27
+ failClosed?: boolean;
28
+ };
15
29
  onProgress?: (event: EvalProgressEvent) => void | Promise<void>;
16
30
  evalIndex?: number;
17
31
  /** Forwarded from EvalRunnerConfig — lets per-turn workflow waits unblock immediately on Stop instead of burning the full idleTimeout each. */
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EAGV,aAAa,EAEd,MAAM,SAAS,CAAA;AAGhB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAoHrD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,aAAa,EACzB,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+IAA+I;IAC/I,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACzC,UAAU,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAC3C,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACnC,WAAW,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC7C,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;CAChB,GACL,OAAO,CAAC,UAAU,CAAC,CA+UrB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CA2GxG"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EAGV,aAAa,EAEd,MAAM,SAAS,CAAA;AAGhB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AA0HrD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,aAAa,EACzB,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;;;OAGG;IACH,KAAK,CAAC,EAAE;QACN,WAAW,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QACnF,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,UAAU,CAAC,EAAE,OAAO,CAAA;KACrB,CAAA;IACD,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+IAA+I;IAC/I,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACzC,UAAU,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAC3C,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACnC,WAAW,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IAC7C,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;CAChB,GACL,OAAO,CAAC,UAAU,CAAC,CAgWrB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CA2GxG"}
package/dist/runner.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/runner.ts
2
+ import { Client as BpClientCtor } from "@botpress/client";
3
+
1
4
  // src/definition.ts
2
5
  var Eval = class {
3
6
  name;
@@ -392,10 +395,11 @@ var SSECollector = class _SSECollector {
392
395
  throw new Error("waitForTurnComplete aborted");
393
396
  }
394
397
  return new Promise((resolve2, reject) => {
398
+ const expected = accept ? [...accept].join(" or ") : "handler.conversation or handler.event";
395
399
  const timer = setTimeout(() => {
400
+ clearStartTimer();
396
401
  this._removeWaiter(waiter);
397
402
  opts.abortSignal?.removeEventListener("abort", onAbort);
398
- const expected = accept ? [...accept].join(" or ") : "handler.conversation or handler.event";
399
403
  const reason = this.streamError ? `SSE stream died: ${this.streamError.message}` : `no ${expected} span completed`;
400
404
  reject(
401
405
  new Error(
@@ -403,8 +407,27 @@ var SSECollector = class _SSECollector {
403
407
  )
404
408
  );
405
409
  }, opts.timeout);
410
+ let startTimer;
411
+ const clearStartTimer = () => {
412
+ if (startTimer !== void 0) clearTimeout(startTimer);
413
+ startTimer = void 0;
414
+ };
415
+ if (opts.handlerStartTimeoutMs !== void 0 && opts.handlerStartTimeoutMs < opts.timeout) {
416
+ startTimer = setTimeout(() => {
417
+ if (this._hasNewHandlerSpan(accept)) return;
418
+ clearTimeout(timer);
419
+ this._removeWaiter(waiter);
420
+ opts.abortSignal?.removeEventListener("abort", onAbort);
421
+ reject(
422
+ new Error(
423
+ `no ${expected} span started within ${opts.handlerStartTimeoutMs}ms \u2014 no handler is subscribed to this turn's trigger, so the turn can never complete; diagnostics: ${this._formatTurnDiagnostics()}`
424
+ )
425
+ );
426
+ }, opts.handlerStartTimeoutMs);
427
+ }
406
428
  const onAbort = () => {
407
429
  clearTimeout(timer);
430
+ clearStartTimer();
408
431
  this._removeWaiter(waiter);
409
432
  reject(new Error("waitForTurnComplete aborted"));
410
433
  };
@@ -412,6 +435,7 @@ var SSECollector = class _SSECollector {
412
435
  const waiter = (_span) => {
413
436
  if (this._hasNewCompletedHandler(accept)) {
414
437
  clearTimeout(timer);
438
+ clearStartTimer();
415
439
  this._removeWaiter(waiter);
416
440
  opts.abortSignal?.removeEventListener("abort", onAbort);
417
441
  this._settleAfterHandler(settle).then(resolve2, reject);
@@ -713,6 +737,18 @@ ${value}` : value;
713
737
  }
714
738
  return false;
715
739
  }
740
+ /**
741
+ * Whether a turn-handler span has appeared since startTurn() — any status. Proves a handler was
742
+ * dispatched. Never consumes keys (unlike {@link _hasNewCompletedHandler}).
743
+ */
744
+ _hasNewHandlerSpan(acceptSpanNames) {
745
+ for (const [key, span] of this.spanMap) {
746
+ if (isTurnHandlerSpan(span) && (!acceptSpanNames || acceptSpanNames.has(span.name)) && !this.seenHandlerKeys.has(key)) {
747
+ return true;
748
+ }
749
+ }
750
+ return false;
751
+ }
716
752
  /**
717
753
  * Check if a handler.workflow span matches the given condition.
718
754
  */
@@ -1356,6 +1392,7 @@ import { randomUUID } from "crypto";
1356
1392
  var DEFAULT_IDLE_TIMEOUT = 3e4;
1357
1393
  var DEFAULT_DEV_SERVER_URL = "http://localhost:3001";
1358
1394
  var WORKFLOW_TRIGGER_TIMEOUT_MS = 5 * 60 * 1e3;
1395
+ var EVENT_HANDLER_START_TIMEOUT_MS = 3e4;
1359
1396
  var BUILT_IN_STATES = {
1360
1397
  conversation: "state",
1361
1398
  user: "userState",
@@ -1435,6 +1472,14 @@ async function runEval(evalDef, connection, options = {}) {
1435
1472
  const logger = options.logger ?? defaultLogger;
1436
1473
  const idleTimeout = evalDef.options?.idleTimeout ?? options.idleTimeout ?? DEFAULT_IDLE_TIMEOUT;
1437
1474
  const judgePassThreshold = evalDef.options?.judgePassThreshold ?? options.judgePassThreshold;
1475
+ if (options.judge) {
1476
+ const judgeClient = new BpClientCtor(options.judge.credentials);
1477
+ await initLLMJudge(judgeClient, {
1478
+ ...options.judge.model !== void 0 ? { model: options.judge.model } : {},
1479
+ ...options.judge.failClosed !== void 0 ? { failClosed: options.judge.failClosed } : {},
1480
+ logger
1481
+ });
1482
+ }
1438
1483
  const start = Date.now();
1439
1484
  const turns = [];
1440
1485
  let outcomeAssertions = [];
@@ -1526,6 +1571,8 @@ async function runEval(evalDef, connection, options = {}) {
1526
1571
  // can get it absorbed by their transcript saves and silently skipped.
1527
1572
  settleQuietMs: 1500,
1528
1573
  settleMaxMs: 15e3,
1574
+ // Event turns fail fast when nothing is subscribed (see the constant).
1575
+ ...turn.event ? { handlerStartTimeoutMs: EVENT_HANDLER_START_TIMEOUT_MS } : {},
1529
1576
  ...options.signal !== void 0 ? { abortSignal: options.signal } : {}
1530
1577
  });
1531
1578
  } catch (err) {
@@ -1545,8 +1592,13 @@ async function runEval(evalDef, connection, options = {}) {
1545
1592
  });
1546
1593
  }
1547
1594
  }
1548
- const turnSpans = collector.getTurnSpans();
1549
- const turnData = transformSpans(turnSpans);
1595
+ let turnData = transformSpans(collector.getTurnSpans());
1596
+ if (turn.event && !turn.expectSilence && turnData.messages.length === 0 && turnData.handlerDuration > 0) {
1597
+ for (let waited = 0; waited < 600 && turnData.messages.length === 0; waited += 150) {
1598
+ await new Promise((resolve2) => setTimeout(resolve2, 150));
1599
+ turnData = transformSpans(collector.getTurnSpans());
1600
+ }
1601
+ }
1550
1602
  const botResponse = turnData.messages.join("\n");
1551
1603
  const botDuration = turnData.handlerDuration;
1552
1604
  const evalStart = Date.now();