skillware-bench 0.3.2__py3-none-any.whl
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.
- skillware_bench/__init__.py +62 -0
- skillware_bench/cli.py +639 -0
- skillware_bench/core/adapters/__init__.py +50 -0
- skillware_bench/core/adapters/anthropic_adapter.py +166 -0
- skillware_bench/core/adapters/base.py +182 -0
- skillware_bench/core/adapters/factory.py +140 -0
- skillware_bench/core/adapters/gemini_adapter.py +179 -0
- skillware_bench/core/adapters/mistral_adapter.py +136 -0
- skillware_bench/core/adapters/mock_adapter.py +100 -0
- skillware_bench/core/adapters/ollama_adapter.py +137 -0
- skillware_bench/core/adapters/openai_adapter.py +164 -0
- skillware_bench/core/adapters/openrouter_adapter.py +446 -0
- skillware_bench/core/adapters/registry.py +8 -0
- skillware_bench/core/agent_loop.py +383 -0
- skillware_bench/core/evaluators.py +246 -0
- skillware_bench/core/models.py +96 -0
- skillware_bench/core/openrouter_engine.py +357 -0
- skillware_bench/core/perturbations.py +130 -0
- skillware_bench/core/provenance.py +371 -0
- skillware_bench/core/runner.py +147 -0
- skillware_bench/core/statistics.py +123 -0
- skillware_bench/dashboard/__init__.py +8 -0
- skillware_bench/dashboard/generator.py +248 -0
- skillware_bench/fixtures/__init__.py +9 -0
- skillware_bench/fixtures/mock_companies_registry.py +419 -0
- skillware_bench/fixtures/mock_inbox_registry.py +228 -0
- skillware_bench/infra/__init__.py +27 -0
- skillware_bench/infra/companies_house_live.py +228 -0
- skillware_bench/infra/disposable_mailbox.py +210 -0
- skillware_bench/infra/evm_testnet_fork.py +179 -0
- skillware_bench/infra/hermetic_mock_server.py +354 -0
- skillware_bench/infra/wallet_screening_live.py +143 -0
- skillware_bench/suites/__init__.py +33 -0
- skillware_bench/suites/chained_pipeline_crucible.py +177 -0
- skillware_bench/suites/companies_house_crucible.py +187 -0
- skillware_bench/suites/constitution_crucible.py +171 -0
- skillware_bench/suites/context_drift_crucible.py +254 -0
- skillware_bench/suites/distraction_registry.py +249 -0
- skillware_bench/suites/gmail_crucible.py +207 -0
- skillware_bench/suites/prompt_injection_crucible.py +263 -0
- skillware_bench/suites/wallet_screening_crucible.py +167 -0
- skillware_bench/viz/__init__.py +8 -0
- skillware_bench/viz/generator.py +179 -0
- skillware_bench-0.3.2.dist-info/METADATA +287 -0
- skillware_bench-0.3.2.dist-info/RECORD +49 -0
- skillware_bench-0.3.2.dist-info/WHEEL +5 -0
- skillware_bench-0.3.2.dist-info/entry_points.txt +2 -0
- skillware_bench-0.3.2.dist-info/licenses/LICENSE +21 -0
- skillware_bench-0.3.2.dist-info/top_level.txt +1 -0
skillware_bench/cli.py
ADDED
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
# Copyright (c) 2026 ARPA Hellenic Logical Systems
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""Command Line Interface for Skillware-Bench."""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
from dotenv import load_dotenv
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
from skillware_bench import __version__
|
|
15
|
+
from skillware_bench.core.runner import HarnessRunner
|
|
16
|
+
from skillware_bench.suites import ALL_CRUCIBLES
|
|
17
|
+
|
|
18
|
+
# Load local .env credentials automatically
|
|
19
|
+
load_dotenv()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def cmd_list(args, console: Console):
|
|
23
|
+
"""List all available crucibles and evaluation suites."""
|
|
24
|
+
console.print(
|
|
25
|
+
Panel(
|
|
26
|
+
f"[bold cyan]Skillware-Bench[/bold cyan] v{__version__} - Crucible Catalog",
|
|
27
|
+
expand=False,
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
for key, crucible_cls in ALL_CRUCIBLES.items():
|
|
31
|
+
scenario = crucible_cls.get_scenario()
|
|
32
|
+
console.print(f"* [bold green]{key}[/bold green] ([dim]{scenario.id}[/dim])")
|
|
33
|
+
console.print(f" Title: {scenario.title}")
|
|
34
|
+
console.print(f" Tier: {scenario.tier.value} | Category: {scenario.category}")
|
|
35
|
+
console.print(f" Skills: {', '.join(scenario.skills)}")
|
|
36
|
+
console.print(f" Prompt: [dim]{scenario.prompt}[/dim]\n")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def cmd_check_env(args, console: Console):
|
|
40
|
+
"""Inspect environment variables and API keys for live provider testing."""
|
|
41
|
+
table = Table(title="Skillware-Bench Environment & Provider Key Verification")
|
|
42
|
+
table.add_column("Provider / Secret", style="cyan")
|
|
43
|
+
table.add_column("Env Var Name", style="magenta")
|
|
44
|
+
table.add_column("Status", justify="center")
|
|
45
|
+
table.add_column("Details", style="dim")
|
|
46
|
+
|
|
47
|
+
keys_to_check = [
|
|
48
|
+
("OpenRouter API", "OPENROUTER_API", "Primary OpenRouter key for frontier/free models"),
|
|
49
|
+
("OpenRouter Alt", "OPENROUTER_API_KEY", "Alternative OpenRouter key variable"),
|
|
50
|
+
("OpenAI API", "OPENAI_API_KEY", "Required for GPT-4o, o3-mini, o1"),
|
|
51
|
+
("Anthropic API", "ANTHROPIC_API_KEY", "Required for Claude 3.7 Sonnet, Haiku"),
|
|
52
|
+
("Google Gemini API", "GEMINI_API_KEY", "Required for Gemini 2.5 Pro/Flash"),
|
|
53
|
+
("Google GenAI Alt", "GOOGLE_API_KEY", "Alternative key for Gemini SDK"),
|
|
54
|
+
("Mistral AI API", "MISTRAL_API_KEY", "Required for Mistral Large, Codestral"),
|
|
55
|
+
("Ollama Host", "OLLAMA_HOST", "Default: http://localhost:11434"),
|
|
56
|
+
("UK Companies House", "COMPANIES_HOUSE_API_KEY", "Required for live Companies House"),
|
|
57
|
+
("Etherscan API", "ETHERSCAN_API_KEY", "Required for live wallet screening"),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
for label, env_var, note in keys_to_check:
|
|
61
|
+
val = os.environ.get(env_var)
|
|
62
|
+
if val:
|
|
63
|
+
masked = f"{val[:4]}...{val[-3:]}" if len(val) > 8 else "***"
|
|
64
|
+
status = "[green]CONFIGURED[/green]"
|
|
65
|
+
details = f"Value: {masked} ({note})"
|
|
66
|
+
else:
|
|
67
|
+
status = "[yellow]UNSET[/yellow]"
|
|
68
|
+
details = note
|
|
69
|
+
table.add_row(label, env_var, status, details)
|
|
70
|
+
|
|
71
|
+
console.print(table)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def cmd_run(args, console: Console):
|
|
75
|
+
"""Run benchmark crucibles and print evaluation matrix."""
|
|
76
|
+
runner = HarnessRunner(console=console)
|
|
77
|
+
suite_label = args.suite or "all"
|
|
78
|
+
is_mock = not args.live if args.live else args.mock
|
|
79
|
+
|
|
80
|
+
mode_label = "MOCK (deterministic offline)" if is_mock else "LIVE (real API execution)"
|
|
81
|
+
console.print(
|
|
82
|
+
f"[bold]Running Skillware-Bench suite='{suite_label}' model='{args.model}' mode='{mode_label}'...[/bold]\n"
|
|
83
|
+
)
|
|
84
|
+
adapter_arg = getattr(args, "adapter", None)
|
|
85
|
+
adapter_path = adapter_arg if isinstance(adapter_arg, str) and adapter_arg.strip() else None
|
|
86
|
+
|
|
87
|
+
deltas = runner.run_suite(
|
|
88
|
+
suite_name=args.suite,
|
|
89
|
+
model_name=args.model,
|
|
90
|
+
mock=is_mock,
|
|
91
|
+
adapter_path=adapter_path,
|
|
92
|
+
)
|
|
93
|
+
runner.render_summary_table(deltas)
|
|
94
|
+
|
|
95
|
+
if args.export:
|
|
96
|
+
runner.export_reports(deltas, output_dir=args.output_dir)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def cmd_compare(args, console: Console):
|
|
100
|
+
"""Print detailed turn-by-turn comparison for a specific crucible."""
|
|
101
|
+
if args.suite not in ALL_CRUCIBLES:
|
|
102
|
+
console.print(
|
|
103
|
+
f"[red]Error: Unknown suite '{args.suite}'. Use 'skillware-bench list' to see available suites.[/red]"
|
|
104
|
+
)
|
|
105
|
+
sys.exit(1)
|
|
106
|
+
|
|
107
|
+
runner = HarnessRunner(console=console)
|
|
108
|
+
deltas = runner.run_suite(suite_name=args.suite, model_name=args.model, mock=True)
|
|
109
|
+
d = deltas[0]
|
|
110
|
+
|
|
111
|
+
console.print(
|
|
112
|
+
Panel(
|
|
113
|
+
f"[bold cyan]Crucible Deep Dive: {d.scenario_id}[/bold cyan] ({d.model})", expand=False
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
console.print("[bold yellow]1. Generic Baseline (Without Skillware):[/bold yellow]")
|
|
117
|
+
b_score = d.baseline.score.composite_score
|
|
118
|
+
console.print(
|
|
119
|
+
f" Latency: {d.baseline.latency_seconds:.1f}s | Tokens: {d.baseline.total_tokens} | Score: {b_score}/100"
|
|
120
|
+
)
|
|
121
|
+
if d.baseline.score.violations:
|
|
122
|
+
console.print(" Violations:")
|
|
123
|
+
for v in d.baseline.score.violations:
|
|
124
|
+
console.print(f" [red][X] {v}[/red]")
|
|
125
|
+
console.print(f" Sample Output: [dim]{d.baseline.trajectory[-1].raw_response}[/dim]\n")
|
|
126
|
+
|
|
127
|
+
console.print("[bold green]2. Skillware Equipped:[/bold green]")
|
|
128
|
+
s_lat = d.with_skillware.latency_seconds
|
|
129
|
+
s_tok = d.with_skillware.total_tokens
|
|
130
|
+
s_score = d.with_skillware.score.composite_score
|
|
131
|
+
console.print(f" Latency: {s_lat:.1f}s | Tokens: {s_tok} | Score: {s_score}/100")
|
|
132
|
+
executed_actions = [tc.action for step in d.with_skillware.trajectory for tc in step.tool_calls]
|
|
133
|
+
console.print(f" Tool Actions Executed: {executed_actions}")
|
|
134
|
+
console.print(f" Sample Output: [dim]{d.with_skillware.trajectory[-1].raw_response}[/dim]\n")
|
|
135
|
+
|
|
136
|
+
console.print("[bold cyan]3. Empirical Lift (Delta Matrix):[/bold cyan]")
|
|
137
|
+
console.print(f" * Latency Reduction: [yellow]-{d.latency_reduction_pct:.1f}%[/yellow]")
|
|
138
|
+
console.print(f" * Token Bloat Reduction: [cyan]-{d.token_reduction_pct:.1f}%[/cyan]")
|
|
139
|
+
console.print(f" * Composite Score Lift: [green]+{d.composite_score_lift:.1f} pts[/green]")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def cmd_verify(args, console: Console):
|
|
143
|
+
"""Verify cryptographic hash chain and Ed25519 signature of an AURA audit trace or benchmark report."""
|
|
144
|
+
from pathlib import Path
|
|
145
|
+
from skillware_bench.core.provenance import ProvenanceEngine
|
|
146
|
+
|
|
147
|
+
target_file = args.trace
|
|
148
|
+
path = Path(target_file)
|
|
149
|
+
if not path.is_file():
|
|
150
|
+
console.print(f"[bold red]Error: Target file not found: {target_file}[/bold red]")
|
|
151
|
+
sys.exit(1)
|
|
152
|
+
|
|
153
|
+
# Check if target is a JSON benchmark report or a JSONL trace
|
|
154
|
+
if target_file.endswith(".json") and not target_file.endswith(".jsonl"):
|
|
155
|
+
console.print(f"[bold]Verifying AURA benchmark report receipt: {target_file}...[/bold]\n")
|
|
156
|
+
try:
|
|
157
|
+
res = ProvenanceEngine.verify_report(target_file)
|
|
158
|
+
if res["valid"]:
|
|
159
|
+
console.print(
|
|
160
|
+
"[bold green][PASS] Benchmark Execution Report Verification Succeeded[/bold green]"
|
|
161
|
+
)
|
|
162
|
+
console.print(f" * Run ID: [cyan]{res['run_id']}[/cyan]")
|
|
163
|
+
console.print(f" * Model: [bold]{res['model']}[/bold]")
|
|
164
|
+
cond_info = (
|
|
165
|
+
f"{res['total_conditions']} "
|
|
166
|
+
f"(Vanilla: {res['vanilla_pass']} passed, Skillware: {res['skillware_pass']} passed)"
|
|
167
|
+
)
|
|
168
|
+
console.print(f" * Conditions Evaluated: {cond_info}")
|
|
169
|
+
console.print(
|
|
170
|
+
f" * AURA Cryptographic Receipt (SHA-256): [green]{res['receipt_hash']}[/green]"
|
|
171
|
+
)
|
|
172
|
+
console.print(f" * Attestation Engine: {res['engine']}")
|
|
173
|
+
console.print(
|
|
174
|
+
" * Integrity Status: [bold green]AUTHENTIC / UNTAMPERED[/bold green]"
|
|
175
|
+
)
|
|
176
|
+
else:
|
|
177
|
+
console.print("[bold red][FAIL] Benchmark Report Verification Failed[/bold red]")
|
|
178
|
+
console.print(f" * Reason: {res.get('error') or res.get('verdict')}")
|
|
179
|
+
if "expected_hash" in res and "computed_hash" in res:
|
|
180
|
+
console.print(f" * Expected Hash: [green]{res['expected_hash']}[/green]")
|
|
181
|
+
console.print(f" * Computed Hash: [red]{res['computed_hash']}[/red]")
|
|
182
|
+
sys.exit(1)
|
|
183
|
+
except Exception as e:
|
|
184
|
+
console.print(f"[bold red]Error verifying report: {str(e)}[/bold red]")
|
|
185
|
+
sys.exit(1)
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
# Otherwise verify as an AURA .aura.jsonl trace
|
|
189
|
+
console.print(f"[bold]Verifying AURA audit trace: {target_file}...[/bold]\n")
|
|
190
|
+
try:
|
|
191
|
+
res = ProvenanceEngine.verify_trace(target_file)
|
|
192
|
+
if res["valid"]:
|
|
193
|
+
console.print(
|
|
194
|
+
"[bold green][PASS] Cryptographic Trace Verification Succeeded[/bold green]"
|
|
195
|
+
)
|
|
196
|
+
console.print(f" * Events Verified: {res['events_verified']}")
|
|
197
|
+
console.print(f" * Root Hash: [cyan]{res['root_hash']}[/cyan]")
|
|
198
|
+
if res.get("signature_verified") is True:
|
|
199
|
+
console.print(" * Ed25519 Signature: [green]AUTHENTIC[/green]")
|
|
200
|
+
elif res.get("signature_verified") is False:
|
|
201
|
+
console.print(" * Ed25519 Signature: [red]INVALID / TAMPERED[/red]")
|
|
202
|
+
else:
|
|
203
|
+
console.print(
|
|
204
|
+
" * Ed25519 Signature: [yellow]UNSET (No .sig receipt found)[/yellow]"
|
|
205
|
+
)
|
|
206
|
+
console.print(f" * Constitutional Approvals: {res['constitutional_approvals']}")
|
|
207
|
+
console.print(f" * Constitutional Violations: {res['constitutional_violations']}")
|
|
208
|
+
else:
|
|
209
|
+
console.print("[bold red][FAIL] Trace Verification Failed[/bold red]")
|
|
210
|
+
console.print(f" * Reason: Hash chain broken at event '{res.get('broken_event_id')}'")
|
|
211
|
+
sys.exit(1)
|
|
212
|
+
except Exception as e:
|
|
213
|
+
console.print(f"[bold red]Error verifying trace: {str(e)}[/bold red]")
|
|
214
|
+
sys.exit(1)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def cmd_export_compliance(args, console: Console):
|
|
218
|
+
"""Export ISO/IEC 42001 and EU AI Act Article 14 regulatory oversight assessment."""
|
|
219
|
+
from skillware_bench.core.provenance import ProvenanceEngine
|
|
220
|
+
|
|
221
|
+
trace_file = args.trace
|
|
222
|
+
output_file = args.output
|
|
223
|
+
console.print(f"[bold]Generating Regulatory Compliance Package for {trace_file}...[/bold]\n")
|
|
224
|
+
try:
|
|
225
|
+
report = ProvenanceEngine.export_compliance_report(trace_file, output_path=output_file)
|
|
226
|
+
table = Table(title="AI Regulatory Oversight Assessment (EU AI Act & ISO 42001)")
|
|
227
|
+
table.add_column("Regulatory Standard", style="cyan")
|
|
228
|
+
table.add_column("Compliance Status", justify="center")
|
|
229
|
+
table.add_column("Audit Evidence / Proof", style="dim")
|
|
230
|
+
|
|
231
|
+
for std_name, std_data in report["standards"].items():
|
|
232
|
+
status_style = (
|
|
233
|
+
"[green]COMPLIANT[/green]"
|
|
234
|
+
if std_data["status"] == "COMPLIANT"
|
|
235
|
+
else f"[yellow]{std_data['status']}[/yellow]"
|
|
236
|
+
)
|
|
237
|
+
table.add_row(std_name.replace("_", " "), status_style, std_data["evidence"])
|
|
238
|
+
|
|
239
|
+
console.print(table)
|
|
240
|
+
except Exception as e:
|
|
241
|
+
console.print(f"[bold red]Error generating compliance export: {str(e)}[/bold red]")
|
|
242
|
+
sys.exit(1)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def cmd_dashboard(args, console: Console):
|
|
246
|
+
"""Generate self-contained interactive HTML dashboard from benchmark runs."""
|
|
247
|
+
from pathlib import Path
|
|
248
|
+
import webbrowser
|
|
249
|
+
from skillware_bench.dashboard import DashboardGenerator
|
|
250
|
+
|
|
251
|
+
runner = HarnessRunner(console=console)
|
|
252
|
+
is_mock = not args.live
|
|
253
|
+
console.print("[bold]Generating interactive Skillware-Bench dashboard...[/bold]\n")
|
|
254
|
+
deltas = runner.run_suite(suite_name=args.suite, model_name=args.model, mock=is_mock)
|
|
255
|
+
output_html = args.output or "./runs/dashboard.html"
|
|
256
|
+
DashboardGenerator.generate_html(deltas, output_path=output_html)
|
|
257
|
+
console.print(f"[bold green]Dashboard generated successfully:[/bold green] {output_html}")
|
|
258
|
+
if getattr(args, "open_browser", False):
|
|
259
|
+
webbrowser.open(str(Path(output_html).resolve().as_uri()))
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def cmd_perturb(args, console: Console):
|
|
263
|
+
"""Generate dynamic anti-contamination scenario variations."""
|
|
264
|
+
from skillware_bench.core.perturbations import PerturbationEngine
|
|
265
|
+
|
|
266
|
+
engine = PerturbationEngine(seed=args.seed)
|
|
267
|
+
console.print(
|
|
268
|
+
Panel(
|
|
269
|
+
"[bold cyan]Skillware-Bench Anti-Contamination Perturbation Engine[/bold cyan]",
|
|
270
|
+
expand=False,
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
suites = [args.suite] if args.suite else list(ALL_CRUCIBLES.keys())
|
|
275
|
+
variants_table = Table(title="Generated Anti-Contamination Scenario Variants")
|
|
276
|
+
variants_table.add_column("Crucible", style="cyan")
|
|
277
|
+
variants_table.add_column("Variant ID", style="magenta")
|
|
278
|
+
variants_table.add_column("Perturbed Prompt", style="dim")
|
|
279
|
+
|
|
280
|
+
count = getattr(args, "count", 3)
|
|
281
|
+
for s_name in suites:
|
|
282
|
+
scenario = ALL_CRUCIBLES[s_name].get_scenario()
|
|
283
|
+
for _ in range(count):
|
|
284
|
+
perturbed = engine.perturb_scenario(scenario)
|
|
285
|
+
preview = perturbed.prompt.replace("\n", " ")[:90] + "..."
|
|
286
|
+
variants_table.add_row(s_name, perturbed.id, preview)
|
|
287
|
+
|
|
288
|
+
console.print(variants_table)
|
|
289
|
+
total_variants = len(suites) * count
|
|
290
|
+
console.print(
|
|
291
|
+
f"[dim]Generated {total_variants} variant(s) across {len(suites)} crucible(s). "
|
|
292
|
+
"Invariants strictly preserved.[/dim]\n"
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def cmd_new_model(args, console: Console):
|
|
297
|
+
"""Scaffold a custom model adapter template for Bring-Your-Own-Model (BYOM) evaluation."""
|
|
298
|
+
raw_name = args.name.lower().replace("-", "_").replace(" ", "_")
|
|
299
|
+
output_path = args.output or f"./custom_adapters/{raw_name}_adapter.py"
|
|
300
|
+
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
|
|
301
|
+
class_name = "".join(part.title() for part in raw_name.split("_")) + "Adapter"
|
|
302
|
+
|
|
303
|
+
template = f'''# Copyright (c) 2026 ARPA Hellenic Logical Systems
|
|
304
|
+
# SPDX-License-Identifier: MIT
|
|
305
|
+
|
|
306
|
+
"""Custom Bring-Your-Own-Model adapter for {args.name}."""
|
|
307
|
+
|
|
308
|
+
from typing import Any, Dict, List, Optional
|
|
309
|
+
from skillware_bench.core.adapters.base import (
|
|
310
|
+
BaseModelAdapter,
|
|
311
|
+
ModelResponse,
|
|
312
|
+
TokenUsage,
|
|
313
|
+
)
|
|
314
|
+
from skillware_bench.core.adapters.registry import AdapterRegistry
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class {class_name}(BaseModelAdapter):
|
|
318
|
+
"""Adapter for {args.name}."""
|
|
319
|
+
|
|
320
|
+
def __init__(self, model_name: str = "{args.name}", **kwargs):
|
|
321
|
+
super().__init__(model_name=model_name, **kwargs)
|
|
322
|
+
|
|
323
|
+
def chat(
|
|
324
|
+
self,
|
|
325
|
+
messages: List[Dict[str, Any]],
|
|
326
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
327
|
+
system_prompt: Optional[str] = None,
|
|
328
|
+
**kwargs,
|
|
329
|
+
) -> ModelResponse:
|
|
330
|
+
"""Execute chat completion with tool calling."""
|
|
331
|
+
# TODO: Implement inference call to your custom LLM or API endpoint
|
|
332
|
+
return ModelResponse(
|
|
333
|
+
content="Processed under constitutional governance.",
|
|
334
|
+
tool_calls=[],
|
|
335
|
+
usage=TokenUsage(prompt_tokens=100, completion_tokens=50),
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
# Automatic BYOM Registration
|
|
340
|
+
AdapterRegistry.register("{args.name}", {class_name})
|
|
341
|
+
'''
|
|
342
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
343
|
+
f.write(template)
|
|
344
|
+
|
|
345
|
+
console.print(
|
|
346
|
+
Panel(
|
|
347
|
+
f"[bold green]Custom Model Adapter Scaffolded Successfully[/bold green]\n\n"
|
|
348
|
+
f"File: [cyan]{output_path}[/cyan]\n\n"
|
|
349
|
+
f"Run benchmark with:\n"
|
|
350
|
+
f"[bold]skillware-bench run --adapter {output_path} --suite gmail[/bold]",
|
|
351
|
+
expand=False,
|
|
352
|
+
)
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def cmd_audit(args, console: Console):
|
|
357
|
+
"""Inspect and audit AURA causal provenance traces in detail."""
|
|
358
|
+
from skillware_bench.core.provenance import ProvenanceEngine
|
|
359
|
+
|
|
360
|
+
trace_file = args.trace
|
|
361
|
+
console.print(f"[bold cyan]Auditing AURA Causal Provenance Trace: {trace_file}[/bold cyan]\n")
|
|
362
|
+
try:
|
|
363
|
+
res = ProvenanceEngine.verify_trace(trace_file)
|
|
364
|
+
if not res["valid"]:
|
|
365
|
+
console.print(
|
|
366
|
+
f"[bold red][FAIL] Trace integrity invalid. Broken at event '{res.get('broken_event_id')}'.[/bold red]"
|
|
367
|
+
)
|
|
368
|
+
sys.exit(1)
|
|
369
|
+
|
|
370
|
+
table = Table(title="Trace Audit Summary")
|
|
371
|
+
table.add_column("Property", style="cyan")
|
|
372
|
+
table.add_column("Value", style="green bold")
|
|
373
|
+
|
|
374
|
+
table.add_row("Events Verified", str(res["events_verified"]))
|
|
375
|
+
table.add_row("Root Hash", res["root_hash"][:16] + "...")
|
|
376
|
+
table.add_row("Constitutional Approvals", str(res["constitutional_approvals"]))
|
|
377
|
+
table.add_row("Constitutional Violations", str(res["constitutional_violations"]))
|
|
378
|
+
sig_val = "AUTHENTIC" if res.get("signature_verified") else "UNSET / NOT SIGNED"
|
|
379
|
+
table.add_row("Ed25519 Signature", sig_val)
|
|
380
|
+
|
|
381
|
+
console.print(table)
|
|
382
|
+
except Exception as e:
|
|
383
|
+
console.print(f"[bold red]Error auditing trace: {str(e)}[/bold red]")
|
|
384
|
+
sys.exit(1)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def cmd_menu(args, console: Console):
|
|
388
|
+
"""Launch interactive pastel dispatch menu."""
|
|
389
|
+
banner_lines = [
|
|
390
|
+
r" ____ _ _ _ _ ____ _",
|
|
391
|
+
r" / ___|| | __(_) | |_ ____ _ _ __ ___ | __ ) ___ _ __ ___| |__",
|
|
392
|
+
r" \___ \| |/ /| | | \ \ /\ / / _` | '__/ _ \_____| _ \ / _ \ '_ \ / __| '_ \\",
|
|
393
|
+
r" ___) | < | | | |\ V V / (_| | | | __/_____| |_) | __/ | | | (__| | | |",
|
|
394
|
+
r" |____/|_|\_\|_|_|_| \_/\_/ \__,_|_| \___| |____/ \___|_| |_|\___|_| |_|",
|
|
395
|
+
]
|
|
396
|
+
banner = "\n" + "\n".join(banner_lines)
|
|
397
|
+
console.print(f"[bold #bae6fd]{banner}[/bold #bae6fd]")
|
|
398
|
+
console.print(
|
|
399
|
+
"[bold #efcefa]Skillware-Bench: The Governed Agent Evaluation Platform[/bold #efcefa]"
|
|
400
|
+
)
|
|
401
|
+
console.print(f"[dim]Version {__version__} | ARPA Hellenic Logical Systems[/dim]\n")
|
|
402
|
+
|
|
403
|
+
menu_table = Table(show_header=False, box=None)
|
|
404
|
+
menu_table.add_column("Key", style="bold #bbf7d0", width=5)
|
|
405
|
+
menu_table.add_column("Command", style="bold cyan", width=16)
|
|
406
|
+
menu_table.add_column("Description", style="dim")
|
|
407
|
+
|
|
408
|
+
menu_table.add_row("[1]", "run", "Execute benchmark suite across frontier/sovereign models")
|
|
409
|
+
menu_table.add_row("[2]", "live", "Launch real-world zero-mock simulation crucibles")
|
|
410
|
+
menu_table.add_row("[3]", "compare", "Turn-by-turn with-vs-without Skillware A/B inspection")
|
|
411
|
+
menu_table.add_row("[4]", "dashboard", "Launch interactive browser dashboard")
|
|
412
|
+
menu_table.add_row("[5]", "audit", "Inspect AURA causal provenance traces and receipts")
|
|
413
|
+
menu_table.add_row("[6]", "perturb", "Generate dynamic anti-contamination task variants")
|
|
414
|
+
menu_table.add_row("[7]", "new-model", "Scaffold custom model adapter plugin")
|
|
415
|
+
menu_table.add_row("[8]", "check-env", "Inspect provider API keys and secrets")
|
|
416
|
+
menu_table.add_row("[9]", "openrouter", "Benchmark models via OpenRouter API")
|
|
417
|
+
menu_table.add_row("[0]", "exit", "Exit Skillware-Bench launcher")
|
|
418
|
+
|
|
419
|
+
console.print(menu_table)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def cmd_openrouter(args, console: Console):
|
|
423
|
+
"""Run OpenRouter benchmark sweep across frontier, sovereign, or free models."""
|
|
424
|
+
from skillware_bench.core.openrouter_engine import OpenRouterBenchmarkEngine
|
|
425
|
+
|
|
426
|
+
models = args.models or ["nvidia/nemotron-3.5-super-vl:free"]
|
|
427
|
+
crucibles = [args.suite] if args.suite else args.crucibles
|
|
428
|
+
engine = OpenRouterBenchmarkEngine(console=console)
|
|
429
|
+
_ = engine.run_sweep(
|
|
430
|
+
models=models,
|
|
431
|
+
crucible_filter=crucibles,
|
|
432
|
+
sweep_name=args.sweep_name or "openrouter_cli_sweep",
|
|
433
|
+
output_dir=args.output_dir or "./runs/openrouter_sweeps",
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def main():
|
|
438
|
+
parser = argparse.ArgumentParser(
|
|
439
|
+
prog="skillware-bench",
|
|
440
|
+
description="The transparent, empirical benchmark suite and evaluation harness for Skillware.",
|
|
441
|
+
)
|
|
442
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
443
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
444
|
+
|
|
445
|
+
# list
|
|
446
|
+
p_list = subparsers.add_parser("list", help="List available simulation crucibles and tiers")
|
|
447
|
+
p_list.set_defaults(func=cmd_list)
|
|
448
|
+
|
|
449
|
+
# check-env
|
|
450
|
+
p_check = subparsers.add_parser(
|
|
451
|
+
"check-env", help="Check configured environment variables and provider keys"
|
|
452
|
+
)
|
|
453
|
+
p_check.set_defaults(func=cmd_check_env)
|
|
454
|
+
|
|
455
|
+
# run
|
|
456
|
+
p_run = subparsers.add_parser("run", help="Run benchmark suite")
|
|
457
|
+
p_run.add_argument(
|
|
458
|
+
"--suite",
|
|
459
|
+
choices=list(ALL_CRUCIBLES.keys()),
|
|
460
|
+
default=None,
|
|
461
|
+
help="Specific crucible suite to run",
|
|
462
|
+
)
|
|
463
|
+
p_run.add_argument("--model", default="golden-target", help="Model name identifier")
|
|
464
|
+
p_run.add_argument(
|
|
465
|
+
"--adapter",
|
|
466
|
+
default=None,
|
|
467
|
+
help="Path to custom model adapter Python file for BYOM evaluation",
|
|
468
|
+
)
|
|
469
|
+
p_run.add_argument(
|
|
470
|
+
"--mock", action="store_true", default=True, help="Run offline mock crucibles"
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
p_run.add_argument(
|
|
474
|
+
"--live",
|
|
475
|
+
action="store_true",
|
|
476
|
+
default=False,
|
|
477
|
+
help="Run live model execution against crucibles",
|
|
478
|
+
)
|
|
479
|
+
p_run.add_argument(
|
|
480
|
+
"--export", action="store_true", default=False, help="Export benchmark reports to JSON"
|
|
481
|
+
)
|
|
482
|
+
p_run.add_argument(
|
|
483
|
+
"--output-dir", default="./runs/reports", help="Output directory for reports"
|
|
484
|
+
)
|
|
485
|
+
p_run.add_argument(
|
|
486
|
+
"--figures",
|
|
487
|
+
action="store_true",
|
|
488
|
+
default=False,
|
|
489
|
+
help="Generate standalone SVG figures (radar and empirical lift delta)",
|
|
490
|
+
)
|
|
491
|
+
p_run.add_argument(
|
|
492
|
+
"--figures-dir",
|
|
493
|
+
default="./runs/figures",
|
|
494
|
+
help="Output directory for generated figures",
|
|
495
|
+
)
|
|
496
|
+
p_run.set_defaults(func=cmd_run)
|
|
497
|
+
|
|
498
|
+
# compare
|
|
499
|
+
p_cmp = subparsers.add_parser(
|
|
500
|
+
"compare", help="Compare with-skillware vs without-skillware in depth"
|
|
501
|
+
)
|
|
502
|
+
p_cmp.add_argument(
|
|
503
|
+
"--suite", required=True, choices=list(ALL_CRUCIBLES.keys()), help="Crucible to inspect"
|
|
504
|
+
)
|
|
505
|
+
p_cmp.add_argument("--model", default="golden-target", help="Model name identifier")
|
|
506
|
+
p_cmp.set_defaults(func=cmd_compare)
|
|
507
|
+
|
|
508
|
+
# verify
|
|
509
|
+
p_verify = subparsers.add_parser(
|
|
510
|
+
"verify", help="Cryptographically verify AURA causal audit traces and signatures"
|
|
511
|
+
)
|
|
512
|
+
p_verify.add_argument(
|
|
513
|
+
"trace", help="Path to .aura.jsonl audit trace or benchmark report .json file"
|
|
514
|
+
)
|
|
515
|
+
p_verify.set_defaults(func=cmd_verify)
|
|
516
|
+
|
|
517
|
+
# export-compliance
|
|
518
|
+
p_comp = subparsers.add_parser(
|
|
519
|
+
"export-compliance",
|
|
520
|
+
help="Generate ISO 42001 and EU AI Act Article 14 compliance report",
|
|
521
|
+
)
|
|
522
|
+
p_comp.add_argument("trace", help="Path to .aura.jsonl audit trace file")
|
|
523
|
+
p_comp.add_argument("--output", default=None, help="Optional output JSON filepath for report")
|
|
524
|
+
p_comp.set_defaults(func=cmd_export_compliance)
|
|
525
|
+
|
|
526
|
+
# dashboard
|
|
527
|
+
p_dash = subparsers.add_parser(
|
|
528
|
+
"dashboard", help="Generate self-contained interactive HTML dashboard"
|
|
529
|
+
)
|
|
530
|
+
p_dash.add_argument(
|
|
531
|
+
"--suite",
|
|
532
|
+
choices=list(ALL_CRUCIBLES.keys()),
|
|
533
|
+
default=None,
|
|
534
|
+
help="Specific crucible suite to benchmark",
|
|
535
|
+
)
|
|
536
|
+
p_dash.add_argument("--model", default="golden-target", help="Model name identifier")
|
|
537
|
+
p_dash.add_argument(
|
|
538
|
+
"--live",
|
|
539
|
+
action="store_true",
|
|
540
|
+
default=False,
|
|
541
|
+
help="Run live model execution",
|
|
542
|
+
)
|
|
543
|
+
p_dash.add_argument(
|
|
544
|
+
"--output",
|
|
545
|
+
default="./runs/dashboard.html",
|
|
546
|
+
help="Output HTML filepath for dashboard",
|
|
547
|
+
)
|
|
548
|
+
p_dash.add_argument(
|
|
549
|
+
"--open-browser",
|
|
550
|
+
action="store_true",
|
|
551
|
+
default=False,
|
|
552
|
+
help="Open generated dashboard in default browser",
|
|
553
|
+
)
|
|
554
|
+
p_dash.set_defaults(func=cmd_dashboard)
|
|
555
|
+
|
|
556
|
+
# perturb
|
|
557
|
+
p_perturb = subparsers.add_parser(
|
|
558
|
+
"perturb", help="Generate dynamic anti-contamination scenario variations"
|
|
559
|
+
)
|
|
560
|
+
p_perturb.add_argument(
|
|
561
|
+
"--suite",
|
|
562
|
+
choices=list(ALL_CRUCIBLES.keys()),
|
|
563
|
+
default=None,
|
|
564
|
+
help="Specific crucible suite to perturb",
|
|
565
|
+
)
|
|
566
|
+
p_perturb.add_argument(
|
|
567
|
+
"--count", type=int, default=3, help="Number of perturbation variants per scenario"
|
|
568
|
+
)
|
|
569
|
+
p_perturb.add_argument("--seed", type=int, default=None, help="Random seed for reproducibility")
|
|
570
|
+
p_perturb.set_defaults(func=cmd_perturb)
|
|
571
|
+
|
|
572
|
+
# new-model
|
|
573
|
+
p_new = subparsers.add_parser(
|
|
574
|
+
"new-model", help="Scaffold custom model adapter template for Bring-Your-Own-Model (BYOM)"
|
|
575
|
+
)
|
|
576
|
+
p_new.add_argument("name", help="Identifier name for custom model adapter (e.g. llama-custom)")
|
|
577
|
+
p_new.add_argument(
|
|
578
|
+
"--output", default=None, help="Output Python filepath for scaffolded adapter"
|
|
579
|
+
)
|
|
580
|
+
p_new.set_defaults(func=cmd_new_model)
|
|
581
|
+
|
|
582
|
+
# audit
|
|
583
|
+
p_audit = subparsers.add_parser("audit", help="Inspect and audit AURA causal provenance traces")
|
|
584
|
+
p_audit.add_argument("trace", help="Path to .aura.jsonl audit trace file")
|
|
585
|
+
p_audit.set_defaults(func=cmd_audit)
|
|
586
|
+
|
|
587
|
+
# openrouter
|
|
588
|
+
p_or = subparsers.add_parser(
|
|
589
|
+
"openrouter", help="Benchmark frontier and sovereign models via OpenRouter API"
|
|
590
|
+
)
|
|
591
|
+
p_or.add_argument(
|
|
592
|
+
"--models",
|
|
593
|
+
nargs="+",
|
|
594
|
+
default=["nvidia/nemotron-3.5-super-vl:free"],
|
|
595
|
+
help="One or more model slugs to evaluate on OpenRouter",
|
|
596
|
+
)
|
|
597
|
+
p_or.add_argument(
|
|
598
|
+
"--suite",
|
|
599
|
+
choices=list(ALL_CRUCIBLES.keys()),
|
|
600
|
+
default=None,
|
|
601
|
+
help="Specific crucible suite to run",
|
|
602
|
+
)
|
|
603
|
+
p_or.add_argument(
|
|
604
|
+
"--crucibles",
|
|
605
|
+
nargs="+",
|
|
606
|
+
default=None,
|
|
607
|
+
help="List of crucibles to run (default: all 7)",
|
|
608
|
+
)
|
|
609
|
+
p_or.add_argument(
|
|
610
|
+
"--sweep-name",
|
|
611
|
+
default="openrouter_cli_sweep",
|
|
612
|
+
help="Custom label for the sweep audit report",
|
|
613
|
+
)
|
|
614
|
+
p_or.add_argument(
|
|
615
|
+
"--output-dir",
|
|
616
|
+
default="./runs/openrouter_sweeps",
|
|
617
|
+
help="Output directory for reports",
|
|
618
|
+
)
|
|
619
|
+
p_or.set_defaults(func=cmd_openrouter)
|
|
620
|
+
|
|
621
|
+
# menu
|
|
622
|
+
p_menu = subparsers.add_parser("menu", help="Launch interactive pastel dispatch launcher")
|
|
623
|
+
p_menu.set_defaults(func=cmd_menu)
|
|
624
|
+
|
|
625
|
+
args = parser.parse_args()
|
|
626
|
+
console = Console()
|
|
627
|
+
|
|
628
|
+
if not args.command:
|
|
629
|
+
if sys.stdin.isatty():
|
|
630
|
+
cmd_menu(args, console)
|
|
631
|
+
else:
|
|
632
|
+
parser.print_help()
|
|
633
|
+
sys.exit(0)
|
|
634
|
+
|
|
635
|
+
args.func(args, console)
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
if __name__ == "__main__":
|
|
639
|
+
main()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Copyright (c) 2026 ARPA Hellenic Logical Systems
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""Model adapters for frontier, sovereign, and mock evaluation targets."""
|
|
5
|
+
|
|
6
|
+
from skillware_bench.core.adapters.anthropic_adapter import AnthropicAdapter
|
|
7
|
+
from skillware_bench.core.adapters.base import (
|
|
8
|
+
BaseModelAdapter,
|
|
9
|
+
ModelResponse,
|
|
10
|
+
TokenUsage,
|
|
11
|
+
ToolCallRequest,
|
|
12
|
+
calculate_cost,
|
|
13
|
+
execute_with_retry,
|
|
14
|
+
)
|
|
15
|
+
from skillware_bench.core.adapters.factory import (
|
|
16
|
+
AdapterRegistry,
|
|
17
|
+
get_adapter,
|
|
18
|
+
load_adapter_from_file,
|
|
19
|
+
)
|
|
20
|
+
from skillware_bench.core.adapters.gemini_adapter import GeminiAdapter
|
|
21
|
+
from skillware_bench.core.adapters.mistral_adapter import MistralAdapter
|
|
22
|
+
from skillware_bench.core.adapters.mock_adapter import (
|
|
23
|
+
MockModelAdapter,
|
|
24
|
+
SimulatedRateLimitError,
|
|
25
|
+
SimulatedServerError,
|
|
26
|
+
)
|
|
27
|
+
from skillware_bench.core.adapters.ollama_adapter import OllamaAdapter
|
|
28
|
+
from skillware_bench.core.adapters.openai_adapter import OpenAIAdapter
|
|
29
|
+
from skillware_bench.core.adapters.openrouter_adapter import OpenRouterAdapter
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"BaseModelAdapter",
|
|
33
|
+
"ModelResponse",
|
|
34
|
+
"TokenUsage",
|
|
35
|
+
"ToolCallRequest",
|
|
36
|
+
"calculate_cost",
|
|
37
|
+
"execute_with_retry",
|
|
38
|
+
"AdapterRegistry",
|
|
39
|
+
"get_adapter",
|
|
40
|
+
"load_adapter_from_file",
|
|
41
|
+
"OpenAIAdapter",
|
|
42
|
+
"OpenRouterAdapter",
|
|
43
|
+
"AnthropicAdapter",
|
|
44
|
+
"GeminiAdapter",
|
|
45
|
+
"MistralAdapter",
|
|
46
|
+
"OllamaAdapter",
|
|
47
|
+
"MockModelAdapter",
|
|
48
|
+
"SimulatedRateLimitError",
|
|
49
|
+
"SimulatedServerError",
|
|
50
|
+
]
|