fluxloop-cli 0.2.17__tar.gz → 0.2.18__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 (50) hide show
  1. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/PKG-INFO +2 -1
  2. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/README.md +1 -0
  3. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/__init__.py +1 -1
  4. fluxloop_cli-0.2.18/fluxloop_cli/commands/__init__.py +15 -0
  5. fluxloop_cli-0.2.18/fluxloop_cli/commands/evaluate.py +183 -0
  6. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/__init__.py +37 -0
  7. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/config.py +707 -0
  8. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/engine/__init__.py +7 -0
  9. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/engine/analysis.py +262 -0
  10. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/engine/core.py +387 -0
  11. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/engine/reporting/__init__.py +49 -0
  12. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/engine/reporting/html.py +257 -0
  13. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/engine/reporting/markdown.py +156 -0
  14. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/engine/success.py +325 -0
  15. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/llm.py +310 -0
  16. fluxloop_cli-0.2.18/fluxloop_cli/evaluation/rules.py +262 -0
  17. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/main.py +2 -1
  18. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/templates.py +107 -16
  19. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli.egg-info/PKG-INFO +2 -1
  20. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli.egg-info/SOURCES.txt +13 -0
  21. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/pyproject.toml +1 -1
  22. fluxloop_cli-0.2.18/tests/test_evaluate_command.py +335 -0
  23. fluxloop_cli-0.2.17/fluxloop_cli/commands/__init__.py +0 -5
  24. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/arg_binder.py +0 -0
  25. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/commands/config.py +0 -0
  26. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/commands/doctor.py +0 -0
  27. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/commands/generate.py +0 -0
  28. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/commands/init.py +0 -0
  29. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/commands/parse.py +0 -0
  30. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/commands/record.py +0 -0
  31. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/commands/run.py +0 -0
  32. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/commands/status.py +0 -0
  33. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/config_loader.py +0 -0
  34. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/config_schema.py +0 -0
  35. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/constants.py +0 -0
  36. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/input_generator.py +0 -0
  37. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/llm_generator.py +0 -0
  38. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/project_paths.py +0 -0
  39. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/runner.py +0 -0
  40. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/target_loader.py +0 -0
  41. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli/validators.py +0 -0
  42. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli.egg-info/dependency_links.txt +0 -0
  43. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli.egg-info/entry_points.txt +0 -0
  44. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli.egg-info/requires.txt +0 -0
  45. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/fluxloop_cli.egg-info/top_level.txt +0 -0
  46. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/setup.cfg +0 -0
  47. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/tests/test_arg_binder.py +0 -0
  48. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/tests/test_config_command.py +0 -0
  49. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/tests/test_input_generator.py +0 -0
  50. {fluxloop_cli-0.2.17 → fluxloop_cli-0.2.18}/tests/test_target_loader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluxloop-cli
3
- Version: 0.2.17
3
+ Version: 0.2.18
4
4
  Summary: FluxLoop CLI for running agent simulations
5
5
  Author-email: FluxLoop Team <team@fluxloop.dev>
6
6
  License: Apache-2.0
@@ -66,6 +66,7 @@ The legacy `setting.yaml` is still supported, but new projects created with
66
66
  - `fluxloop generate inputs` – produce input variations for the active project
67
67
  - `fluxloop run experiment` – execute an experiment using `configs/simulation.yaml`
68
68
  - `fluxloop parse experiment` – convert experiment outputs into readable artifacts
69
+ - `fluxloop evaluate experiment` – score experiment outputs and generate evaluation reports
69
70
  - `fluxloop config set-llm` – update LLM provider/model in `configs/input.yaml`
70
71
  - `fluxloop record enable|disable|status` – toggle recording mode across `.env` and simulation config
71
72
  - `fluxloop doctor` – summarize Python, FluxLoop CLI/MCP, and MCP index state for the active environment
@@ -26,6 +26,7 @@ The legacy `setting.yaml` is still supported, but new projects created with
26
26
  - `fluxloop generate inputs` – produce input variations for the active project
27
27
  - `fluxloop run experiment` – execute an experiment using `configs/simulation.yaml`
28
28
  - `fluxloop parse experiment` – convert experiment outputs into readable artifacts
29
+ - `fluxloop evaluate experiment` – score experiment outputs and generate evaluation reports
29
30
  - `fluxloop config set-llm` – update LLM provider/model in `configs/input.yaml`
30
31
  - `fluxloop record enable|disable|status` – toggle recording mode across `.env` and simulation config
