ruby-skill-bench 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +17 -7
- data/docs/architecture.md +131 -170
- data/docs/blind-comparisons.md +9 -0
- data/docs/first-eval-guide.md +1 -1
- data/lib/skill_bench/cli/help_printer.rb +2 -1
- data/lib/skill_bench/clients/all.rb +2 -0
- data/lib/skill_bench/clients/provider_schemas.rb +9 -0
- data/lib/skill_bench/clients/providers/bedrock.rb +49 -0
- data/lib/skill_bench/clients/providers/xai.rb +43 -0
- data/lib/skill_bench/config/defaults.rb +3 -1
- data/lib/skill_bench/config/env_overrides.rb +10 -1
- data/lib/skill_bench/evaluation/runner.rb +5 -4
- data/lib/skill_bench/judge/variance.rb +49 -0
- data/lib/skill_bench/services/delta_table_formatter.rb +17 -1
- data/lib/skill_bench/trend_tracker/trend_calculator.rb +10 -1
- data/lib/skill_bench/version.rb +1 -1
- data/lib/skill_bench.rb +1 -0
- metadata +10 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 78725cfc6d6bcc768b30cdd3da521d4895b7f24d1bd388e1056818a45ae71714
|
|
4
|
+
data.tar.gz: f589debec2945344a06c59443dab4b0a64ee969467cb814d86674e2b706acdf9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6981311a0bd893fb0f0a443cec3934623ae5fab6c1088ea8b720ce55ef0b901b6851bfc06aed29b355dd187cfc0864c24e59bc2959b24e2bd3479bdeb920db03
|
|
7
|
+
data.tar.gz: ed4e639a719d6ab023e53f43484d48e591d1289559d8b178705905e480dbc682fe109915a461d8b547a733507f132b113790db6b7c325df5df7c27719c649b52
|
data/README.md
CHANGED
|
@@ -21,7 +21,7 @@ This repo is one of 6 in a composable AI skill ecosystem:
|
|
|
21
21
|
| [`agent-mcp-runtime`](https://github.com/igmarin/agent-mcp-runtime) | Rust CLI runtime (pack resolution, MCP) |
|
|
22
22
|
| [**`ruby-skill-bench`**](https://github.com/igmarin/ruby-skill-bench) | Benchmark/eval engine |
|
|
23
23
|
|
|
24
|
-
See the [Ecosystem Overview](https://github.com/igmarin/agent-mcp-runtime/blob/main/docs/ecosystem.md) for the full architecture.
|
|
24
|
+
See the [Ecosystem Overview](https://github.com/igmarin/agent-mcp-runtime/blob/main/docs/ecosystem.md) for the full architecture. What comes next in this repo: [ROADMAP.md](ROADMAP.md).
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
@@ -31,7 +31,7 @@ See the [Ecosystem Overview](https://github.com/igmarin/agent-mcp-runtime/blob/m
|
|
|
31
31
|
- **Isolated Git Sandboxes**: Every run operates in a temporary repo. Clean diffs, zero side-effects, 100% reproducibility.
|
|
32
32
|
- **Blind Judging with Dimensions**: LLM judge scores baseline and context independently across 5 canonical dimensions (Correctness, Skill Adherence, Code Quality, Test Coverage, Documentation). Eval authors configure weights and thresholds via `criteria.json`.
|
|
33
33
|
- **Sophisticated ReAct Loop**: Employs a robust `Thought → Tool → Observation` loop to handle complex, multi-step engineering tasks.
|
|
34
|
-
- **Multi-Provider Ecosystem**: Native support for **OpenAI**, **Anthropic**, **Google Gemini**, **Azure OpenAI**, **Ollama**, **Groq**, **DeepSeek**, **Mistral**, and **
|
|
34
|
+
- **Multi-Provider Ecosystem**: Native support for **OpenAI**, **Anthropic**, **Google Gemini**, **Azure OpenAI**, **Ollama**, **Groq**, **DeepSeek**, **Mistral**, **OpenCode**, **OpenRouter**, **xAI**, and **Amazon Bedrock**.
|
|
35
35
|
- **Standardized Intelligence**: Consistent reporting format regardless of the underlying LLM provider.
|
|
36
36
|
|
|
37
37
|
---
|
|
@@ -61,18 +61,28 @@ CLI / API → RunnerService → Sandbox + ReAct Agent → LLM Client Layer → P
|
|
|
61
61
|
| **OpenAI** | `SKILL_BENCH_OPENAI_API_KEY` | `:openai` |
|
|
62
62
|
| **Anthropic** | `SKILL_BENCH_ANTHROPIC_API_KEY` | `:anthropic` |
|
|
63
63
|
| **Gemini** | `SKILL_BENCH_GEMINI_API_KEY` | `:gemini` |
|
|
64
|
-
| **Azure** | `
|
|
64
|
+
| **Azure** | `SKILL_BENCH_AZURE_OPENAI_API_KEY` | `:azure` |
|
|
65
65
|
| **Ollama** | — | `:ollama` |
|
|
66
66
|
| **Groq** | `SKILL_BENCH_GROQ_API_KEY` | `:groq` |
|
|
67
67
|
| **DeepSeek** | `SKILL_BENCH_DEEPSEEK_API_KEY` | `:deepseek` |
|
|
68
|
-
| **Mistral** | `
|
|
68
|
+
| **Mistral** | set `api_key` in `skill-bench.json` (no env mapping) | `:mistral` |
|
|
69
69
|
| **OpenCode** | `SKILL_BENCH_OPENCODE_API_KEY`, `SKILL_BENCH_OPENCODE_BASE_URL` | `:opencode` |
|
|
70
|
+
| **OpenRouter** | `SKILL_BENCH_OPENROUTER_API_KEY` | `:openrouter` |
|
|
71
|
+
| **xAI** | `SKILL_BENCH_XAI_API_KEY` or `XAI_API_KEY` | `:xai` |
|
|
72
|
+
| **Bedrock** | `SKILL_BENCH_BEDROCK_API_KEY` or `AWS_BEARER_TOKEN_BEDROCK` | `:bedrock` |
|
|
73
|
+
| **Mock** | — (offline, `skill-bench init --mock`) | `:mock` |
|
|
70
74
|
|
|
71
75
|
> **Note:** Environment variables are loaded automatically. You can also configure provider settings in `skill-bench.json` (created by `skill-bench init`).
|
|
72
76
|
>
|
|
73
77
|
> **OpenCode requires a custom `base_url`:** OpenCode does not host a public LLM API. You must provide your own OpenAI-compatible endpoint (e.g. a LiteLLM proxy, self-hosted vLLM, or company gateway) via the `base_url` config key. Without it, the provider will fail with "Base URL not set for Opencode".
|
|
74
78
|
>
|
|
75
|
-
> **Mistral** uses Mistral's OpenAI-compatible chat completions API (default model `mistral-large-latest`).
|
|
79
|
+
> **Mistral** uses Mistral's OpenAI-compatible chat completions API (default model `mistral-large-latest`). Scaffold with `skill-bench init --mistral` and set `config.api_key` in `skill-bench.json`. There is no `SKILL_BENCH_MISTRAL_API_KEY` env mapping.
|
|
80
|
+
>
|
|
81
|
+
> **OpenRouter** uses OpenRouter's OpenAI-compatible API (default model `anthropic/claude-3.5-sonnet`). Set `SKILL_BENCH_OPENROUTER_API_KEY` and scaffold with `skill-bench init --openrouter`.
|
|
82
|
+
>
|
|
83
|
+
> **xAI** uses xAI's OpenAI-compatible chat completions API (default model `grok-4`). Set `SKILL_BENCH_XAI_API_KEY` or `XAI_API_KEY` and scaffold with `skill-bench init --xai`.
|
|
84
|
+
>
|
|
85
|
+
> **Amazon Bedrock** uses the Runtime OpenAI-compatible Chat Completions path (`https://bedrock-runtime.<region>.amazonaws.com/openai/v1/chat/completions`) with a Bedrock API key. Default region `us-east-1` (`SKILL_BENCH_BEDROCK_REGION` or `AWS_REGION`), default model `amazon.nova-lite-v1:0`. Scaffold with `skill-bench init --bedrock`. IAM SigV4 signing is not in this client.
|
|
76
86
|
|
|
77
87
|
### Command Allowlist
|
|
78
88
|
|
|
@@ -144,7 +154,7 @@ skill-bench init --openai
|
|
|
144
154
|
}
|
|
145
155
|
```
|
|
146
156
|
|
|
147
|
-
**Available providers:** `--openai`, `--anthropic`, `--gemini`, `--ollama`, `--azure`, `--groq`, `--deepseek`, `--mistral`, `--opencode`
|
|
157
|
+
**Available providers:** `--openai`, `--anthropic`, `--gemini`, `--ollama`, `--azure`, `--groq`, `--deepseek`, `--mistral`, `--opencode`, `--openrouter`, `--xai`, `--bedrock`, plus `--mock` for an offline config with no API key.
|
|
148
158
|
|
|
149
159
|
**Zero-config offline path:** `skill-bench init --mock` scaffolds a minimal offline config that needs no API key and no network — `{"provider":"mock","max_execution_time":30}`. Use it to try the full flow (and run the bundled examples) before wiring up a real provider.
|
|
150
160
|
|
|
@@ -908,7 +918,7 @@ Your eval result depends on **both** conditions. Here is every scenario:
|
|
|
908
918
|
- **Traceability**: Every thought and tool call is logged with full backtrace for post-mortem analysis.
|
|
909
919
|
- **Robust Error Recovery**: Handles provider outages and rate limits gracefully with standardized error logging.
|
|
910
920
|
- **XML-Safe Output**: JUnit XML output is properly escaped to prevent injection attacks.
|
|
911
|
-
- **Test Coverage**:
|
|
921
|
+
- **Test Coverage**: 910 tests covering core engine, CLI commands, and all provider clients. Run `bundle exec rake test` to see the current count.
|
|
912
922
|
|
|
913
923
|
## Testing
|
|
914
924
|
|
data/docs/architecture.md
CHANGED
|
@@ -1,209 +1,170 @@
|
|
|
1
|
-
# SkillBench
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
### `SkillBench::CLI` Commands
|
|
36
|
-
|
|
37
|
-
- `InitCommand` — Creates `skill-bench.json` configuration
|
|
38
|
-
- `RunCommand` — Executes evaluations
|
|
39
|
-
- `SkillCommand` — Scaffolds new skills with templates
|
|
40
|
-
- `EvalCommand` — Creates evaluation scenarios
|
|
41
|
-
|
|
42
|
-
### `SkillBench::Services::TemplateRegistry`
|
|
1
|
+
# SkillBench architecture
|
|
2
|
+
|
|
3
|
+
## Executive summary
|
|
4
|
+
|
|
5
|
+
Ruby Skill Bench is a Ruby gem that runs the same coding task twice: once without skill context (baseline) and once with it (context). An LLM judge scores each run independently. `DeltaReport` then decides pass or fail from the context total and the delta.
|
|
6
|
+
|
|
7
|
+
Source of truth for a run is the eval directory (`task.md` + `criteria.json`), the skill files loaded into context, and `skill-bench.json` (plus env overrides). Runtime artifacts are a temp git sandbox and `.skill-bench-trends.json`.
|
|
8
|
+
|
|
9
|
+
The rule a contributor must not break: host command execution is fail-closed. `run_command` runs inside the Docker `evaluator-sandbox` image when a daemon and image are available. Otherwise it refuses unless `allow_host_execution` is explicitly true.
|
|
10
|
+
|
|
11
|
+
### System architecture
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
User / CI
|
|
15
|
+
|
|
|
16
|
+
v
|
|
17
|
+
bin/skill-bench -> SkillBench::CLI
|
|
18
|
+
|
|
|
19
|
+
+-- init / validate / skill / eval / compare
|
|
20
|
+
+-- run -> Commands::Run -> Services::RunnerService
|
|
21
|
+
|
|
|
22
|
+
+-- EvalResolver, SkillResolverService, ProviderResolver
|
|
23
|
+
+-- ContextLoaderService (ContextHydrator)
|
|
24
|
+
+-- Parallel: baseline agent | context agent
|
|
25
|
+
| Sandbox (tmp git, optional Docker)
|
|
26
|
+
| ReactAgent + Tools + Client
|
|
27
|
+
+-- Evaluation::Runner (blind Judge x2, concurrent)
|
|
28
|
+
+-- DeltaReport
|
|
29
|
+
+-- TrendRecorderService
|
|
30
|
+
+-- CostCalculator
|
|
31
|
+
v
|
|
32
|
+
OutputFormatter (human / json / junit / html)
|
|
33
|
+
```
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
- Supports three template types: `task_md`, `criteria_json`, `skill_md`
|
|
46
|
-
- Offers 10 Rails pattern categories: `crud`, `api`, `background_job`, `controller`, `model`, `migration`, `concern`, `policy`, `form_object`, `view_component`
|
|
47
|
-
- Enables variable interpolation using `{{variable_name}}` syntax
|
|
48
|
-
- Used for programmatic eval creation and tool building
|
|
35
|
+
Outside the gem: LLM HTTP APIs (OpenAI-compatible and native), optional Docker daemon, optional ecosystem `registry.json` for `--pack`.
|
|
49
36
|
|
|
50
|
-
###
|
|
37
|
+
### Dependency hierarchy
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
39
|
+
```text
|
|
40
|
+
CLI -> Commands -> Services (RunnerService, BatchRunnerService, ComparisonRunner)
|
|
41
|
+
|
|
|
42
|
+
+--> Agent / Tools
|
|
43
|
+
+--> Execution (Sandbox, ContextHydrator)
|
|
44
|
+
+--> Evaluation::Runner -> Judge -> Clients
|
|
45
|
+
+--> Config
|
|
46
|
+
+--> Models (Eval, Skill, Criteria, Provider)
|
|
47
|
+
```
|
|
56
48
|
|
|
57
|
-
|
|
49
|
+
Dependencies point inward: CLI does not call providers; agents do not load config files; clients do not know about evals. Adding a provider means a `Clients::Providers::*` class plus a `ProviderSchemas` entry. Adding a command means a `Cli::*Command` plus a `Commands::*` object. Do not reverse that.
|
|
58
50
|
|
|
59
|
-
|
|
60
|
-
- Supports tool usage (e.g., `read_file`, `write_file`, `run_shell_command`).
|
|
61
|
-
- Manages conversation history.
|
|
51
|
+
---
|
|
62
52
|
|
|
63
|
-
|
|
53
|
+
## Eval run lifecycle
|
|
64
54
|
|
|
65
|
-
-
|
|
66
|
-
- Handles Faraday connection setup and timeouts.
|
|
67
|
-
- Centralizes error logging and response normalization.
|
|
68
|
-
- Delegates to `ResponseParser`, `ResponseErrorHandler`, and `RequestBuilder`
|
|
55
|
+
Exact order for `skill-bench run <eval> --skill <name>`:
|
|
69
56
|
|
|
70
|
-
|
|
57
|
+
1. `SkillBench::CLI` shifts the subcommand and calls `Cli::RunCommand`.
|
|
58
|
+
2. `RunCommand` parses flags (`--skill`, `--pack`, `--format`, `--all`, `--evals-dir`, `--summary`, `--cache`). `--cache` sets `SKILL_BENCH_CACHE=1`.
|
|
59
|
+
3. `Commands::Run` calls `Services::RunnerService.call`.
|
|
60
|
+
4. `EvalResolver` loads `task.md` and `criteria.json`. Path with `/` is used as-is; otherwise `evals/` is prepended.
|
|
61
|
+
5. `SkillResolverService` resolves skills (local path, `skill_sources`, or `--pack` via `Registry::PackResolver`).
|
|
62
|
+
6. `ProviderResolver` builds a `Models::Provider` from `Config` (defaults, then `~/.skill-bench.json`, then `./skill-bench.json`, then `ENV`).
|
|
63
|
+
7. `ContextLoaderService` / `Execution::ContextHydrator` reads skill files. Allowed extensions: `.md`, `.rb`, `.json`, `.yml`, `.yaml`, `.txt`. Per-file cap 50_000 bytes. Total cap 1_000_000 bytes. Symlinks are skipped. Empty context is an error.
|
|
64
|
+
8. `RunnerService` runs baseline and context agents concurrently (`Parallel.map`, two threads). Each agent runs inside `Execution::Sandbox.run`: copy sources into `Dir.mktmpdir`, hardened `git init`, start Docker if available, yield, stop container, delete the tempdir.
|
|
65
|
+
9. `Evaluation::Runner` judges baseline and context concurrently. Both judges get `skill_context: nil` (skill text is only for the executing agent). The judge never sees both outputs in one call.
|
|
66
|
+
10. `DeltaReport` computes per-dimension deltas. Verdict is `context_total >= pass_threshold AND total_delta >= minimum_delta`.
|
|
67
|
+
11. `TrendRecorderService` appends `.skill-bench-trends.json` and keeps `.skill-bench-trends.json.bak`.
|
|
68
|
+
12. `CostCalculator` estimates USD from aggregated token usage.
|
|
69
|
+
13. `Cli::ResultPrinter` / `SkillBench::OutputFormatter` prints human, json, junit, or html and returns exit 0 or 1.
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
- Human format displays a dimension table with baseline, context, and delta columns
|
|
74
|
-
- Escapes XML output to prevent injection
|
|
75
|
-
- Provides exit codes for CI/CD integration
|
|
71
|
+
Batch mode (`--all` / `--evals-dir`) uses `BatchRunnerService` instead of a single `RunnerService` call. `compare` runs `RunnerService` twice via `ComparisonRunner`.
|
|
76
72
|
|
|
77
|
-
|
|
73
|
+
Failure behavior:
|
|
78
74
|
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
75
|
+
- Missing provider config returns a config error envelope; it does not raise through the CLI.
|
|
76
|
+
- Agent `:error` status short-circuits judging.
|
|
77
|
+
- Judge parse failure returns `{ success: false, response: { error: { message: } } }`.
|
|
78
|
+
- `run_command` with no container and `allow_host_execution` false returns `HOST_EXECUTION_REFUSED` and does not exec.
|
|
82
79
|
|
|
83
|
-
|
|
80
|
+
---
|
|
84
81
|
|
|
85
|
-
|
|
82
|
+
## Components
|
|
86
83
|
|
|
87
|
-
###
|
|
84
|
+
### `SkillBench::CLI`
|
|
88
85
|
|
|
89
|
-
|
|
90
|
-
# RunnerService builds this and passes it to EvaluationRunner.call
|
|
91
|
-
evaluation = {
|
|
92
|
-
task: "Create a UserRegistrationService...", # from task.md
|
|
93
|
-
criteria: <Criteria object>, # from criteria.json
|
|
94
|
-
skill_context: "<agent_context>...SKILL.md...</agent_context>", # from ContextHydrator
|
|
95
|
-
baseline_output: '{"result":"...","status":":success"}', # from baseline agent run
|
|
96
|
-
context_output: '{"result":"...","status":":success"}' # from context agent run
|
|
97
|
-
}
|
|
98
|
-
```
|
|
86
|
+
Owns ARGV dispatch and exit codes. Does not own eval scoring. Subcommands: `init`, `run`, `compare`, `skill`, `eval`, `validate`/`doctor`, `help`.
|
|
99
87
|
|
|
100
|
-
###
|
|
101
|
-
|
|
102
|
-
```ruby
|
|
103
|
-
# First call — baseline (no skill context)
|
|
104
|
-
JudgePrompt.call(
|
|
105
|
-
task: task,
|
|
106
|
-
criteria: criteria,
|
|
107
|
-
skill_context: "", # empty string for baseline
|
|
108
|
-
agent_output: baseline_output
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
# Second call — context (with skill context)
|
|
112
|
-
JudgePrompt.call(
|
|
113
|
-
task: task,
|
|
114
|
-
criteria: criteria,
|
|
115
|
-
skill_context: skill_context, # XML-wrapped SKILL.md
|
|
116
|
-
agent_output: context_output
|
|
117
|
-
)
|
|
118
|
-
```
|
|
88
|
+
### `Services::RunnerService`
|
|
119
89
|
|
|
120
|
-
|
|
90
|
+
Owns one eval's baseline+context orchestration, token aggregation, and the envelope passed to the printer. Does not own HTTP or git. Depends on resolvers, sandbox/agent spawners, `Evaluation::Runner`, `TrendRecorderService`, `CostCalculator`.
|
|
121
91
|
|
|
122
|
-
|
|
92
|
+
### `Execution::Sandbox`
|
|
123
93
|
|
|
124
|
-
|
|
125
|
-
{
|
|
126
|
-
"dimensions": {
|
|
127
|
-
"correctness": { "score": 28, "max_score": 30, "reasoning": "All requirements met." },
|
|
128
|
-
"skill_adherence": { "score": 22, "max_score": 25, "reasoning": "Used .call pattern correctly." }
|
|
129
|
-
},
|
|
130
|
-
"overall_reasoning": "Solid implementation."
|
|
131
|
-
}
|
|
132
|
-
```
|
|
94
|
+
Owns the temp directory, hardened git (`core.hooksPath=/dev/null`, no source `.git` copy), and container lifecycle. Image ref is `evaluator-sandbox:<VERSION>`. Docker run flags: `--network none`, non-root `--user uid:gid`, `--cap-drop ALL` (then CHOWN and DAC_OVERRIDE), `--security-opt no-new-privileges`, volume mount of the sandbox. Does not own command allowlisting (`Tools::RunCommand` does).
|
|
133
95
|
|
|
134
|
-
`
|
|
96
|
+
### `Execution::ContextHydrator`
|
|
135
97
|
|
|
136
|
-
|
|
98
|
+
Owns packing skill files into XML for the agent system prompt. Does not own skill path resolution.
|
|
137
99
|
|
|
138
|
-
|
|
139
|
-
# DeltaReport receives two JudgeResponse objects
|
|
140
|
-
baseline = {
|
|
141
|
-
'correctness' => { score: 12, max_score: 30 },
|
|
142
|
-
'skill_adherence' => { score: 5, max_score: 25 }
|
|
143
|
-
}
|
|
100
|
+
### `Agent::ReactAgent`
|
|
144
101
|
|
|
145
|
-
|
|
146
|
-
'correctness' => { score: 28, max_score: 30 },
|
|
147
|
-
'skill_adherence' => { score: 22, max_score: 25 }
|
|
148
|
-
}
|
|
102
|
+
Owns the Thought → Tool → Observation loop, capped at `max_iterations` (default 25). Tools: `read_file`, `write_file`, `run_command`. Does not own judging.
|
|
149
103
|
|
|
150
|
-
|
|
151
|
-
deltas = {
|
|
152
|
-
'correctness' => 16, # 28 - 12
|
|
153
|
-
'skill_adherence' => 17 # 22 - 5
|
|
154
|
-
}
|
|
104
|
+
### `Evaluation::Runner`
|
|
155
105
|
|
|
156
|
-
|
|
157
|
-
context_total = 50 # 28 + 22
|
|
158
|
-
verdict = context_total >= pass_threshold && (context_total - baseline_total) >= minimum_delta
|
|
159
|
-
```
|
|
106
|
+
Owns blind judging and the call to `DeltaReport`. Two judge HTTP calls, concurrent, order preserved. Does not own agent execution.
|
|
160
107
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
The evaluator relies on a strict directory convention:
|
|
164
|
-
|
|
165
|
-
```bash
|
|
166
|
-
project-root/
|
|
167
|
-
├── skill-bench.json # Provider configuration
|
|
168
|
-
├── skills/
|
|
169
|
-
│ └── my-service/
|
|
170
|
-
│ └── SKILL.md # Skill instructions
|
|
171
|
-
├── evals/
|
|
172
|
-
│ └── my-first-eval/
|
|
173
|
-
│ ├── task.md # Agent prompt
|
|
174
|
-
│ └── criteria.json # Scoring rules
|
|
175
|
-
└── .skill-bench-trends.json # Benchmark history (auto-generated)
|
|
176
|
-
```
|
|
108
|
+
### `DeltaReport`
|
|
177
109
|
|
|
178
|
-
|
|
110
|
+
Owns per-dimension arithmetic and the boolean verdict. Does not call the network.
|
|
179
111
|
|
|
180
|
-
###
|
|
112
|
+
### `Clients::BaseClient` and `ProviderRegistry`
|
|
181
113
|
|
|
182
|
-
|
|
114
|
+
Own HTTP to LLM providers (Faraday, retries, response normalization). Registered providers: openai, anthropic, gemini, ollama, azure, groq, deepseek, mistral, opencode, openrouter, xai, bedrock, plus mock and null. Do not own eval files.
|
|
183
115
|
|
|
184
|
-
|
|
185
|
-
skills/my-service/SKILL.md
|
|
186
|
-
skills/api/rest-collection/SKILL.md
|
|
187
|
-
skills/workflows/tdd-loop/SKILL.md
|
|
188
|
-
```
|
|
116
|
+
### `Config`
|
|
189
117
|
|
|
190
|
-
|
|
118
|
+
Owns the hierarchy Defaults → home JSON → local JSON → ENV. `EnvOverrides` maps `SKILL_BENCH_*` keys. Mistral has no env mapping; its key is `config.api_key` in JSON. Azure env key is `SKILL_BENCH_AZURE_OPENAI_API_KEY`.
|
|
191
119
|
|
|
192
|
-
###
|
|
120
|
+
### `SkillBench::OutputFormatter`
|
|
193
121
|
|
|
194
|
-
|
|
122
|
+
Owns user-facing result text. Delegates to `JsonFormatter`, `JUnitFormatter`, `HtmlFormatter`, `DeltaTableFormatter`, `IterationFormatter`, `FeedbackGenerator`. Distinct from `Services::OutputFormatter`, which stringifies agent output for the judge.
|
|
195
123
|
|
|
196
|
-
|
|
197
|
-
2. Otherwise, prepend `evals/` (e.g., `my-eval` → `evals/my-eval`)
|
|
124
|
+
### `ErrorLogger`
|
|
198
125
|
|
|
199
|
-
|
|
126
|
+
Logs message plus first five backtrace lines. Uses `Rails.logger` when `Rails` is defined; otherwise `warn`.
|
|
200
127
|
|
|
201
|
-
|
|
202
|
-
- `criteria.json` — the scoring rules (optional; defaults to empty criteria if missing)
|
|
128
|
+
---
|
|
203
129
|
|
|
204
130
|
## Command isolation
|
|
205
131
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
132
|
+
See [docker.md](docker.md) for the image contract and `rake docker:build`. Live Docker tests are opt-in via `SKILL_BENCH_DOCKER_TESTS=1`.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Source map
|
|
137
|
+
|
|
138
|
+
| Concept | Authoritative file |
|
|
139
|
+
| --- | --- |
|
|
140
|
+
| CLI dispatch | `lib/skill_bench/cli.rb` |
|
|
141
|
+
| Single eval | `lib/skill_bench/services/runner_service.rb` |
|
|
142
|
+
| Blind judge | `lib/skill_bench/evaluation/runner.rb` |
|
|
143
|
+
| Verdict | `lib/skill_bench/delta_report.rb` |
|
|
144
|
+
| Sandbox + Docker | `lib/skill_bench/execution/sandbox.rb` |
|
|
145
|
+
| Host exec gate | `lib/skill_bench/tools/run_command.rb` |
|
|
146
|
+
| Context caps | `lib/skill_bench/constants.rb` (`ContextHydration`) |
|
|
147
|
+
| Config hierarchy | `lib/skill_bench/config.rb` |
|
|
148
|
+
| Provider env keys | `lib/skill_bench/config/env_overrides.rb` |
|
|
149
|
+
| Provider list | `lib/skill_bench/clients/provider_schemas.rb` |
|
|
150
|
+
| Image contract | `docs/docker.md` |
|
|
151
|
+
|
|
152
|
+
Deprecated entry points still in the tree, not on the live path: `SkillBench::Runner`, `EvaluateCommand`, `Task::Evaluator`.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Verification
|
|
157
|
+
|
|
158
|
+
| Claim | Evidence |
|
|
159
|
+
| --- | --- |
|
|
160
|
+
| CLI subcommands | `SkillBench::CLI#call` case in `lib/skill_bench/cli.rb` |
|
|
161
|
+
| Concurrent agents | `RunnerService#run_agents_concurrently` |
|
|
162
|
+
| Concurrent judges | `Evaluation::Runner#run_judges_concurrently` |
|
|
163
|
+
| Verdict formula | `DeltaReport#determine_verdict` |
|
|
164
|
+
| Fail-closed host exec | `Tools::RunCommand` + `Sandbox` comments and `allow_host_execution` default |
|
|
165
|
+
| Docker flags | `Sandbox#start_container` |
|
|
166
|
+
| Context size caps | `Constants::ContextHydration` + `ContextHydrator#collect_context_files` |
|
|
167
|
+
| `--cache` | `Cli::RunCommand` sets `ENV['SKILL_BENCH_CACHE']`; `HelpPrinter` documents it |
|
|
168
|
+
| OpenRouter env | `EnvOverrides::ENV_TO_PROVIDER_SETTINGS` |
|
|
169
|
+
|
|
170
|
+
Evidence gap: whether every provider in `ProviderSchemas` is registered at boot is covered by client tests under `test/evaluator/clients/`, not by a single registry integration test named in this document.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Comparing skill revisions
|
|
2
|
+
|
|
3
|
+
Use the same task, fixture commit, acceptance tests, criteria file, model, host configuration, and repetition count for no skills, current skills, and revised skills. Keep a held-out task set. Run the current and revised conditions against the same no-skills baseline protocol; archive the actual outputs and provenance alongside each report.
|
|
4
|
+
|
|
5
|
+
`Evaluation::Runner` gives both independent judges the same task and criteria. Skill instructions are supplied only to the executing agent. Its `skill_context` argument remains accepted for compatibility but is deliberately omitted from judging. The active evaluator scores one output per independent request, so there is no paired presentation order. A future paired judge must shuffle anonymous outputs and map scores back outside its prompt.
|
|
6
|
+
|
|
7
|
+
Write criteria as observable task requirements before generating outputs. The legacy `skill_adherence` dimension name remains supported; its description must state an independently assessable requirement rather than ask the judge to infer whether a skill was used. Keep treatment names and instructions out of criteria and agent summaries sent to the judge.
|
|
8
|
+
|
|
9
|
+
Executable acceptance tests take precedence over prose scores. Preserve test failures and unavailable checks as evidence; a prose score cannot turn either into a passing execution result. Report repetitions, fixture and skill commits, host/model versions, actual usage, and incomplete runs. A small or inconclusive sample establishes no statistical improvement. Paid comparisons require a manual invocation and an explicit usage cap; the unit suite makes no provider calls.
|
data/docs/first-eval-guide.md
CHANGED
|
@@ -42,7 +42,7 @@ skill-bench init --openai
|
|
|
42
42
|
|
|
43
43
|
This creates `skill-bench.json` with the OpenAI provider config. Use `--force` to overwrite.
|
|
44
44
|
|
|
45
|
-
**Available providers:** `--openai`, `--anthropic`, `--gemini`, `--ollama`, `--azure`, `--groq`, `--deepseek`, `--opencode`
|
|
45
|
+
**Available providers:** `--openai`, `--anthropic`, `--gemini`, `--ollama`, `--azure`, `--groq`, `--deepseek`, `--mistral`, `--opencode`, `--openrouter`, `--xai`, `--bedrock`, plus `--mock` for an offline config with no API key.
|
|
46
46
|
|
|
47
47
|
> **What is `skill-bench.json`?** This is your config file. It stores your API key, chosen LLM model, timeout, and allowed shell commands. Think of it as `.env` but structured as JSON. You edit it; SkillBench reads it.
|
|
48
48
|
|
|
@@ -8,7 +8,7 @@ module SkillBench
|
|
|
8
8
|
#
|
|
9
9
|
# @return [Integer] Exit code (always 0)
|
|
10
10
|
def self.call
|
|
11
|
-
providers = SkillBench::Clients::ProviderSchemas.names.map { |name| "--#{name}" }.join(', ')
|
|
11
|
+
providers = (SkillBench::Clients::ProviderSchemas.names.map { |name| "--#{name}" } + ['--mock']).join(', ')
|
|
12
12
|
|
|
13
13
|
puts <<~USAGE
|
|
14
14
|
Usage: skill-bench <subcommand> [options]
|
|
@@ -28,6 +28,7 @@ module SkillBench
|
|
|
28
28
|
--all Run every eval under evals/ (batch mode)
|
|
29
29
|
--evals-dir DIR Run every eval under DIR (batch mode)
|
|
30
30
|
--summary Emit a JSON summary gate for a batch run (batch mode)
|
|
31
|
+
--cache Enable content-addressed response caching
|
|
31
32
|
|
|
32
33
|
compare <skill-name> --variant-a SPEC --variant-b SPEC --eval PATH
|
|
33
34
|
Compare the same skill across two pack variants
|
|
@@ -20,4 +20,6 @@ require_relative 'providers/groq'
|
|
|
20
20
|
require_relative 'providers/deepseek'
|
|
21
21
|
require_relative 'providers/mistral'
|
|
22
22
|
require_relative 'providers/openrouter'
|
|
23
|
+
require_relative 'providers/xai'
|
|
24
|
+
require_relative 'providers/bedrock'
|
|
23
25
|
require_relative 'providers/mock'
|
|
@@ -51,6 +51,15 @@ module SkillBench
|
|
|
51
51
|
openrouter: {
|
|
52
52
|
api_key: nil,
|
|
53
53
|
model: 'anthropic/claude-3.5-sonnet'
|
|
54
|
+
}.freeze,
|
|
55
|
+
xai: {
|
|
56
|
+
api_key: nil,
|
|
57
|
+
model: 'grok-4'
|
|
58
|
+
}.freeze,
|
|
59
|
+
bedrock: {
|
|
60
|
+
api_key: nil,
|
|
61
|
+
model: 'amazon.nova-lite-v1:0',
|
|
62
|
+
location: 'us-east-1'
|
|
54
63
|
}.freeze
|
|
55
64
|
}.freeze
|
|
56
65
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../base_client'
|
|
4
|
+
require_relative '../provider_registry'
|
|
5
|
+
|
|
6
|
+
module SkillBench
|
|
7
|
+
module Clients
|
|
8
|
+
module Providers
|
|
9
|
+
# Amazon Bedrock LLM client.
|
|
10
|
+
# Uses Bedrock Runtime's OpenAI-compatible Chat Completions path with a
|
|
11
|
+
# Bedrock API key (bearer). IAM SigV4 signing is a follow-up.
|
|
12
|
+
class Bedrock < BaseClient
|
|
13
|
+
SkillBench::Clients::ProviderRegistry.register(:bedrock, self)
|
|
14
|
+
|
|
15
|
+
DEFAULT_REGION = 'us-east-1'
|
|
16
|
+
|
|
17
|
+
# Returns the provider identifier.
|
|
18
|
+
#
|
|
19
|
+
# @return [Symbol]
|
|
20
|
+
def provider_name
|
|
21
|
+
:bedrock
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
protected
|
|
25
|
+
|
|
26
|
+
# Returns the Bedrock Runtime host for the configured region.
|
|
27
|
+
#
|
|
28
|
+
# @return [String]
|
|
29
|
+
def base_url
|
|
30
|
+
@base_url_config || "https://bedrock-runtime.#{region}.amazonaws.com"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns the OpenAI-compatible chat completions path.
|
|
34
|
+
#
|
|
35
|
+
# @return [String]
|
|
36
|
+
def request_path
|
|
37
|
+
@request_path_config || '/openai/v1/chat/completions'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def region
|
|
43
|
+
loc = @location.to_s.strip
|
|
44
|
+
loc.empty? ? DEFAULT_REGION : loc
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../base_client'
|
|
4
|
+
require_relative '../provider_registry'
|
|
5
|
+
|
|
6
|
+
module SkillBench
|
|
7
|
+
module Clients
|
|
8
|
+
module Providers
|
|
9
|
+
# xAI (Grok) LLM client.
|
|
10
|
+
# Uses xAI's OpenAI-compatible chat completions API with bearer-token auth.
|
|
11
|
+
class Xai < BaseClient
|
|
12
|
+
SkillBench::Clients::ProviderRegistry.register(:xai, self)
|
|
13
|
+
|
|
14
|
+
# Returns the provider identifier.
|
|
15
|
+
#
|
|
16
|
+
# @return [Symbol]
|
|
17
|
+
def provider_name
|
|
18
|
+
:xai
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
protected
|
|
22
|
+
|
|
23
|
+
# Returns the base URL for the xAI API.
|
|
24
|
+
#
|
|
25
|
+
# The version segment lives in {#request_path} so Faraday does not drop
|
|
26
|
+
# it (an absolute request path replaces any path component of the
|
|
27
|
+
# connection base URL).
|
|
28
|
+
#
|
|
29
|
+
# @return [String]
|
|
30
|
+
def base_url
|
|
31
|
+
@base_url_config || 'https://api.x.ai'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns the request path for chat completions.
|
|
35
|
+
#
|
|
36
|
+
# @return [String]
|
|
37
|
+
def request_path
|
|
38
|
+
@request_path_config || '/v1/chat/completions'
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -36,7 +36,9 @@ module SkillBench
|
|
|
36
36
|
groq: { api_key: nil, model: 'llama-3.3-70b-versatile' },
|
|
37
37
|
deepseek: { api_key: nil, model: 'deepseek-chat' },
|
|
38
38
|
opencode: { api_key: nil, model: 'opencode-model', base_url: nil },
|
|
39
|
-
openrouter: { api_key: nil, model: 'anthropic/claude-3.5-sonnet' }
|
|
39
|
+
openrouter: { api_key: nil, model: 'anthropic/claude-3.5-sonnet' },
|
|
40
|
+
xai: { api_key: nil, model: 'grok-4' },
|
|
41
|
+
bedrock: { api_key: nil, model: 'amazon.nova-lite-v1:0', location: 'us-east-1' }
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
end
|
|
@@ -49,7 +49,16 @@ module SkillBench
|
|
|
49
49
|
'SKILL_BENCH_OPENCODE_MODEL' => %i[opencode model],
|
|
50
50
|
'OPENCODE_MODEL' => %i[opencode model],
|
|
51
51
|
'SKILL_BENCH_OPENROUTER_API_KEY' => %i[openrouter api_key],
|
|
52
|
-
'SKILL_BENCH_OPENROUTER_MODEL' => %i[openrouter model]
|
|
52
|
+
'SKILL_BENCH_OPENROUTER_MODEL' => %i[openrouter model],
|
|
53
|
+
'SKILL_BENCH_XAI_API_KEY' => %i[xai api_key],
|
|
54
|
+
'XAI_API_KEY' => %i[xai api_key],
|
|
55
|
+
'SKILL_BENCH_XAI_MODEL' => %i[xai model],
|
|
56
|
+
'XAI_MODEL' => %i[xai model],
|
|
57
|
+
'SKILL_BENCH_BEDROCK_API_KEY' => %i[bedrock api_key],
|
|
58
|
+
'AWS_BEARER_TOKEN_BEDROCK' => %i[bedrock api_key],
|
|
59
|
+
'SKILL_BENCH_BEDROCK_MODEL' => %i[bedrock model],
|
|
60
|
+
'SKILL_BENCH_BEDROCK_REGION' => %i[bedrock location],
|
|
61
|
+
'AWS_REGION' => %i[bedrock location]
|
|
53
62
|
}.freeze
|
|
54
63
|
|
|
55
64
|
# Returns provider overrides from the given environment.
|
|
@@ -31,6 +31,7 @@ module SkillBench
|
|
|
31
31
|
def initialize(task:, criteria:, skill_context:, baseline_output:, context_output:, judge_params: {})
|
|
32
32
|
@task = task
|
|
33
33
|
@criteria = criteria
|
|
34
|
+
# Callers still pass skill_context; it is not forwarded to judges.
|
|
34
35
|
@skill_context = skill_context
|
|
35
36
|
@baseline_output = baseline_output
|
|
36
37
|
@context_output = context_output
|
|
@@ -66,17 +67,17 @@ module SkillBench
|
|
|
66
67
|
# @return [Array(Hash, Hash)] Baseline and context judge results, in order.
|
|
67
68
|
def run_judges_concurrently
|
|
68
69
|
runs = [
|
|
69
|
-
-> { judge_run(baseline_output
|
|
70
|
-
-> { judge_run(context_output
|
|
70
|
+
-> { judge_run(baseline_output) },
|
|
71
|
+
-> { judge_run(context_output) }
|
|
71
72
|
]
|
|
72
73
|
Parallel.map(runs, in_threads: runs.size, &:call)
|
|
73
74
|
end
|
|
74
75
|
|
|
75
|
-
def judge_run(output
|
|
76
|
+
def judge_run(output)
|
|
76
77
|
prompt_result = Judge::Prompt.call(
|
|
77
78
|
task: task,
|
|
78
79
|
criteria: criteria,
|
|
79
|
-
skill_context:
|
|
80
|
+
skill_context: nil,
|
|
80
81
|
agent_output: output
|
|
81
82
|
)
|
|
82
83
|
return prompt_result unless prompt_result[:success]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../error_logger'
|
|
4
|
+
|
|
5
|
+
module SkillBench
|
|
6
|
+
module Judge
|
|
7
|
+
# Sample statistics over repeated judge totals.
|
|
8
|
+
class Variance
|
|
9
|
+
# @param totals [Array<Numeric>] Judge totals from repeated runs of the same eval.
|
|
10
|
+
# @return [Hash] `{ success: true, response: { n:, mean:, stddev:, spread: } }`
|
|
11
|
+
def self.call(totals:)
|
|
12
|
+
new(totals).call
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @param totals [Array<Numeric>]
|
|
16
|
+
def initialize(totals)
|
|
17
|
+
@totals = totals
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @return [Hash]
|
|
21
|
+
def call
|
|
22
|
+
nums = Array(@totals).map { |total| Float(total) }
|
|
23
|
+
return empty_result(count: nums.size) if nums.size < 2
|
|
24
|
+
|
|
25
|
+
mean = nums.sum / nums.size
|
|
26
|
+
sample_variance = nums.sum { |value| (value - mean)**2 } / (nums.size - 1)
|
|
27
|
+
|
|
28
|
+
{
|
|
29
|
+
success: true,
|
|
30
|
+
response: {
|
|
31
|
+
n: nums.size,
|
|
32
|
+
mean: mean,
|
|
33
|
+
stddev: Math.sqrt(sample_variance),
|
|
34
|
+
spread: nums.max - nums.min
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
rescue StandardError => e
|
|
38
|
+
SkillBench::ErrorLogger.log_error(e, 'Judge::Variance Error')
|
|
39
|
+
{ success: false, response: { error: { message: e.message } } }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def empty_result(count:)
|
|
45
|
+
{ success: true, response: { n: count, mean: nil, stddev: nil, spread: nil } }
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -65,7 +65,23 @@ module SkillBench
|
|
|
65
65
|
context_icon = trend_icon(trend[:context_trend])
|
|
66
66
|
baseline_delta = trend[:baseline_delta]
|
|
67
67
|
context_delta = trend[:context_delta]
|
|
68
|
-
|
|
68
|
+
lines = [
|
|
69
|
+
" TREND: baseline #{baseline_icon} (#{delta_str(baseline_delta)}), context #{context_icon} (#{delta_str(context_delta)})"
|
|
70
|
+
]
|
|
71
|
+
lines << format_judge_variance(trend[:judge_variance])
|
|
72
|
+
lines.compact.join("\n")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private_class_method def self.format_judge_variance(stats)
|
|
76
|
+
return nil unless stats.is_a?(Hash) && stats[:n].to_i >= 2
|
|
77
|
+
|
|
78
|
+
Kernel.format(
|
|
79
|
+
' JUDGE n=%<n>d mean=%<mean>.1f σ=%<stddev>.1f spread=%<spread>.1f',
|
|
80
|
+
n: stats[:n],
|
|
81
|
+
mean: stats[:mean],
|
|
82
|
+
stddev: stats[:stddev],
|
|
83
|
+
spread: stats[:spread]
|
|
84
|
+
)
|
|
69
85
|
end
|
|
70
86
|
end
|
|
71
87
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../judge/variance'
|
|
4
|
+
|
|
3
5
|
module SkillBench
|
|
4
6
|
class TrendTracker
|
|
5
7
|
# Calculates performance trends between evaluation results
|
|
@@ -25,7 +27,8 @@ module SkillBench
|
|
|
25
27
|
context_trend: trend_direction(current_context, previous_context),
|
|
26
28
|
baseline_delta: current_baseline - previous_baseline,
|
|
27
29
|
context_delta: current_context - previous_context,
|
|
28
|
-
previous_run: previous[:timestamp]
|
|
30
|
+
previous_run: previous[:timestamp],
|
|
31
|
+
judge_variance: judge_variance(matching, current_context)
|
|
29
32
|
}
|
|
30
33
|
end
|
|
31
34
|
|
|
@@ -54,6 +57,12 @@ module SkillBench
|
|
|
54
57
|
|
|
55
58
|
current > previous ? :improved : :regressed
|
|
56
59
|
end
|
|
60
|
+
|
|
61
|
+
def judge_variance(matching, current_context)
|
|
62
|
+
totals = matching.filter_map { |entry| entry[:context_total] } + [current_context]
|
|
63
|
+
result = SkillBench::Judge::Variance.call(totals: totals)
|
|
64
|
+
result[:success] ? result[:response] : nil
|
|
65
|
+
end
|
|
57
66
|
end
|
|
58
67
|
end
|
|
59
68
|
end
|
data/lib/skill_bench/version.rb
CHANGED
data/lib/skill_bench.rb
CHANGED
|
@@ -22,6 +22,7 @@ require_relative 'skill_bench/judge'
|
|
|
22
22
|
require_relative 'skill_bench/judge/judge'
|
|
23
23
|
require_relative 'skill_bench/judge/prompt'
|
|
24
24
|
require_relative 'skill_bench/judge/response'
|
|
25
|
+
require_relative 'skill_bench/judge/variance'
|
|
25
26
|
|
|
26
27
|
# Agent subsystem
|
|
27
28
|
require_relative 'skill_bench/agent'
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-skill-bench
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ismael Marin
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-09-14 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: cgi
|
|
@@ -86,6 +87,7 @@ files:
|
|
|
86
87
|
- README.md
|
|
87
88
|
- bin/skill-bench
|
|
88
89
|
- docs/architecture.md
|
|
90
|
+
- docs/blind-comparisons.md
|
|
89
91
|
- docs/docker.md
|
|
90
92
|
- docs/first-eval-guide.md
|
|
91
93
|
- docs/testing-guide.md
|
|
@@ -119,6 +121,7 @@ files:
|
|
|
119
121
|
- lib/skill_bench/clients/provider_schemas.rb
|
|
120
122
|
- lib/skill_bench/clients/providers/anthropic.rb
|
|
121
123
|
- lib/skill_bench/clients/providers/azure_openai.rb
|
|
124
|
+
- lib/skill_bench/clients/providers/bedrock.rb
|
|
122
125
|
- lib/skill_bench/clients/providers/deepseek.rb
|
|
123
126
|
- lib/skill_bench/clients/providers/gemini.rb
|
|
124
127
|
- lib/skill_bench/clients/providers/groq.rb
|
|
@@ -129,6 +132,7 @@ files:
|
|
|
129
132
|
- lib/skill_bench/clients/providers/openai.rb
|
|
130
133
|
- lib/skill_bench/clients/providers/opencode.rb
|
|
131
134
|
- lib/skill_bench/clients/providers/openrouter.rb
|
|
135
|
+
- lib/skill_bench/clients/providers/xai.rb
|
|
132
136
|
- lib/skill_bench/clients/request_builder.rb
|
|
133
137
|
- lib/skill_bench/clients/response_builder.rb
|
|
134
138
|
- lib/skill_bench/clients/response_error_handler.rb
|
|
@@ -171,6 +175,7 @@ files:
|
|
|
171
175
|
- lib/skill_bench/judge/judge.rb
|
|
172
176
|
- lib/skill_bench/judge/prompt.rb
|
|
173
177
|
- lib/skill_bench/judge/response.rb
|
|
178
|
+
- lib/skill_bench/judge/variance.rb
|
|
174
179
|
- lib/skill_bench/migration/provider_migrator.rb
|
|
175
180
|
- lib/skill_bench/models/config.rb
|
|
176
181
|
- lib/skill_bench/models/criteria_validator.rb
|
|
@@ -240,6 +245,7 @@ licenses:
|
|
|
240
245
|
metadata:
|
|
241
246
|
rubygems_mfa_required: 'true'
|
|
242
247
|
source_code_uri: https://github.com/igmarin/ruby-skill-bench
|
|
248
|
+
post_install_message:
|
|
243
249
|
rdoc_options: []
|
|
244
250
|
require_paths:
|
|
245
251
|
- lib
|
|
@@ -254,7 +260,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
254
260
|
- !ruby/object:Gem::Version
|
|
255
261
|
version: '0'
|
|
256
262
|
requirements: []
|
|
257
|
-
rubygems_version:
|
|
263
|
+
rubygems_version: 3.5.22
|
|
264
|
+
signing_key:
|
|
258
265
|
specification_version: 4
|
|
259
266
|
summary: The evaluation engine for AI Agent Skills benchmarking.
|
|
260
267
|
test_files: []
|