code-oracle 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. code_oracle/__init__.py +30 -0
  2. code_oracle/cli.py +795 -0
  3. code_oracle/config.py +145 -0
  4. code_oracle/dataset.py +5325 -0
  5. code_oracle/dead_code/__init__.py +32 -0
  6. code_oracle/dead_code/detector.py +379 -0
  7. code_oracle/dead_code/entrypoints.py +333 -0
  8. code_oracle/dead_code/models.py +255 -0
  9. code_oracle/dead_code/semantics.py +416 -0
  10. code_oracle/decision.py +906 -0
  11. code_oracle/engine.py +430 -0
  12. code_oracle/export_onnx.py +436 -0
  13. code_oracle/hook.py +531 -0
  14. code_oracle/indexer.py +894 -0
  15. code_oracle/languages/__init__.py +114 -0
  16. code_oracle/languages/common.py +127 -0
  17. code_oracle/languages/go.py +395 -0
  18. code_oracle/languages/python.py +336 -0
  19. code_oracle/languages/rust.py +474 -0
  20. code_oracle/languages/typescript.py +775 -0
  21. code_oracle/linearizer.py +166 -0
  22. code_oracle/locator.py +301 -0
  23. code_oracle/models.py +237 -0
  24. code_oracle/perf_lint/__init__.py +38 -0
  25. code_oracle/perf_lint/engine.py +234 -0
  26. code_oracle/perf_lint/models.py +229 -0
  27. code_oracle/perf_lint/rules/__init__.py +31 -0
  28. code_oracle/perf_lint/rules/async_blocking.py +143 -0
  29. code_oracle/perf_lint/rules/n_plus_one.py +232 -0
  30. code_oracle/perf_lint/rules/nested_loops.py +137 -0
  31. code_oracle/perf_lint/rules/unclosed_res.py +494 -0
  32. code_oracle/perf_lint/visitor.py +299 -0
  33. code_oracle/server.py +184 -0
  34. code_oracle/slicer.py +225 -0
  35. code_oracle/symbolic.py +459 -0
  36. code_oracle-0.1.0.dist-info/METADATA +225 -0
  37. code_oracle-0.1.0.dist-info/RECORD +40 -0
  38. code_oracle-0.1.0.dist-info/WHEEL +4 -0
  39. code_oracle-0.1.0.dist-info/entry_points.txt +2 -0
  40. code_oracle-0.1.0.dist-info/licenses/LICENSE +190 -0
