Commit Graph

1599 Commits

Author SHA1 Message Date
jgrusewski
ddacfbbff1 feat(supervised): record learning rate and epoch duration metrics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:10:00 +01:00
jgrusewski
afa506495f feat(ppo): record entropy, KL, advantage, explained variance, epoch duration metrics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:09:59 +01:00
jgrusewski
47f8c6109d feat(dqn): record Q-value, gradient norm, epoch duration, buffer size metrics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:09:58 +01:00
jgrusewski
6c7a7275de feat(fxt): add grad norm, RL diagnostics, and hyperopt trial detail to monitor
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:09:56 +01:00
jgrusewski
ab218121b6 feat(monitoring): wire 12 new metric→proto field mappings in group_into_sessions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:09:54 +01:00
jgrusewski
7983302a52 proto(monitoring): add 12 new TrainingSession fields for diagnostics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:00:36 +01:00
jgrusewski
b08b67a5cd feat(metrics): register 17 new Prometheus metrics with verbose gating
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:56:51 +01:00
jgrusewski
2e3ac28e1b docs: implementation plan for 17 additional training metrics
9 tasks covering: training_metrics.rs (definitions + verbose gate),
monitoring.proto (12 new fields), service.rs (match arms + tests),
fxt monitor CLI (grad norm, RL diagnostics, hyperopt trial detail),
DQN/PPO trainers (per-epoch recording), supervised binary (LR + duration),
hyperopt binaries (elapsed_seconds), and full workspace verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:48:18 +01:00
jgrusewski
477b9cf1cf docs: GPU PER sum-tree implementation plan (13 tasks)
TDD plan covering GpuReplayBuffer, proportional + rank-based GPU
sampling, priority scatter updates, async loss readback, trainer
integration, OOM fallback, and distribution correctness tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:45:27 +01:00
jgrusewski
9105921f8d docs: additional training metrics design — 17 new Prometheus metrics
Two-tier system: 12 always-on gauges (RL diagnostics, gradient health,
hyperopt intra-trial) + 5 opt-in verbose metrics gated behind
FOXHUNT_VERBOSE_METRICS=1. Extends proto, monitoring service, and CLI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:44:45 +01:00
jgrusewski
995c473ad9 docs: GPU-resident PER sum-tree design
Approved design for moving Prioritized Experience Replay entirely to
GPU — flat priority array with parallel prefix-sum sampling, GPU-resident
ring buffer for experiences, async loss readback. Eliminates the last
major CPU bottleneck in the DQN training loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:40:57 +01:00
jgrusewski
2f97402d5d fix(monitoring): use active_workers metric instead of kube_job query
CI training jobs run as GitLab runner pods, not K8s Jobs, so the
kube_job_status_active query always returned 0. Switch to
foxhunt_training_active_workers which is emitted by all training
binaries regardless of deployment method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:21:15 +01:00
jgrusewski
85725abd8f feat(monitoring): per-epoch Prometheus metrics + CI scrape plumbing
- DQN/PPO trainers now record epoch, loss, val_loss, batch/s every epoch
- CI training jobs get Prometheus scrape annotations via runner overrides
- Allow Prometheus (foxhunt namespace) to scrape foxhunt-ci pods
- Skip no-model metrics in monitoring service session grouping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:10:52 +01:00
jgrusewski
6b67f6193a infra: expose monitoring service via monitor.fxhnt.ai Tailscale proxy
Add DNS record, nginx gRPC proxy block, network policy for Tailscale
ingress, and auto-detect fxhnt.ai in fxt monitor URL derivation.
Show GPU telemetry even when no training sessions are active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:56:40 +01:00
jgrusewski
d9b266e1d3 infra: add monitoring-service network policy, allow web-gateway egress
monitoring-service needs egress to Prometheus (port 80/9090) and ingress
from web-gateway (port 50057). web-gateway needs egress to monitoring-service.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:44:27 +01:00
jgrusewski
6d93ee4db0 ci: add monitoring_service to deploy binary copy and restart loops
The compile-services job already builds monitoring_service but the deploy
job was missing it from the PVC copy loop and rollout restart/wait loops.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:34:50 +01:00
jgrusewski
2f193dca6d fix(ml): GPU experience collector gate — support hybrid distributional+dueling
The GPU experience collector gate checked only `dqn.dueling_q_network`
(plain dueling), but with both `use_dueling: true` AND `use_distributional: true`
(the defaults), DQN creates hybrid `dist_dueling_q_network` instead, leaving
the plain dueling fields as None. This meant the GPU collector never initialized
despite curiosity being enabled.

Fix: Add else-if fallback to check `dist_dueling_q_network`/`dist_dueling_target_network`
when plain dueling fields are None. Same fix applied to the weight sync site.

