| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| export type JsonPrimitive = string | number | boolean | null; | |
| export type JsonValue = | |
| | JsonPrimitive | |
| | readonly JsonValue[] | |
| | { readonly [key: string]: JsonValue }; | |
| export type JsonObject = { readonly [key: string]: JsonValue }; | |
| export function decodeJsonObject(value: unknown, where: string): JsonObject { | |
| if (!value || typeof value !== 'object' || Array.isArray(value)) { | |
| throw new Error(`${where} must be an object`); | |
| } | |
| return Object.fromEntries( | |
| Object.entries(value).map(([key, child]) => [key, decodeJsonValue(child, `${where}.${key}`)]), | |
| ); | |
| } | |
| function decodeJsonValue(value: unknown, where: string): JsonValue { | |
| if (value === null || typeof value === 'string' || typeof value === 'boolean') return value; | |
| if (typeof value === 'number' && Number.isFinite(value)) return value; | |
| if (Array.isArray(value)) { | |
| return value.map((child, index) => decodeJsonValue(child, `${where}[${index}]`)); | |
| } | |
| if (value && typeof value === 'object') return decodeJsonObject(value, where); | |
| throw new Error(`${where} must be JSON`); | |
| } | |
| export interface ExperimentSpec { | |
| readonly schemaVersion: 'maka.eval.v1'; | |
| readonly id: string; | |
| readonly benchmark: { | |
| readonly id: string; | |
| readonly version: string; | |
| readonly config: JsonObject; | |
| }; | |
| readonly executor: { readonly kind: string; readonly config: JsonObject }; | |
| readonly execution: { readonly maxConcurrentTaskGroups: number }; | |
| readonly subjects: readonly { | |
| readonly id: string; | |
| readonly kind: 'maka' | 'external'; | |
| readonly credentials: readonly string[]; | |
| readonly config: JsonObject; | |
| }[]; | |
| readonly tasks: readonly { | |
| readonly id: string; | |
| readonly input: string; | |
| readonly config: JsonObject; | |
| }[]; | |
| readonly repetitions: number; | |
| readonly budget: JsonObject; | |
| readonly verifier: JsonObject; | |
| } | |
| export interface ExperimentCell { | |
| readonly id: string; | |
| readonly experimentId: string; | |
| readonly benchmark: ExperimentSpec['benchmark']; | |
| readonly executor: ExperimentSpec['executor']; | |
| readonly subject: ExperimentSpec['subjects'][number]; | |
| readonly task: ExperimentSpec['tasks'][number]; | |
| readonly repetition: number; | |
| readonly budget: JsonObject; | |
| readonly verifier: JsonObject; | |
| } | |
| export function expandExperiment(spec: ExperimentSpec): ExperimentCell[] { | |
| return spec.tasks.flatMap((task) => | |
| Array.from({ length: spec.repetitions }, (_, repetition) => repetition + 1).flatMap( | |
| (repetition) => | |
| spec.subjects.map((subject) => ({ | |
| id: `${task.id}::${repetition}::${subject.id}`, | |
| experimentId: spec.id, | |
| benchmark: spec.benchmark, | |
| executor: spec.executor, | |
| subject, | |
| task, | |
| repetition, | |
| budget: spec.budget, | |
| verifier: spec.verifier, | |
| })), | |
| ), | |
| ); | |
| } |