fluxloop-cli 0.2.12__tar.gz → 0.2.13__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.12 → fluxloop_cli-0.2.13}/PKG-INFO +1 -1
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/__init__.py +1 -1
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/runner.py +88 -14
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli.egg-info/PKG-INFO +1 -1
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/pyproject.toml +1 -1
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/README.md +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/arg_binder.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/commands/__init__.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/commands/config.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/commands/generate.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/commands/init.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/commands/parse.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/commands/record.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/commands/run.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/commands/status.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/config_loader.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/config_schema.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/constants.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/input_generator.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/llm_generator.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/main.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/project_paths.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/target_loader.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/templates.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli/validators.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli.egg-info/SOURCES.txt +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli.egg-info/dependency_links.txt +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli.egg-info/entry_points.txt +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli.egg-info/requires.txt +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/fluxloop_cli.egg-info/top_level.txt +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/setup.cfg +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/tests/test_arg_binder.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/tests/test_config_command.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/tests/test_input_generator.py +0 -0
- {fluxloop_cli-0.2.12 → fluxloop_cli-0.2.13}/tests/test_target_loader.py +0 -0
|
@@ -57,8 +57,17 @@ class ExperimentRunner:
|
|
|
57
57
|
|
|
58
58
|
offline_dir = output_base / "artifacts"
|
|
59
59
|
offline_dir.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
# Ensure downstream load_env() calls don't re-enable collector unintentionally
|
|
61
|
+
should_use_collector = (not no_collector) and bool(config.collector_url)
|
|
62
|
+
if not should_use_collector:
|
|
63
|
+
os.environ["FLUXLOOP_USE_COLLECTOR"] = "false"
|
|
64
|
+
else:
|
|
65
|
+
os.environ["FLUXLOOP_USE_COLLECTOR"] = "true"
|
|
66
|
+
# Pin offline store dir in env so refresh_config respects our path
|
|
67
|
+
os.environ.setdefault("FLUXLOOP_OFFLINE_DIR", str(offline_dir))
|
|
68
|
+
os.environ.setdefault("FLUXLOOP_OFFLINE_ENABLED", "true")
|
|
60
69
|
fluxloop.configure(
|
|
61
|
-
use_collector=
|
|
70
|
+
use_collector=should_use_collector,
|
|
62
71
|
collector_url=config.collector_url or None,
|
|
63
72
|
api_key=config.collector_api_key,
|
|
64
73
|
offline_store_enabled=True,
|
|
@@ -347,6 +356,11 @@ class ExperimentRunner:
|
|
|
347
356
|
final_output = self._extract_final_output(callback_messages, observations)
|
|
348
357
|
if final_output is not None:
|
|
349
358
|
result = final_output
|
|
359
|
+
else:
|
|
360
|
+
# As a fallback, coerce async streams or objects into text
|
|
361
|
+
coerced = await self._coerce_result_to_text(result)
|
|
362
|
+
if coerced is not None:
|
|
363
|
+
result = coerced
|
|
350
364
|
|
|
351
365
|
# Mark successful
|
|
352
366
|
self.results["successful"] += 1
|
|
@@ -610,6 +624,33 @@ class ExperimentRunner:
|
|
|
610
624
|
"args": list(args),
|
|
611
625
|
"kwargs": kwargs,
|
|
612
626
|
}
|
|
627
|
+
|
|
628
|
+
async def _coerce_result_to_text(self, value: Any) -> Optional[str]:
|
|
629
|
+
"""Best-effort conversion of agent result into text for summaries.
|
|
630
|
+
|
|
631
|
+
Handles async generators/iterables, known streaming item shapes, and strings.
|
|
632
|
+
"""
|
|
633
|
+
import inspect as _inspect
|
|
634
|
+
|
|
635
|
+
if value is None:
|
|
636
|
+
return None
|
|
637
|
+
|
|
638
|
+
if isinstance(value, str):
|
|
639
|
+
return value
|
|
640
|
+
|
|
641
|
+
# Async iterable/generator → consume to text
|
|
642
|
+
if _inspect.isasyncgen(value) or hasattr(value, "__aiter__"):
|
|
643
|
+
try:
|
|
644
|
+
return await self._consume_async_iterable(value)
|
|
645
|
+
except Exception:
|
|
646
|
+
return None
|
|
647
|
+
|
|
648
|
+
# Try extracting text from known event-like objects
|
|
649
|
+
fallback = self._extract_stream_text(value)
|
|
650
|
+
if fallback:
|
|
651
|
+
return fallback
|
|
652
|
+
|
|
653
|
+
return None
|
|
613
654
|
|
|
614
655
|
def _save_results(self) -> None:
|
|
615
656
|
"""Save results to output directory."""
|
|
@@ -676,26 +717,59 @@ class ExperimentRunner:
|
|
|
676
717
|
if not trace_ids:
|
|
677
718
|
return
|
|
678
719
|
|
|
679
|
-
|
|
680
|
-
|
|
720
|
+
# Gather candidate offline stores
|
|
721
|
+
candidates = []
|
|
722
|
+
try:
|
|
723
|
+
from fluxloop import get_config as _get_sdk_config # type: ignore
|
|
724
|
+
sdk_dir = Path(_get_sdk_config().offline_store_dir)
|
|
725
|
+
candidates.append(sdk_dir / "observations.jsonl")
|
|
726
|
+
except Exception:
|
|
727
|
+
pass
|
|
728
|
+
|
|
729
|
+
candidates.append(self.offline_dir / "observations.jsonl")
|
|
730
|
+
# Legacy fallback removed now that SDK defaults to experiments/artifacts
|
|
731
|
+
|
|
732
|
+
# Unique existing paths in priority order
|
|
733
|
+
seen = set()
|
|
734
|
+
existing: list[Path] = []
|
|
735
|
+
for p in candidates:
|
|
736
|
+
try:
|
|
737
|
+
rp = p.resolve()
|
|
738
|
+
except Exception:
|
|
739
|
+
continue
|
|
740
|
+
if rp in seen:
|
|
741
|
+
continue
|
|
742
|
+
seen.add(rp)
|
|
743
|
+
if rp.exists():
|
|
744
|
+
existing.append(rp)
|
|
745
|
+
|
|
746
|
+
if not existing:
|
|
681
747
|
return
|
|
682
748
|
|
|
683
749
|
destination = self.output_dir / "observations.jsonl"
|
|
684
750
|
copied = 0
|
|
751
|
+
seen_lines = set()
|
|
685
752
|
|
|
686
|
-
with
|
|
687
|
-
|
|
688
|
-
) as dst:
|
|
689
|
-
for line in src:
|
|
690
|
-
if not line.strip():
|
|
691
|
-
continue
|
|
753
|
+
with destination.open("w", encoding="utf-8") as dst:
|
|
754
|
+
for source_path in existing:
|
|
692
755
|
try:
|
|
693
|
-
|
|
694
|
-
|
|
756
|
+
with source_path.open("r", encoding="utf-8") as src:
|
|
757
|
+
for line in src:
|
|
758
|
+
if not line.strip():
|
|
759
|
+
continue
|
|
760
|
+
try:
|
|
761
|
+
record = json.loads(line)
|
|
762
|
+
except json.JSONDecodeError:
|
|
763
|
+
continue
|
|
764
|
+
if record.get("trace_id") in trace_ids:
|
|
765
|
+
key = (record.get("id"), record.get("start_time"))
|
|
766
|
+
if key in seen_lines:
|
|
767
|
+
continue
|
|
768
|
+
dst.write(json.dumps(record) + "\n")
|
|
769
|
+
seen_lines.add(key)
|
|
770
|
+
copied += 1
|
|
771
|
+
except OSError:
|
|
695
772
|
continue
|
|
696
|
-
if record.get("trace_id") in trace_ids:
|
|
697
|
-
dst.write(json.dumps(record) + "\n")
|
|
698
|
-
copied += 1
|
|
699
773
|
|
|
700
774
|
console.print(
|
|
701
775
|
f"[green]✅ Saved {copied} observations to {destination.name}[/green]"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|