vieval 0.0.8 → 0.0.10

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.
Files changed (34) hide show
  1. package/README.md +51 -4
  2. package/dist/bin/vieval.mjs +1 -1
  3. package/dist/cli/index.mjs +1 -1
  4. package/dist/{cli-Dao25VxV.mjs → cli-DTDgaqeI.mjs} +669 -599
  5. package/dist/cli-DTDgaqeI.mjs.map +1 -0
  6. package/dist/config.d.mts +1 -1
  7. package/dist/core/assertions/index.d.mts +1 -1
  8. package/dist/core/inference-executors/index.d.mts +1 -1
  9. package/dist/core/inference-executors/index.mjs +10 -4
  10. package/dist/core/inference-executors/index.mjs.map +1 -1
  11. package/dist/core/processors/results/index.d.mts +1 -1
  12. package/dist/core/runner/index.d.mts +2 -2
  13. package/dist/core/runner/index.mjs +2 -2
  14. package/dist/core/scheduler/index.d.mts +1 -1
  15. package/dist/core/scheduler/index.mjs +2 -2
  16. package/dist/core/scheduler/index.mjs.map +1 -1
  17. package/dist/{env-BeHv_5mo.d.mts → env-DfWZy_n4.d.mts} +14 -9
  18. package/dist/env-nV5rVErX.mjs +35 -0
  19. package/dist/env-nV5rVErX.mjs.map +1 -0
  20. package/dist/{index-fakXoZEe.d.mts → index-Bg0atWBF.d.mts} +4 -3
  21. package/dist/{index-BkjyCInx.d.mts → index-D_aMeWqO.d.mts} +2 -2
  22. package/dist/index.d.mts +2 -2
  23. package/dist/index.mjs +21 -26
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/plugins/chat-models/index.d.mts +1 -1
  26. package/dist/plugins/chat-models/index.mjs +15 -13
  27. package/dist/plugins/chat-models/index.mjs.map +1 -1
  28. package/dist/{registry-BHGMxjpA.mjs → registry-DMnwE_mY.mjs} +54 -10
  29. package/dist/registry-DMnwE_mY.mjs.map +1 -0
  30. package/package.json +1 -1
  31. package/dist/cli-Dao25VxV.mjs.map +0 -1
  32. package/dist/env-BFSjny07.mjs +0 -41
  33. package/dist/env-BFSjny07.mjs.map +0 -1
  34. package/dist/registry-BHGMxjpA.mjs.map +0 -1
package/dist/config.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { C as TaskDefinition, D as TaskRunContext, E as TaskReporterHooks, O as TaskRunOutput, R as ModelDefinition, S as TaskConcurrencyConfig, T as TaskReporterEventPayload, _ as ScopedMatrices, a as CliOpenTelemetryReportingConfig, b as TaskCaseReporterPayload, c as EvalDefinition, d as MatrixAxisValues, f as MatrixDefinition, g as MatrixValue, h as MatrixRow, i as Awaitable, l as EvalModule, m as MatrixPrimitive, n as defineEval, o as CliReportingConfig, p as MatrixLayer, r as defineTask, s as CollectedEvalEntry, t as ConfigHookPlugin, u as EvalModuleMap, v as TaskAutoRetryDelay, w as TaskExecutionPolicy, x as TaskCaseState, y as TaskCaseReporterEndPayload, z as resolveModelByName } from "./index-BkjyCInx.mjs";
1
+ import { C as TaskDefinition, D as TaskRunContext, E as TaskReporterHooks, O as TaskRunOutput, R as ModelDefinition, S as TaskConcurrencyConfig, T as TaskReporterEventPayload, _ as ScopedMatrices, a as CliOpenTelemetryReportingConfig, b as TaskCaseReporterPayload, c as EvalDefinition, d as MatrixAxisValues, f as MatrixDefinition, g as MatrixValue, h as MatrixRow, i as Awaitable, l as EvalModule, m as MatrixPrimitive, n as defineEval, o as CliReportingConfig, p as MatrixLayer, r as defineTask, s as CollectedEvalEntry, t as ConfigHookPlugin, u as EvalModuleMap, v as TaskAutoRetryDelay, w as TaskExecutionPolicy, x as TaskCaseState, y as TaskCaseReporterEndPayload, z as resolveModelByName } from "./index-D_aMeWqO.mjs";
2
2
  export { Awaitable, CliOpenTelemetryReportingConfig, CliReportingConfig, CollectedEvalEntry, ConfigHookPlugin, EvalDefinition, EvalModule, EvalModuleMap, MatrixAxisValues, MatrixDefinition, MatrixLayer, MatrixPrimitive, MatrixRow, MatrixValue, ModelDefinition, ScopedMatrices, TaskAutoRetryDelay, TaskCaseReporterEndPayload, TaskCaseReporterPayload, TaskCaseState, TaskConcurrencyConfig, TaskDefinition, TaskExecutionPolicy, TaskReporterEventPayload, TaskReporterHooks, TaskRunContext, TaskRunOutput, defineEval, defineTask, resolveModelByName };
@@ -1,4 +1,4 @@
1
- import { X as RunScoreKind, Y as RunScore } from "../../index-BkjyCInx.mjs";
1
+ import { X as RunScoreKind, Y as RunScore } from "../../index-D_aMeWqO.mjs";
2
2
 
3
3
  //#region src/core/assertions/index.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { a as requiredEnvFrom, i as envFrom, n as EnvValueType, r as RequiredEnvFromOptions, t as EnvFromOptions } from "../../env-BeHv_5mo.mjs";
1
+ import { a as requiredEnvFrom, i as envFrom, n as EnvValueType, r as RequiredEnvFromOptions, t as EnvFromOptions } from "../../env-DfWZy_n4.mjs";
2
2
  import { createOpenAI } from "@xsai-ext/providers/create";
3
3
 
4
4
  //#region src/core/inference-executors/retry-policy.d.ts
@@ -1,4 +1,4 @@
1
- import { n as requiredEnvFrom, t as envFrom } from "../../env-BFSjny07.mjs";
1
+ import { n as requiredEnvFrom, t as envFrom } from "../../env-nV5rVErX.mjs";
2
2
  import process from "node:process";
3
3
  import { errorMessageFrom, errorNameFrom, sleep } from "@moeru/std";
4
4
  import { createOpenAI } from "@xsai-ext/providers/create";
