Files
foxhunt/services/api_gateway/src/grpc/monitoring_proxy.rs
jgrusewski 392655d5fb fix: add GetEpochHistory to api_gateway proxy + clippy fixes
- Implement GetEpochHistory forwarding in MonitoringServiceProxy
- Fix clippy integer suffix style (0usize → 0_usize)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:20:11 +01:00

109 lines
3.7 KiB
Rust

//! Monitoring Service Proxy - Zero-copy gRPC forwarding for training metrics
//!
//! Forwards `GetLiveTrainingMetrics` and `StreamTrainingMetrics` from FXT clients
//! to the monitoring-service backend. Follows the same pattern as `MlTrainingProxy`.
use futures::Stream;
use std::pin::Pin;
use tonic::{Request, Response, Status};
use tracing::{error, info, instrument};
use crate::monitoring::monitoring_service_client::MonitoringServiceClient;
use crate::monitoring::monitoring_service_server::{MonitoringService, MonitoringServiceServer};
use crate::monitoring::{
GetEpochHistoryRequest, GetEpochHistoryResponse, GetLiveTrainingMetricsRequest,
GetLiveTrainingMetricsResponse, StreamTrainingMetricsRequest,
};
/// Monitoring Service Proxy
///
/// Zero-copy forwarding of gRPC requests to the monitoring-service backend.
#[derive(Debug, Clone)]
pub struct MonitoringServiceProxy {
client: MonitoringServiceClient<tonic::transport::Channel>,
}
impl MonitoringServiceProxy {
pub fn new(client: MonitoringServiceClient<tonic::transport::Channel>) -> Self {
Self { client }
}
pub fn into_server(self) -> MonitoringServiceServer<MonitoringServiceProxy> {
MonitoringServiceServer::new(self)
}
}
#[tonic::async_trait]
impl MonitoringService for MonitoringServiceProxy {
type StreamTrainingMetricsStream =
Pin<Box<dyn Stream<Item = Result<GetLiveTrainingMetricsResponse, Status>> + Send>>;
#[instrument(skip(self, request), fields(request_id = %uuid::Uuid::new_v4()), err)]
async fn get_live_training_metrics(
&self,
request: Request<GetLiveTrainingMetricsRequest>,
) -> Result<Response<GetLiveTrainingMetricsResponse>, Status> {
info!("Proxying GetLiveTrainingMetrics request");
let mut client = self.client.clone();
let response = client
.get_live_training_metrics(request)
.await
.map_err(|e| {
error!("Backend GetLiveTrainingMetrics failed: {}", e);
e
})?;
info!("GetLiveTrainingMetrics forwarded successfully");
Ok(response)
}
#[instrument(skip(self, request), fields(request_id = %uuid::Uuid::new_v4()), err)]
async fn stream_training_metrics(
&self,
request: Request<StreamTrainingMetricsRequest>,
) -> Result<Response<Self::StreamTrainingMetricsStream>, Status> {
info!("Proxying StreamTrainingMetrics streaming request");
let mut client = self.client.clone();
let stream_response = client
.stream_training_metrics(request)
.await
.map_err(|e| {
error!("Backend StreamTrainingMetrics failed: {}", e);
e
})?;
let stream = stream_response.into_inner();
let boxed_stream = Box::pin(stream) as Self::StreamTrainingMetricsStream;
info!("StreamTrainingMetrics forwarded successfully");
Ok(Response::new(boxed_stream))
}
#[instrument(skip(self, request), fields(request_id = %uuid::Uuid::new_v4()), err)]
async fn get_epoch_history(
&self,
request: Request<GetEpochHistoryRequest>,
) -> Result<Response<GetEpochHistoryResponse>, Status> {
info!("Proxying GetEpochHistory request");
let mut client = self.client.clone();
let response = client.get_epoch_history(request).await.map_err(|e| {
error!("Backend GetEpochHistory failed: {}", e);
e
})?;
info!("GetEpochHistory forwarded successfully");
Ok(response)
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_proxy_creation() {
// Full integration tests require running backend service
}
}