Also fix test_train_with_empty_data_completes_gracefully: reduce to 5 epochs with
early stopping disabled. The debug-mode async state machine is large enough that
empty-data epochs run ~180ms each (vs ~3ms in release), triggering both plateau
and patience-based early stopping. The test purpose is crash-freedom, not timing.

2497 tests pass, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:32:41 +01:00
jgrusewski
bb133d6619 feat(monitoring): add monitoring service for live training metrics over gRPC
New monitoring_service bridges Prometheus training/GPU metrics to gRPC,
enabling `fxt train monitor` TUI and web-dashboard live updates without
direct Prometheus dependency on clients.

Components:
- 6 new hyperopt Prometheus metrics (common::metrics::training_metrics)
- monitoring_service: Prometheus-to-gRPC bridge (port 50057)
- fxt train monitor: live TUI + --once snapshot mode
- web-gateway: REST endpoint + WS training_progress poller
- K8s deployment manifest + CI integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:07:42 +01:00
jgrusewski
1a62060dab fix(clippy): resolve all workspace clippy warnings
Fix pre-existing and new clippy issues across 5 files:
- service_auth.rs: .to_string() → .to_owned() on &str, backtick doc items
- streams.rs: backtick doc items, allow cognitive_complexity on reconnect_loop
- main.rs: .to_owned(), allow infinite_loop, drop must_use, rename shadow
- start.rs: eliminate shadow_reuse on endpoint binding
- enhanced_ml.rs: remove unnecessary f64 cast

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:06:25 +01:00
jgrusewski
928d228db8 ci: add monitoring_service to compile-services job
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:52:28 +01:00
jgrusewski
3491749927 feat(web-gateway): integrate monitoring service for live training metrics
Wire monitoring_service gRPC into web-gateway: proto compilation,
config/state/client plumbing, REST endpoint at /api/monitoring/live-metrics,
and a 3s polling bridge that broadcasts training_progress over WebSocket.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:50:19 +01:00
jgrusewski
06cf6bf39e feat(fxt): add 'fxt train monitor' subcommand with live TUI
Adds a new CLI subcommand that connects to the monitoring service via
gRPC to display live training metrics. Supports single-snapshot mode
(--once), model filtering (--model), and configurable streaming
interval. Renders per-model epoch/loss table, GPU telemetry, hyperopt
progress, and health counters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:41:54 +01:00
jgrusewski
dd14d58fc3 infra: add K8s deployment manifest for monitoring-service
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:38:08 +01:00
jgrusewski
9e20337ee4 feat(monitoring): add monitoring_service crate with Prometheus-to-gRPC bridge
Prometheus client queries training/hyperopt/GPU/K8s metrics, gRPC service
exposes unary + server-streaming RPCs, 4 unit tests, zero clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:35:38 +01:00
jgrusewski
c4693403d5 feat(ml): wire hyperopt Prometheus metrics into training binaries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:25:56 +01:00
jgrusewski
33d3cf2dd2 feat(monitoring): add monitoring.proto with gRPC service definition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:21:29 +01:00
jgrusewski
354cd5c116 feat(metrics): add 6 hyperopt Prometheus metrics to training_metrics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:18:50 +01:00
jgrusewski
148d4cbb43 feat(ml): ungate GPU experience collector + async PER pre-sampling
Enable curiosity module by default (weight 0.0→0.1) to satisfy the
three-way gate (dueling + target + curiosity) that was blocking the
GPU experience collector CUDA kernel. This eliminates the 30-40% CPU
experience collection phase that was the main GPU idle bottleneck.

Additional changes:
- BatchSample API: train_step/compute_gradients now accept
  Option<BatchSample> instead of Option<Vec<Experience>>, preserving
  PER importance-sampling weights and indices through pre-sampling.
- Async PER pre-sampling: train_step_single_batch and
  train_step_with_accumulation now pre-sample from the replay buffer
  using a READ lock before acquiring the WRITE lock for GPU training.
  This separates CPU sampling (~250μs) from GPU forward/backward (~3ms).
- Delete dead EpochPrefetcher: the binary (train_baseline_rl.rs) already
  implements fold prefetching with background thread + mpsc channel +
  GPU double-buffering, making the trainer's EpochPrefetcher redundant.
- Hyperopt DQN bounds: curiosity_weight min 0.0→0.01 so PSO can never
  fully disable curiosity (which would re-gate the GPU collector).

