Download src/yaml_edit/ml/predictor.py from erayack/yaml-edit: direct link, hf CLI and curl.
- Browser
- Download file 3.22 kB
-
https://huggingface.co/erayack/yaml-edit/resolve/main/src/yaml_edit/ml/predictor.py
- Command line
-
hf download hf://erayack/yaml-edit/src/yaml_edit/ml/predictor.py
-
curl -L -o predictor.py https://huggingface.co/erayack/yaml-edit/resolve/main/src/yaml_edit/ml/predictor.py
3.22 kB
| """Default-off learned predictor that can only compile core-valid copied actions.""" | |
| from dataclasses import dataclass | |
| import torch | |
| from yaml_edit.engine import preview, propose | |
| from .checkpoint import ArtifactConfig | |
| from .features import MlInputError, prepare_input | |
| from .model import CandidateScorer, collate_features | |
| _DISPOSITIONS = ("supported", "needs_clarification", "unsupported") | |
| class LearnedPredictor: | |
| """Callable evaluator-compatible wrapper around a loaded candidate scorer.""" | |
| config: ArtifactConfig | |
| model: CandidateScorer | |
| device: str = "cpu" | |
| def __post_init__(self) -> None: | |
| """Require a coherent checkpoint and explicit device without fallback.""" | |
| self.config.validate() | |
| self.model.validate_architecture( | |
| self.config.feature_dimension, self.config.hidden_dimension | |
| ) | |
| if self.device not in {"cpu", "mps"}: | |
| raise ValueError("Device must be explicitly 'cpu' or 'mps'.") | |
| if self.device == "mps" and not torch.backends.mps.is_available(): | |
| raise ValueError("MPS was requested but is not available.") | |
| self.model.to(torch.device(self.device)) | |
| self.model.eval() | |
| def __call__(self, source: str, instruction: str) -> dict[str, object]: | |
| """Predict from source/instruction only, abstaining if no valid action exists.""" | |
| try: | |
| prepared = prepare_input(source, instruction) | |
| except MlInputError: | |
| return {"disposition": "unsupported", "proposal": None} | |
| batch = collate_features( | |
| [list(prepared.vector)], | |
| [[list(action.features) for action in prepared.actions]], | |
| ) | |
| device = torch.device(self.device) | |
| with torch.no_grad(): | |
| disposition_logits, action_logits = self.model( | |
| batch.vectors.to(device), | |
| batch.actions.to(device), | |
| batch.action_mask.to(device), | |
| ) | |
| disposition_probabilities = torch.softmax(disposition_logits[0], dim=0) | |
| disposition_index = int(torch.argmax(disposition_probabilities).item()) | |
| supported_confidence = float(disposition_probabilities[0].item()) | |
| if disposition_index == 0 and supported_confidence < self.config.disposition_threshold: | |
| disposition_index = int(torch.argmax(disposition_probabilities[1:]).item()) + 1 | |
| if disposition_index != 0: | |
| return {"disposition": _DISPOSITIONS[disposition_index], "proposal": None} | |
| if not prepared.actions: | |
| return {"disposition": "unsupported", "proposal": None} | |
| with torch.no_grad(): | |
| action_probabilities = torch.softmax(action_logits[0, : len(prepared.actions)], dim=0) | |
| action_index = int(torch.argmax(action_probabilities).item()) | |
| if float(action_probabilities[action_index].item()) < self.config.action_threshold: | |
| return {"disposition": "unsupported", "proposal": None} | |
| action = prepared.actions[action_index] | |
| proposal = propose(source, action.candidate.id, action.value) | |
| preview(source, proposal) | |
| return {"disposition": "supported", "proposal": proposal.to_dict()} | |