Update app.py
Browse files
app.py
CHANGED
|
@@ -3,27 +3,21 @@ import pandas as pd
|
|
| 3 |
import numpy as np
|
| 4 |
import json
|
| 5 |
import plotly.graph_objects as go
|
| 6 |
-
import plotly.express as px
|
| 7 |
-
from datetime import datetime
|
| 8 |
-
import hashlib
|
| 9 |
from typing import Dict, List, Tuple, Optional
|
| 10 |
-
import re
|
| 11 |
-
from collections import defaultdict
|
| 12 |
-
import seaborn as sns
|
| 13 |
-
import matplotlib.pyplot as plt
|
| 14 |
-
from io import BytesIO
|
| 15 |
-
import base64
|
| 16 |
|
| 17 |
# Import evaluation modules
|
| 18 |
from evaluator_module import AetherScoreEvaluator
|
| 19 |
from visualizer_module import EvaluationVisualizer
|
| 20 |
from report_generator import ReportGenerator
|
| 21 |
|
| 22 |
-
#
|
| 23 |
evaluator = AetherScoreEvaluator()
|
| 24 |
visualizer = EvaluationVisualizer()
|
| 25 |
report_gen = ReportGenerator()
|
| 26 |
|
|
|
|
|
|
|
|
|
|
| 27 |
# CSS for better styling
|
| 28 |
custom_css = """
|
| 29 |
.gradio-container {
|
|
@@ -36,65 +30,58 @@ custom_css = """
|
|
| 36 |
color: white;
|
| 37 |
margin: 10px 0;
|
| 38 |
}
|
| 39 |
-
.leaderboard-row {
|
| 40 |
-
transition: all 0.3s ease;
|
| 41 |
-
}
|
| 42 |
-
.leaderboard-row:hover {
|
| 43 |
-
transform: translateX(5px);
|
| 44 |
-
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
| 45 |
-
}
|
| 46 |
"""
|
| 47 |
|
|
|
|
|
|
|
| 48 |
def process_single_evaluation(
|
| 49 |
prompt: str,
|
| 50 |
response: str,
|
| 51 |
expected_answer: Optional[str] = None,
|
| 52 |
agent_name: str = "Agent-1",
|
| 53 |
task_type: str = "general"
|
| 54 |
-
) -> Tuple[Dict,
|
| 55 |
"""Process a single agent response evaluation"""
|
| 56 |
-
|
| 57 |
if not prompt or not response:
|
| 58 |
-
return {},
|
| 59 |
-
|
| 60 |
# Evaluate the response
|
| 61 |
-
|
| 62 |
prompt=prompt,
|
| 63 |
response=response,
|
| 64 |
expected_answer=expected_answer,
|
| 65 |
task_type=task_type
|
| 66 |
)
|
|
|
|
| 67 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
# Generate visualizations
|
| 69 |
spider_chart = visualizer.create_spider_chart(scores, agent_name)
|
| 70 |
score_bars = visualizer.create_score_bars(scores, agent_name)
|
| 71 |
-
|
| 72 |
# Generate explanation
|
| 73 |
-
explanation = evaluator.generate_explanation(scores
|
| 74 |
-
|
| 75 |
# Format scores for display
|
| 76 |
-
scores_display = {
|
| 77 |
-
|
| 78 |
-
"Instruction Following": f"{scores.get('instruction_following', 0):.2f}",
|
| 79 |
-
"Hallucination Score": f"{scores.get('hallucination_score', 0):.2f}",
|
| 80 |
-
"Assumption Control": f"{scores.get('assumption_control', 0):.2f}",
|
| 81 |
-
"Coherence": f"{scores.get('coherence', 0):.2f}",
|
| 82 |
-
"Accuracy": f"{scores.get('accuracy', 0):.2f}"
|
| 83 |
-
}
|
| 84 |
|
| 85 |
return scores_display, spider_chart, score_bars, explanation
|
| 86 |
|
| 87 |
def process_batch_evaluation(
|
| 88 |
file_input,
|
| 89 |
evaluation_mode: str = "comprehensive"
|
| 90 |
-
) -> Tuple[
|
| 91 |
"""Process batch evaluation from uploaded file"""
|
| 92 |
-
|
| 93 |
if file_input is None:
|
| 94 |
-
return
|
| 95 |
-
|
| 96 |
try:
|
| 97 |
-
# Parse the uploaded file
|
| 98 |
if file_input.name.endswith('.json'):
|
| 99 |
with open(file_input.name, 'r') as f:
|
| 100 |
data = json.load(f)
|
|
@@ -104,55 +91,45 @@ def process_batch_evaluation(
|
|
| 104 |
for line in f:
|
| 105 |
data.append(json.loads(line))
|
| 106 |
else:
|
| 107 |
-
|
| 108 |
-
|
| 109 |
# Process batch evaluation
|
| 110 |
results = evaluator.evaluate_batch(data, mode=evaluation_mode)
|
| 111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
# Generate visualizations
|
| 113 |
heatmap = visualizer.create_evaluation_heatmap(results)
|
| 114 |
distribution = visualizer.create_score_distribution(results)
|
| 115 |
trends = visualizer.create_performance_trends(results)
|
| 116 |
-
|
| 117 |
-
# Generate comprehensive report
|
| 118 |
report = report_gen.generate_batch_report(results)
|
| 119 |
-
|
| 120 |
-
# Create leaderboard
|
| 121 |
leaderboard = create_leaderboard(results)
|
| 122 |
|
| 123 |
return heatmap, distribution, trends, report, leaderboard
|
| 124 |
|
| 125 |
except Exception as e:
|
| 126 |
error_msg = f"Error processing batch file: {str(e)}"
|
| 127 |
-
return
|
| 128 |
|
| 129 |
def create_leaderboard(results: List[Dict]) -> pd.DataFrame:
|
| 130 |
"""Create a leaderboard from evaluation results"""
|
| 131 |
-
|
| 132 |
-
agent_scores = defaultdict(list)
|
| 133 |
-
|
| 134 |
-
for result in results:
|
| 135 |
-
agent_name = result.get('agent_name', 'Unknown')
|
| 136 |
-
overall_score = result.get('scores', {}).get('overall_score', 0)
|
| 137 |
-
agent_scores[agent_name].append(overall_score)
|
| 138 |
|
| 139 |
leaderboard_data = []
|
| 140 |
for agent, scores in agent_scores.items():
|
| 141 |
leaderboard_data.append({
|
| 142 |
-
'Rank': 0,
|
| 143 |
-
'
|
| 144 |
-
'
|
| 145 |
-
'Max Score': np.max(scores),
|
| 146 |
-
'Min Score': np.min(scores),
|
| 147 |
-
'Std Dev': np.std(scores),
|
| 148 |
-
'Evaluations': len(scores)
|
| 149 |
})
|
| 150 |
|
| 151 |
-
df = pd.DataFrame(leaderboard_data)
|
| 152 |
-
df = df.sort_values('Avg Score', ascending=False)
|
| 153 |
df['Rank'] = range(1, len(df) + 1)
|
| 154 |
|
| 155 |
-
# Format scores
|
| 156 |
for col in ['Avg Score', 'Max Score', 'Min Score', 'Std Dev']:
|
| 157 |
df[col] = df[col].apply(lambda x: f"{x:.3f}")
|
| 158 |
|
|
@@ -161,366 +138,136 @@ def create_leaderboard(results: List[Dict]) -> pd.DataFrame:
|
|
| 161 |
def compare_agents(
|
| 162 |
agent1_file,
|
| 163 |
agent2_file,
|
| 164 |
-
|
| 165 |
-
) -> Tuple[str, str, str, str]:
|
| 166 |
"""Compare two agents' performance"""
|
| 167 |
-
|
| 168 |
if not agent1_file or not agent2_file:
|
| 169 |
-
return
|
| 170 |
-
|
| 171 |
try:
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
agent1_name="Agent 1",
|
| 185 |
-
agent2_name="Agent 2"
|
| 186 |
-
)
|
| 187 |
-
|
| 188 |
-
radar_comparison = visualizer.create_radar_comparison(
|
| 189 |
-
agent1_results,
|
| 190 |
-
agent2_results
|
| 191 |
-
)
|
| 192 |
-
|
| 193 |
-
performance_delta = visualizer.create_performance_delta(
|
| 194 |
-
agent1_results,
|
| 195 |
-
agent2_results
|
| 196 |
-
)
|
| 197 |
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
)
|
| 203 |
|
| 204 |
return comparison_chart, radar_comparison, performance_delta, comparison_report
|
| 205 |
|
| 206 |
except Exception as e:
|
| 207 |
error_msg = f"Error comparing agents: {str(e)}"
|
| 208 |
-
return
|
| 209 |
|
| 210 |
-
def
|
| 211 |
-
"""
|
| 212 |
-
if
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
data = []
|
| 217 |
-
with open(file_input.name, 'r') as f:
|
| 218 |
-
for line in f:
|
| 219 |
-
data.append(json.loads(line))
|
| 220 |
-
return data
|
| 221 |
-
else:
|
| 222 |
-
raise ValueError("Unsupported file format")
|
| 223 |
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 228 |
-
|
| 229 |
-
if export_format == "json":
|
| 230 |
-
filename = f"aetherscore_results_{timestamp}.json"
|
| 231 |
-
with open(filename, 'w') as f:
|
| 232 |
-
json.dump(results_data, f, indent=2)
|
| 233 |
-
elif export_format == "csv":
|
| 234 |
-
filename = f"aetherscore_results_{timestamp}.csv"
|
| 235 |
-
df = pd.DataFrame(results_data)
|
| 236 |
-
df.to_csv(filename, index=False)
|
| 237 |
-
else:
|
| 238 |
-
filename = f"aetherscore_results_{timestamp}.html"
|
| 239 |
-
# Generate HTML report
|
| 240 |
-
html_content = report_gen.generate_html_report(results_data)
|
| 241 |
-
with open(filename, 'w') as f:
|
| 242 |
-
f.write(html_content)
|
| 243 |
-
|
| 244 |
-
return filename
|
| 245 |
|
| 246 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
with gr.Blocks(title="AetherScore - AI Agent Evaluation Framework", css=custom_css) as demo:
|
| 248 |
-
|
| 249 |
-
gr.Markdown("""
|
| 250 |
-
# π AetherScore - Advanced AI Agent Evaluation Framework
|
| 251 |
-
|
| 252 |
-
**Comprehensive evaluation system for AI agents with multi-dimensional scoring, explainability, and advanced visualizations**
|
| 253 |
-
|
| 254 |
-
---
|
| 255 |
-
""")
|
| 256 |
|
| 257 |
with gr.Tabs():
|
| 258 |
# Tab 1: Single Evaluation
|
| 259 |
with gr.TabItem("π― Single Evaluation"):
|
| 260 |
-
gr.Markdown("### Evaluate a single agent response")
|
| 261 |
-
|
| 262 |
with gr.Row():
|
| 263 |
with gr.Column(scale=1):
|
| 264 |
-
prompt_input = gr.Textbox(
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
)
|
| 269 |
-
response_input = gr.Textbox(
|
| 270 |
-
label="Agent Response",
|
| 271 |
-
placeholder="Enter the agent's response...",
|
| 272 |
-
lines=3
|
| 273 |
-
)
|
| 274 |
-
expected_answer = gr.Textbox(
|
| 275 |
-
label="Expected Answer (Optional)",
|
| 276 |
-
placeholder="Enter the expected/correct answer for accuracy scoring...",
|
| 277 |
-
lines=2
|
| 278 |
-
)
|
| 279 |
-
|
| 280 |
-
with gr.Row():
|
| 281 |
-
agent_name_input = gr.Textbox(
|
| 282 |
-
label="Agent Name",
|
| 283 |
-
value="Agent-1",
|
| 284 |
-
scale=1
|
| 285 |
-
)
|
| 286 |
-
task_type = gr.Dropdown(
|
| 287 |
-
label="Task Type",
|
| 288 |
-
choices=["general", "QA", "summarization", "reasoning", "code"],
|
| 289 |
-
value="general",
|
| 290 |
-
scale=1
|
| 291 |
-
)
|
| 292 |
-
|
| 293 |
evaluate_btn = gr.Button("π Evaluate", variant="primary")
|
| 294 |
-
|
| 295 |
with gr.Column(scale=1):
|
| 296 |
scores_output = gr.JSON(label="π Evaluation Scores")
|
| 297 |
-
explanation_output = gr.Textbox(
|
| 298 |
-
label="π Explanation",
|
| 299 |
-
lines=6,
|
| 300 |
-
interactive=False
|
| 301 |
-
)
|
| 302 |
-
|
| 303 |
with gr.Row():
|
| 304 |
-
spider_chart_output = gr.
|
| 305 |
-
score_bars_output = gr.
|
| 306 |
|
| 307 |
-
evaluate_btn.click(
|
| 308 |
-
|
| 309 |
-
inputs=[prompt_input, response_input, expected_answer, agent_name_input, task_type],
|
| 310 |
-
outputs=[scores_output, spider_chart_output, score_bars_output, explanation_output]
|
| 311 |
-
)
|
| 312 |
-
|
| 313 |
# Tab 2: Batch Evaluation
|
| 314 |
with gr.TabItem("π¦ Batch Evaluation"):
|
| 315 |
-
gr.Markdown("### Evaluate multiple agents and responses at scale")
|
| 316 |
-
|
| 317 |
with gr.Row():
|
| 318 |
with gr.Column(scale=1):
|
| 319 |
-
batch_file = gr.File(
|
| 320 |
-
|
| 321 |
-
file_types=[".json", ".jsonl"]
|
| 322 |
-
)
|
| 323 |
-
evaluation_mode = gr.Radio(
|
| 324 |
-
label="Evaluation Mode",
|
| 325 |
-
choices=["comprehensive", "fast", "detailed"],
|
| 326 |
-
value="comprehensive"
|
| 327 |
-
)
|
| 328 |
batch_evaluate_btn = gr.Button("π Run Batch Evaluation", variant="primary")
|
| 329 |
-
|
| 330 |
-
gr.Markdown("""
|
| 331 |
-
**Expected Format:**
|
| 332 |
-
```json
|
| 333 |
-
{
|
| 334 |
-
"task_id": "task_001",
|
| 335 |
-
"agent_name": "GPT-4",
|
| 336 |
-
"prompt": "...",
|
| 337 |
-
"response": "...",
|
| 338 |
-
"expected_answer": "..." (optional)
|
| 339 |
-
}
|
| 340 |
-
```
|
| 341 |
-
""")
|
| 342 |
-
|
| 343 |
with gr.Column(scale=2):
|
| 344 |
-
batch_report = gr.Textbox(
|
| 345 |
-
label="π Evaluation Report",
|
| 346 |
-
lines=10,
|
| 347 |
-
interactive=False
|
| 348 |
-
)
|
| 349 |
-
|
| 350 |
with gr.Row():
|
| 351 |
-
heatmap_output = gr.
|
| 352 |
-
distribution_output = gr.
|
|
|
|
|
|
|
| 353 |
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
label="π Agent Leaderboard",
|
| 357 |
-
interactive=False
|
| 358 |
-
)
|
| 359 |
-
|
| 360 |
-
batch_evaluate_btn.click(
|
| 361 |
-
fn=process_batch_evaluation,
|
| 362 |
-
inputs=[batch_file, evaluation_mode],
|
| 363 |
-
outputs=[heatmap_output, distribution_output, trends_output, batch_report, leaderboard_output]
|
| 364 |
-
)
|
| 365 |
-
|
| 366 |
# Tab 3: Agent Comparison
|
| 367 |
with gr.TabItem("βοΈ Agent Comparison"):
|
| 368 |
-
gr.Markdown("### Compare performance between two agents")
|
| 369 |
-
|
| 370 |
with gr.Row():
|
| 371 |
with gr.Column():
|
| 372 |
-
agent1_file = gr.File(
|
| 373 |
-
|
| 374 |
-
file_types=[".json", ".jsonl"]
|
| 375 |
-
)
|
| 376 |
-
agent2_file = gr.File(
|
| 377 |
-
label="Agent 2 Results (JSON/JSONL)",
|
| 378 |
-
file_types=[".json", ".jsonl"]
|
| 379 |
-
)
|
| 380 |
-
comparison_metric = gr.Dropdown(
|
| 381 |
-
label="Comparison Focus",
|
| 382 |
-
choices=["all", "accuracy", "hallucination", "instruction_following"],
|
| 383 |
-
value="all"
|
| 384 |
-
)
|
| 385 |
compare_btn = gr.Button("βοΈ Compare Agents", variant="primary")
|
| 386 |
-
|
| 387 |
with gr.Column():
|
| 388 |
-
comparison_report = gr.Textbox(
|
| 389 |
-
label="π Comparison Analysis",
|
| 390 |
-
lines=8,
|
| 391 |
-
interactive=False
|
| 392 |
-
)
|
| 393 |
-
|
| 394 |
with gr.Row():
|
| 395 |
-
comparison_chart = gr.
|
| 396 |
-
radar_comparison = gr.
|
|
|
|
| 397 |
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
compare_btn.click(
|
| 401 |
-
fn=compare_agents,
|
| 402 |
-
inputs=[agent1_file, agent2_file, comparison_metric],
|
| 403 |
-
outputs=[comparison_chart, radar_comparison, performance_delta, comparison_report]
|
| 404 |
-
)
|
| 405 |
-
|
| 406 |
# Tab 4: Explainability Dashboard
|
| 407 |
with gr.TabItem("π Explainability"):
|
| 408 |
-
gr.Markdown("### Deep dive into evaluation decisions")
|
| 409 |
-
|
| 410 |
with gr.Row():
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
label="Evaluation ID or Task ID",
|
| 414 |
-
placeholder="Enter evaluation ID to analyze..."
|
| 415 |
-
)
|
| 416 |
-
load_eval_btn = gr.Button("Load Evaluation", variant="secondary")
|
| 417 |
-
|
| 418 |
-
with gr.Column():
|
| 419 |
-
eval_details = gr.JSON(label="Evaluation Details")
|
| 420 |
-
|
| 421 |
with gr.Row():
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
label="Detailed Score Breakdown",
|
| 427 |
-
lines=10,
|
| 428 |
-
interactive=False
|
| 429 |
-
)
|
| 430 |
-
|
| 431 |
-
# Tab 5: Settings & Export
|
| 432 |
-
with gr.TabItem("βοΈ Settings & Export"):
|
| 433 |
-
gr.Markdown("### Configuration and Export Options")
|
| 434 |
-
|
| 435 |
-
with gr.Row():
|
| 436 |
-
with gr.Column():
|
| 437 |
-
gr.Markdown("#### Evaluation Settings")
|
| 438 |
-
|
| 439 |
-
hallucination_threshold = gr.Slider(
|
| 440 |
-
label="Hallucination Detection Threshold",
|
| 441 |
-
minimum=0.0,
|
| 442 |
-
maximum=1.0,
|
| 443 |
-
value=0.7,
|
| 444 |
-
step=0.1
|
| 445 |
-
)
|
| 446 |
-
|
| 447 |
-
coherence_weight = gr.Slider(
|
| 448 |
-
label="Coherence Weight",
|
| 449 |
-
minimum=0.0,
|
| 450 |
-
maximum=1.0,
|
| 451 |
-
value=0.25,
|
| 452 |
-
step=0.05
|
| 453 |
-
)
|
| 454 |
-
|
| 455 |
-
accuracy_weight = gr.Slider(
|
| 456 |
-
label="Accuracy Weight",
|
| 457 |
-
minimum=0.0,
|
| 458 |
-
maximum=1.0,
|
| 459 |
-
value=0.25,
|
| 460 |
-
step=0.05
|
| 461 |
-
)
|
| 462 |
-
|
| 463 |
-
save_settings_btn = gr.Button("πΎ Save Settings", variant="secondary")
|
| 464 |
-
|
| 465 |
-
with gr.Column():
|
| 466 |
-
gr.Markdown("#### Export Options")
|
| 467 |
-
|
| 468 |
-
export_format = gr.Radio(
|
| 469 |
-
label="Export Format",
|
| 470 |
-
choices=["JSON", "CSV", "HTML Report"],
|
| 471 |
-
value="JSON"
|
| 472 |
-
)
|
| 473 |
-
|
| 474 |
-
include_visualizations = gr.Checkbox(
|
| 475 |
-
label="Include Visualizations in Export",
|
| 476 |
-
value=True
|
| 477 |
-
)
|
| 478 |
-
|
| 479 |
-
export_btn = gr.Button("π₯ Export Results", variant="primary")
|
| 480 |
-
export_status = gr.Textbox(
|
| 481 |
-
label="Export Status",
|
| 482 |
-
interactive=False
|
| 483 |
-
)
|
| 484 |
-
|
| 485 |
-
gr.Markdown("""
|
| 486 |
-
---
|
| 487 |
-
|
| 488 |
-
### π About AetherScore
|
| 489 |
-
|
| 490 |
-
AetherScore is a comprehensive evaluation framework designed to assess AI agents across multiple dimensions:
|
| 491 |
-
|
| 492 |
-
- **Instruction Following**: How well the agent adheres to given instructions
|
| 493 |
-
- **Hallucination Detection**: Identifies fabricated or unsupported information
|
| 494 |
-
- **Assumption Control**: Measures unwarranted assumptions made by the agent
|
| 495 |
-
- **Coherence**: Evaluates logical consistency and flow
|
| 496 |
-
- **Accuracy**: Compares responses against ground truth when available
|
| 497 |
-
|
| 498 |
-
### π― Key Features
|
| 499 |
-
|
| 500 |
-
- **Multi-dimensional Scoring**: Comprehensive evaluation across 5+ metrics
|
| 501 |
-
- **Batch Processing**: Evaluate thousands of responses efficiently
|
| 502 |
-
- **Advanced Visualizations**: Spider charts, heatmaps, trend analysis
|
| 503 |
-
- **Explainable AI**: Understand why scores were assigned
|
| 504 |
-
- **Agent Comparison**: Head-to-head performance analysis
|
| 505 |
-
- **Customizable Weights**: Adjust importance of different metrics
|
| 506 |
-
|
| 507 |
-
### π§ API Integration
|
| 508 |
-
|
| 509 |
-
```python
|
| 510 |
-
# Example API usage
|
| 511 |
-
from aetherscore import evaluate
|
| 512 |
-
|
| 513 |
-
result = evaluate(
|
| 514 |
-
prompt="Your prompt here",
|
| 515 |
-
response="Agent response",
|
| 516 |
-
metrics=["all"]
|
| 517 |
-
)
|
| 518 |
-
```
|
| 519 |
-
|
| 520 |
-
---
|
| 521 |
-
|
| 522 |
-
**Version**: 1.0.0 | **License**: MIT | **GitHub**: [AetherScore](https://github.com/aetherscore)
|
| 523 |
-
""")
|
| 524 |
|
| 525 |
if __name__ == "__main__":
|
| 526 |
demo.launch(share=True)
|
|
|
|
| 3 |
import numpy as np
|
| 4 |
import json
|
| 5 |
import plotly.graph_objects as go
|
|
|
|
|
|
|
|
|
|
| 6 |
from typing import Dict, List, Tuple, Optional
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
# Import evaluation modules
|
| 9 |
from evaluator_module import AetherScoreEvaluator
|
| 10 |
from visualizer_module import EvaluationVisualizer
|
| 11 |
from report_generator import ReportGenerator
|
| 12 |
|
| 13 |
+
# --- Global Components & Storage ---
|
| 14 |
evaluator = AetherScoreEvaluator()
|
| 15 |
visualizer = EvaluationVisualizer()
|
| 16 |
report_gen = ReportGenerator()
|
| 17 |
|
| 18 |
+
# In-memory storage for explainability feature
|
| 19 |
+
evaluation_storage: Dict[str, Dict] = {}
|
| 20 |
+
|
| 21 |
# CSS for better styling
|
| 22 |
custom_css = """
|
| 23 |
.gradio-container {
|
|
|
|
| 30 |
color: white;
|
| 31 |
margin: 10px 0;
|
| 32 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
"""
|
| 34 |
|
| 35 |
+
# --- Core Functions ---
|
| 36 |
+
|
| 37 |
def process_single_evaluation(
|
| 38 |
prompt: str,
|
| 39 |
response: str,
|
| 40 |
expected_answer: Optional[str] = None,
|
| 41 |
agent_name: str = "Agent-1",
|
| 42 |
task_type: str = "general"
|
| 43 |
+
) -> Tuple[Dict, go.Figure, go.Figure, str]:
|
| 44 |
"""Process a single agent response evaluation"""
|
|
|
|
| 45 |
if not prompt or not response:
|
| 46 |
+
return {}, go.Figure(), go.Figure(), "Please provide both prompt and response."
|
| 47 |
+
|
| 48 |
# Evaluate the response
|
| 49 |
+
eval_result = evaluator.evaluate_single(
|
| 50 |
prompt=prompt,
|
| 51 |
response=response,
|
| 52 |
expected_answer=expected_answer,
|
| 53 |
task_type=task_type
|
| 54 |
)
|
| 55 |
+
scores = eval_result.get("scores", {})
|
| 56 |
|
| 57 |
+
# Store result for explainability
|
| 58 |
+
eval_id = scores.get("eval_id", "")
|
| 59 |
+
if eval_id:
|
| 60 |
+
evaluation_storage[eval_id] = eval_result
|
| 61 |
+
print(f"Stored evaluation {eval_id}")
|
| 62 |
+
|
| 63 |
# Generate visualizations
|
| 64 |
spider_chart = visualizer.create_spider_chart(scores, agent_name)
|
| 65 |
score_bars = visualizer.create_score_bars(scores, agent_name)
|
| 66 |
+
|
| 67 |
# Generate explanation
|
| 68 |
+
explanation = evaluator.generate_explanation(scores)
|
| 69 |
+
|
| 70 |
# Format scores for display
|
| 71 |
+
scores_display = {k: f"{v:.2f}" for k, v in scores.items() if isinstance(v, float)}
|
| 72 |
+
scores_display["eval_id"] = eval_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
return scores_display, spider_chart, score_bars, explanation
|
| 75 |
|
| 76 |
def process_batch_evaluation(
|
| 77 |
file_input,
|
| 78 |
evaluation_mode: str = "comprehensive"
|
| 79 |
+
) -> Tuple[go.Figure, go.Figure, go.Figure, str, pd.DataFrame]:
|
| 80 |
"""Process batch evaluation from uploaded file"""
|
|
|
|
| 81 |
if file_input is None:
|
| 82 |
+
return go.Figure(), go.Figure(), go.Figure(), "Please upload a file.", pd.DataFrame()
|
| 83 |
+
|
| 84 |
try:
|
|
|
|
| 85 |
if file_input.name.endswith('.json'):
|
| 86 |
with open(file_input.name, 'r') as f:
|
| 87 |
data = json.load(f)
|
|
|
|
| 91 |
for line in f:
|
| 92 |
data.append(json.loads(line))
|
| 93 |
else:
|
| 94 |
+
raise ValueError("Unsupported file format. Please upload JSON or JSONL.")
|
| 95 |
+
|
| 96 |
# Process batch evaluation
|
| 97 |
results = evaluator.evaluate_batch(data, mode=evaluation_mode)
|
| 98 |
|
| 99 |
+
# Store all results for explainability
|
| 100 |
+
for res in results:
|
| 101 |
+
eval_id = res.get('scores', {}).get('eval_id')
|
| 102 |
+
if eval_id:
|
| 103 |
+
evaluation_storage[eval_id] = res
|
| 104 |
+
|
| 105 |
# Generate visualizations
|
| 106 |
heatmap = visualizer.create_evaluation_heatmap(results)
|
| 107 |
distribution = visualizer.create_score_distribution(results)
|
| 108 |
trends = visualizer.create_performance_trends(results)
|
|
|
|
|
|
|
| 109 |
report = report_gen.generate_batch_report(results)
|
|
|
|
|
|
|
| 110 |
leaderboard = create_leaderboard(results)
|
| 111 |
|
| 112 |
return heatmap, distribution, trends, report, leaderboard
|
| 113 |
|
| 114 |
except Exception as e:
|
| 115 |
error_msg = f"Error processing batch file: {str(e)}"
|
| 116 |
+
return go.Figure(), go.Figure(), go.Figure(), error_msg, pd.DataFrame()
|
| 117 |
|
| 118 |
def create_leaderboard(results: List[Dict]) -> pd.DataFrame:
|
| 119 |
"""Create a leaderboard from evaluation results"""
|
| 120 |
+
agent_scores = evaluator.get_agent_scores_from_results(results)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
leaderboard_data = []
|
| 123 |
for agent, scores in agent_scores.items():
|
| 124 |
leaderboard_data.append({
|
| 125 |
+
'Rank': 0, 'Agent': agent, 'Avg Score': np.mean(scores),
|
| 126 |
+
'Max Score': np.max(scores), 'Min Score': np.min(scores),
|
| 127 |
+
'Std Dev': np.std(scores), 'Evaluations': len(scores)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
})
|
| 129 |
|
| 130 |
+
df = pd.DataFrame(leaderboard_data).sort_values('Avg Score', ascending=False)
|
|
|
|
| 131 |
df['Rank'] = range(1, len(df) + 1)
|
| 132 |
|
|
|
|
| 133 |
for col in ['Avg Score', 'Max Score', 'Min Score', 'Std Dev']:
|
| 134 |
df[col] = df[col].apply(lambda x: f"{x:.3f}")
|
| 135 |
|
|
|
|
| 138 |
def compare_agents(
|
| 139 |
agent1_file,
|
| 140 |
agent2_file,
|
| 141 |
+
) -> Tuple[go.Figure, go.Figure, go.Figure, str]:
|
|
|
|
| 142 |
"""Compare two agents' performance"""
|
|
|
|
| 143 |
if not agent1_file or not agent2_file:
|
| 144 |
+
return go.Figure(), go.Figure(), go.Figure(), "Please upload files for both agents."
|
| 145 |
+
|
| 146 |
try:
|
| 147 |
+
def load_agent_data(file):
|
| 148 |
+
if file.name.endswith('.json'):
|
| 149 |
+
with open(file.name, 'r') as f: return json.load(f)
|
| 150 |
+
elif file.name.endswith('.jsonl'):
|
| 151 |
+
data = [];
|
| 152 |
+
with open(file.name, 'r') as f:
|
| 153 |
+
for line in f: data.append(json.loads(line))
|
| 154 |
+
return data
|
| 155 |
+
raise ValueError("Unsupported file format")
|
| 156 |
+
|
| 157 |
+
agent1_results = evaluator.evaluate_batch(load_agent_data(agent1_file))
|
| 158 |
+
agent2_results = evaluator.evaluate_batch(load_agent_data(agent2_file))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
+
comparison_chart = visualizer.create_agent_comparison(agent1_results, agent2_results)
|
| 161 |
+
radar_comparison = visualizer.create_radar_comparison(agent1_results, agent2_results)
|
| 162 |
+
performance_delta = visualizer.create_performance_delta(agent1_results, agent2_results)
|
| 163 |
+
comparison_report = report_gen.generate_comparison_report(agent1_results, agent2_results)
|
|
|
|
| 164 |
|
| 165 |
return comparison_chart, radar_comparison, performance_delta, comparison_report
|
| 166 |
|
| 167 |
except Exception as e:
|
| 168 |
error_msg = f"Error comparing agents: {str(e)}"
|
| 169 |
+
return go.Figure(), go.Figure(), go.Figure(), error_msg
|
| 170 |
|
| 171 |
+
def get_detailed_explanation(eval_id: str) -> Tuple[Dict, str]:
|
| 172 |
+
"""Retrieve and format the detailed explanation for an evaluation ID."""
|
| 173 |
+
if not eval_id:
|
| 174 |
+
return {}, "Please enter an Evaluation ID."
|
| 175 |
+
|
| 176 |
+
eval_result = evaluation_storage.get(eval_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
|
| 178 |
+
if not eval_result:
|
| 179 |
+
return {}, f"Error: Evaluation ID '{eval_id}' not found in memory."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
|
| 181 |
+
scores = eval_result.get("scores", {})
|
| 182 |
+
reasons = eval_result.get("reasons", {})
|
| 183 |
+
|
| 184 |
+
# Format details for JSON display
|
| 185 |
+
details_to_display = {
|
| 186 |
+
"evaluation_id": scores.get("eval_id"),
|
| 187 |
+
"timestamp": scores.get("timestamp"),
|
| 188 |
+
"task_type": scores.get("task_type"),
|
| 189 |
+
"scores": {k: v for k, v in scores.items() if k not in ["eval_id", "timestamp", "task_type"]}
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
# Format breakdown for text display
|
| 193 |
+
breakdown = []
|
| 194 |
+
breakdown.append(f"### Detailed Breakdown for Evaluation: {eval_id}\n")
|
| 195 |
+
for metric, reason in reasons.items():
|
| 196 |
+
metric_name = metric.replace('_', ' ').title()
|
| 197 |
+
score = scores.get(metric, 0)
|
| 198 |
+
breakdown.append(f"**{metric_name}**: {score:.2f}\n"
|
| 199 |
+
f"> *Reasoning*: {reason}\n")
|
| 200 |
+
|
| 201 |
+
return details_to_display, "\n".join(breakdown)
|
| 202 |
+
|
| 203 |
+
# --- Gradio Interface ---
|
| 204 |
with gr.Blocks(title="AetherScore - AI Agent Evaluation Framework", css=custom_css) as demo:
|
| 205 |
+
gr.Markdown("# π AetherScore - Advanced AI Agent Evaluation Framework")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
|
| 207 |
with gr.Tabs():
|
| 208 |
# Tab 1: Single Evaluation
|
| 209 |
with gr.TabItem("π― Single Evaluation"):
|
|
|
|
|
|
|
| 210 |
with gr.Row():
|
| 211 |
with gr.Column(scale=1):
|
| 212 |
+
prompt_input = gr.Textbox(label="Prompt", lines=3)
|
| 213 |
+
response_input = gr.Textbox(label="Agent Response", lines=3)
|
| 214 |
+
expected_answer = gr.Textbox(label="Expected Answer (Optional)", lines=2)
|
| 215 |
+
agent_name_input = gr.Textbox(label="Agent Name", value="Agent-1")
|
| 216 |
+
task_type = gr.Dropdown(label="Task Type", choices=["general", "QA", "summarization", "reasoning", "code"], value="general")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
evaluate_btn = gr.Button("π Evaluate", variant="primary")
|
|
|
|
| 218 |
with gr.Column(scale=1):
|
| 219 |
scores_output = gr.JSON(label="π Evaluation Scores")
|
| 220 |
+
explanation_output = gr.Textbox(label="π Explanation", lines=6, interactive=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
with gr.Row():
|
| 222 |
+
spider_chart_output = gr.Plot(label="πΈοΈ Spider Chart")
|
| 223 |
+
score_bars_output = gr.Plot(label="π Score Breakdown")
|
| 224 |
|
| 225 |
+
evaluate_btn.click(fn=process_single_evaluation, inputs=[prompt_input, response_input, expected_answer, agent_name_input, task_type], outputs=[scores_output, spider_chart_output, score_bars_output, explanation_output])
|
| 226 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
# Tab 2: Batch Evaluation
|
| 228 |
with gr.TabItem("π¦ Batch Evaluation"):
|
|
|
|
|
|
|
| 229 |
with gr.Row():
|
| 230 |
with gr.Column(scale=1):
|
| 231 |
+
batch_file = gr.File(label="Upload Evaluation File (JSON/JSONL)", file_types=[".json", ".jsonl"])
|
| 232 |
+
evaluation_mode = gr.Radio(label="Evaluation Mode", choices=["comprehensive", "fast", "detailed"], value="comprehensive")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
batch_evaluate_btn = gr.Button("π Run Batch Evaluation", variant="primary")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
with gr.Column(scale=2):
|
| 235 |
+
batch_report = gr.Textbox(label="π Evaluation Report", lines=10, interactive=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
with gr.Row():
|
| 237 |
+
heatmap_output = gr.Plot(label="πΊοΈ Evaluation Heatmap")
|
| 238 |
+
distribution_output = gr.Plot(label="π Score Distribution")
|
| 239 |
+
trends_output = gr.Plot(label="π Performance Trends")
|
| 240 |
+
leaderboard_output = gr.Dataframe(label="π Agent Leaderboard", interactive=False)
|
| 241 |
|
| 242 |
+
batch_evaluate_btn.click(fn=process_batch_evaluation, inputs=[batch_file, evaluation_mode], outputs=[heatmap_output, distribution_output, trends_output, batch_report, leaderboard_output])
|
| 243 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
# Tab 3: Agent Comparison
|
| 245 |
with gr.TabItem("βοΈ Agent Comparison"):
|
|
|
|
|
|
|
| 246 |
with gr.Row():
|
| 247 |
with gr.Column():
|
| 248 |
+
agent1_file = gr.File(label="Agent 1 Results (JSON/JSONL)", file_types=[".json", ".jsonl"])
|
| 249 |
+
agent2_file = gr.File(label="Agent 2 Results (JSON/JSONL)", file_types=[".json", ".jsonl"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
compare_btn = gr.Button("βοΈ Compare Agents", variant="primary")
|
|
|
|
| 251 |
with gr.Column():
|
| 252 |
+
comparison_report = gr.Textbox(label="π Comparison Analysis", lines=8, interactive=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
with gr.Row():
|
| 254 |
+
comparison_chart = gr.Plot(label="π Performance Comparison")
|
| 255 |
+
radar_comparison = gr.Plot(label="π― Radar Comparison")
|
| 256 |
+
performance_delta = gr.Plot(label="π Performance Delta Analysis")
|
| 257 |
|
| 258 |
+
compare_btn.click(fn=compare_agents, inputs=[agent1_file, agent2_file], outputs=[comparison_chart, radar_comparison, performance_delta, comparison_report])
|
| 259 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
# Tab 4: Explainability Dashboard
|
| 261 |
with gr.TabItem("π Explainability"):
|
| 262 |
+
gr.Markdown("### Deep dive into evaluation decisions. Enter an `eval_id` from a previous run.")
|
|
|
|
| 263 |
with gr.Row():
|
| 264 |
+
eval_id_input = gr.Textbox(label="Evaluation ID", placeholder="Enter evaluation ID to analyze...")
|
| 265 |
+
load_eval_btn = gr.Button("Load Evaluation", variant="secondary")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
with gr.Row():
|
| 267 |
+
eval_details = gr.JSON(label="Evaluation Details")
|
| 268 |
+
detailed_breakdown = gr.Markdown(label="Detailed Score Breakdown")
|
| 269 |
+
|
| 270 |
+
load_eval_btn.click(fn=get_detailed_explanation, inputs=[eval_id_input], outputs=[eval_details, detailed_breakdown])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
|
| 272 |
if __name__ == "__main__":
|
| 273 |
demo.launch(share=True)
|