mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 08:01:25 +00:00
b883f9a9d2
* feat: add ai chat schedule and trigger tools * refactor: use zod for ai chat workspace tools * refactor: let ai provide runnable target fields * refactor: generate ai chat workspace tool schemas * fix: add object type to composed tool schemas * fix: avoid top-level trigger schema unions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: block undeployed workspace ai tools Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: inject ai workspace tool target Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add ai evals for workspace tools Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: make workspace tool eval prompts realistic Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: surface workspace tool errors * fix: show workspace tool success details * fix: describe workspace tool path format * fix: clarify workspace path examples * fix: tighten workspace tool validation * fix: align workspace tool prompts * chore: mark generated chat schemas * chore: mark generated cli skills --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
85 lines
2.3 KiB
TypeScript
85 lines
2.3 KiB
TypeScript
import { readFile } from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { parse } from "yaml";
|
|
import type {
|
|
CliValidationSpec,
|
|
EvalCase,
|
|
EvalCaseRuntimeSpec,
|
|
EvalMode,
|
|
EvalValidationSpec,
|
|
} from "./types";
|
|
|
|
const REPO_ROOT = fileURLToPath(new URL("../../", import.meta.url));
|
|
const CASES_DIR = path.join(REPO_ROOT, "ai_evals", "cases");
|
|
|
|
interface RawEvalCase {
|
|
id: string;
|
|
prompt: string;
|
|
initial?: string;
|
|
expected?: string;
|
|
validate?: EvalValidationSpec;
|
|
toolExpect?: EvalCase["toolExpect"];
|
|
cliExpect?: CliValidationSpec;
|
|
judgeChecklist?: string[];
|
|
skipJudge?: boolean;
|
|
runtime?: EvalCaseRuntimeSpec;
|
|
}
|
|
export function getRepoRoot(): string {
|
|
return REPO_ROOT;
|
|
}
|
|
|
|
export function getAiEvalsRoot(): string {
|
|
return path.join(REPO_ROOT, "ai_evals");
|
|
}
|
|
|
|
export async function loadCases(mode: EvalMode): Promise<EvalCase[]> {
|
|
const filePath = path.join(CASES_DIR, `${mode}.yaml`);
|
|
const raw = await readFile(filePath, "utf8");
|
|
const parsed = parse(raw);
|
|
|
|
if (!Array.isArray(parsed)) {
|
|
throw new Error(`Expected ${filePath} to contain a YAML list of cases`);
|
|
}
|
|
|
|
return (parsed as RawEvalCase[]).map((entry) => ({
|
|
id: entry.id,
|
|
prompt: entry.prompt,
|
|
initialPath: resolveFixturePath(entry.initial),
|
|
expectedPath: resolveFixturePath(entry.expected),
|
|
validate: entry.validate,
|
|
toolExpect: entry.toolExpect,
|
|
cliExpect: entry.cliExpect,
|
|
judgeChecklist: entry.judgeChecklist,
|
|
skipJudge: entry.skipJudge,
|
|
runtime: entry.runtime,
|
|
}));
|
|
}
|
|
|
|
export async function loadSelectedCases(
|
|
mode: EvalMode,
|
|
selectedIds: string[]
|
|
): Promise<EvalCase[]> {
|
|
const allCases = await loadCases(mode);
|
|
if (selectedIds.length === 0) {
|
|
return allCases;
|
|
}
|
|
|
|
const caseMap = new Map(allCases.map((entry) => [entry.id, entry]));
|
|
const missing = selectedIds.filter((id) => !caseMap.has(id));
|
|
if (missing.length > 0) {
|
|
throw new Error(
|
|
`Unknown ${mode} case${missing.length === 1 ? "" : "s"}: ${missing.join(", ")}`
|
|
);
|
|
}
|
|
|
|
return selectedIds.map((id) => caseMap.get(id)!);
|
|
}
|
|
|
|
function resolveFixturePath(value: string | undefined): string | undefined {
|
|
if (!value) {
|
|
return undefined;
|
|
}
|
|
return path.isAbsolute(value) ? value : path.join(REPO_ROOT, value);
|
|
}
|