clear-eval 2.0.2__tar.gz → 2.0.4__tar.gz
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.
- {clear_eval-2.0.2 → clear_eval-2.0.4}/PKG-INFO +1 -1
- {clear_eval-2.0.2 → clear_eval-2.0.4}/pyproject.toml +1 -1
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/dashboard/agentic_workflow_dashboard.py +35 -8
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/dashboard/generate_static_dashboard.py +121 -7
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/argument_definitions.py +9 -1
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/build_json_results.py +131 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/full_traces_evaluation/clear_analysis/base_clear_runner.py +6 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/full_traces_evaluation/run_trajectory_evaluation_pipeline.py +33 -2
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/full_traces_evaluation/trace_evaluation/base_evaluator.py +18 -2
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/full_traces_evaluation/trace_evaluation/full_trajectory_evaluator.py +130 -215
- clear_eval-2.0.4/src/clear_eval/agentic/pipeline/full_traces_evaluation/trace_evaluation/full_trajectory_prompts.py +229 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/preprocess_traces/process_langfuse_traces.py +81 -6
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/run_clear_agentic_eval.py +11 -2
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/default_agentic_config.yaml +13 -1
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/utils.py +5 -1
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/constants.py +11 -1
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/eval_utils.py +58 -15
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/full_pipeline.py +11 -3
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/inference_utils/llm_client.py +153 -64
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/setup/default_config.yaml +1 -1
- clear_eval-2.0.4/src/clear_eval/pipeline/use_cases/tool_call_use_case.py +375 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/PKG-INFO +1 -1
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/SOURCES.txt +1 -0
- clear_eval-2.0.2/src/clear_eval/pipeline/use_cases/tool_call_use_case.py +0 -482
- {clear_eval-2.0.2 → clear_eval-2.0.4}/LICENSE +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/README.md +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/setup.cfg +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/__init__.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/dashboard/launch_dashboard.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/dashboard/path_analysis.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/create_ui_input.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/full_traces_evaluation/clear_analysis/issues_clear_runner.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/full_traces_evaluation/clear_analysis/root_cause_clear_runner.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/full_traces_evaluation/clear_analysis/run_clear_analysis.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/full_traces_evaluation/trace_evaluation/rubric_evaluator.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/full_traces_evaluation/trace_evaluation/rubric_generator.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/full_traces_evaluation/trace_evaluation/task_success_evaluator.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/preprocess_traces/compact_trace_formatter.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/preprocess_traces/preprocess_traces.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/preprocess_traces/process_mlflow_traces.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/preprocess_traces/trace_utils.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/run_clear_step_analysis.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/__init__.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/config_example.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/config_example.yaml +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/rh_config_example.yaml +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/test_tools_config_example.yaml +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/analysis_runner.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/args.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/cli.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/dashboard/__init__.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/dashboard/show_analysis_dashboard.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/load_ui.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/logging_config.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/__init__.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/caching_utils.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/config_loader.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/evaluation_criteria.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/external_judge.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/inference_utils/__init__.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/inference_utils/endpoint_backends.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/inference_utils/langchain_chat_models.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/propmts.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/setup/__init__.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/threading_utils.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/use_cases/__init__.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/use_cases/eval_use_case.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/use_cases/external_judge_use_case.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/use_cases/use_case_utils.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/__init__.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/pipeline_summary.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-006eee07eed4484592661b1bdba32a75.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-0e0148ed21eeebc046f03f63e4418723.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-0e1ef041647642c958a8aaa1892fdb88.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-4ae9a4305b7a0319ec516ef4eef4ae17.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-4eead8c00e900cc84f217a9a10724ecf.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-50adf266e9c1522dae57a6290a61596f.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-554015d661e4f69cfc629b6d46898421.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-55a6003784e34390f47c5c929d8f4db0.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-5818239708258c81fff3a4bb0b144802.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-5e63ae3886b225ae89a07b7fa4aafe0d.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-640f5750b635929a88547548f0cbb4da.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-6b2d02f30d4f0d3f4ca7d8f1469913ae.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-6c852575331f2e0a02a331d1ddaae59f.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-8e68becd37326e6fec6a7d3d3b47dbbb.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-b35ed52266fd023dd808950c6848f062.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-bccc245d6d47cdf730fd5db3028b52a3.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-d6010cdc5e36a50046fc10f33f0bc427.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-edadad8b3a67a24a43d145ae07c0c12a.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-f082801e1021022e0756e7d089f1558c.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/traces_data/tr-f34a9851b31fafffe527c5b2d6262de8.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_results/mlflow/my_experiment/unified_ui_results.zip +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/langfuse/434f3f96ac470e805428c4be45b4f2a3.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/langfuse/4c354cfb5b17f61af073df6796f2b236.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/langfuse/4deb34a4521fcedf92083d0a93fe11d4.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/langfuse/c685b7eb7826a87149bd2c42a50e47c5.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/langfuse/ebf1d6897941adb63bcc62aa7c50e8f8.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-006eee07eed4484592661b1bdba32a75.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-0e0148ed21eeebc046f03f63e4418723.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-0e1ef041647642c958a8aaa1892fdb88.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-4ae9a4305b7a0319ec516ef4eef4ae17.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-4eead8c00e900cc84f217a9a10724ecf.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-50adf266e9c1522dae57a6290a61596f.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-554015d661e4f69cfc629b6d46898421.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-55a6003784e34390f47c5c929d8f4db0.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-5818239708258c81fff3a4bb0b144802.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-5e63ae3886b225ae89a07b7fa4aafe0d.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-640f5750b635929a88547548f0cbb4da.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-6b2d02f30d4f0d3f4ca7d8f1469913ae.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-6c852575331f2e0a02a331d1ddaae59f.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-8e68becd37326e6fec6a7d3d3b47dbbb.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-b35ed52266fd023dd808950c6848f062.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-bccc245d6d47cdf730fd5db3028b52a3.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-d6010cdc5e36a50046fc10f33f0bc427.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-edadad8b3a67a24a43d145ae07c0c12a.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-f082801e1021022e0756e7d089f1558c.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/mlflow/tr-f34a9851b31fafffe527c5b2d6262de8.json +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/gsm8k/__init__.py +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/gsm8k/analysis_results_gsm8k_default.zip +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/gsm8k/gsm8k_default_predictions.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/tool_calls/tool_calls_sample_data.csv +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/dependency_links.txt +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/entry_points.txt +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/requires.txt +0 -0
- {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "clear_eval"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.4"
|
|
8
8
|
description = "A python API sdk facilitating Error Analysis via LLM-as-a-Judge"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Lilach Eden", email = "lilache@il.ibm.com"},
|
{clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/dashboard/agentic_workflow_dashboard.py
RENAMED
|
@@ -2174,7 +2174,8 @@ def main_page():
|
|
|
2174
2174
|
# Try to get dimensions from parsed_evaluation first (preferred structure)
|
|
2175
2175
|
step_quality = parsed_eval.get("step_quality_dimensions", {})
|
|
2176
2176
|
trajectory_dims = parsed_eval.get("trajectory_dimensions", {})
|
|
2177
|
-
|
|
2177
|
+
custom_dims = parsed_eval.get("dimensions", {})
|
|
2178
|
+
|
|
2178
2179
|
# Collect step quality dimension scores
|
|
2179
2180
|
for criterion, details in step_quality.items():
|
|
2180
2181
|
score = details.get("score")
|
|
@@ -2183,7 +2184,7 @@ def main_page():
|
|
|
2183
2184
|
all_dimension_scores[f"Step Quality: {criterion}"].append(float(score))
|
|
2184
2185
|
except (ValueError, TypeError):
|
|
2185
2186
|
print(f"⚠️ Warning: Could not convert score to float for {criterion}: {score}")
|
|
2186
|
-
|
|
2187
|
+
|
|
2187
2188
|
# Collect trace dimension scores
|
|
2188
2189
|
for criterion, details in trajectory_dims.items():
|
|
2189
2190
|
score = details.get("score")
|
|
@@ -2192,9 +2193,19 @@ def main_page():
|
|
|
2192
2193
|
all_dimension_scores[f"Trace: {criterion}"].append(float(score))
|
|
2193
2194
|
except (ValueError, TypeError):
|
|
2194
2195
|
print(f"⚠️ Warning: Could not convert score to float for {criterion}: {score}")
|
|
2195
|
-
|
|
2196
|
+
|
|
2197
|
+
# Collect custom flat dimension scores
|
|
2198
|
+
if custom_dims and not step_quality and not trajectory_dims:
|
|
2199
|
+
for criterion, details in custom_dims.items():
|
|
2200
|
+
score = details.get("score")
|
|
2201
|
+
if score is not None:
|
|
2202
|
+
try:
|
|
2203
|
+
all_dimension_scores[criterion].append(float(score))
|
|
2204
|
+
except (ValueError, TypeError):
|
|
2205
|
+
print(f"⚠️ Warning: Could not convert score to float for {criterion}: {score}")
|
|
2206
|
+
|
|
2196
2207
|
# Fallback: if no dimensions found in parsed_evaluation, try dimension_scores directly
|
|
2197
|
-
if not step_quality and not trajectory_dims and dimension_scores:
|
|
2208
|
+
if not step_quality and not trajectory_dims and not custom_dims and dimension_scores:
|
|
2198
2209
|
for criterion, score in dimension_scores.items():
|
|
2199
2210
|
if score is not None:
|
|
2200
2211
|
try:
|
|
@@ -3083,7 +3094,8 @@ def main_page():
|
|
|
3083
3094
|
# Separate step quality and trace dimensions
|
|
3084
3095
|
step_quality = parsed_eval.get("step_quality_dimensions", {})
|
|
3085
3096
|
trajectory_dims = parsed_eval.get("trajectory_dimensions", {})
|
|
3086
|
-
|
|
3097
|
+
custom_dims = parsed_eval.get("dimensions", {})
|
|
3098
|
+
|
|
3087
3099
|
if step_quality:
|
|
3088
3100
|
ui.html('<div style="font-weight: 600; color: #1E293B; margin: 12px 0 8px 0; font-size: 15px;">Step Quality Dimensions</div>')
|
|
3089
3101
|
for criterion, details in step_quality.items():
|
|
@@ -3091,7 +3103,7 @@ def main_page():
|
|
|
3091
3103
|
justification = details.get("justification", "No justification provided")
|
|
3092
3104
|
score_color = score_to_hex(score)
|
|
3093
3105
|
definition = get_dimension_definition(criterion)
|
|
3094
|
-
|
|
3106
|
+
|
|
3095
3107
|
with ui.row().classes("w-full items-center gap-2 mb-2"):
|
|
3096
3108
|
with ui.expansion(f"{criterion}: {score:.2f}", icon="check_circle").classes("flex-1").style(f"border-left: 4px solid {score_color};"):
|
|
3097
3109
|
ui.html(f"""
|
|
@@ -3101,7 +3113,7 @@ def main_page():
|
|
|
3101
3113
|
""")
|
|
3102
3114
|
with ui.button(icon="info", on_click=lambda: None).props("flat dense round size=sm").classes("text-blue-500"):
|
|
3103
3115
|
ui.tooltip(definition).classes("bg-slate-800 text-white text-sm max-w-md")
|
|
3104
|
-
|
|
3116
|
+
|
|
3105
3117
|
if trajectory_dims:
|
|
3106
3118
|
ui.html('<div style="font-weight: 600; color: #1E293B; margin: 20px 0 8px 0; font-size: 15px;">Trace-Level Dimensions</div>')
|
|
3107
3119
|
for criterion, details in trajectory_dims.items():
|
|
@@ -3109,7 +3121,7 @@ def main_page():
|
|
|
3109
3121
|
justification = details.get("justification", "No justification provided")
|
|
3110
3122
|
score_color = score_to_hex(score)
|
|
3111
3123
|
definition = get_dimension_definition(criterion)
|
|
3112
|
-
|
|
3124
|
+
|
|
3113
3125
|
with ui.row().classes("w-full items-center gap-2 mb-2"):
|
|
3114
3126
|
with ui.expansion(f"{criterion}: {score:.2f}", icon="check_circle").classes("flex-1").style(f"border-left: 4px solid {score_color};"):
|
|
3115
3127
|
ui.html(f"""
|
|
@@ -3120,6 +3132,21 @@ def main_page():
|
|
|
3120
3132
|
with ui.button(icon="info", on_click=lambda: None).props("flat dense round size=sm").classes("text-blue-500"):
|
|
3121
3133
|
ui.tooltip(definition).classes("bg-slate-800 text-white text-sm max-w-md")
|
|
3122
3134
|
|
|
3135
|
+
if custom_dims and not step_quality and not trajectory_dims:
|
|
3136
|
+
ui.html('<div style="font-weight: 600; color: #1E293B; margin: 12px 0 8px 0; font-size: 15px;">Evaluation Dimensions</div>')
|
|
3137
|
+
for criterion, details in custom_dims.items():
|
|
3138
|
+
score = float(details.get("score", 0))
|
|
3139
|
+
justification = details.get("justification", "No justification provided")
|
|
3140
|
+
score_color = score_to_hex(score)
|
|
3141
|
+
|
|
3142
|
+
with ui.row().classes("w-full items-center gap-2 mb-2"):
|
|
3143
|
+
with ui.expansion(f"{criterion}: {score:.2f}", icon="check_circle").classes("flex-1").style(f"border-left: 4px solid {score_color};"):
|
|
3144
|
+
ui.html(f"""
|
|
3145
|
+
<div style="padding: 8px; background: #FAFAFA; border-radius: 6px; color: #475569; font-size: 13px; line-height: 1.5;">
|
|
3146
|
+
{justification}
|
|
3147
|
+
</div>
|
|
3148
|
+
""")
|
|
3149
|
+
|
|
3123
3150
|
def render_rubric_evaluation(rubric_eval):
|
|
3124
3151
|
"""Render the rubric-based trace evaluation."""
|
|
3125
3152
|
# Overall score card
|
{clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/dashboard/generate_static_dashboard.py
RENAMED
|
@@ -42,7 +42,7 @@ def _maybe_parse_json_text(value):
|
|
|
42
42
|
return value
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def load_json_data(json_path,
|
|
45
|
+
def load_json_data(json_path, num_examples=3):
|
|
46
46
|
"""Load all dashboard data from clear_results.json and map it to the HTML data shape."""
|
|
47
47
|
json_path = Path(json_path)
|
|
48
48
|
|
|
@@ -79,8 +79,8 @@ def load_json_data(json_path, include_examples=True):
|
|
|
79
79
|
for issue in issues:
|
|
80
80
|
issue_text = issue.get("issue_text") or issue.get("issue_id") or ""
|
|
81
81
|
examples = []
|
|
82
|
-
if
|
|
83
|
-
for occurrence in issue.get("occurrences", [])[:
|
|
82
|
+
if num_examples:
|
|
83
|
+
for occurrence in issue.get("occurrences", [])[:num_examples]:
|
|
84
84
|
tasks_list.add(occurrence.get("trace_id"))
|
|
85
85
|
span_reference = occurrence.get("span_reference", {})
|
|
86
86
|
input_output_pair = occurrence.get("input_output_pair", {})
|
|
@@ -121,13 +121,22 @@ def load_json_data(json_path, include_examples=True):
|
|
|
121
121
|
key=lambda row: -row["count"],
|
|
122
122
|
) + [row for row in issues_table if row["is_no_issue"]]
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
entry = {
|
|
125
125
|
"total_evals": int(summary.get("total_interactions", 0)),
|
|
126
126
|
"avg_score": round(float(summary["avg_score"]), 2) if summary.get("avg_score") is not None else None,
|
|
127
127
|
"unique_issues": len(issues),
|
|
128
128
|
"unique_tasks": node_stats.get(agent_name, {}).get("unique_tasks", 0),
|
|
129
129
|
"issues_table": issues_table,
|
|
130
|
+
"recommendations": None,
|
|
130
131
|
}
|
|
132
|
+
if agent_type == "tools":
|
|
133
|
+
# Recommendations now travel inside the per-agent payload
|
|
134
|
+
# produced by build_json_results._aggregate_recommendations,
|
|
135
|
+
# so the dashboard just reads the key off the JSON.
|
|
136
|
+
recs = agent_type_payload.get("recommendations")
|
|
137
|
+
if recs and (recs.get("by_tool") or recs.get("by_system_prompt")):
|
|
138
|
+
entry["recommendations"] = recs
|
|
139
|
+
agents_data[agent_label] = entry
|
|
131
140
|
|
|
132
141
|
return {
|
|
133
142
|
"unique_tasks": int(stats.get("total_traces", 0)),
|
|
@@ -234,6 +243,26 @@ body{font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;background:
|
|
|
234
243
|
.chat-message-content{font-size:14px;color:#1E293B;white-space:pre-wrap;word-break:break-word;line-height:1.6}
|
|
235
244
|
.chat-message-tools{margin-top:10px;padding-top:10px;border-top:1px dashed #CBD5E1}
|
|
236
245
|
.expand-indicator{display:block;margin-top:8px;color:#6366F1;font-weight:800;font-size:13px}
|
|
246
|
+
.recs-block{margin-top:24px}
|
|
247
|
+
.recs-subblock{margin-top:14px}
|
|
248
|
+
.recs-subblock-title{font-size:14px;font-weight:800;color:#4338CA;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
|
|
249
|
+
.rec-card{background:#fff;border:1px solid #D8E1EE;border-radius:12px;padding:14px 16px;margin-bottom:12px;box-shadow:0 1px 3px rgba(15,23,42,.04)}
|
|
250
|
+
.rec-card-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:8px}
|
|
251
|
+
.rec-target-pill{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;padding:2px 8px;border-radius:999px;background:#EEF2FF;color:#4338CA}
|
|
252
|
+
.rec-target-pill.system{background:#DBEAFE;color:#1D4ED8}
|
|
253
|
+
.rec-target-pill.tool{background:#DCFCE7;color:#166534}
|
|
254
|
+
.rec-target-pill.param{background:#FEF3C7;color:#92400E}
|
|
255
|
+
.rec-scope{font-size:13px;color:#334155;font-weight:600}
|
|
256
|
+
.rec-importance{margin-left:auto;font-size:12px;font-weight:700;color:#6366F1}
|
|
257
|
+
.rec-count{font-size:12px;color:#64748B}
|
|
258
|
+
.rec-rationale{font-size:13px;color:#475569;line-height:1.6;margin-bottom:10px}
|
|
259
|
+
.rec-diff{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:12.5px;background:#0F172A;color:#E2E8F0;padding:12px 14px;border-radius:10px;overflow:auto;max-height:260px;white-space:pre;line-height:1.5}
|
|
260
|
+
.rec-diff .rec-line-add{color:#86EFAC}
|
|
261
|
+
.rec-diff .rec-line-del{color:#FCA5A5}
|
|
262
|
+
.rec-diff .rec-line-hunk{color:#93C5FD}
|
|
263
|
+
.rec-tool-header{display:flex;align-items:baseline;gap:12px;margin:14px 0 6px}
|
|
264
|
+
.rec-tool-name{font-size:15px;font-weight:800;color:#0F172A}
|
|
265
|
+
.rec-tool-count{font-size:12px;color:#64748B}
|
|
237
266
|
@media(max-width:768px){.container{padding:16px}.dashboard-header{padding:24px}.agent-metrics-grid{grid-template-columns:repeat(2,1fr)}}
|
|
238
267
|
.info-tooltip{position:relative;display:inline-block}
|
|
239
268
|
.info-tooltip-trigger{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;margin-left:6px;border-radius:50%;background:#E2E8F0;color:#475569;font-size:11px;font-weight:700;cursor:help;vertical-align:middle}
|
|
@@ -567,7 +596,8 @@ function heatFg(val, min, max) {
|
|
|
567
596
|
<div class="agent-metric"><div class="agent-metric-label">#Traces</div><div class="agent-metric-value">${ad.unique_tasks}</div></div>
|
|
568
597
|
</div></div>
|
|
569
598
|
<div class="section-header" style="margin-top:0"><div class="section-title">Discovered Issues</div><div class="section-subtitle">Recurring problems identified in this agent's outputs <span class="info-tooltip"><span class="info-tooltip-trigger" tabindex="0" aria-label="Issue matching explanation">i</span><span class="info-tooltip-bubble">Each llm call can be matched to any number of discovered issues, or to no issues.</span></span></div></div>
|
|
570
|
-
${tableHtml}
|
|
599
|
+
${tableHtml}
|
|
600
|
+
${renderRecommendations(ad.recommendations)}`;
|
|
571
601
|
container.appendChild(sec);
|
|
572
602
|
}
|
|
573
603
|
})();
|
|
@@ -579,14 +609,98 @@ function toggleIssueExamples(id, rowEl){
|
|
|
579
609
|
const indicator=rowEl.querySelector('.expand-indicator');
|
|
580
610
|
if(indicator) indicator.textContent=isHidden?'▾ Examples':'▸ Examples';
|
|
581
611
|
}
|
|
612
|
+
|
|
613
|
+
// ─── SPARC recommendations renderer ─────────────────────────────────────
|
|
614
|
+
function escHtml(s){return esc(s||'');}
|
|
615
|
+
function renderDiff(raw){
|
|
616
|
+
if(!raw) return '';
|
|
617
|
+
const lines=String(raw).split('\n');
|
|
618
|
+
const out=lines.map(line=>{
|
|
619
|
+
const safe=escHtml(line);
|
|
620
|
+
if(line.startsWith('+++')||line.startsWith('---')) return `<span class="rec-line-hunk">${safe}</span>`;
|
|
621
|
+
if(line.startsWith('@@')) return `<span class="rec-line-hunk">${safe}</span>`;
|
|
622
|
+
if(line.startsWith('+')) return `<span class="rec-line-add">${safe}</span>`;
|
|
623
|
+
if(line.startsWith('-')) return `<span class="rec-line-del">${safe}</span>`;
|
|
624
|
+
return safe;
|
|
625
|
+
});
|
|
626
|
+
return `<div class="rec-diff">${out.join('\n')}</div>`;
|
|
627
|
+
}
|
|
628
|
+
function targetPillClass(target){
|
|
629
|
+
if(target==='system_prompt') return 'system';
|
|
630
|
+
if(target==='tool_description') return 'tool';
|
|
631
|
+
if(target==='parameter_description'||target==='parameter_examples') return 'param';
|
|
632
|
+
return '';
|
|
633
|
+
}
|
|
634
|
+
function renderRecCard(rec, scope){
|
|
635
|
+
const imp=(typeof rec.importance_mean==='number')?rec.importance_mean:(rec.importance||0);
|
|
636
|
+
const count=rec.count||1;
|
|
637
|
+
const cls=targetPillClass(rec.target||'system_prompt');
|
|
638
|
+
const rationale=rec.rationale?`<div class="rec-rationale">${escHtml(rec.rationale)}</div>`:'';
|
|
639
|
+
const scopeLabel=scope?`<span class="rec-scope">${escHtml(scope)}</span>`:'';
|
|
640
|
+
return `<div class="rec-card">
|
|
641
|
+
<div class="rec-card-head">
|
|
642
|
+
<span class="rec-target-pill ${cls}">${escHtml(rec.target||'')}</span>
|
|
643
|
+
${scopeLabel}
|
|
644
|
+
<span class="rec-count">×${count}</span>
|
|
645
|
+
<span class="rec-importance">importance ${imp.toFixed(2)}</span>
|
|
646
|
+
</div>
|
|
647
|
+
${rationale}
|
|
648
|
+
${renderDiff(rec.diff)}
|
|
649
|
+
</div>`;
|
|
650
|
+
}
|
|
651
|
+
function renderRecommendations(recs){
|
|
652
|
+
if(!recs) return '';
|
|
653
|
+
const byTool=recs.by_tool||{};
|
|
654
|
+
const bySystem=recs.by_system_prompt||[];
|
|
655
|
+
const toolEntries=Object.entries(byTool);
|
|
656
|
+
if(!toolEntries.length && !bySystem.length) return '';
|
|
657
|
+
|
|
658
|
+
const header=`<div class="section-header" style="margin-top:28px">
|
|
659
|
+
<div class="section-title">SPARC Recommendations</div>
|
|
660
|
+
<div class="section-subtitle">Unified-diff suggestions derived from the call judgments (${recs.total||0} recommendation occurrence(s))</div>
|
|
661
|
+
</div>`;
|
|
662
|
+
|
|
663
|
+
let body='<div class="recs-block">';
|
|
664
|
+
|
|
665
|
+
if(bySystem.length){
|
|
666
|
+
body+='<div class="recs-subblock"><div class="recs-subblock-title">System Prompt</div>';
|
|
667
|
+
for(const r of bySystem){ body+=renderRecCard(r, null); }
|
|
668
|
+
body+='</div>';
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if(toolEntries.length){
|
|
672
|
+
body+='<div class="recs-subblock"><div class="recs-subblock-title">Per-Tool Suggestions</div>';
|
|
673
|
+
// Sort tools by total recommendations descending
|
|
674
|
+
toolEntries.sort((a,b)=>{
|
|
675
|
+
const ca=a[1].reduce((s,x)=>s+(x.count||1),0);
|
|
676
|
+
const cb=b[1].reduce((s,x)=>s+(x.count||1),0);
|
|
677
|
+
return cb-ca || a[0].localeCompare(b[0]);
|
|
678
|
+
});
|
|
679
|
+
for(const [toolName, items] of toolEntries){
|
|
680
|
+
const total=items.reduce((s,x)=>s+(x.count||1),0);
|
|
681
|
+
body+=`<div class="rec-tool-header">
|
|
682
|
+
<span class="rec-tool-name">${escHtml(toolName)}</span>
|
|
683
|
+
<span class="rec-tool-count">${items.length} distinct suggestion(s), ${total} occurrence(s)</span>
|
|
684
|
+
</div>`;
|
|
685
|
+
for(const r of items){
|
|
686
|
+
const scope=r.parameter_name?`${toolName} · ${r.parameter_name}`:toolName;
|
|
687
|
+
body+=renderRecCard(r, scope);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
body+='</div>';
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
body+='</div>';
|
|
694
|
+
return header+body;
|
|
695
|
+
}
|
|
582
696
|
</script>
|
|
583
697
|
</body>
|
|
584
698
|
</html>"""
|
|
585
699
|
|
|
586
700
|
|
|
587
|
-
def generate_html(json_path, output_path=None,
|
|
701
|
+
def generate_html(json_path, output_path=None, num_examples=3):
|
|
588
702
|
"""Generate static HTML dashboard from a clear_results.json file."""
|
|
589
|
-
data = load_json_data(json_path,
|
|
703
|
+
data = load_json_data(json_path, num_examples=num_examples)
|
|
590
704
|
logging.info(f"Generating Static HTML report from {json_path}")
|
|
591
705
|
|
|
592
706
|
input_path = Path(json_path)
|
{clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/argument_definitions.py
RENAMED
|
@@ -10,7 +10,7 @@ reusable argument groups that can be composed based on each script's needs.
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import argparse
|
|
13
|
-
from clear_eval.args import str2bool
|
|
13
|
+
from clear_eval.args import str2bool, parse_dict
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def add_agentic_pipeline_args(parser: argparse.ArgumentParser) -> None:
|
|
@@ -132,6 +132,14 @@ def add_full_trajectory_args(parser: argparse.ArgumentParser) -> None:
|
|
|
132
132
|
type=int,
|
|
133
133
|
help="Model context window size (for full trajectory)"
|
|
134
134
|
)
|
|
135
|
+
group.add_argument(
|
|
136
|
+
"--full-trace-evaluation-criteria",
|
|
137
|
+
type=parse_dict,
|
|
138
|
+
default=None,
|
|
139
|
+
help="JSON dict of evaluation criteria for full trace evaluation. "
|
|
140
|
+
"Replaces all default dimensions when set. "
|
|
141
|
+
"Example: '{\"Correctness\": \"Accurate results\", \"Efficiency\": \"No redundant steps\"}'"
|
|
142
|
+
)
|
|
135
143
|
# Note: --max-files is defined in add_agentic_pipeline_args() (shared across pipelines)
|
|
136
144
|
# Do not add it here to avoid conflicts
|
|
137
145
|
|
|
@@ -9,10 +9,12 @@ Creates a structured JSON with all issues mapped to spans, including:
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import ast
|
|
12
|
+
import hashlib
|
|
12
13
|
import json
|
|
13
14
|
import logging
|
|
14
15
|
import math
|
|
15
16
|
import os
|
|
17
|
+
import re
|
|
16
18
|
from collections import defaultdict
|
|
17
19
|
from datetime import datetime
|
|
18
20
|
from pathlib import Path
|
|
@@ -28,6 +30,9 @@ logger = logging.getLogger(__name__)
|
|
|
28
30
|
# Must match the subdir name used in run_clear_step_analysis.run_analysis_for_agent.
|
|
29
31
|
TOOL_CALLS_SUBDIR = "tool_calls"
|
|
30
32
|
|
|
33
|
+
# SPARC recommendation targets that scope to a specific tool (vs. system prompt)
|
|
34
|
+
_TOOL_SCOPED_TARGETS = ("tool_description", "parameter_description", "parameter_examples")
|
|
35
|
+
|
|
31
36
|
|
|
32
37
|
def build_workflow_graph(traj_df):
|
|
33
38
|
"""Build workflow graph data from trajectory dataframe."""
|
|
@@ -142,6 +147,126 @@ def _parse_meta_data(meta_data_str):
|
|
|
142
147
|
return {}
|
|
143
148
|
|
|
144
149
|
|
|
150
|
+
_WHITESPACE_RE = re.compile(r"\s+")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _normalize_diff(diff: str) -> str:
|
|
154
|
+
"""Whitespace-normalize a unified diff for stable hashing/dedup."""
|
|
155
|
+
if not isinstance(diff, str):
|
|
156
|
+
return ""
|
|
157
|
+
return _WHITESPACE_RE.sub(" ", diff).strip()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _diff_hash(diff: str) -> str:
|
|
161
|
+
"""Stable short hash of the normalized diff text for grouping."""
|
|
162
|
+
return hashlib.sha1(_normalize_diff(diff).encode("utf-8")).hexdigest()[:16]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _parse_recs(cell: Any) -> List[Dict[str, Any]]:
|
|
166
|
+
"""Parse a ``sparc_recommendations`` cell into a list of rec dicts.
|
|
167
|
+
|
|
168
|
+
Accepts JSON arrays (the canonical form), already-parsed lists, or
|
|
169
|
+
``"[]"``/empty/NaN. Anything else returns an empty list."""
|
|
170
|
+
if cell is None or (isinstance(cell, float) and pd.isna(cell)):
|
|
171
|
+
return []
|
|
172
|
+
if isinstance(cell, list):
|
|
173
|
+
return [r for r in cell if isinstance(r, dict)]
|
|
174
|
+
if isinstance(cell, str) and cell.strip():
|
|
175
|
+
try:
|
|
176
|
+
parsed = json.loads(cell)
|
|
177
|
+
except (json.JSONDecodeError, TypeError):
|
|
178
|
+
return []
|
|
179
|
+
if isinstance(parsed, list):
|
|
180
|
+
return [r for r in parsed if isinstance(r, dict)]
|
|
181
|
+
return []
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _aggregate_recommendations(results_df: pd.DataFrame) -> Optional[Dict[str, Any]]:
|
|
185
|
+
"""Group per-row SPARC recommendations into ``by_tool`` and
|
|
186
|
+
``by_system_prompt`` buckets.
|
|
187
|
+
|
|
188
|
+
Returns ``None`` if the DataFrame has no ``sparc_recommendations`` column
|
|
189
|
+
or if no row produced any recommendation (runtime mode / static-only)."""
|
|
190
|
+
if "sparc_recommendations" not in results_df.columns:
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
# by_tool: {tool_name: {hash: accumulator}}
|
|
194
|
+
by_tool: Dict[str, Dict[str, Dict[str, Any]]] = defaultdict(dict)
|
|
195
|
+
# by_system_prompt: {hash: accumulator}
|
|
196
|
+
by_system: Dict[str, Dict[str, Any]] = {}
|
|
197
|
+
total = 0
|
|
198
|
+
|
|
199
|
+
for _, row in results_df.iterrows():
|
|
200
|
+
for rec in _parse_recs(row.get("sparc_recommendations")):
|
|
201
|
+
target = rec.get("target")
|
|
202
|
+
diff = rec.get("diff") or ""
|
|
203
|
+
if not target or not diff:
|
|
204
|
+
continue
|
|
205
|
+
try:
|
|
206
|
+
importance = max(0.0, min(1.0, float(rec.get("importance", 0.0))))
|
|
207
|
+
except (TypeError, ValueError):
|
|
208
|
+
importance = 0.0
|
|
209
|
+
h = _diff_hash(diff)
|
|
210
|
+
entry_template = {
|
|
211
|
+
"hash": h,
|
|
212
|
+
"target": target,
|
|
213
|
+
"diff": diff,
|
|
214
|
+
"rationale": rec.get("rationale", ""),
|
|
215
|
+
"tool_name": rec.get("tool_name"),
|
|
216
|
+
"parameter_name": rec.get("parameter_name"),
|
|
217
|
+
"count": 0,
|
|
218
|
+
"importance_sum": 0.0,
|
|
219
|
+
"importance_max": 0.0,
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if target == "system_prompt":
|
|
223
|
+
bucket = by_system.setdefault(h, dict(entry_template))
|
|
224
|
+
elif target in _TOOL_SCOPED_TARGETS:
|
|
225
|
+
tool_name = rec.get("tool_name") or "<unknown>"
|
|
226
|
+
bucket = by_tool[tool_name].setdefault(h, dict(entry_template))
|
|
227
|
+
else:
|
|
228
|
+
continue
|
|
229
|
+
|
|
230
|
+
bucket["count"] += 1
|
|
231
|
+
bucket["importance_sum"] += importance
|
|
232
|
+
if importance > bucket["importance_max"]:
|
|
233
|
+
bucket["importance_max"] = importance
|
|
234
|
+
total += 1
|
|
235
|
+
|
|
236
|
+
if total == 0:
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
def _finalize(entries: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
240
|
+
out = []
|
|
241
|
+
for e in entries:
|
|
242
|
+
mean = e["importance_sum"] / e["count"] if e["count"] else 0.0
|
|
243
|
+
out.append({
|
|
244
|
+
"hash": e["hash"],
|
|
245
|
+
"target": e["target"],
|
|
246
|
+
"diff": e["diff"],
|
|
247
|
+
"rationale": e["rationale"],
|
|
248
|
+
"tool_name": e["tool_name"],
|
|
249
|
+
"parameter_name": e["parameter_name"],
|
|
250
|
+
"count": e["count"],
|
|
251
|
+
"importance_mean": round(mean, 4),
|
|
252
|
+
"importance_max": round(e["importance_max"], 4),
|
|
253
|
+
})
|
|
254
|
+
out.sort(key=lambda r: (-r["importance_mean"], -r["count"], r["target"]))
|
|
255
|
+
return out
|
|
256
|
+
|
|
257
|
+
by_tool_out: Dict[str, List[Dict[str, Any]]] = {
|
|
258
|
+
tool: _finalize(list(entries.values()))
|
|
259
|
+
for tool, entries in by_tool.items()
|
|
260
|
+
}
|
|
261
|
+
by_system_out = _finalize(list(by_system.values()))
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
"by_tool": by_tool_out,
|
|
265
|
+
"by_system_prompt": by_system_out,
|
|
266
|
+
"total": total,
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
|
|
145
270
|
def _parse_issues_list(recurring_issues_str):
|
|
146
271
|
"""Parse recurring issues string into a list."""
|
|
147
272
|
if pd.isna(recurring_issues_str) or not recurring_issues_str:
|
|
@@ -315,6 +440,12 @@ def _process_results_dir(
|
|
|
315
440
|
|
|
316
441
|
result["no_issues"] = no_issue_spans
|
|
317
442
|
|
|
443
|
+
# Aggregate SPARC actionable recommendations (tool-call results only carry
|
|
444
|
+
# the column; reasoning_eval CSVs don't, so this is a no-op there).
|
|
445
|
+
recs = _aggregate_recommendations(results_df)
|
|
446
|
+
if recs is not None:
|
|
447
|
+
result["recommendations"] = recs
|
|
448
|
+
|
|
318
449
|
# Attach scores and weighted severity for pass/fail calculation
|
|
319
450
|
result["_scores"] = [_safe_float(row.get('score', 0)) for _, row in results_df.iterrows()]
|
|
320
451
|
result["_weighted_severity"] = sum(
|
|
@@ -44,6 +44,7 @@ class BaseClearRunner(ABC):
|
|
|
44
44
|
output_dir: Path,
|
|
45
45
|
inference_config: InferenceConfig,
|
|
46
46
|
overwrite: bool = False,
|
|
47
|
+
predefined_issues: list = None,
|
|
47
48
|
):
|
|
48
49
|
"""
|
|
49
50
|
Initialize CLEAR runner.
|
|
@@ -53,11 +54,13 @@ class BaseClearRunner(ABC):
|
|
|
53
54
|
output_dir: Base directory for CLEAR analysis outputs
|
|
54
55
|
inference_config: LLM inference configuration
|
|
55
56
|
overwrite: Whether to overwrite existing CLEAR results
|
|
57
|
+
predefined_issues: Predefined issues list to skip issue discovery
|
|
56
58
|
"""
|
|
57
59
|
self.eval_results_dir = Path(eval_results_dir)
|
|
58
60
|
self.output_dir = Path(output_dir)
|
|
59
61
|
self.inference_config = inference_config
|
|
60
62
|
self.overwrite = overwrite
|
|
63
|
+
self.predefined_issues = predefined_issues
|
|
61
64
|
|
|
62
65
|
@abstractmethod
|
|
63
66
|
def get_source_name(self) -> str:
|
|
@@ -219,6 +222,9 @@ class BaseClearRunner(ABC):
|
|
|
219
222
|
"endpoint_url": self.inference_config.endpoint_url,
|
|
220
223
|
}
|
|
221
224
|
|
|
225
|
+
if self.predefined_issues:
|
|
226
|
+
analysis_kwargs["predefined_issues"] = self.predefined_issues
|
|
227
|
+
|
|
222
228
|
run_clear_eval_aggregation(**analysis_kwargs)
|
|
223
229
|
logger.info(f"Completed: {group_key}")
|
|
224
230
|
return True
|
|
@@ -99,6 +99,9 @@ Arguments:
|
|
|
99
99
|
--overwrite: Re-run even if results exist
|
|
100
100
|
--max-workers: Number of parallel workers (default: 7)
|
|
101
101
|
--max-files: Limit files to process (for testing)
|
|
102
|
+
--full-trace-evaluation-criteria: JSON dict of custom evaluation criteria
|
|
103
|
+
Replaces all default dimensions when set
|
|
104
|
+
--predefined-issues: Predefined issues list for CLEAR analysis
|
|
102
105
|
|
|
103
106
|
Output Structure:
|
|
104
107
|
output_dir/
|
|
@@ -273,6 +276,7 @@ def run_full_trajectory_evaluation(
|
|
|
273
276
|
overwrite: bool,
|
|
274
277
|
max_workers: int,
|
|
275
278
|
max_files: Optional[int],
|
|
279
|
+
full_trace_evaluation_criteria: Optional[dict] = None,
|
|
276
280
|
) -> bool:
|
|
277
281
|
"""Run full trajectory evaluation."""
|
|
278
282
|
logger.info("=" * 80)
|
|
@@ -288,6 +292,7 @@ def run_full_trajectory_evaluation(
|
|
|
288
292
|
overwrite=overwrite,
|
|
289
293
|
max_workers=max_workers,
|
|
290
294
|
max_files=max_files,
|
|
295
|
+
full_trace_evaluation_criteria=full_trace_evaluation_criteria,
|
|
291
296
|
)
|
|
292
297
|
evaluator.run_pipeline()
|
|
293
298
|
logger.info("Full trajectory evaluation completed successfully")
|
|
@@ -378,8 +383,19 @@ def run_clear_analysis(
|
|
|
378
383
|
clear_types: List[str],
|
|
379
384
|
inference_config: InferenceConfig,
|
|
380
385
|
overwrite: bool,
|
|
386
|
+
predefined_issues: Optional[list] = None,
|
|
381
387
|
) -> bool:
|
|
382
|
-
"""
|
|
388
|
+
"""
|
|
389
|
+
Run CLEAR analysis on evaluation results.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
eval_results_dir: Directory containing evaluation result files
|
|
393
|
+
clear_output_dir: Output directory for CLEAR analysis
|
|
394
|
+
clear_types: List of CLEAR analysis types to run
|
|
395
|
+
inference_config: LLM inference configuration
|
|
396
|
+
overwrite: Whether to overwrite existing results
|
|
397
|
+
predefined_issues: If set, skip issue discovery and use these issues directly
|
|
398
|
+
"""
|
|
383
399
|
if not clear_types:
|
|
384
400
|
logger.info("Skipping CLEAR analysis (none requested)")
|
|
385
401
|
return True
|
|
@@ -400,6 +416,7 @@ def run_clear_analysis(
|
|
|
400
416
|
output_dir=clear_output_dir,
|
|
401
417
|
inference_config=inference_config,
|
|
402
418
|
overwrite=overwrite,
|
|
419
|
+
predefined_issues=predefined_issues,
|
|
403
420
|
)
|
|
404
421
|
runner.run_analysis()
|
|
405
422
|
logger.info("Root cause CLEAR analysis completed successfully")
|
|
@@ -416,6 +433,7 @@ def run_clear_analysis(
|
|
|
416
433
|
output_dir=clear_output_dir,
|
|
417
434
|
inference_config=inference_config,
|
|
418
435
|
overwrite=overwrite,
|
|
436
|
+
predefined_issues=predefined_issues,
|
|
419
437
|
)
|
|
420
438
|
runner.run_analysis()
|
|
421
439
|
logger.info("Issues CLEAR analysis completed successfully")
|
|
@@ -517,6 +535,8 @@ def run_trajectory_evaluation_pipeline(
|
|
|
517
535
|
overwrite: bool = False,
|
|
518
536
|
max_workers: int = 10,
|
|
519
537
|
max_files: Optional[int] = None,
|
|
538
|
+
predefined_issues: Optional[list] = None,
|
|
539
|
+
full_trace_evaluation_criteria: Optional[dict] = None,
|
|
520
540
|
) -> tuple[List[str], List[str]]:
|
|
521
541
|
"""
|
|
522
542
|
Run trajectory evaluation pipeline on CSV trajectory data.
|
|
@@ -536,6 +556,8 @@ def run_trajectory_evaluation_pipeline(
|
|
|
536
556
|
overwrite: Re-run even if results exist
|
|
537
557
|
max_workers: Number of parallel workers
|
|
538
558
|
max_files: Limit files to process
|
|
559
|
+
predefined_issues: Predefined issues list to skip issue discovery in CLEAR analysis
|
|
560
|
+
full_trace_evaluation_criteria: Custom flat evaluation criteria dict for full trajectory eval
|
|
539
561
|
|
|
540
562
|
Returns:
|
|
541
563
|
Tuple of (completed_evals, failed_evals)
|
|
@@ -634,7 +656,10 @@ def run_trajectory_evaluation_pipeline(
|
|
|
634
656
|
failed_evals.append(EVAL_TYPE_TASK_SUCCESS)
|
|
635
657
|
|
|
636
658
|
if EVAL_TYPE_FULL_TRAJECTORY in eval_types:
|
|
637
|
-
if run_full_trajectory_evaluation(
|
|
659
|
+
if run_full_trajectory_evaluation(
|
|
660
|
+
**eval_kwargs,
|
|
661
|
+
full_trace_evaluation_criteria=full_trace_evaluation_criteria,
|
|
662
|
+
):
|
|
638
663
|
completed_evals.append(EVAL_TYPE_FULL_TRAJECTORY)
|
|
639
664
|
else:
|
|
640
665
|
failed_evals.append(EVAL_TYPE_FULL_TRAJECTORY)
|
|
@@ -662,6 +687,7 @@ def run_trajectory_evaluation_pipeline(
|
|
|
662
687
|
clear_types=clear_types,
|
|
663
688
|
inference_config=inference_config,
|
|
664
689
|
overwrite=overwrite,
|
|
690
|
+
predefined_issues=predefined_issues,
|
|
665
691
|
)
|
|
666
692
|
|
|
667
693
|
# Print summary
|
|
@@ -695,6 +721,9 @@ def main():
|
|
|
695
721
|
config.get('run_name')
|
|
696
722
|
)
|
|
697
723
|
|
|
724
|
+
# Update config with resolved run_name so downstream components use the same name
|
|
725
|
+
config['run_name'] = run_name
|
|
726
|
+
|
|
698
727
|
# Convert paths
|
|
699
728
|
traj_input_dir = Path(config['data_dir'])
|
|
700
729
|
rubric_dir = Path(config['rubric_dir']) if config.get('rubric_dir') else None
|
|
@@ -744,6 +773,8 @@ def main():
|
|
|
744
773
|
overwrite=config.get('overwrite'),
|
|
745
774
|
max_workers=config.get('max_workers'),
|
|
746
775
|
max_files=config.get('max_files'),
|
|
776
|
+
predefined_issues=config.get('predefined_issues'),
|
|
777
|
+
full_trace_evaluation_criteria=config.get('full_trace_evaluation_criteria'),
|
|
747
778
|
)
|
|
748
779
|
|
|
749
780
|
if failed_evals:
|