3737
3838# Anomaly type mapping from analyzer names to proto enum values.
3939_ANOMALY_TYPE_MAP : dict [str , int ] = {
40- "logit_analyzer" : 1 , # ANOMALY_TYPE_LOGIT_DRIFT
41- "entropy_analyzer" : 2 , # ANOMALY_TYPE_ENTROPY_ANOMALY
42- "kl_divergence" : 3 , # ANOMALY_TYPE_KL_DIVERGENCE
43- "gradient_monitor" : 4 , # ANOMALY_TYPE_GRADIENT_NORM_SPIKE
44- "loss_monitor" : 5 , # ANOMALY_TYPE_LOSS_SPIKE
45- "ddp_divergence" : 6 , # ANOMALY_TYPE_CROSS_RANK_DIVERGENCE
40+ "logit_analyzer" : 1 , # ANOMALY_TYPE_LOGIT_DRIFT
41+ "entropy_analyzer" : 2 , # ANOMALY_TYPE_ENTROPY_ANOMALY
42+ "kl_divergence" : 3 , # ANOMALY_TYPE_KL_DIVERGENCE
43+ "gradient_monitor" : 4 , # ANOMALY_TYPE_GRADIENT_NORM_SPIKE
44+ "loss_monitor" : 5 , # ANOMALY_TYPE_LOSS_SPIKE
45+ "ddp_divergence" : 6 , # ANOMALY_TYPE_CROSS_RANK_DIVERGENCE
4646 "checkpoint_validator" : 7 , # ANOMALY_TYPE_CHECKPOINT_DIVERGENCE
47- "invariant_checker" : 8 , # ANOMALY_TYPE_INVARIANT_VIOLATION
47+ "invariant_checker" : 8 , # ANOMALY_TYPE_INVARIANT_VIOLATION
4848}
4949
5050# Severity mapping from string labels to proto enum values.
@@ -174,8 +174,11 @@ async def start(self) -> None:
174174
175175 # Try to import the generated protobuf stubs.
176176 self ._stub = self ._create_stub ()
177- logger .info ("grpc_client_started" , endpoint = self ._config .endpoint ,
178- proto_stubs = self ._proto_available )
177+ logger .info (
178+ "grpc_client_started" ,
179+ endpoint = self ._config .endpoint ,
180+ proto_stubs = self ._proto_available ,
181+ )
179182 except ImportError :
180183 logger .warning ("grpc_not_available" , msg = "Running without gRPC reporting." )
181184 self ._channel = None
@@ -187,13 +190,15 @@ def _create_stub(self) -> Any:
187190 """Create the AnomalyService stub, trying generated stubs first."""
188191 try :
189192 from sentinel .v1 import anomaly_pb2_grpc # type: ignore[import-untyped]
193+
190194 stub = anomaly_pb2_grpc .AnomalyServiceStub (self ._channel )
191195 self ._proto_available = True
192196 logger .debug ("using_generated_proto_stubs" )
193197 return stub
194198 except ImportError :
195- logger .debug ("generated_proto_stubs_not_found" ,
196- msg = "Falling back to manual serialization" )
199+ logger .debug (
200+ "generated_proto_stubs_not_found" , msg = "Falling back to manual serialization"
201+ )
197202 self ._proto_available = False
198203 return None
199204
@@ -217,8 +222,7 @@ async def stop(self) -> None:
217222 self ._stream = None
218223 if self ._channel is not None :
219224 await self ._channel .close ()
220- logger .info ("grpc_client_stopped" ,
221- pending_acks = len (self ._pending_acks ))
225+ logger .info ("grpc_client_stopped" , pending_acks = len (self ._pending_acks ))
222226
223227 # ------------------------------------------------------------------
224228 # Event submission
@@ -283,9 +287,7 @@ def _event_to_proto_dict(self, event: AnomalyEvent) -> dict[str, Any]:
283287 else :
284288 severity_val = _SEVERITY_MAP .get (str (severity_str ).lower (), 2 )
285289
286- anomaly_type_val = _ANOMALY_TYPE_MAP .get (
287- getattr (event , "analyzer" , "" ), 0
288- )
290+ anomaly_type_val = _ANOMALY_TYPE_MAP .get (getattr (event , "analyzer" , "" ), 0 )
289291
290292 # Generate a deterministic event ID from content hash.
291293 content = f"{ event .analyzer } :{ event .stat_name } :{ event .sample_count } :{ event .observed_value } "
@@ -307,9 +309,7 @@ def _event_to_proto_dict(self, event: AnomalyEvent) -> dict[str, Any]:
307309 f"{ event .analyzer } /{ event .stat_name } : observed={ event .observed_value :.6f} , "
308310 f"ewma={ event .ewma_value :.6f} , ucl={ event .ucl } , lcl={ event .lcl } "
309311 ),
310- "tensor_fingerprint" : hashlib .sha256 (
311- f"{ event .observed_value } " .encode ()
312- ).digest ()[:16 ],
312+ "tensor_fingerprint" : hashlib .sha256 (f"{ event .observed_value } " .encode ()).digest ()[:16 ],
313313 "timestamp" : time .time (),
314314 "metadata" : event .details if isinstance (event .details , dict ) else {},
315315 "step_number" : event .sample_count ,
@@ -428,9 +428,7 @@ async def _do_send_proto(self, report: AnomalyReport) -> None:
428428 if self ._stream is None :
429429 self ._stream = self ._stub .StreamAnomalyEvents ()
430430 # Start background ack reader.
431- self ._ack_task = asyncio .create_task (
432- self ._read_acks_proto ()
433- )
431+ self ._ack_task = asyncio .create_task (self ._read_acks_proto ())
434432
435433 self ._pending_acks [report .sequence_number ] = time .time ()
436434 await self ._stream .write (batch )
@@ -470,13 +468,15 @@ async def _do_send_manual(self, report: AnomalyReport) -> None:
470468 """
471469 import json
472470
473-
474- payload = json .dumps ({
475- "source_hostname" : report .source_hostname ,
476- "sequence_number" : report .sequence_number ,
477- "batch_timestamp" : {"seconds" : int (report .timestamp )},
478- "events" : report .events ,
479- }, default = str ).encode ("utf-8" )
471+ payload = json .dumps (
472+ {
473+ "source_hostname" : report .source_hostname ,
474+ "sequence_number" : report .sequence_number ,
475+ "batch_timestamp" : {"seconds" : int (report .timestamp )},
476+ "events" : report .events ,
477+ },
478+ default = str ,
479+ ).encode ("utf-8" )
480480
481481 call = self ._channel .unary_unary (
482482 "/sentinel.v1.AnomalyService/StreamAnomalyEvents" ,
0 commit comments