|
10 | 10 | from __future__ import annotations |
11 | 11 |
|
12 | 12 | import collections |
| 13 | +import json |
| 14 | +import re |
13 | 15 | from typing import Iterable, List, Sequence, Tuple |
14 | 16 |
|
15 | 17 | import numpy as np |
@@ -222,6 +224,38 @@ def predict_link(query_dst: torch.Tensor, llm_dst: int) -> torch.Tensor: |
222 | 224 | return (query_dst == llm_dst).float() |
223 | 225 |
|
224 | 226 |
|
| 227 | +def extract_destination_node(output: object) -> int: |
| 228 | + """Extract ``destination_node`` from model output. |
| 229 | +
|
| 230 | + Tries default strict JSON parse, with a minimal |
| 231 | + fallback for slightly malformed text that still contains the key/value. |
| 232 | + """ |
| 233 | + if output is None: |
| 234 | + raise ValueError('Model output is None') |
| 235 | + |
| 236 | + text = output if isinstance(output, str) else str(output) |
| 237 | + text = text.strip() |
| 238 | + if not text: |
| 239 | + raise ValueError('Model output is empty') |
| 240 | + |
| 241 | + try: |
| 242 | + return int(json.loads(text)['destination_node']) |
| 243 | + except Exception: |
| 244 | + pass |
| 245 | + |
| 246 | + if isinstance(output, dict) and 'destination_node' in output: |
| 247 | + return int(output['destination_node']) |
| 248 | + if hasattr(output, 'destination_node'): |
| 249 | + return int(getattr(output, 'destination_node')) |
| 250 | + |
| 251 | + # Fallback for partially malformed JSON/text containing the key-value pair. |
| 252 | + m = re.search(r'"destination_node"\s*:\s*(-?\d+)', text) |
| 253 | + if m: |
| 254 | + return int(m.group(1)) |
| 255 | + |
| 256 | + raise ValueError(f'Could not extract destination_node from output: {text[:160]}') |
| 257 | + |
| 258 | + |
225 | 259 | class BackgroundBuffer: |
226 | 260 | """Sliding window of the most recent global ``(src, dst, ts)`` edges. |
227 | 261 |
|
|
0 commit comments