Files

204 lines
6.9 KiB
Python
Raw Permalink Normal View History

"""FedMart adapter for XIC telemetry ingestion and spec map registration.
Provides:
- emit_telemetry(telemetry): normalize and forward telemetry to FedMart
- register_spec_map(spec_map): push XIC specification status to FedMart
- Control hooks for guardrail actions (pause, throttle)
"""
import json
import time
from typing import Dict, Any, List, Optional
from datetime import datetime
from pathlib import Path
class FedMartAdapter:
"""Adapter for XIC → FedMart integration."""
def __init__(self, endpoint: Optional[str] = None, local_mode: bool = False):
"""Initialize FedMart adapter.
Args:
endpoint: FedMart ingestion endpoint (default: http://localhost:8080/fedmart/ingest)
local_mode: If True, store telemetry locally instead of sending to remote
"""
self.endpoint = endpoint or "http://localhost:8080/fedmart/ingest"
self.local_mode = local_mode
self.telemetry_buffer: List[Dict[str, Any]] = []
self.spec_status = {}
def emit_telemetry(self, telemetry: Dict[str, Any]) -> bool:
"""Emit telemetry event to FedMart.
Args:
telemetry: Telemetry dict with schema-defined fields
Returns:
True if successful, False otherwise
"""
# Normalize and validate
normalized = self._normalize_telemetry(telemetry)
if self.local_mode:
# Store locally
self.telemetry_buffer.append(normalized)
print(f"[FEDMART] Telemetry buffered locally (total: {len(self.telemetry_buffer)})")
return True
else:
# In production, this would POST to FedMart endpoint
return self._send_to_fedmart(normalized)
def register_spec_map(self, spec_map: Dict[str, Any]) -> bool:
"""Register XIC specification status with FedMart.
Args:
spec_map: Dict with spec entries (instruction, phase, status, coverage)
Returns:
True if successful, False otherwise
"""
self.spec_status.update(spec_map)
if self.local_mode:
print(f"[FEDMART] Spec map registered locally ({len(self.spec_status)} entries)")
return True
else:
return self._send_spec_to_fedmart(spec_map)
def pause_run(self, run_id: str) -> bool:
"""Pause a pipeline run (guardrail control action).
Args:
run_id: Unique run identifier
Returns:
True if pause command accepted
"""
print(f"[FEDMART-CONTROL] Pause requested for run {run_id}")
if self.local_mode:
return True
# In production: POST to /fedmart/control/pause with run_id
return True
def throttle_run(self, run_id: str, factor: float = 0.5) -> bool:
"""Throttle a pipeline run (reduce glyph_count or slow execution).
Args:
run_id: Unique run identifier
factor: Throttle factor (0.5 = 50% speed)
Returns:
True if throttle command accepted
"""
print(f"[FEDMART-CONTROL] Throttle {factor:.1%} requested for run {run_id}")
if self.local_mode:
return True
# In production: POST to /fedmart/control/throttle with run_id and factor
return True
def _normalize_telemetry(self, telemetry: Dict[str, Any]) -> Dict[str, Any]:
"""Normalize telemetry to schema.
Ensures timestamp is ISO 8601, adds defaults, etc.
"""
normalized = dict(telemetry)
# Ensure timestamp is ISO 8601
if "timestamp" not in normalized:
normalized["timestamp"] = datetime.utcnow().isoformat() + "Z"
elif isinstance(normalized["timestamp"], float):
normalized["timestamp"] = datetime.utcfromtimestamp(
normalized["timestamp"]
).isoformat() + "Z"
# Ensure run_id
if "run_id" not in normalized:
normalized["run_id"] = self._generate_run_id()
# Ensure defaults
normalized.setdefault("event_type", "symbolic_pipeline_run")
normalized.setdefault("glyph_ids", [])
normalized.setdefault("glyph_count", len(normalized.get("glyph_ids", [])))
normalized.setdefault("global_resonance_score", 0.0)
normalized.setdefault("steps_executed", 0)
normalized.setdefault("guardrails_triggered", [])
# Ensure resonance_map_summary
if "resonance_map_summary" not in normalized:
normalized["resonance_map_summary"] = {
"top_glyphs": [],
"average_resonance": 0.0,
}
return normalized
def _send_to_fedmart(self, telemetry: Dict[str, Any]) -> bool:
"""Send telemetry to FedMart ingestion endpoint.
In production, this would use requests or httpx.
For now, it's a stub.
"""
# TODO: implement actual HTTP POST
# response = requests.post(f"{self.endpoint}/xic", json=telemetry)
# return response.status_code == 202
print(f"[FEDMART] Would POST telemetry to {self.endpoint}/xic")
return True
def _send_spec_to_fedmart(self, spec_map: Dict[str, Any]) -> bool:
"""Send spec map to FedMart spec registration endpoint."""
# TODO: implement actual HTTP POST
print(f"[FEDMART] Would POST spec map to {self.endpoint}/spec_map")
return True
def _generate_run_id(self) -> str:
"""Generate a unique run ID."""
return f"xic_{int(time.time() * 1000)}"
def get_telemetry_buffer(self) -> List[Dict[str, Any]]:
"""Get all buffered telemetry (local mode only)."""
return self.telemetry_buffer.copy()
def clear_telemetry_buffer(self) -> None:
"""Clear telemetry buffer."""
self.telemetry_buffer.clear()
# Global singleton instance
_adapter_instance: Optional[FedMartAdapter] = None
def get_adapter(local_mode: bool = True) -> FedMartAdapter:
"""Get or create the global FedMart adapter instance.
Args:
local_mode: If True, buffer telemetry locally (default)
Returns:
FedMartAdapter singleton
"""
global _adapter_instance
if _adapter_instance is None:
_adapter_instance = FedMartAdapter(local_mode=local_mode)
return _adapter_instance
def emit_telemetry(telemetry: Dict[str, Any]) -> bool:
"""Convenience function to emit telemetry via global adapter."""
return get_adapter().emit_telemetry(telemetry)
def register_spec_map(spec_map: Dict[str, Any]) -> bool:
"""Convenience function to register spec map via global adapter."""
return get_adapter().register_spec_map(spec_map)
def pause_run(run_id: str) -> bool:
"""Convenience function to pause a run via global adapter."""
return get_adapter().pause_run(run_id)
def throttle_run(run_id: str, factor: float = 0.5) -> bool:
"""Convenience function to throttle a run via global adapter."""
return get_adapter().throttle_run(run_id, factor)