routing-agent / policy.py
Ugas's picture
Upload routing agent adapters and source code
975dbdd verified
Raw History Blame Contribute Delete
3.04 kB
import os
import torch
import logging
logger = logging.getLogger(__name__)
from model import RouterNetwork
class RouterPolicy:
"""Thin wrapper around RouterNetwork for loading, inference and checkpointing."""
def __init__(self, model: RouterNetwork, device: str = "cpu"):
self.model = model.to(device)
self.device = torch.device(device)
self.model.eval()
@classmethod
def load(cls, checkpoint_path: str, embedding_dim: int | None = None, device: str = "cpu"):
"""Load RouterNetwork from checkpoint. If checkpoint contains container dict with
'model_state_dict' will use that, otherwise will try to load state_dict directly.
embedding_dim may be required if checkpoint does not include meta info.
"""
checkpoint_path = os.path.abspath(checkpoint_path)
if not os.path.exists(checkpoint_path):
raise FileNotFoundError(f"Router checkpoint not found: {checkpoint_path}")
sd = torch.load(checkpoint_path, map_location="cpu")
# Unpack common formats
if isinstance(sd, dict) and "model_state_dict" in sd:
model_state = sd["model_state_dict"]
meta = sd.get("meta", {})
else:
model_state = sd
meta = {}
# Infer embedding dim from meta if available
emb_dim = embedding_dim or meta.get("embedding_dim") or meta.get("embed_dim") or 384
model = RouterNetwork(embedding_dim=emb_dim)
missing, unexpected = model.load_state_dict(model_state, strict=False)
if missing or unexpected:
logger.info(f"RouterPolicy.load: missing keys={list(missing)} unexpected={list(unexpected)}")
return cls(model, device=device)
def predict_sync(self, emb_tensor: torch.Tensor):
"""Synchronous prediction: accepts a 1-D or 2-D embedding tensor and returns
(action_idx:int, prob:float, value:float).
"""
was_1d = False
if emb_tensor.dim() == 1:
emb_tensor = emb_tensor.unsqueeze(0)
was_1d = True
emb = emb_tensor.to(self.device)
with torch.no_grad():
logits, value = self.model(emb)
probs = torch.softmax(logits, dim=-1)
top_idx = int(probs.argmax(dim=-1)[0].item())
top_prob = float(probs[0, top_idx].item())
val = float(value.squeeze(1)[0].item()) if value is not None else 0.0
return top_idx, top_prob, val
def save_checkpoint(self, path: str, optimizer: torch.optim.Optimizer | None = None, meta: dict | None = None):
path = os.path.abspath(path)
payload = {
"model_state_dict": self.model.state_dict(),
"meta": meta or {}
}
if optimizer is not None:
try:
payload["optimizer_state_dict"] = optimizer.state_dict()
except Exception:
logger.warning("Could not include optimizer state in checkpoint")
torch.save(payload, path)
return path