10 files changed, -195 net lines. 2497 tests pass, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:14:14 +01:00
jgrusewski
0c227bb7d0 docs: add monitoring service design — live training metrics over gRPC
New standalone monitoring_service that bridges Prometheus → gRPC,
enabling `fxt train monitor` and web-gateway WS streaming for
all training jobs (CI and service-managed).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:05:48 +01:00
jgrusewski
4b2f9e8133 fix(ml): GPU-aware PSO thread cap — 3→7 parallel trials on L40S
The auto-detect heuristic used cpus/2 ("smart cap"), designed for
CPU-bound workloads.  DQN/PPO trials are GPU-bound — each rayon
thread submits CUDA kernels and waits on cudaDeviceSynchronize(),
using minimal CPU.  cpus-1 is the correct cap.

On L40S-1-48G (8 vCPU): 3 threads → 7 threads (2.3× more trials).
Also bumps CI CPU limit 7500m→8000m to expose all 8 cores.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:55:54 +01:00
jgrusewski
64ca8f97ce fix(observability): dedicated OTLP runtime for sync training binaries
The batch span processor needs a tokio runtime for gRPC transport and
periodic flush. Async services already have one via #[tokio::main], but
sync training binaries (hyperopt, train, evaluate) don't.

Previous approach (making binaries async with #[tokio::main]) caused
"Cannot start a runtime from within a runtime" panics because the ML
crate's internal code creates its own tokio runtimes for block_on().

New approach: build_otel_tracer() detects runtime context via
Handle::try_current(). If absent, it creates a dedicated 1-worker
multi-thread runtime stored in a process-lifetime OnceLock. The worker
thread actively polls the OTLP batch export task.

Reverts training binaries to sync fn main() so internal runtime creation
(hyperopt adapters, DQN/PPO trainers) continues working as before.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:54:10 +01:00
jgrusewski
77f8ce0b3a feat(ml): GPU Phase 3 — production hotpath elimination
14 commits eliminating GPU→CPU synchronization violations across
all 10 ML model trainers and ensemble inference pipeline:

P0 Training: GPU-accumulated loss in Liquid/TLOB/TFT/Diffusion,
batched gradient norms in TFT/TLOB/Diffusion (N syncs → 1)

P1 Training+Inference: KAN spline GPU-native forward, PPO metrics
batching (4→1), ensemble predict_raw() trait + GPU aggregation,
CudaStreamPool + StreamAwareEnsemble for CUDA multi-stream

P2 Minor: Mamba2 hyperopt GPU tensor creation, DQN GPU argmax

29 files changed, +1895/-425 lines. 2502 tests pass, 0 clippy warnings.
2026-03-02 18:48:04 +01:00
jgrusewski
fcf87a5f72 fix(ml): add tokio runtime to training binaries for OTLP export
All 6 training binaries (hyperopt_baseline_rl, hyperopt_baseline_supervised,
train_baseline_rl, train_baseline_supervised, evaluate_baseline,
evaluate_supervised) used sync fn main() but the OTLP batch exporter
requires a tokio runtime (tonic/hyper-util gRPC transport). This caused
an immediate panic on CI when OTEL_EXPORTER_OTLP_ENDPOINT was set.

Fix: #[tokio::main(flavor = "current_thread")] on all 6 binaries.
Also fix pre-existing clippy warnings (shadow, let_underscore_must_use,
doc_markdown, cognitive_complexity, integer_division, unsafe_code).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:16:37 +01:00
jgrusewski
4c57f3d704 fix(ml): DQN single-step action selection uses GPU argmax
Replace CPU-side argmax (to_vec1 + iter enumerate max_by) with
GPU-native argmax(D). Single u32 scalar extraction instead of
full Q-value vector transfer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:03:20 +01:00
jgrusewski
129c2f19b8 fix(ml): mamba2 hyperopt tensors created on GPU instead of CPU
Replace &Device::Cpu with &self.device for input and target tensor
creation in Mamba2 hyperopt adapter. Avoids unnecessary CPU→GPU
transfer during hyperparameter optimization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:03:10 +01:00
jgrusewski
85138e2fbd feat(ml): add StreamAwareEnsemble with per-model CUDA streams
Stream-aware ensemble that runs models on separate CUDA streams
for true GPU-level parallelism. Falls back to rayon on CPU.
Uses CudaStreamPool for synchronization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:59:12 +01:00
jgrusewski
550944ebf2 feat(ml): add CudaStreamPool for multi-stream ensemble inference
CUDA stream pool with CPU no-op fallback. Foundation for
StreamAwareEnsemble that runs models on separate CUDA streams.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:52:45 +01:00
jgrusewski
58dc95cf54 feat(ml): InferenceEnsemble GPU-aggregated prediction — N syncs → 1
Use predict_raw() to collect raw GPU tensors from adapters. Stack,
sigmoid, weighted-sum on GPU before single extraction. Falls back
to CPU path for adapters without tensor output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:46:21 +01:00
jgrusewski
78f5ead601 feat(ml): implement predict_raw() for 5 scalar-output ensemble adapters
Override predict_raw() in TGGN, TLOB, KAN, xLSTM, Diffusion adapters
to return raw GPU tensors. Enables GPU-side ensemble aggregation
instead of per-model CPU extraction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:46:09 +01:00
jgrusewski
f8cec1d6f9 feat(ml): KAN spline GPU-native forward — eliminate GPU→CPU→GPU roundtrip
Replace clamped.to_vec1() CPU loop with GPU-native floor/ceil/frac
operations. Removes 3 tensor transfers per forward pass (N floats
down + 3*N up). All index computation now stays on GPU.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:41:14 +01:00
jgrusewski
3aab43614a feat(ml): PPO metrics batch extraction — 4 GPU syncs → 1
Stack var_returns, var_residuals, mean_reward, var_reward into single
tensor before extraction. Uses broadcast_sub for GPU-native mean
centering instead of scalar round-trip.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:41:11 +01:00
jgrusewski
c91995e41d feat(ml): add RawPrediction + predict_raw() default to ModelInferenceAdapter
Backward-compatible trait extension. Default predict_raw() wraps
predict() result with tensor: None. Adapters can override to return
raw GPU tensors for GPU-side ensemble aggregation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:41:08 +01:00
jgrusewski
a57e4911ff fix(ci): fix PVC-based deploy pipeline (stale pods, ordering, cross-node RWO)
The S3→PVC migration had several issues causing deploy failures:

- Stale binary-writer pod from previous failed deploys blocked new ones
  (terminated pods can't be updated via kubectl apply)
- Services were applied BEFORE binaries written to PVC, so first deploy
  or empty PVC caused pods to crash (binary not found)
- GPU overlay files in services/ dir were auto-applied by kubectl apply,
  referencing nonexistent S3/minio secrets and PVCs
- Training job template mounted foxhunt-binaries PVC but training runs
  on ci-training node — RWO PVC is bound to foxhunt node

Fixes:
- Reorder deploy: write binaries → apply manifests → rollout restart
- Clean up stale writer pods before creating new ones
- Move GPU overlays to gpu-overlays/ (manual apply only), update to PVC
- Add training-binaries PVC for GPU node, best-effort population
- Training job template uses initContainer to copy from training PVC
- set -e for critical path, set +e for optional training binary copy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:40:28 +01:00
jgrusewski
e12cdfa005 feat(ml): diffusion trainer GPU-accumulated loss + batched grad norm
Replace per-param gradient norm extraction with batched Tensor::stack
pattern (N GPU syncs → 1). Remove per-batch loss scalar extraction
from backward(), defer to epoch-level accumulation. Apply GPU-accumulated
validation loss (stack + mean_all instead of per-batch to_scalar).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:30:19 +01:00
jgrusewski
4cf181c8fb feat(ml): TFT batched gradient norm — N GPU syncs → 1
Replace per-param gradient norm extraction loop with batched
Tensor::stack pattern. Single GPU→CPU sync instead of one per
parameter tensor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:30:10 +01:00
jgrusewski
7d2b56a3ad feat(ml): TLOB batched parameter norm — N GPU syncs → 1
Replace per-param calculate_gradient_norm() loop with batched
Tensor::stack pattern. Single GPU→CPU sync instead of one per
parameter tensor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:29:33 +01:00
jgrusewski
fd80dbc07e feat(ml): TFT trainer GPU-accumulated loss — eliminate per-batch to_vec0
Replace per-batch loss.to_vec0() in TFT training/validation loops
with GPU tensor accumulation. Single extraction per epoch + NaN guard
every 100 batches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:25:57 +01:00
jgrusewski
2c493f4305 feat(ml): liquid trainer GPU-accumulated loss — eliminate per-batch to_scalar
Replace per-batch .to_dtype(F64).to_scalar() in Liquid train/validate
with GPU tensor accumulation. Single extraction per epoch + NaN guard
every 100 batches. Removes ~100-500 GPU→CPU syncs per epoch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:25:56 +01:00
jgrusewski
851546b322 feat(ml): TLOB trainer GPU-accumulated loss — eliminate per-batch to_scalar
Replace per-batch loss.to_scalar() in TLOB train_epoch/validate_epoch
with GPU tensor accumulation. Single extraction per epoch + NaN guard
every 100 batches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:25:54 +01:00
jgrusewski
85a51991e9 fix(ci): use training-bin/ dir to avoid collision with source bin/fxt/
The before_script was copying training binaries to ${CI_PROJECT_DIR}/bin/
which collides with the git checkout bin/fxt/ directory. chmod +x on the
fxt subdirectory fails with "Operation not permitted", crashing the job.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:12:04 +01:00