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
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Judge-decided actions for the agentic diagnosis loop.
|
|
2
|
+
|
|
3
|
+
Each decision turn, the CLI judge returns one strict JSON object describing
|
|
4
|
+
the next tool to call. ``parse_action`` turns that raw text into a validated
|
|
5
|
+
:class:`Action`, using the shared :func:`~evalrx.agent_runtime.json_shape.validate_json_shape`
|
|
6
|
+
(re-exported here for convenience — also used by M3's hypothesis parser and
|
|
7
|
+
failure-mode cluster naming).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
import re
|
|
15
|
+
import time
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
from evalrx.agent_runtime.json_shape import validate_json_shape
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from evalrx.eval_agent.agentic.board import EvidenceBoard
|
|
23
|
+
from evalrx.eval_agent.agentic.tools import ToolRegistry
|
|
24
|
+
from evalrx.eval_agent.run_logger_v2 import RunLoggerV2
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
ACTION_SCHEMA: dict[str, Any] = {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["tool", "rationale"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"tool": {"type": "string", "minLength": 1},
|
|
33
|
+
"params": {"type": "object"},
|
|
34
|
+
"rationale": {"type": "string", "minLength": 1},
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ActionParseError(Exception):
|
|
40
|
+
"""Raised when the judge's raw output cannot be turned into a valid Action."""
|
|
41
|
+
|
|
42
|
+
def __init__(self, message: str, errors: list[str] | None = None) -> None:
|
|
43
|
+
super().__init__(message)
|
|
44
|
+
self.errors = errors or []
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class Action:
|
|
49
|
+
"""One judge-decided tool call."""
|
|
50
|
+
|
|
51
|
+
tool: str
|
|
52
|
+
params: dict[str, Any] = field(default_factory=dict)
|
|
53
|
+
rationale: str = ""
|
|
54
|
+
raw: str = ""
|
|
55
|
+
repair_attempts: int = 0
|
|
56
|
+
valid: bool = True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _strip_fences(text: str) -> str:
|
|
60
|
+
text = text.strip()
|
|
61
|
+
if text.startswith("```"):
|
|
62
|
+
text = re.sub(r"^```\w*\n?", "", text)
|
|
63
|
+
text = re.sub(r"\n?```\s*$", "", text)
|
|
64
|
+
return text.strip()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def parse_action(raw: str, registry: "ToolRegistry") -> Action:
|
|
68
|
+
"""Parse the judge's raw text into a validated :class:`Action`.
|
|
69
|
+
|
|
70
|
+
Raises :class:`ActionParseError` when the text is not JSON, fails the
|
|
71
|
+
action schema, or names a tool the registry doesn't know about.
|
|
72
|
+
"""
|
|
73
|
+
text = _strip_fences(raw)
|
|
74
|
+
try:
|
|
75
|
+
data = json.loads(text)
|
|
76
|
+
except json.JSONDecodeError as exc:
|
|
77
|
+
raise ActionParseError(f"not valid JSON: {exc}") from exc
|
|
78
|
+
|
|
79
|
+
errors = validate_json_shape(data, ACTION_SCHEMA)
|
|
80
|
+
if errors:
|
|
81
|
+
raise ActionParseError("action schema validation failed", errors)
|
|
82
|
+
|
|
83
|
+
tool = str(data["tool"])
|
|
84
|
+
if tool not in registry.tool_names():
|
|
85
|
+
raise ActionParseError(
|
|
86
|
+
f"unknown tool {tool!r}", [f"must be one of {sorted(registry.tool_names())}"]
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
params = data.get("params") or {}
|
|
90
|
+
if not isinstance(params, dict):
|
|
91
|
+
raise ActionParseError("params must be an object", [f"$.params: got {type(params).__name__}"])
|
|
92
|
+
|
|
93
|
+
return Action(tool=tool, params=params, rationale=str(data["rationale"]), raw=raw)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _fallback_action(board: "EvidenceBoard") -> Action:
|
|
97
|
+
"""Deterministic next-step heuristic: the first unmet M1->M4 stage.
|
|
98
|
+
|
|
99
|
+
Used when the judge's output is unusable even after one repair prompt, so
|
|
100
|
+
a stuck judge doesn't stall the run — the action is marked ``valid=False``
|
|
101
|
+
so the loop can track a consecutive-failure streak and give up cleanly.
|
|
102
|
+
"""
|
|
103
|
+
if not board.probe_findings:
|
|
104
|
+
tool = "run_probe"
|
|
105
|
+
elif not board.stats_findings:
|
|
106
|
+
tool = "run_stats"
|
|
107
|
+
elif not board.hypotheses:
|
|
108
|
+
tool = "propose_hypotheses"
|
|
109
|
+
elif not board.has_supported_hypothesis():
|
|
110
|
+
tool = "test_hypothesis"
|
|
111
|
+
else:
|
|
112
|
+
tool = "stop"
|
|
113
|
+
params = {"resolved": True, "reason": "fallback heuristic"} if tool == "stop" else {}
|
|
114
|
+
return Action(
|
|
115
|
+
tool=tool, params=params,
|
|
116
|
+
rationale="fallback heuristic: judge output unusable", valid=False,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def decide(
|
|
121
|
+
judge: Any,
|
|
122
|
+
board: "EvidenceBoard",
|
|
123
|
+
registry: "ToolRegistry",
|
|
124
|
+
*,
|
|
125
|
+
run_logger: "RunLoggerV2 | None" = None,
|
|
126
|
+
step: int = 0,
|
|
127
|
+
max_repairs: int = 1,
|
|
128
|
+
) -> Action:
|
|
129
|
+
"""Ask the judge for the next action, repairing/falling back as needed.
|
|
130
|
+
|
|
131
|
+
Always returns an :class:`Action` — never raises. When the judge's
|
|
132
|
+
response can't be salvaged after *max_repairs* repair prompts, returns the
|
|
133
|
+
deterministic :func:`_fallback_action` with ``valid=False``.
|
|
134
|
+
"""
|
|
135
|
+
from evalrx.eval_agent.prompts.agentic import ACTION_REPAIR_PROMPT, DECISION_PROMPT
|
|
136
|
+
|
|
137
|
+
evidence = board.to_prompt(registry)
|
|
138
|
+
prompt = DECISION_PROMPT.format(evidence=evidence)
|
|
139
|
+
judge_calls: list[dict[str, Any]] = []
|
|
140
|
+
started = time.perf_counter()
|
|
141
|
+
try:
|
|
142
|
+
raw = judge.generate(prompt)
|
|
143
|
+
except Exception as exc: # noqa: BLE001
|
|
144
|
+
judge_calls.append({
|
|
145
|
+
"role": "agent_decision", "operation": "generate", "inputs": prompt,
|
|
146
|
+
"output": None, "error": str(exc),
|
|
147
|
+
"duration_sec": time.perf_counter() - started,
|
|
148
|
+
})
|
|
149
|
+
raw = ""
|
|
150
|
+
action: Action | None = _fallback_action(board)
|
|
151
|
+
else:
|
|
152
|
+
judge_calls.append({
|
|
153
|
+
"role": "agent_decision", "operation": "generate", "inputs": prompt,
|
|
154
|
+
"output": str(raw), "error": None,
|
|
155
|
+
"duration_sec": time.perf_counter() - started,
|
|
156
|
+
})
|
|
157
|
+
try:
|
|
158
|
+
action = parse_action(raw, registry)
|
|
159
|
+
except ActionParseError as exc:
|
|
160
|
+
action = None
|
|
161
|
+
last_raw, last_exc = raw, exc
|
|
162
|
+
if action is None:
|
|
163
|
+
for attempt in range(1, max_repairs + 1):
|
|
164
|
+
repair_prompt = ACTION_REPAIR_PROMPT.format(
|
|
165
|
+
raw=last_raw,
|
|
166
|
+
errors="\n".join(last_exc.errors) or str(last_exc),
|
|
167
|
+
evidence=evidence,
|
|
168
|
+
)
|
|
169
|
+
started = time.perf_counter()
|
|
170
|
+
try:
|
|
171
|
+
last_raw = judge.generate(repair_prompt)
|
|
172
|
+
except Exception as exc: # noqa: BLE001
|
|
173
|
+
judge_calls.append({
|
|
174
|
+
"role": "agent_decision_repair", "operation": "generate",
|
|
175
|
+
"inputs": repair_prompt, "output": None, "error": str(exc),
|
|
176
|
+
"duration_sec": time.perf_counter() - started,
|
|
177
|
+
"metadata": {"attempt": attempt},
|
|
178
|
+
})
|
|
179
|
+
break
|
|
180
|
+
judge_calls.append({
|
|
181
|
+
"role": "agent_decision_repair", "operation": "generate",
|
|
182
|
+
"inputs": repair_prompt, "output": str(last_raw), "error": None,
|
|
183
|
+
"duration_sec": time.perf_counter() - started,
|
|
184
|
+
"metadata": {"attempt": attempt},
|
|
185
|
+
})
|
|
186
|
+
try:
|
|
187
|
+
action = parse_action(last_raw, registry)
|
|
188
|
+
action.repair_attempts = attempt
|
|
189
|
+
break
|
|
190
|
+
except ActionParseError as exc2:
|
|
191
|
+
last_exc = exc2
|
|
192
|
+
action = None
|
|
193
|
+
if action is None:
|
|
194
|
+
action = _fallback_action(board)
|
|
195
|
+
action.raw = raw
|
|
196
|
+
|
|
197
|
+
if run_logger is not None:
|
|
198
|
+
try:
|
|
199
|
+
log_kwargs = dict(
|
|
200
|
+
step=step,
|
|
201
|
+
action=action.tool,
|
|
202
|
+
params=action.params,
|
|
203
|
+
rationale=action.rationale,
|
|
204
|
+
valid=action.valid,
|
|
205
|
+
repair_attempts=action.repair_attempts,
|
|
206
|
+
fallback_used=not action.valid,
|
|
207
|
+
judge_prompt=prompt,
|
|
208
|
+
judge_raw=raw,
|
|
209
|
+
)
|
|
210
|
+
if getattr(run_logger, "preserve_full_model_io", False):
|
|
211
|
+
log_kwargs["judge_calls"] = judge_calls
|
|
212
|
+
run_logger.log_agent_decision(**log_kwargs)
|
|
213
|
+
except Exception: # noqa: BLE001 — logging must never break the loop
|
|
214
|
+
logger.warning("decide: log_agent_decision failed", exc_info=True)
|
|
215
|
+
|
|
216
|
+
return action
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""The evidence board: everything the judge sees when deciding the next action.
|
|
2
|
+
|
|
3
|
+
The host reconstructs the full decision context from this board on every
|
|
4
|
+
turn — there is no CLI session state carried between judge calls, only what
|
|
5
|
+
is serialized here.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import time
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class BudgetState:
|
|
17
|
+
"""Guardrails on how long an agentic run may keep deciding actions."""
|
|
18
|
+
|
|
19
|
+
max_actions: int = 12
|
|
20
|
+
token_budget: int = 0
|
|
21
|
+
time_budget_sec: float = 0.0
|
|
22
|
+
actions_taken: int = 0
|
|
23
|
+
tokens_used: int = 0
|
|
24
|
+
_started_monotonic: float = 0.0
|
|
25
|
+
|
|
26
|
+
def start(self) -> None:
|
|
27
|
+
self._started_monotonic = time.monotonic()
|
|
28
|
+
|
|
29
|
+
def exhausted(self) -> str | None:
|
|
30
|
+
"""Return which budget tripped (for ``stopped_by``), or ``None``."""
|
|
31
|
+
if self.max_actions > 0 and self.actions_taken >= self.max_actions:
|
|
32
|
+
return "max_actions"
|
|
33
|
+
if self.token_budget > 0 and self.tokens_used >= self.token_budget:
|
|
34
|
+
return "budget"
|
|
35
|
+
if (
|
|
36
|
+
self.time_budget_sec > 0
|
|
37
|
+
and self._started_monotonic > 0
|
|
38
|
+
and (time.monotonic() - self._started_monotonic) >= self.time_budget_sec
|
|
39
|
+
):
|
|
40
|
+
return "time_budget"
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class EvidenceBoard:
|
|
46
|
+
"""Accumulated evidence the judge reasons over, one decision turn at a time."""
|
|
47
|
+
|
|
48
|
+
protocol_summary: str = ""
|
|
49
|
+
data_summary: dict[str, Any] = field(default_factory=dict)
|
|
50
|
+
probe_findings: list[dict[str, Any]] = field(default_factory=list)
|
|
51
|
+
stats_findings: list[dict[str, Any]] = field(default_factory=list)
|
|
52
|
+
stats_confirmatory: bool = False
|
|
53
|
+
explore_takeaways: list[dict[str, Any]] = field(default_factory=list)
|
|
54
|
+
failure_modes: list[dict[str, Any]] = field(default_factory=list)
|
|
55
|
+
probe_search_findings: list[dict[str, Any]] = field(default_factory=list)
|
|
56
|
+
hypotheses: list[dict[str, Any]] = field(default_factory=list)
|
|
57
|
+
action_log: list[dict[str, Any]] = field(default_factory=list)
|
|
58
|
+
budget: BudgetState = field(default_factory=BudgetState)
|
|
59
|
+
|
|
60
|
+
def has_supported_hypothesis(self) -> bool:
|
|
61
|
+
return any(
|
|
62
|
+
h.get("status") == "supported" and h.get("is_consistent_with_protocol", True)
|
|
63
|
+
for h in self.hypotheses
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def calls_made(self, tool: str) -> int:
|
|
67
|
+
return sum(1 for a in self.action_log if a.get("tool") == tool)
|
|
68
|
+
|
|
69
|
+
def to_prompt(self, registry: "Any") -> str:
|
|
70
|
+
"""Render the board as the decision prompt's evidence section."""
|
|
71
|
+
lines: list[str] = []
|
|
72
|
+
if self.protocol_summary:
|
|
73
|
+
lines.append(f"PROTOCOL:\n{self.protocol_summary}\n")
|
|
74
|
+
if self.data_summary:
|
|
75
|
+
lines.append(f"DATA: {self.data_summary}\n")
|
|
76
|
+
|
|
77
|
+
lines.append(f"PROBE FINDINGS (M1): {self.probe_findings or 'none yet'}")
|
|
78
|
+
lines.append(
|
|
79
|
+
f"STATS FINDINGS (M2, confirmatory={self.stats_confirmatory}): "
|
|
80
|
+
f"{self.stats_findings or 'none yet'}"
|
|
81
|
+
)
|
|
82
|
+
if self.explore_takeaways:
|
|
83
|
+
lines.append(f"EXPLORE TAKEAWAYS: {self.explore_takeaways}")
|
|
84
|
+
if self.failure_modes:
|
|
85
|
+
lines.append(f"FAILURE MODES (clustered): {self.failure_modes}")
|
|
86
|
+
if self.probe_search_findings:
|
|
87
|
+
lines.append(f"PROBE SEARCH (newly synthesized failures): {self.probe_search_findings}")
|
|
88
|
+
lines.append(f"HYPOTHESES: {self.hypotheses or 'none yet'}")
|
|
89
|
+
|
|
90
|
+
if self.action_log:
|
|
91
|
+
lines.append("\nACTIONS TAKEN SO FAR:")
|
|
92
|
+
for a in self.action_log:
|
|
93
|
+
lines.append(
|
|
94
|
+
f" [{a['step']}] {a['tool']}({a.get('params', {})}) "
|
|
95
|
+
f"-> ok={a['ok']}: {a['summary']}"
|
|
96
|
+
)
|
|
97
|
+
else:
|
|
98
|
+
lines.append("\nACTIONS TAKEN SO FAR: none")
|
|
99
|
+
|
|
100
|
+
remaining = (
|
|
101
|
+
self.budget.max_actions - self.budget.actions_taken
|
|
102
|
+
if self.budget.max_actions > 0
|
|
103
|
+
else "unlimited"
|
|
104
|
+
)
|
|
105
|
+
lines.append(f"\nACTIONS REMAINING: {remaining}")
|
|
106
|
+
lines.append(f"\nAVAILABLE TOOLS:\n{registry.catalog_for_prompt(self)}")
|
|
107
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""AgenticDiagnoseLoop — an LLM-driven decision loop over the M1-M4 stages.
|
|
2
|
+
|
|
3
|
+
``VLDiagnoseLoop`` runs M1->M2->M3->M4 in a hardcoded sequence every cycle.
|
|
4
|
+
This loop keeps every stage (and the confirm-split / post-loop run_m5/run_fix
|
|
5
|
+
discipline) exactly as-is, but replaces the fixed sequence with a CLI judge
|
|
6
|
+
that decides, one tool call at a time, what to do next — probe, run stats,
|
|
7
|
+
explore the raw data, propose hypotheses, test one, fix, or stop. The host,
|
|
8
|
+
not the judge, enforces call caps, tool preconditions, and the pre-registered
|
|
9
|
+
stopping discipline (no declaring success without a tested, supported,
|
|
10
|
+
protocol-consistent hypothesis).
|
|
11
|
+
|
|
12
|
+
``VLDiagnoseLoop`` itself is untouched — this is a new mode, not a
|
|
13
|
+
replacement (see the module docstring in ``eval_agent/loop.py``).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
from typing import TYPE_CHECKING, Any
|
|
20
|
+
|
|
21
|
+
from evalrx.eval_agent.agentic.actions import decide
|
|
22
|
+
from evalrx.eval_agent.agentic.board import BudgetState, EvidenceBoard
|
|
23
|
+
from evalrx.eval_agent.agentic.tools import ToolRegistry, _RunState, build_default_registry
|
|
24
|
+
from evalrx.eval_agent.loop import VLDiagnoseLoop
|
|
25
|
+
from evalrx.eval_agent.loop_reports import VLDiagnoseReport
|
|
26
|
+
from evalrx.eval_agent.run_metadata import _attach_run_logger, _data_provenance, _run_config
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from evalrx.core.case import CaseBatch
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
_MAX_INVALID_STREAK = 3
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _protocol_summary(protocol: "Any | None") -> str:
|
|
37
|
+
if protocol is None:
|
|
38
|
+
return ""
|
|
39
|
+
bits = [f"description: {getattr(protocol, 'description', '')}"]
|
|
40
|
+
domain = getattr(protocol, "task_domain", None)
|
|
41
|
+
if domain:
|
|
42
|
+
bits.append(f"task_domain: {domain}")
|
|
43
|
+
patterns = getattr(protocol, "failure_patterns", None)
|
|
44
|
+
if patterns:
|
|
45
|
+
bits.append(f"failure_patterns: {patterns}")
|
|
46
|
+
return "\n".join(bits)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AgenticDiagnoseLoop(VLDiagnoseLoop):
|
|
50
|
+
"""M1-M4 investigation driven by judge decisions instead of a fixed cycle.
|
|
51
|
+
|
|
52
|
+
Reuses :class:`~evalrx.eval_agent.loop.VLDiagnoseLoop`'s constructor,
|
|
53
|
+
stage helpers (``_do_m1``..``_do_m4``), confirm-split, and post-loop
|
|
54
|
+
``run_m5``/``run_fix`` — only :meth:`run` differs.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
judge: CLI-backed judge that decides the next action each
|
|
58
|
+
turn (e.g. ``ClaudeModel()`` / ``AgyModel()``).
|
|
59
|
+
Required — this is a genuinely new capability, not
|
|
60
|
+
a Gemini-fallback stage judge.
|
|
61
|
+
explorer: Optional ``ExploratoryAnalysisAgent`` — when given,
|
|
62
|
+
registers the ``explore_data`` tool.
|
|
63
|
+
max_actions: Hard cap on judge decision turns (default 12).
|
|
64
|
+
time_budget_sec: Wall-clock cap for the whole run (0 = unlimited).
|
|
65
|
+
registry_factory: Advanced override — ``(loop, run_state) -> ToolRegistry``
|
|
66
|
+
instead of :func:`build_default_registry`.
|
|
67
|
+
**kwargs: Forwarded to ``VLDiagnoseLoop.__init__`` (model,
|
|
68
|
+
protocol, probe_agent, stats_agent, hypothesis_tester,
|
|
69
|
+
surgery_agent, fix_agent, store, token_budget,
|
|
70
|
+
confirm_split, run_logger, ...).
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
model: "Any",
|
|
76
|
+
protocol: "Any",
|
|
77
|
+
*,
|
|
78
|
+
judge: "Any",
|
|
79
|
+
explorer: "Any | None" = None,
|
|
80
|
+
max_actions: int = 12,
|
|
81
|
+
time_budget_sec: float = 0.0,
|
|
82
|
+
registry_factory: "Any | None" = None,
|
|
83
|
+
**kwargs: Any,
|
|
84
|
+
) -> None:
|
|
85
|
+
super().__init__(model, protocol, **kwargs)
|
|
86
|
+
if judge is None:
|
|
87
|
+
raise ValueError(
|
|
88
|
+
"AgenticDiagnoseLoop requires a decision judge, e.g. "
|
|
89
|
+
"judge=ClaudeModel() or judge=AgyModel()."
|
|
90
|
+
)
|
|
91
|
+
self.judge = judge
|
|
92
|
+
self.explorer = explorer
|
|
93
|
+
self.max_actions = max_actions
|
|
94
|
+
self.time_budget_sec = time_budget_sec
|
|
95
|
+
self._registry_factory = registry_factory
|
|
96
|
+
|
|
97
|
+
def run(self, data: "CaseBatch") -> VLDiagnoseReport:
|
|
98
|
+
"""Drive the judge-decided action loop to a stop or a budget limit."""
|
|
99
|
+
self._tokens_used = 0
|
|
100
|
+
timings: dict[str, float] = {}
|
|
101
|
+
|
|
102
|
+
explore, confirm = self._split_explore_confirm(data)
|
|
103
|
+
original_data = data
|
|
104
|
+
if confirm is not None:
|
|
105
|
+
data = explore
|
|
106
|
+
|
|
107
|
+
_attach_run_logger(self.run_logger, self.probe_agent, self.stats_agent)
|
|
108
|
+
if self.run_logger is not None:
|
|
109
|
+
# _run_config records the M3 *stage* judge (loop.diagnosis_agent.judge),
|
|
110
|
+
# not the decision judge driving this loop's own tool-calling — add it
|
|
111
|
+
# explicitly so the dashboard header can show who is actually deciding.
|
|
112
|
+
cfg = _run_config(self, data, loop_name="AgenticDiagnoseLoop")
|
|
113
|
+
cfg["decision_judge"] = repr(self.judge)
|
|
114
|
+
cfg["max_actions"] = self.max_actions
|
|
115
|
+
self.run_logger.log_run_start(cfg)
|
|
116
|
+
self.run_logger.log_cases(data)
|
|
117
|
+
|
|
118
|
+
board = EvidenceBoard(
|
|
119
|
+
protocol_summary=_protocol_summary(self.protocol),
|
|
120
|
+
data_summary=_data_provenance(data),
|
|
121
|
+
budget=BudgetState(
|
|
122
|
+
max_actions=self.max_actions,
|
|
123
|
+
token_budget=self.token_budget,
|
|
124
|
+
time_budget_sec=self.time_budget_sec,
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
board.budget.start()
|
|
128
|
+
|
|
129
|
+
state = _RunState(original_data=original_data, data=data, timings=timings)
|
|
130
|
+
registry: ToolRegistry = (
|
|
131
|
+
self._registry_factory(self, state)
|
|
132
|
+
if self._registry_factory is not None
|
|
133
|
+
else build_default_registry(self, state)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
stopped_by = "max_actions"
|
|
137
|
+
invalid_streak = 0
|
|
138
|
+
step = 0
|
|
139
|
+
while True:
|
|
140
|
+
reason = board.budget.exhausted()
|
|
141
|
+
if reason:
|
|
142
|
+
stopped_by = reason
|
|
143
|
+
break
|
|
144
|
+
|
|
145
|
+
action = decide(self.judge, board, registry, run_logger=self.run_logger, step=step)
|
|
146
|
+
board.budget.actions_taken += 1
|
|
147
|
+
invalid_streak = 0 if action.valid else invalid_streak + 1
|
|
148
|
+
|
|
149
|
+
outcome = registry.dispatch(action, board)
|
|
150
|
+
board.action_log.append({
|
|
151
|
+
"step": step, "tool": action.tool, "params": action.params,
|
|
152
|
+
"ok": outcome.ok, "summary": outcome.summary,
|
|
153
|
+
})
|
|
154
|
+
if self.run_logger is not None:
|
|
155
|
+
self.run_logger.log_agent_tool(
|
|
156
|
+
step, tool=action.tool, ok=outcome.ok,
|
|
157
|
+
summary=outcome.summary, error=outcome.error,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if action.tool == "stop" and outcome.ok:
|
|
161
|
+
stopped_by = "agent_stop"
|
|
162
|
+
step += 1
|
|
163
|
+
break
|
|
164
|
+
|
|
165
|
+
if invalid_streak >= _MAX_INVALID_STREAK:
|
|
166
|
+
logger.warning(
|
|
167
|
+
"AgenticDiagnoseLoop: %d consecutive invalid actions — stopping.",
|
|
168
|
+
invalid_streak,
|
|
169
|
+
)
|
|
170
|
+
stopped_by = "invalid_actions"
|
|
171
|
+
step += 1
|
|
172
|
+
break
|
|
173
|
+
|
|
174
|
+
step += 1
|
|
175
|
+
|
|
176
|
+
verified = self.hypothesis_tester.best_hypotheses(state.all_test_results)
|
|
177
|
+
report = VLDiagnoseReport(
|
|
178
|
+
cycles=step,
|
|
179
|
+
resolved=bool(verified),
|
|
180
|
+
stopped_by=stopped_by,
|
|
181
|
+
verified_hypotheses=verified,
|
|
182
|
+
final_hypotheses=state.all_hypotheses,
|
|
183
|
+
all_test_results=state.all_test_results,
|
|
184
|
+
final_stats_report=state.stats_report,
|
|
185
|
+
store=self.store,
|
|
186
|
+
_run_id=self._run_id,
|
|
187
|
+
)
|
|
188
|
+
if self.run_logger:
|
|
189
|
+
self.run_logger.log_loop_end(report, tokens_used=self._tokens_used, timings=timings)
|
|
190
|
+
return report
|