LocalEval is a benchmark harness and web workbench for evaluating workflow-oriented model behavior in a controlled local environment.
It includes:
- a Go backend that runs code in isolated Docker containers
- a PostgreSQL-backed benchmark runner with scaffold-aware execution, grading, and reporting
- a Next.js web workbench for browsing suites, launching runs, and reviewing results
- a structured catalog for models, tasks, scaffolds, tools, and artifact expectations
LocalEval is designed for benchmark workflows where outcome quality and process quality both matter.
The current implementation supports:
- isolated code execution in Docker
- LM Studio and customer-hosted OpenAI-compatible model endpoints
- URL-based model connections with configurable authentication headers
- scaffold-aware benchmark comparisons
- artifact-aware grading and report generation
- a web interface for run operations, authoring, and inspection
cmd/evaluator- HTTP server, benchmark CLI entrypoint, and runtime wiringinternal/- execution, scoring, PostgreSQL repositories, model adapters, and control-plane primitivesweb/- Next.js workbench UI and related client componentsdb/- Drizzle schema and checked-in PostgreSQL migrations
- Go 1.24+
- Docker and Docker Compose
- Node.js 20+ and
pnpmif you want to run the web workbench locally - Python 3 if you want to run the Compose smoke-test script
- A running Docker daemon
git clone <repository-url>
cd go-exec-sandbox
go mod download
cd web && pnpm installPostgreSQL is the only runtime persistence backend. The API and worker require
DATABASE_URL; the one-shot migrate service uses the separate
MIGRATION_DATABASE_URL bootstrap role. PostgreSQL stores catalog, run,
execution, evidence, event, job, and metadata state. Local files are reserved
for artifact bytes behind the storage abstraction.
Model connections are external HTTP endpoints. LM Studio is the supported local OpenAI-compatible integration; deployed or self-hosted customer endpoints use the same synchronous protocol. Authentication is configured through secret references and declared request headers. The benchmark control plane stores typed connection revisions and never stores returned secret values.
go run ./cmd/evaluatorThis starts the API when the required PostgreSQL configuration is available.
docker compose up -d --build
docker compose ps
docker compose logs -f api workerCompose starts exactly five services:
postgres— pinned PostgreSQL with the namedpostgres_datavolume;migrate— one-shot Drizzle migration service using the bootstrap database role;api— Go API using the application database role;worker— Go execution worker using the application database role and customer-provided model or agent endpoints;frontend— Next.js workbench onhttp://localhost:3000.
migrate waits for healthy PostgreSQL. The API and worker wait for successful
migration completion and verify schema readiness before serving. Neither
service applies migrations at startup. The API and worker share only the
artifact-byte volume; PostgreSQL is the source of truth for all control-plane
and execution state.
Set database credentials through environment variables or secret files:
POSTGRES_PASSWORD_FILE, POSTGRES_APP_PASSWORD_FILE, DATABASE_URL, and
MIGRATION_DATABASE_URL. EXPECTED_SCHEMA_HASH is required by the API and
worker readiness checks.
The browser sends same-origin /api/v1 requests through the Next.js proxy. The
public NEXT_PUBLIC_API_BASE_URL value (http://localhost:8080 in Compose) is
available to the UI and operator-facing links. The Next.js /api/v1 route handler
forwards requests at runtime using BACKEND_ORIGIN=http://api:8080, including live
event streams and request bodies. Internal service names are never sent to the
browser, and changing the backend origin does not require rebuilding the frontend
image.
To stop the stack without deleting named volumes:
docker compose downValidate the service isolation without starting containers:
bash scripts/test-compose-layout.sh
bash scripts/compose-inspect.sh docker-compose.ymlRun the full Compose smoke test when Docker is available:
bash scripts/test-compose-e2e.shThe E2E stack uses isolated host ports 28080 (API) and 23000 (frontend) so it
can run alongside the default application Compose stack on 8080/3000.
Provision isolated databases for development verification:
bash scripts/setup-test-database.sh # TEST_DATABASE_URL for live contracts
bash scripts/setup-app-database.sh # application Compose stack databasecd web
pnpm devThe web app runs on the default Next.js port and provides:
- dashboard views
- suite and task authoring
- run launch forms
- live run progress
- comparison pages
- run artifact and event inspection
GET /ping
POST /execute
Executes source code inside an isolated Docker container.
GET /metrics
Returns request and error counts.
POST /benchmark/run
Runs the configured benchmark and returns a JSON report.
The web workbench also uses versioned backend routes under /api/v1/ for run,
catalog, artifact, comparison, and import/export operations.
go-exec-sandbox/
├── cmd/
│ └── evaluator/ # HTTP server, benchmark CLI, and wiring
├── db/ # Drizzle schema and PostgreSQL migrations
├── docs/ # Specs, plans, and research notes
├── internal/
│ ├── accesspolicy/ # Visibility and access rules
│ ├── attachments/ # Artifact and attachment handling
│ ├── benchmark/ # Benchmark orchestration and reporting
│ ├── controlplane/ # Run records, versions, and event plumbing
│ ├── grading/ # Outcome and judge evaluation logic
│ ├── httpapi/ # Versioned backend routes and handlers
│ ├── modeladapter/ # Provider adapters
│ ├── modelruntime/ # External model endpoint resolution and checks
│ ├── prompting/ # Prompt rendering and parsing
│ ├── runner/ # Execution modes and task runners
│ ├── sandbox/ # Docker-based code execution
│ ├── scoring/ # Scoring and judge logic
│ ├── solver/ # Model execution strategies
│ └── tenancy/ # Scoped storage and credentials handling
├── web/ # Next.js workbench
├── docker-compose.yml # Local multi-service setup
├── Dockerfile # Backend container image
└── README.md
PostgreSQL contract tests require a separate isolated test database. The
application Compose database is provisioned independently and must never be used
as TEST_DATABASE_URL.
# Isolated contract-test database (host port defaults to 55433)
bash scripts/setup-test-database.sh
source .test-db.env
export EXPECTED_SCHEMA_HASH="$(awk -F= '$1 == "EXPECTED_SCHEMA_HASH" { print $2; exit }' .env.example)"
# Application Compose database (default project: localeval)
bash scripts/setup-app-database.sh
source .app-db.env
# Contract, schema, and repository verification
bash scripts/test-postgres-contract.sh
# Full acceptance matrix (provisions test DB, static checks, Compose E2E)
bash scripts/test-acceptance.sh
# Cleanup isolated databases when finished
bash scripts/cleanup-postgres-environments.sh allscripts/test-postgres-contract.sh provisions a fresh test database by default,
exports redacted connection metadata, runs live postgres_contract tests with
-p 1 -shuffle=off, and fails if TEST_DATABASE_URL is missing. It never
silently skips live database coverage.
scripts/setup-test-database.sh and scripts/setup-app-database.sh print
redacted URLs and the checked-in EXPECTED_SCHEMA_HASH. Secrets are written only
to gitignored .test-db.env / .app-db.env files.
The app-role replay privilege contract is opt-in and requires explicit
DATABASE_URL, MIGRATION_DATABASE_URL, and TEST_DATABASE_URL inputs. It
canonicalizes all three URLs before any mutation, runs sabotage and replay in a
single transaction, and rolls back the transaction before checking the app
connections. After sourcing the isolated test environment, run it with:
RUN_APP_ROLE_RECONCILIATION_CONTRACT=1 bash scripts/test-postgres-contract.shThis contract fails closed unless all three URLs use exactly one loopback host
(localhost, 127.0.0.1, or ::1) and each connected database name matches
localeval_test_*. It requires exact equality of the connected canonical
database identity across app, migration, and test URLs; equal URL strings alone
are never considered safe. It deliberately repairs injected stale grants twice
with the canonical db/provision/reconcile-app-role.sql, runs every mutating
probe and effective privilege check inside rollback-only transactions, and
performs only read-only cleanup verification after rollback. The production
runtime does not require the test database marker.
go test ./...
go test -race ./...
go vet ./...Live PostgreSQL repository contracts:
source .test-db.env
export EXPECTED_SCHEMA_HASH="$(awk -F= '$1 == "EXPECTED_SCHEMA_HASH" { print $2; exit }' .env.example)"
go test -tags=postgres_contract -p 1 -shuffle=off ./internal/postgres/... -count=1bash scripts/test-web.shOr manually:
cd web
pnpm test
pnpm lint
pnpm buildsource .app-db.env
bash scripts/test-database-migrations.sh
bash scripts/test-database-migrations-docker.sh
pnpm --dir db test
pnpm --dir db schema:checkbash scripts/test-compose-layout.sh
bash scripts/compose-inspect.sh docker-compose.yml
bash scripts/test-compose-e2e.sh
bash scripts/scan-active-policy.shThe Compose E2E path uses the guided import fixture in
testdata/compose-e2e/guided-import/ and a deterministic OpenAI-compatible
HTTP model fixture. It launches through the frontend /api/v1 proxy and
asserts scoped preview, mapping, publication idempotency, typed run creation,
and durable run completion.
go build -o evaluator ./cmd/evaluator
cd web && pnpm buildMIT License.