"""TensorBoard scalar ingestion with optional dependency isolation.""" from __future__ import annotations from pathlib import Path from .storage import TuningStorage class TensorboardUnavailable(RuntimeError): pass def ingest_scalars(storage: TuningStorage, trial_id: str, log_dir: Path) -> int: """Reload all scalar events and idempotently upsert them into SQLite.""" try: from tensorboard.backend.event_processing.event_accumulator import EventAccumulator except ImportError as error: raise TensorboardUnavailable( "缺少 tensorboard,请安装 training_server/requirements.txt" ) from error if not log_dir.is_dir(): return 0 accumulator = EventAccumulator(str(log_dir), size_guidance={"scalars": 0}) try: accumulator.Reload() except (OSError, ValueError): return 0 points: list[tuple[str, int, float, float]] = [] for tag in accumulator.Tags().get("scalars", []): for event in accumulator.Scalars(tag): points.append((tag, int(event.step), float(event.wall_time), float(event.value))) if points: storage.insert_metrics(trial_id, points) return len(points)