evalrx 0.1.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.
- evalrx/__init__.py +139 -0
- evalrx/agent_assets/__init__.py +2 -0
- evalrx/agent_assets/skills/README.md +28 -0
- evalrx/agent_assets/skills/eval-chart-style/SKILL.md +172 -0
- evalrx/agent_assets/skills/evalrx-report-ui/SKILL.md +116 -0
- evalrx/agent_assets/skills/nature-figure/LICENSE +201 -0
- evalrx/agent_assets/skills/nature-figure/README.md +412 -0
- evalrx/agent_assets/skills/nature-figure/SKILL.md +60 -0
- evalrx/agent_assets/skills/nature-figure/manifest.yaml +59 -0
- evalrx/agent_assets/skills/nature-figure/references/api.md +436 -0
- evalrx/agent_assets/skills/nature-figure/references/backend-selection.md +100 -0
- evalrx/agent_assets/skills/nature-figure/references/chart-types.md +281 -0
- evalrx/agent_assets/skills/nature-figure/references/common-patterns.md +350 -0
- evalrx/agent_assets/skills/nature-figure/references/demos.md +65 -0
- evalrx/agent_assets/skills/nature-figure/references/design-theory.md +439 -0
- evalrx/agent_assets/skills/nature-figure/references/figure-contract.md +93 -0
- evalrx/agent_assets/skills/nature-figure/references/figure-legend-conventions.md +71 -0
- evalrx/agent_assets/skills/nature-figure/references/nature-2026-observations.md +112 -0
- evalrx/agent_assets/skills/nature-figure/references/qa-contract.md +119 -0
- evalrx/agent_assets/skills/nature-figure/references/r-template-index.md +66 -0
- evalrx/agent_assets/skills/nature-figure/references/r-workflow.md +161 -0
- evalrx/agent_assets/skills/nature-figure/references/tutorials.md +251 -0
- evalrx/agent_assets/skills/nature-figure/static/core/contract.md +29 -0
- evalrx/agent_assets/skills/nature-figure/static/core/stance.md +37 -0
- evalrx/agent_assets/skills/nature-figure/static/fragments/backend/python.md +37 -0
- evalrx/agent_assets/skills/nature-figure/static/fragments/backend/r.md +44 -0
- evalrx/agent_assets/skills/outcome-driver-analysis/SKILL.md +213 -0
- evalrx/agent_assets/skills/outcome-driver-analysis/assets/analysis_report_template.md +53 -0
- evalrx/agent_assets/skills/outcome-driver-analysis/references/model_selection.md +72 -0
- evalrx/agent_assets/skills/outcome-driver-analysis/scripts/explanatory_var_eda.R +130 -0
- evalrx/agent_assets/skills/outcome-driver-analysis/scripts/explanatory_var_eda.py +150 -0
- evalrx/agent_assets/skills/outcome-driver-analysis/scripts/fit_outcome_model.R +181 -0
- evalrx/agent_assets/skills/outcome-driver-analysis/scripts/fit_outcome_model.py +186 -0
- evalrx/agent_assets/skills/outcome-driver-analysis/scripts/univariate_eda.R +149 -0
- evalrx/agent_assets/skills/outcome-driver-analysis/scripts/univariate_eda.py +177 -0
- evalrx/agent_assets/skills.py +27 -0
- evalrx/agent_runtime/__init__.py +78 -0
- evalrx/agent_runtime/_docker_runner.py +89 -0
- evalrx/agent_runtime/cli_runtime.py +103 -0
- evalrx/agent_runtime/cli_transcript.py +138 -0
- evalrx/agent_runtime/cli_types.py +68 -0
- evalrx/agent_runtime/codegen/__init__.py +5 -0
- evalrx/agent_runtime/codegen/runner.py +94 -0
- evalrx/agent_runtime/experiment_harness.py +117 -0
- evalrx/agent_runtime/factory.py +102 -0
- evalrx/agent_runtime/json_shape.py +44 -0
- evalrx/agent_runtime/judges/__init__.py +28 -0
- evalrx/agent_runtime/judges/agy.py +179 -0
- evalrx/agent_runtime/judges/autodetect.py +135 -0
- evalrx/agent_runtime/judges/claude.py +159 -0
- evalrx/agent_runtime/judges/codex.py +120 -0
- evalrx/agent_runtime/providers/__init__.py +21 -0
- evalrx/agent_runtime/providers/antigravity.py +31 -0
- evalrx/agent_runtime/providers/base.py +145 -0
- evalrx/agent_runtime/providers/claude_code.py +49 -0
- evalrx/agent_runtime/providers/codex.py +37 -0
- evalrx/agent_runtime/providers/gemini_cli.py +26 -0
- evalrx/agent_runtime/providers/kimi_cli.py +27 -0
- evalrx/agent_runtime/providers/opencode.py +27 -0
- evalrx/agent_runtime/providers/registry.py +58 -0
- evalrx/agent_runtime/sandbox.py +517 -0
- evalrx/agent_runtime/skill_audit.py +143 -0
- evalrx/agent_runtime/skills/__init__.py +19 -0
- evalrx/agent_runtime/skills/installer.py +68 -0
- evalrx/agent_runtime/skills/prompt_policy.py +86 -0
- evalrx/agent_runtime/skills/resolver.py +19 -0
- evalrx/analysis/__init__.py +132 -0
- evalrx/analysis/adjudicate.py +154 -0
- evalrx/analysis/analysis_module.py +361 -0
- evalrx/analysis/api.py +171 -0
- evalrx/analysis/case_studio.py +651 -0
- evalrx/analysis/cli.py +114 -0
- evalrx/analysis/dashboard.py +350 -0
- evalrx/analysis/eval_case_matrix.py +118 -0
- evalrx/analysis/eval_viz_theme.py +833 -0
- evalrx/analysis/explore_run.py +333 -0
- evalrx/analysis/explorer.py +1276 -0
- evalrx/analysis/failure_modes.py +607 -0
- evalrx/analysis/fused_pipeline.py +489 -0
- evalrx/analysis/holdout.py +300 -0
- evalrx/analysis/hypothesis_agent.py +230 -0
- evalrx/analysis/narration.py +177 -0
- evalrx/analysis/operationalize.py +442 -0
- evalrx/analysis/plain_language.py +42 -0
- evalrx/analysis/planner.py +283 -0
- evalrx/analysis/probe_search.py +203 -0
- evalrx/analysis/profile.py +268 -0
- evalrx/analysis/prompts/__init__.py +0 -0
- evalrx/analysis/prompts/explorer.py +417 -0
- evalrx/analysis/prompts/failure_modes.py +33 -0
- evalrx/analysis/prompts/holdout.py +27 -0
- evalrx/analysis/prompts/hypothesis_agent.py +78 -0
- evalrx/analysis/prompts/run_codebase.py +47 -0
- evalrx/analysis/prompts/stats_agent.py +72 -0
- evalrx/analysis/prompts/stats_tool_generator.py +43 -0
- evalrx/analysis/result_marker.py +47 -0
- evalrx/analysis/run_codebase.py +242 -0
- evalrx/analysis/run_view.py +205 -0
- evalrx/analysis/stage_views.py +93 -0
- evalrx/analysis/stats_agent.py +944 -0
- evalrx/analysis/stats_tool_agent.py +261 -0
- evalrx/analysis/stats_tool_generator.py +415 -0
- evalrx/analysis/stats_tools.py +1153 -0
- evalrx/analysis/trajectory_records.py +193 -0
- evalrx/analysis/workbench.py +431 -0
- evalrx/analyzers/__init__.py +42 -0
- evalrx/analyzers/agent/__init__.py +25 -0
- evalrx/analyzers/agent/counterfactual.py +84 -0
- evalrx/analyzers/agent/first_error_judge.py +96 -0
- evalrx/analyzers/agent/ignored_obs.py +81 -0
- evalrx/analyzers/agent/loop_detect.py +79 -0
- evalrx/analyzers/agent/reliability.py +165 -0
- evalrx/analyzers/agent/tool_shap.py +225 -0
- evalrx/analyzers/agent/trajectory_rubric.py +168 -0
- evalrx/analyzers/attention/__init__.py +19 -0
- evalrx/analyzers/attention/relative_attn.py +610 -0
- evalrx/analyzers/attention/rollout.py +73 -0
- evalrx/analyzers/attention/sink.py +56 -0
- evalrx/analyzers/attention/summary.py +190 -0
- evalrx/analyzers/attribution/__init__.py +6 -0
- evalrx/analyzers/attribution/generic_attn.py +31 -0
- evalrx/analyzers/attribution/gradcam.py +30 -0
- evalrx/analyzers/base.py +12 -0
- evalrx/analyzers/geometry/__init__.py +6 -0
- evalrx/analyzers/geometry/cka.py +70 -0
- evalrx/analyzers/geometry/linear_probe.py +157 -0
- evalrx/analyzers/hallucination/__init__.py +9 -0
- evalrx/analyzers/hallucination/chair.py +78 -0
- evalrx/analyzers/hallucination/opera.py +29 -0
- evalrx/analyzers/hallucination/pope.py +119 -0
- evalrx/analyzers/hallucination/selfcheck.py +155 -0
- evalrx/analyzers/hallucination/vcd.py +29 -0
- evalrx/analyzers/lens/__init__.py +7 -0
- evalrx/analyzers/lens/layer_contrast.py +133 -0
- evalrx/analyzers/lens/logit_lens.py +138 -0
- evalrx/analyzers/lens/tuned_lens.py +30 -0
- evalrx/analyzers/patching/__init__.py +5 -0
- evalrx/analyzers/patching/causal_trace.py +30 -0
- evalrx/analyzers/perturbation/__init__.py +23 -0
- evalrx/analyzers/perturbation/_shapley.py +54 -0
- evalrx/analyzers/perturbation/context_shap.py +174 -0
- evalrx/analyzers/perturbation/cot_faithfulness.py +239 -0
- evalrx/analyzers/perturbation/format_sensitivity.py +237 -0
- evalrx/analyzers/perturbation/mm_shap.py +146 -0
- evalrx/analyzers/perturbation/modality_ablation.py +196 -0
- evalrx/analyzers/perturbation/perturbation_battery.py +274 -0
- evalrx/analyzers/perturbation/prompt_contrast.py +265 -0
- evalrx/analyzers/perturbation/rise.py +94 -0
- evalrx/analyzers/perturbation/vl_shap.py +102 -0
- evalrx/analyzers/reasoning/__init__.py +33 -0
- evalrx/analyzers/reasoning/_text.py +328 -0
- evalrx/analyzers/reasoning/answer_extraction_audit.py +327 -0
- evalrx/analyzers/reasoning/arith_audit.py +226 -0
- evalrx/analyzers/reasoning/contamination.py +214 -0
- evalrx/analyzers/reasoning/knowledge_split.py +253 -0
- evalrx/analyzers/reasoning/self_repair.py +246 -0
- evalrx/analyzers/reasoning/step_rollout_value.py +216 -0
- evalrx/analyzers/reasoning/termination_audit.py +258 -0
- evalrx/analyzers/uncertainty/__init__.py +18 -0
- evalrx/analyzers/uncertainty/calibration.py +174 -0
- evalrx/analyzers/uncertainty/coverage_gap.py +199 -0
- evalrx/analyzers/uncertainty/entropy.py +90 -0
- evalrx/analyzers/uncertainty/logprob_entropy.py +69 -0
- evalrx/analyzers/uncertainty/self_consistency.py +204 -0
- evalrx/analyzers/uncertainty/verbalized_conf.py +64 -0
- evalrx/cli.py +411 -0
- evalrx/config.py +77 -0
- evalrx/contract/__init__.py +179 -0
- evalrx/contract/common.py +452 -0
- evalrx/contract/emit.py +948 -0
- evalrx/contract/export.py +237 -0
- evalrx/contract/m1.py +325 -0
- evalrx/contract/m2.py +317 -0
- evalrx/contract/m3.py +165 -0
- evalrx/contract/m4.py +130 -0
- evalrx/contract/m5.py +292 -0
- evalrx/contract/methodology.py +76 -0
- evalrx/contract/pre_m1.py +58 -0
- evalrx/contract/typescript.py +140 -0
- evalrx/core/__init__.py +85 -0
- evalrx/core/analyzer.py +174 -0
- evalrx/core/capability.py +54 -0
- evalrx/core/case.py +443 -0
- evalrx/core/experiment.py +106 -0
- evalrx/core/model.py +198 -0
- evalrx/core/pipeline.py +42 -0
- evalrx/core/registry.py +142 -0
- evalrx/core/result.py +64 -0
- evalrx/core/spec.py +173 -0
- evalrx/core/tokentype.py +165 -0
- evalrx/core/tool.py +92 -0
- evalrx/datasets/__init__.py +41 -0
- evalrx/datasets/base.py +68 -0
- evalrx/datasets/gui_os.py +52 -0
- evalrx/datasets/llm_qa.py +57 -0
- evalrx/datasets/pure_qa.py +12 -0
- evalrx/datasets/vlm_qa.py +695 -0
- evalrx/datasets/web_search_qa.py +52 -0
- evalrx/eval_agent/__init__.py +341 -0
- evalrx/eval_agent/_tools.py +81 -0
- evalrx/eval_agent/ab_runner.py +50 -0
- evalrx/eval_agent/agentic/__init__.py +43 -0
- evalrx/eval_agent/agentic/actions.py +216 -0
- evalrx/eval_agent/agentic/board.py +107 -0
- evalrx/eval_agent/agentic/loop.py +190 -0
- evalrx/eval_agent/agentic/tools.py +538 -0
- evalrx/eval_agent/checkpoint.py +57 -0
- evalrx/eval_agent/cli_agent.py +59 -0
- evalrx/eval_agent/cli_skills.py +5 -0
- evalrx/eval_agent/evolution.py +396 -0
- evalrx/eval_agent/git_manager.py +215 -0
- evalrx/eval_agent/hypothesis.py +172 -0
- evalrx/eval_agent/label_quarantine.py +209 -0
- evalrx/eval_agent/legacy.py +530 -0
- evalrx/eval_agent/log_schema.py +497 -0
- evalrx/eval_agent/loop.py +2159 -0
- evalrx/eval_agent/loop_reports.py +116 -0
- evalrx/eval_agent/model_instrumentation.py +282 -0
- evalrx/eval_agent/narration.py +193 -0
- evalrx/eval_agent/nl_runner.py +460 -0
- evalrx/eval_agent/orchestrator.py +61 -0
- evalrx/eval_agent/preregister.py +93 -0
- evalrx/eval_agent/prompts/__init__.py +1 -0
- evalrx/eval_agent/prompts/agentic.py +46 -0
- evalrx/eval_agent/prompts/case_discovery.py +25 -0
- evalrx/eval_agent/prompts/diagnosis.py +125 -0
- evalrx/eval_agent/prompts/experiment_writer.py +265 -0
- evalrx/eval_agent/prompts/explore_step.py +37 -0
- evalrx/eval_agent/prompts/fix_agent.py +257 -0
- evalrx/eval_agent/prompts/hypothesis_tester.py +15 -0
- evalrx/eval_agent/prompts/nl_runner.py +38 -0
- evalrx/eval_agent/prompts/probe_agent.py +25 -0
- evalrx/eval_agent/prompts/probe_candidate_generator.py +14 -0
- evalrx/eval_agent/prompts/probe_generator.py +35 -0
- evalrx/eval_agent/prompts/whitebox_probe_generator.py +38 -0
- evalrx/eval_agent/report.py +58 -0
- evalrx/eval_agent/run_context.py +354 -0
- evalrx/eval_agent/run_log.schema.json +1215 -0
- evalrx/eval_agent/run_logger_v2.py +1764 -0
- evalrx/eval_agent/run_metadata.py +208 -0
- evalrx/eval_agent/stages/__init__.py +56 -0
- evalrx/eval_agent/stages/case_discovery.py +293 -0
- evalrx/eval_agent/stages/diagnosis.py +1017 -0
- evalrx/eval_agent/stages/experiment_writer.py +1634 -0
- evalrx/eval_agent/stages/fix_agent.py +3916 -0
- evalrx/eval_agent/stages/fix_internals.py +499 -0
- evalrx/eval_agent/stages/fix_pipeline.py +725 -0
- evalrx/eval_agent/stages/fix_tiers.py +187 -0
- evalrx/eval_agent/stages/fix_tools.py +1034 -0
- evalrx/eval_agent/stages/hypothesis_tester.py +1014 -0
- evalrx/eval_agent/stages/probe.py +439 -0
- evalrx/eval_agent/stages/probe_agent.py +1079 -0
- evalrx/eval_agent/stages/probe_candidate_generator.py +128 -0
- evalrx/eval_agent/stages/probe_generator.py +326 -0
- evalrx/eval_agent/stages/probe_search_agent.py +106 -0
- evalrx/eval_agent/stages/protocol.py +112 -0
- evalrx/eval_agent/stages/repair_catalog.py +273 -0
- evalrx/eval_agent/stages/surgery.py +524 -0
- evalrx/eval_agent/stages/whitebox_probe_generator.py +351 -0
- evalrx/eval_agent/store.py +231 -0
- evalrx/logging_utils.py +112 -0
- evalrx/models/__init__.py +161 -0
- evalrx/models/_discover.py +101 -0
- evalrx/models/agent.py +380 -0
- evalrx/models/backends/__init__.py +58 -0
- evalrx/models/backends/api.py +169 -0
- evalrx/models/backends/base.py +57 -0
- evalrx/models/backends/gemini_compat.py +579 -0
- evalrx/models/backends/hf_local.py +2074 -0
- evalrx/models/backends/openai_compat.py +301 -0
- evalrx/models/backends/vllm_offline.py +116 -0
- evalrx/models/base.py +24 -0
- evalrx/models/blackbox/__init__.py +4 -0
- evalrx/models/blackbox/agent.py +31 -0
- evalrx/models/blackbox/base.py +29 -0
- evalrx/models/blackbox/gemini.py +279 -0
- evalrx/models/blackbox/llm_api.py +17 -0
- evalrx/models/blackbox/vlm_api.py +17 -0
- evalrx/models/compose.py +66 -0
- evalrx/models/inference.py +88 -0
- evalrx/models/paper_methods/__init__.py +8 -0
- evalrx/models/paper_methods/aad.py +53 -0
- evalrx/models/paper_methods/ifcd.py +204 -0
- evalrx/models/paper_methods/pai.py +164 -0
- evalrx/models/paper_methods/tcd.py +202 -0
- evalrx/models/paper_methods/vcd.py +45 -0
- evalrx/models/paper_methods/vicrop.py +137 -0
- evalrx/models/toolcodec.py +143 -0
- evalrx/models/tools/__init__.py +20 -0
- evalrx/models/tools/perception.py +300 -0
- evalrx/models/tools/visual.py +174 -0
- evalrx/models/whitebox/__init__.py +26 -0
- evalrx/models/whitebox/agent.py +31 -0
- evalrx/models/whitebox/base.py +24 -0
- evalrx/models/whitebox/qwen.py +61 -0
- evalrx/models/whitebox/qwen2_5_omni.py +29 -0
- evalrx/models/whitebox/qwen2_audio.py +25 -0
- evalrx/models/whitebox/qwen_omni.py +53 -0
- evalrx/models/whitebox/qwen_vl.py +62 -0
- evalrx/observability/__init__.py +21 -0
- evalrx/observability/envelope.py +122 -0
- evalrx/observability/outbox.py +111 -0
- evalrx/observability/tracer.py +882 -0
- evalrx/reporting/__init__.py +28 -0
- evalrx/reporting/case_study.py +947 -0
- evalrx/reporting/compiler.py +587 -0
- evalrx/reporting/dynamic.py +1882 -0
- evalrx/reporting/html_report.py +2225 -0
- evalrx/reporting/langfuse_exporter.py +38 -0
- evalrx/reporting/langfuse_source.py +155 -0
- evalrx/reporting/model.py +151 -0
- evalrx/reporting/run_events.py +184 -0
- evalrx/reporting/server.py +557 -0
- evalrx/reporting/stages.py +58 -0
- evalrx/reporting/static_export.py +142 -0
- evalrx/reporting/web_dist/index.html +146 -0
- evalrx/specs.py +727 -0
- evalrx/stats/__init__.py +47 -0
- evalrx/stats/api.py +192 -0
- evalrx/stats/bootstrap.py +86 -0
- evalrx/stats/ebh.py +27 -0
- evalrx/stats/evalue.py +98 -0
- evalrx/stats/friedman.py +138 -0
- evalrx/stats/mcnemar.py +40 -0
- evalrx/stats/multiplicity.py +159 -0
- evalrx/stats/subset_sampling.py +55 -0
- evalrx/term_links.py +43 -0
- evalrx/viz/__init__.py +7 -0
- evalrx/viz/labels.py +77 -0
- evalrx/viz/prompts.py +39 -0
- evalrx/viz/renderer.py +590 -0
- evalrx/viz/schema.py +36 -0
- evalrx/viz/style.py +134 -0
- evalrx-0.1.2.dist-info/METADATA +532 -0
- evalrx-0.1.2.dist-info/RECORD +339 -0
- evalrx-0.1.2.dist-info/WHEEL +5 -0
- evalrx-0.1.2.dist-info/entry_points.txt +3 -0
- evalrx-0.1.2.dist-info/licenses/LICENSE +121 -0
- evalrx-0.1.2.dist-info/top_level.txt +1 -0
evalrx/cli.py
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"""Top-level EvalRX command-line interface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from evalrx.analysis.dashboard import launch_dashboard
|
|
9
|
+
from evalrx.analysis.explore_run import run_explore
|
|
10
|
+
from evalrx.analysis.run_codebase import run_codebase_cli
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def serve_report(
|
|
14
|
+
run_dir: str | Path | None,
|
|
15
|
+
*,
|
|
16
|
+
port: int,
|
|
17
|
+
no_audio: bool = False,
|
|
18
|
+
open_browser: bool = True,
|
|
19
|
+
block: bool = True,
|
|
20
|
+
runs_root: str | Path | None = None,
|
|
21
|
+
) -> int:
|
|
22
|
+
"""CLI seam for the dynamic UI (signature kept for compatibility/tests)."""
|
|
23
|
+
del no_audio, block
|
|
24
|
+
from evalrx.reporting.server import serve_dynamic_report
|
|
25
|
+
|
|
26
|
+
return serve_dynamic_report(run_dir, port=port, open_browser=open_browser, runs_root=runs_root)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _langfuse_cache(trace_id: str) -> Path:
|
|
30
|
+
"""Stable local cache for a Langfuse-backed static report, not run storage."""
|
|
31
|
+
safe_id = "".join(char for char in trace_id if char.isalnum() or char in "-_")
|
|
32
|
+
if not safe_id:
|
|
33
|
+
raise ValueError("--trace-id must contain at least one letter or number")
|
|
34
|
+
return Path(".evalrx-cache") / safe_id
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _resolve_report_source(source: str, trace_id: str | None, run_dir: str | None) -> str | None:
|
|
38
|
+
if source == "auto":
|
|
39
|
+
if trace_id:
|
|
40
|
+
try:
|
|
41
|
+
from evalrx.reporting.langfuse_source import LangfuseRunSource
|
|
42
|
+
|
|
43
|
+
return str(LangfuseRunSource().materialize(trace_id, _langfuse_cache(trace_id)))
|
|
44
|
+
except (ImportError, LookupError, RuntimeError):
|
|
45
|
+
pass
|
|
46
|
+
return run_dir
|
|
47
|
+
if source == "local":
|
|
48
|
+
return run_dir
|
|
49
|
+
if not trace_id:
|
|
50
|
+
raise ValueError("--trace-id is required with --source langfuse")
|
|
51
|
+
from evalrx.reporting.langfuse_source import LangfuseRunSource
|
|
52
|
+
|
|
53
|
+
return str(LangfuseRunSource().materialize(trace_id, _langfuse_cache(trace_id)))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def main(argv: list[str] | None = None) -> int:
|
|
57
|
+
parser = argparse.ArgumentParser(
|
|
58
|
+
prog="evalrx",
|
|
59
|
+
description="EvalRX command-line interface.",
|
|
60
|
+
)
|
|
61
|
+
parser.add_argument(
|
|
62
|
+
"-v", "--verbose",
|
|
63
|
+
action="store_true",
|
|
64
|
+
help="Print evalrx' internal stage-by-stage narration "
|
|
65
|
+
"(evalrx.enable_console_logging()) to stdout. Applies to any subcommand.",
|
|
66
|
+
)
|
|
67
|
+
sub = parser.add_subparsers(dest="command")
|
|
68
|
+
|
|
69
|
+
explore = sub.add_parser(
|
|
70
|
+
"explore",
|
|
71
|
+
help="Run a single-shot exploratory analysis over a results directory.",
|
|
72
|
+
description="Run one Lambda-style exploratory analysis (no interactive REPL).",
|
|
73
|
+
)
|
|
74
|
+
explore.add_argument("path", nargs="?", help="File or directory of JSON/JSONL results.")
|
|
75
|
+
explore.add_argument(
|
|
76
|
+
"-q",
|
|
77
|
+
"--question",
|
|
78
|
+
default="Explore this dataset and surface the patterns that matter.",
|
|
79
|
+
help="Natural-language analysis question for the local coding agent.",
|
|
80
|
+
)
|
|
81
|
+
explore.add_argument(
|
|
82
|
+
"--outcome-col",
|
|
83
|
+
default=None,
|
|
84
|
+
help="Name of the target/outcome column, if any (e.g. 'label'). Omit to "
|
|
85
|
+
"auto-detect by name heuristics, or fall back to unsupervised EDA "
|
|
86
|
+
"when the data has no recognizable outcome.",
|
|
87
|
+
)
|
|
88
|
+
explore.add_argument(
|
|
89
|
+
"--out",
|
|
90
|
+
default="evalrx_explore_output",
|
|
91
|
+
help="Output directory for report/code/figures/tables.",
|
|
92
|
+
)
|
|
93
|
+
explore.add_argument(
|
|
94
|
+
"--backend",
|
|
95
|
+
"--coder-provider",
|
|
96
|
+
dest="coder_provider",
|
|
97
|
+
default="antigravity",
|
|
98
|
+
choices=["antigravity", "codex", "claude_code", "opencode", "gemini_cli", "kimi_cli"],
|
|
99
|
+
help="Local CLI coding-agent backend.",
|
|
100
|
+
)
|
|
101
|
+
explore.add_argument("--model", "--coder-model", dest="coder_model", default="")
|
|
102
|
+
explore.add_argument("--coder-binary", default="")
|
|
103
|
+
explore.add_argument("--max-rows", type=int, default=2000)
|
|
104
|
+
explore.add_argument("--max-files", type=int, default=200)
|
|
105
|
+
explore.add_argument("--include-tool-calls", action="store_true")
|
|
106
|
+
explore.add_argument("--timeout-sec", type=int, default=120)
|
|
107
|
+
explore.add_argument("--max-attempts", type=int, default=2)
|
|
108
|
+
explore.add_argument(
|
|
109
|
+
"--dashboard",
|
|
110
|
+
action="store_true",
|
|
111
|
+
help="Deprecated alias for --serve-report.",
|
|
112
|
+
)
|
|
113
|
+
explore.add_argument("--serve-report", action="store_true",
|
|
114
|
+
help="Serve the generated static HTML report when done.")
|
|
115
|
+
explore.add_argument("--port", type=int, default=None, help="Optional report-server port.")
|
|
116
|
+
explore.add_argument(
|
|
117
|
+
"--skill", action="append", default=[], metavar="DIR",
|
|
118
|
+
help="Agent-Skill directory (with SKILL.md) to style agent-authored "
|
|
119
|
+
"figures (e.g. nature-figure). Repeatable. claude/agy/codex backends.",
|
|
120
|
+
)
|
|
121
|
+
explore.add_argument(
|
|
122
|
+
"--allow-skills", action="store_true",
|
|
123
|
+
help="Enable the Skill tool so ~/.claude/skills are usable too "
|
|
124
|
+
"(implied by --skill).",
|
|
125
|
+
)
|
|
126
|
+
explore.add_argument(
|
|
127
|
+
"--no-skills", dest="use_bundled_skills", action="store_false", default=True,
|
|
128
|
+
help="Do not apply the package's bundled skills (e.g. nature-figure).",
|
|
129
|
+
)
|
|
130
|
+
explore.add_argument(
|
|
131
|
+
"--no-hypotheses", dest="propose_hypotheses", action="store_false", default=True,
|
|
132
|
+
help="Skip M3 (falsifiable hypotheses proposed from the M2 takeaways). "
|
|
133
|
+
"Runs by default after a successful explore.",
|
|
134
|
+
)
|
|
135
|
+
explore.add_argument(
|
|
136
|
+
"--holdout-frac", type=float, default=0.0,
|
|
137
|
+
help="Fraction of rows to hold out BEFORE exploration (outcome-"
|
|
138
|
+
"stratified, deterministic). 0 disables (default).",
|
|
139
|
+
)
|
|
140
|
+
explore.add_argument(
|
|
141
|
+
"--holdout-confirm", action="store_true",
|
|
142
|
+
help="After exploring, re-test the frozen recipes + hypotheses on the "
|
|
143
|
+
"held-out rows (writes confirm_report.json — the dashboard's "
|
|
144
|
+
"Held-out Verdicts tab). Requires --holdout-frac > 0.",
|
|
145
|
+
)
|
|
146
|
+
explore.add_argument("--holdout-seed", type=int, default=0,
|
|
147
|
+
help="Seed for the held-out split (default 0).")
|
|
148
|
+
explore.add_argument(
|
|
149
|
+
"--judge-model", default="claude-opus-4-8",
|
|
150
|
+
help="LLM judge grading each hypothesis against the held-out table "
|
|
151
|
+
"(only used with --holdout-confirm).",
|
|
152
|
+
)
|
|
153
|
+
explore.add_argument("--progress-path", default="",
|
|
154
|
+
help="Append durable workbench progress events to this JSONL path.")
|
|
155
|
+
explore.add_argument(
|
|
156
|
+
"--no-narrate", dest="narrate", action="store_false", default=True,
|
|
157
|
+
help="Do not print live per-stage narration (M2/M3/... lines) to the terminal.",
|
|
158
|
+
)
|
|
159
|
+
explore.add_argument("--thread-id", default="", help=argparse.SUPPRESS)
|
|
160
|
+
explore.add_argument("--turn-id", default="", help=argparse.SUPPRESS)
|
|
161
|
+
|
|
162
|
+
run_codebase = sub.add_parser(
|
|
163
|
+
"run-codebase",
|
|
164
|
+
help="Run a user's evaluation codebase, then explore the results it produces.",
|
|
165
|
+
description="A CLI coding agent runs the codebase at PATH inside an isolated copy, "
|
|
166
|
+
"harvests its per-case records (a records.json/.jsonl output contract), "
|
|
167
|
+
"and runs `evalrx explore` (M2+M3) over them.",
|
|
168
|
+
)
|
|
169
|
+
run_codebase.add_argument("path", help="Directory containing the codebase to run.")
|
|
170
|
+
run_codebase.add_argument(
|
|
171
|
+
"-q", "--question",
|
|
172
|
+
default="Explore this dataset and surface the patterns that matter.",
|
|
173
|
+
help="Natural-language analysis question, also given to the run agent as task context.",
|
|
174
|
+
)
|
|
175
|
+
run_codebase.add_argument(
|
|
176
|
+
"--outcome-col", default=None,
|
|
177
|
+
help="Name of the target/outcome column, if any (e.g. 'label'). Omit to auto-detect.",
|
|
178
|
+
)
|
|
179
|
+
run_codebase.add_argument("--out", default="evalrx_run_codebase_output",
|
|
180
|
+
help="Output directory for workspace/records/report/figures/tables.")
|
|
181
|
+
run_codebase.add_argument(
|
|
182
|
+
"--backend", "--coder-provider", dest="coder_provider", default="claude_code",
|
|
183
|
+
choices=["antigravity", "codex", "claude_code", "opencode", "gemini_cli", "kimi_cli"],
|
|
184
|
+
help="Local CLI coding-agent backend used both to run the codebase and to explore it.",
|
|
185
|
+
)
|
|
186
|
+
run_codebase.add_argument("--model", "--coder-model", dest="coder_model", default="")
|
|
187
|
+
run_codebase.add_argument("--coder-binary", default="")
|
|
188
|
+
run_codebase.add_argument("--records-name", default=None,
|
|
189
|
+
help="Output-contract filename the run agent must write "
|
|
190
|
+
"(default: records.json).")
|
|
191
|
+
run_codebase.add_argument("--timeout-sec", type=int, default=1200)
|
|
192
|
+
run_codebase.add_argument("--max-attempts", type=int, default=2)
|
|
193
|
+
run_codebase.add_argument(
|
|
194
|
+
"--no-explore", dest="analyze", action="store_false", default=True,
|
|
195
|
+
help="Only run the codebase and harvest records; skip the explore (M2+M3) step.",
|
|
196
|
+
)
|
|
197
|
+
run_codebase.add_argument(
|
|
198
|
+
"--dashboard", action="store_true",
|
|
199
|
+
help="Deprecated alias for --serve-report.",
|
|
200
|
+
)
|
|
201
|
+
run_codebase.add_argument("--serve-report", action="store_true",
|
|
202
|
+
help="Serve the generated static HTML report when done.")
|
|
203
|
+
run_codebase.add_argument("--port", type=int, default=None, help="Optional report-server port.")
|
|
204
|
+
run_codebase.add_argument(
|
|
205
|
+
"--no-narrate", dest="narrate", action="store_false", default=True,
|
|
206
|
+
help="Do not print live per-stage narration (run/M2/M3/... lines) to the terminal.",
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
dashboard = sub.add_parser(
|
|
210
|
+
"dashboard",
|
|
211
|
+
help="Deprecated alias for the dependency-free 'serve' command.",
|
|
212
|
+
description="Deprecated alias for the dependency-free static report server.",
|
|
213
|
+
)
|
|
214
|
+
dashboard.add_argument("run_dir", help="An explore output dir or a loop-run dir.")
|
|
215
|
+
dashboard.add_argument("--port", type=int, default=None, help="Optional report-server port.")
|
|
216
|
+
|
|
217
|
+
serve = sub.add_parser(
|
|
218
|
+
"serve",
|
|
219
|
+
help="Publish (if needed) and serve the dynamic completed-run UI.",
|
|
220
|
+
description="Generate (if needed) ReportData and serve the agent-composed React UI backed by Langfuse or a local run cache.",
|
|
221
|
+
)
|
|
222
|
+
serve.add_argument("run_dir", nargs="?", default=None, help="Run directory. Omit to start empty and drop a zipped run on the page.")
|
|
223
|
+
serve.add_argument("--port", type=int, default=8501, help="Loopback port (default: 8501).")
|
|
224
|
+
serve.add_argument("--no-audio", action="store_true", help=argparse.SUPPRESS)
|
|
225
|
+
serve.add_argument("--no-browser", action="store_true", help="Do not open a browser automatically.")
|
|
226
|
+
serve.add_argument("--source", choices=["auto", "local", "langfuse"], default="auto", help="Run data source (default: Langfuse when --trace-id is set, otherwise local).")
|
|
227
|
+
serve.add_argument("--trace-id", default=None, help="Langfuse trace id (required for --source langfuse).")
|
|
228
|
+
serve.add_argument("--runs-root", default=None, help="Where the runs panel looks for other experiments (default: run_dir's parent, or the current directory).")
|
|
229
|
+
|
|
230
|
+
report_cmd = sub.add_parser(
|
|
231
|
+
"report",
|
|
232
|
+
help="Explicitly export a portable self-contained HTML snapshot.",
|
|
233
|
+
description="Uses the same React/json-render layout as `serve`; the dynamic UI remains the primary experience.",
|
|
234
|
+
)
|
|
235
|
+
report_cmd.add_argument("run_dir", nargs="?", default="outputs", help="Run directory holding run.json/M1..M5, or logs/")
|
|
236
|
+
report_cmd.add_argument("--example-dir", default=None, help="Root holding data/ manifest.")
|
|
237
|
+
report_cmd.add_argument("--out", "-o", default=None, help="Output HTML path (default: <run_dir>/report.html).")
|
|
238
|
+
report_cmd.add_argument("--no-audio", action="store_true", help="Skip audio transcoding.")
|
|
239
|
+
report_cmd.add_argument("--embed-media", choices=["representative", "all", "none"], default="representative", help="Media to inline in the portable export (default: representative).")
|
|
240
|
+
report_cmd.add_argument("--audio-bitrate", default="48k", help="Transcode inlined lossless audio to mono MP3 at this bitrate via ffmpeg (default: 48k; 'none' = inline the original bytes).")
|
|
241
|
+
report_cmd.add_argument("--source", choices=["local", "langfuse"], default="local", help="Run data source.")
|
|
242
|
+
report_cmd.add_argument("--trace-id", default=None, help="Langfuse trace id (required for --source langfuse).")
|
|
243
|
+
|
|
244
|
+
publish_cmd = sub.add_parser(
|
|
245
|
+
"publish-report",
|
|
246
|
+
help="Compile and cache ReportData plus a validated json-render layout.",
|
|
247
|
+
)
|
|
248
|
+
publish_cmd.add_argument("run_dir", nargs="?", default="outputs", help="Completed run directory.")
|
|
249
|
+
publish_cmd.add_argument("--example-dir", default=None, help="Optional benchmark/example root for legacy runs.")
|
|
250
|
+
publish_cmd.add_argument("--source", choices=["auto", "local", "langfuse"], default="auto")
|
|
251
|
+
publish_cmd.add_argument("--trace-id", default=None)
|
|
252
|
+
|
|
253
|
+
langfuse_cmd = sub.add_parser(
|
|
254
|
+
"export-langfuse",
|
|
255
|
+
help="Export a diagnostic run to Langfuse trace JSON format or sync live.",
|
|
256
|
+
description="Map an EvalRX run (M1-M4, fixes, scores) to Langfuse Traces, Spans, and Scores.",
|
|
257
|
+
)
|
|
258
|
+
langfuse_cmd.add_argument("run_dir", nargs="?", default="outputs", help="Run directory.")
|
|
259
|
+
langfuse_cmd.add_argument("--out", "-o", default=None, help="Output JSON path.")
|
|
260
|
+
langfuse_cmd.add_argument("--sync", action="store_true", help="Sync live to Langfuse server.")
|
|
261
|
+
|
|
262
|
+
backfill_langfuse = sub.add_parser(
|
|
263
|
+
"backfill-langfuse",
|
|
264
|
+
help="Queue an existing run (run.json/M1..M5) for reliable Langfuse ingestion.",
|
|
265
|
+
)
|
|
266
|
+
backfill_langfuse.add_argument("run_dir", help="Run directory holding run.json/M1..M5, or logs/.")
|
|
267
|
+
backfill_langfuse.add_argument("--dry-run", action="store_true", help="Inspect the run without writing an outbox.")
|
|
268
|
+
|
|
269
|
+
args = parser.parse_args(argv)
|
|
270
|
+
if args.verbose:
|
|
271
|
+
from evalrx.logging_utils import enable_console_logging
|
|
272
|
+
|
|
273
|
+
enable_console_logging()
|
|
274
|
+
if args.command == "explore":
|
|
275
|
+
if not args.path:
|
|
276
|
+
parser.error("evalrx explore requires a results path")
|
|
277
|
+
from evalrx.analysis.narration import MultiSink, TerminalNarrator
|
|
278
|
+
|
|
279
|
+
event_sink = None
|
|
280
|
+
if args.progress_path:
|
|
281
|
+
from evalrx.analysis.workbench import EventSink
|
|
282
|
+
|
|
283
|
+
event_sink = EventSink(
|
|
284
|
+
args.progress_path,
|
|
285
|
+
thread_id=args.thread_id or "standalone",
|
|
286
|
+
turn_id=args.turn_id or "explore",
|
|
287
|
+
)
|
|
288
|
+
event_sink.emit("job", "started", "Analysis worker started")
|
|
289
|
+
# "job" bookkeeping above/below stays workbench-only (event_sink), not
|
|
290
|
+
# narrated -- it wraps the whole process rather than one M-stage.
|
|
291
|
+
progress_sink = MultiSink(TerminalNarrator() if args.narrate else None, event_sink) or None
|
|
292
|
+
code = run_explore(
|
|
293
|
+
args.path,
|
|
294
|
+
question=args.question,
|
|
295
|
+
out=args.out,
|
|
296
|
+
coder_provider=args.coder_provider,
|
|
297
|
+
coder_model=args.coder_model,
|
|
298
|
+
coder_binary=args.coder_binary,
|
|
299
|
+
max_rows=args.max_rows,
|
|
300
|
+
max_files=args.max_files,
|
|
301
|
+
include_tool_calls=args.include_tool_calls,
|
|
302
|
+
timeout_sec=args.timeout_sec,
|
|
303
|
+
max_attempts=args.max_attempts,
|
|
304
|
+
dashboard=args.dashboard or args.serve_report,
|
|
305
|
+
dashboard_port=args.port,
|
|
306
|
+
skills=args.skill,
|
|
307
|
+
allow_skills=args.allow_skills,
|
|
308
|
+
use_bundled_skills=args.use_bundled_skills,
|
|
309
|
+
outcome_col=args.outcome_col,
|
|
310
|
+
propose_hypotheses=args.propose_hypotheses,
|
|
311
|
+
holdout_frac=args.holdout_frac,
|
|
312
|
+
holdout_seed=args.holdout_seed,
|
|
313
|
+
holdout_confirm=args.holdout_confirm,
|
|
314
|
+
judge_model=args.judge_model,
|
|
315
|
+
progress_sink=progress_sink,
|
|
316
|
+
)
|
|
317
|
+
if event_sink is not None:
|
|
318
|
+
event_sink.emit(
|
|
319
|
+
"job", "completed" if code == 0 else "failed",
|
|
320
|
+
"Analysis worker completed" if code == 0 else "Analysis worker failed",
|
|
321
|
+
)
|
|
322
|
+
return code
|
|
323
|
+
if args.command == "run-codebase":
|
|
324
|
+
from evalrx.analysis.explorer import RECORDS_FILENAME
|
|
325
|
+
from evalrx.analysis.narration import TerminalNarrator
|
|
326
|
+
|
|
327
|
+
return run_codebase_cli(
|
|
328
|
+
args.path,
|
|
329
|
+
out=args.out,
|
|
330
|
+
coder_provider=args.coder_provider,
|
|
331
|
+
coder_model=args.coder_model,
|
|
332
|
+
coder_binary=args.coder_binary,
|
|
333
|
+
outcome_col=args.outcome_col,
|
|
334
|
+
records_name=args.records_name or RECORDS_FILENAME,
|
|
335
|
+
timeout_sec=args.timeout_sec,
|
|
336
|
+
max_attempts=args.max_attempts,
|
|
337
|
+
question=args.question,
|
|
338
|
+
analyze=args.analyze,
|
|
339
|
+
dashboard=args.dashboard or args.serve_report,
|
|
340
|
+
dashboard_port=args.port,
|
|
341
|
+
progress_sink=TerminalNarrator() if args.narrate else None,
|
|
342
|
+
)
|
|
343
|
+
if args.command == "dashboard":
|
|
344
|
+
return launch_dashboard(args.run_dir, port=args.port)
|
|
345
|
+
if args.command == "serve":
|
|
346
|
+
try:
|
|
347
|
+
source_dir = _resolve_report_source(args.source, args.trace_id, args.run_dir)
|
|
348
|
+
except (ImportError, LookupError, RuntimeError, ValueError) as exc:
|
|
349
|
+
parser.error(str(exc))
|
|
350
|
+
try:
|
|
351
|
+
return serve_report(
|
|
352
|
+
source_dir, port=args.port, no_audio=args.no_audio,
|
|
353
|
+
open_browser=not args.no_browser, runs_root=args.runs_root,
|
|
354
|
+
)
|
|
355
|
+
except ImportError as exc:
|
|
356
|
+
parser.error(str(exc))
|
|
357
|
+
if args.command == "publish-report":
|
|
358
|
+
from evalrx.reporting.dynamic import publish_report
|
|
359
|
+
|
|
360
|
+
try:
|
|
361
|
+
source_dir = _resolve_report_source(args.source, args.trace_id, args.run_dir)
|
|
362
|
+
published = publish_report(source_dir, example_dir=args.example_dir)
|
|
363
|
+
except (ImportError, LookupError, RuntimeError, ValueError) as exc:
|
|
364
|
+
parser.error(str(exc))
|
|
365
|
+
print(f"Published report data: {published.data_path}")
|
|
366
|
+
print(f"Published report layout: {published.spec_path} ({published.generated_by})")
|
|
367
|
+
return 0
|
|
368
|
+
if args.command == "report":
|
|
369
|
+
from evalrx.reporting.static_export import export_static_report
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
source_dir = _resolve_report_source(args.source, args.trace_id, args.run_dir)
|
|
373
|
+
except (ImportError, LookupError, RuntimeError, ValueError) as exc:
|
|
374
|
+
parser.error(str(exc))
|
|
375
|
+
|
|
376
|
+
export_static_report(
|
|
377
|
+
run_dir=source_dir,
|
|
378
|
+
example_dir=args.example_dir,
|
|
379
|
+
out_path=args.out,
|
|
380
|
+
embed_media="none" if args.no_audio else args.embed_media,
|
|
381
|
+
audio_bitrate=None if str(getattr(args, "audio_bitrate", "48k")).lower() == "none" else args.audio_bitrate,
|
|
382
|
+
)
|
|
383
|
+
return 0
|
|
384
|
+
if args.command == "export-langfuse":
|
|
385
|
+
from evalrx.reporting.langfuse_exporter import (
|
|
386
|
+
export_to_langfuse_bundle,
|
|
387
|
+
sync_to_langfuse_live,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
if args.sync:
|
|
391
|
+
return 0 if sync_to_langfuse_live(args.run_dir) else 1
|
|
392
|
+
out_p = args.out or (Path(args.run_dir) / "langfuse_trace.json")
|
|
393
|
+
export_to_langfuse_bundle(args.run_dir, out_p)
|
|
394
|
+
return 0
|
|
395
|
+
if args.command == "backfill-langfuse":
|
|
396
|
+
from evalrx.observability import backfill_run_to_langfuse
|
|
397
|
+
|
|
398
|
+
summary = backfill_run_to_langfuse(args.run_dir, dry_run=args.dry_run)
|
|
399
|
+
print(
|
|
400
|
+
"Langfuse backfill: "
|
|
401
|
+
f"trace={summary['trace_id']} events={summary['events']} "
|
|
402
|
+
f"published={summary['published']} pending={summary['pending']}"
|
|
403
|
+
)
|
|
404
|
+
return 0
|
|
405
|
+
|
|
406
|
+
parser.print_help()
|
|
407
|
+
return 0
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
if __name__ == "__main__": # pragma: no cover
|
|
411
|
+
raise SystemExit(main())
|
evalrx/config.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Config loading and dataclasses for evalrx."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ModelConfig:
|
|
14
|
+
"""Declarative model selection — resolved to a spec + backend at load time.
|
|
15
|
+
|
|
16
|
+
``name`` is a :mod:`evalrx.specs` key (e.g. ``"qwen2.5-7b-instruct"``) or a
|
|
17
|
+
legacy alias (e.g. ``"qwen"``); resolution happens in
|
|
18
|
+
:func:`evalrx.models.load_model` via ``compose(spec, backend)``. No
|
|
19
|
+
checkpoint is baked here — identity lives in the spec.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
name: Spec key or legacy alias.
|
|
23
|
+
checkpoint: Optional override of the spec's ``hf_repo``.
|
|
24
|
+
device: Runtime device (``"auto"``, ``"cuda"``, ``"cpu"``, ``"cuda:N"``).
|
|
25
|
+
dtype: Runtime dtype (``"bfloat16"``, ``"float16"``, ``"float32"``).
|
|
26
|
+
backend: ``"hf_local"`` (internals) / ``"api"`` / ``"vllm_offline"``.
|
|
27
|
+
want: Capability names the backend must provide (e.g. ``["attention"]``).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
name: str
|
|
31
|
+
checkpoint: str | None = None
|
|
32
|
+
device: str = "auto"
|
|
33
|
+
dtype: str = "bfloat16"
|
|
34
|
+
backend: str = "hf_local"
|
|
35
|
+
want: list[str] = field(default_factory=list)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class AnalysisConfig:
|
|
40
|
+
model: ModelConfig
|
|
41
|
+
analysis: str
|
|
42
|
+
analysis_kwargs: dict[str, Any] = field(default_factory=dict)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def load_config(path: str | Path) -> AnalysisConfig:
|
|
46
|
+
"""Load an AnalysisConfig from a YAML file.
|
|
47
|
+
|
|
48
|
+
Supports both short form::
|
|
49
|
+
|
|
50
|
+
model: qwen2.5-7b-instruct # spec key (or a legacy alias like "qwen")
|
|
51
|
+
analysis: attention
|
|
52
|
+
|
|
53
|
+
and full form::
|
|
54
|
+
|
|
55
|
+
model:
|
|
56
|
+
name: qwen2.5-7b-instruct
|
|
57
|
+
backend: hf_local # hf_local | api | vllm_offline
|
|
58
|
+
device: auto
|
|
59
|
+
dtype: bfloat16
|
|
60
|
+
want: [attention] # capabilities the backend must provide
|
|
61
|
+
analysis: attention # registered analyzer name
|
|
62
|
+
analysis_kwargs:
|
|
63
|
+
layer: -1
|
|
64
|
+
head: mean
|
|
65
|
+
top_k: 10
|
|
66
|
+
"""
|
|
67
|
+
with open(path) as f:
|
|
68
|
+
raw = yaml.safe_load(f)
|
|
69
|
+
|
|
70
|
+
model_raw = raw["model"]
|
|
71
|
+
model_cfg = ModelConfig(name=model_raw) if isinstance(model_raw, str) else ModelConfig(**model_raw)
|
|
72
|
+
|
|
73
|
+
return AnalysisConfig(
|
|
74
|
+
model=model_cfg,
|
|
75
|
+
analysis=raw["analysis"],
|
|
76
|
+
analysis_kwargs=raw.get("analysis_kwargs", {}),
|
|
77
|
+
)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Wire contract for the M1-M4 diagnosis pipeline.
|
|
2
|
+
|
|
3
|
+
The single machine-readable definition of what crosses each stage boundary.
|
|
4
|
+
Python is the source of truth; JSON Schema and TypeScript are generated from it
|
|
5
|
+
(``python -m evalrx.contract.export``) so the three can never drift.
|
|
6
|
+
|
|
7
|
+
from evalrx.contract import STAGE_IO, ProbeOutput
|
|
8
|
+
|
|
9
|
+
ProbeOutput.model_validate_json(open("probe.json").read()) # backend gate
|
|
10
|
+
STAGE_IO["m1"].output.model_json_schema() # frontend types
|
|
11
|
+
|
|
12
|
+
Scope: this describes the SERIALIZED shape, not the in-memory dataclasses. They
|
|
13
|
+
differ (``AnalysisReport.to_dict()`` drops ``raw_results``), and only the
|
|
14
|
+
serialized shape is what another process actually receives.
|
|
15
|
+
|
|
16
|
+
Reader-facing text
|
|
17
|
+
------------------
|
|
18
|
+
Every field a frontend puts on screen is written for a reader with no
|
|
19
|
+
background in this field -- assume a bright high-school student who has never
|
|
20
|
+
heard of an e-value, a tier, or a paired flip. That obligation sits here, on
|
|
21
|
+
the producer, and not on the frontend, for one reason: a consumer handed
|
|
22
|
+
``mcnemar_evalue`` or ``audio_evidence_then_answer`` can only guess, and a
|
|
23
|
+
guess dressed as a label ("Mcnemar Evalue") reads as though the run explained
|
|
24
|
+
itself when it did not.
|
|
25
|
+
|
|
26
|
+
So the contract separates the two jobs wherever they collide:
|
|
27
|
+
|
|
28
|
+
* an IDENTIFIER (``FixAttemptWire.name``, a signal's dotted path) joins records
|
|
29
|
+
and must never change to suit a reader;
|
|
30
|
+
* a LABEL (``StatsToolResultWire.measured``, ``FixAttemptWire.ref``) is what a
|
|
31
|
+
reader points at;
|
|
32
|
+
* a SENTENCE (``StatsToolResultWire.means``, ``FixAttemptWire.headline``,
|
|
33
|
+
``MethodologyWire.summary``) says what it means in plain language.
|
|
34
|
+
|
|
35
|
+
A producer with nothing readable to offer leaves the label and sentence empty.
|
|
36
|
+
An honest blank is a smaller error than a title-cased slug, because the blank
|
|
37
|
+
is visible and the slug is not.
|
|
38
|
+
|
|
39
|
+
Requires the ``contract`` extra: ``pip install evalrx[contract]``.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
from typing import NamedTuple
|
|
45
|
+
|
|
46
|
+
from evalrx.contract.common import (
|
|
47
|
+
SCHEMA_VERSION,
|
|
48
|
+
ArtifactRef,
|
|
49
|
+
CaseBatchRef,
|
|
50
|
+
CaseBatchWire,
|
|
51
|
+
EvidenceGrade,
|
|
52
|
+
ExternalRef,
|
|
53
|
+
FailureCaseWire,
|
|
54
|
+
HypothesisStatus,
|
|
55
|
+
InputsWire,
|
|
56
|
+
JoinReport,
|
|
57
|
+
Label,
|
|
58
|
+
MediaRef,
|
|
59
|
+
Modality,
|
|
60
|
+
ProvenanceWire,
|
|
61
|
+
Source,
|
|
62
|
+
StageEnvelope,
|
|
63
|
+
StageState,
|
|
64
|
+
StageStatus,
|
|
65
|
+
StepWire,
|
|
66
|
+
TrajectoryWire,
|
|
67
|
+
)
|
|
68
|
+
from evalrx.contract.m1 import (
|
|
69
|
+
AnalyzerSelection,
|
|
70
|
+
FindingsWire,
|
|
71
|
+
ModelRef,
|
|
72
|
+
PerCaseRow,
|
|
73
|
+
ProbeInput,
|
|
74
|
+
ProbeOutput,
|
|
75
|
+
ProtocolWire,
|
|
76
|
+
ResultWire,
|
|
77
|
+
)
|
|
78
|
+
from evalrx.contract.m2 import (
|
|
79
|
+
AnalysisFindingWire,
|
|
80
|
+
AnalysisInput,
|
|
81
|
+
CorrectedRejections,
|
|
82
|
+
ExploreContextWire,
|
|
83
|
+
StatsReportWire,
|
|
84
|
+
StatsToolResultWire,
|
|
85
|
+
)
|
|
86
|
+
from evalrx.contract.m3 import (
|
|
87
|
+
DiagnosisInput,
|
|
88
|
+
DiagnosisOutput,
|
|
89
|
+
HypothesisWire,
|
|
90
|
+
)
|
|
91
|
+
from evalrx.contract.m4 import (
|
|
92
|
+
HypothesisTestInput,
|
|
93
|
+
HypothesisTestOutput,
|
|
94
|
+
HypothesisTestResultWire,
|
|
95
|
+
TestEvidence,
|
|
96
|
+
)
|
|
97
|
+
from evalrx.contract.m5 import (
|
|
98
|
+
FixAttemptWire,
|
|
99
|
+
FixInput,
|
|
100
|
+
FixOutput,
|
|
101
|
+
InterventionOutput,
|
|
102
|
+
SurgeryInput,
|
|
103
|
+
)
|
|
104
|
+
from evalrx.contract.methodology import MethodologyWire
|
|
105
|
+
from evalrx.contract.pre_m1 import ProbeSearchInput, ProbeSearchOutput
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class StageContract(NamedTuple):
|
|
109
|
+
"""One stage's input/output pair plus what it is for."""
|
|
110
|
+
|
|
111
|
+
stage: str
|
|
112
|
+
purpose: str
|
|
113
|
+
input: type
|
|
114
|
+
output: type
|
|
115
|
+
optional: bool = False
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
#: The pipeline, as data. Docs, schema export and stage-map UIs all read this
|
|
119
|
+
#: instead of restating the wiring in prose that then drifts.
|
|
120
|
+
#:
|
|
121
|
+
#: Execution order note: the numbering is registration order, not run order.
|
|
122
|
+
#: ``VLDiagnoseLoop`` runs M1->M2->M3->M4 as its cycle and calls M5 once after,
|
|
123
|
+
#: because M5 is expensive and should only run on a hypothesis M4 verified.
|
|
124
|
+
STAGE_IO: dict[str, StageContract] = {
|
|
125
|
+
"pre_m1": StageContract(
|
|
126
|
+
"pre_m1", "Synthesize new failing cases (output is DATA, not a verdict).",
|
|
127
|
+
ProbeSearchInput, ProbeSearchOutput, optional=True,
|
|
128
|
+
),
|
|
129
|
+
"m1": StageContract(
|
|
130
|
+
"m1", "Measure: run analyzers, emit per-case numbers. No pass/fail judgement.",
|
|
131
|
+
ProbeInput, ProbeOutput,
|
|
132
|
+
),
|
|
133
|
+
"m2": StageContract(
|
|
134
|
+
"m2", "Screen: join numbers to labels, test which signals track failure, correct for multiplicity.",
|
|
135
|
+
AnalysisInput, StatsReportWire,
|
|
136
|
+
),
|
|
137
|
+
"m3": StageContract(
|
|
138
|
+
"m3", "Explain: propose falsifiable mechanism hypotheses, each with a routable test_design.",
|
|
139
|
+
DiagnosisInput, DiagnosisOutput,
|
|
140
|
+
),
|
|
141
|
+
"m4": StageContract(
|
|
142
|
+
"m4", "Adjudicate: statistical gate AND protocol-consistency gate. Both, or not SUPPORTED.",
|
|
143
|
+
HypothesisTestInput, HypothesisTestOutput,
|
|
144
|
+
),
|
|
145
|
+
"m5_surgery": StageContract(
|
|
146
|
+
"m5_surgery", "Intervene: change one variable, re-run, read as causal evidence. Does not repair.",
|
|
147
|
+
SurgeryInput, InterventionOutput,
|
|
148
|
+
),
|
|
149
|
+
"m5_fix": StageContract(
|
|
150
|
+
"m5_fix", "Repair: propose candidates, validate paired against the unmodified baseline.",
|
|
151
|
+
FixInput, FixOutput, optional=True,
|
|
152
|
+
),
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
__all__ = [
|
|
156
|
+
"SCHEMA_VERSION", "STAGE_IO", "StageContract",
|
|
157
|
+
# common
|
|
158
|
+
"ArtifactRef", "CaseBatchRef", "CaseBatchWire", "EvidenceGrade", "ExternalRef",
|
|
159
|
+
"FailureCaseWire", "HypothesisStatus", "InputsWire", "JoinReport", "Label",
|
|
160
|
+
"MediaRef", "Modality", "ProvenanceWire", "Source", "StageEnvelope", "StageState",
|
|
161
|
+
"StageStatus", "StepWire", "TrajectoryWire",
|
|
162
|
+
# pre-M1
|
|
163
|
+
"ProbeSearchInput", "ProbeSearchOutput",
|
|
164
|
+
# M1
|
|
165
|
+
"ProbeInput", "ProbeOutput", "ResultWire", "FindingsWire", "PerCaseRow",
|
|
166
|
+
"ModelRef", "ProtocolWire", "AnalyzerSelection",
|
|
167
|
+
# M2
|
|
168
|
+
"AnalysisInput", "StatsReportWire", "StatsToolResultWire", "AnalysisFindingWire",
|
|
169
|
+
"CorrectedRejections", "ExploreContextWire",
|
|
170
|
+
# M3
|
|
171
|
+
"DiagnosisInput", "DiagnosisOutput", "HypothesisWire",
|
|
172
|
+
# M5
|
|
173
|
+
"SurgeryInput", "InterventionOutput",
|
|
174
|
+
"FixInput", "FixOutput", "FixAttemptWire",
|
|
175
|
+
# methodology
|
|
176
|
+
"MethodologyWire",
|
|
177
|
+
# M4
|
|
178
|
+
"HypothesisTestInput", "HypothesisTestOutput", "HypothesisTestResultWire", "TestEvidence",
|
|
179
|
+
]
|