chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
44
third_party/pi/packages/evals/docker/Dockerfile
vendored
Normal file
44
third_party/pi/packages/evals/docker/Dockerfile
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
FROM node:24-slim AS builder
|
||||
WORKDIR /work
|
||||
# Build against an ephemeral repository mount. Only outputs staged under
|
||||
# /opt/evaluator persist; node_modules and dist created in /repo are discarded.
|
||||
RUN --mount=type=bind,source=.,target=/repo,rw \
|
||||
--mount=type=cache,target=/root/.npm \
|
||||
cd /repo \
|
||||
&& npm ci --ignore-scripts \
|
||||
&& npm run build:offline \
|
||||
&& node packages/evals/docker/install-runtime.mjs /opt/evaluator
|
||||
|
||||
# Both variants use the same installed runtime. The control removes only the
|
||||
# coding-agent documentation whose effect the experiment measures.
|
||||
FROM builder AS without-docs-install
|
||||
RUN rm -rf \
|
||||
/opt/evaluator/install/node_modules/@earendil-works/pi-coding-agent/README.md \
|
||||
/opt/evaluator/install/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md \
|
||||
/opt/evaluator/install/node_modules/@earendil-works/pi-coding-agent/docs \
|
||||
/opt/evaluator/install/node_modules/@earendil-works/pi-coding-agent/examples
|
||||
|
||||
FROM node:24-slim AS evaluator
|
||||
COPY --from=builder /opt/evaluator/root /repo
|
||||
WORKDIR /repo/packages/evals
|
||||
# Vitest starts as root so it can load evaluator code. Before prompting the
|
||||
# model, the harness permanently drops to UID 65532. Keep evaluator sources
|
||||
# root-only so model tools and generated code cannot inspect judges or fixtures.
|
||||
RUN chmod -R go-rwx \
|
||||
/repo/packages/evals/src \
|
||||
/repo/packages/evals/evals \
|
||||
/repo/packages/evals/docker \
|
||||
&& chmod go-rwx \
|
||||
/repo/vitest.base.ts \
|
||||
/repo/packages/evals/vitest.evals.config.ts \
|
||||
&& mkdir -p /run/pi-eval-secrets \
|
||||
&& chmod 0700 /run/pi-eval-secrets
|
||||
ENTRYPOINT ["node", "--experimental-strip-types", "docker/entrypoint.ts"]
|
||||
|
||||
FROM evaluator AS without_docs
|
||||
COPY --from=without-docs-install /opt/evaluator/install/node_modules /repo/node_modules
|
||||
ENV PI_EVAL_VARIANT=without_docs
|
||||
|
||||
FROM evaluator AS with_docs
|
||||
COPY --from=builder /opt/evaluator/install/node_modules /repo/node_modules
|
||||
ENV PI_EVAL_VARIANT=with_docs
|
||||
7
third_party/pi/packages/evals/docker/Dockerfile.dockerignore
vendored
Normal file
7
third_party/pi/packages/evals/docker/Dockerfile.dockerignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.git
|
||||
**/.eval
|
||||
**/node_modules
|
||||
**/dist
|
||||
**/coverage
|
||||
**/.env
|
||||
**/.env.*
|
||||
152
third_party/pi/packages/evals/docker/entrypoint.ts
vendored
Normal file
152
third_party/pi/packages/evals/docker/entrypoint.ts
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { chownSync, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, statSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const variant = process.env.PI_EVAL_VARIANT;
|
||||
|
||||
function parseId(name: string): number {
|
||||
const value = Number(process.env[name]);
|
||||
if (!Number.isSafeInteger(value) || value < 1) throw new Error(`${name} must be a positive integer.`);
|
||||
return value;
|
||||
}
|
||||
|
||||
const sandboxUid = parseId("PI_EVAL_SANDBOX_UID");
|
||||
const sandboxGid = parseId("PI_EVAL_SANDBOX_GID");
|
||||
|
||||
function assertDirectoryEntries(path: string, expectedEntries: readonly string[]): void {
|
||||
const actual = readdirSync(path).sort();
|
||||
const expected = [...expectedEntries].sort();
|
||||
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
||||
throw new Error(`Unexpected eval image contents in ${path}: ${actual.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertWorkspace(): void {
|
||||
assertDirectoryEntries("/repo", ["node_modules", "package.json", "packages", "vitest.base.ts"]);
|
||||
assertDirectoryEntries("/repo/packages", ["evals"]);
|
||||
assertDirectoryEntries("/repo/packages/evals", ["docker", "evals", "package.json", "src", "vitest.evals.config.ts"]);
|
||||
const codingAgentDir = "/repo/node_modules/@earendil-works/pi-coding-agent";
|
||||
for (const name of ["package.json", "dist/index.js"]) {
|
||||
if (!existsSync(join(codingAgentDir, name))) throw new Error(`Installed coding-agent is missing ${name}.`);
|
||||
}
|
||||
const internalScope = "/repo/node_modules/@earendil-works";
|
||||
for (const packageName of readdirSync(internalScope)) {
|
||||
if (packageName === "pi-coding-agent") continue;
|
||||
const packageDirectory = join(internalScope, packageName);
|
||||
for (const entry of readdirSync(packageDirectory, { withFileTypes: true })) {
|
||||
if (
|
||||
(entry.isDirectory() && ["docs", "examples", "src", "test", "tests"].includes(entry.name)) ||
|
||||
(entry.isFile() && /^(?:readme|changelog)(?:\..+)?$/i.test(entry.name))
|
||||
) {
|
||||
throw new Error(`Internal dependency documentation is model-visible: ${packageName}/${entry.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (variant === "without_docs") {
|
||||
for (const name of ["README.md", "CHANGELOG.md", "docs", "examples"]) {
|
||||
if (existsSync(join(codingAgentDir, name))) throw new Error(`without_docs contains coding-agent ${name}.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (variant !== "with_docs") throw new Error("Eval image has no valid PI_EVAL_VARIANT.");
|
||||
for (const path of [
|
||||
join(codingAgentDir, "README.md"),
|
||||
join(codingAgentDir, "CHANGELOG.md"),
|
||||
join(codingAgentDir, "docs/models.md"),
|
||||
join(codingAgentDir, "examples/README.md"),
|
||||
]) {
|
||||
if (!existsSync(path) || readFileSync(path).length === 0) throw new Error(`Missing documentation: ${path}`);
|
||||
}
|
||||
}
|
||||
|
||||
function chownTree(path: string, uid: number, gid: number): void {
|
||||
const stats = lstatSync(path);
|
||||
if (stats.isDirectory() && !stats.isSymbolicLink()) {
|
||||
for (const entry of readdirSync(path)) chownTree(join(path, entry), uid, gid);
|
||||
}
|
||||
chownSync(path, uid, gid);
|
||||
}
|
||||
|
||||
function assertRootOnly(path: string): void {
|
||||
const stats = statSync(path);
|
||||
if (stats.uid !== 0 || (stats.mode & 0o077) !== 0) {
|
||||
throw new Error(`Evaluator source must be readable only by root: ${path}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertSandboxCannotRead(path: string): void {
|
||||
const method = statSync(path).isDirectory() ? "readdirSync" : "readFileSync";
|
||||
const probe = spawnSync(process.execPath, ["--eval", `require("node:fs").${method}(${JSON.stringify(path)})`], {
|
||||
uid: sandboxUid,
|
||||
gid: sandboxGid,
|
||||
stdio: "ignore",
|
||||
});
|
||||
if (probe.status === 0) throw new Error(`The model-facing user can read evaluator source: ${path}`);
|
||||
}
|
||||
|
||||
assertWorkspace();
|
||||
const evalRoot = "/repo/packages/evals/evals";
|
||||
for (const path of [
|
||||
evalRoot,
|
||||
"/repo/packages/evals/src",
|
||||
"/repo/packages/evals/docker",
|
||||
"/repo/packages/evals/vitest.evals.config.ts",
|
||||
"/repo/vitest.base.ts",
|
||||
]) {
|
||||
assertRootOnly(path);
|
||||
assertSandboxCannotRead(path);
|
||||
}
|
||||
const codingAgentEntry = fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"));
|
||||
if (codingAgentEntry !== "/repo/node_modules/@earendil-works/pi-coding-agent/dist/index.js") {
|
||||
throw new Error(`Eval does not resolve pi-coding-agent from dist: ${codingAgentEntry}`);
|
||||
}
|
||||
|
||||
const agentDir = "/tmp/pi-eval-host-agent";
|
||||
mkdirSync(agentDir, { recursive: true });
|
||||
const authSource = "/run/pi-eval-secrets/auth.json";
|
||||
if (existsSync(authSource)) copyFileSync(authSource, join(agentDir, "auth.json"));
|
||||
chownTree(agentDir, sandboxUid, sandboxGid);
|
||||
chownTree("/artifacts", sandboxUid, sandboxGid);
|
||||
process.env.HOME = "/tmp/pi-eval-bootstrap";
|
||||
process.env.USERPROFILE = process.env.HOME;
|
||||
process.env.PI_CODING_AGENT_DIR = agentDir;
|
||||
process.env.PI_EVAL_CONTAINER = "1";
|
||||
process.umask(0o022);
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const vitestCli = resolve(dirname(require.resolve("vitest/package.json")), "vitest.mjs");
|
||||
const inputArgs = process.argv.slice(2);
|
||||
const discover = inputArgs.includes("--discover");
|
||||
const vitestArgs = inputArgs.filter((argument) => argument !== "--discover");
|
||||
const evalFiles = vitestArgs.filter((argument) => argument.endsWith(".eval.ts"));
|
||||
if (evalFiles.length === 0) throw new Error("The container runner requires an explicit eval file.");
|
||||
|
||||
for (const evalFile of evalFiles) {
|
||||
assertRootOnly(evalFile);
|
||||
assertSandboxCannotRead(evalFile);
|
||||
}
|
||||
|
||||
const commandArgs = discover
|
||||
? [vitestCli, "list", "--config", "vitest.evals.config.ts", ...vitestArgs, "--json=/artifacts/discovered-tests.json"]
|
||||
: [
|
||||
vitestCli,
|
||||
"run",
|
||||
"--config",
|
||||
"vitest.evals.config.ts",
|
||||
...vitestArgs,
|
||||
"--reporter=vitest-evals/reporter",
|
||||
"--reporter=json",
|
||||
"--outputFile=/artifacts/vitest.json",
|
||||
];
|
||||
const result = spawnSync(process.execPath, commandArgs, { cwd: packageRoot, stdio: "inherit", env: process.env });
|
||||
if (result.error) throw result.error;
|
||||
|
||||
const artifactUid = process.env.PI_EVAL_ARTIFACT_UID;
|
||||
const artifactGid = process.env.PI_EVAL_ARTIFACT_GID;
|
||||
if (artifactUid !== undefined && artifactGid !== undefined) {
|
||||
chownTree("/artifacts", parseId("PI_EVAL_ARTIFACT_UID"), parseId("PI_EVAL_ARTIFACT_GID"));
|
||||
}
|
||||
process.exit(result.status ?? 1);
|
||||
70
third_party/pi/packages/evals/docker/install-runtime.mjs
vendored
Normal file
70
third_party/pi/packages/evals/docker/install-runtime.mjs
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { installConsumer } from "../../../scripts/local-package-install.mjs";
|
||||
import { produceArtifactSet } from "../../../scripts/package-artifacts.mjs";
|
||||
import { codingAgentName } from "../../../scripts/coding-agent-smoke.mjs";
|
||||
|
||||
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
|
||||
const outputDirectory = process.argv[2];
|
||||
if (!outputDirectory || process.argv.length !== 3) {
|
||||
throw new Error("Usage: node packages/evals/docker/install-runtime.mjs <output-directory>");
|
||||
}
|
||||
|
||||
const evalPackage = JSON.parse(readFileSync(join(repositoryRoot, "packages/evals/package.json"), "utf8"));
|
||||
const artifactSet = produceArtifactSet({
|
||||
build: false,
|
||||
outDir: join(outputDirectory, "artifacts"),
|
||||
repoRoot: repositoryRoot,
|
||||
// Docker build contexts may omit Git metadata; this artifact set is consumed only within this image build.
|
||||
source: null,
|
||||
});
|
||||
const artifactNames = new Set(artifactSet.packages.map((pkg) => pkg.name));
|
||||
const evaluatorDependencies = Object.entries(evalPackage.devDependencies).filter(([name]) => !artifactNames.has(name));
|
||||
const installDirectory = join(outputDirectory, "install");
|
||||
installConsumer({ artifactSet, directory: installDirectory, packageNames: [codingAgentName] });
|
||||
execFileSync(
|
||||
"npm",
|
||||
[
|
||||
"install",
|
||||
"--ignore-scripts",
|
||||
"--omit=dev",
|
||||
"--no-audit",
|
||||
"--no-fund",
|
||||
"--no-save",
|
||||
...evaluatorDependencies.map(([name, version]) => `${name}@${version}`),
|
||||
],
|
||||
{ cwd: installDirectory, stdio: "inherit" },
|
||||
);
|
||||
|
||||
for (const packageName of artifactNames) {
|
||||
if (packageName === codingAgentName) continue;
|
||||
const packageDirectory = join(installDirectory, "node_modules", ...packageName.split("/"));
|
||||
if (!existsSync(packageDirectory)) continue;
|
||||
for (const entry of readdirSync(packageDirectory, { withFileTypes: true })) {
|
||||
if (
|
||||
(entry.isDirectory() && ["docs", "examples", "src", "test", "tests"].includes(entry.name)) ||
|
||||
(entry.isFile() && /^(?:readme|changelog)(?:\..+)?$/i.test(entry.name))
|
||||
) {
|
||||
rmSync(join(packageDirectory, entry.name), { force: true, recursive: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const runtimeRoot = join(outputDirectory, "root");
|
||||
const runtimeEvalRoot = join(runtimeRoot, "packages/evals");
|
||||
mkdirSync(join(runtimeEvalRoot, "docker"), { recursive: true });
|
||||
for (const file of ["package.json", "vitest.base.ts"]) {
|
||||
cpSync(join(repositoryRoot, file), join(runtimeRoot, file));
|
||||
}
|
||||
for (const file of ["package.json", "vitest.evals.config.ts"]) {
|
||||
cpSync(join(repositoryRoot, "packages/evals", file), join(runtimeEvalRoot, file));
|
||||
}
|
||||
for (const directory of ["src", "evals"]) {
|
||||
cpSync(join(repositoryRoot, "packages/evals", directory), join(runtimeEvalRoot, directory), { recursive: true });
|
||||
}
|
||||
cpSync(
|
||||
join(repositoryRoot, "packages/evals/docker/entrypoint.ts"),
|
||||
join(runtimeEvalRoot, "docker/entrypoint.ts"),
|
||||
);
|
||||
Reference in New Issue
Block a user