| # Forge Code Evaluations |
|
|
| A flexible evaluation framework for running automated tests and benchmarks against Forge Code commands. |
|
|
| ## Quick Start |
|
|
| ### Setup |
|
|
| Before running evaluations, create a `forgee` symlink to the debug binary: |
|
|
| ```bash |
| # Create symlink in your PATH (e.g., ~/bin or /usr/local/bin) |
| ln -sf /path/to/code-forge/target/debug/forge ~/forgee |
| |
| # Or if ~/bin is in your PATH |
| ln -sf $(pwd)/target/debug/forge ~/bin/forgee |
| ``` |
|
|
| **Why is this needed?** Tasks execute in temporary directories, so relative paths like `../../target/debug/forge` won't work. The `forgee` symlink provides a stable absolute path that works from any directory. |
|
|
| ### Running Evaluations |
|
|
| ```bash |
| # Run an evaluation |
| npm run eval ./evals/create_skill/task.yml |
| |
| # Set custom log level |
| LOG_LEVEL=debug npm run eval ./evals/create_skill/task.yml |
| ``` |
|
|
| ## How It Works |
|
|
| The evaluation system executes commands based on task definitions and validates their output. It supports: |
|
|
| - **Parallel execution** with configurable concurrency |
| - **Timeout handling** for long-running tasks |
| - **Data-driven testing** using CSV files |
| - **Output validation** using regex patterns |
| - **Debug artifacts** stored with timestamps |
|
|
| ## Project Structure |
|
|
| ``` |
| benchmarks/ |
| βββ cli.ts # Main CLI entry point |
| βββ command-generator.ts # Command template rendering |
| βββ task-executor.ts # Task execution with timeout support |
| βββ model.ts # TypeScript types for tasks |
| βββ parse.ts # CLI argument parsing |
| βββ evals/ # Evaluation definitions |
| βββ create_skill/ |
| βββ task.yml # Task definition |
| βββ create_skill_tasks.csv # Test data |
| βββ debug/ # Debug outputs (timestamped) |
| ``` |
|
|
| ## Creating an Evaluation |
|
|
| ### 1. Create an Evaluation Directory |
|
|
| ```bash |
| mkdir -p evals/my_eval |
| ``` |
|
|
| ### 2. Create a `task.yml` File |
|
|
| The task file defines how to run your evaluation: |
|
|
| ```yaml |
| # Optional: Commands to run before evaluation starts |
| before_run: |
| - cargo build |
| - npm install |
| |
| # Required: Command(s) to execute for each test case |
| # Single command |
| run: forgee -p '{{prompt}}' |
| |
| # Or multiple commands (executed sequentially) |
| run: |
| - echo "Step 1: {{task}}" |
| - forgee -p '{{prompt}}' |
| - echo "Step 2: Complete" |
| |
| # Execution configuration |
| parallelism: 10 # Number of tasks to run in parallel (default: 1) |
| timeout: 60 # Timeout in seconds (optional) |
| early_exit: true # Stop execution when validations pass (optional) |
| |
| # Optional: Validations to run on output |
| validations: |
| - name: "Check success message" |
| type: regex |
| regex: \[[0-9:]*\] Skill create-skill |
| |
| # Required: Data sources for test cases |
| sources: |
| - csv: my_tasks.csv |
| ``` |
|
|
| #### Task File Schema |
|
|
| **`before_run`** (optional): Array of shell commands to execute before running tasks |
| - Runs sequentially before the main command execution |
| - Executes in a temporary directory created for the evaluation run |
| - Useful for building binaries or setting up dependencies |
| |
| **`run`** (required): Command(s) to execute for each test case |
| - Can be a single string or an array of strings |
| - Commands support template placeholders (e.g., `{{variable}}`) |
| - Multiple commands are executed sequentially |
| - If any command fails, subsequent commands are skipped |
| |
| **`parallelism`** (optional): Number of tasks to run concurrently (default: 1) |
| |
| **`timeout`** (optional): Maximum execution time in seconds per task |
| |
| **`early_exit`** (optional): Stop command execution when all validations pass |
|
|
| **`validations`** (optional): Array of validation rules |
| - `name`: Human-readable description |
| - `type`: Validation type. Supported values: |
| - `regex`: Match output against a regular expression pattern |
| - `shell`: Execute a shell command with output as stdin |
| - For `regex` type: |
| - `regex`: Regular expression pattern to match in output |
| - For `shell` type: |
| - `command`: Shell command to execute (receives task output via stdin) |
| - `exit_code`: Expected exit code (default: 0) |
|
|
| **`sources`** (required): Array of data sources |
| - Currently supports CSV files: `- csv: filename.csv` |
| - Future: Command output: `- cmd: command` |
|
|
| ### 3. Create Test Data (CSV) |
|
|
| Create a CSV file with columns matching your template variables: |
|
|
| ```csv |
| prompt,expected_output |
| "Create a backup script","backup.sh" |
| "Generate API client","api_client.py" |
| ``` |
|
|
| The column names (e.g., `prompt`, `expected_output`) become template variables you can use in your command with `{{column_name}}`. |
|
|
| ### 4. Run the Evaluation |
|
|
| ```bash |
| npm run eval ./evals/my_eval/task.yml |
| ``` |
|
|
| ## Template Variables |
|
|
| Commands support Handlebars template syntax. Variables are populated from CSV columns: |
|
|
| ```yaml |
| run: |
| command: ./tool --input '{{input_file}}' --format {{format}} --verbose |
| ``` |
|
|
| With CSV: |
| ```csv |
| input_file,format |
| data1.txt,json |
| data2.txt,yaml |
| ``` |
|
|
| ## Output and Debugging |
|
|
| ### Debug Artifacts |
|
|
| Each run creates a timestamped debug directory: |
|
|
| ``` |
| evals/my_eval/debug/2025-11-23T10-30-45-123Z/ |
| βββ task-1.log |
| βββ task-2.log |
| βββ task-3.log |
| ``` |
|
|
| Each log file contains the full output (stdout + stderr) from the command execution. |
|
|
| ### Task Status |
|
|
| Tasks can have four statuses: |
|
|
| - **`passed`**: Task completed successfully and passed all validations |
| - **`validation_failed`**: Task completed but failed one or more validations |
| - **`timeout`**: Task exceeded the timeout limit |
| - **`failed`**: Task execution failed (non-zero exit code) |
| |
| ### Logging |
| |
| **Human-readable output (default):** |
| ```bash |
| npm run eval ./evals/my_eval/task.yml |
| ``` |
| |
| **Machine-readable JSON output:** |
| ```bash |
| LOG_JSON=1 npm run eval ./evals/my_eval/task.yml | jq . |
| ``` |
|
|
| **Debug logging:** |
| ```bash |
| LOG_LEVEL=debug npm run eval ./evals/my_eval/task.yml |
| ``` |
|
|
| ## Examples |
|
|
| ### Example 1: Simple Sequential Execution |
|
|
| ```yaml |
| run: echo "Processing {{name}}" |
| sources: |
| - csv: names.csv |
| ``` |
|
|
| ```csv |
| name |
| Alice |
| Bob |
| Charlie |
| ``` |
|
|
| ### Example 2: Parallel Execution with Timeout |
|
|
| ```yaml |
| run: ./slow_task --id {{task_id}} |
| parallelism: 5 |
| timeout: 30 |
| sources: |
| - csv: tasks.csv |
| ``` |
|
|
| ### Example 3: Multiple Commands |
|
|
| ```yaml |
| run: |
| - echo "Starting task {{id}}" |
| - ./process --input {{file}} |
| - echo "Task {{id}} complete" |
| parallelism: 3 |
| timeout: 120 |
| sources: |
| - csv: tasks.csv |
| ``` |
|
|
| ### Example 4: Shell Command Validation |
|
|
| ```yaml |
| run: echo "{{message}}" |
| parallelism: 3 |
| validations: |
| # Using grep to check if output contains specific text |
| - name: "Contains 'test' word" |
| type: shell |
| command: grep -q "test" |
| exit_code: 0 |
| |
| # Count words and ensure it's greater than 2 |
| - name: "More than 2 words" |
| type: shell |
| command: test $(wc -w | awk '{print $1}') -gt 2 |
| exit_code: 0 |
| |
| # Traditional regex validation (for comparison) |
| - name: "Contains test or validation" |
| type: regex |
| regex: "test|validation" |
| sources: |
| - csv: messages.csv |
| ``` |
|
|
| ### Example 5: Regex Validation |
|
|
| ```yaml |
| run: cargo test {{test_name}} |
| validations: |
| - name: "All tests passed" |
| type: regex |
| regex: test result:\s+ok |
| sources: |
| - csv: tests.csv |
| ``` |
|
|
| ## Tips |
|
|
| 1. **Use quotes in commands**: When passing CSV values with spaces, wrap them in quotes: |
| ```yaml |
| command: forge -p '{{prompt}}' |
| ``` |
|
|
| 2. **Build before running**: Use `before_run` to ensure binaries are up-to-date: |
| ```yaml |
| before_run: |
| - cargo build |
| ``` |
|
|
| 3. **Start with low parallelism**: Test with `parallelism: 1` first, then increase: |
| ```yaml |
| parallelism: 1 # Start here |
| ``` |
|
|
| 4. **Set appropriate timeouts**: Add timeouts to prevent hanging: |
| ```yaml |
| timeout: 60 # seconds |
| ``` |
|
|
| 5. **Check debug logs**: When tasks fail, check the debug directory for full output: |
| ```bash |
| cat evals/my_eval/debug/*/task-1.log |
| ``` |
|
|
| ## Exit Codes |
|
|
| The CLI exits with: |
| - **0**: All tasks passed |
| - **1**: One or more tasks failed (excluding timeouts and validation failures) |
|
|