vieval 0.0.5 → 0.0.7

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 (42) hide show
  1. package/README.md +219 -109
  2. package/dist/bin/vieval.mjs +1 -1
  3. package/dist/cli/index.mjs +1 -1
  4. package/dist/{cli-DayPXzHX.mjs → cli-ImxGpoYQ.mjs} +1447 -195
  5. package/dist/cli-ImxGpoYQ.mjs.map +1 -0
  6. package/dist/config.d.mts +2 -2
  7. package/dist/config.mjs +1 -1
  8. package/dist/core/assertions/index.d.mts +1 -1
  9. package/dist/core/inference-executors/index.d.mts +1 -1
  10. package/dist/core/inference-executors/index.mjs +1 -1
  11. package/dist/core/processors/results/index.d.mts +1 -1
  12. package/dist/core/runner/index.d.mts +3 -2
  13. package/dist/core/runner/index.mjs +3 -2
  14. package/dist/core/runner/index.mjs.map +1 -1
  15. package/dist/core/scheduler/index.d.mts +2 -0
  16. package/dist/core/scheduler/index.mjs +188 -0
  17. package/dist/core/scheduler/index.mjs.map +1 -0
  18. package/dist/{env-BFSjny07.mjs → env--94B0UtW.mjs} +1 -1
  19. package/dist/{env-BFSjny07.mjs.map → env--94B0UtW.mjs.map} +1 -1
  20. package/dist/{env-BTq3dV7C.d.mts → env-BeHv_5mo.d.mts} +1 -1
  21. package/dist/{expect-extensions-QLXESWjn.mjs → expect-extensions-DCSqlneN.mjs} +1 -1
  22. package/dist/{expect-extensions-QLXESWjn.mjs.map → expect-extensions-DCSqlneN.mjs.map} +1 -1
  23. package/dist/expect.mjs +1 -1
  24. package/dist/{index-OEdqjQSe.d.mts → index-5R1_k2nv.d.mts} +195 -3
  25. package/dist/index-fakXoZEe.d.mts +147 -0
  26. package/dist/index.d.mts +120 -13
  27. package/dist/index.mjs +286 -54
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/{models-D_MsBtYw.mjs → models-DIGdOUpJ.mjs} +1 -1
  30. package/dist/models-DIGdOUpJ.mjs.map +1 -0
  31. package/dist/plugins/chat-models/index.d.mts +27 -1
  32. package/dist/plugins/chat-models/index.mjs +29 -1
  33. package/dist/plugins/chat-models/index.mjs.map +1 -1
  34. package/dist/queue-DsZQkZO_.mjs +21 -0
  35. package/dist/queue-DsZQkZO_.mjs.map +1 -0
  36. package/dist/{registry-CwcMMjnZ.mjs → registry-BHGMxjpA.mjs} +164 -6
  37. package/dist/registry-BHGMxjpA.mjs.map +1 -0
  38. package/dist/testing/expect-extensions.mjs +1 -1
  39. package/package.json +8 -1
  40. package/dist/cli-DayPXzHX.mjs.map +0 -1
  41. package/dist/models-D_MsBtYw.mjs.map +0 -1
  42. package/dist/registry-CwcMMjnZ.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/dsl/task.ts"],"sourcesContent":["import type { TaskRunContext, TaskRunOutput } from '../config'\nimport type { RunScoreKind } from '../core/runner'\n\nimport { errorMessageFrom } from '@moeru/std'\n\nimport { defineEval, defineTask } from '../config'\nimport { registerEvalDefinition } from './registry'\n\n/**\n * Runtime context provided to a task case callback.\n */\nexport interface CaseRunContext<TInput> extends TaskRunContext {\n /**\n * Case-scoped matrix payload.\n */\n matrix: TaskRunContext['task']['matrix'] & { inputs: TInput }\n /**\n * Overrides one case score family with a custom normalized value.\n *\n * Use when:\n * - one case computes a benchmark-native score that should flow into run aggregation\n *\n * Expects:\n * - `score` to stay in the `0..1` range\n */\n score: (score: number, kind?: RunScoreKind) => void\n /**\n * Emits one custom case metric into report events.\n *\n * Use when:\n * - tasks need structured benchmark metadata beyond exact/judge score families\n *\n * Expects:\n * - `name` to be a stable metric identifier\n * - `value` to be JSON-serializable\n */\n metric: (name: string, value: boolean | number | string | null) => void\n}\n\n/**\n * Callback for one task case.\n */\nexport type CaseRunner<TInput> = (context: CaseRunContext<TInput>) => Promise<void> | void\n\ninterface RegisteredCase<TInput> {\n input: TInput\n name: string\n run: CaseRunner<TInput>\n}\n\nfunction cloneCaseMatrix(matrix: TaskRunContext['task']['matrix']): TaskRunContext['task']['matrix'] {\n return {\n eval: {\n ...matrix.eval,\n },\n meta: {\n ...matrix.meta,\n },\n run: {\n ...matrix.run,\n },\n }\n}\n\nfunction createTaskCaseReporterId(index: number, name: string): string {\n return `${index}:${encodeURIComponent(name)}`\n}\n\nfunction assertValidScore(score: number): void {\n if (!Number.isFinite(score) || score < 0 || score > 1) {\n throw new Error(`Case score must be a finite number in range 0..1, got \"${score}\".`)\n }\n}\n\nfunction emitCaseStart(\n hooks: TaskRunContext['reporterHooks'] | undefined,\n payload: {\n index: number\n name: string\n total: number\n },\n): void {\n try {\n hooks?.onCaseStart?.(payload)\n }\n catch {\n // Reporter hooks must never affect task scoring.\n }\n}\n\nfunction emitCaseEnd(\n hooks: TaskRunContext['reporterHooks'] | undefined,\n payload: {\n index: number\n state: 'passed' | 'failed'\n name: string\n total: number\n errorMessage?: string\n },\n): void {\n try {\n hooks?.onCaseEnd?.(payload)\n }\n catch {\n // Reporter hooks must never affect task scoring.\n }\n}\n\n/**\n * Builder callbacks passed into `describeTask`.\n */\nexport interface DescribeTaskBuilder {\n /**\n * Registers one explicit case.\n */\n caseOf: {\n (name: string, run: CaseRunner<undefined>): void\n <TInput>(name: string, run: CaseRunner<TInput>, options: { input: TInput }): void\n }\n /**\n * Registers multiple cases from input list.\n */\n casesFromInputs: <TInput>(\n namePrefix: string,\n inputs: readonly TInput[],\n run: CaseRunner<TInput>,\n ) => void\n}\n\n/**\n * Options for `describeTask`.\n */\nexport interface DescribeTaskOptions {\n /**\n * Optional description override.\n */\n description?: string\n}\n\nfunction createCaseBuilder(registeredCases: RegisteredCase<unknown>[]): DescribeTaskBuilder {\n function registerCase(name: string, run: CaseRunner<undefined>): void\n function registerCase<TInput>(name: string, run: CaseRunner<TInput>, options: { input: TInput }): void\n function registerCase<TInput>(\n name: string,\n run: CaseRunner<TInput> | CaseRunner<undefined>,\n options?: { input: TInput },\n ): void {\n registeredCases.push({\n input: options?.input,\n name,\n run: run as CaseRunner<unknown>,\n })\n }\n\n return {\n caseOf: registerCase,\n casesFromInputs(namePrefix, inputs, run) {\n inputs.forEach((input, index) => {\n registeredCases.push({\n input,\n name: `${namePrefix} #${index + 1}`,\n run: run as CaseRunner<unknown>,\n })\n })\n },\n }\n}\n\nlet activeCasesStack: RegisteredCase<unknown>[][] = []\n\nfunction withActiveCases<T>(cases: RegisteredCase<unknown>[], callback: () => T): T {\n activeCasesStack = [...activeCasesStack, cases]\n\n try {\n return callback()\n }\n finally {\n activeCasesStack = activeCasesStack.slice(0, -1)\n }\n}\n\nfunction getActiveCases(): RegisteredCase<unknown>[] {\n const active = activeCasesStack.at(-1)\n if (active == null) {\n throw new Error('caseOf/casesFromInputs must be called inside describeTask/describeEval.')\n }\n\n return active\n}\n\n/**\n * Registers one case in the currently active task scope.\n */\nexport function caseOf(\n name: string,\n run: CaseRunner<undefined>,\n): void\n\nexport function caseOf<TInput>(\n name: string,\n run: CaseRunner<TInput>,\n options: { input: TInput },\n): void\n\nexport function caseOf<TInput>(\n name: string,\n run: CaseRunner<TInput> | CaseRunner<undefined>,\n options?: { input: TInput },\n): void {\n getActiveCases().push({\n input: options?.input,\n name,\n run: run as CaseRunner<unknown>,\n })\n}\n\n/**\n * Registers multiple cases in the currently active task scope.\n */\nexport function casesFromInputs<TInput>(\n namePrefix: string,\n inputs: readonly TInput[],\n run: CaseRunner<TInput>,\n): void {\n inputs.forEach((input, index) => {\n getActiveCases().push({\n input,\n name: `${namePrefix} #${index + 1}`,\n run: run as CaseRunner<unknown>,\n })\n })\n}\n\n/**\n * Defines one eval task with task/case semantics similar to Vitest.\n *\n * Use when:\n * - task behavior should be declared with `caseOf` and `casesFromInputs`\n * - business agent code should be imported and run from eval task files\n */\nexport function describeTask(\n name: string,\n build: ((builder: DescribeTaskBuilder) => void) | (() => void),\n options: DescribeTaskOptions = {},\n) {\n const registeredCases: RegisteredCase<unknown>[] = []\n const builder = createCaseBuilder(registeredCases)\n withActiveCases(registeredCases, () => {\n if (build.length > 0) {\n (build as (builder: DescribeTaskBuilder) => void)(builder)\n return\n }\n\n ;(build as () => void)()\n })\n\n const description = options.description ?? name\n\n const definition = defineEval({\n description,\n name,\n task: defineTask({\n id: name,\n async run(context): Promise<TaskRunOutput> {\n if (registeredCases.length === 0) {\n return {\n scores: [{ kind: 'exact', score: 1 }],\n }\n }\n\n const totalCases = registeredCases.length\n\n const scoreBucketsByKind: Record<RunScoreKind, number[]> = {\n exact: [],\n judge: [],\n }\n\n await Promise.all(\n registeredCases.map(async (taskCase, index) => {\n emitCaseStart(context.reporterHooks, {\n index,\n name: taskCase.name,\n total: totalCases,\n })\n\n let state: 'passed' | 'failed' = 'passed'\n let errorMessage: string | undefined\n const caseId = createTaskCaseReporterId(index, taskCase.name)\n const customScoresByKind = new Map<RunScoreKind, number>()\n\n try {\n await taskCase.run({\n ...context,\n matrix: {\n ...cloneCaseMatrix(context.task.matrix),\n inputs: taskCase.input,\n },\n metric(name, value) {\n context.reporterHooks?.onEvent?.({\n caseId,\n data: {\n name,\n value,\n },\n event: 'task.case.metric',\n })\n },\n score(score, kind = 'exact') {\n assertValidScore(score)\n customScoresByKind.set(kind, score)\n },\n })\n }\n catch (error) {\n state = 'failed'\n errorMessage = errorMessageFrom(error) ?? 'Unknown case failure.'\n }\n finally {\n emitCaseEnd(context.reporterHooks, {\n ...(errorMessage == null ? {} : { errorMessage }),\n index,\n state,\n name: taskCase.name,\n total: totalCases,\n })\n }\n\n if (state === 'failed') {\n scoreBucketsByKind.exact.push(0)\n return\n }\n\n if (customScoresByKind.size === 0) {\n scoreBucketsByKind.exact.push(1)\n return\n }\n\n scoreBucketsByKind.exact.push(customScoresByKind.get('exact') ?? 1)\n const judgeScore = customScoresByKind.get('judge')\n if (judgeScore != null) {\n scoreBucketsByKind.judge.push(judgeScore)\n }\n }),\n )\n\n const scores = (Object.keys(scoreBucketsByKind) as RunScoreKind[])\n .filter(kind => scoreBucketsByKind[kind].length > 0)\n .map((kind) => {\n const values = scoreBucketsByKind[kind]\n const total = values.reduce((sum, value) => sum + value, 0)\n return {\n kind,\n score: total / values.length,\n }\n })\n\n return {\n scores,\n }\n },\n }),\n })\n\n registerEvalDefinition(definition)\n\n return definition\n}\n\n/**\n * Alias of `describeTask` for eval-centric naming.\n */\nexport const describeEval = describeTask\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/dsl/task.ts"],"sourcesContent":["import type { TaskConcurrencyConfig, TaskExecutionPolicy, TaskReporterEventPayload, TaskRunContext, TaskRunOutput } from '../config'\nimport type { RunScoreKind } from '../core/runner'\nimport type { TelemetryAttributeValue } from '../core/telemetry'\n\nimport { errorMessageFrom, sleep } from '@moeru/std'\n\nimport { defineEval, defineTask } from '../config'\nimport { createSchedulerQueue } from '../core/scheduler/queue'\nimport { createNoopTelemetryRuntime } from '../core/telemetry'\nimport { registerEvalDefinition } from './registry'\n\n/**\n * Runtime context provided to a task case callback.\n */\nexport interface CaseRunContext<TInput> extends TaskRunContext {\n /**\n * Case-scoped matrix payload.\n */\n matrix: TaskRunContext['task']['matrix'] & { inputs: TInput }\n /**\n * Overrides one case score family with a custom normalized value.\n *\n * Use when:\n * - one case computes a benchmark-native score that should flow into run aggregation\n *\n * Expects:\n * - `score` to stay in the `0..1` range\n */\n score: (score: number, kind?: RunScoreKind) => void\n /**\n * Emits one custom case metric into report events.\n *\n * Use when:\n * - tasks need structured benchmark metadata beyond exact/judge score families\n *\n * Expects:\n * - `name` to be a stable metric identifier\n * - `value` to be JSON-serializable\n */\n metric: (name: string, value: TelemetryAttributeValue) => void\n /**\n * Cooperative abort signal for the current case execution.\n */\n signal: AbortSignal\n}\n\n/**\n * Callback for one task case.\n */\nexport type CaseRunner<TInput> = (context: CaseRunContext<TInput>) => Promise<unknown> | unknown\n\ninterface RegisteredCase<TInput> {\n concurrency?: number\n executionPolicy?: TaskExecutionPolicy\n input: TInput\n name: string\n queueKey?: object\n run: CaseRunner<TInput>\n}\n\n/**\n * Per-group options for `casesFromInputs`.\n *\n * Use when:\n * - one generated case group should run with a lower case concurrency than the task default\n * - a task should keep a broader task-level cap while one expensive case family stays bounded\n *\n * Expects:\n * - `concurrency` to be a positive integer when provided\n *\n * Returns:\n * - one partial case-group execution descriptor\n */\nexport interface CasesFromInputsOptions extends TaskExecutionPolicy {\n /**\n * Case-level concurrency cap for cases registered by one `casesFromInputs(...)` call.\n */\n concurrency?: number\n}\n\n/**\n * Per-case registration options for `caseOf`.\n */\nexport interface CaseRegistrationOptions<TInput> extends TaskExecutionPolicy {\n /**\n * Optional case input payload.\n */\n input: TInput\n}\n\ninterface CaseExecutionOutcome {\n errorMessage?: string\n output?: unknown\n scoresByKind: Map<RunScoreKind, number>\n state: 'failed' | 'passed' | 'timeout'\n}\n\nfunction cloneCaseMatrix(matrix: TaskRunContext['task']['matrix']): TaskRunContext['task']['matrix'] {\n return {\n eval: {\n ...matrix.eval,\n },\n meta: {\n ...matrix.meta,\n },\n run: {\n ...matrix.run,\n },\n }\n}\n\nfunction createTaskCaseReporterId(index: number, name: string): string {\n return `${index}:${encodeURIComponent(name)}`\n}\n\nfunction isTelemetryAttributeScalar(value: unknown): value is boolean | number | string {\n return typeof value === 'boolean' || typeof value === 'number' || typeof value === 'string'\n}\n\nfunction isTelemetryAttributeArray(value: readonly TelemetryAttributeValue[]): value is readonly boolean[] | readonly number[] | readonly string[] {\n return value.every(isTelemetryAttributeScalar)\n}\n\nfunction canAttachMetricAsAttribute(value: TelemetryAttributeValue): value is boolean | number | string | readonly boolean[] | readonly number[] | readonly string[] {\n if (isTelemetryAttributeScalar(value)) {\n return true\n }\n\n return Array.isArray(value) && isTelemetryAttributeArray(value)\n}\n\nfunction assertValidScore(score: number): void {\n if (!Number.isFinite(score) || score < 0 || score > 1) {\n throw new Error(`Case score must be a finite number in range 0..1, got \"${score}\".`)\n }\n}\n\nfunction assertNonNegativeInteger(value: number, label: string): void {\n if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {\n throw new Error(`Invalid ${label}: ${String(value)}`)\n }\n}\n\nfunction assertNonNegativeNumber(value: number, label: string): void {\n if (!Number.isFinite(value) || value < 0) {\n throw new Error(`Invalid ${label}: ${String(value)}`)\n }\n}\n\nfunction assertPositiveInteger(value: number, label: string): void {\n if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {\n throw new Error(`Invalid ${label}: ${String(value)}`)\n }\n}\n\nfunction autoRetryDelayMs(retryIndex: number): number {\n // Retry index 1 is the first retry after the initial case failure.\n return 500 * 2 ** (retryIndex - 1)\n}\n\nfunction resolveAutoRetryDelay(policy: TaskExecutionPolicy, retryIndex: number): number {\n const delay = policy.autoRetryDelay\n\n if (delay == null) {\n return autoRetryDelayMs(retryIndex)\n }\n\n return typeof delay === 'number' ? delay : delay(retryIndex)\n}\n\nfunction emitCaseStart(\n hooks: TaskRunContext['reporterHooks'] | undefined,\n payload: {\n autoRetry?: number\n index: number\n input?: unknown\n name: string\n retryIndex?: number\n total: number\n },\n): void {\n try {\n hooks?.onCaseStart?.(payload)\n }\n catch {\n // Reporter hooks must never affect task scoring.\n }\n}\n\nfunction emitCaseEnd(\n hooks: TaskRunContext['reporterHooks'] | undefined,\n payload: {\n index: number\n output?: unknown\n state: 'passed' | 'failed' | 'timeout'\n name: string\n total: number\n errorMessage?: string\n },\n): void {\n try {\n hooks?.onCaseEnd?.(payload)\n }\n catch {\n // Reporter hooks must never affect task scoring.\n }\n}\n\nfunction emitReporterEvent(\n hooks: TaskRunContext['reporterHooks'] | undefined,\n payload: TaskReporterEventPayload,\n): void {\n try {\n hooks?.onEvent?.(payload)\n }\n catch {\n // Reporter hooks must never affect task scoring.\n }\n}\n\nfunction createCaseTimeoutError(timeout: number): Error {\n const error = new Error(`Case timed out after ${timeout}ms.`)\n error.name = 'TimeoutError'\n return error\n}\n\nfunction normalizeExecutionPolicy(policy: TaskExecutionPolicy | undefined, label: string): TaskExecutionPolicy | undefined {\n if (policy == null) {\n return undefined\n }\n\n if (policy.autoAttempt != null) {\n assertNonNegativeInteger(policy.autoAttempt, `${label} autoAttempt`)\n }\n\n if (policy.autoRetry != null) {\n assertNonNegativeInteger(policy.autoRetry, `${label} autoRetry`)\n }\n\n if (typeof policy.autoRetryDelay === 'number') {\n assertNonNegativeNumber(policy.autoRetryDelay, `${label} autoRetryDelay`)\n }\n\n if (policy.timeout != null) {\n assertPositiveInteger(policy.timeout, `${label} timeout`)\n }\n\n const normalized = {\n autoAttempt: policy.autoAttempt,\n autoRetry: policy.autoRetry,\n autoRetryDelay: policy.autoRetryDelay,\n timeout: policy.timeout,\n }\n\n return Object.values(normalized).some(value => value != null)\n ? normalized\n : undefined\n}\n\nfunction resolveCaseExecutionPolicy(\n taskCase: RegisteredCase<unknown>,\n taskExecutionPolicy: TaskExecutionPolicy | undefined,\n): Required<Pick<TaskExecutionPolicy, 'autoAttempt' | 'autoRetry'>> & Pick<TaskExecutionPolicy, 'autoRetryDelay' | 'timeout'> {\n return {\n autoAttempt: taskCase.executionPolicy?.autoAttempt ?? taskExecutionPolicy?.autoAttempt ?? 0,\n autoRetry: taskCase.executionPolicy?.autoRetry ?? taskExecutionPolicy?.autoRetry ?? 0,\n autoRetryDelay: taskCase.executionPolicy?.autoRetryDelay ?? taskExecutionPolicy?.autoRetryDelay,\n timeout: taskCase.executionPolicy?.timeout ?? taskExecutionPolicy?.timeout,\n }\n}\n\nasync function runCaseOnce(\n context: TaskRunContext,\n taskCase: RegisteredCase<unknown>,\n index: number,\n timeout: number | undefined,\n): Promise<CaseExecutionOutcome> {\n const customScoresByKind = new Map<RunScoreKind, number>()\n const abortController = new AbortController()\n const telemetry = context.telemetry ?? createNoopTelemetryRuntime()\n const caseId = createTaskCaseReporterId(index, taskCase.name)\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined\n let timedOut = false\n let settled = false\n\n try {\n return await telemetry.withSpan('vieval.case', {\n 'vieval.case.id': caseId,\n 'vieval.case.name': taskCase.name,\n 'vieval.task.id': context.task.id,\n 'vieval.task.name': context.task.entry.name,\n }, async () => {\n const runPromise = Promise.resolve(taskCase.run({\n ...context,\n matrix: {\n ...cloneCaseMatrix(context.task.matrix),\n inputs: taskCase.input,\n },\n metric(name, value) {\n if (abortController.signal.aborted || settled) {\n return\n }\n\n emitReporterEvent(context.reporterHooks, {\n caseId,\n data: {\n name,\n value,\n },\n event: 'task.case.metric',\n })\n telemetry.addEvent('vieval.case.metric', { name, value })\n if (canAttachMetricAsAttribute(value)) {\n telemetry.setAttributes({ [name]: value })\n }\n },\n score(score, kind = 'exact') {\n if (abortController.signal.aborted || settled) {\n return\n }\n\n assertValidScore(score)\n customScoresByKind.set(kind, score)\n telemetry.addEvent('vieval.case.score', {\n 'vieval.score.kind': kind,\n 'vieval.score.value': score,\n })\n emitReporterEvent(context.reporterHooks, {\n caseId,\n data: { kind, score },\n event: 'task.case.score',\n })\n },\n signal: abortController.signal,\n }))\n\n if (timeout != null) {\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n timedOut = true\n abortController.abort(createCaseTimeoutError(timeout))\n reject(createCaseTimeoutError(timeout))\n }, timeout)\n })\n\n const output = await Promise.race([runPromise, timeoutPromise])\n settled = true\n return {\n output,\n scoresByKind: customScoresByKind,\n state: 'passed',\n }\n }\n\n const output = await runPromise\n settled = true\n return {\n output,\n scoresByKind: customScoresByKind,\n state: 'passed',\n }\n })\n }\n catch (error) {\n settled = true\n return {\n errorMessage: errorMessageFrom(error) ?? (timedOut && timeout != null ? `Case timed out after ${timeout}ms.` : 'Unknown case failure.'),\n scoresByKind: customScoresByKind,\n state: timedOut ? 'timeout' : 'failed',\n }\n }\n finally {\n if (timeoutHandle != null) {\n clearTimeout(timeoutHandle)\n }\n }\n}\n\nasync function executeRegisteredCase(\n context: TaskRunContext,\n taskCase: RegisteredCase<unknown>,\n index: number,\n totalCases: number,\n taskExecutionPolicy: TaskExecutionPolicy | undefined,\n): Promise<CaseExecutionOutcome> {\n const resolvedPolicy = resolveCaseExecutionPolicy(taskCase, taskExecutionPolicy)\n let lastOutcome: CaseExecutionOutcome | undefined\n\n for (let retryIndex = 0; retryIndex <= resolvedPolicy.autoRetry; retryIndex += 1) {\n if (retryIndex > 0) {\n const retryDelayMs = resolveAutoRetryDelay(resolvedPolicy, retryIndex)\n assertNonNegativeNumber(retryDelayMs, 'autoRetryDelay result')\n\n if (retryDelayMs > 0) {\n await sleep(retryDelayMs)\n }\n }\n\n emitCaseStart(context.reporterHooks, {\n ...(resolvedPolicy.autoRetry > 0\n ? {\n autoRetry: resolvedPolicy.autoRetry,\n retryIndex,\n }\n : {}),\n index,\n ...(taskCase.input === undefined ? {} : { input: taskCase.input }),\n name: taskCase.name,\n total: totalCases,\n })\n lastOutcome = await runCaseOnce(context, taskCase, index, resolvedPolicy.timeout)\n if (lastOutcome.state === 'passed') {\n return lastOutcome\n }\n }\n\n return lastOutcome ?? {\n errorMessage: 'Unknown case failure.',\n scoresByKind: new Map(),\n state: 'failed',\n }\n}\n\nfunction collectCaseOutcomeScores(\n outcome: CaseExecutionOutcome,\n scoreBucketsByKind: Record<RunScoreKind, number[]>,\n): void {\n if (outcome.state !== 'passed') {\n scoreBucketsByKind.exact.push(0)\n return\n }\n\n if (outcome.scoresByKind.size === 0) {\n scoreBucketsByKind.exact.push(1)\n return\n }\n\n scoreBucketsByKind.exact.push(outcome.scoresByKind.get('exact') ?? 1)\n const judgeScore = outcome.scoresByKind.get('judge')\n if (judgeScore != null) {\n scoreBucketsByKind.judge.push(judgeScore)\n }\n}\n\n/**\n * Builder callbacks passed into `describeTask`.\n */\nexport interface DescribeTaskBuilder {\n /**\n * Registers one explicit case.\n */\n caseOf: {\n (name: string, run: CaseRunner<undefined>): void\n <TInput>(name: string, run: CaseRunner<TInput>, options: CaseRegistrationOptions<TInput>): void\n }\n /**\n * Registers multiple cases from input list.\n */\n casesFromInputs: <TInput>(\n namePrefix: string,\n inputs: readonly TInput[],\n run: CaseRunner<TInput>,\n options?: CasesFromInputsOptions,\n ) => void\n}\n\n/**\n * Options for `describeTask`.\n */\nexport interface DescribeTaskOptions extends TaskExecutionPolicy {\n /**\n * Optional description override.\n */\n description?: string\n /**\n * Optional task-local concurrency overrides.\n *\n * Use when:\n * - one task should cap attempt fan-out independently from the surrounding project\n * - one task should cap case fan-out without changing global scheduling defaults\n *\n * Expects:\n * - each provided value to be a positive integer\n *\n * @default inherited from project or CLI concurrency settings\n */\n concurrency?: TaskConcurrencyConfig\n}\n\nfunction createCaseBuilder(registeredCases: RegisteredCase<unknown>[]): DescribeTaskBuilder {\n function registerCase(name: string, run: CaseRunner<undefined>): void\n function registerCase<TInput>(name: string, run: CaseRunner<TInput>, options: CaseRegistrationOptions<TInput>): void\n function registerCase<TInput>(\n name: string,\n run: CaseRunner<TInput> | CaseRunner<undefined>,\n options?: CaseRegistrationOptions<TInput>,\n ): void {\n registeredCases.push({\n executionPolicy: normalizeExecutionPolicy(options, 'task case'),\n input: options?.input,\n name,\n run: run as CaseRunner<unknown>,\n })\n }\n\n return {\n caseOf: registerCase,\n casesFromInputs(namePrefix, inputs, run, options) {\n const queueKey = options?.concurrency == null ? undefined : {}\n\n inputs.forEach((input, index) => {\n registeredCases.push({\n concurrency: options?.concurrency,\n executionPolicy: normalizeExecutionPolicy(options, 'casesFromInputs'),\n input,\n name: `${namePrefix} #${index + 1}`,\n queueKey,\n run: run as CaseRunner<unknown>,\n })\n })\n },\n }\n}\n\nlet activeCasesStack: RegisteredCase<unknown>[][] = []\n\nfunction withActiveCases<T>(cases: RegisteredCase<unknown>[], callback: () => T): T {\n activeCasesStack = [...activeCasesStack, cases]\n\n try {\n return callback()\n }\n finally {\n activeCasesStack = activeCasesStack.slice(0, -1)\n }\n}\n\nfunction getActiveCases(): RegisteredCase<unknown>[] {\n const active = activeCasesStack.at(-1)\n if (active == null) {\n throw new Error('caseOf/casesFromInputs must be called inside describeTask/describeEval.')\n }\n\n return active\n}\n\n/**\n * Registers one case in the currently active task scope.\n */\nexport function caseOf(\n name: string,\n run: CaseRunner<undefined>,\n): void\n\nexport function caseOf<TInput>(\n name: string,\n run: CaseRunner<TInput>,\n options: CaseRegistrationOptions<TInput>,\n): void\n\nexport function caseOf<TInput>(\n name: string,\n run: CaseRunner<TInput> | CaseRunner<undefined>,\n options?: CaseRegistrationOptions<TInput>,\n): void {\n getActiveCases().push({\n executionPolicy: normalizeExecutionPolicy(options, 'task case'),\n input: options?.input,\n name,\n run: run as CaseRunner<unknown>,\n })\n}\n\n/**\n * Registers multiple cases in the currently active task scope.\n */\nexport function casesFromInputs<TInput>(\n namePrefix: string,\n inputs: readonly TInput[],\n run: CaseRunner<TInput>,\n options?: CasesFromInputsOptions,\n): void {\n const queueKey = options?.concurrency == null ? undefined : {}\n\n inputs.forEach((input, index) => {\n getActiveCases().push({\n concurrency: options?.concurrency,\n executionPolicy: normalizeExecutionPolicy(options, 'casesFromInputs'),\n input,\n name: `${namePrefix} #${index + 1}`,\n queueKey,\n run: run as CaseRunner<unknown>,\n })\n })\n}\n\n/**\n * Resolves the effective case concurrency for one registered task case.\n *\n * Before:\n * - registered case override `2`, task default `4`\n * - registered case override `undefined`, task default `3`\n *\n * After:\n * - `2`\n * - `3`\n */\nfunction resolveCaseConcurrency(\n taskCase: RegisteredCase<unknown>,\n taskConcurrency: TaskConcurrencyConfig | undefined,\n runtimeConcurrency: TaskConcurrencyConfig | undefined,\n): number | undefined {\n const concurrency = runtimeConcurrency?.case ?? taskCase.concurrency ?? taskConcurrency?.case\n if (concurrency == null) {\n return undefined\n }\n\n if (!Number.isFinite(concurrency) || !Number.isInteger(concurrency) || concurrency <= 0) {\n throw new Error(`Invalid task case concurrency: ${String(concurrency)}`)\n }\n\n return concurrency\n}\n\nfunction resolveCaseQueueKey(taskCase: RegisteredCase<unknown>, defaultQueueKey: object): object {\n return taskCase.queueKey ?? defaultQueueKey\n}\n\n/**\n * Defines one eval task with task/case semantics similar to Vitest.\n *\n * Use when:\n * - task behavior should be declared with `caseOf` and `casesFromInputs`\n * - business agent code should be imported and run from eval task files\n */\nexport function describeTask(\n name: string,\n build: ((builder: DescribeTaskBuilder) => void) | (() => void),\n options: DescribeTaskOptions = {},\n) {\n const registeredCases: RegisteredCase<unknown>[] = []\n const builder = createCaseBuilder(registeredCases)\n withActiveCases(registeredCases, () => {\n if (build.length > 0) {\n (build as (builder: DescribeTaskBuilder) => void)(builder)\n return\n }\n\n ;(build as () => void)()\n })\n\n const description = options.description ?? name\n const taskExecutionPolicy = normalizeExecutionPolicy(options, 'describeTask')\n\n const definition = defineEval({\n description,\n name,\n task: defineTask({\n concurrency: options.concurrency,\n executionPolicy: taskExecutionPolicy,\n id: name,\n async run(context): Promise<TaskRunOutput> {\n if (registeredCases.length === 0) {\n return {\n scores: [{ kind: 'exact', score: 1 }],\n }\n }\n\n const totalCases = registeredCases.length\n const scoreBucketsByKind: Record<RunScoreKind, number[]> = {\n exact: [],\n judge: [],\n }\n const defaultCaseQueueKey = {}\n const caseQueues = new Map<object, ReturnType<typeof createSchedulerQueue>>()\n const hasAutoAttempt = registeredCases.some(taskCase => resolveCaseExecutionPolicy(taskCase, taskExecutionPolicy).autoAttempt > 0)\n const runtimeTaskConcurrency = context.task.entry.task?.concurrency ?? options.concurrency\n\n if (!hasAutoAttempt) {\n await Promise.all(\n registeredCases.map(async (taskCase, index) => {\n const executeCase = async () => {\n const outcome = await executeRegisteredCase(context, taskCase, index, totalCases, taskExecutionPolicy)\n emitCaseEnd(context.reporterHooks, {\n ...(outcome.errorMessage == null ? {} : { errorMessage: outcome.errorMessage }),\n index,\n ...(outcome.output === undefined ? {} : { output: outcome.output }),\n state: outcome.state,\n name: taskCase.name,\n total: totalCases,\n })\n collectCaseOutcomeScores(outcome, scoreBucketsByKind)\n }\n\n const concurrency = resolveCaseConcurrency(taskCase, runtimeTaskConcurrency, context.runtimeConcurrency)\n if (concurrency == null) {\n await executeCase()\n return\n }\n\n const queueKey = resolveCaseQueueKey(taskCase, defaultCaseQueueKey)\n const queue = caseQueues.get(queueKey) ?? createSchedulerQueue(concurrency)\n caseQueues.set(queueKey, queue)\n await queue.run(executeCase)\n }),\n )\n }\n else {\n let finalOutcomes: CaseExecutionOutcome[] = []\n let attemptIndex = 0\n\n for (;;) {\n finalOutcomes = await Promise.all(\n registeredCases.map(async (taskCase, index) => {\n const executeCase = async () => await executeRegisteredCase(context, taskCase, index, totalCases, taskExecutionPolicy)\n const concurrency = resolveCaseConcurrency(taskCase, runtimeTaskConcurrency, context.runtimeConcurrency)\n if (concurrency == null) {\n return await executeCase()\n }\n\n const queueKey = resolveCaseQueueKey(taskCase, defaultCaseQueueKey)\n const queue = caseQueues.get(queueKey) ?? createSchedulerQueue(concurrency)\n caseQueues.set(queueKey, queue)\n return await queue.run(executeCase)\n }),\n )\n\n const shouldContinue = finalOutcomes.some((outcome, index) => {\n if (outcome.state === 'passed') {\n return false\n }\n\n const taskCase = registeredCases[index]\n if (taskCase == null) {\n return false\n }\n\n return attemptIndex < resolveCaseExecutionPolicy(taskCase, taskExecutionPolicy).autoAttempt\n })\n\n if (!shouldContinue) {\n break\n }\n\n attemptIndex += 1\n }\n\n finalOutcomes.forEach((outcome, index) => {\n const taskCase = registeredCases[index]\n if (taskCase == null) {\n return\n }\n\n emitCaseEnd(context.reporterHooks, {\n ...(outcome.errorMessage == null ? {} : { errorMessage: outcome.errorMessage }),\n index,\n ...(outcome.output === undefined ? {} : { output: outcome.output }),\n state: outcome.state,\n name: taskCase.name,\n total: totalCases,\n })\n collectCaseOutcomeScores(outcome, scoreBucketsByKind)\n })\n }\n\n const scores = (Object.keys(scoreBucketsByKind) as RunScoreKind[])\n .filter(kind => scoreBucketsByKind[kind].length > 0)\n .map((kind) => {\n const values = scoreBucketsByKind[kind]\n const total = values.reduce((sum, value) => sum + value, 0)\n return {\n kind,\n score: total / values.length,\n }\n })\n\n return {\n scores,\n }\n },\n }),\n })\n\n registerEvalDefinition(definition)\n\n return definition\n}\n\n/**\n * Alias of `describeTask` for eval-centric naming.\n */\nexport const describeEval = describeTask\n"],"mappings":"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"}
@@ -11,4 +11,4 @@ function resolveModelByName(models, name) {
11
11
  //#endregion
12
12
  export { resolveModelByName as t };
13
13
 
14
- //# sourceMappingURL=models-D_MsBtYw.mjs.map
14
+ //# sourceMappingURL=models-DIGdOUpJ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models-DIGdOUpJ.mjs","names":[],"sources":["../src/config/models.ts"],"sourcesContent":["import type { TaskExecutionPolicy } from './types'\n\n/**\n * Canonical model definition consumed by vieval runtime and config.\n *\n * Use when:\n * - declaring models in `vieval.config.*`\n * - resolving task runtime models by id, alias, or concrete model name\n *\n * Expects:\n * - `id` to be stable and unique within one config\n * - `inferenceExecutorId` to match scheduler/executor identifiers\n *\n * Returns:\n * - one normalized model registration record\n */\nexport interface ModelDefinition {\n /**\n * Stable model id.\n */\n id: string\n /**\n * Inference-executor id used for matching and reporting.\n */\n inferenceExecutorId: string\n /**\n * Executor reference passed through config.\n *\n * `vieval` core treats this as opaque runtime metadata. Builder plugins can\n * narrow this field with plugin-specific executor input types.\n */\n inferenceExecutor: unknown\n /**\n * Concrete model name passed to the inference executor.\n */\n model: string\n /**\n * Alias names that can resolve this model.\n */\n aliases: string[]\n /**\n * Optional execution policy hints attached to this model.\n */\n executionPolicy?: TaskExecutionPolicy\n /**\n * Optional model-level call parameters.\n */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Resolves one model by id, model name, or alias in registration order.\n *\n * Returns:\n * - the first matching model, or `undefined` when no match exists\n */\nexport function resolveModelByName(\n models: readonly ModelDefinition[],\n name: string,\n): ModelDefinition | undefined {\n return models.find(model => model.id === name || model.model === name || model.aliases.includes(name))\n}\n"],"mappings":";;;;;;;AAwDA,SAAgB,mBACd,QACA,MAC6B;AAC7B,QAAO,OAAO,MAAK,UAAS,MAAM,OAAO,QAAQ,MAAM,UAAU,QAAQ,MAAM,QAAQ,SAAS,KAAK,CAAC"}
@@ -1,4 +1,4 @@
1
- import { M as ModelDefinition, l as MatrixDefinition, t as ConfigHookPlugin, x as TaskRunContext } from "../../index-OEdqjQSe.mjs";
1
+ import { D as TaskRunContext, f as MatrixDefinition, t as ConfigHookPlugin, w as TaskExecutionPolicy, z as ModelDefinition } from "../../index-5R1_k2nv.mjs";
2
2
 
3
3
  //#region src/plugins/chat-models/runtime-config.d.ts
4
4
  /**
@@ -382,6 +382,32 @@ interface ChatModelFromBaseOptions {
382
382
  * Alias names used by `resolveModelByName`.
383
383
  */
384
384
  aliases?: string[];
385
+ /**
386
+ * Optional execution policy hints attached to this model.
387
+ */
388
+ executionPolicy?: TaskExecutionPolicy;
389
+ /**
390
+ * Additional retries allowed within the current attempt.
391
+ *
392
+ * @default 0
393
+ */
394
+ autoRetry?: number;
395
+ /**
396
+ * Delay in milliseconds before a retry starts.
397
+ *
398
+ * @default retryIndex => 500 * 2 ** (retryIndex - 1)
399
+ */
400
+ autoRetryDelay?: TaskExecutionPolicy['autoRetryDelay'];
401
+ /**
402
+ * Additional full task attempts allowed after the current attempt settles.
403
+ *
404
+ * @default 0
405
+ */
406
+ autoAttempt?: number;
407
+ /**
408
+ * Timeout in milliseconds for model-backed work.
409
+ */
410
+ timeout?: number;
385
411
  /**
386
412
  * Optional model-level call parameters.
387
413
  */
@@ -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--94B0UtW.mjs";
2
2
  import process from "node:process";
3
3
  import { errorMessageFrom } from "@moeru/std";
4
4
  //#region src/plugins/chat-models/runtime-config.ts
@@ -288,6 +288,33 @@ function emitChatModelErrorTelemetry(context, options) {
288
288
  }
289
289
  //#endregion
290
290
  //#region src/plugins/chat-models/index.ts
291
+ function normalizeExecutionPolicy(policy) {
292
+ if (policy == null) return;
293
+ const normalized = {
294
+ autoAttempt: policy.autoAttempt,
295
+ autoRetry: policy.autoRetry,
296
+ autoRetryDelay: policy.autoRetryDelay,
297
+ timeout: policy.timeout
298
+ };
299
+ return Object.values(normalized).some((value) => value != null) ? normalized : void 0;
300
+ }
301
+ function hasJudgeAlias(model) {
302
+ return [
303
+ ...model.aliases ?? [],
304
+ ...model.id == null ? [] : [model.id],
305
+ model.model
306
+ ].some((value) => value.toLowerCase().includes("judge"));
307
+ }
308
+ function resolveModelExecutionPolicy(options) {
309
+ const explicitPolicy = normalizeExecutionPolicy({
310
+ autoAttempt: options.autoAttempt ?? options.executionPolicy?.autoAttempt,
311
+ autoRetry: options.autoRetry ?? options.executionPolicy?.autoRetry,
312
+ autoRetryDelay: options.autoRetryDelay ?? options.executionPolicy?.autoRetryDelay,
313
+ timeout: options.timeout ?? options.executionPolicy?.timeout
314
+ });
315
+ if (explicitPolicy != null && Object.keys(explicitPolicy).length > 0) return explicitPolicy;
316
+ if (hasJudgeAlias(options)) return { autoRetry: 3 };
317
+ }
291
318
  function normalizeInferenceExecutorId(inferenceExecutor, inferenceExecutorId) {
292
319
  if (typeof inferenceExecutor === "string") return inferenceExecutor;
293
320
  return inferenceExecutorId ?? "custom";
@@ -424,6 +451,7 @@ function chatModelFrom(options) {
424
451
  } : void 0;
425
452
  return {
426
453
  aliases: options.aliases ?? [],
454
+ executionPolicy: resolveModelExecutionPolicy(options),
427
455
  id: options.id ?? createDefaultModelId(inferenceExecutorId, options.model),
428
456
  inferenceExecutor: fallbackInferenceExecutor,
429
457
  inferenceExecutorId,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/chat-models/runtime-config.ts","../../../src/plugins/chat-models/telemetry.ts","../../../src/plugins/chat-models/index.ts"],"sourcesContent":["import type { ModelDefinition } from '../../config/models'\nimport type { ChatModelHeaders } from './index'\n\nimport { envFrom, requiredEnvFrom } from '../../core/inference-executors/env'\n\n/**\n * Runtime config consumed by OpenAI-compatible provider constructors.\n */\nexport interface OpenAIChatModelRuntimeConfig {\n /**\n * Resolved inference executor kind.\n */\n inferenceExecutor: 'openai'\n /**\n * Concrete model name.\n */\n model: string\n /**\n * Required API key.\n */\n apiKey: string\n /**\n * Optional base URL override.\n */\n baseURL?: string\n /**\n * Optional request headers.\n */\n headers?: ChatModelHeaders\n}\n\n/**\n * Runtime config consumed by Ollama provider constructors.\n */\nexport interface OllamaChatModelRuntimeConfig {\n /**\n * Resolved inference executor kind.\n */\n inferenceExecutor: 'ollama'\n /**\n * Concrete model name.\n */\n model: string\n /**\n * Optional base URL override.\n */\n baseURL?: string\n /**\n * Optional request headers.\n */\n headers?: ChatModelHeaders\n}\n\n/**\n * Runtime config consumed by OpenRouter provider constructors.\n */\nexport interface OpenRouterChatModelRuntimeConfig {\n /**\n * Resolved inference executor kind.\n */\n inferenceExecutor: 'openrouter'\n /**\n * Concrete model name.\n */\n model: string\n /**\n * Required API key.\n */\n apiKey: string\n /**\n * Optional base URL override.\n */\n baseURL?: string\n /**\n * Optional request headers.\n */\n headers?: ChatModelHeaders\n}\n\n/**\n * Union of normalized runtime configs for supported chat-model executors.\n */\nexport type ChatModelRuntimeConfig\n = OpenAIChatModelRuntimeConfig\n | OllamaChatModelRuntimeConfig\n | OpenRouterChatModelRuntimeConfig\n\nfunction getParameters(model: ModelDefinition): Record<string, unknown> {\n return model.parameters ?? {}\n}\n\nfunction parseOptionalStringParameter(\n parameters: Record<string, unknown>,\n key: string,\n modelId: string,\n): string | undefined {\n const value = parameters[key]\n const normalized = value == null ? undefined : String(value)\n\n return envFrom(normalized, {\n name: `${modelId}.parameters.${key}`,\n type: 'string',\n })\n}\n\nfunction parseRequiredStringParameter(\n parameters: Record<string, unknown>,\n key: string,\n modelId: string,\n): string {\n const value = parameters[key]\n const normalized = value == null ? undefined : String(value)\n\n return requiredEnvFrom(normalized, {\n name: `${modelId}.parameters.${key}`,\n type: 'string',\n })\n}\n\nfunction parseHeadersParameter(\n parameters: Record<string, unknown>,\n modelId: string,\n): ChatModelHeaders | undefined {\n const headers = parameters.headers\n if (headers == null) {\n return undefined\n }\n\n if (typeof headers !== 'object' || Array.isArray(headers)) {\n throw new TypeError(`Invalid ${modelId}.parameters.headers: expected an object.`)\n }\n\n const normalized: Record<string, string | string[]> = {}\n for (const [key, value] of Object.entries(headers as Record<string, unknown>)) {\n if (typeof value === 'string') {\n normalized[key] = value\n continue\n }\n\n if (Array.isArray(value) && value.every(item => typeof item === 'string')) {\n normalized[key] = value\n continue\n }\n\n throw new Error(`Invalid ${modelId}.parameters.headers.${key}: expected string or string[].`)\n }\n\n return normalized\n}\n\n/**\n * Normalizes one configured chat model into runtime executor config.\n *\n * Use when:\n * - eval code needs typed provider constructor options from `context.model()`\n * - model parameters should be validated once with clear error messages\n *\n * Expects:\n * - `model.inferenceExecutorId` to be one of the supported executor ids\n * - required OpenAI fields (apiKey) to exist in `model.parameters`\n *\n * Returns:\n * - validated runtime config union for OpenAI or Ollama\n */\nexport function toChatModelRuntimeConfig(model: ModelDefinition): ChatModelRuntimeConfig {\n const parameters = getParameters(model)\n\n if (model.inferenceExecutorId === 'openai') {\n return {\n apiKey: parseRequiredStringParameter(parameters, 'apiKey', model.id),\n baseURL: parseOptionalStringParameter(parameters, 'baseURL', model.id),\n headers: parseHeadersParameter(parameters, model.id),\n inferenceExecutor: 'openai',\n model: model.model,\n }\n }\n\n if (model.inferenceExecutorId === 'ollama') {\n return {\n baseURL: parseOptionalStringParameter(parameters, 'baseURL', model.id),\n headers: parseHeadersParameter(parameters, model.id),\n inferenceExecutor: 'ollama',\n model: model.model,\n }\n }\n\n if (model.inferenceExecutorId === 'openrouter') {\n return {\n apiKey: parseRequiredStringParameter(parameters, 'apiKey', model.id),\n baseURL: parseOptionalStringParameter(parameters, 'baseURL', model.id),\n headers: parseHeadersParameter(parameters, model.id),\n inferenceExecutor: 'openrouter',\n model: model.model,\n }\n }\n\n throw new Error(`Unsupported chat inference executor \"${model.inferenceExecutorId}\" for model \"${model.id}\".`)\n}\n\n/**\n * Resolves OpenAI runtime config from one resolved run-context model.\n *\n * Use when:\n * - task execution already has `context.model()` output\n * - eval code wants typed OpenAI provider options with a concise helper name\n *\n * Expects:\n * - `model` to resolve to an OpenAI-backed chat model\n *\n * Returns:\n * - validated OpenAI runtime config\n */\nexport function openaiFromRunContext(model: ModelDefinition): OpenAIChatModelRuntimeConfig {\n const runtimeConfig = toChatModelRuntimeConfig(model)\n if (runtimeConfig.inferenceExecutor !== 'openai') {\n throw new Error(`Expected openai model, got \"${runtimeConfig.inferenceExecutor}\" for \"${model.id}\".`)\n }\n\n return runtimeConfig\n}\n\n/**\n * Resolves Ollama runtime config from one resolved run-context model.\n *\n * Use when:\n * - task execution already has `context.model()` output\n * - eval code wants typed Ollama provider options with a concise helper name\n *\n * Expects:\n * - `model` to resolve to an Ollama-backed chat model\n *\n * Returns:\n * - validated Ollama runtime config\n */\nexport function ollamaFromRunContext(model: ModelDefinition): OllamaChatModelRuntimeConfig {\n const runtimeConfig = toChatModelRuntimeConfig(model)\n if (runtimeConfig.inferenceExecutor !== 'ollama') {\n throw new Error(`Expected ollama model, got \"${runtimeConfig.inferenceExecutor}\" for \"${model.id}\".`)\n }\n\n return runtimeConfig\n}\n\n/**\n * Resolves OpenRouter runtime config from one resolved run-context model.\n *\n * Use when:\n * - task execution already has `context.model()` output\n * - eval code wants typed OpenRouter provider options with a concise helper name\n *\n * Expects:\n * - `model` to resolve to an OpenRouter-backed chat model\n *\n * Returns:\n * - validated OpenRouter runtime config\n */\nexport function openrouterFromRunContext(model: ModelDefinition): OpenRouterChatModelRuntimeConfig {\n const runtimeConfig = toChatModelRuntimeConfig(model)\n if (runtimeConfig.inferenceExecutor !== 'openrouter') {\n throw new Error(`Expected openrouter model, got \"${runtimeConfig.inferenceExecutor}\" for \"${model.id}\".`)\n }\n\n return runtimeConfig\n}\n","import type { TaskRunContext } from '../../config/types'\n\nimport { errorMessageFrom } from '@moeru/std'\n\n/**\n * Represents one normalized chat-model tool call.\n *\n * Use when:\n * - report events need tool-call level payloads that remain provider-neutral\n *\n * Expects:\n * - `name` to be stable enough for aggregation and assertion checks\n * - `args` to be JSON-serializable\n */\nexport interface ChatModelToolCall {\n /**\n * Optional provider-assigned tool-call identifier.\n */\n id?: string\n /**\n * Tool name.\n */\n name: string\n /**\n * Parsed tool arguments object/value.\n */\n args: unknown\n}\n\n/**\n * Provider identity attached to chat-model telemetry events.\n */\nexport interface ChatModelTelemetryProvider {\n /**\n * Provider id, for example `openai`.\n */\n id: string\n /**\n * Optional concrete model id/name.\n */\n model?: string\n}\n\n/**\n * Input options for response telemetry emission.\n */\nexport interface EmitChatModelResponseTelemetryOptions {\n /**\n * Optional case id for case-scoped telemetry events.\n */\n caseId?: string\n /**\n * Optional response latency in milliseconds.\n */\n latencyMs?: number\n /**\n * Optional provider identity payload.\n */\n provider?: ChatModelTelemetryProvider\n /**\n * Raw chat-model response object from the inference library/provider.\n */\n response: unknown\n}\n\n/**\n * Input options for request telemetry emission.\n */\nexport interface EmitChatModelRequestTelemetryOptions {\n /**\n * Optional case id for case-scoped telemetry events.\n */\n caseId?: string\n /**\n * Optional request payload metadata.\n */\n data?: unknown\n /**\n * Optional provider identity payload.\n */\n provider?: ChatModelTelemetryProvider\n}\n\n/**\n * Input options for error telemetry emission.\n */\nexport interface EmitChatModelErrorTelemetryOptions {\n /**\n * Optional case id for case-scoped telemetry events.\n */\n caseId?: string\n /**\n * Error payload emitted by the inference client/runtime.\n */\n error: unknown\n /**\n * Optional provider identity payload.\n */\n provider?: ChatModelTelemetryProvider\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (value == null || typeof value !== 'object') {\n return undefined\n }\n\n return value as Record<string, unknown>\n}\n\nfunction parseMaybeJson(value: unknown): unknown {\n if (typeof value !== 'string') {\n return value\n }\n\n try {\n return JSON.parse(value)\n }\n catch {\n return value\n }\n}\n\n/**\n * Extracts normalized tool calls from one chat-model response shape.\n *\n * Use when:\n * - downstream scoring, reporting, or analysis should inspect tool call usage\n * - provider payload differences should stay hidden behind one stable shape\n *\n * Returns:\n * - normalized list of `{ id?, name, args }` tool calls\n */\nexport function extractChatModelToolCalls(response: unknown): ChatModelToolCall[] {\n const responseRecord = asRecord(response)\n if (responseRecord == null) {\n return []\n }\n\n const rawToolCalls = responseRecord.toolCalls ?? responseRecord.tool_calls\n if (!Array.isArray(rawToolCalls)) {\n return []\n }\n\n const toolCalls: ChatModelToolCall[] = []\n\n for (const rawToolCall of rawToolCalls) {\n const toolCallRecord = asRecord(rawToolCall)\n if (toolCallRecord == null) {\n continue\n }\n\n const functionPayload = asRecord(toolCallRecord.function)\n const name = typeof toolCallRecord.name === 'string'\n ? toolCallRecord.name\n : typeof functionPayload?.name === 'string'\n ? functionPayload.name\n : undefined\n\n if (name == null || name.length === 0) {\n continue\n }\n\n const rawArgs = toolCallRecord.args\n ?? toolCallRecord.arguments\n ?? functionPayload?.args\n ?? functionPayload?.arguments\n\n toolCalls.push({\n args: parseMaybeJson(rawArgs),\n id: typeof toolCallRecord.id === 'string' ? toolCallRecord.id : undefined,\n name,\n })\n }\n\n return toolCalls\n}\n\n/**\n * Extracts numeric metering dimensions from one chat-model response usage block.\n *\n * Use when:\n * - report events should capture usage dimensions in a modality-neutral map\n *\n * Returns:\n * - numeric dimensions keyed by provider usage field names\n */\nexport function extractMeteringDimensions(response: unknown): Record<string, number> {\n const responseRecord = asRecord(response)\n const usage = asRecord(responseRecord?.usage)\n if (usage == null) {\n return {}\n }\n\n const dimensions: Record<string, number> = {}\n\n for (const [key, value] of Object.entries(usage)) {\n if (typeof value !== 'number' || Number.isNaN(value)) {\n continue\n }\n\n dimensions[key] = value\n }\n\n return dimensions\n}\n\n/**\n * Emits chat-model response telemetry as reportable task events.\n *\n * Use when:\n * - task code receives one chat-model response and wants standardized report events\n * - `ToolCall*` and metering metrics should be persisted in `events.jsonl`\n *\n * Expects:\n * - `context.reporterHooks?.onEvent` to be available in CLI execution paths\n *\n * Returns:\n * - no return value; this is a best-effort reporting helper\n */\nexport function emitChatModelResponseTelemetry(\n context: TaskRunContext,\n options: EmitChatModelResponseTelemetryOptions,\n): void {\n const toolCalls = extractChatModelToolCalls(options.response)\n const meteringDimensions = extractMeteringDimensions(options.response)\n\n if (toolCalls.length > 0) {\n meteringDimensions.tool_call_count = toolCalls.length\n }\n\n const data = {\n metering: {\n dimensions: meteringDimensions,\n latency_ms: options.latencyMs,\n },\n metrics: {\n 'vieval.chat.tool_call_count': toolCalls.length,\n },\n modality: 'chat',\n provider: options.provider,\n toolCalls,\n }\n\n context.reporterHooks?.onEvent?.({\n caseId: options.caseId,\n data,\n event: 'InferenceResponse',\n })\n\n for (const toolCall of toolCalls) {\n context.reporterHooks?.onEvent?.({\n caseId: options.caseId,\n data: {\n modality: 'chat',\n provider: options.provider,\n toolCall,\n },\n event: 'ToolCallStarted',\n })\n context.reporterHooks?.onEvent?.({\n caseId: options.caseId,\n data: {\n modality: 'chat',\n provider: options.provider,\n toolCall,\n },\n event: 'ToolCallEnded',\n })\n }\n}\n\n/**\n * Emits chat-model request telemetry as a reportable task event.\n *\n * Use when:\n * - task code submits one model request and wants request-side traceability\n *\n * Expects:\n * - `context.reporterHooks?.onEvent` to be available in CLI execution paths\n */\nexport function emitChatModelRequestTelemetry(\n context: TaskRunContext,\n options: EmitChatModelRequestTelemetryOptions,\n): void {\n context.reporterHooks?.onEvent?.({\n caseId: options.caseId,\n data: {\n data: options.data,\n modality: 'chat',\n provider: options.provider,\n },\n event: 'InferenceRequest',\n })\n}\n\n/**\n * Emits chat-model failure telemetry as a reportable task event.\n *\n * Use when:\n * - one inference call fails and report artifacts should include normalized error context\n *\n * Expects:\n * - `context.reporterHooks?.onEvent` to be available in CLI execution paths\n */\nexport function emitChatModelErrorTelemetry(\n context: TaskRunContext,\n options: EmitChatModelErrorTelemetryOptions,\n): void {\n context.reporterHooks?.onEvent?.({\n caseId: options.caseId,\n data: {\n error: errorMessageFrom(options.error) ?? 'Unknown inference error.',\n modality: 'chat',\n provider: options.provider,\n },\n event: 'InferenceError',\n })\n}\n","import type { MatrixDefinition } from '../../config'\nimport type { ModelDefinition } from '../../config/models'\nimport type { ConfigHookPlugin } from '../../config/plugin'\nimport type { EnvFromOptions, RequiredEnvFromOptions } from '../../core/inference-executors/env'\n\nimport process from 'node:process'\n\nimport { envFrom, requiredEnvFrom } from '../../core/inference-executors/env'\n\n/**\n * Minimal inference-executor shape expected by chat model runtime callers.\n */\nexport interface ChatModelExecutorLike {\n chat: (model: string) => Record<string, unknown>\n}\n\n/**\n * Inference-executor input accepted by `chatModelFrom`.\n */\nexport type ChatModelExecutorInput = string | ChatModelExecutorLike\n\n/**\n * Chat-model header payload accepted by executor parameters.\n */\nexport type ChatModelHeaders = Record<string, string | string[]>\n\n/**\n * Runtime env context passed to model callback resolvers.\n */\nexport interface ChatModelResolverContext {\n env: Record<string, string>\n}\n\n/**\n * Value-or-callback resolver used by model runtime fields.\n */\nexport type ChatModelResolverValue<TValue> = TValue | ((config: ChatModelResolverContext) => Promise<TValue> | TValue)\n\n/**\n * OpenAI-specific inference executor config shape.\n */\nexport interface OpenAIChatModelInferenceExecutor {\n inferenceExecutor: 'openai'\n apiKey?: ChatModelResolverValue<string>\n baseURL?: ChatModelResolverValue<string>\n headers?: ChatModelResolverValue<ChatModelHeaders>\n}\n\n/**\n * Ollama-specific inference executor config shape.\n */\nexport interface OllamaChatModelInferenceExecutor {\n inferenceExecutor: 'ollama'\n baseURL?: ChatModelResolverValue<string>\n headers?: ChatModelResolverValue<ChatModelHeaders>\n}\n\n/**\n * OpenRouter-specific inference executor config shape.\n */\nexport interface OpenRouterChatModelInferenceExecutor {\n inferenceExecutor: 'openrouter'\n apiKey?: ChatModelResolverValue<string>\n baseURL?: ChatModelResolverValue<string>\n headers?: ChatModelResolverValue<ChatModelHeaders>\n}\n\n/**\n * Generic inference executor config shape.\n */\nexport interface GenericChatModelInferenceExecutor {\n inferenceExecutor?: ChatModelExecutorInput\n}\n\n/**\n * Union of supported inference executor config shapes for `chatModelFrom`.\n */\nexport type ChatModelInferenceExecutor\n = OpenAIChatModelInferenceExecutor\n | OllamaChatModelInferenceExecutor\n | OpenRouterChatModelInferenceExecutor\n | GenericChatModelInferenceExecutor\n\n/**\n * Common builder input fields for `chatModelFrom`.\n */\nexport interface ChatModelFromBaseOptions {\n /**\n * Provider id registered through `ChatProviders`.\n *\n * Use when:\n * - model runtime transport and credentials should be delegated to a named provider preset\n *\n * Expects:\n * - one `ChatProviders` plugin entry to expose the same id\n */\n provider?: string\n /**\n * Inference-executor id or inference-executor instance.\n */\n inferenceExecutor?: ChatModelExecutorInput\n /**\n * Optional explicit inference-executor id for inference-executor instances.\n *\n * @default 'custom'\n */\n inferenceExecutorId?: string\n /**\n * Concrete model name.\n */\n model: string\n /**\n * Optional stable model id.\n *\n * @default `${inferenceExecutorId}:${model}`\n */\n id?: string\n /**\n * Alias names used by `resolveModelByName`.\n */\n aliases?: string[]\n /**\n * Optional model-level call parameters.\n */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Builder input for `chatModelFrom`.\n */\nexport type ChatModelFromOptions = ChatModelInferenceExecutor & ChatModelFromBaseOptions\n\n/**\n * Chat-model specific specialization of the canonical `ModelDefinition`.\n */\nexport type ChatModelDefinition = Omit<ModelDefinition, 'inferenceExecutor'> & {\n inferenceExecutor: ChatModelExecutorInput\n provider?: string\n runtimeResolvers?: {\n apiKey?: ChatModelResolverValue<string>\n baseURL?: ChatModelResolverValue<string>\n headers?: ChatModelResolverValue<ChatModelHeaders>\n }\n}\n\n/**\n * Env-key map for optional provider parameters.\n *\n * Use when:\n * - provider parameter values should be read from env keys\n * - missing keys should resolve to `undefined`\n */\nexport type OptionalProviderEnvMap = Record<string, string>\n\n/**\n * Env-key map for required provider parameters.\n *\n * Use when:\n * - provider parameter values must exist before model execution\n * - missing keys should throw with key-aware error messages\n */\nexport type RequiredProviderEnvMap = Record<string, string>\n\n/**\n * One provider definition consumed by chat model presets.\n */\nexport interface ChatProviderDefinition {\n /**\n * Stable provider id referenced by `chatModelFrom({ provider })`.\n */\n id: string\n /**\n * Inference-executor id or instance used by this provider preset.\n */\n inferenceExecutor: ChatModelExecutorInput\n /**\n * Optional explicit inference-executor id for inference-executor instances.\n *\n * @default 'custom'\n */\n inferenceExecutorId?: string\n /**\n * Optional literal provider-level parameters.\n */\n parameters?: Record<string, unknown>\n /**\n * Optional provider parameters resolved via `envFrom`.\n *\n * Expects:\n * - map key is the provider parameter name\n * - map value is the env key name\n */\n optionalEnv?: OptionalProviderEnvMap\n /**\n * Required provider parameters resolved via `requiredEnvFrom`.\n *\n * Expects:\n * - map key is the provider parameter name\n * - map value is the env key name\n */\n requiredEnv?: RequiredProviderEnvMap\n}\n\n/**\n * Builder input for `chatProviderFrom`.\n */\nexport interface ChatProviderFromOptions extends ChatProviderDefinition {\n}\n\n/**\n * Options for the built-in `ChatProviders` plugin.\n */\nexport interface ChatProvidersPluginOptions {\n /**\n * Provider definitions to append to config.\n */\n providers: readonly ChatProviderDefinition[]\n /**\n * Optional explicit env source used for env-backed provider parameters.\n *\n * @default process.env\n */\n env?: NodeJS.ProcessEnv\n}\n\n/**\n * Partial config shape needed by the chat models plugin.\n */\nexport interface PluginConfig {\n env?: NodeJS.ProcessEnv\n chatProviders?: ChatProviderDefinition[]\n models?: ModelDefinition[]\n}\n\n/**\n * Plugin type bound to the minimal config shape used by model plugins.\n */\nexport type Plugin = ConfigHookPlugin<PluginConfig>\n\nfunction normalizeInferenceExecutorId(\n inferenceExecutor: ChatModelExecutorInput,\n inferenceExecutorId: string | undefined,\n): string {\n if (typeof inferenceExecutor === 'string') {\n return inferenceExecutor\n }\n\n return inferenceExecutorId ?? 'custom'\n}\n\nfunction createDefaultModelId(inferenceExecutorId: string, model: string): string {\n return `${inferenceExecutorId}:${model}`\n}\n\nfunction normalizeEnvRecord(env: NodeJS.ProcessEnv): Record<string, string> {\n const normalized: Record<string, string> = {}\n for (const [key, value] of Object.entries(env)) {\n if (typeof value === 'string') {\n normalized[key] = value\n }\n }\n\n return normalized\n}\n\nasync function resolveChatModelResolverValue<TValue>(\n value: ChatModelResolverValue<TValue>,\n context: ChatModelResolverContext,\n): Promise<TValue> {\n if (typeof value === 'function') {\n const resolver = value as (config: ChatModelResolverContext) => Promise<TValue> | TValue\n return await resolver(context)\n }\n\n return value\n}\n\nfunction resolveRequiredStringValue(value: string | undefined, name: string): string {\n return requiredEnvFrom(value, {\n name,\n type: 'string',\n })\n}\n\nfunction resolveOptionalStringValue(value: string | undefined, name: string): string | undefined {\n return envFrom(value, {\n name,\n type: 'string',\n })\n}\n\nfunction resolveOptionalEnvValue(\n env: NodeJS.ProcessEnv,\n envKey: string,\n): string | undefined {\n const options: EnvFromOptions = {\n name: envKey,\n type: 'string',\n }\n\n return envFrom(env[envKey], options)\n}\n\nfunction resolveRequiredEnvValue(\n env: NodeJS.ProcessEnv,\n envKey: string,\n): string {\n const options: RequiredEnvFromOptions = {\n name: envKey,\n type: 'string',\n }\n\n return requiredEnvFrom(env[envKey], options)\n}\n\nfunction resolveProviderParameters(\n provider: ChatProviderDefinition,\n env: NodeJS.ProcessEnv,\n): Record<string, unknown> | undefined {\n const parameters: Record<string, unknown> = {\n ...provider.parameters,\n }\n\n for (const [parameterName, envKey] of Object.entries(provider.optionalEnv ?? {})) {\n const resolved = resolveOptionalEnvValue(env, envKey)\n if (resolved != null) {\n parameters[parameterName] = resolved\n }\n }\n\n for (const [parameterName, envKey] of Object.entries(provider.requiredEnv ?? {})) {\n parameters[parameterName] = resolveRequiredEnvValue(env, envKey)\n }\n\n return Object.keys(parameters).length > 0 ? parameters : undefined\n}\n\nfunction normalizeChatProviderDefinition(\n provider: ChatProviderDefinition,\n env: NodeJS.ProcessEnv,\n): ChatProviderDefinition {\n return {\n id: provider.id,\n inferenceExecutor: provider.inferenceExecutor,\n inferenceExecutorId: normalizeInferenceExecutorId(provider.inferenceExecutor, provider.inferenceExecutorId),\n optionalEnv: provider.optionalEnv,\n parameters: resolveProviderParameters(provider, env),\n requiredEnv: provider.requiredEnv,\n }\n}\n\nfunction createProviderMap(config: PluginConfig): Map<string, ChatProviderDefinition> {\n const providerMap = new Map<string, ChatProviderDefinition>()\n for (const provider of config.chatProviders ?? []) {\n providerMap.set(provider.id, provider)\n }\n\n return providerMap\n}\n\nfunction resolveModelProvider(\n model: ChatModelDefinition,\n providerMap: ReadonlyMap<string, ChatProviderDefinition>,\n): ChatModelDefinition {\n if (model.provider == null) {\n return model\n }\n\n const provider = providerMap.get(model.provider)\n if (provider == null) {\n throw new Error(`Unknown chat provider \"${model.provider}\" referenced by model \"${model.id}\".`)\n }\n\n return {\n ...model,\n inferenceExecutor: provider.inferenceExecutor,\n inferenceExecutorId: provider.inferenceExecutorId ?? normalizeInferenceExecutorId(provider.inferenceExecutor, provider.inferenceExecutorId),\n parameters: {\n ...provider.parameters,\n ...model.parameters,\n },\n }\n}\n\nasync function resolveModelRuntimeResolvers(\n model: ChatModelDefinition,\n context: ChatModelResolverContext,\n): Promise<Record<string, unknown> | undefined> {\n if (model.runtimeResolvers == null) {\n return undefined\n }\n\n const resolvedParameters: Record<string, unknown> = {}\n\n if (model.runtimeResolvers.apiKey != null) {\n const resolvedApiKey = await resolveChatModelResolverValue(model.runtimeResolvers.apiKey, context)\n resolvedParameters.apiKey = resolveRequiredStringValue(resolvedApiKey, `${model.id}.apiKey`)\n }\n\n if (model.runtimeResolvers.baseURL != null) {\n const resolvedBaseURL = await resolveChatModelResolverValue(model.runtimeResolvers.baseURL, context)\n const normalizedBaseURL = resolveOptionalStringValue(resolvedBaseURL, `${model.id}.baseURL`)\n if (normalizedBaseURL != null) {\n resolvedParameters.baseURL = normalizedBaseURL\n }\n }\n\n if (model.runtimeResolvers.headers != null) {\n const resolvedHeaders = await resolveChatModelResolverValue(model.runtimeResolvers.headers, context)\n resolvedParameters.headers = resolvedHeaders\n }\n\n return Object.keys(resolvedParameters).length > 0 ? resolvedParameters : undefined\n}\n\nasync function resolveChatModelDefinition(\n model: ChatModelDefinition,\n config: PluginConfig,\n): Promise<ChatModelDefinition> {\n const providerResolvedModel = resolveModelProvider(model, createProviderMap(config))\n const resolvedRuntimeParameters = await resolveModelRuntimeResolvers(providerResolvedModel, {\n env: normalizeEnvRecord(config.env ?? process.env),\n })\n\n if (resolvedRuntimeParameters == null) {\n return providerResolvedModel\n }\n\n return {\n ...providerResolvedModel,\n parameters: {\n ...providerResolvedModel.parameters,\n ...resolvedRuntimeParameters,\n },\n }\n}\n\nfunction isOpenAIChatModelInferenceExecutor(\n options: ChatModelFromOptions,\n): options is ChatModelFromBaseOptions & OpenAIChatModelInferenceExecutor {\n return options.inferenceExecutor === 'openai'\n}\n\nfunction isOllamaChatModelInferenceExecutor(\n options: ChatModelFromOptions,\n): options is ChatModelFromBaseOptions & OllamaChatModelInferenceExecutor {\n return options.inferenceExecutor === 'ollama'\n}\n\nfunction isOpenRouterChatModelInferenceExecutor(\n options: ChatModelFromOptions,\n): options is ChatModelFromBaseOptions & OpenRouterChatModelInferenceExecutor {\n return options.inferenceExecutor === 'openrouter'\n}\n\n/**\n * Builds one normalized chat model definition.\n *\n * Use when:\n * - registering chat models through config plugins\n * - a single model needs aliases for matrix selection or judge lookup\n */\nexport function chatModelFrom(options: ChatModelFromOptions): ChatModelDefinition {\n const fallbackInferenceExecutor = options.inferenceExecutor ?? options.provider ?? 'custom'\n const inferenceExecutorId = normalizeInferenceExecutorId(fallbackInferenceExecutor, options.inferenceExecutorId)\n const runtimeResolvers = isOpenAIChatModelInferenceExecutor(options)\n ? {\n apiKey: options.apiKey,\n baseURL: options.baseURL,\n headers: options.headers,\n }\n : isOllamaChatModelInferenceExecutor(options)\n ? {\n baseURL: options.baseURL,\n headers: options.headers,\n }\n : isOpenRouterChatModelInferenceExecutor(options)\n ? {\n apiKey: options.apiKey,\n baseURL: options.baseURL,\n headers: options.headers,\n }\n : undefined\n\n return {\n aliases: options.aliases ?? [],\n id: options.id ?? createDefaultModelId(inferenceExecutorId, options.model),\n inferenceExecutor: fallbackInferenceExecutor,\n inferenceExecutorId,\n model: options.model,\n parameters: options.parameters,\n provider: options.provider,\n runtimeResolvers,\n }\n}\n\n/**\n * Builds one normalized chat provider definition.\n *\n * Use when:\n * - one provider preset should be reused across multiple chat models\n * - provider configuration should support required/optional env-backed parameters\n */\nexport function chatProviderFrom(options: ChatProviderFromOptions): ChatProviderDefinition {\n return {\n id: options.id,\n inferenceExecutor: options.inferenceExecutor,\n inferenceExecutorId: normalizeInferenceExecutorId(options.inferenceExecutor, options.inferenceExecutorId),\n optionalEnv: options.optionalEnv,\n parameters: options.parameters,\n requiredEnv: options.requiredEnv,\n }\n}\n\n/**\n * Options for the built-in `ChatModels` plugin.\n */\nexport interface ChatModelsPluginOptions {\n /**\n * Chat model definitions to append to config.\n */\n models: readonly ChatModelDefinition[]\n}\n\n/**\n * Creates a run-matrix `model` axis from configured chat model names.\n *\n * Use when:\n * - run matrix should iterate over explicit chat model ids/aliases\n * - project configs want a concise model-axis helper\n *\n * Expects:\n * - each provided name to match a configured model id or alias at runtime\n *\n * Returns:\n * - matrix axis object compatible with `runMatrix.extend/override`\n */\nexport function chatModelMatrix(...names: string[]): MatrixDefinition {\n return {\n model: Array.from(new Set(names)),\n }\n}\n\n/**\n * Built-in chat providers plugin that contributes provider presets to config.\n *\n * Use when:\n * - provider runtime config should be centralized and reusable\n * - provider parameters should be resolved from env via `envFrom`/`requiredEnvFrom`\n */\nexport function ChatProviders(options: ChatProvidersPluginOptions): Plugin {\n return {\n configVieval(config) {\n const env = config.env ?? options.env ?? process.env\n const normalizedProviders = options.providers.map(provider => normalizeChatProviderDefinition(provider, env))\n\n return {\n ...config,\n chatProviders: [\n ...(config.chatProviders ?? []),\n ...normalizedProviders,\n ],\n }\n },\n name: 'vieval:chat-providers',\n }\n}\n\n/**\n * Built-in chat models plugin that contributes model definitions to vieval config.\n *\n * Use when:\n * - chat-model registration should stay in config-level plugin setup\n * - tasks and assertions resolve models by name or alias at runtime\n */\nexport function ChatModels(options: ChatModelsPluginOptions): Plugin {\n return {\n async configVieval(config) {\n const resolvedModels = await Promise.all(options.models.map(async model => resolveChatModelDefinition(model, config)))\n\n return {\n ...config,\n models: [\n ...(config.models ?? []),\n ...resolvedModels,\n ],\n }\n },\n name: 'vieval:chat-models',\n }\n}\n\nexport * from './runtime-config'\nexport * from './telemetry'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/chat-models/runtime-config.ts","../../../src/plugins/chat-models/telemetry.ts","../../../src/plugins/chat-models/index.ts"],"sourcesContent":["import type { ModelDefinition } from '../../config/models'\nimport type { ChatModelHeaders } from './index'\n\nimport { envFrom, requiredEnvFrom } from '../../core/inference-executors/env'\n\n/**\n * Runtime config consumed by OpenAI-compatible provider constructors.\n */\nexport interface OpenAIChatModelRuntimeConfig {\n /**\n * Resolved inference executor kind.\n */\n inferenceExecutor: 'openai'\n /**\n * Concrete model name.\n */\n model: string\n /**\n * Required API key.\n */\n apiKey: string\n /**\n * Optional base URL override.\n */\n baseURL?: string\n /**\n * Optional request headers.\n */\n headers?: ChatModelHeaders\n}\n\n/**\n * Runtime config consumed by Ollama provider constructors.\n */\nexport interface OllamaChatModelRuntimeConfig {\n /**\n * Resolved inference executor kind.\n */\n inferenceExecutor: 'ollama'\n /**\n * Concrete model name.\n */\n model: string\n /**\n * Optional base URL override.\n */\n baseURL?: string\n /**\n * Optional request headers.\n */\n headers?: ChatModelHeaders\n}\n\n/**\n * Runtime config consumed by OpenRouter provider constructors.\n */\nexport interface OpenRouterChatModelRuntimeConfig {\n /**\n * Resolved inference executor kind.\n */\n inferenceExecutor: 'openrouter'\n /**\n * Concrete model name.\n */\n model: string\n /**\n * Required API key.\n */\n apiKey: string\n /**\n * Optional base URL override.\n */\n baseURL?: string\n /**\n * Optional request headers.\n */\n headers?: ChatModelHeaders\n}\n\n/**\n * Union of normalized runtime configs for supported chat-model executors.\n */\nexport type ChatModelRuntimeConfig\n = OpenAIChatModelRuntimeConfig\n | OllamaChatModelRuntimeConfig\n | OpenRouterChatModelRuntimeConfig\n\nfunction getParameters(model: ModelDefinition): Record<string, unknown> {\n return model.parameters ?? {}\n}\n\nfunction parseOptionalStringParameter(\n parameters: Record<string, unknown>,\n key: string,\n modelId: string,\n): string | undefined {\n const value = parameters[key]\n const normalized = value == null ? undefined : String(value)\n\n return envFrom(normalized, {\n name: `${modelId}.parameters.${key}`,\n type: 'string',\n })\n}\n\nfunction parseRequiredStringParameter(\n parameters: Record<string, unknown>,\n key: string,\n modelId: string,\n): string {\n const value = parameters[key]\n const normalized = value == null ? undefined : String(value)\n\n return requiredEnvFrom(normalized, {\n name: `${modelId}.parameters.${key}`,\n type: 'string',\n })\n}\n\nfunction parseHeadersParameter(\n parameters: Record<string, unknown>,\n modelId: string,\n): ChatModelHeaders | undefined {\n const headers = parameters.headers\n if (headers == null) {\n return undefined\n }\n\n if (typeof headers !== 'object' || Array.isArray(headers)) {\n throw new TypeError(`Invalid ${modelId}.parameters.headers: expected an object.`)\n }\n\n const normalized: Record<string, string | string[]> = {}\n for (const [key, value] of Object.entries(headers as Record<string, unknown>)) {\n if (typeof value === 'string') {\n normalized[key] = value\n continue\n }\n\n if (Array.isArray(value) && value.every(item => typeof item === 'string')) {\n normalized[key] = value\n continue\n }\n\n throw new Error(`Invalid ${modelId}.parameters.headers.${key}: expected string or string[].`)\n }\n\n return normalized\n}\n\n/**\n * Normalizes one configured chat model into runtime executor config.\n *\n * Use when:\n * - eval code needs typed provider constructor options from `context.model()`\n * - model parameters should be validated once with clear error messages\n *\n * Expects:\n * - `model.inferenceExecutorId` to be one of the supported executor ids\n * - required OpenAI fields (apiKey) to exist in `model.parameters`\n *\n * Returns:\n * - validated runtime config union for OpenAI or Ollama\n */\nexport function toChatModelRuntimeConfig(model: ModelDefinition): ChatModelRuntimeConfig {\n const parameters = getParameters(model)\n\n if (model.inferenceExecutorId === 'openai') {\n return {\n apiKey: parseRequiredStringParameter(parameters, 'apiKey', model.id),\n baseURL: parseOptionalStringParameter(parameters, 'baseURL', model.id),\n headers: parseHeadersParameter(parameters, model.id),\n inferenceExecutor: 'openai',\n model: model.model,\n }\n }\n\n if (model.inferenceExecutorId === 'ollama') {\n return {\n baseURL: parseOptionalStringParameter(parameters, 'baseURL', model.id),\n headers: parseHeadersParameter(parameters, model.id),\n inferenceExecutor: 'ollama',\n model: model.model,\n }\n }\n\n if (model.inferenceExecutorId === 'openrouter') {\n return {\n apiKey: parseRequiredStringParameter(parameters, 'apiKey', model.id),\n baseURL: parseOptionalStringParameter(parameters, 'baseURL', model.id),\n headers: parseHeadersParameter(parameters, model.id),\n inferenceExecutor: 'openrouter',\n model: model.model,\n }\n }\n\n throw new Error(`Unsupported chat inference executor \"${model.inferenceExecutorId}\" for model \"${model.id}\".`)\n}\n\n/**\n * Resolves OpenAI runtime config from one resolved run-context model.\n *\n * Use when:\n * - task execution already has `context.model()` output\n * - eval code wants typed OpenAI provider options with a concise helper name\n *\n * Expects:\n * - `model` to resolve to an OpenAI-backed chat model\n *\n * Returns:\n * - validated OpenAI runtime config\n */\nexport function openaiFromRunContext(model: ModelDefinition): OpenAIChatModelRuntimeConfig {\n const runtimeConfig = toChatModelRuntimeConfig(model)\n if (runtimeConfig.inferenceExecutor !== 'openai') {\n throw new Error(`Expected openai model, got \"${runtimeConfig.inferenceExecutor}\" for \"${model.id}\".`)\n }\n\n return runtimeConfig\n}\n\n/**\n * Resolves Ollama runtime config from one resolved run-context model.\n *\n * Use when:\n * - task execution already has `context.model()` output\n * - eval code wants typed Ollama provider options with a concise helper name\n *\n * Expects:\n * - `model` to resolve to an Ollama-backed chat model\n *\n * Returns:\n * - validated Ollama runtime config\n */\nexport function ollamaFromRunContext(model: ModelDefinition): OllamaChatModelRuntimeConfig {\n const runtimeConfig = toChatModelRuntimeConfig(model)\n if (runtimeConfig.inferenceExecutor !== 'ollama') {\n throw new Error(`Expected ollama model, got \"${runtimeConfig.inferenceExecutor}\" for \"${model.id}\".`)\n }\n\n return runtimeConfig\n}\n\n/**\n * Resolves OpenRouter runtime config from one resolved run-context model.\n *\n * Use when:\n * - task execution already has `context.model()` output\n * - eval code wants typed OpenRouter provider options with a concise helper name\n *\n * Expects:\n * - `model` to resolve to an OpenRouter-backed chat model\n *\n * Returns:\n * - validated OpenRouter runtime config\n */\nexport function openrouterFromRunContext(model: ModelDefinition): OpenRouterChatModelRuntimeConfig {\n const runtimeConfig = toChatModelRuntimeConfig(model)\n if (runtimeConfig.inferenceExecutor !== 'openrouter') {\n throw new Error(`Expected openrouter model, got \"${runtimeConfig.inferenceExecutor}\" for \"${model.id}\".`)\n }\n\n return runtimeConfig\n}\n","import type { TaskRunContext } from '../../config/types'\n\nimport { errorMessageFrom } from '@moeru/std'\n\n/**\n * Represents one normalized chat-model tool call.\n *\n * Use when:\n * - report events need tool-call level payloads that remain provider-neutral\n *\n * Expects:\n * - `name` to be stable enough for aggregation and assertion checks\n * - `args` to be JSON-serializable\n */\nexport interface ChatModelToolCall {\n /**\n * Optional provider-assigned tool-call identifier.\n */\n id?: string\n /**\n * Tool name.\n */\n name: string\n /**\n * Parsed tool arguments object/value.\n */\n args: unknown\n}\n\n/**\n * Provider identity attached to chat-model telemetry events.\n */\nexport interface ChatModelTelemetryProvider {\n /**\n * Provider id, for example `openai`.\n */\n id: string\n /**\n * Optional concrete model id/name.\n */\n model?: string\n}\n\n/**\n * Input options for response telemetry emission.\n */\nexport interface EmitChatModelResponseTelemetryOptions {\n /**\n * Optional case id for case-scoped telemetry events.\n */\n caseId?: string\n /**\n * Optional response latency in milliseconds.\n */\n latencyMs?: number\n /**\n * Optional provider identity payload.\n */\n provider?: ChatModelTelemetryProvider\n /**\n * Raw chat-model response object from the inference library/provider.\n */\n response: unknown\n}\n\n/**\n * Input options for request telemetry emission.\n */\nexport interface EmitChatModelRequestTelemetryOptions {\n /**\n * Optional case id for case-scoped telemetry events.\n */\n caseId?: string\n /**\n * Optional request payload metadata.\n */\n data?: unknown\n /**\n * Optional provider identity payload.\n */\n provider?: ChatModelTelemetryProvider\n}\n\n/**\n * Input options for error telemetry emission.\n */\nexport interface EmitChatModelErrorTelemetryOptions {\n /**\n * Optional case id for case-scoped telemetry events.\n */\n caseId?: string\n /**\n * Error payload emitted by the inference client/runtime.\n */\n error: unknown\n /**\n * Optional provider identity payload.\n */\n provider?: ChatModelTelemetryProvider\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (value == null || typeof value !== 'object') {\n return undefined\n }\n\n return value as Record<string, unknown>\n}\n\nfunction parseMaybeJson(value: unknown): unknown {\n if (typeof value !== 'string') {\n return value\n }\n\n try {\n return JSON.parse(value)\n }\n catch {\n return value\n }\n}\n\n/**\n * Extracts normalized tool calls from one chat-model response shape.\n *\n * Use when:\n * - downstream scoring, reporting, or analysis should inspect tool call usage\n * - provider payload differences should stay hidden behind one stable shape\n *\n * Returns:\n * - normalized list of `{ id?, name, args }` tool calls\n */\nexport function extractChatModelToolCalls(response: unknown): ChatModelToolCall[] {\n const responseRecord = asRecord(response)\n if (responseRecord == null) {\n return []\n }\n\n const rawToolCalls = responseRecord.toolCalls ?? responseRecord.tool_calls\n if (!Array.isArray(rawToolCalls)) {\n return []\n }\n\n const toolCalls: ChatModelToolCall[] = []\n\n for (const rawToolCall of rawToolCalls) {\n const toolCallRecord = asRecord(rawToolCall)\n if (toolCallRecord == null) {\n continue\n }\n\n const functionPayload = asRecord(toolCallRecord.function)\n const name = typeof toolCallRecord.name === 'string'\n ? toolCallRecord.name\n : typeof functionPayload?.name === 'string'\n ? functionPayload.name\n : undefined\n\n if (name == null || name.length === 0) {\n continue\n }\n\n const rawArgs = toolCallRecord.args\n ?? toolCallRecord.arguments\n ?? functionPayload?.args\n ?? functionPayload?.arguments\n\n toolCalls.push({\n args: parseMaybeJson(rawArgs),\n id: typeof toolCallRecord.id === 'string' ? toolCallRecord.id : undefined,\n name,\n })\n }\n\n return toolCalls\n}\n\n/**\n * Extracts numeric metering dimensions from one chat-model response usage block.\n *\n * Use when:\n * - report events should capture usage dimensions in a modality-neutral map\n *\n * Returns:\n * - numeric dimensions keyed by provider usage field names\n */\nexport function extractMeteringDimensions(response: unknown): Record<string, number> {\n const responseRecord = asRecord(response)\n const usage = asRecord(responseRecord?.usage)\n if (usage == null) {\n return {}\n }\n\n const dimensions: Record<string, number> = {}\n\n for (const [key, value] of Object.entries(usage)) {\n if (typeof value !== 'number' || Number.isNaN(value)) {\n continue\n }\n\n dimensions[key] = value\n }\n\n return dimensions\n}\n\n/**\n * Emits chat-model response telemetry as reportable task events.\n *\n * Use when:\n * - task code receives one chat-model response and wants standardized report events\n * - `ToolCall*` and metering metrics should be persisted in `events.jsonl`\n *\n * Expects:\n * - `context.reporterHooks?.onEvent` to be available in CLI execution paths\n *\n * Returns:\n * - no return value; this is a best-effort reporting helper\n */\nexport function emitChatModelResponseTelemetry(\n context: TaskRunContext,\n options: EmitChatModelResponseTelemetryOptions,\n): void {\n const toolCalls = extractChatModelToolCalls(options.response)\n const meteringDimensions = extractMeteringDimensions(options.response)\n\n if (toolCalls.length > 0) {\n meteringDimensions.tool_call_count = toolCalls.length\n }\n\n const data = {\n metering: {\n dimensions: meteringDimensions,\n latency_ms: options.latencyMs,\n },\n metrics: {\n 'vieval.chat.tool_call_count': toolCalls.length,\n },\n modality: 'chat',\n provider: options.provider,\n toolCalls,\n }\n\n context.reporterHooks?.onEvent?.({\n caseId: options.caseId,\n data,\n event: 'InferenceResponse',\n })\n\n for (const toolCall of toolCalls) {\n context.reporterHooks?.onEvent?.({\n caseId: options.caseId,\n data: {\n modality: 'chat',\n provider: options.provider,\n toolCall,\n },\n event: 'ToolCallStarted',\n })\n context.reporterHooks?.onEvent?.({\n caseId: options.caseId,\n data: {\n modality: 'chat',\n provider: options.provider,\n toolCall,\n },\n event: 'ToolCallEnded',\n })\n }\n}\n\n/**\n * Emits chat-model request telemetry as a reportable task event.\n *\n * Use when:\n * - task code submits one model request and wants request-side traceability\n *\n * Expects:\n * - `context.reporterHooks?.onEvent` to be available in CLI execution paths\n */\nexport function emitChatModelRequestTelemetry(\n context: TaskRunContext,\n options: EmitChatModelRequestTelemetryOptions,\n): void {\n context.reporterHooks?.onEvent?.({\n caseId: options.caseId,\n data: {\n data: options.data,\n modality: 'chat',\n provider: options.provider,\n },\n event: 'InferenceRequest',\n })\n}\n\n/**\n * Emits chat-model failure telemetry as a reportable task event.\n *\n * Use when:\n * - one inference call fails and report artifacts should include normalized error context\n *\n * Expects:\n * - `context.reporterHooks?.onEvent` to be available in CLI execution paths\n */\nexport function emitChatModelErrorTelemetry(\n context: TaskRunContext,\n options: EmitChatModelErrorTelemetryOptions,\n): void {\n context.reporterHooks?.onEvent?.({\n caseId: options.caseId,\n data: {\n error: errorMessageFrom(options.error) ?? 'Unknown inference error.',\n modality: 'chat',\n provider: options.provider,\n },\n event: 'InferenceError',\n })\n}\n","import type { MatrixDefinition, TaskExecutionPolicy } from '../../config'\nimport type { ModelDefinition } from '../../config/models'\nimport type { ConfigHookPlugin } from '../../config/plugin'\nimport type { EnvFromOptions, RequiredEnvFromOptions } from '../../core/inference-executors/env'\n\nimport process from 'node:process'\n\nimport { envFrom, requiredEnvFrom } from '../../core/inference-executors/env'\n\n/**\n * Minimal inference-executor shape expected by chat model runtime callers.\n */\nexport interface ChatModelExecutorLike {\n chat: (model: string) => Record<string, unknown>\n}\n\n/**\n * Inference-executor input accepted by `chatModelFrom`.\n */\nexport type ChatModelExecutorInput = string | ChatModelExecutorLike\n\n/**\n * Chat-model header payload accepted by executor parameters.\n */\nexport type ChatModelHeaders = Record<string, string | string[]>\n\n/**\n * Runtime env context passed to model callback resolvers.\n */\nexport interface ChatModelResolverContext {\n env: Record<string, string>\n}\n\n/**\n * Value-or-callback resolver used by model runtime fields.\n */\nexport type ChatModelResolverValue<TValue> = TValue | ((config: ChatModelResolverContext) => Promise<TValue> | TValue)\n\n/**\n * OpenAI-specific inference executor config shape.\n */\nexport interface OpenAIChatModelInferenceExecutor {\n inferenceExecutor: 'openai'\n apiKey?: ChatModelResolverValue<string>\n baseURL?: ChatModelResolverValue<string>\n headers?: ChatModelResolverValue<ChatModelHeaders>\n}\n\n/**\n * Ollama-specific inference executor config shape.\n */\nexport interface OllamaChatModelInferenceExecutor {\n inferenceExecutor: 'ollama'\n baseURL?: ChatModelResolverValue<string>\n headers?: ChatModelResolverValue<ChatModelHeaders>\n}\n\n/**\n * OpenRouter-specific inference executor config shape.\n */\nexport interface OpenRouterChatModelInferenceExecutor {\n inferenceExecutor: 'openrouter'\n apiKey?: ChatModelResolverValue<string>\n baseURL?: ChatModelResolverValue<string>\n headers?: ChatModelResolverValue<ChatModelHeaders>\n}\n\n/**\n * Generic inference executor config shape.\n */\nexport interface GenericChatModelInferenceExecutor {\n inferenceExecutor?: ChatModelExecutorInput\n}\n\n/**\n * Union of supported inference executor config shapes for `chatModelFrom`.\n */\nexport type ChatModelInferenceExecutor\n = OpenAIChatModelInferenceExecutor\n | OllamaChatModelInferenceExecutor\n | OpenRouterChatModelInferenceExecutor\n | GenericChatModelInferenceExecutor\n\n/**\n * Common builder input fields for `chatModelFrom`.\n */\nexport interface ChatModelFromBaseOptions {\n /**\n * Provider id registered through `ChatProviders`.\n *\n * Use when:\n * - model runtime transport and credentials should be delegated to a named provider preset\n *\n * Expects:\n * - one `ChatProviders` plugin entry to expose the same id\n */\n provider?: string\n /**\n * Inference-executor id or inference-executor instance.\n */\n inferenceExecutor?: ChatModelExecutorInput\n /**\n * Optional explicit inference-executor id for inference-executor instances.\n *\n * @default 'custom'\n */\n inferenceExecutorId?: string\n /**\n * Concrete model name.\n */\n model: string\n /**\n * Optional stable model id.\n *\n * @default `${inferenceExecutorId}:${model}`\n */\n id?: string\n /**\n * Alias names used by `resolveModelByName`.\n */\n aliases?: string[]\n /**\n * Optional execution policy hints attached to this model.\n */\n executionPolicy?: TaskExecutionPolicy\n /**\n * Additional retries allowed within the current attempt.\n *\n * @default 0\n */\n autoRetry?: number\n /**\n * Delay in milliseconds before a retry starts.\n *\n * @default retryIndex => 500 * 2 ** (retryIndex - 1)\n */\n autoRetryDelay?: TaskExecutionPolicy['autoRetryDelay']\n /**\n * Additional full task attempts allowed after the current attempt settles.\n *\n * @default 0\n */\n autoAttempt?: number\n /**\n * Timeout in milliseconds for model-backed work.\n */\n timeout?: number\n /**\n * Optional model-level call parameters.\n */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Builder input for `chatModelFrom`.\n */\nexport type ChatModelFromOptions = ChatModelInferenceExecutor & ChatModelFromBaseOptions\n\n/**\n * Chat-model specific specialization of the canonical `ModelDefinition`.\n */\nexport type ChatModelDefinition = Omit<ModelDefinition, 'inferenceExecutor'> & {\n inferenceExecutor: ChatModelExecutorInput\n provider?: string\n runtimeResolvers?: {\n apiKey?: ChatModelResolverValue<string>\n baseURL?: ChatModelResolverValue<string>\n headers?: ChatModelResolverValue<ChatModelHeaders>\n }\n}\n\nfunction normalizeExecutionPolicy(\n policy: TaskExecutionPolicy | undefined,\n): TaskExecutionPolicy | undefined {\n if (policy == null) {\n return undefined\n }\n\n const normalized = {\n autoAttempt: policy.autoAttempt,\n autoRetry: policy.autoRetry,\n autoRetryDelay: policy.autoRetryDelay,\n timeout: policy.timeout,\n }\n\n return Object.values(normalized).some(value => value != null)\n ? normalized\n : undefined\n}\n\nfunction hasJudgeAlias(model: Pick<ChatModelFromBaseOptions, 'aliases' | 'id' | 'model'>): boolean {\n return [\n ...(model.aliases ?? []),\n ...(model.id == null ? [] : [model.id]),\n model.model,\n ].some(value => value.toLowerCase().includes('judge'))\n}\n\nfunction resolveModelExecutionPolicy(options: ChatModelFromOptions): TaskExecutionPolicy | undefined {\n const explicitPolicy = normalizeExecutionPolicy({\n autoAttempt: options.autoAttempt ?? options.executionPolicy?.autoAttempt,\n autoRetry: options.autoRetry ?? options.executionPolicy?.autoRetry,\n autoRetryDelay: options.autoRetryDelay ?? options.executionPolicy?.autoRetryDelay,\n timeout: options.timeout ?? options.executionPolicy?.timeout,\n })\n\n if (explicitPolicy != null && Object.keys(explicitPolicy).length > 0) {\n return explicitPolicy\n }\n\n if (hasJudgeAlias(options)) {\n return {\n autoRetry: 3,\n }\n }\n\n return undefined\n}\n\n/**\n * Env-key map for optional provider parameters.\n *\n * Use when:\n * - provider parameter values should be read from env keys\n * - missing keys should resolve to `undefined`\n */\nexport type OptionalProviderEnvMap = Record<string, string>\n\n/**\n * Env-key map for required provider parameters.\n *\n * Use when:\n * - provider parameter values must exist before model execution\n * - missing keys should throw with key-aware error messages\n */\nexport type RequiredProviderEnvMap = Record<string, string>\n\n/**\n * One provider definition consumed by chat model presets.\n */\nexport interface ChatProviderDefinition {\n /**\n * Stable provider id referenced by `chatModelFrom({ provider })`.\n */\n id: string\n /**\n * Inference-executor id or instance used by this provider preset.\n */\n inferenceExecutor: ChatModelExecutorInput\n /**\n * Optional explicit inference-executor id for inference-executor instances.\n *\n * @default 'custom'\n */\n inferenceExecutorId?: string\n /**\n * Optional literal provider-level parameters.\n */\n parameters?: Record<string, unknown>\n /**\n * Optional provider parameters resolved via `envFrom`.\n *\n * Expects:\n * - map key is the provider parameter name\n * - map value is the env key name\n */\n optionalEnv?: OptionalProviderEnvMap\n /**\n * Required provider parameters resolved via `requiredEnvFrom`.\n *\n * Expects:\n * - map key is the provider parameter name\n * - map value is the env key name\n */\n requiredEnv?: RequiredProviderEnvMap\n}\n\n/**\n * Builder input for `chatProviderFrom`.\n */\nexport interface ChatProviderFromOptions extends ChatProviderDefinition {\n}\n\n/**\n * Options for the built-in `ChatProviders` plugin.\n */\nexport interface ChatProvidersPluginOptions {\n /**\n * Provider definitions to append to config.\n */\n providers: readonly ChatProviderDefinition[]\n /**\n * Optional explicit env source used for env-backed provider parameters.\n *\n * @default process.env\n */\n env?: NodeJS.ProcessEnv\n}\n\n/**\n * Partial config shape needed by the chat models plugin.\n */\nexport interface PluginConfig {\n env?: NodeJS.ProcessEnv\n chatProviders?: ChatProviderDefinition[]\n models?: ModelDefinition[]\n}\n\n/**\n * Plugin type bound to the minimal config shape used by model plugins.\n */\nexport type Plugin = ConfigHookPlugin<PluginConfig>\n\nfunction normalizeInferenceExecutorId(\n inferenceExecutor: ChatModelExecutorInput,\n inferenceExecutorId: string | undefined,\n): string {\n if (typeof inferenceExecutor === 'string') {\n return inferenceExecutor\n }\n\n return inferenceExecutorId ?? 'custom'\n}\n\nfunction createDefaultModelId(inferenceExecutorId: string, model: string): string {\n return `${inferenceExecutorId}:${model}`\n}\n\nfunction normalizeEnvRecord(env: NodeJS.ProcessEnv): Record<string, string> {\n const normalized: Record<string, string> = {}\n for (const [key, value] of Object.entries(env)) {\n if (typeof value === 'string') {\n normalized[key] = value\n }\n }\n\n return normalized\n}\n\nasync function resolveChatModelResolverValue<TValue>(\n value: ChatModelResolverValue<TValue>,\n context: ChatModelResolverContext,\n): Promise<TValue> {\n if (typeof value === 'function') {\n const resolver = value as (config: ChatModelResolverContext) => Promise<TValue> | TValue\n return await resolver(context)\n }\n\n return value\n}\n\nfunction resolveRequiredStringValue(value: string | undefined, name: string): string {\n return requiredEnvFrom(value, {\n name,\n type: 'string',\n })\n}\n\nfunction resolveOptionalStringValue(value: string | undefined, name: string): string | undefined {\n return envFrom(value, {\n name,\n type: 'string',\n })\n}\n\nfunction resolveOptionalEnvValue(\n env: NodeJS.ProcessEnv,\n envKey: string,\n): string | undefined {\n const options: EnvFromOptions = {\n name: envKey,\n type: 'string',\n }\n\n return envFrom(env[envKey], options)\n}\n\nfunction resolveRequiredEnvValue(\n env: NodeJS.ProcessEnv,\n envKey: string,\n): string {\n const options: RequiredEnvFromOptions = {\n name: envKey,\n type: 'string',\n }\n\n return requiredEnvFrom(env[envKey], options)\n}\n\nfunction resolveProviderParameters(\n provider: ChatProviderDefinition,\n env: NodeJS.ProcessEnv,\n): Record<string, unknown> | undefined {\n const parameters: Record<string, unknown> = {\n ...provider.parameters,\n }\n\n for (const [parameterName, envKey] of Object.entries(provider.optionalEnv ?? {})) {\n const resolved = resolveOptionalEnvValue(env, envKey)\n if (resolved != null) {\n parameters[parameterName] = resolved\n }\n }\n\n for (const [parameterName, envKey] of Object.entries(provider.requiredEnv ?? {})) {\n parameters[parameterName] = resolveRequiredEnvValue(env, envKey)\n }\n\n return Object.keys(parameters).length > 0 ? parameters : undefined\n}\n\nfunction normalizeChatProviderDefinition(\n provider: ChatProviderDefinition,\n env: NodeJS.ProcessEnv,\n): ChatProviderDefinition {\n return {\n id: provider.id,\n inferenceExecutor: provider.inferenceExecutor,\n inferenceExecutorId: normalizeInferenceExecutorId(provider.inferenceExecutor, provider.inferenceExecutorId),\n optionalEnv: provider.optionalEnv,\n parameters: resolveProviderParameters(provider, env),\n requiredEnv: provider.requiredEnv,\n }\n}\n\nfunction createProviderMap(config: PluginConfig): Map<string, ChatProviderDefinition> {\n const providerMap = new Map<string, ChatProviderDefinition>()\n for (const provider of config.chatProviders ?? []) {\n providerMap.set(provider.id, provider)\n }\n\n return providerMap\n}\n\nfunction resolveModelProvider(\n model: ChatModelDefinition,\n providerMap: ReadonlyMap<string, ChatProviderDefinition>,\n): ChatModelDefinition {\n if (model.provider == null) {\n return model\n }\n\n const provider = providerMap.get(model.provider)\n if (provider == null) {\n throw new Error(`Unknown chat provider \"${model.provider}\" referenced by model \"${model.id}\".`)\n }\n\n return {\n ...model,\n inferenceExecutor: provider.inferenceExecutor,\n inferenceExecutorId: provider.inferenceExecutorId ?? normalizeInferenceExecutorId(provider.inferenceExecutor, provider.inferenceExecutorId),\n parameters: {\n ...provider.parameters,\n ...model.parameters,\n },\n }\n}\n\nasync function resolveModelRuntimeResolvers(\n model: ChatModelDefinition,\n context: ChatModelResolverContext,\n): Promise<Record<string, unknown> | undefined> {\n if (model.runtimeResolvers == null) {\n return undefined\n }\n\n const resolvedParameters: Record<string, unknown> = {}\n\n if (model.runtimeResolvers.apiKey != null) {\n const resolvedApiKey = await resolveChatModelResolverValue(model.runtimeResolvers.apiKey, context)\n resolvedParameters.apiKey = resolveRequiredStringValue(resolvedApiKey, `${model.id}.apiKey`)\n }\n\n if (model.runtimeResolvers.baseURL != null) {\n const resolvedBaseURL = await resolveChatModelResolverValue(model.runtimeResolvers.baseURL, context)\n const normalizedBaseURL = resolveOptionalStringValue(resolvedBaseURL, `${model.id}.baseURL`)\n if (normalizedBaseURL != null) {\n resolvedParameters.baseURL = normalizedBaseURL\n }\n }\n\n if (model.runtimeResolvers.headers != null) {\n const resolvedHeaders = await resolveChatModelResolverValue(model.runtimeResolvers.headers, context)\n resolvedParameters.headers = resolvedHeaders\n }\n\n return Object.keys(resolvedParameters).length > 0 ? resolvedParameters : undefined\n}\n\nasync function resolveChatModelDefinition(\n model: ChatModelDefinition,\n config: PluginConfig,\n): Promise<ChatModelDefinition> {\n const providerResolvedModel = resolveModelProvider(model, createProviderMap(config))\n const resolvedRuntimeParameters = await resolveModelRuntimeResolvers(providerResolvedModel, {\n env: normalizeEnvRecord(config.env ?? process.env),\n })\n\n if (resolvedRuntimeParameters == null) {\n return providerResolvedModel\n }\n\n return {\n ...providerResolvedModel,\n parameters: {\n ...providerResolvedModel.parameters,\n ...resolvedRuntimeParameters,\n },\n }\n}\n\nfunction isOpenAIChatModelInferenceExecutor(\n options: ChatModelFromOptions,\n): options is ChatModelFromBaseOptions & OpenAIChatModelInferenceExecutor {\n return options.inferenceExecutor === 'openai'\n}\n\nfunction isOllamaChatModelInferenceExecutor(\n options: ChatModelFromOptions,\n): options is ChatModelFromBaseOptions & OllamaChatModelInferenceExecutor {\n return options.inferenceExecutor === 'ollama'\n}\n\nfunction isOpenRouterChatModelInferenceExecutor(\n options: ChatModelFromOptions,\n): options is ChatModelFromBaseOptions & OpenRouterChatModelInferenceExecutor {\n return options.inferenceExecutor === 'openrouter'\n}\n\n/**\n * Builds one normalized chat model definition.\n *\n * Use when:\n * - registering chat models through config plugins\n * - a single model needs aliases for matrix selection or judge lookup\n */\nexport function chatModelFrom(options: ChatModelFromOptions): ChatModelDefinition {\n const fallbackInferenceExecutor = options.inferenceExecutor ?? options.provider ?? 'custom'\n const inferenceExecutorId = normalizeInferenceExecutorId(fallbackInferenceExecutor, options.inferenceExecutorId)\n const runtimeResolvers = isOpenAIChatModelInferenceExecutor(options)\n ? {\n apiKey: options.apiKey,\n baseURL: options.baseURL,\n headers: options.headers,\n }\n : isOllamaChatModelInferenceExecutor(options)\n ? {\n baseURL: options.baseURL,\n headers: options.headers,\n }\n : isOpenRouterChatModelInferenceExecutor(options)\n ? {\n apiKey: options.apiKey,\n baseURL: options.baseURL,\n headers: options.headers,\n }\n : undefined\n\n return {\n aliases: options.aliases ?? [],\n executionPolicy: resolveModelExecutionPolicy(options),\n id: options.id ?? createDefaultModelId(inferenceExecutorId, options.model),\n inferenceExecutor: fallbackInferenceExecutor,\n inferenceExecutorId,\n model: options.model,\n parameters: options.parameters,\n provider: options.provider,\n runtimeResolvers,\n }\n}\n\n/**\n * Builds one normalized chat provider definition.\n *\n * Use when:\n * - one provider preset should be reused across multiple chat models\n * - provider configuration should support required/optional env-backed parameters\n */\nexport function chatProviderFrom(options: ChatProviderFromOptions): ChatProviderDefinition {\n return {\n id: options.id,\n inferenceExecutor: options.inferenceExecutor,\n inferenceExecutorId: normalizeInferenceExecutorId(options.inferenceExecutor, options.inferenceExecutorId),\n optionalEnv: options.optionalEnv,\n parameters: options.parameters,\n requiredEnv: options.requiredEnv,\n }\n}\n\n/**\n * Options for the built-in `ChatModels` plugin.\n */\nexport interface ChatModelsPluginOptions {\n /**\n * Chat model definitions to append to config.\n */\n models: readonly ChatModelDefinition[]\n}\n\n/**\n * Creates a run-matrix `model` axis from configured chat model names.\n *\n * Use when:\n * - run matrix should iterate over explicit chat model ids/aliases\n * - project configs want a concise model-axis helper\n *\n * Expects:\n * - each provided name to match a configured model id or alias at runtime\n *\n * Returns:\n * - matrix axis object compatible with `runMatrix.extend/override`\n */\nexport function chatModelMatrix(...names: string[]): MatrixDefinition {\n return {\n model: Array.from(new Set(names)),\n }\n}\n\n/**\n * Built-in chat providers plugin that contributes provider presets to config.\n *\n * Use when:\n * - provider runtime config should be centralized and reusable\n * - provider parameters should be resolved from env via `envFrom`/`requiredEnvFrom`\n */\nexport function ChatProviders(options: ChatProvidersPluginOptions): Plugin {\n return {\n configVieval(config) {\n const env = config.env ?? options.env ?? process.env\n const normalizedProviders = options.providers.map(provider => normalizeChatProviderDefinition(provider, env))\n\n return {\n ...config,\n chatProviders: [\n ...(config.chatProviders ?? []),\n ...normalizedProviders,\n ],\n }\n },\n name: 'vieval:chat-providers',\n }\n}\n\n/**\n * Built-in chat models plugin that contributes model definitions to vieval config.\n *\n * Use when:\n * - chat-model registration should stay in config-level plugin setup\n * - tasks and assertions resolve models by name or alias at runtime\n */\nexport function ChatModels(options: ChatModelsPluginOptions): Plugin {\n return {\n async configVieval(config) {\n const resolvedModels = await Promise.all(options.models.map(async model => resolveChatModelDefinition(model, config)))\n\n return {\n ...config,\n models: [\n ...(config.models ?? []),\n ...resolvedModels,\n ],\n }\n },\n name: 'vieval:chat-models',\n }\n}\n\nexport * from './runtime-config'\nexport * from './telemetry'\n"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { newQueue } from "@henrygd/queue";
2
+ //#region src/core/scheduler/queue.ts
3
+ /**
4
+ * Creates a scheduler queue backed by `@henrygd/queue`.
5
+ *
6
+ * Before:
7
+ * - `2`
8
+ *
9
+ * After:
10
+ * - `SchedulerQueue { run() }`
11
+ */
12
+ function createSchedulerQueue(concurrency) {
13
+ const queue = newQueue(concurrency);
14
+ return { run(execute) {
15
+ return queue.add(execute);
16
+ } };
17
+ }
18
+ //#endregion
19
+ export { createSchedulerQueue as t };
20
+
21
+ //# sourceMappingURL=queue-DsZQkZO_.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-DsZQkZO_.mjs","names":[],"sources":["../src/core/scheduler/queue.ts"],"sourcesContent":["import { newQueue } from '@henrygd/queue'\n\n/**\n * Minimal async queue adapter used by the scheduler runtime.\n *\n * Use when:\n * - a scheduler scope needs a concurrency cap\n * - queued work should be delegated through `@henrygd/queue`\n *\n * Expects:\n * - `concurrency` is a positive integer\n *\n * Returns:\n * - a small wrapper with a `run` method for queued work\n */\nexport interface SchedulerQueue {\n run: <T>(execute: () => Promise<T>) => Promise<T>\n}\n\n/**\n * Creates a scheduler queue backed by `@henrygd/queue`.\n *\n * Before:\n * - `2`\n *\n * After:\n * - `SchedulerQueue { run() }`\n */\nexport function createSchedulerQueue(concurrency: number): SchedulerQueue {\n const queue = newQueue(concurrency)\n\n return {\n run<T>(execute: () => Promise<T>) {\n return queue.add(execute)\n },\n }\n}\n"],"mappings":";;;;;;;;;;;AA4BA,SAAgB,qBAAqB,aAAqC;CACxE,MAAM,QAAQ,SAAS,YAAY;AAEnC,QAAO,EACL,IAAO,SAA2B;AAChC,SAAO,MAAM,IAAI,QAAQ;IAE5B"}