@@ -150,15 +150,21 @@ function createOpenAIFromEnv(source = {}, defaults = {}) {
150
150
  const apiKeyEnvKey = source.apiKey ?? "OPENAI_API_KEY";
151
151
  const baseURLEnvKey = source.baseURL ?? "OPENAI_BASE_URL";
152
152
  const modelEnvKey = source.model ?? "OPENAI_MODEL";
153
- const apiKey = requiredEnvFrom(env[apiKeyEnvKey] ?? defaults.apiKey, {
153
+ const envWithDefaults = {
154
+ ...defaults.apiKey == null ? {} : { [apiKeyEnvKey]: defaults.apiKey },
155
+ ...defaults.baseURL == null ? {} : { [baseURLEnvKey]: defaults.baseURL },
156
+ ...defaults.model == null ? {} : { [modelEnvKey]: defaults.model },
157
+ ...env
158
+ };
159
+ const apiKey = requiredEnvFrom(envWithDefaults, {
154
160
  name: apiKeyEnvKey,
155
161
  type: "string"
156
162
  });
157
- const model = requiredEnvFrom(env[modelEnvKey] ?? defaults.model, {
163
+ const model = requiredEnvFrom(envWithDefaults, {
158
164
  name: modelEnvKey,
159
165
  type: "string"
160
166
  });
161
- const baseURL = envFrom(env[baseURLEnvKey] ?? defaults.baseURL, {
167
+ const baseURL = envFrom(envWithDefaults, {
162
168
  name: baseURLEnvKey,
163
169
  type: "string"
164
170
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["defaultSleep"],"sources":["../../../src/core/inference-executors/retry-policy.ts","../../../src/core/inference-executors/adapters.ts","../../../src/core/inference-executors/remote-providers/openai/index.ts"],"sourcesContent":["import { sleep as defaultSleep, errorMessageFrom, errorNameFrom } from '@moeru/std'\n\n/**\n * Describes how provider retries should behave.\n *\n * ASCII flow:\n * attempt -> run request -> success return\n * attempt -> run request -> retriable failure -> sleep -> next attempt\n * attempt -> run request -> non-retriable failure -> throw\n */\nexport interface RetryPolicy {\n /**\n * Maximum number of total attempts, including the first try.\n */\n maxAttempts: number\n /**\n * Returns the wait time for a retry attempt.\n */\n delayMs: (attempt: number) => number\n /**\n * Determines whether an error can be retried safely.\n */\n shouldRetry: (error: unknown) => boolean\n /**\n * Suspends execution between retries.\n */\n sleep: (milliseconds: number) => Promise<void>\n}\n\n/**\n * Configures a retry policy before a provider call is executed.\n *\n * Use when:\n * - you want the default retry classifier but need to tune attempts or delay\n * - you need to replace the sleeper in tests\n *\n * Expects:\n * - `maxAttempts` to be a finite integer greater than or equal to `1`\n * - `delayMs` to return a non-negative wait time in milliseconds\n */\nexport interface RetryPolicyOptions {\n /**\n * Maximum total attempts, including the first request.\n *\n * @default 3\n */\n maxAttempts?: number\n /**\n * Computes the delay for a retry attempt.\n *\n * The attempt number starts at `1` for the first retry.\n */\n delayMs?: (attempt: number) => number\n /**\n * Overrides the retry classifier.\n */\n shouldRetry?: (error: unknown) => boolean\n /**\n * Overrides the sleeper used between attempts.\n */\n sleep?: (milliseconds: number) => Promise<void>\n}\n\nconst retryableStatusCodes = new Set([408, 425, 429, 500, 502, 503, 504])\nconst retryableErrorNames = new Set(['TimeoutError', 'FetchError'])\nconst retryableMessagePatterns = [\n /rate limit/i,\n /rate-limited/i,\n /temporarily unavailable/i,\n /service unavailable/i,\n /server error/i,\n /fetch failed/i,\n /network error/i,\n /socket hang up/i,\n /econnreset/i,\n /econnrefused/i,\n /eai_again/i,\n /etimedout/i,\n /timed out/i,\n /timeout/i,\n]\n\nfunction getStatusCode(error: unknown): number | undefined {\n if (error == null || typeof error !== 'object') {\n return undefined\n }\n\n const maybeStatusCode = (error as { statusCode?: unknown }).statusCode\n if (typeof maybeStatusCode === 'number') {\n return maybeStatusCode\n }\n\n const maybeStatus = (error as { status?: unknown }).status\n if (typeof maybeStatus === 'number') {\n return maybeStatus\n }\n\n const response = (error as { response?: unknown }).response\n if (response == null || typeof response !== 'object') {\n return undefined\n }\n\n const responseStatus = (response as { status?: unknown }).status\n return typeof responseStatus === 'number' ? responseStatus : undefined\n}\n\n/**\n * Returns true when a provider failure is temporary and a retry is reasonable.\n *\n * Use when:\n * - the upstream failure is a transport problem or a 5xx/429 response\n *\n * Expects:\n * - provider errors to expose a status code, name, or message when possible\n */\nexport function isRetriableProviderError(error: unknown): boolean {\n const statusCode = getStatusCode(error)\n\n if (statusCode != null) {\n return retryableStatusCodes.has(statusCode)\n }\n\n const errorName = errorNameFrom(error)\n if (errorName != null && retryableErrorNames.has(errorName)) {\n return true\n }\n\n const errorMessage = errorMessageFrom(error)\n if (errorMessage == null) {\n return false\n }\n\n return retryableMessagePatterns.some(pattern => pattern.test(errorMessage))\n}\n\nfunction defaultDelayMs(attempt: number): number {\n return 500 * 2 ** (attempt - 1)\n}\n\n/**\n * Creates a retry policy for provider work.\n *\n * Use when:\n * - you need a reusable retry runner for eval-time provider calls\n * - you want to keep retry behavior deterministic in tests\n *\n * Expects:\n * - callers to treat `maxAttempts` as total attempts, not retries\n *\n * Throws:\n * - `RangeError` when `maxAttempts` is not a finite integer greater than or equal to `1`\n */\nfunction assertValidMaxAttempts(value: number): number {\n if (!Number.isFinite(value) || !Number.isInteger(value) || value < 1) {\n throw new RangeError('maxAttempts must be a finite integer greater than or equal to 1.')\n }\n\n return value\n}\n\nexport function createRetryPolicy(options: RetryPolicyOptions = {}): RetryPolicy {\n const maxAttempts = assertValidMaxAttempts(options.maxAttempts ?? 3)\n\n return {\n maxAttempts,\n delayMs: options.delayMs ?? defaultDelayMs,\n shouldRetry: options.shouldRetry ?? isRetriableProviderError,\n sleep: options.sleep ?? defaultSleep,\n }\n}\n\n/**\n * Runs an operation with bounded retries.\n *\n * Use when:\n * - you are calling an LLM provider or other temporary upstream dependency\n * - non-retriable failures should bubble immediately\n *\n * Expects:\n * - the operation to be idempotent across attempts\n */\nexport async function runWithRetry<T>(operation: () => Promise<T>, policy: RetryPolicy = createRetryPolicy()): Promise<T> {\n for (let attempt = 1; attempt <= policy.maxAttempts; attempt += 1) {\n try {\n return await operation()\n }\n catch (error) {\n if (attempt >= policy.maxAttempts || !policy.shouldRetry(error)) {\n throw error\n }\n\n const delayMilliseconds = policy.delayMs(attempt)\n if (delayMilliseconds > 0) {\n await policy.sleep(delayMilliseconds)\n }\n }\n }\n\n throw new Error('Retry loop exited without returning a value.')\n}\n","import type { RetryPolicy, RetryPolicyOptions } from './retry-policy'\n\nimport { createRetryPolicy, runWithRetry } from './retry-policy'\n\n/**\n * Bundles a provider with the retry policy used to call it.\n *\n * Use when:\n * - a provider instance should travel with the retry runner that governs it\n * - you want call sites to share one retry configuration object\n */\nexport interface ProviderAdapter<TProvider> {\n /**\n * The underlying provider instance.\n */\n provider: TProvider\n /**\n * The retry policy used for provider calls.\n */\n retryPolicy: RetryPolicy\n /**\n * Runs a provider-dependent operation with the adapter retry policy.\n */\n runWithRetry: <TResult>(operation: () => Promise<TResult>) => Promise<TResult>\n}\n\n/**\n * Creates a provider adapter with the default retry policy.\n *\n * Use when:\n * - you have a provider instance and want a consistent retry wrapper\n *\n * Expects:\n * - the provider to be safe to reuse across attempts\n */\nexport function createProviderAdapter<TProvider>(provider: TProvider, options: RetryPolicyOptions = {}): ProviderAdapter<TProvider> {\n const retryPolicy = createRetryPolicy(options)\n\n return {\n provider,\n retryPolicy,\n runWithRetry: operation => runWithRetry(operation, retryPolicy),\n }\n}\n","import type { ProviderAdapter } from '../../adapters'\nimport type { RetryPolicyOptions } from '../../retry-policy'\n\nimport process from 'node:process'\n\nimport { createOpenAI } from '@xsai-ext/providers/create'\n\nimport { createProviderAdapter } from '../../adapters'\nimport { envFrom, requiredEnvFrom } from '../../env'\n\n/**\n * Represents the OpenAI provider instance returned by xsai.\n */\nexport type OpenAIProvider = ReturnType<typeof createOpenAI>\n\n/**\n * Represents the OpenAI adapter used by vieval.\n */\nexport type OpenAIProviderAdapter = ProviderAdapter<OpenAIProvider>\n\n/**\n * Configures env key names and source for OpenAI provider setup.\n */\nexport interface OpenAIEnvSourceOptions {\n /**\n * Environment object used for variable lookup.\n *\n * @default process.env\n */\n env?: NodeJS.ProcessEnv\n /**\n * Env key name for API key.\n *\n * @default 'OPENAI_API_KEY'\n */\n apiKey?: string\n /**\n * Env key name for base URL.\n *\n * @default 'OPENAI_BASE_URL'\n */\n baseURL?: string\n /**\n * Env key name for model.\n *\n * @default 'OPENAI_MODEL'\n */\n model?: string\n}\n\n/**\n * Configures fallback defaults when env values are missing.\n */\nexport interface OpenAIFromEnvDefaultOptions {\n /**\n * API key fallback value.\n */\n apiKey?: string\n /**\n * Base URL fallback value.\n */\n baseURL?: string\n /**\n * Model fallback value.\n */\n model?: string\n /**\n * Retry policy override passed to provider adapter.\n */\n retryOptions?: RetryPolicyOptions\n}\n\n/**\n * Result produced by `createOpenAIFromEnv`.\n */\nexport interface OpenAIFromEnvResult {\n adapter: OpenAIProviderAdapter\n apiKey: string\n baseURL?: string\n model: string\n}\n\n/**\n * Minimal response shape returned by text-generation calls.\n */\nexport interface OpenAITextGenerationResult {\n /**\n * Text output from the provider.\n *\n * Some OpenAI-compatible implementations may return `null`.\n */\n text?: string | null\n}\n\n/**\n * Normalizes provider text output to a safe string.\n *\n * Before: `{ text: null }`\n * After: `''`\n *\n * Before: `{ text: 'hello' }`\n * After: `'hello'`\n */\nexport function normalizeOpenAITextOutput(result: OpenAITextGenerationResult): string {\n return typeof result.text === 'string' ? result.text : ''\n}\n\n/**\n * Creates an OpenAI provider adapter using environment variables with defaults.\n *\n * Example:\n * `const runtime = createOpenAIFromEnv({}, { model: 'gpt-4.1-mini' })`\n */\nexport function createOpenAIFromEnv(\n source: OpenAIEnvSourceOptions = {},\n defaults: OpenAIFromEnvDefaultOptions = {},\n): OpenAIFromEnvResult {\n const env = source.env ?? process.env\n const apiKeyEnvKey = source.apiKey ?? 'OPENAI_API_KEY'\n const baseURLEnvKey = source.baseURL ?? 'OPENAI_BASE_URL'\n const modelEnvKey = source.model ?? 'OPENAI_MODEL'\n\n const apiKey = requiredEnvFrom(env[apiKeyEnvKey] ?? defaults.apiKey, {\n name: apiKeyEnvKey,\n type: 'string',\n })\n const model = requiredEnvFrom(env[modelEnvKey] ?? defaults.model, {\n name: modelEnvKey,\n type: 'string',\n })\n const baseURL = envFrom(env[baseURLEnvKey] ?? defaults.baseURL, {\n name: baseURLEnvKey,\n type: 'string',\n })\n const adapter = createOpenAIProviderAdapter(apiKey, baseURL, defaults.retryOptions)\n\n return {\n adapter,\n apiKey,\n baseURL,\n model,\n }\n}\n\n/**\n * Creates an OpenAI provider adapter for eval-time requests.\n *\n * Use when:\n * - an eval needs the OpenAI SDK surface plus the shared retry runner\n *\n * Expects:\n * - `apiKey` and `baseURL` to point at an OpenAI-compatible endpoint\n * - `retryOptions` to follow the same invariants as `createRetryPolicy`\n */\nexport function createOpenAIProviderAdapter(apiKey: string, baseURL?: string, retryOptions: RetryPolicyOptions = {}): OpenAIProviderAdapter {\n return createProviderAdapter(createOpenAI(apiKey, baseURL), retryOptions)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["defaultSleep"],"sources":["../../../src/core/inference-executors/retry-policy.ts","../../../src/core/inference-executors/adapters.ts","../../../src/core/inference-executors/remote-providers/openai/index.ts"],"sourcesContent":["import { sleep as defaultSleep, errorMessageFrom, errorNameFrom } from '@moeru/std'\n\n/**\n * Describes how provider retries should behave.\n *\n * ASCII flow:\n * attempt -> run request -> success return\n * attempt -> run request -> retriable failure -> sleep -> next attempt\n * attempt -> run request -> non-retriable failure -> throw\n */\nexport interface RetryPolicy {\n /**\n * Maximum number of total attempts, including the first try.\n */\n maxAttempts: number\n /**\n * Returns the wait time for a retry attempt.\n */\n delayMs: (attempt: number) => number\n /**\n * Determines whether an error can be retried safely.\n */\n shouldRetry: (error: unknown) => boolean\n /**\n * Suspends execution between retries.\n */\n sleep: (milliseconds: number) => Promise<void>\n}\n\n/**\n * Configures a retry policy before a provider call is executed.\n *\n * Use when:\n * - you want the default retry classifier but need to tune attempts or delay\n * - you need to replace the sleeper in tests\n *\n * Expects:\n * - `maxAttempts` to be a finite integer greater than or equal to `1`\n * - `delayMs` to return a non-negative wait time in milliseconds\n */\nexport interface RetryPolicyOptions {\n /**\n * Maximum total attempts, including the first request.\n *\n * @default 3\n */\n maxAttempts?: number\n /**\n * Computes the delay for a retry attempt.\n *\n * The attempt number starts at `1` for the first retry.\n */\n delayMs?: (attempt: number) => number\n /**\n * Overrides the retry classifier.\n */\n shouldRetry?: (error: unknown) => boolean\n /**\n * Overrides the sleeper used between attempts.\n */\n sleep?: (milliseconds: number) => Promise<void>\n}\n\nconst retryableStatusCodes = new Set([408, 425, 429, 500, 502, 503, 504])\nconst retryableErrorNames = new Set(['TimeoutError', 'FetchError'])\nconst retryableMessagePatterns = [\n /rate limit/i,\n /rate-limited/i,\n /temporarily unavailable/i,\n /service unavailable/i,\n /server error/i,\n /fetch failed/i,\n /network error/i,\n /socket hang up/i,\n /econnreset/i,\n /econnrefused/i,\n /eai_again/i,\n /etimedout/i,\n /timed out/i,\n /timeout/i,\n]\n\nfunction getStatusCode(error: unknown): number | undefined {\n if (error == null || typeof error !== 'object') {\n return undefined\n }\n\n const maybeStatusCode = (error as { statusCode?: unknown }).statusCode\n if (typeof maybeStatusCode === 'number') {\n return maybeStatusCode\n }\n\n const maybeStatus = (error as { status?: unknown }).status\n if (typeof maybeStatus === 'number') {\n return maybeStatus\n }\n\n const response = (error as { response?: unknown }).response\n if (response == null || typeof response !== 'object') {\n return undefined\n }\n\n const responseStatus = (response as { status?: unknown }).status\n return typeof responseStatus === 'number' ? responseStatus : undefined\n}\n\n/**\n * Returns true when a provider failure is temporary and a retry is reasonable.\n *\n * Use when:\n * - the upstream failure is a transport problem or a 5xx/429 response\n *\n * Expects:\n * - provider errors to expose a status code, name, or message when possible\n */\nexport function isRetriableProviderError(error: unknown): boolean {\n const statusCode = getStatusCode(error)\n\n if (statusCode != null) {\n return retryableStatusCodes.has(statusCode)\n }\n\n const errorName = errorNameFrom(error)\n if (errorName != null && retryableErrorNames.has(errorName)) {\n return true\n }\n\n const errorMessage = errorMessageFrom(error)\n if (errorMessage == null) {\n return false\n }\n\n return retryableMessagePatterns.some(pattern => pattern.test(errorMessage))\n}\n\nfunction defaultDelayMs(attempt: number): number {\n return 500 * 2 ** (attempt - 1)\n}\n\n/**\n * Creates a retry policy for provider work.\n *\n * Use when:\n * - you need a reusable retry runner for eval-time provider calls\n * - you want to keep retry behavior deterministic in tests\n *\n * Expects:\n * - callers to treat `maxAttempts` as total attempts, not retries\n *\n * Throws:\n * - `RangeError` when `maxAttempts` is not a finite integer greater than or equal to `1`\n */\nfunction assertValidMaxAttempts(value: number): number {\n if (!Number.isFinite(value) || !Number.isInteger(value) || value < 1) {\n throw new RangeError('maxAttempts must be a finite integer greater than or equal to 1.')\n }\n\n return value\n}\n\nexport function createRetryPolicy(options: RetryPolicyOptions = {}): RetryPolicy {\n const maxAttempts = assertValidMaxAttempts(options.maxAttempts ?? 3)\n\n return {\n maxAttempts,\n delayMs: options.delayMs ?? defaultDelayMs,\n shouldRetry: options.shouldRetry ?? isRetriableProviderError,\n sleep: options.sleep ?? defaultSleep,\n }\n}\n\n/**\n * Runs an operation with bounded retries.\n *\n * Use when:\n * - you are calling an LLM provider or other temporary upstream dependency\n * - non-retriable failures should bubble immediately\n *\n * Expects:\n * - the operation to be idempotent across attempts\n */\nexport async function runWithRetry<T>(operation: () => Promise<T>, policy: RetryPolicy = createRetryPolicy()): Promise<T> {\n for (let attempt = 1; attempt <= policy.maxAttempts; attempt += 1) {\n try {\n return await operation()\n }\n catch (error) {\n if (attempt >= policy.maxAttempts || !policy.shouldRetry(error)) {\n throw error\n }\n\n const delayMilliseconds = policy.delayMs(attempt)\n if (delayMilliseconds > 0) {\n await policy.sleep(delayMilliseconds)\n }\n }\n }\n\n throw new Error('Retry loop exited without returning a value.')\n}\n","import type { RetryPolicy, RetryPolicyOptions } from './retry-policy'\n\nimport { createRetryPolicy, runWithRetry } from './retry-policy'\n\n/**\n * Bundles a provider with the retry policy used to call it.\n *\n * Use when:\n * - a provider instance should travel with the retry runner that governs it\n * - you want call sites to share one retry configuration object\n */\nexport interface ProviderAdapter<TProvider> {\n /**\n * The underlying provider instance.\n */\n provider: TProvider\n /**\n * The retry policy used for provider calls.\n */\n retryPolicy: RetryPolicy\n /**\n * Runs a provider-dependent operation with the adapter retry policy.\n */\n runWithRetry: <TResult>(operation: () => Promise<TResult>) => Promise<TResult>\n}\n\n/**\n * Creates a provider adapter with the default retry policy.\n *\n * Use when:\n * - you have a provider instance and want a consistent retry wrapper\n *\n * Expects:\n * - the provider to be safe to reuse across attempts\n */\nexport function createProviderAdapter<TProvider>(provider: TProvider, options: RetryPolicyOptions = {}): ProviderAdapter<TProvider> {\n const retryPolicy = createRetryPolicy(options)\n\n return {\n provider,\n retryPolicy,\n runWithRetry: operation => runWithRetry(operation, retryPolicy),\n }\n}\n","import type { ProviderAdapter } from '../../adapters'\nimport type { RetryPolicyOptions } from '../../retry-policy'\n\nimport process from 'node:process'\n\nimport { createOpenAI } from '@xsai-ext/providers/create'\n\nimport { createProviderAdapter } from '../../adapters'\nimport { envFrom, requiredEnvFrom } from '../../env'\n\n/**\n * Represents the OpenAI provider instance returned by xsai.\n */\nexport type OpenAIProvider = ReturnType<typeof createOpenAI>\n\n/**\n * Represents the OpenAI adapter used by vieval.\n */\nexport type OpenAIProviderAdapter = ProviderAdapter<OpenAIProvider>\n\n/**\n * Configures env key names and source for OpenAI provider setup.\n */\nexport interface OpenAIEnvSourceOptions {\n /**\n * Environment object used for variable lookup.\n *\n * @default process.env\n */\n env?: NodeJS.ProcessEnv\n /**\n * Env key name for API key.\n *\n * @default 'OPENAI_API_KEY'\n */\n apiKey?: string\n /**\n * Env key name for base URL.\n *\n * @default 'OPENAI_BASE_URL'\n */\n baseURL?: string\n /**\n * Env key name for model.\n *\n * @default 'OPENAI_MODEL'\n */\n model?: string\n}\n\n/**\n * Configures fallback defaults when env values are missing.\n */\nexport interface OpenAIFromEnvDefaultOptions {\n /**\n * API key fallback value.\n */\n apiKey?: string\n /**\n * Base URL fallback value.\n */\n baseURL?: string\n /**\n * Model fallback value.\n */\n model?: string\n /**\n * Retry policy override passed to provider adapter.\n */\n retryOptions?: RetryPolicyOptions\n}\n\n/**\n * Result produced by `createOpenAIFromEnv`.\n */\nexport interface OpenAIFromEnvResult {\n adapter: OpenAIProviderAdapter\n apiKey: string\n baseURL?: string\n model: string\n}\n\n/**\n * Minimal response shape returned by text-generation calls.\n */\nexport interface OpenAITextGenerationResult {\n /**\n * Text output from the provider.\n *\n * Some OpenAI-compatible implementations may return `null`.\n */\n text?: string | null\n}\n\n/**\n * Normalizes provider text output to a safe string.\n *\n * Before: `{ text: null }`\n * After: `''`\n *\n * Before: `{ text: 'hello' }`\n * After: `'hello'`\n */\nexport function normalizeOpenAITextOutput(result: OpenAITextGenerationResult): string {\n return typeof result.text === 'string' ? result.text : ''\n}\n\n/**\n * Creates an OpenAI provider adapter using environment variables with defaults.\n *\n * Example:\n * `const runtime = createOpenAIFromEnv({}, { model: 'gpt-4.1-mini' })`\n */\nexport function createOpenAIFromEnv(\n source: OpenAIEnvSourceOptions = {},\n defaults: OpenAIFromEnvDefaultOptions = {},\n): OpenAIFromEnvResult {\n const env = source.env ?? process.env\n const apiKeyEnvKey = source.apiKey ?? 'OPENAI_API_KEY'\n const baseURLEnvKey = source.baseURL ?? 'OPENAI_BASE_URL'\n const modelEnvKey = source.model ?? 'OPENAI_MODEL'\n\n const envWithDefaults = {\n ...(defaults.apiKey == null ? {} : { [apiKeyEnvKey]: defaults.apiKey }),\n ...(defaults.baseURL == null ? {} : { [baseURLEnvKey]: defaults.baseURL }),\n ...(defaults.model == null ? {} : { [modelEnvKey]: defaults.model }),\n ...env,\n }\n\n const apiKey = requiredEnvFrom(envWithDefaults, {\n name: apiKeyEnvKey,\n type: 'string',\n })\n const model = requiredEnvFrom(envWithDefaults, {\n name: modelEnvKey,\n type: 'string',\n })\n const baseURL = envFrom(envWithDefaults, {\n name: baseURLEnvKey,\n type: 'string',\n })\n const adapter = createOpenAIProviderAdapter(apiKey, baseURL, defaults.retryOptions)\n\n return {\n adapter,\n apiKey,\n baseURL,\n model,\n }\n}\n\n/**\n * Creates an OpenAI provider adapter for eval-time requests.\n *\n * Use when:\n * - an eval needs the OpenAI SDK surface plus the shared retry runner\n *\n * Expects:\n * - `apiKey` and `baseURL` to point at an OpenAI-compatible endpoint\n * - `retryOptions` to follow the same invariants as `createRetryPolicy`\n */\nexport function createOpenAIProviderAdapter(apiKey: string, baseURL?: string, retryOptions: RetryPolicyOptions = {}): OpenAIProviderAdapter {\n return createProviderAdapter(createOpenAI(apiKey, baseURL), retryOptions)\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { K as AggregatedRunResults } from "../../../index-BkjyCInx.mjs";
1
+ import { K as AggregatedRunResults } from "../../../index-D_aMeWqO.mjs";
2
2
 
3
3
  //#region src/core/processors/results/policies/hybrid-threshold.d.ts
4
4
  /**
@@ -1,3 +1,3 @@
1
- import { $ as InferenceExecutor, A as RunScheduledTasksOptions, B as asProjectRelativePath, F as CreateTaskExecutionContextOptions, G as AggregatedProviderSummary, H as CreateVievalRunnerRuntimeContextOptions, I as TaskExecutionContext, J as RunResult, K as AggregatedRunResults, L as createTaskExecutionContext, M as RunnerTaskState, N as ScheduledTaskExecutor, P as runScheduledTasks, Q as CreateRunnerScheduleOptions, U as RunnerRuntimeContext, V as collectEvalEntries, W as createRunnerRuntimeContext, X as RunScoreKind, Y as RunScore, Z as aggregateRunResults, at as ScheduledTaskMatrixMeta, ct as createFilesystemTaskCacheRuntime, dt as CacheFileOptions, et as RunnerMatrixDefinition, ft as CacheNamespace, it as ScheduledTaskMatrix, j as RunnerExecutionError, lt as normalizeCacheFilePathSegments, nt as RunnerMatrixSelection, ot as createRunnerSchedule, pt as TaskCacheRuntime, q as AggregatedRunSummary, rt as ScheduledTask, st as CreateFilesystemTaskCacheRuntimeOptions, tt as RunnerMatrixInput, ut as CacheFileHandle } from "../../index-BkjyCInx.mjs";
2
- import { a as SchedulerMiddleware, c as SchedulerScopeContext, i as SchedulerConcurrencyConfig, n as getActiveScopes, o as SchedulerRuntime, r as CreateSchedulerRuntimeOptions, s as SchedulerScope, t as createSchedulerRuntime } from "../../index-fakXoZEe.mjs";
1
+ import { $ as InferenceExecutor, A as RunScheduledTasksOptions, B as asProjectRelativePath, F as CreateTaskExecutionContextOptions, G as AggregatedProviderSummary, H as CreateVievalRunnerRuntimeContextOptions, I as TaskExecutionContext, J as RunResult, K as AggregatedRunResults, L as createTaskExecutionContext, M as RunnerTaskState, N as ScheduledTaskExecutor, P as runScheduledTasks, Q as CreateRunnerScheduleOptions, U as RunnerRuntimeContext, V as collectEvalEntries, W as createRunnerRuntimeContext, X as RunScoreKind, Y as RunScore, Z as aggregateRunResults, at as ScheduledTaskMatrixMeta, ct as createFilesystemTaskCacheRuntime, dt as CacheFileOptions, et as RunnerMatrixDefinition, ft as CacheNamespace, it as ScheduledTaskMatrix, j as RunnerExecutionError, lt as normalizeCacheFilePathSegments, nt as RunnerMatrixSelection, ot as createRunnerSchedule, pt as TaskCacheRuntime, q as AggregatedRunSummary, rt as ScheduledTask, st as CreateFilesystemTaskCacheRuntimeOptions, tt as RunnerMatrixInput, ut as CacheFileHandle } from "../../index-D_aMeWqO.mjs";
2
+ import { a as SchedulerMiddleware, c as SchedulerScopeContext, i as SchedulerConcurrencyConfig, n as getActiveScopes, o as SchedulerRuntime, r as CreateSchedulerRuntimeOptions, s as SchedulerScope, t as createSchedulerRuntime } from "../../index-Bg0atWBF.mjs";
3
3
  export { AggregatedProviderSummary, AggregatedRunResults, AggregatedRunSummary, CacheFileHandle, CacheFileOptions, CacheNamespace, CreateFilesystemTaskCacheRuntimeOptions, CreateRunnerScheduleOptions, CreateSchedulerRuntimeOptions, CreateTaskExecutionContextOptions, CreateVievalRunnerRuntimeContextOptions, InferenceExecutor, RunResult, RunScheduledTasksOptions, RunScore, RunScoreKind, RunnerExecutionError, RunnerMatrixDefinition, RunnerMatrixInput, RunnerMatrixSelection, RunnerRuntimeContext, RunnerTaskState, ScheduledTask, ScheduledTaskExecutor, ScheduledTaskMatrix, ScheduledTaskMatrixMeta, SchedulerConcurrencyConfig, SchedulerMiddleware, SchedulerRuntime, SchedulerScope, SchedulerScopeContext, TaskCacheRuntime, TaskExecutionContext, aggregateRunResults, asProjectRelativePath, collectEvalEntries, createFilesystemTaskCacheRuntime, createRunnerRuntimeContext, createRunnerSchedule, createSchedulerRuntime, createTaskExecutionContext, getActiveScopes, normalizeCacheFilePathSegments, runScheduledTasks };
@@ -2,11 +2,11 @@ import { createSchedulerRuntime, getActiveScopes } from "../scheduler/index.mjs"
2
2
  import { createRequire } from "node:module";
3
3
  import process from "node:process";
4
4
  import { errorMessageFrom } from "@moeru/std";
5
- import { basename, dirname, join, relative } from "node:path";
6
5
  import { access, mkdir, readFile, rename, writeFile } from "node:fs/promises";
6
+ import { basename, dirname, join, relative } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
- import { Buffer } from "node:buffer";
9
8
  import { createReadStream, createWriteStream } from "node:fs";
9
+ import { Buffer } from "node:buffer";
10
10
  import { limitConcurrency } from "@vitest/runner/utils";
11
11
  //#region src/core/cache/filesystem.ts
12
12
  function sanitizePathSegment(value) {
@@ -1,2 +1,2 @@
1
- import { a as SchedulerMiddleware, c as SchedulerScopeContext, i as SchedulerConcurrencyConfig, n as getActiveScopes, o as SchedulerRuntime, r as CreateSchedulerRuntimeOptions, s as SchedulerScope, t as createSchedulerRuntime } from "../../index-fakXoZEe.mjs";
1
+ import { a as SchedulerMiddleware, c as SchedulerScopeContext, i as SchedulerConcurrencyConfig, n as getActiveScopes, o as SchedulerRuntime, r as CreateSchedulerRuntimeOptions, s as SchedulerScope, t as createSchedulerRuntime } from "../../index-Bg0atWBF.mjs";
2
2
  export { CreateSchedulerRuntimeOptions, SchedulerConcurrencyConfig, SchedulerMiddleware, SchedulerRuntime, SchedulerScope, SchedulerScopeContext, createSchedulerRuntime, getActiveScopes };
@@ -45,7 +45,7 @@ function createSchedulerRuntime(options = {}) {
45
45
  * Resolves the scheduler scopes that apply to a context.
46
46
  *
47
47
  * Before:
48
- * - `{ scope: 'case', workspaceId: 'ws', experimentId: 'exp', caseId: 'case-1' }`
48
+ * - `{ scope: 'case', workspaceId: 'ws', projectName: 'project', caseId: 'case-1' }`
49
49
  *
50
50
  * After:
51
51
  * - `['workspace', 'project', 'task', 'attempt', 'case']` up to the requested scope
@@ -86,7 +86,7 @@ function getScopeQueue(scope, context, queues) {
86
86
  return queue;
87
87
  }
88
88
  function getSchedulerScopeInstanceKey(scope, context) {
89
- const workspaceKey = `workspace:${context.workspaceId}:experiment:${context.experimentId}`;
89
+ const workspaceKey = `workspace:${context.workspaceId}`;
90
90
  const projectKey = `${workspaceKey}:project:${context.projectName ?? "(missing-project)"}`;
91
91
  const taskKey = `${projectKey}:task:${context.taskId ?? "(missing-task)"}`;
92
92
  const attemptKey = `${taskKey}:attempt:${context.attemptIndex ?? "(missing-attempt)"}`;
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/core/scheduler/runtime.ts"],"sourcesContent":["import type {\n CreateSchedulerRuntimeOptions,\n SchedulerConcurrencyConfig,\n SchedulerMiddleware,\n SchedulerRuntime,\n SchedulerScope,\n SchedulerScopeContext,\n} from './types'\n\nimport { createSchedulerQueue } from './queue'\n\nconst schedulerScopeOrder: SchedulerScope[] = [\n 'workspace',\n 'project',\n 'task',\n 'attempt',\n 'case',\n]\n\n/**\n * Creates the core scheduler runtime used to serialize work by scope.\n *\n * Call stack:\n *\n * {@link createSchedulerRuntime}\n * -> `createRuntimeQueues`\n * -> `runtime.runCase(context, execute)`\n * -> `runWithQueues`\n * -> `runAcquireMiddleware`\n * -> `execute`\n * -> `runReleaseMiddleware`\n *\n * Use when:\n * - runner code needs concurrency caps for queued case execution\n * - middleware should wrap work with acquire/release lifecycle hooks\n *\n * Expects:\n * - middleware is ordered from outermost to innermost concern\n * - concurrency caps are positive integers when provided\n *\n * Returns:\n * - a scheduler runtime with case execution support\n */\nexport function createSchedulerRuntime(\n options: CreateSchedulerRuntimeOptions = {},\n): SchedulerRuntime {\n const middleware = options.middleware ?? []\n const queues = createRuntimeQueues(options.concurrency ?? {})\n\n return {\n runCase<T>(context: SchedulerScopeContext, execute: () => Promise<T>) {\n const activeScopes = getActiveScopes(context)\n\n return runWithQueues(activeScopes, context, queues, () => {\n if (middleware.length === 0) {\n return execute()\n }\n\n return runWithMiddlewareEnvelope(middleware, context, execute)\n })\n },\n }\n}\n\n/**\n * Resolves the scheduler scopes that apply to a context.\n *\n * Before:\n * - `{ scope: 'case', workspaceId: 'ws', experimentId: 'exp', caseId: 'case-1' }`\n *\n * After:\n * - `['workspace', 'project', 'task', 'attempt', 'case']` up to the requested scope\n */\nexport function getActiveScopes(context: SchedulerScopeContext): SchedulerScope[] {\n const targetScopeIndex = schedulerScopeOrder.indexOf(context.scope)\n\n if (targetScopeIndex < 0) {\n return []\n }\n\n return schedulerScopeOrder.slice(0, targetScopeIndex + 1)\n}\n\nfunction createRuntimeQueues(concurrency: SchedulerConcurrencyConfig) {\n const queues = new Map<SchedulerScope, SchedulerScopeQueueRegistry>()\n\n for (const scope of schedulerScopeOrder) {\n const scopeConcurrency = concurrency[scope]\n\n if (scopeConcurrency === undefined) {\n continue\n }\n\n validateSchedulerConcurrency(scope, scopeConcurrency)\n\n queues.set(scope, {\n concurrency: scopeConcurrency,\n instances: new Map<string, ReturnType<typeof createSchedulerQueue>>(),\n })\n }\n\n return queues\n}\n\nasync function runWithQueues<T>(\n scopes: SchedulerScope[],\n context: SchedulerScopeContext,\n queues: Map<SchedulerScope, SchedulerScopeQueueRegistry>,\n execute: () => Promise<T>,\n index = 0,\n): Promise<T> {\n const scope = scopes[index]\n\n if (scope === undefined) {\n return execute()\n }\n\n const queue = getScopeQueue(scope, context, queues)\n\n if (queue === undefined) {\n return runWithQueues(scopes, context, queues, execute, index + 1)\n }\n\n return queue.run(() => runWithQueues(scopes, context, queues, execute, index + 1))\n}\n\ninterface SchedulerScopeQueueRegistry {\n concurrency: number\n instances: Map<string, ReturnType<typeof createSchedulerQueue>>\n}\n\ninterface SchedulerEnvelopeResult<T> {\n releaseStack: SchedulerMiddleware[]\n outcome: SchedulerExecutionOutcome<T>\n}\n\ninterface SchedulerExecutionFailure {\n error: unknown\n status: 'failed'\n}\n\ninterface SchedulerExecutionSkipped {\n status: 'skipped'\n}\n\ninterface SchedulerExecutionSuccess<T> {\n status: 'succeeded'\n value: T\n}\n\ntype SchedulerExecutionOutcome<T>\n = | SchedulerExecutionFailure\n | SchedulerExecutionSkipped\n | SchedulerExecutionSuccess<T>\n\nfunction getScopeQueue(\n scope: SchedulerScope,\n context: SchedulerScopeContext,\n queues: Map<SchedulerScope, SchedulerScopeQueueRegistry>,\n) {\n const queueRegistry = queues.get(scope)\n\n if (queueRegistry === undefined) {\n return undefined\n }\n\n const scopeKey = getSchedulerScopeInstanceKey(scope, context)\n const existingQueue = queueRegistry.instances.get(scopeKey)\n\n if (existingQueue !== undefined) {\n return existingQueue\n }\n\n const queue = createSchedulerQueue(queueRegistry.concurrency)\n queueRegistry.instances.set(scopeKey, queue)\n return queue\n}\n\nfunction getSchedulerScopeInstanceKey(\n scope: SchedulerScope,\n context: SchedulerScopeContext,\n): string {\n const workspaceKey = `workspace:${context.workspaceId}:experiment:${context.experimentId}`\n const projectKey = `${workspaceKey}:project:${context.projectName ?? '(missing-project)'}`\n const taskKey = `${projectKey}:task:${context.taskId ?? '(missing-task)'}`\n const attemptKey = `${taskKey}:attempt:${context.attemptIndex ?? '(missing-attempt)'}`\n\n switch (scope) {\n case 'workspace':\n return workspaceKey\n case 'project':\n return projectKey\n case 'task':\n return taskKey\n case 'attempt':\n return attemptKey\n case 'case':\n return attemptKey\n }\n}\n\nasync function runWithMiddlewareEnvelope<T>(\n middleware: SchedulerMiddleware[],\n context: SchedulerScopeContext,\n execute: () => Promise<T>,\n): Promise<T> {\n const result = await runAcquireMiddleware(middleware, context, execute, 0)\n\n try {\n switch (result.outcome.status) {\n case 'succeeded':\n return result.outcome.value\n case 'failed':\n throw result.outcome.error\n case 'skipped':\n throw createSchedulerShortCircuitError()\n }\n }\n finally {\n await runReleaseMiddleware(result.releaseStack, context, result.releaseStack.length - 1)\n }\n}\n\nasync function runAcquireMiddleware<T>(\n middleware: SchedulerMiddleware[],\n context: SchedulerScopeContext,\n execute: () => Promise<T>,\n index: number,\n): Promise<SchedulerEnvelopeResult<T>> {\n const currentMiddleware = middleware[index]\n\n if (currentMiddleware === undefined) {\n return createSchedulerExecutionResult([], execute)\n }\n\n let nextResult = createSchedulerShortCircuitResult<T>()\n let didCallNext = false\n\n const next = async () => {\n didCallNext = true\n nextResult = await runAcquireMiddleware(middleware, context, execute, index + 1)\n }\n\n try {\n if (currentMiddleware.onAcquire === undefined) {\n await next()\n }\n else {\n await currentMiddleware.onAcquire(context, next)\n }\n }\n catch (error) {\n if (!didCallNext) {\n return createSchedulerFailureResult([], error)\n }\n\n return createSchedulerFailureResult(\n [currentMiddleware, ...nextResult.releaseStack],\n error,\n )\n }\n\n return {\n releaseStack: [currentMiddleware, ...nextResult.releaseStack],\n outcome: nextResult.outcome,\n }\n}\n\nasync function runReleaseMiddleware(\n releaseStack: SchedulerMiddleware[],\n context: SchedulerScopeContext,\n index: number,\n): Promise<void> {\n const currentMiddleware = releaseStack[index]\n\n if (currentMiddleware === undefined) {\n return\n }\n\n if (currentMiddleware.onRelease === undefined) {\n await runReleaseMiddleware(releaseStack, context, index - 1)\n return\n }\n\n await currentMiddleware.onRelease(context, async () => {\n await runReleaseMiddleware(releaseStack, context, index - 1)\n })\n}\n\nasync function createSchedulerExecutionResult<T>(\n releaseStack: SchedulerMiddleware[],\n execute: () => Promise<T>,\n): Promise<SchedulerEnvelopeResult<T>> {\n try {\n return {\n releaseStack,\n outcome: {\n status: 'succeeded',\n value: await execute(),\n },\n }\n }\n catch (error) {\n return {\n releaseStack,\n outcome: {\n status: 'failed',\n error,\n },\n }\n }\n}\n\nfunction createSchedulerFailureResult<T>(\n releaseStack: SchedulerMiddleware[],\n error: unknown,\n): SchedulerEnvelopeResult<T> {\n return {\n releaseStack,\n outcome: {\n status: 'failed',\n error,\n },\n }\n}\n\nfunction createSchedulerShortCircuitResult<T>(): SchedulerEnvelopeResult<T> {\n return {\n releaseStack: [],\n outcome: {\n status: 'skipped',\n },\n }\n}\n\nfunction validateSchedulerConcurrency(scope: SchedulerScope, concurrency: number): void {\n if (!Number.isFinite(concurrency) || !Number.isInteger(concurrency) || concurrency <= 0) {\n throw new Error(`Invalid scheduler concurrency for \"${scope}\": ${String(concurrency)}`)\n }\n}\n\nfunction createSchedulerShortCircuitError(): Error {\n return new Error('Scheduler middleware short-circuited execution.')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/core/scheduler/runtime.ts"],"sourcesContent":["import type {\n CreateSchedulerRuntimeOptions,\n SchedulerConcurrencyConfig,\n SchedulerMiddleware,\n SchedulerRuntime,\n SchedulerScope,\n SchedulerScopeContext,\n} from './types'\n\nimport { createSchedulerQueue } from './queue'\n\nconst schedulerScopeOrder: SchedulerScope[] = [\n 'workspace',\n 'project',\n 'task',\n 'attempt',\n 'case',\n]\n\n/**\n * Creates the core scheduler runtime used to serialize work by scope.\n *\n * Call stack:\n *\n * {@link createSchedulerRuntime}\n * -> `createRuntimeQueues`\n * -> `runtime.runCase(context, execute)`\n * -> `runWithQueues`\n * -> `runAcquireMiddleware`\n * -> `execute`\n * -> `runReleaseMiddleware`\n *\n * Use when:\n * - runner code needs concurrency caps for queued case execution\n * - middleware should wrap work with acquire/release lifecycle hooks\n *\n * Expects:\n * - middleware is ordered from outermost to innermost concern\n * - concurrency caps are positive integers when provided\n *\n * Returns:\n * - a scheduler runtime with case execution support\n */\nexport function createSchedulerRuntime(\n options: CreateSchedulerRuntimeOptions = {},\n): SchedulerRuntime {\n const middleware = options.middleware ?? []\n const queues = createRuntimeQueues(options.concurrency ?? {})\n\n return {\n runCase<T>(context: SchedulerScopeContext, execute: () => Promise<T>) {\n const activeScopes = getActiveScopes(context)\n\n return runWithQueues(activeScopes, context, queues, () => {\n if (middleware.length === 0) {\n return execute()\n }\n\n return runWithMiddlewareEnvelope(middleware, context, execute)\n })\n },\n }\n}\n\n/**\n * Resolves the scheduler scopes that apply to a context.\n *\n * Before:\n * - `{ scope: 'case', workspaceId: 'ws', projectName: 'project', caseId: 'case-1' }`\n *\n * After:\n * - `['workspace', 'project', 'task', 'attempt', 'case']` up to the requested scope\n */\nexport function getActiveScopes(context: SchedulerScopeContext): SchedulerScope[] {\n const targetScopeIndex = schedulerScopeOrder.indexOf(context.scope)\n\n if (targetScopeIndex < 0) {\n return []\n }\n\n return schedulerScopeOrder.slice(0, targetScopeIndex + 1)\n}\n\nfunction createRuntimeQueues(concurrency: SchedulerConcurrencyConfig) {\n const queues = new Map<SchedulerScope, SchedulerScopeQueueRegistry>()\n\n for (const scope of schedulerScopeOrder) {\n const scopeConcurrency = concurrency[scope]\n\n if (scopeConcurrency === undefined) {\n continue\n }\n\n validateSchedulerConcurrency(scope, scopeConcurrency)\n\n queues.set(scope, {\n concurrency: scopeConcurrency,\n instances: new Map<string, ReturnType<typeof createSchedulerQueue>>(),\n })\n }\n\n return queues\n}\n\nasync function runWithQueues<T>(\n scopes: SchedulerScope[],\n context: SchedulerScopeContext,\n queues: Map<SchedulerScope, SchedulerScopeQueueRegistry>,\n execute: () => Promise<T>,\n index = 0,\n): Promise<T> {\n const scope = scopes[index]\n\n if (scope === undefined) {\n return execute()\n }\n\n const queue = getScopeQueue(scope, context, queues)\n\n if (queue === undefined) {\n return runWithQueues(scopes, context, queues, execute, index + 1)\n }\n\n return queue.run(() => runWithQueues(scopes, context, queues, execute, index + 1))\n}\n\ninterface SchedulerScopeQueueRegistry {\n concurrency: number\n instances: Map<string, ReturnType<typeof createSchedulerQueue>>\n}\n\ninterface SchedulerEnvelopeResult<T> {\n releaseStack: SchedulerMiddleware[]\n outcome: SchedulerExecutionOutcome<T>\n}\n\ninterface SchedulerExecutionFailure {\n error: unknown\n status: 'failed'\n}\n\ninterface SchedulerExecutionSkipped {\n status: 'skipped'\n}\n\ninterface SchedulerExecutionSuccess<T> {\n status: 'succeeded'\n value: T\n}\n\ntype SchedulerExecutionOutcome<T>\n = | SchedulerExecutionFailure\n | SchedulerExecutionSkipped\n | SchedulerExecutionSuccess<T>\n\nfunction getScopeQueue(\n scope: SchedulerScope,\n context: SchedulerScopeContext,\n queues: Map<SchedulerScope, SchedulerScopeQueueRegistry>,\n) {\n const queueRegistry = queues.get(scope)\n\n if (queueRegistry === undefined) {\n return undefined\n }\n\n const scopeKey = getSchedulerScopeInstanceKey(scope, context)\n const existingQueue = queueRegistry.instances.get(scopeKey)\n\n if (existingQueue !== undefined) {\n return existingQueue\n }\n\n const queue = createSchedulerQueue(queueRegistry.concurrency)\n queueRegistry.instances.set(scopeKey, queue)\n return queue\n}\n\nfunction getSchedulerScopeInstanceKey(\n scope: SchedulerScope,\n context: SchedulerScopeContext,\n): string {\n const workspaceKey = `workspace:${context.workspaceId}`\n const projectKey = `${workspaceKey}:project:${context.projectName ?? '(missing-project)'}`\n const taskKey = `${projectKey}:task:${context.taskId ?? '(missing-task)'}`\n const attemptKey = `${taskKey}:attempt:${context.attemptIndex ?? '(missing-attempt)'}`\n\n switch (scope) {\n case 'workspace':\n return workspaceKey\n case 'project':\n return projectKey\n case 'task':\n return taskKey\n case 'attempt':\n return attemptKey\n case 'case':\n return attemptKey\n }\n}\n\nasync function runWithMiddlewareEnvelope<T>(\n middleware: SchedulerMiddleware[],\n context: SchedulerScopeContext,\n execute: () => Promise<T>,\n): Promise<T> {\n const result = await runAcquireMiddleware(middleware, context, execute, 0)\n\n try {\n switch (result.outcome.status) {\n case 'succeeded':\n return result.outcome.value\n case 'failed':\n throw result.outcome.error\n case 'skipped':\n throw createSchedulerShortCircuitError()\n }\n }\n finally {\n await runReleaseMiddleware(result.releaseStack, context, result.releaseStack.length - 1)\n }\n}\n\nasync function runAcquireMiddleware<T>(\n middleware: SchedulerMiddleware[],\n context: SchedulerScopeContext,\n execute: () => Promise<T>,\n index: number,\n): Promise<SchedulerEnvelopeResult<T>> {\n const currentMiddleware = middleware[index]\n\n if (currentMiddleware === undefined) {\n return createSchedulerExecutionResult([], execute)\n }\n\n let nextResult = createSchedulerShortCircuitResult<T>()\n let didCallNext = false\n\n const next = async () => {\n didCallNext = true\n nextResult = await runAcquireMiddleware(middleware, context, execute, index + 1)\n }\n\n try {\n if (currentMiddleware.onAcquire === undefined) {\n await next()\n }\n else {\n await currentMiddleware.onAcquire(context, next)\n }\n }\n catch (error) {\n if (!didCallNext) {\n return createSchedulerFailureResult([], error)\n }\n\n return createSchedulerFailureResult(\n [currentMiddleware, ...nextResult.releaseStack],\n error,\n )\n }\n\n return {\n releaseStack: [currentMiddleware, ...nextResult.releaseStack],\n outcome: nextResult.outcome,\n }\n}\n\nasync function runReleaseMiddleware(\n releaseStack: SchedulerMiddleware[],\n context: SchedulerScopeContext,\n index: number,\n): Promise<void> {\n const currentMiddleware = releaseStack[index]\n\n if (currentMiddleware === undefined) {\n return\n }\n\n if (currentMiddleware.onRelease === undefined) {\n await runReleaseMiddleware(releaseStack, context, index - 1)\n return\n }\n\n await currentMiddleware.onRelease(context, async () => {\n await runReleaseMiddleware(releaseStack, context, index - 1)\n })\n}\n\nasync function createSchedulerExecutionResult<T>(\n releaseStack: SchedulerMiddleware[],\n execute: () => Promise<T>,\n): Promise<SchedulerEnvelopeResult<T>> {\n try {\n return {\n releaseStack,\n outcome: {\n status: 'succeeded',\n value: await execute(),\n },\n }\n }\n catch (error) {\n return {\n releaseStack,\n outcome: {\n status: 'failed',\n error,\n },\n }\n }\n}\n\nfunction createSchedulerFailureResult<T>(\n releaseStack: SchedulerMiddleware[],\n error: unknown,\n): SchedulerEnvelopeResult<T> {\n return {\n releaseStack,\n outcome: {\n status: 'failed',\n error,\n },\n }\n}\n\nfunction createSchedulerShortCircuitResult<T>(): SchedulerEnvelopeResult<T> {\n return {\n releaseStack: [],\n outcome: {\n status: 'skipped',\n },\n }\n}\n\nfunction validateSchedulerConcurrency(scope: SchedulerScope, concurrency: number): void {\n if (!Number.isFinite(concurrency) || !Number.isInteger(concurrency) || concurrency <= 0) {\n throw new Error(`Invalid scheduler concurrency for \"${scope}\": ${String(concurrency)}`)\n }\n}\n\nfunction createSchedulerShortCircuitError(): Error {\n return new Error('Scheduler middleware short-circuited execution.')\n}\n"],"mappings":"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"}
@@ -7,6 +7,10 @@ type EnvValueType = 'string';
7
7
  * Common options for env readers.
8
8
  */
9
9
  interface EnvFromOptions {
10
+ /**
11
+ * Env key to read and use in error messages.
12
+ */
13
+ name: string;
10
14
  /**
11
15
  * Expected env value type.
12
16
  */
@@ -17,10 +21,6 @@ interface EnvFromOptions {
17
21
  * @default false
18
22
  */
19
23
  required?: boolean;
20
- /**
21
- * Optional key name used for clearer error messages.
22
- */
23
- name?: string;
24
24
  }
25
25
  /**
26
26
  * Env options used by the required helper.
@@ -28,20 +28,25 @@ interface EnvFromOptions {
28
28
  * `required` is intentionally omitted because this helper is always required.
29
29
  */
30
30
  type RequiredEnvFromOptions = Omit<EnvFromOptions, 'required'>;
31
+ type EnvSource = Record<string, string | undefined>;
31
32
  /**
32
33
  * Parses one env value with optional required behavior.
33
34
  *
34
35
  * Example:
35
- * `const apiKey = envFrom(process.env.OPENAI_API_KEY, { type: 'string', required: true, name: 'OPENAI_API_KEY' })`
36
+ * `const apiKey = envFrom(process.env, { type: 'string', required: true, name: 'OPENAI_API_KEY' })`
36
37
  */
37
- declare function envFrom(value: string | undefined, options: EnvFromOptions): string | undefined;
38
+ declare function envFrom<TEnv extends EnvSource>(env: TEnv, options: EnvFromOptions & {
39
+ name: keyof TEnv & string;
40
+ }): string | undefined;
38
41
  /**
39
42
  * Parses one required env value.
40
43
  *
41
44
  * Example:
42
- * `const apiKey = requiredEnvFrom(process.env.OPENAI_API_KEY, { type: 'string', name: 'OPENAI_API_KEY' })`
45
+ * `const apiKey = requiredEnvFrom(process.env, { type: 'string', name: 'OPENAI_API_KEY' })`
43
46
  */
44
- declare function requiredEnvFrom(value: string | undefined, options: RequiredEnvFromOptions): string;
47
+ declare function requiredEnvFrom<TEnv extends EnvSource>(env: TEnv, options: RequiredEnvFromOptions & {
48
+ name: keyof TEnv & string;
49
+ }): string;
45
50
  //#endregion
46
51
  export { requiredEnvFrom as a, envFrom as i, EnvValueType as n, RequiredEnvFromOptions as r, EnvFromOptions as t };
47
- //# sourceMappingURL=env-BeHv_5mo.d.mts.map
52
+ //# sourceMappingURL=env-DfWZy_n4.d.mts.map
@@ -0,0 +1,35 @@
1
+ //#region src/core/inference-executors/env.ts
2
+ function assertNonEmptyString(value, options) {
3
+ if (value == null || value.trim().length === 0) {
4
+ if (options.required === true) throw new Error(`Missing required ${options.name}.`);
5
+ return;
6
+ }
7
+ return value;
8
+ }
9
+ /**
10
+ * Parses one env value with optional required behavior.
11
+ *
12
+ * Example:
13
+ * `const apiKey = envFrom(process.env, { type: 'string', required: true, name: 'OPENAI_API_KEY' })`
14
+ */
15
+ function envFrom(env, options) {
16
+ if (options.type === "string") return assertNonEmptyString(env[options.name], options);
17
+ }
18
+ /**
19
+ * Parses one required env value.
20
+ *
21
+ * Example:
22
+ * `const apiKey = requiredEnvFrom(process.env, { type: 'string', name: 'OPENAI_API_KEY' })`
23
+ */
24
+ function requiredEnvFrom(env, options) {
25
+ const parsed = envFrom(env, {
26
+ ...options,
27
+ required: true
28
+ });
29
+ if (parsed == null) throw new Error(`Missing required ${options.name}.`);
30
+ return parsed;
31
+ }
32
+ //#endregion
33
+ export { requiredEnvFrom as n, envFrom as t };
34
+
35
+ //# sourceMappingURL=env-nV5rVErX.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-nV5rVErX.mjs","names":[],"sources":["../src/core/inference-executors/env.ts"],"sourcesContent":["/**\n * Supported env value coercion types.\n */\nexport type EnvValueType = 'string'\n\n/**\n * Common options for env readers.\n */\nexport interface EnvFromOptions {\n /**\n * Env key to read and use in error messages.\n */\n name: string\n /**\n * Expected env value type.\n */\n type: EnvValueType\n /**\n * Whether an empty or missing value should throw.\n *\n * @default false\n */\n required?: boolean\n}\n\n/**\n * Env options used by the required helper.\n *\n * `required` is intentionally omitted because this helper is always required.\n */\nexport type RequiredEnvFromOptions = Omit<EnvFromOptions, 'required'>\n\ntype EnvSource = Record<string, string | undefined>\n\nfunction assertNonEmptyString(value: string | undefined, options: EnvFromOptions): string | undefined {\n if (value == null || value.trim().length === 0) {\n if (options.required === true) {\n throw new Error(`Missing required ${options.name}.`)\n }\n\n return undefined\n }\n\n return value\n}\n\n/**\n * Parses one env value with optional required behavior.\n *\n * Example:\n * `const apiKey = envFrom(process.env, { type: 'string', required: true, name: 'OPENAI_API_KEY' })`\n */\nexport function envFrom<TEnv extends EnvSource>(\n env: TEnv,\n options: EnvFromOptions & { name: keyof TEnv & string },\n): string | undefined {\n if (options.type === 'string') {\n return assertNonEmptyString(env[options.name], options)\n }\n\n return undefined\n}\n\n/**\n * Parses one required env value.\n *\n * Example:\n * `const apiKey = requiredEnvFrom(process.env, { type: 'string', name: 'OPENAI_API_KEY' })`\n */\nexport function requiredEnvFrom<TEnv extends EnvSource>(\n env: TEnv,\n options: RequiredEnvFromOptions & { name: keyof TEnv & string },\n): string {\n const parsed = envFrom(env, {\n ...options,\n required: true,\n })\n\n if (parsed == null) {\n throw new Error(`Missing required ${options.name}.`)\n }\n\n return parsed\n}\n"],"mappings":";AAkCA,SAAS,qBAAqB,OAA2B,SAA6C;AACpG,KAAI,SAAS,QAAQ,MAAM,MAAM,CAAC,WAAW,GAAG;AAC9C,MAAI,QAAQ,aAAa,KACvB,OAAM,IAAI,MAAM,oBAAoB,QAAQ,KAAK,GAAG;AAGtD;;AAGF,QAAO;;;;;;;;AAST,SAAgB,QACd,KACA,SACoB;AACpB,KAAI,QAAQ,SAAS,SACnB,QAAO,qBAAqB,IAAI,QAAQ,OAAO,QAAQ;;;;;;;;AAY3D,SAAgB,gBACd,KACA,SACQ;CACR,MAAM,SAAS,QAAQ,KAAK;EAC1B,GAAG;EACH,UAAU;EACX,CAAC;AAEF,KAAI,UAAU,KACZ,OAAM,IAAI,MAAM,oBAAoB,QAAQ,KAAK,GAAG;AAGtD,QAAO"}
@@ -22,7 +22,8 @@ type SchedulerScope = 'workspace' | 'project' | 'task' | 'attempt' | 'case';
22
22
  * - runtime helpers need to know which hierarchical scope is being executed
23
23
  *
24
24
  * Expects:
25
- * - `workspaceId` and `experimentId` are always present
25
+ * - `workspaceId` is always present
26
+ * - `experimentId` is metadata for middleware and logging, not queue partitioning
26
27
  * - narrower ids are only provided when the selected scope requires them
27
28
  *
28
29
  * Returns:
@@ -136,7 +137,7 @@ declare function createSchedulerRuntime(options?: CreateSchedulerRuntimeOptions)
136
137
  * Resolves the scheduler scopes that apply to a context.
137
138
  *
138
139
  * Before:
139
- * - `{ scope: 'case', workspaceId: 'ws', experimentId: 'exp', caseId: 'case-1' }`
140
+ * - `{ scope: 'case', workspaceId: 'ws', projectName: 'project', caseId: 'case-1' }`
140
141
  *
141
142
  * After:
142
143
  * - `['workspace', 'project', 'task', 'attempt', 'case']` up to the requested scope
@@ -144,4 +145,4 @@ declare function createSchedulerRuntime(options?: CreateSchedulerRuntimeOptions)
144
145
  declare function getActiveScopes(context: SchedulerScopeContext): SchedulerScope[];
145
146
  //#endregion
146
147
  export { SchedulerMiddleware as a, SchedulerScopeContext as c, SchedulerConcurrencyConfig as i, getActiveScopes as n, SchedulerRuntime as o, CreateSchedulerRuntimeOptions as r, SchedulerScope as s, createSchedulerRuntime as t };
147
- //# sourceMappingURL=index-fakXoZEe.d.mts.map
148
+ //# sourceMappingURL=index-Bg0atWBF.d.mts.map
@@ -1,5 +1,5 @@
1
- import { Buffer } from "node:buffer";
2
1
  import { ReadStream, WriteStream } from "node:fs";
2
+ import { Buffer } from "node:buffer";
3
3
 
4
4
  //#region src/core/cache/types.d.ts
5
5
  /**
@@ -1354,4 +1354,4 @@ interface ConfigHookPlugin<TConfig> {
1354
1354
  }
1355
1355
  //#endregion
1356
1356
  export { InferenceExecutor as $, RunScheduledTasksOptions as A, asProjectRelativePath as B, TaskDefinition as C, TaskRunContext as D, TaskReporterHooks as E, CreateTaskExecutionContextOptions as F, AggregatedProviderSummary as G, CreateVievalRunnerRuntimeContextOptions as H, TaskExecutionContext as I, RunResult as J, AggregatedRunResults as K, createTaskExecutionContext as L, RunnerTaskState as M, ScheduledTaskExecutor as N, TaskRunOutput as O, runScheduledTasks as P, CreateRunnerScheduleOptions as Q, ModelDefinition as R, TaskConcurrencyConfig as S, TaskReporterEventPayload as T, RunnerRuntimeContext as U, collectEvalEntries as V, createRunnerRuntimeContext as W, RunScoreKind as X, RunScore as Y, aggregateRunResults as Z, ScopedMatrices as _, CliOpenTelemetryReportingConfig as a, ScheduledTaskMatrixMeta as at, TaskCaseReporterPayload as b, EvalDefinition as c, createFilesystemTaskCacheRuntime as ct, MatrixAxisValues as d, CacheFileOptions as dt, RunnerMatrixDefinition as et, MatrixDefinition as f, CacheNamespace as ft, MatrixValue as g, MatrixRow as h, Awaitable as i, ScheduledTaskMatrix as it, RunnerExecutionError as j, TelemetryAttributeValue as k, EvalModule as l, normalizeCacheFilePathSegments as lt, MatrixPrimitive as m, defineEval as n, RunnerMatrixSelection as nt, CliReportingConfig as o, createRunnerSchedule as ot, MatrixLayer as p, TaskCacheRuntime as pt, AggregatedRunSummary as q, defineTask as r, ScheduledTask as rt, CollectedEvalEntry as s, CreateFilesystemTaskCacheRuntimeOptions as st, ConfigHookPlugin as t, RunnerMatrixInput as tt, EvalModuleMap as u, CacheFileHandle as ut, TaskAutoRetryDelay as v, TaskExecutionPolicy as w, TaskCaseState as x, TaskCaseReporterEndPayload as y, resolveModelByName as z };
1357
- //# sourceMappingURL=index-BkjyCInx.d.mts.map
1357
+ //# sourceMappingURL=index-D_aMeWqO.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as InferenceExecutor, D as TaskRunContext, I as TaskExecutionContext, J as RunResult, O as TaskRunOutput, R as ModelDefinition, S as TaskConcurrencyConfig, X as RunScoreKind, f as MatrixDefinition, k as TelemetryAttributeValue, o as CliReportingConfig, p as MatrixLayer, rt as ScheduledTask, t as ConfigHookPlugin, w as TaskExecutionPolicy } from "./index-BkjyCInx.mjs";
2
- import { a as requiredEnvFrom } from "./env-BeHv_5mo.mjs";
1
+ import { $ as InferenceExecutor, D as TaskRunContext, I as TaskExecutionContext, J as RunResult, O as TaskRunOutput, R as ModelDefinition, S as TaskConcurrencyConfig, X as RunScoreKind, f as MatrixDefinition, k as TelemetryAttributeValue, o as CliReportingConfig, p as MatrixLayer, rt as ScheduledTask, t as ConfigHookPlugin, w as TaskExecutionPolicy } from "./index-D_aMeWqO.mjs";
2
+ import { a as requiredEnvFrom } from "./env-DfWZy_n4.mjs";
3
3
  import { expect } from "./expect.mjs";
4
4
  import * as _$c12 from "c12";
5
5
 
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { i as registerEvalDefinition, l as loadEnv, o as createNoopTelemetryRuntime, s as defineConfig } from "./registry-BHGMxjpA.mjs";
1
+ import { i as registerEvalDefinition, l as loadEnv, o as createNoopTelemetryRuntime, s as defineConfig } from "./registry-DMnwE_mY.mjs";
2
2
  import { t as createSchedulerQueue } from "./queue-DsZQkZO_.mjs";
3
- import { n as requiredEnvFrom } from "./env-BFSjny07.mjs";
3
+ import { n as requiredEnvFrom } from "./env-nV5rVErX.mjs";
4
4
  import { defineEval, defineTask } from "./config.mjs";
5
5
  import { expect } from "./expect.mjs";
6
6
  import { errorMessageFrom, sleep } from "@moeru/std";
@@ -219,6 +219,16 @@ function collectCaseOutcomeScores(outcome, scoreBucketsByKind) {
219
219
  const judgeScore = outcome.scoresByKind.get("judge");
220
220
  if (judgeScore != null) scoreBucketsByKind.judge.push(judgeScore);
221
221
  }
222
+ function emitCaseOutcome(context, taskCase, outcome, index, totalCases) {
223
+ emitCaseEnd(context.reporterHooks, {
224
+ ...outcome.errorMessage == null ? {} : { errorMessage: outcome.errorMessage },
225
+ index,
226
+ ...outcome.output === void 0 ? {} : { output: outcome.output },
227
+ state: outcome.state,
228
+ name: taskCase.name,
229
+ total: totalCases
230
+ });
231
+ }
222
232
  function createCaseBuilder(registeredCases) {
223
233
  function registerCase(name, run, options) {
224
234
  registeredCases.push({
@@ -346,14 +356,7 @@ function describeTask(name, build, options = {}) {
346
356
  if (!hasAutoAttempt) await Promise.all(registeredCases.map(async (taskCase, index) => {
347
357
  const executeCase = async () => {
348
358
  const outcome = await executeRegisteredCase(context, taskCase, index, totalCases, taskExecutionPolicy);
349
- emitCaseEnd(context.reporterHooks, {
350
- ...outcome.errorMessage == null ? {} : { errorMessage: outcome.errorMessage },
351
- index,
352
- ...outcome.output === void 0 ? {} : { output: outcome.output },
353
- state: outcome.state,
354
- name: taskCase.name,
355
- total: totalCases
356
- });
359
+ emitCaseOutcome(context, taskCase, outcome, index, totalCases);
357
360
  collectCaseOutcomeScores(outcome, scoreBucketsByKind);
358
361
  };
359
362
  const concurrency = resolveCaseConcurrency(taskCase, runtimeTaskConcurrency, context.runtimeConcurrency);
@@ -367,10 +370,9 @@ function describeTask(name, build, options = {}) {
367
370
  await queue.run(executeCase);
368
371
  }));
369
372
  else {
370
- let finalOutcomes = [];
371
373
  let attemptIndex = 0;
372
374
  for (;;) {
373
- finalOutcomes = await Promise.all(registeredCases.map(async (taskCase, index) => {
375
+ const attemptOutcomes = await Promise.all(registeredCases.map(async (taskCase, index) => {
374
376
  const executeCase = async () => await executeRegisteredCase(context, taskCase, index, totalCases, taskExecutionPolicy);
375
377
  const concurrency = resolveCaseConcurrency(taskCase, runtimeTaskConcurrency, context.runtimeConcurrency);
376
378
  if (concurrency == null) return await executeCase();
@@ -379,7 +381,13 @@ function describeTask(name, build, options = {}) {
379
381
  caseQueues.set(queueKey, queue);
380
382
  return await queue.run(executeCase);
381
383
  }));
382
- if (!finalOutcomes.some((outcome, index) => {
384
+ attemptOutcomes.forEach((outcome, index) => {
385
+ const taskCase = registeredCases[index];
386
+ if (taskCase == null) return;
387
+ emitCaseOutcome(context, taskCase, outcome, index, totalCases);
388
+ collectCaseOutcomeScores(outcome, scoreBucketsByKind);
389
+ });
390
+ if (!attemptOutcomes.some((outcome, index) => {
383
391
  if (outcome.state === "passed") return false;
384
392
  const taskCase = registeredCases[index];
385
393
  if (taskCase == null) return false;
@@ -387,19 +395,6 @@ function describeTask(name, build, options = {}) {
387
395
  })) break;
388
396
  attemptIndex += 1;
389
397
  }
390
- finalOutcomes.forEach((outcome, index) => {
391
- const taskCase = registeredCases[index];
392
- if (taskCase == null) return;
393
- emitCaseEnd(context.reporterHooks, {
394
- ...outcome.errorMessage == null ? {} : { errorMessage: outcome.errorMessage },
395
- index,
396
- ...outcome.output === void 0 ? {} : { output: outcome.output },
397
- state: outcome.state,
398
- name: taskCase.name,
399
- total: totalCases
400
- });
401
- collectCaseOutcomeScores(outcome, scoreBucketsByKind);
402
- });
403
398
  }
404
399
  return { scores: Object.keys(scoreBucketsByKind).filter((kind) => scoreBucketsByKind[kind].length > 0).map((kind) => {
405
400
  const values = scoreBucketsByKind[kind];