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.
Files changed (125) hide show
  1. {clear_eval-2.0.2 → clear_eval-2.0.4}/PKG-INFO +1 -1
  2. {clear_eval-2.0.2 → clear_eval-2.0.4}/pyproject.toml +1 -1
  3. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/dashboard/agentic_workflow_dashboard.py +35 -8
  4. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/dashboard/generate_static_dashboard.py +121 -7
  5. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/argument_definitions.py +9 -1
  6. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/build_json_results.py +131 -0
  7. {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
  8. {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
  9. {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
  10. {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
  11. clear_eval-2.0.4/src/clear_eval/agentic/pipeline/full_traces_evaluation/trace_evaluation/full_trajectory_prompts.py +229 -0
  12. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/preprocess_traces/process_langfuse_traces.py +81 -6
  13. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/run_clear_agentic_eval.py +11 -2
  14. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/default_agentic_config.yaml +13 -1
  15. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/utils.py +5 -1
  16. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/constants.py +11 -1
  17. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/eval_utils.py +58 -15
  18. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/full_pipeline.py +11 -3
  19. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/inference_utils/llm_client.py +153 -64
  20. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/setup/default_config.yaml +1 -1
  21. clear_eval-2.0.4/src/clear_eval/pipeline/use_cases/tool_call_use_case.py +375 -0
  22. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/PKG-INFO +1 -1
  23. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/SOURCES.txt +1 -0
  24. clear_eval-2.0.2/src/clear_eval/pipeline/use_cases/tool_call_use_case.py +0 -482
  25. {clear_eval-2.0.2 → clear_eval-2.0.4}/LICENSE +0 -0
  26. {clear_eval-2.0.2 → clear_eval-2.0.4}/README.md +0 -0
  27. {clear_eval-2.0.2 → clear_eval-2.0.4}/setup.cfg +0 -0
  28. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/__init__.py +0 -0
  29. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/dashboard/launch_dashboard.py +0 -0
  30. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/dashboard/path_analysis.py +0 -0
  31. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/create_ui_input.py +0 -0
  32. {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
  33. {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
  34. {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
  35. {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
  36. {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
  37. {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
  38. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/preprocess_traces/compact_trace_formatter.py +0 -0
  39. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/preprocess_traces/preprocess_traces.py +0 -0
  40. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/preprocess_traces/process_mlflow_traces.py +0 -0
  41. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/preprocess_traces/trace_utils.py +0 -0
  42. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/run_clear_step_analysis.py +0 -0
  43. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/__init__.py +0 -0
  44. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/config_example.json +0 -0
  45. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/config_example.yaml +0 -0
  46. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/rh_config_example.yaml +0 -0
  47. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/agentic/pipeline/setup/test_tools_config_example.yaml +0 -0
  48. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/analysis_runner.py +0 -0
  49. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/args.py +0 -0
  50. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/cli.py +0 -0
  51. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/dashboard/__init__.py +0 -0
  52. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/dashboard/show_analysis_dashboard.py +0 -0
  53. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/load_ui.py +0 -0
  54. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/logging_config.py +0 -0
  55. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/__init__.py +0 -0
  56. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/caching_utils.py +0 -0
  57. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/config_loader.py +0 -0
  58. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/evaluation_criteria.py +0 -0
  59. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/external_judge.py +0 -0
  60. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/inference_utils/__init__.py +0 -0
  61. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/inference_utils/endpoint_backends.py +0 -0
  62. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/inference_utils/langchain_chat_models.py +0 -0
  63. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/propmts.py +0 -0
  64. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/setup/__init__.py +0 -0
  65. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/threading_utils.py +0 -0
  66. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/use_cases/__init__.py +0 -0
  67. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/use_cases/eval_use_case.py +0 -0
  68. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/use_cases/external_judge_use_case.py +0 -0
  69. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/pipeline/use_cases/use_case_utils.py +0 -0
  70. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/__init__.py +0 -0
  71. {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
  72. {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
  73. {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
  74. {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
  75. {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
  76. {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
  77. {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
  78. {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
  79. {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
  80. {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
  81. {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
  82. {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
  83. {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
  84. {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
  85. {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
  86. {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
  87. {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
  88. {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
  89. {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
  90. {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
  91. {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
  92. {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
  93. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/langfuse/434f3f96ac470e805428c4be45b4f2a3.json +0 -0
  94. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/langfuse/4c354cfb5b17f61af073df6796f2b236.json +0 -0
  95. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/langfuse/4deb34a4521fcedf92083d0a93fe11d4.json +0 -0
  96. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/langfuse/c685b7eb7826a87149bd2c42a50e47c5.json +0 -0
  97. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/agentic/research_agent_traces/langfuse/ebf1d6897941adb63bcc62aa7c50e8f8.json +0 -0
  98. {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
  99. {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
  100. {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
  101. {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
  102. {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
  103. {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
  104. {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
  105. {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
  106. {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
  107. {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
  108. {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
  109. {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
  110. {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
  111. {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
  112. {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
  113. {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
  114. {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
  115. {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
  116. {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
  117. {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
  118. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/gsm8k/__init__.py +0 -0
  119. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/gsm8k/analysis_results_gsm8k_default.zip +0 -0
  120. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/gsm8k/gsm8k_default_predictions.csv +0 -0
  121. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval/sample_data/tool_calls/tool_calls_sample_data.csv +0 -0
  122. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/dependency_links.txt +0 -0
  123. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/entry_points.txt +0 -0
  124. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/requires.txt +0 -0
  125. {clear_eval-2.0.2 → clear_eval-2.0.4}/src/clear_eval.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clear_eval
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: A python API sdk facilitating Error Analysis via LLM-as-a-Judge
5
5
  Author-email: Lilach Eden <lilache@il.ibm.com>, Asaf Yehudai <asaf.yehudai@ibm.com>
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "clear_eval"
7
- version = "2.0.2"
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"},
@@ -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
@@ -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, include_examples=True):
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 include_examples:
83
- for occurrence in issue.get("occurrences", [])[:3]:
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
- agents_data[agent_label] = {
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, include_examples=False):
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, include_examples=include_examples)
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)
@@ -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
- """Run CLEAR analysis on evaluation results."""
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(**eval_kwargs):
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: