fluxloop-cli 0.2.19__tar.gz → 0.2.21__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.
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/PKG-INFO +1 -1
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/__init__.py +1 -1
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/commands/evaluate.py +16 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/commands/init.py +10 -22
- fluxloop_cli-0.2.21/fluxloop_cli/environment.py +71 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/config.py +15 -1
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/engine/reporting/html.py +13 -13
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/runner.py +10 -13
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/templates.py +4 -3
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli.egg-info/PKG-INFO +1 -1
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli.egg-info/SOURCES.txt +1 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/pyproject.toml +1 -1
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/tests/test_evaluate_command.py +48 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/README.md +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/arg_binder.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/commands/__init__.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/commands/config.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/commands/doctor.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/commands/generate.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/commands/parse.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/commands/record.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/commands/run.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/commands/status.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/config_loader.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/config_schema.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/constants.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/__init__.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/engine/__init__.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/engine/analysis.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/engine/core.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/engine/reporting/__init__.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/engine/reporting/markdown.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/engine/success.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/llm.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/prompts/__init__.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/rules.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/input_generator.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/llm_generator.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/main.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/project_paths.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/target_loader.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/validators.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli.egg-info/dependency_links.txt +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli.egg-info/entry_points.txt +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli.egg-info/requires.txt +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli.egg-info/top_level.txt +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/setup.cfg +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/tests/test_analysis_recommendations.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/tests/test_arg_binder.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/tests/test_config_command.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/tests/test_evaluation_llm.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/tests/test_input_generator.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/tests/test_prompt_library.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/tests/test_success_criteria.py +0 -0
- {fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/tests/test_target_loader.py +0 -0
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import os
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Optional
|
|
7
8
|
|
|
8
9
|
import typer
|
|
9
10
|
from rich.console import Console
|
|
10
11
|
|
|
12
|
+
from ..environment import load_env_chain
|
|
11
13
|
from ..evaluation import EvaluationOptions, load_evaluation_config, run_evaluation
|
|
12
14
|
|
|
13
15
|
console = Console()
|
|
@@ -134,6 +136,20 @@ def experiment(
|
|
|
134
136
|
except Exception as exc: # noqa: BLE001
|
|
135
137
|
raise typer.BadParameter(f"Failed to load evaluation config: {exc}") from exc
|
|
136
138
|
|
|
139
|
+
def _log_env_error(path: Path, exc: Exception) -> None:
|
|
140
|
+
console.log(
|
|
141
|
+
f"[yellow]Warning:[/yellow] Failed to load environment from {path}: {exc}"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
load_env_chain(
|
|
145
|
+
evaluation_config.get_source_dir(),
|
|
146
|
+
refresh_config=True,
|
|
147
|
+
on_error=_log_env_error,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if llm_api_key is None:
|
|
151
|
+
llm_api_key = os.getenv("FLUXLOOP_LLM_API_KEY") or os.getenv("OPENAI_API_KEY")
|
|
152
|
+
|
|
137
153
|
output_dir = output
|
|
138
154
|
if not output_dir.is_absolute():
|
|
139
155
|
output_dir = resolved_experiment_dir / output_dir
|
|
@@ -137,27 +137,11 @@ def project(
|
|
|
137
137
|
section_path = section_paths[key]
|
|
138
138
|
section_path.write_text(content)
|
|
139
139
|
|
|
140
|
-
# Create project .env (
|
|
141
|
-
root_env_file = root_dir / ".env"
|
|
140
|
+
# Create project .env (single source of truth)
|
|
142
141
|
console.print("🔐 Creating project .env...")
|
|
143
142
|
recordings_dir = project_path / "recordings"
|
|
144
143
|
recordings_dir.mkdir(exist_ok=True)
|
|
145
|
-
|
|
146
|
-
# If a root .env exists, seed project .env with its contents so project can override locally
|
|
147
|
-
if root_env_file.exists():
|
|
148
|
-
try:
|
|
149
|
-
root_contents = root_env_file.read_text()
|
|
150
|
-
except Exception:
|
|
151
|
-
root_contents = ""
|
|
152
|
-
merged = (
|
|
153
|
-
"# Project .env (seeded from FluxLoop root .env)\n"
|
|
154
|
-
+ (root_contents if root_contents.endswith("\n") else root_contents + "\n")
|
|
155
|
-
+ "\n# Project-specific overrides (take precedence)\n"
|
|
156
|
-
)
|
|
157
|
-
env_file.write_text(merged)
|
|
158
|
-
console.print("[dim]Seeded from root .env (project overrides take precedence).[/dim]")
|
|
159
|
-
else:
|
|
160
|
-
env_file.write_text(create_env_file())
|
|
144
|
+
env_file.write_text(create_env_file())
|
|
161
145
|
|
|
162
146
|
# Update .gitignore
|
|
163
147
|
if not gitignore_file.exists():
|
|
@@ -195,12 +179,16 @@ def project(
|
|
|
195
179
|
# Show next steps
|
|
196
180
|
console.print("\n[bold]Next steps:[/bold]")
|
|
197
181
|
console.print("1. Review configs in [cyan]configs/[/cyan] (project/input/simulation/evaluation)")
|
|
198
|
-
console.print("2.
|
|
182
|
+
console.print("2. Configure secrets via [cyan].env[/cyan] or [green]fluxloop config set-llm[/green]")
|
|
199
183
|
if with_example:
|
|
200
|
-
console.print("3.
|
|
184
|
+
console.print("3. Customize the sample agent in [cyan]examples/simple_agent.py[/cyan]")
|
|
201
185
|
else:
|
|
202
|
-
console.print("3.
|
|
203
|
-
|
|
186
|
+
console.print("3. Create your agent: [green]fluxloop init agent <name>[/green]")
|
|
187
|
+
console.print("4. Generate inputs: [green]fluxloop generate inputs[/green]")
|
|
188
|
+
console.print("5. Run the experiment: [green]fluxloop run experiment[/green]")
|
|
189
|
+
console.print("6. Parse outputs: [green]fluxloop parse experiment[/green]")
|
|
190
|
+
console.print("7. Evaluate results (optional): [green]fluxloop evaluate experiment[/green]")
|
|
191
|
+
console.print("8. Diagnose environment anytime: [green]fluxloop doctor[/green]")
|
|
204
192
|
|
|
205
193
|
|
|
206
194
|
@app.command()
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utilities for applying project environment settings across CLI commands.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Callable, Iterable, Optional, Set
|
|
9
|
+
|
|
10
|
+
import fluxloop
|
|
11
|
+
|
|
12
|
+
EnvErrorHandler = Callable[[Path, Exception], None]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_env_chain(
|
|
16
|
+
source_dir: Optional[Path],
|
|
17
|
+
*,
|
|
18
|
+
additional_paths: Optional[Iterable[Path]] = None,
|
|
19
|
+
refresh_config: bool = True,
|
|
20
|
+
on_error: Optional[EnvErrorHandler] = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Load environment variables from `.env` files related to a project.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
source_dir:
|
|
28
|
+
Directory associated with the current config (usually the config file's parent).
|
|
29
|
+
additional_paths:
|
|
30
|
+
Extra `.env` files to consider (applied after the standard chain).
|
|
31
|
+
refresh_config:
|
|
32
|
+
Whether to refresh the fluxloop config after loading.
|
|
33
|
+
on_error:
|
|
34
|
+
Optional callback invoked when loading a path fails.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
if source_dir is None:
|
|
38
|
+
candidates: Iterable[Path] = additional_paths or []
|
|
39
|
+
else:
|
|
40
|
+
resolved_source = source_dir.resolve()
|
|
41
|
+
parents: list[Path] = []
|
|
42
|
+
parent = resolved_source.parent
|
|
43
|
+
if parent != resolved_source:
|
|
44
|
+
parents.append(parent / ".env")
|
|
45
|
+
parents.append(resolved_source / ".env")
|
|
46
|
+
|
|
47
|
+
extra = list(additional_paths or [])
|
|
48
|
+
candidates = [*parents, *extra]
|
|
49
|
+
|
|
50
|
+
seen: Set[Path] = set()
|
|
51
|
+
for path in candidates:
|
|
52
|
+
# Avoid duplicate loads and normalize the path
|
|
53
|
+
resolved = path.resolve()
|
|
54
|
+
if resolved in seen:
|
|
55
|
+
continue
|
|
56
|
+
seen.add(resolved)
|
|
57
|
+
|
|
58
|
+
if not resolved.exists():
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
fluxloop.load_env(resolved, override=True, refresh_config=refresh_config)
|
|
63
|
+
except Exception as exc: # noqa: BLE001
|
|
64
|
+
if on_error is None:
|
|
65
|
+
raise
|
|
66
|
+
on_error(resolved, exc)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
__all__ = ["load_env_chain", "EnvErrorHandler"]
|
|
70
|
+
|
|
71
|
+
|
|
@@ -230,6 +230,14 @@ class EvaluationConfig:
|
|
|
230
230
|
report: ReportConfig = field(default_factory=ReportConfig)
|
|
231
231
|
advanced: AdvancedConfig = field(default_factory=AdvancedConfig)
|
|
232
232
|
|
|
233
|
+
def set_source_dir(self, source_dir: Path) -> None:
|
|
234
|
+
"""Remember the directory where this config file was loaded from."""
|
|
235
|
+
self._source_dir = source_dir
|
|
236
|
+
|
|
237
|
+
def get_source_dir(self) -> Optional[Path]:
|
|
238
|
+
"""Return the directory where the config file was loaded from."""
|
|
239
|
+
return getattr(self, "_source_dir", None)
|
|
240
|
+
|
|
233
241
|
|
|
234
242
|
def _load_yaml(path: Path) -> Dict[str, Any]:
|
|
235
243
|
if not path.exists():
|
|
@@ -704,7 +712,9 @@ def load_evaluation_config(path: Path) -> EvaluationConfig:
|
|
|
704
712
|
report = _parse_report(data.get("report"))
|
|
705
713
|
advanced = _parse_advanced(data.get("advanced"))
|
|
706
714
|
|
|
707
|
-
|
|
715
|
+
config_dir = path.parent.resolve()
|
|
716
|
+
|
|
717
|
+
config = EvaluationConfig(
|
|
708
718
|
evaluators=evaluators,
|
|
709
719
|
aggregate=aggregate,
|
|
710
720
|
limits=limits,
|
|
@@ -715,4 +725,8 @@ def load_evaluation_config(path: Path) -> EvaluationConfig:
|
|
|
715
725
|
advanced=advanced,
|
|
716
726
|
)
|
|
717
727
|
|
|
728
|
+
config.set_source_dir(config_dir)
|
|
729
|
+
|
|
730
|
+
return config
|
|
731
|
+
|
|
718
732
|
|
{fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/engine/reporting/html.py
RENAMED
|
@@ -48,8 +48,8 @@ DEFAULT_TEMPLATE = r"""<!DOCTYPE html>
|
|
|
48
48
|
<main class="max-w-6xl mx-auto p-8 space-y-6">
|
|
49
49
|
<header class="space-y-6">
|
|
50
50
|
<div>
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
<h1 class="text-3xl font-bold">[[TITLE]]</h1>
|
|
52
|
+
<p class="text-sm text-slate-300">Generated at [[DATE]]</p>
|
|
53
53
|
</div>
|
|
54
54
|
<nav class="flex flex-wrap gap-2" role="tablist" aria-label="Report sections">
|
|
55
55
|
<button class="tab-button px-4 py-2 rounded-full bg-sky-500/20 border border-sky-400 text-sky-200 font-semibold" data-tab-button="summary">Executive Summary</button>
|
|
@@ -203,14 +203,14 @@ DEFAULT_TEMPLATE = r"""<!DOCTYPE html>
|
|
|
203
203
|
});
|
|
204
204
|
}
|
|
205
205
|
container.innerHTML = cards
|
|
206
|
-
|
|
206
|
+
.map(
|
|
207
207
|
(card) => `
|
|
208
208
|
<div class="bg-slate-900 rounded-xl p-4 border border-slate-800 shadow-inner">
|
|
209
209
|
<p class="text-slate-400 text-xs uppercase tracking-wide">${card.label}</p>
|
|
210
210
|
<p class="text-2xl font-semibold mt-2">${card.value}</p>
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
</div>
|
|
212
|
+
`
|
|
213
|
+
)
|
|
214
214
|
.join("");
|
|
215
215
|
}
|
|
216
216
|
|
|
@@ -236,21 +236,21 @@ DEFAULT_TEMPLATE = r"""<!DOCTYPE html>
|
|
|
236
236
|
if (!payload) return;
|
|
237
237
|
const checks = Object.entries(payload);
|
|
238
238
|
if (!checks.length) return;
|
|
239
|
-
|
|
239
|
+
const sectionTitle = section.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
240
240
|
const wrapper = document.createElement("div");
|
|
241
241
|
wrapper.className = "bg-slate-900 border border-slate-800 rounded-xl p-4 space-y-2";
|
|
242
242
|
wrapper.innerHTML = `<h3 class="text-lg font-semibold">${sectionTitle}</h3>`;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
const list = document.createElement("ul");
|
|
244
|
+
list.className = "space-y-1 text-sm text-slate-300";
|
|
245
|
+
for (const [name, details] of checks) {
|
|
246
246
|
const prettyName = toTitleCase(name);
|
|
247
|
-
|
|
247
|
+
const status = details.met === true ? "✅ Met" : details.met === false ? "❌ Not met" : "⚪️ Not evaluated";
|
|
248
248
|
const meta = { ...details };
|
|
249
249
|
delete meta.met;
|
|
250
250
|
const extra =
|
|
251
251
|
Object.keys(meta).length > 0 ? `<span class="text-slate-400"> ${JSON.stringify(meta)}</span>` : "";
|
|
252
252
|
list.innerHTML += `<li>${status} · ${prettyName}${extra}</li>`;
|
|
253
|
-
|
|
253
|
+
}
|
|
254
254
|
wrapper.appendChild(list);
|
|
255
255
|
container.appendChild(wrapper);
|
|
256
256
|
});
|
|
@@ -453,7 +453,7 @@ DEFAULT_TEMPLATE = r"""<!DOCTYPE html>
|
|
|
453
453
|
const shown = filtered.length;
|
|
454
454
|
const suffix = total === 1 ? "" : "s";
|
|
455
455
|
hint.textContent = `${shown} of ${total} trace${suffix} shown`;
|
|
456
|
-
|
|
456
|
+
}
|
|
457
457
|
}
|
|
458
458
|
|
|
459
459
|
initTabs();
|
|
@@ -21,6 +21,7 @@ from fluxloop.buffer import EventBuffer
|
|
|
21
21
|
from fluxloop.schemas import ExperimentConfig, PersonaConfig
|
|
22
22
|
from rich.console import Console
|
|
23
23
|
|
|
24
|
+
from .environment import load_env_chain
|
|
24
25
|
from .target_loader import TargetLoader
|
|
25
26
|
from .arg_binder import ArgBinder
|
|
26
27
|
|
|
@@ -97,21 +98,17 @@ class ExperimentRunner:
|
|
|
97
98
|
"""Load environment variables from .env and runner settings."""
|
|
98
99
|
|
|
99
100
|
source_dir = self.config.get_source_dir()
|
|
100
|
-
env_candidates: List[Path] = []
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
env_candidates.append(parent / ".env")
|
|
107
|
-
env_candidates.append(source_dir / ".env")
|
|
102
|
+
def _log_env_error(path: Path, exc: Exception) -> None:
|
|
103
|
+
console.log(
|
|
104
|
+
f"[yellow]Warning:[/yellow] Failed to load environment from {path}: {exc}"
|
|
105
|
+
)
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
console.log(f"[yellow]Warning:[/yellow] Failed to load environment from {candidate}")
|
|
107
|
+
load_env_chain(
|
|
108
|
+
source_dir,
|
|
109
|
+
refresh_config=True,
|
|
110
|
+
on_error=_log_env_error,
|
|
111
|
+
)
|
|
115
112
|
|
|
116
113
|
env_vars = getattr(self.config.runner, "environment_vars", {}) or {}
|
|
117
114
|
for key, value in env_vars.items():
|
|
@@ -522,15 +522,16 @@ def create_env_file() -> str:
|
|
|
522
522
|
FLUXLOOP_SAMPLE_RATE=1.0
|
|
523
523
|
# Argument Recording (global toggle)
|
|
524
524
|
FLUXLOOP_RECORD_ARGS=false
|
|
525
|
-
FLUXLOOP_RECORDING_FILE=
|
|
525
|
+
FLUXLOOP_RECORDING_FILE=
|
|
526
|
+
# Example: recordings/args_recording.jsonl (project-relative) or absolute path
|
|
526
527
|
|
|
527
528
|
# Service Configuration
|
|
528
529
|
FLUXLOOP_SERVICE_NAME=my-agent
|
|
529
530
|
FLUXLOOP_ENVIRONMENT=development
|
|
530
531
|
|
|
531
532
|
# LLM API Keys (if needed)
|
|
532
|
-
|
|
533
|
-
# ANTHROPIC_API_KEY=
|
|
533
|
+
OPENAI_API_KEY=
|
|
534
|
+
# ANTHROPIC_API_KEY=
|
|
534
535
|
|
|
535
536
|
# Other Configuration
|
|
536
537
|
# Add your custom environment variables here
|
|
@@ -333,3 +333,51 @@ def test_evaluate_phase2_extended_outputs(tmp_path: Path) -> None:
|
|
|
333
333
|
assert "outliers" in performance_analysis and "trends" in performance_analysis
|
|
334
334
|
comparison = analysis["comparison"]
|
|
335
335
|
assert "baseline_path" in comparison and comparison["baseline_path"].endswith("baseline_summary.json")
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def test_evaluate_loads_env_for_llm(tmp_path: Path, monkeypatch) -> None:
|
|
339
|
+
project_dir = tmp_path
|
|
340
|
+
configs_dir = project_dir / "configs"
|
|
341
|
+
configs_dir.mkdir()
|
|
342
|
+
|
|
343
|
+
env_path = project_dir / ".env"
|
|
344
|
+
env_path.write_text("FLUXLOOP_LLM_API_KEY=sk-test-key\n", encoding="utf-8")
|
|
345
|
+
|
|
346
|
+
eval_config = configs_dir / "evaluation.yaml"
|
|
347
|
+
_write_eval_config(eval_config, include_llm=True)
|
|
348
|
+
|
|
349
|
+
experiment_dir = project_dir / "experiments" / "run_1"
|
|
350
|
+
experiment_dir.mkdir(parents=True, exist_ok=True)
|
|
351
|
+
_write_trace_summary(experiment_dir / "trace_summary.jsonl")
|
|
352
|
+
|
|
353
|
+
captured: dict[str, object] = {}
|
|
354
|
+
|
|
355
|
+
def fake_run_evaluation(exp_dir, config, options):
|
|
356
|
+
captured["llm_api_key"] = options.llm_api_key
|
|
357
|
+
captured["sample_rate"] = options.sample_rate
|
|
358
|
+
return {}
|
|
359
|
+
|
|
360
|
+
monkeypatch.delenv("FLUXLOOP_LLM_API_KEY", raising=False)
|
|
361
|
+
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
362
|
+
monkeypatch.setattr(
|
|
363
|
+
"fluxloop_cli.commands.evaluate.run_evaluation", fake_run_evaluation
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
result = runner.invoke(
|
|
367
|
+
cli_main.app,
|
|
368
|
+
[
|
|
369
|
+
"evaluate",
|
|
370
|
+
"experiment",
|
|
371
|
+
str(experiment_dir),
|
|
372
|
+
"--config",
|
|
373
|
+
str(eval_config),
|
|
374
|
+
"--output",
|
|
375
|
+
"evaluation",
|
|
376
|
+
],
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
monkeypatch.delenv("FLUXLOOP_LLM_API_KEY", raising=False)
|
|
380
|
+
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
381
|
+
|
|
382
|
+
assert result.exit_code == 0, result.output
|
|
383
|
+
assert captured["llm_api_key"] == "sk-test-key"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/engine/reporting/__init__.py
RENAMED
|
File without changes
|
{fluxloop_cli-0.2.19 → fluxloop_cli-0.2.21}/fluxloop_cli/evaluation/engine/reporting/markdown.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|