31
32
  - `fluxloop doctor` – summarize Python, FluxLoop CLI/MCP, and MCP index state for the active environment
@@ -2,7 +2,7 @@
2
2
  FluxLoop CLI - Command-line interface for running agent simulations.
3
3
  """
4
4
 
5
- __version__ = "0.2.17"
5
+ __version__ = "0.2.18"
6
6
 
7
7
  from .main import app
8
8
 
@@ -0,0 +1,15 @@
1
+ """CLI commands."""
2
+
3
+ from . import config, doctor, evaluate, generate, init, parse, record, run, status
4
+
5
+ __all__ = [
6
+ "config",
7
+ "doctor",
8
+ "evaluate",
9
+ "generate",
10
+ "init",
11
+ "parse",
12
+ "record",
13
+ "run",
14
+ "status",
15
+ ]
@@ -0,0 +1,183 @@
1
+ """Evaluate command for scoring experiment outputs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ import typer
9
+ from rich.console import Console
10
+
11
+ from ..evaluation import EvaluationOptions, load_evaluation_config, run_evaluation
12
+
13
+ console = Console()
14
+ app = typer.Typer(help="Evaluate experiment outputs and generate reports.")
15
+
16
+
17
+ @app.command()
18
+ def experiment(
19
+ experiment_dir: Path = typer.Argument(
20
+ ...,
21
+ help="Path to the experiment output directory",
22
+ exists=True,
23
+ dir_okay=True,
24
+ file_okay=False,
25
+ resolve_path=True,
26
+ ),
27
+ config: Path = typer.Option(
28
+ Path("configs/evaluation.yaml"),
29
+ "--config",
30
+ "-c",
31
+ help="Path to evaluation configuration file",
32
+ ),
33
+ output: Path = typer.Option(
34
+ Path("evaluation"),
35
+ "--output",
36
+ "-o",
37
+ help="Output directory name (relative to the experiment directory)",
38
+ ),
39
+ overwrite: bool = typer.Option(
40
+ False,
41
+ "--overwrite",
42
+ help="Overwrite output directory if it already exists",
43
+ ),
44
+ llm_api_key: Optional[str] = typer.Option(
45
+ None,
46
+ "--llm-api-key",
47
+ help="LLM API key for judge evaluators (optional)",
48
+ envvar="FLUXLOOP_LLM_API_KEY",
49
+ ),
50
+ sample_rate: Optional[float] = typer.Option(
51
+ None,
52
+ "--sample-rate",
53
+ help="Override LLM evaluation sample rate (0.0-1.0)",
54
+ ),
55
+ max_llm_calls: Optional[int] = typer.Option(
56
+ None,
57
+ "--max-llm",
58
+ help="Maximum number of LLM evaluations to run",
59
+ ),
60
+ report: Optional[str] = typer.Option(
61
+ None,
62
+ "--report",
63
+ help="Report output format to generate (md, html, both)",
64
+ metavar="FORMAT",
65
+ ),
66
+ report_template: Optional[Path] = typer.Option(
67
+ None,
68
+ "--report-template",
69
+ help="Path to custom HTML report template",
70
+ exists=False,
71
+ file_okay=True,
72
+ dir_okay=False,
73
+ ),
74
+ baseline: Optional[Path] = typer.Option(
75
+ None,
76
+ "--baseline",
77
+ help="Path to baseline summary.json file for comparisons",
78
+ exists=False,
79
+ file_okay=True,
80
+ dir_okay=False,
81
+ ),
82
+ verbose: bool = typer.Option(
83
+ False,
84
+ "--verbose",
85
+ help="Enable verbose logging",
86
+ ),
87
+ ):
88
+ """
89
+ Evaluate experiment outputs and generate aggregate reports.
90
+ """
91
+
92
+ resolved_experiment_dir = experiment_dir.resolve()
93
+ if not resolved_experiment_dir.is_dir():
94
+ raise typer.BadParameter(f"Experiment directory not found: {resolved_experiment_dir}")
95
+
96
+ if not config.is_absolute():
97
+ config_path = (Path.cwd() / config).resolve()
98
+ else:
99
+ config_path = config
100
+
101
+ if sample_rate is not None and not 0.0 <= sample_rate <= 1.0:
102
+ raise typer.BadParameter("--sample-rate must be between 0.0 and 1.0")
103
+
104
+ if max_llm_calls is not None and max_llm_calls < 0:
105
+ raise typer.BadParameter("--max-llm must be a non-negative integer")
106
+
107
+ report_format: Optional[str] = None
108
+ if report is not None:
109
+ candidate = report.lower()
110
+ if candidate not in {"md", "html", "both"}:
111
+ raise typer.BadParameter("--report must be one of: md, html, both")
112
+ report_format = candidate
113
+
114
+ report_template_path: Optional[Path] = None
115
+ if report_template is not None:
116
+ resolved_template = report_template
117
+ if not resolved_template.is_absolute():
118
+ resolved_template = (Path.cwd() / resolved_template).resolve()
119
+ if not resolved_template.exists():
120
+ raise typer.BadParameter(f"Report template not found: {resolved_template}")
121
+ report_template_path = resolved_template
122
+
123
+ baseline_path: Optional[Path] = None
124
+ if baseline is not None:
125
+ resolved_baseline = baseline
126
+ if not resolved_baseline.is_absolute():
127
+ resolved_baseline = (Path.cwd() / resolved_baseline).resolve()
128
+ baseline_path = resolved_baseline
129
+
130
+ try:
131
+ evaluation_config = load_evaluation_config(config_path)
132
+ except FileNotFoundError as exc:
133
+ raise typer.BadParameter(str(exc)) from exc
134
+ except Exception as exc: # noqa: BLE001
135
+ raise typer.BadParameter(f"Failed to load evaluation config: {exc}") from exc
136
+
137
+ output_dir = output
138
+ if not output_dir.is_absolute():
139
+ output_dir = resolved_experiment_dir / output_dir
140
+
141
+ options = EvaluationOptions(
142
+ output_dir=output_dir,
143
+ overwrite=overwrite,
144
+ llm_api_key=llm_api_key,
145
+ sample_rate=sample_rate,
146
+ max_llm_calls=max_llm_calls,
147
+ verbose=verbose,
148
+ report_format=report_format, # type: ignore[arg-type]
149
+ report_template=report_template_path,
150
+ baseline_path=baseline_path,
151
+ )
152
+
153
+ effective_report_format = report_format or evaluation_config.report.output
154
+ template_display = (
155
+ str(report_template_path)
156
+ if report_template_path is not None
157
+ else evaluation_config.report.template_path or "default"
158
+ )
159
+ baseline_display = (
160
+ str(baseline_path)
161
+ if baseline_path is not None
162
+ else evaluation_config.additional_analysis.comparison.baseline_path
163
+ )
164
+
165
+ message_lines = [
166
+ f"📊 Evaluating experiment at [cyan]{resolved_experiment_dir}[/cyan]",
167
+ f"⚙️ Config: [magenta]{config_path}[/magenta]",
168
+ f"📁 Output: [green]{output_dir}[/green]",
169
+ f"📝 Report: [yellow]{effective_report_format.upper()}[/yellow] (template: [cyan]{template_display}[/cyan])",
170
+ ]
171
+ if baseline_display:
172
+ message_lines.append(f"📈 Baseline: [cyan]{baseline_display}[/cyan]")
173
+
174
+ console.print("\n".join(message_lines))
175
+
176
+ summary = run_evaluation(resolved_experiment_dir, evaluation_config, options)
177
+
178
+ if verbose:
179
+ console.print("\n[bold]Summary[/bold]")
180
+ for key, value in summary.items():
181
+ console.print(f"• {key}: {value}")
182
+
183
+
@@ -0,0 +1,37 @@
1
+ """
2
+ Evaluation framework for FluxLoop experiments.
3
+ """
4
+
5
+ from .config import (
6
+ AdditionalAnalysisConfig,
7
+ AdvancedConfig,
8
+ AggregateConfig,
9
+ EvaluationConfig,
10
+ EvaluationGoalConfig,
11
+ EvaluatorConfig,
12
+ LimitsConfig,
13
+ ReportConfig,
14
+ ReportOutput,
15
+ RuleDefinition,
16
+ SuccessCriteriaConfig,
17
+ load_evaluation_config,
18
+ )
19
+ from .engine import EvaluationOptions, run_evaluation
20
+
21
+ __all__ = [
22
+ "AdditionalAnalysisConfig",
23
+ "AdvancedConfig",
24
+ "AggregateConfig",
25
+ "EvaluationConfig",
26
+ "EvaluationGoalConfig",
27
+ "EvaluatorConfig",
28
+ "LimitsConfig",
29
+ "ReportConfig",
30
+ "ReportOutput",
31
+ "RuleDefinition",
32
+ "SuccessCriteriaConfig",
33
+ "EvaluationOptions",
34
+ "load_evaluation_config",
35
+ "run_evaluation",
36
+ ]
37
+