openagent-eval 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openagent_eval/__init__.py +4 -0
- openagent_eval/cli/__init__.py +5 -0
- openagent_eval/cli/commands/__init__.py +17 -0
- openagent_eval/cli/commands/compare.py +75 -0
- openagent_eval/cli/commands/doctor.py +100 -0
- openagent_eval/cli/commands/init.py +51 -0
- openagent_eval/cli/commands/list_evaluations.py +49 -0
- openagent_eval/cli/commands/report.py +64 -0
- openagent_eval/cli/commands/run.py +77 -0
- openagent_eval/cli/main.py +43 -0
- openagent_eval/cli/utils/__init__.py +6 -0
- openagent_eval/cli/utils/callbacks.py +14 -0
- openagent_eval/cli/utils/constants.py +47 -0
- openagent_eval/cli/utils/display.py +88 -0
- openagent_eval/cli/utils/helpers.py +118 -0
- openagent_eval/config/__init__.py +21 -0
- openagent_eval/config/loader.py +121 -0
- openagent_eval/config/models.py +142 -0
- openagent_eval/config/validator.py +87 -0
- openagent_eval/core/__init__.py +8 -0
- openagent_eval/core/engine.py +154 -0
- openagent_eval/core/executor.py +163 -0
- openagent_eval/core/pipeline.py +312 -0
- openagent_eval/core/registry.py +152 -0
- openagent_eval/datasets/__init__.py +37 -0
- openagent_eval/datasets/base.py +190 -0
- openagent_eval/datasets/csv_loader.py +178 -0
- openagent_eval/datasets/factory.py +115 -0
- openagent_eval/datasets/hf_loader.py +283 -0
- openagent_eval/datasets/json_loader.py +156 -0
- openagent_eval/datasets/jsonl_loader.py +165 -0
- openagent_eval/datasets/models.py +88 -0
- openagent_eval/datasets/pdf_loader.py +250 -0
- openagent_eval/exceptions/__init__.py +50 -0
- openagent_eval/exceptions/base.py +33 -0
- openagent_eval/exceptions/cli.py +97 -0
- openagent_eval/exceptions/config.py +41 -0
- openagent_eval/exceptions/dataset.py +119 -0
- openagent_eval/exceptions/metric.py +123 -0
- openagent_eval/exceptions/plugin.py +93 -0
- openagent_eval/exceptions/provider.py +123 -0
- openagent_eval/integrations/__init__.py +0 -0
- openagent_eval/metrics/__init__.py +113 -0
- openagent_eval/metrics/base.py +85 -0
- openagent_eval/metrics/cost/__init__.py +11 -0
- openagent_eval/metrics/cost/tokens.py +114 -0
- openagent_eval/metrics/generation/__init__.py +27 -0
- openagent_eval/metrics/generation/bertscore.py +117 -0
- openagent_eval/metrics/generation/bleu.py +100 -0
- openagent_eval/metrics/generation/exact_match.py +51 -0
- openagent_eval/metrics/generation/f1.py +77 -0
- openagent_eval/metrics/generation/faithfulness.py +118 -0
- openagent_eval/metrics/generation/hallucination.py +119 -0
- openagent_eval/metrics/generation/relevancy.py +128 -0
- openagent_eval/metrics/generation/rouge.py +86 -0
- openagent_eval/metrics/generation/similarity.py +102 -0
- openagent_eval/metrics/models.py +63 -0
- openagent_eval/metrics/performance/__init__.py +11 -0
- openagent_eval/metrics/performance/latency.py +64 -0
- openagent_eval/metrics/retrieval/__init__.py +23 -0
- openagent_eval/metrics/retrieval/hit_rate.py +59 -0
- openagent_eval/metrics/retrieval/mrr.py +66 -0
- openagent_eval/metrics/retrieval/ndcg.py +125 -0
- openagent_eval/metrics/retrieval/precision.py +64 -0
- openagent_eval/metrics/retrieval/precision_at_k.py +62 -0
- openagent_eval/metrics/retrieval/recall.py +65 -0
- openagent_eval/metrics/retrieval/recall_at_k.py +67 -0
- openagent_eval/plugins/__init__.py +22 -0
- openagent_eval/plugins/discovery.py +107 -0
- openagent_eval/plugins/examples/__init__.py +9 -0
- openagent_eval/plugins/examples/custom_metric.py +70 -0
- openagent_eval/plugins/loader.py +130 -0
- openagent_eval/plugins/manager.py +125 -0
- openagent_eval/providers/__init__.py +18 -0
- openagent_eval/providers/base/__init__.py +11 -0
- openagent_eval/providers/base/llm.py +96 -0
- openagent_eval/providers/base/retriever.py +83 -0
- openagent_eval/providers/embedders/__init__.py +37 -0
- openagent_eval/providers/embedders/base.py +58 -0
- openagent_eval/providers/embedders/mock.py +44 -0
- openagent_eval/providers/embedders/sentence_transformers.py +69 -0
- openagent_eval/providers/factory.py +176 -0
- openagent_eval/providers/llm/__init__.py +50 -0
- openagent_eval/providers/llm/anthropic.py +253 -0
- openagent_eval/providers/llm/gemini.py +232 -0
- openagent_eval/providers/llm/groq.py +314 -0
- openagent_eval/providers/llm/mock.py +93 -0
- openagent_eval/providers/llm/ollama.py +406 -0
- openagent_eval/providers/llm/openai.py +330 -0
- openagent_eval/providers/llm/openrouter.py +224 -0
- openagent_eval/providers/models.py +112 -0
- openagent_eval/providers/retrievers/__init__.py +67 -0
- openagent_eval/providers/retrievers/_scoring.py +91 -0
- openagent_eval/providers/retrievers/bm25.py +145 -0
- openagent_eval/providers/retrievers/chroma.py +186 -0
- openagent_eval/providers/retrievers/elasticsearch.py +132 -0
- openagent_eval/providers/retrievers/faiss.py +140 -0
- openagent_eval/providers/retrievers/http.py +189 -0
- openagent_eval/providers/retrievers/memory.py +174 -0
- openagent_eval/providers/retrievers/mock.py +69 -0
- openagent_eval/providers/retrievers/pgvector.py +130 -0
- openagent_eval/providers/retrievers/pinecone.py +101 -0
- openagent_eval/providers/retrievers/qdrant.py +104 -0
- openagent_eval/providers/retrievers/weaviate.py +101 -0
- openagent_eval/reports/__init__.py +33 -0
- openagent_eval/reports/base.py +164 -0
- openagent_eval/reports/comparison.py +167 -0
- openagent_eval/reports/html.py +158 -0
- openagent_eval/reports/json_report.py +150 -0
- openagent_eval/reports/manager.py +200 -0
- openagent_eval/reports/markdown.py +185 -0
- openagent_eval/reports/templates/report.html +259 -0
- openagent_eval/reports/terminal.py +203 -0
- openagent_eval/types/__init__.py +0 -0
- openagent_eval/utils/__init__.py +0 -0
- openagent_eval-0.1.0.dist-info/METADATA +356 -0
- openagent_eval-0.1.0.dist-info/RECORD +120 -0
- openagent_eval-0.1.0.dist-info/WHEEL +4 -0
- openagent_eval-0.1.0.dist-info/entry_points.txt +2 -0
- openagent_eval-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""CLI commands for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from openagent_eval.cli.commands.init import init_command
|
|
4
|
+
from openagent_eval.cli.commands.run import run_command
|
|
5
|
+
from openagent_eval.cli.commands.report import report_command
|
|
6
|
+
from openagent_eval.cli.commands.compare import compare_command
|
|
7
|
+
from openagent_eval.cli.commands.list_evaluations import list_command
|
|
8
|
+
from openagent_eval.cli.commands.doctor import doctor_command
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"init_command",
|
|
12
|
+
"run_command",
|
|
13
|
+
"report_command",
|
|
14
|
+
"compare_command",
|
|
15
|
+
"list_command",
|
|
16
|
+
"doctor_command",
|
|
17
|
+
]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Compare command for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from openagent_eval.exceptions.cli import CommandError
|
|
11
|
+
from openagent_eval.reports.comparison import ComparisonReport
|
|
12
|
+
from openagent_eval.reports.base import ExperimentComparison
|
|
13
|
+
from openagent_eval.reports.manager import ReportManager
|
|
14
|
+
from openagent_eval.cli.utils.constants import DEFAULT_OUTPUT_DIR
|
|
15
|
+
from openagent_eval.cli.utils.helpers import resolve_report_id
|
|
16
|
+
|
|
17
|
+
console = Console()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def compare_command(
|
|
21
|
+
experiment_a: str = typer.Argument(
|
|
22
|
+
help="First experiment ID or path.",
|
|
23
|
+
),
|
|
24
|
+
experiment_b: str = typer.Argument(
|
|
25
|
+
help="Second experiment ID or path.",
|
|
26
|
+
),
|
|
27
|
+
metrics: list[str] = typer.Option(
|
|
28
|
+
None,
|
|
29
|
+
"--metrics",
|
|
30
|
+
"-m",
|
|
31
|
+
help="Specific metrics to compare (default: all).",
|
|
32
|
+
),
|
|
33
|
+
output_dir: str = typer.Option(
|
|
34
|
+
None,
|
|
35
|
+
"--output-dir",
|
|
36
|
+
"-d",
|
|
37
|
+
help="Directory where reports are stored (default: ./reports).",
|
|
38
|
+
),
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Compare two evaluation experiments side by side."""
|
|
41
|
+
console.print("[bold blue]OpenAgent Eval[/bold blue] - Experiment Comparison")
|
|
42
|
+
console.print(f"[dim]Comparing: {experiment_a} vs {experiment_b}[/dim]\n")
|
|
43
|
+
|
|
44
|
+
manager = ReportManager()
|
|
45
|
+
reports_dir = Path(output_dir) if output_dir else DEFAULT_OUTPUT_DIR
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
data_a = resolve_report_id(experiment_a, reports_dir, manager)
|
|
49
|
+
except CommandError as e:
|
|
50
|
+
console.print(f"[red]Error:[/red] {e.message}")
|
|
51
|
+
raise typer.Exit(code=e.exit_code) from None
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
data_b = resolve_report_id(experiment_b, reports_dir, manager)
|
|
55
|
+
except CommandError as e:
|
|
56
|
+
console.print(f"[red]Error:[/red] {e.message}")
|
|
57
|
+
raise typer.Exit(code=e.exit_code) from None
|
|
58
|
+
|
|
59
|
+
# Reconstruct EvaluationReports and extract PipelineResults
|
|
60
|
+
report_a = manager.reconstruct(data_a)
|
|
61
|
+
report_b = manager.reconstruct(data_b)
|
|
62
|
+
|
|
63
|
+
# Create ExperimentComparison (needs PipelineResult objects)
|
|
64
|
+
comparison = ExperimentComparison(
|
|
65
|
+
baseline_name=experiment_a,
|
|
66
|
+
experiment_name=experiment_b,
|
|
67
|
+
baseline_results=report_a.result,
|
|
68
|
+
experiment_results=report_b.result,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Generate comparison
|
|
72
|
+
generator = ComparisonReport()
|
|
73
|
+
comparison_output = generator.generate(comparison)
|
|
74
|
+
|
|
75
|
+
console.print(comparison_output)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Doctor command for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
|
|
13
|
+
from openagent_eval import __version__
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def doctor_command(
|
|
19
|
+
verbose: bool = typer.Option(
|
|
20
|
+
False,
|
|
21
|
+
"--verbose",
|
|
22
|
+
"-v",
|
|
23
|
+
help="Show detailed information.",
|
|
24
|
+
),
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Check environment and dependencies for OpenAgent Eval."""
|
|
27
|
+
console.print("[bold blue]OpenAgent Eval[/bold blue] - Environment Check\n")
|
|
28
|
+
|
|
29
|
+
table = Table(title="Environment Status")
|
|
30
|
+
table.add_column("Component", style="cyan")
|
|
31
|
+
table.add_column("Status", style="bold")
|
|
32
|
+
table.add_column("Details", style="dim")
|
|
33
|
+
|
|
34
|
+
python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
35
|
+
python_ok = sys.version_info >= (3, 11)
|
|
36
|
+
table.add_row(
|
|
37
|
+
"Python",
|
|
38
|
+
"[green]OK[/green]" if python_ok else "[red]MISSING[/red]",
|
|
39
|
+
f"v{python_version}" + ("" if python_ok else " (3.11+ required)"),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
dependencies = [
|
|
43
|
+
("typer", "CLI framework"),
|
|
44
|
+
("rich", "Terminal UI"),
|
|
45
|
+
("pydantic", "Data validation"),
|
|
46
|
+
("yaml", "Configuration"),
|
|
47
|
+
("loguru", "Logging"),
|
|
48
|
+
("jinja2", "HTML templates"),
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
for dep_name, dep_desc in dependencies:
|
|
52
|
+
try:
|
|
53
|
+
importlib.import_module(dep_name.replace("-", "_"))
|
|
54
|
+
table.add_row(dep_name, "[green]OK[/green]", dep_desc)
|
|
55
|
+
except ImportError:
|
|
56
|
+
table.add_row(dep_name, "[red]MISSING[/red]", f"{dep_desc} (not installed)")
|
|
57
|
+
|
|
58
|
+
console.print(table)
|
|
59
|
+
|
|
60
|
+
key_table = Table(title="API Key Availability")
|
|
61
|
+
key_table.add_column("Provider", style="cyan")
|
|
62
|
+
key_table.add_column("Environment Variable", style="yellow")
|
|
63
|
+
key_table.add_column("Status", style="bold")
|
|
64
|
+
|
|
65
|
+
api_keys = [
|
|
66
|
+
("OpenAI", "OPENAI_API_KEY"),
|
|
67
|
+
("Gemini", "GEMINI_API_KEY"),
|
|
68
|
+
("Anthropic", "ANTHROPIC_API_KEY"),
|
|
69
|
+
("Groq", "GROQ_API_KEY"),
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
available_providers: list[str] = []
|
|
73
|
+
for provider_name, env_var in api_keys:
|
|
74
|
+
key_value = os.environ.get(env_var)
|
|
75
|
+
if key_value:
|
|
76
|
+
key_table.add_row(provider_name, env_var, "[green]Available[/green]")
|
|
77
|
+
available_providers.append(provider_name)
|
|
78
|
+
else:
|
|
79
|
+
key_table.add_row(provider_name, env_var, "[dim]Not set[/dim]")
|
|
80
|
+
|
|
81
|
+
console.print(key_table)
|
|
82
|
+
|
|
83
|
+
console.print("\n[bold]Summary:[/bold]")
|
|
84
|
+
if python_ok:
|
|
85
|
+
console.print("[green]OK[/green] Python version is compatible")
|
|
86
|
+
else:
|
|
87
|
+
console.print("[red]MISSING[/red] Python 3.11+ required")
|
|
88
|
+
|
|
89
|
+
if available_providers:
|
|
90
|
+
console.print(
|
|
91
|
+
f"[green]OK[/green] Available providers: {', '.join(available_providers)}"
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
console.print("[yellow]WARNING[/yellow] No API keys configured")
|
|
95
|
+
|
|
96
|
+
if verbose:
|
|
97
|
+
console.print(f"\n[dim]Python: {sys.executable}[/dim]")
|
|
98
|
+
console.print(f"[dim]Version: {__version__}[/dim]")
|
|
99
|
+
|
|
100
|
+
console.print("\n[dim]Run 'pip install openagent-eval[all]' to install all dependencies.[/dim]")
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Init command for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.prompt import Confirm
|
|
10
|
+
|
|
11
|
+
from openagent_eval.exceptions import ConfigurationError
|
|
12
|
+
from openagent_eval.cli.utils.constants import DEFAULT_CONFIG_CONTENT
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def init_command(
|
|
18
|
+
config_path: str = typer.Option(
|
|
19
|
+
"config.yaml",
|
|
20
|
+
"--config",
|
|
21
|
+
"-c",
|
|
22
|
+
help="Path to create configuration file.",
|
|
23
|
+
),
|
|
24
|
+
force: bool = typer.Option(
|
|
25
|
+
False,
|
|
26
|
+
"--force",
|
|
27
|
+
"-f",
|
|
28
|
+
help="Overwrite existing configuration file.",
|
|
29
|
+
),
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Create a new evaluation configuration file."""
|
|
32
|
+
path = Path(config_path)
|
|
33
|
+
|
|
34
|
+
if path.exists() and not force and not Confirm.ask(
|
|
35
|
+
f"Configuration file '{config_path}' already exists. Overwrite?"
|
|
36
|
+
):
|
|
37
|
+
console.print("[yellow]Aborted.[/yellow]")
|
|
38
|
+
raise typer.Exit()
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
path.write_text(DEFAULT_CONFIG_CONTENT, encoding="utf-8")
|
|
43
|
+
console.print(f"[green]OK[/green] Configuration created: {config_path}")
|
|
44
|
+
console.print("\n[yellow]Next steps:[/yellow]")
|
|
45
|
+
console.print(" 1. Edit the configuration file")
|
|
46
|
+
console.print(" 2. Run [bold]oaeval run[/bold] to start evaluation")
|
|
47
|
+
except OSError as e:
|
|
48
|
+
raise ConfigurationError(
|
|
49
|
+
message=f"Failed to create configuration: {e}",
|
|
50
|
+
config_path=config_path,
|
|
51
|
+
) from e
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""List command for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from openagent_eval.reports.manager import ReportManager
|
|
11
|
+
from openagent_eval.cli.utils.constants import DEFAULT_OUTPUT_DIR
|
|
12
|
+
from openagent_eval.cli.utils.display import display_report_list
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def list_command(
|
|
18
|
+
limit: int = typer.Option(
|
|
19
|
+
10,
|
|
20
|
+
"--limit",
|
|
21
|
+
"-l",
|
|
22
|
+
help="Number of evaluations to show.",
|
|
23
|
+
),
|
|
24
|
+
output: str = typer.Option(
|
|
25
|
+
None,
|
|
26
|
+
"--output",
|
|
27
|
+
"-o",
|
|
28
|
+
help="Filter by output format.",
|
|
29
|
+
),
|
|
30
|
+
output_dir: str = typer.Option(
|
|
31
|
+
None,
|
|
32
|
+
"--output-dir",
|
|
33
|
+
"-d",
|
|
34
|
+
help="Directory where reports are stored (default: ./reports).",
|
|
35
|
+
),
|
|
36
|
+
) -> None:
|
|
37
|
+
"""List previous evaluation runs."""
|
|
38
|
+
console.print("[bold blue]OpenAgent Eval[/bold blue] - Evaluation History\n")
|
|
39
|
+
|
|
40
|
+
manager = ReportManager()
|
|
41
|
+
reports_dir = Path(output_dir) if output_dir else DEFAULT_OUTPUT_DIR
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
reports = manager.list_reports(reports_dir)
|
|
45
|
+
except Exception as e:
|
|
46
|
+
console.print(f"[red]Error:[/red] Failed to list reports: {e}")
|
|
47
|
+
raise typer.Exit(code=1) from e
|
|
48
|
+
|
|
49
|
+
display_report_list(reports, limit, output, reports_dir, manager)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Report command for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from openagent_eval.exceptions.cli import CommandError
|
|
11
|
+
from openagent_eval.reports.manager import ReportManager
|
|
12
|
+
from openagent_eval.reports.terminal import TerminalReport
|
|
13
|
+
from openagent_eval.cli.utils.constants import DEFAULT_OUTPUT_DIR
|
|
14
|
+
from openagent_eval.cli.utils.helpers import get_report_generator, resolve_report_id
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def report_command(
|
|
20
|
+
report_id: str = typer.Argument(
|
|
21
|
+
help="Report ID or 'latest' for the most recent report.",
|
|
22
|
+
),
|
|
23
|
+
output: str = typer.Option(
|
|
24
|
+
"terminal",
|
|
25
|
+
"--output",
|
|
26
|
+
"-o",
|
|
27
|
+
help="Output format (terminal, markdown, html, json).",
|
|
28
|
+
),
|
|
29
|
+
output_dir: str = typer.Option(
|
|
30
|
+
None,
|
|
31
|
+
"--output-dir",
|
|
32
|
+
"-d",
|
|
33
|
+
help="Directory where reports are stored (default: ./reports).",
|
|
34
|
+
),
|
|
35
|
+
) -> None:
|
|
36
|
+
"""View evaluation reports."""
|
|
37
|
+
console.print("[bold blue]OpenAgent Eval[/bold blue] - Report Viewer")
|
|
38
|
+
console.print(f"[dim]Report: {report_id}[/dim]\n")
|
|
39
|
+
|
|
40
|
+
manager = ReportManager()
|
|
41
|
+
reports_dir = Path(output_dir) if output_dir else DEFAULT_OUTPUT_DIR
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
data = resolve_report_id(report_id, reports_dir, manager)
|
|
45
|
+
except CommandError as e:
|
|
46
|
+
console.print(f"[red]Error:[/red] {e.message}")
|
|
47
|
+
raise typer.Exit(code=e.exit_code) from None
|
|
48
|
+
|
|
49
|
+
evaluation_report = manager.reconstruct(data)
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
generator = get_report_generator(output)
|
|
53
|
+
except CommandError as e:
|
|
54
|
+
console.print(f"[red]Error:[/red] {e.message}")
|
|
55
|
+
raise typer.Exit(code=e.exit_code) from None
|
|
56
|
+
|
|
57
|
+
if output == "terminal":
|
|
58
|
+
gen = TerminalReport()
|
|
59
|
+
gen.print_report(evaluation_report)
|
|
60
|
+
else:
|
|
61
|
+
report_content = generator.generate(evaluation_report)
|
|
62
|
+
console.print(report_content)
|
|
63
|
+
|
|
64
|
+
console.print(f"\n[dim]Report ID: {data.get('report_id', 'unknown')}[/dim]")
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Run command for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
10
|
+
|
|
11
|
+
from openagent_eval import __version__
|
|
12
|
+
from openagent_eval.exceptions import ConfigurationError
|
|
13
|
+
from openagent_eval.reports.manager import ReportManager
|
|
14
|
+
from openagent_eval.cli.utils.helpers import (
|
|
15
|
+
apply_output_override,
|
|
16
|
+
execute_evaluation,
|
|
17
|
+
load_config_from_path,
|
|
18
|
+
load_dataset_for_run,
|
|
19
|
+
)
|
|
20
|
+
from openagent_eval.cli.utils.display import display_run_result
|
|
21
|
+
|
|
22
|
+
console = Console()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def run_command(
|
|
26
|
+
config_path: str = typer.Argument(
|
|
27
|
+
help="Path to configuration file.",
|
|
28
|
+
),
|
|
29
|
+
output: str = typer.Option(
|
|
30
|
+
None,
|
|
31
|
+
"--output",
|
|
32
|
+
"-o",
|
|
33
|
+
help="Override output format (terminal, markdown, html, json).",
|
|
34
|
+
),
|
|
35
|
+
verbose: bool = typer.Option(
|
|
36
|
+
False,
|
|
37
|
+
"--verbose",
|
|
38
|
+
"-v",
|
|
39
|
+
help="Enable verbose output.",
|
|
40
|
+
),
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Run evaluation pipeline with the specified configuration."""
|
|
43
|
+
path = Path(config_path)
|
|
44
|
+
|
|
45
|
+
if not path.exists():
|
|
46
|
+
raise ConfigurationError(
|
|
47
|
+
message=f"Configuration file not found: {config_path}",
|
|
48
|
+
config_path=config_path,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
console.print(f"[bold blue]OpenAgent Eval[/bold blue] v{__version__}")
|
|
52
|
+
console.print(f"[dim]Configuration: {config_path}[/dim]\n")
|
|
53
|
+
|
|
54
|
+
with Progress(
|
|
55
|
+
SpinnerColumn(),
|
|
56
|
+
TextColumn("[progress.description]{task.description}"),
|
|
57
|
+
console=console,
|
|
58
|
+
) as progress:
|
|
59
|
+
task = progress.add_task("Loading configuration...", total=None)
|
|
60
|
+
config = load_config_from_path(config_path)
|
|
61
|
+
apply_output_override(config, output)
|
|
62
|
+
|
|
63
|
+
progress.update(task, description="Loading dataset...")
|
|
64
|
+
dataset_items = load_dataset_for_run(config)
|
|
65
|
+
|
|
66
|
+
progress.update(task, description="Running evaluation...")
|
|
67
|
+
report = execute_evaluation(config, dataset_items)
|
|
68
|
+
|
|
69
|
+
progress.update(task, description="Generating report...")
|
|
70
|
+
format_name = config.report.output.value
|
|
71
|
+
manager = ReportManager()
|
|
72
|
+
output_dir = Path(config.report.output_dir)
|
|
73
|
+
report_path = manager.save_report(report, output_dir)
|
|
74
|
+
|
|
75
|
+
progress.update(task, description="Complete!", completed=True)
|
|
76
|
+
|
|
77
|
+
display_run_result(report, format_name, report_path, output_dir, verbose)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Main CLI entry point for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from openagent_eval.cli.utils.callbacks import version_callback
|
|
8
|
+
from openagent_eval.cli.commands.init import init_command
|
|
9
|
+
from openagent_eval.cli.commands.run import run_command
|
|
10
|
+
from openagent_eval.cli.commands.report import report_command
|
|
11
|
+
from openagent_eval.cli.commands.compare import compare_command
|
|
12
|
+
from openagent_eval.cli.commands.list_evaluations import list_command
|
|
13
|
+
from openagent_eval.cli.commands.doctor import doctor_command
|
|
14
|
+
|
|
15
|
+
app = typer.Typer(
|
|
16
|
+
name="oaeval",
|
|
17
|
+
help="Open-source CLI framework for evaluating RAG systems and AI Agents.",
|
|
18
|
+
no_args_is_help=True,
|
|
19
|
+
add_completion=False,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@app.callback()
|
|
24
|
+
def main(
|
|
25
|
+
version: bool = typer.Option(
|
|
26
|
+
False,
|
|
27
|
+
"--version",
|
|
28
|
+
"-V",
|
|
29
|
+
help="Show version and exit.",
|
|
30
|
+
callback=version_callback,
|
|
31
|
+
is_eager=True,
|
|
32
|
+
),
|
|
33
|
+
) -> None:
|
|
34
|
+
"""OpenAgent Eval - Evaluate RAG systems and AI Agents."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Register commands with explicit names
|
|
38
|
+
app.command(name="init")(init_command)
|
|
39
|
+
app.command(name="run")(run_command)
|
|
40
|
+
app.command(name="report")(report_command)
|
|
41
|
+
app.command(name="compare")(compare_command)
|
|
42
|
+
app.command(name="list")(list_command)
|
|
43
|
+
app.command(name="doctor")(doctor_command)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""CLI callbacks for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def version_callback(value: bool) -> None:
|
|
9
|
+
"""Print version and exit."""
|
|
10
|
+
if value:
|
|
11
|
+
from openagent_eval import __version__
|
|
12
|
+
|
|
13
|
+
typer.echo(f"OpenAgent Eval v{__version__}")
|
|
14
|
+
raise typer.Exit()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""CLI constants for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
DEFAULT_OUTPUT_DIR = Path("./reports")
|
|
8
|
+
|
|
9
|
+
DEFAULT_CONFIG_CONTENT = """\
|
|
10
|
+
# OpenAgent Eval Configuration
|
|
11
|
+
# See documentation for options: https://github.com/OpenAgentHQ/openagent-eval
|
|
12
|
+
#
|
|
13
|
+
# For a fully offline dry-run (no API keys / vector store required), set:
|
|
14
|
+
# llm.provider: mock
|
|
15
|
+
# retriever.provider: mock
|
|
16
|
+
|
|
17
|
+
dataset:
|
|
18
|
+
path: data/questions.json
|
|
19
|
+
# limit: 100
|
|
20
|
+
|
|
21
|
+
llm:
|
|
22
|
+
provider: openai
|
|
23
|
+
model: gpt-4o-mini
|
|
24
|
+
temperature: 0.0
|
|
25
|
+
|
|
26
|
+
retriever:
|
|
27
|
+
provider: chroma
|
|
28
|
+
settings:
|
|
29
|
+
collection_name: my_collection
|
|
30
|
+
|
|
31
|
+
metrics:
|
|
32
|
+
retrieval:
|
|
33
|
+
- context_precision
|
|
34
|
+
- context_recall
|
|
35
|
+
- mrr
|
|
36
|
+
generation:
|
|
37
|
+
- faithfulness
|
|
38
|
+
- answer_relevancy
|
|
39
|
+
performance:
|
|
40
|
+
- latency
|
|
41
|
+
cost:
|
|
42
|
+
- token_count
|
|
43
|
+
|
|
44
|
+
report:
|
|
45
|
+
output: terminal
|
|
46
|
+
output_dir: ./reports
|
|
47
|
+
"""
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""CLI display helpers for OpenAgent Eval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def display_run_result(
|
|
15
|
+
report: Any,
|
|
16
|
+
format_name: str,
|
|
17
|
+
report_path: Path,
|
|
18
|
+
output_dir: Path,
|
|
19
|
+
verbose: bool,
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Display the evaluation run result."""
|
|
22
|
+
console.print("\n[green]OK[/green] Evaluation complete!")
|
|
23
|
+
|
|
24
|
+
if hasattr(report, "summary"):
|
|
25
|
+
summary = report.summary
|
|
26
|
+
total = summary.get("total_items", summary.get("total", 0))
|
|
27
|
+
errors = summary.get("failed_evaluations", summary.get("errors", 0))
|
|
28
|
+
console.print(f"[dim]Items: {total} | Errors: {errors}[/dim]")
|
|
29
|
+
|
|
30
|
+
console.print(f"[dim]Report saved to: {report_path}[/dim]")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def display_report_list(
|
|
34
|
+
reports: list[dict[str, str]],
|
|
35
|
+
limit: int,
|
|
36
|
+
output_filter: str | None,
|
|
37
|
+
output_dir: Path,
|
|
38
|
+
manager: Any,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Display a table of evaluation reports."""
|
|
41
|
+
from openagent_eval.reports.manager import ReportManager
|
|
42
|
+
|
|
43
|
+
if not reports:
|
|
44
|
+
console.print("[yellow]No evaluations found.[/yellow]")
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
if output_filter:
|
|
48
|
+
filtered: list[dict[str, str]] = []
|
|
49
|
+
for r in reports:
|
|
50
|
+
try:
|
|
51
|
+
data = manager.load_report(r["report_id"], output_dir)
|
|
52
|
+
report_format = (
|
|
53
|
+
data.get("config", {}).get("report", {}).get("output", "terminal")
|
|
54
|
+
)
|
|
55
|
+
if report_format == output_filter:
|
|
56
|
+
filtered.append(r)
|
|
57
|
+
except (FileNotFoundError, KeyError):
|
|
58
|
+
continue
|
|
59
|
+
reports = filtered
|
|
60
|
+
|
|
61
|
+
reports = reports[:limit]
|
|
62
|
+
|
|
63
|
+
table = Table(title="Recent Evaluations")
|
|
64
|
+
table.add_column("ID", style="cyan")
|
|
65
|
+
table.add_column("Date", style="green")
|
|
66
|
+
table.add_column("Config", style="yellow")
|
|
67
|
+
table.add_column("Status", style="bold")
|
|
68
|
+
|
|
69
|
+
for r in reports:
|
|
70
|
+
report_id = r["report_id"]
|
|
71
|
+
created_at = r.get("created_at", "unknown")
|
|
72
|
+
config_name = "unknown"
|
|
73
|
+
status = "OK"
|
|
74
|
+
try:
|
|
75
|
+
data = manager.load_report(report_id, output_dir)
|
|
76
|
+
config_name = data.get("config", {}).get("dataset", {}).get("path", "unknown")
|
|
77
|
+
errors = data.get("errors", [])
|
|
78
|
+
status = "[green]OK[/green]" if not errors else "[red]FAILED[/red]"
|
|
79
|
+
except (FileNotFoundError, KeyError):
|
|
80
|
+
status = "[yellow]UNKNOWN[/yellow]"
|
|
81
|
+
|
|
82
|
+
if created_at and "T" in created_at:
|
|
83
|
+
created_at = created_at.split("T")[0]
|
|
84
|
+
|
|
85
|
+
table.add_row(report_id, created_at, config_name, status)
|
|
86
|
+
|
|
87
|
+
console.print(table)
|
|
88
|
+
console.print(f"\n[dim]Showing {len(reports)} evaluations[/dim]")
|