@@ -0,0 +1,906 @@
1
+ """
2
+ Laya ModernBERT Decision Head and Risk Calibration for Code Oracle.
3
+
4
+ Evaluates linearized Micro-DSL subgraphs using fine-tuned Laya weights via ONNX Runtime
5
+ (or PyTorch / legacy fallback) or falls back deterministically to symbolic gate verdicts.
6
+ Supports pure ONNX inference with zero hard dependency on PyTorch at runtime.
7
+ """
8
+
9
+ import contextlib
10
+ import io
11
+ import json
12
+ import logging
13
+ import math
14
+ import os
15
+ import warnings
16
+ from dataclasses import dataclass
17
+ from pathlib import Path
18
+ from typing import Any, Dict, List, Optional, Tuple, Union
19
+
20
+ try:
21
+ import numpy as np
22
+ HAS_NUMPY = True
23
+ except ImportError:
24
+ np = None
25
+ HAS_NUMPY = False
26
+
27
+ try:
28
+ import torch
29
+ import torch.nn as nn
30
+ import torch.nn.functional as F
31
+ HAS_TORCH = True
32
+ except ImportError:
33
+ torch = None
34
+ nn = None
35
+ F = None
36
+ HAS_TORCH = False
37
+
38
+ try:
39
+ import onnxruntime as ort
40
+ HAS_ORT = True
41
+ except ImportError:
42
+ ort = None
43
+ HAS_ORT = False
44
+
45
+ from code_oracle.models import RiskTaxonomyScores
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+ # Verification Questions Definition for Laya Typed-Decisions Head
50
+ VERIFICATION_QUESTIONS = {
51
+ "status": {
52
+ "type": "choice",
53
+ "instructions": "Determine if this code patch proposal should be APPROVED or REJECTED based on AST topology, cycles, and contract invariants:",
54
+ "criteria": {
55
+ "APPROVED": "Clean invariant, acyclic call/import topology, parameter contracts valid",
56
+ "REJECTED": "Contains circular dependencies, arity mismatches, unexpected keywords, or deleted symbol references",
57
+ },
58
+ },
59
+ "risk": {
60
+ "type": "score",
61
+ "instructions": "Calibrate the semantic risk of this patch proposal from 0 (completely safe) to 4 (critical breaking change):",
62
+ "criteria": [
63
+ "Level 0: Safe / Invariants Preserved",
64
+ "Level 1: Low Risk / Harmless Additions",
65
+ "Level 2: Moderate Risk / Signature Drift",
66
+ "Level 3: High Risk / Broken Callers",
67
+ "Level 4: Critical / Topological Cycle",
68
+ ],
69
+ },
70
+ }
71
+
72
+ _ModuleBase = nn.Module if (HAS_TORCH and nn is not None) else object
73
+
74
+
75
+ @dataclass
76
+ class EnhancedDecisionResult:
77
+ """
78
+ Rich outcome of Laya multi-task neural decision head.
79
+ Supports unpacking as (status, confidence, risk_score) for backward compatibility.
80
+ """
81
+ status: str
82
+ confidence: float
83
+ risk_score: float
84
+ epistemic_uncertainty: float
85
+ risk_taxonomy: RiskTaxonomyScores
86
+ active_risk_categories: List[str]
87
+ is_neural_calibrated: bool = False
88
+ engine_mode: str = "heuristic"
89
+
90
+ def __iter__(self):
91
+ """Enable tuple unpacking: status, conf, risk = result"""
92
+ return iter((self.status, self.confidence, self.risk_score))
93
+
94
+
95
+ class ModernBERTMultiTaskModel(_ModuleBase):
96
+ """
97
+ Hard Parameter Sharing Multi-Task Network over ModernBERT representations.
98
+ Branches from pooled token representation h_pool in R^hidden_size (default 768)
99
+ or optionally couples end-to-end with a ModernBERT encoder:
100
+ - Head 1: Continuous Risk Regression (Huber Loss / Sigmoid)
101
+ - Head 2: Multi-Label Risk Taxonomy (5-Class BCEWithLogits / Sigmoid)
102
+ - Head 3: Epistemic Uncertainty Estimation (Heteroscedastic log-variance with clamp [-6.0, 6.0])
103
+ """
104
+
105
+ TAXONOMY_CLASSES = [
106
+ "BreakingPublicAPI",
107
+ "SecuritySurface",
108
+ "ConcurrencyHazard",
109
+ "PerformanceRegression",
110
+ "SilentLogicDrift",
111
+ ]
112
+
113
+ def __init__(
114
+ self,
115
+ hidden_size_or_encoder: Any = 768,
116
+ num_taxonomy_classes: int = 5,
117
+ **kwargs,
118
+ ):
119
+ if not HAS_TORCH:
120
+ raise RuntimeError("PyTorch is required to instantiate ModernBERTMultiTaskModel")
121
+ super().__init__()
122
+
123
+ encoder_name = kwargs.get("encoder_name")
124
+ if isinstance(hidden_size_or_encoder, str):
125
+ encoder_name = hidden_size_or_encoder
126
+ hidden_size = kwargs.get("hidden_size", 768)
127
+ else:
128
+ hidden_size = int(hidden_size_or_encoder)
129
+
130
+ self.encoder_name = encoder_name
131
+ self.encoder = None
132
+ if encoder_name:
133
+ from transformers import AutoModel
134
+ self.encoder = AutoModel.from_pretrained(encoder_name)
135
+ hidden_size = self.encoder.config.hidden_size
136
+
137
+ self.hidden_size = hidden_size
138
+ self.num_taxonomy_classes = num_taxonomy_classes
139
+
140
+ # Head 1: Continuous Risk Regression MLP
141
+ # Dense(hidden_size -> 256) -> GELU -> LayerNorm -> Dense(256 -> 1)
142
+ self.risk_head = nn.Sequential(
143
+ nn.Linear(hidden_size, 256),
144
+ nn.GELU(),
145
+ nn.LayerNorm(256),
146
+ nn.Linear(256, 1),
147
+ )
148
+
149
+ # Head 2: Multi-Label Taxonomy MLP
150
+ # Dense(hidden_size -> 256) -> GELU -> LayerNorm -> Dense(256 -> num_taxonomy_classes)
151
+ self.taxonomy_head = nn.Sequential(
152
+ nn.Linear(hidden_size, 256),
153
+ nn.GELU(),
154
+ nn.LayerNorm(256),
155
+ nn.Linear(256, num_taxonomy_classes),
156
+ )
157
+
158
+ # Head 3: Epistemic Uncertainty MLP
159
+ # Dense(hidden_size -> 128) -> GELU -> Dense(128 -> 1) [log sigma^2]
160
+ self.uncertainty_head = nn.Sequential(
161
+ nn.Linear(hidden_size, 128),
162
+ nn.GELU(),
163
+ nn.Linear(128, 1),
164
+ )
165
+
166
+ def forward(self, *args, **kwargs) -> Dict[str, Any]:
167
+ """
168
+ Forward pass. Accepts either:
169
+ - `h_pool: torch.Tensor` (B, hidden_size) directly.
170
+ - `(input_ids, attention_mask)` tensors when initialized with an encoder.
171
+ """
172
+ if not HAS_TORCH:
173
+ raise RuntimeError("PyTorch is required for model forward pass")
174
+
175
+ if "input_ids" in kwargs:
176
+ input_ids = kwargs["input_ids"]
177
+ attention_mask = kwargs.get("attention_mask")
178
+ if attention_mask is None:
179
+ attention_mask = torch.ones_like(input_ids)
180
+ elif len(args) >= 2 and hasattr(args[0], "dtype") and args[0].dtype in (torch.int32, torch.int64):
181
+ input_ids = args[0]
182
+ attention_mask = args[1]
183
+ elif len(args) == 1 and hasattr(args[0], "dtype") and args[0].dtype in (torch.int32, torch.int64):
184
+ input_ids = args[0]
185
+ attention_mask = torch.ones_like(input_ids)
186
+ elif len(args) == 1:
187
+ h_pool = args[0]
188
+ input_ids = None
189
+ elif "h_pool" in kwargs:
190
+ h_pool = kwargs["h_pool"]
191
+ input_ids = None
192
+ else:
193
+ raise ValueError("Expected either (input_ids, attention_mask) or (h_pool)")
194
+
195
+ if input_ids is not None:
196
+ if self.encoder is None:
197
+ raise RuntimeError("Encoder not loaded. Initialize with encoder_name to accept input_ids.")
198
+
199
+ outputs = self.encoder(input_ids=input_ids, attention_mask=attention_mask)
200
+ token_embeddings = outputs.last_hidden_state
201
+ input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
202
+ sum_embeddings = torch.sum(token_embeddings * input_mask_expanded, 1)
203
+ sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9)
204
+ h_pool = sum_embeddings / sum_mask
205
+
206
+ # Head 1: Risk score (continuous in 0.0 .. 1.0)
207
+ risk_raw = self.risk_head(h_pool)
208
+ risk_score = torch.sigmoid(risk_raw)
209
+
210
+ # Head 2: Multi-label taxonomy
211
+ taxonomy_logits = self.taxonomy_head(h_pool)
212
+ taxonomy_probs = torch.sigmoid(taxonomy_logits)
213
+
214
+ # Head 3: Epistemic Uncertainty with bounded damping [-6.0, 6.0]
215
+ s = self.uncertainty_head(h_pool)
216
+ log_variance = torch.clamp(s, min=-6.0, max=6.0)
217
+ variance = torch.exp(log_variance)
218
+ sigma = torch.sqrt(variance)
219
+ confidence = 1.0 - torch.clamp(sigma, min=0.0, max=1.0)
220
+
221
+ return {
222
+ "risk_score": risk_score,
223
+ "risk_logits": risk_raw,
224
+ "taxonomy_logits": taxonomy_logits,
225
+ "taxonomy_probs": taxonomy_probs,
226
+ "log_variance": log_variance,
227
+ "variance": variance,
228
+ "confidence": confidence,
229
+ }
230
+
231
+ def compute_loss(
232
+ self,
233
+ risk_pred: Any,
234
+ risk_target: Any,
235
+ taxonomy_logits: Any,
236
+ taxonomy_target: Any,
237
+ log_variance: Any,
238
+ delta: float = 0.1,
239
+ pos_weight: Optional[Any] = None,
240
+ risk_pos_weight: float = 1.0,
241
+ homoscedastic_weights: Optional[Tuple[float, float, float]] = None,
242
+ ) -> Dict[str, Any]:
243
+ """
244
+ Compute unified multi-task loss:
245
+ - L_risk: Huber loss (delta=0.1) with optional asymmetric risk weighting (risk_pos_weight)
246
+ - L_tax: BCEWithLogitsLoss (with optional pos_weight)
247
+ - L_unc: Heteroscedastic negative log-likelihood:
248
+ 0.5 * exp(-s) * (risk_target - risk_pred)^2 + 0.5 * s
249
+ """
250
+ if not HAS_TORCH:
251
+ raise RuntimeError("PyTorch is required for compute_loss")
252
+
253
+ risk_pred = risk_pred.view(-1, 1)
254
+ risk_target = risk_target.view(-1, 1).float()
255
+ log_variance = log_variance.view(-1, 1)
256
+ taxonomy_target = taxonomy_target.float()
257
+
258
+ # 1. Continuous Risk Huber Loss (with optional asymmetric risk weighting)
259
+ if risk_pos_weight > 1.0:
260
+ risk_weight = torch.where(risk_target >= 0.5, risk_pos_weight, 1.0)
261
+ l_risk = torch.mean(risk_weight * F.huber_loss(risk_pred, risk_target, delta=delta, reduction="none"))
262
+ else:
263
+ l_risk = F.huber_loss(risk_pred, risk_target, delta=delta)
264
+
265
+ # 2. Multi-Label Taxonomy Loss
266
+ l_tax = F.binary_cross_entropy_with_logits(taxonomy_logits, taxonomy_target, pos_weight=pos_weight)
267
+
268
+ # 3. Heteroscedastic Uncertainty Loss
269
+ diff_sq = (risk_target - risk_pred) ** 2
270
+ l_unc = torch.mean(0.5 * torch.exp(-log_variance) * diff_sq + 0.5 * log_variance)
271
+
272
+ # Total combined loss
273
+ if homoscedastic_weights is not None:
274
+ s1, s2, s3 = homoscedastic_weights
275
+ t_s1 = s1 if isinstance(s1, torch.Tensor) else torch.tensor(float(s1), device=risk_pred.device)
276
+ t_s2 = s2 if isinstance(s2, torch.Tensor) else torch.tensor(float(s2), device=risk_pred.device)
277
+ t_s3 = s3 if isinstance(s3, torch.Tensor) else torch.tensor(float(s3), device=risk_pred.device)
278
+ l_total = (
279
+ 0.5 / (t_s1 ** 2) * l_risk
280
+ + 0.5 / (t_s2 ** 2) * l_tax
281
+ + 0.5 / (t_s3 ** 2) * l_unc
282
+ + torch.log(torch.abs(t_s1 * t_s2 * t_s3) + 1e-8)
283
+ )
284
+ else:
285
+ l_total = l_risk + l_tax + l_unc
286
+
287
+ return {
288
+ "loss_total": l_total,
289
+ "loss_risk": l_risk,
290
+ "loss_taxonomy": l_tax,
291
+ "loss_uncertainty": l_unc,
292
+ }
293
+
294
+
295
+ class ModernBERTWithMultiTaskHead(ModernBERTMultiTaskModel):
296
+ """
297
+ End-to-end ModernBERT encoder coupled with 3 multi-task evaluation heads:
298
+ 1. Continuous calibrated risk regression (0.0 to 1.0)
299
+ 2. Multi-label risk taxonomy (5 classes)
300
+ 3. Epistemic uncertainty estimation
301
+ Matches the weights stored in fine-tuned model.safetensors.
302
+ """
303
+
304
+ def __init__(self, encoder_name: str = "answerdotai/ModernBERT-base"):
305
+ super().__init__(hidden_size_or_encoder=encoder_name)
306
+
307
+
308
+ class LayaDecisionHead:
309
+ """
310
+ Lean neural decision head interfacing with Laya ModernBERT (421M large or 164M base).
311
+ Provides sub-50ms local verification with calibrated risk scores.
312
+ Prioritizes ONNX Runtime (dynamic INT8 or FP32) with graceful fallback to PyTorch
313
+ or deterministic heuristic gate verdicts.
314
+ """
315
+
316
+ DEFAULT_HF_REPO: str = "wxsys/tyranid-bert"
317
+
318
+ def __init__(
319
+ self,
320
+ weights_path: Optional[Path] = None,
321
+ enabled: bool = False,
322
+ quantize_int8: Optional[bool] = None,
323
+ risk_threshold: Optional[float] = None,
324
+ temperature: Optional[float] = None,
325
+ prefer_onnx: bool = True,
326
+ ):
327
+ self.enabled = enabled
328
+ self.prefer_onnx = prefer_onnx
329
+ self._risk_threshold_explicit = risk_threshold is not None
330
+ if risk_threshold is not None:
331
+ self.risk_threshold = float(risk_threshold)
332
+ elif "CODE_ORACLE_RISK_THRESHOLD" in os.environ:
333
+ try:
334
+ self.risk_threshold = float(os.environ["CODE_ORACLE_RISK_THRESHOLD"])
335
+ except ValueError:
336
+ self.risk_threshold = 0.50
337
+ else:
338
+ self.risk_threshold = 0.50
339
+
340
+ self._temperature_explicit = temperature is not None
341
+ if temperature is not None:
342
+ self.temperature = float(temperature)
343
+ elif "CODE_ORACLE_TEMPERATURE" in os.environ:
344
+ try:
345
+ self.temperature = float(os.environ["CODE_ORACLE_TEMPERATURE"])
346
+ except ValueError:
347
+ self.temperature = 1.0
348
+ else:
349
+ self.temperature = 1.0
350
+
351
+ self.quantize_int8 = (
352
+ quantize_int8
353
+ if quantize_int8 is not None
354
+ else os.environ.get("CODE_ORACLE_INT8", "1").lower() in ("1", "true", "yes")
355
+ )
356
+ self._explicit_weights_path = weights_path
357
+ self.weights_path = self._resolve_weights_path(weights_path) if enabled else None
358
+ self.agent = None
359
+ self.pytorch_multitask_model: Optional[Any] = None
360
+ self.onnx_session: Optional[Any] = None
361
+ self.onnx_model_path: Optional[Path] = None
362
+ self.tokenizer = None
363
+ self.multi_task_model: Optional[ModernBERTMultiTaskModel] = (
364
+ ModernBERTMultiTaskModel() if HAS_TORCH else None
365
+ )
366
+ self._engine_mode: str = "heuristic"
367
+ self._loaded = False
368
+ if self.enabled and self.weights_path and self.weights_path.exists():
369
+ self._try_load_model()
370
+
371
+ def _resolve_weights_path(self, explicit_path: Optional[Path]) -> Optional[Path]:
372
+ if explicit_path:
373
+ return Path(explicit_path).resolve()
374
+
375
+ # Check explicit environment variable
376
+ env_weights = os.environ.get("CODE_ORACLE_WEIGHTS")
377
+ if env_weights and Path(env_weights).exists():
378
+ return Path(env_weights).resolve()
379
+
380
+ # Candidate local paths
381
+ candidates = [
382
+ Path.cwd() / ".code_oracle" / "weights",
383
+ Path.home() / ".cache" / "code_oracle" / "weights",
384
+ Path(__file__).resolve().parent / "weights",
385
+ Path.cwd() / "weights_base",
386
+ Path(__file__).resolve().parent.parent.parent / "weights_base",
387
+ Path.cwd() / "weights",
388
+ Path("/content/code_oracle_laya_base_model"),
389
+ Path("/content/code_oracle_laya_model"),
390
+ ]
391
+ target_files = ["model_int8.onnx", "model.onnx", "model.safetensors"]
392
+ for c in candidates:
393
+ if c.exists() and any((c / f).exists() for f in target_files):
394
+ return c.resolve()
395
+
396
+ # Attempt downloading from Hugging Face Hub if auto-download enabled
397
+ auto_download = os.environ.get("CODE_ORACLE_AUTO_DOWNLOAD", "1").lower() in ("1", "true", "yes")
398
+ if auto_download:
399
+ try:
400
+ from huggingface_hub import snapshot_download
401
+
402
+ cache_dir = Path.home() / ".cache" / "code_oracle" / "weights"
403
+ repo_id = os.environ.get("CODE_ORACLE_HF_REPO", self.DEFAULT_HF_REPO)
404
+ hf_token = os.environ.get("HF_TOKEN")
405
+ downloaded = snapshot_download(
406
+ repo_id=repo_id,
407
+ local_dir=str(cache_dir),
408
+ token=hf_token,
409
+ )
410
+ p = Path(downloaded)
411
+ if p.exists() and any((p / f).exists() for f in target_files):
412
+ return p.resolve()
413
+ except Exception as exc:
414
+ logger.debug("Failed to auto-download weights from Hugging Face: %s", exc)
415
+
416
+ return None
417
+
418
+ @staticmethod
419
+ def _tune_cpu_threads() -> int:
420
+ """
421
+ Auto-tune thread settings for CPU inference.
422
+ Restricts threads to physical cores to avoid hyperthread cache contention.
423
+ """
424
+ try:
425
+ if HAS_TORCH and torch is not None and torch.cuda.is_available():
426
+ return torch.get_num_threads()
427
+
428
+ physical_cores = None
429
+ if os.path.exists("/proc/cpuinfo"):
430
+ try:
431
+ with open("/proc/cpuinfo", "r", encoding="utf-8") as f:
432
+ cores = set()
433
+ phys_id = "0"
434
+ for line in f:
435
+ if line.startswith("physical id"):
436
+ phys_id = line.split(":")[1].strip()
437
+ elif line.startswith("core id"):
438
+ core_id = line.split(":")[1].strip()
439
+ cores.add(f"{phys_id}:{core_id}")
440
+ if cores:
441
+ physical_cores = len(cores)
442
+ except Exception:
443
+ pass
444
+
445
+ if not physical_cores:
446
+ total = os.cpu_count() or 1
447
+ physical_cores = max(1, total // 2 if total > 2 else total)
448
+
449
+ tuned_threads = max(1, min(physical_cores, 8))
450
+ if HAS_TORCH and torch is not None:
451
+ torch.set_num_threads(tuned_threads)
452
+ return tuned_threads
453
+ except Exception:
454
+ return 1
455
+
456
+ def _load_tokenizer(self) -> None:
457
+ """Load tokenizer using transformers or standalone tokenizers library."""
458
+ # 1. Try AutoTokenizer from transformers
459
+ try:
460
+ from transformers import AutoTokenizer
461
+ self.tokenizer = AutoTokenizer.from_pretrained(str(self.weights_path))
462
+ return
463
+ except Exception as e_tf:
464
+ logger.debug(f"AutoTokenizer loading skipped: {e_tf}")
465
+
466
+ # 2. Try pure tokenizers library from tokenizer.json
467
+ if self.weights_path is not None:
468
+ tok_json = self.weights_path / "tokenizer.json"
469
+ if tok_json.exists():
470
+ try:
471
+ from tokenizers import Tokenizer
472
+ self.tokenizer = Tokenizer.from_file(str(tok_json))
473
+ return
474
+ except Exception as e_tk:
475
+ logger.debug(f"tokenizers.Tokenizer loading skipped: {e_tk}")
476
+
477
+ def _tokenize(self, text: str, max_length: int = 512) -> Tuple[Any, Any]:
478
+ """Tokenize text into numpy input_ids and attention_mask."""
479
+ if self.tokenizer is None:
480
+ raise RuntimeError("Tokenizer not initialized")
481
+
482
+ if hasattr(self.tokenizer, "batch_encode_plus") or callable(self.tokenizer):
483
+ try:
484
+ tokens = self.tokenizer(
485
+ text,
486
+ return_tensors="np",
487
+ truncation=True,
488
+ max_length=max_length,
489
+ )
490
+ return tokens["input_ids"].astype(np.int64), tokens["attention_mask"].astype(np.int64)
491
+ except Exception:
492
+ pass
493
+
494
+ if hasattr(self.tokenizer, "encode"):
495
+ enc = self.tokenizer.encode(text)
496
+ ids = enc.ids[:max_length]
497
+ mask = enc.attention_mask[:max_length]
498
+ return np.array([ids], dtype=np.int64), np.array([mask], dtype=np.int64)
499
+
500
+ raise RuntimeError("Unsupported tokenizer instance")
501
+
502
+ def _try_load_model(self) -> None:
503
+ try:
504
+ tuned_threads = self._tune_cpu_threads()
505
+
506
+ # Read config.json if available to extract threshold and calibrated temperature
507
+ config_file = self.weights_path / "config.json"
508
+ if config_file.exists():
509
+ try:
510
+ with open(config_file, "r", encoding="utf-8") as f_cfg:
511
+ cfg_data = json.load(f_cfg)
512
+ if not self._risk_threshold_explicit and "CODE_ORACLE_RISK_THRESHOLD" not in os.environ:
513
+ if "default_decision_threshold" in cfg_data:
514
+ self.risk_threshold = float(cfg_data["default_decision_threshold"])
515
+ if not self._temperature_explicit and "CODE_ORACLE_TEMPERATURE" not in os.environ:
516
+ if "calibrated_temperature" in cfg_data:
517
+ self.temperature = float(cfg_data["calibrated_temperature"])
518
+ except Exception as e_cfg:
519
+ logger.debug(f"Could not load config.json: {e_cfg}")
520
+
521
+ env_engine = os.environ.get("CODE_ORACLE_ENGINE", "").lower().strip()
522
+ if env_engine == "heuristic":
523
+ self._loaded = False
524
+ self._engine_mode = "heuristic"
525
+ return
526
+
527
+ # 1. Prioritize ONNX Runtime inference
528
+ if self.prefer_onnx and HAS_ORT and env_engine != "pytorch":
529
+ if env_engine == "onnx_fp32":
530
+ candidate_onnx_files = [self.weights_path / "model.onnx"]
531
+ elif env_engine == "onnx_int8":
532
+ candidate_onnx_files = [self.weights_path / "model_int8.onnx"]
533
+ elif self.quantize_int8:
534
+ candidate_onnx_files = [
535
+ self.weights_path / "model_int8.onnx",
536
+ self.weights_path / "model.onnx",
537
+ ]
538
+ else:
539
+ candidate_onnx_files = [
540
+ self.weights_path / "model.onnx",
541
+ self.weights_path / "model_int8.onnx",
542
+ ]
543
+
544
+ for onnx_file in candidate_onnx_files:
545
+ if onnx_file.exists():
546
+ try:
547
+ logger.info(f"Loading ONNX decision session from {onnx_file}")
548
+ sess_opts = ort.SessionOptions()
549
+ sess_opts.intra_op_num_threads = tuned_threads
550
+ sess_opts.inter_op_num_threads = 1
551
+ sess_opts.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
552
+ self.onnx_session = ort.InferenceSession(
553
+ str(onnx_file),
554
+ sess_options=sess_opts,
555
+ providers=["CPUExecutionProvider"],
556
+ )
557
+ self.onnx_model_path = onnx_file
558
+ self._engine_mode = "onnx_int8" if "int8" in onnx_file.name else "onnx_fp32"
559
+ self._load_tokenizer()
560
+ self._loaded = True
561
+ logger.info(f"Successfully loaded ONNX decision head ({self._engine_mode})")
562
+ return
563
+ except Exception as e_onnx:
564
+ logger.warning(f"Failed to load ONNX model {onnx_file}: {e_onnx}. Trying fallback...")
565
+
566
+ # 2. Check for native Multi-Task PyTorch safetensors model (fallback)
567
+ safetensors_file = self.weights_path / "model.safetensors"
568
+ if HAS_TORCH and safetensors_file.exists() and env_engine not in ("onnx_int8", "onnx_fp32"):
569
+ try:
570
+ from safetensors.torch import load_file
571
+ logger.info(f"Checking native Multi-Task ModernBERT weights from {self.weights_path}")
572
+ sd = load_file(str(safetensors_file))
573
+ if any("risk_head" in k for k in sd.keys()):
574
+ mt_model = ModernBERTWithMultiTaskHead()
575
+ mt_model.load_state_dict(sd)
576
+ mt_model.eval()
577
+ self.pytorch_multitask_model = mt_model
578
+ self._load_tokenizer()
579
+ self._engine_mode = "pytorch"
580
+ self._loaded = True
581
+ logger.info("Successfully loaded native Multi-Task ModernBERT PyTorch model")
582
+ return
583
+ except Exception as e_native:
584
+ logger.debug(f"Native Multi-Task loading skipped: {e_native}")
585
+
586
+ # 3. Legacy laya.load loader (fallback)
587
+ if env_engine not in ("onnx_int8", "onnx_fp32"):
588
+ try:
589
+ import laya
590
+ logger.info(f"Loading fine-tuned Laya weights from {self.weights_path}")
591
+ with warnings.catch_warnings(), contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
592
+ warnings.simplefilter("ignore")
593
+ self.agent = laya.load(str(self.weights_path))
594
+
595
+ if self.quantize_int8 and self.agent and hasattr(self.agent, "model") and HAS_TORCH:
596
+ try:
597
+ with warnings.catch_warnings():
598
+ warnings.simplefilter("ignore")
599
+ self.agent.model.encoder = torch.ao.quantization.quantize_dynamic(
600
+ self.agent.model.encoder,
601
+ {torch.nn.Linear},
602
+ dtype=torch.qint8,
603
+ )
604
+ logger.info("Applied dynamic INT8 quantization to encoder linear layers")
605
+ except Exception as q_err:
606
+ logger.debug(f"Dynamic INT8 quantization skipped: {q_err}")
607
+
608
+ self._engine_mode = "pytorch"
609
+ self._loaded = True
610
+ return
611
+ except Exception as e_laya:
612
+ logger.debug(f"Legacy laya loading skipped: {e_laya}")
613
+
614
+ self._loaded = False
615
+ self._engine_mode = "heuristic"
616
+ except Exception as e:
617
+ logger.warning(f"Could not load decision model from {self.weights_path}: {e}")
618
+ self.agent = None
619
+ self.pytorch_multitask_model = None
620
+ self.onnx_session = None
621
+ self._loaded = False
622
+ self._engine_mode = "heuristic"
623
+
624
+ def enable_neural_head(self) -> bool:
625
+ """Dynamically enable and load neural head if weights exist and not loaded."""
626
+ if not self._loaded:
627
+ self.enabled = True
628
+ if not self.weights_path:
629
+ self.weights_path = self._resolve_weights_path(getattr(self, "_explicit_weights_path", None))
630
+ if self.weights_path and self.weights_path.exists():
631
+ self._try_load_model()
632
+ return self.is_neural_enabled
633
+
634
+ @property
635
+ def is_neural_enabled(self) -> bool:
636
+ """Returns True if fine-tuned neural weights (ONNX, PyTorch, or legacy) are loaded and active."""
637
+ return self._loaded and (
638
+ self.onnx_session is not None
639
+ or self.pytorch_multitask_model is not None
640
+ or self.agent is not None
641
+ )
642
+
643
+ @property
644
+ def engine_mode(self) -> str:
645
+ """Active engine status: 'onnx_int8', 'onnx_fp32', 'pytorch', or 'heuristic'."""
646
+ if not self.is_neural_enabled:
647
+ return "heuristic"
648
+ return self._engine_mode
649
+
650
+ def predict(
651
+ self,
652
+ linearized_dsl: str,
653
+ symbolic_status: str,
654
+ symbolic_confidence: float,
655
+ has_violations: bool,
656
+ ) -> Tuple[str, float, float]:
657
+ """
658
+ Evaluate linearized DSL subgraph.
659
+ Returns: (verdict_status, confidence, risk_score)
660
+ Maintains backward compatibility with 3-tuple return format.
661
+ """
662
+ res = self.predict_multi_task(
663
+ linearized_dsl=linearized_dsl,
664
+ symbolic_status=symbolic_status,
665
+ symbolic_confidence=symbolic_confidence,
666
+ has_violations=has_violations,
667
+ )
668
+ return res.status, res.confidence, res.risk_score
669
+
670
+ def predict_multi_task(
671
+ self,
672
+ linearized_dsl: str,
673
+ symbolic_status: str,
674
+ symbolic_confidence: float,
675
+ has_violations: bool,
676
+ violations: Optional[List[str]] = None,
677
+ cycles: Optional[List[List[str]]] = None,
678
+ taxonomy_threshold: float = 0.5,
679
+ risk_threshold: Optional[float] = None,
680
+ temperature: Optional[float] = None,
681
+ ) -> EnhancedDecisionResult:
682
+ """
683
+ Evaluate linearized DSL subgraph with Multi-Task Risk Taxonomy
684
+ and Heteroscedastic Epistemic Uncertainty.
685
+ """
686
+ violations = violations or []
687
+ cycles = cycles or []
688
+ eff_risk_threshold = self.risk_threshold if risk_threshold is None else risk_threshold
689
+ eff_temperature = self.temperature if temperature is None else temperature
690
+
691
+ # Hard rule: If deterministic symbolic gate caught a definite violation (cycle or arity),
692
+ # symbolic gate has absolute veto power (REJECTED).
693
+ if has_violations or symbolic_status == "REJECTED":
694
+ has_cycle = bool(cycles or any("CIRCULAR_DEPENDENCY" in v or "cycle" in v.lower() for v in violations))
695
+ has_broken_api = any(
696
+ any(k in v for k in ["ARITY_MISMATCH", "BROKEN_REFERENCE", "requires at least", "unexpected keyword", "SYNTAX_ERROR"])
697
+ for v in violations
698
+ ) or not has_cycle
699
+ has_sec = any("security" in v.lower() or "auth" in v.lower() for v in violations)
700
+ has_perf = any("perf" in v.lower() or "loop" in v.lower() for v in violations)
701
+
702
+ tax_scores = RiskTaxonomyScores(
703
+ breaking_public_api=0.95 if has_broken_api else 0.40,
704
+ security_surface=0.90 if has_sec else 0.05,
705
+ concurrency_hazard=0.95 if has_cycle else 0.05,
706
+ performance_regression=0.90 if has_perf else 0.05,
707
+ silent_logic_drift=0.85,
708
+ )
709
+ return EnhancedDecisionResult(
710
+ status="REJECTED",
711
+ confidence=1.0,
712
+ risk_score=0.95,
713
+ epistemic_uncertainty=0.01,
714
+ risk_taxonomy=tax_scores,
715
+ active_risk_categories=tax_scores.active_categories(threshold=taxonomy_threshold),
716
+ is_neural_calibrated=False,
717
+ engine_mode=self.engine_mode,
718
+ )
719
+
720
+ # If neural weights are available, run inference
721
+ if self.is_neural_enabled:
722
+ # 1. Prioritized ONNX Runtime inference
723
+ if self.onnx_session is not None and self.tokenizer is not None and HAS_NUMPY:
724
+ try:
725
+ input_ids_np, attention_mask_np = self._tokenize(linearized_dsl, max_length=512)
726
+ ort_inputs = {
727
+ "input_ids": input_ids_np,
728
+ "attention_mask": attention_mask_np,
729
+ }
730
+ output_names = [o.name for o in self.onnx_session.get_outputs()]
731
+ ort_outputs = self.onnx_session.run(output_names, ort_inputs)
732
+ out_map = dict(zip(output_names, ort_outputs))
733
+
734
+ raw_risk = float(out_map["risk_score"][0][0])
735
+ raw_logits = float(out_map["risk_logits"][0][0]) if "risk_logits" in out_map else None
736
+
737
+ if eff_temperature is not None and eff_temperature > 0 and abs(eff_temperature - 1.0) > 1e-4:
738
+ if raw_logits is not None:
739
+ scaled_risk = 1.0 / (1.0 + math.exp(-raw_logits / eff_temperature))
740
+ else:
741
+ eps = 1e-6
742
+ clamped_r = min(max(raw_risk, eps), 1.0 - eps)
743
+ z = math.log(clamped_r / (1.0 - clamped_r))
744
+ scaled_risk = 1.0 / (1.0 + math.exp(-z / eff_temperature))
745
+ pred_risk = round(scaled_risk, 4)
746
+ else:
747
+ pred_risk = round(raw_risk, 4)
748
+
749
+ tax_probs = [float(x) for x in out_map["taxonomy_probs"][0]]
750
+ pred_conf = round(float(out_map["confidence"][0][0]), 4)
751
+ log_var = float(out_map["log_variance"][0][0])
752
+ epistemic_uncertainty = round(float(math.sqrt(math.exp(log_var))), 4)
753
+
754
+ pred_status = "APPROVED" if pred_risk < eff_risk_threshold else "REJECTED"
755
+
756
+ tax_scores = RiskTaxonomyScores(
757
+ breaking_public_api=round(tax_probs[0], 4),
758
+ security_surface=round(tax_probs[1], 4),
759
+ concurrency_hazard=round(tax_probs[2], 4),
760
+ performance_regression=round(tax_probs[3], 4),
761
+ silent_logic_drift=round(tax_probs[4], 4),
762
+ )
763
+ return EnhancedDecisionResult(
764
+ status=pred_status,
765
+ confidence=pred_conf,
766
+ risk_score=pred_risk,
767
+ epistemic_uncertainty=epistemic_uncertainty,
768
+ risk_taxonomy=tax_scores,
769
+ active_risk_categories=tax_scores.active_categories(threshold=taxonomy_threshold),
770
+ is_neural_calibrated=True,
771
+ engine_mode=self.engine_mode,
772
+ )
773
+ except Exception as e_onnx_inf:
774
+ logger.warning(f"ONNX inference error: {e_onnx_inf}. Falling back to PyTorch / heuristics.")
775
+
776
+ # 2. Native Multi-Task PyTorch Model
777
+ if self.pytorch_multitask_model is not None and self.tokenizer is not None and HAS_TORCH:
778
+ try:
779
+ tokens = self.tokenizer(
780
+ linearized_dsl,
781
+ return_tensors="pt",
782
+ truncation=True,
783
+ max_length=512,
784
+ )
785
+ with torch.no_grad():
786
+ out = self.pytorch_multitask_model(tokens["input_ids"], tokens["attention_mask"])
787
+
788
+ raw_risk = out["risk_score"].item()
789
+ raw_logits = out.get("risk_logits")
790
+
791
+ if eff_temperature is not None and eff_temperature > 0 and abs(eff_temperature - 1.0) > 1e-4:
792
+ if raw_logits is not None:
793
+ scaled_risk = torch.sigmoid(raw_logits / eff_temperature).item()
794
+ else:
795
+ eps = 1e-6
796
+ clamped_r = min(max(raw_risk, eps), 1.0 - eps)
797
+ z = math.log(clamped_r / (1.0 - clamped_r))
798
+ scaled_risk = 1.0 / (1.0 + math.exp(-z / eff_temperature))
799
+ pred_risk = round(scaled_risk, 4)
800
+ else:
801
+ pred_risk = round(raw_risk, 4)
802
+
803
+ tax_probs = out["taxonomy_probs"].squeeze(0).tolist()
804
+ pred_conf = round(out["confidence"].item(), 4)
805
+ s = out["log_variance"]
806
+ epistemic_uncertainty = round(float(torch.exp(s).sqrt().item()), 4)
807
+
808
+ pred_status = "APPROVED" if pred_risk < eff_risk_threshold else "REJECTED"
809
+
810
+ tax_scores = RiskTaxonomyScores(
811
+ breaking_public_api=round(float(tax_probs[0]), 4),
812
+ security_surface=round(float(tax_probs[1]), 4),
813
+ concurrency_hazard=round(float(tax_probs[2]), 4),
814
+ performance_regression=round(float(tax_probs[3]), 4),
815
+ silent_logic_drift=round(float(tax_probs[4]), 4),
816
+ )
817
+ return EnhancedDecisionResult(
818
+ status=pred_status,
819
+ confidence=pred_conf,
820
+ risk_score=pred_risk,
821
+ epistemic_uncertainty=epistemic_uncertainty,
822
+ risk_taxonomy=tax_scores,
823
+ active_risk_categories=tax_scores.active_categories(threshold=taxonomy_threshold),
824
+ is_neural_calibrated=True,
825
+ engine_mode="pytorch",
826
+ )
827
+ except Exception as e_nt:
828
+ logger.warning(f"Native Multi-Task inference error: {e_nt}. Falling back.")
829
+
830
+ # 3. Legacy laya agent
831
+ elif self.agent is not None:
832
+ try:
833
+ with warnings.catch_warnings(), contextlib.redirect_stdout(io.StringIO()):
834
+ warnings.simplefilter("ignore")
835
+ res = self.agent.predict(linearized_dsl, VERIFICATION_QUESTIONS)
836
+ status_ans = res["answers"]["status"]
837
+ risk_ans = res["answers"]["risk"]
838
+
839
+ pred_status = status_ans["choice"]
840
+ pred_confidence = max(symbolic_confidence, float(status_ans["confidence"]))
841
+ pred_risk = float(risk_ans["score"]) / 4.0 # Normalize 0..4 to 0.0..1.0
842
+ if pred_risk >= eff_risk_threshold:
843
+ pred_status = "REJECTED"
844
+
845
+ epistemic_uncertainty = max(0.0001, round((1.0 - pred_confidence) ** 2, 4))
846
+
847
+ has_api_drift = any(k in linearized_dsl for k in ["PARAM", "SIG", "ARITY", "DELETED"])
848
+ has_cycle_signal = any(k in linearized_dsl for k in ["CYCLE", "MUTUAL", "SCC"])
849
+ has_sec_signal = any(k in linearized_dsl.lower() for k in ["secret", "auth", "token", "pwd", "taint"])
850
+ has_perf_signal = any(k in linearized_dsl for k in ["LOOP", "QUERY", "PERF"])
851
+
852
+ tax_scores = RiskTaxonomyScores(
853
+ breaking_public_api=round(min(0.99, max(0.02, pred_risk * 1.2 if has_api_drift else pred_risk * 0.4)), 4),
854
+ security_surface=round(min(0.99, max(0.01, 0.85 if has_sec_signal else pred_risk * 0.15)), 4),
855
+ concurrency_hazard=round(min(0.99, max(0.01, 0.90 if has_cycle_signal else pred_risk * 0.2)), 4),
856
+ performance_regression=round(min(0.99, max(0.01, 0.85 if has_perf_signal else pred_risk * 0.2)), 4),
857
+ silent_logic_drift=round(min(0.95, max(0.02, pred_risk * 0.7)), 4),
858
+ )
859
+
860
+ return EnhancedDecisionResult(
861
+ status=pred_status,
862
+ confidence=pred_confidence,
863
+ risk_score=pred_risk,
864
+ epistemic_uncertainty=epistemic_uncertainty,
865
+ risk_taxonomy=tax_scores,
866
+ active_risk_categories=tax_scores.active_categories(threshold=taxonomy_threshold),
867
+ is_neural_calibrated=True,
868
+ engine_mode="pytorch",
869
+ )
870
+ except Exception as e:
871
+ logger.warning(f"Laya neural inference error: {e}. Falling back to symbolic gate.")
872
+
873
+ # Fallback to deterministic symbolic gate result
874
+ if symbolic_status == "APPROVED":
875
+ risk = 0.05
876
+ tax_scores = RiskTaxonomyScores(
877
+ breaking_public_api=0.02,
878
+ security_surface=0.01,
879
+ concurrency_hazard=0.01,
880
+ performance_regression=0.01,
881
+ silent_logic_drift=0.02,
882
+ )
883
+ else:
884
+ risk = 0.95
885
+ tax_scores = RiskTaxonomyScores(
886
+ breaking_public_api=0.95,
887
+ security_surface=0.05,
888
+ concurrency_hazard=0.10,
889
+ performance_regression=0.05,
890
+ silent_logic_drift=0.85,
891
+ )
892
+
893
+ fallback_status = symbolic_status
894
+ if fallback_status == "APPROVED" and risk >= eff_risk_threshold:
895
+ fallback_status = "REJECTED"
896
+
897
+ return EnhancedDecisionResult(
898
+ status=fallback_status,
899
+ confidence=symbolic_confidence,
900
+ risk_score=risk,
901
+ epistemic_uncertainty=0.02 if symbolic_status == "APPROVED" else 0.01,
902
+ risk_taxonomy=tax_scores,
903
+ active_risk_categories=tax_scores.active_categories(threshold=taxonomy_threshold),
904
+ is_neural_calibrated=False,
905
+ engine_mode="heuristic",
906
+ )