mockgen-classifier-v1 / handler.py
Unseen1980's picture
Upload handler.py with huggingface_hub
bb739b4 verified
Raw
History Blame Contribute Delete
5.49 kB
import json
from pathlib import Path
from typing import Any
import numpy as np
import onnxruntime as ort
def _stringify(value: Any) -> str:
if value is None:
return ""
if isinstance(value, list):
return ", ".join(_stringify(item) for item in value if _stringify(item))
if isinstance(value, dict):
return json.dumps(value, separators=(",", ":"))
return str(value)
def _normalize_unicode_codepoint_v1(text: str) -> str:
parts: list[str] = []
ascii_run = ""
def flush_ascii() -> None:
nonlocal ascii_run
if ascii_run:
parts.append(ascii_run)
ascii_run = ""
for char in text:
codepoint = ord(char)
if codepoint < 128:
ascii_run += char
elif char.isalnum():
flush_ascii()
parts.append(f"u{codepoint:04x}")
else:
ascii_run += " "
flush_ascii()
return " ".join(parts)
def _field_text(property_payload: dict[str, Any]) -> str:
semantic = property_payload.get("semantic") if isinstance(property_payload.get("semantic"), dict) else {}
source = property_payload.get("x-mockgen-source") if isinstance(property_payload.get("x-mockgen-source"), dict) else {}
sap_annotations = (
source.get("sapAnnotations")
or source.get("sourceAnnotations")
or source.get("annotations")
or source.get("annotationSummary")
or source.get("sap_annotations_summary")
)
fields = [
("property_name", property_payload.get("name") or property_payload.get("property_name")),
("label", semantic.get("label") or property_payload.get("label")),
("entity_type_name", property_payload.get("entity") or property_payload.get("entity_type_name")),
("neighbor_properties", semantic.get("related") or property_payload.get("related") or property_payload.get("neighbor_properties")),
("type", property_payload.get("type")),
("sap_annotations_summary", sap_annotations),
("locale", source.get("locale") or property_payload.get("locale")),
]
return " [SEP] ".join(
f"{field}: {text_value}"
for field, value in fields
if (text_value := _stringify(value).strip())
)
def _input_text(value: Any) -> str:
if isinstance(value, str):
stripped = value.strip()
if stripped.startswith("{"):
try:
parsed = json.loads(stripped)
if isinstance(parsed, dict):
return _field_text(parsed)
except json.JSONDecodeError:
pass
parsed_lines = _parse_field_card(stripped)
if parsed_lines:
return _field_text(parsed_lines)
return value
if isinstance(value, dict):
return _field_text(value)
raise TypeError("inputs must be a classifier text string or a property metadata object")
def _parse_field_card(text: str) -> dict[str, Any] | None:
aliases = {
"entity": "entity",
"entity_type": "entity",
"entity_type_name": "entity",
"field": "name",
"name": "name",
"property": "name",
"property_name": "name",
"label": "label",
"type": "type",
"related": "related",
"neighbors": "related",
"neighbor_properties": "related",
}
parsed: dict[str, Any] = {}
for raw_line in text.splitlines():
if ":" not in raw_line:
continue
key, raw_value = raw_line.split(":", 1)
normalized_key = key.strip().lower().replace(" ", "_").replace("-", "_")
target = aliases.get(normalized_key)
if not target:
continue
value = raw_value.strip()
if not value:
continue
parsed[target] = [part.strip() for part in value.split(",") if part.strip()] if target == "related" else value
return parsed if {"entity", "name", "type"}.issubset(parsed) else None
def _softmax(scores: np.ndarray) -> np.ndarray:
shifted = scores - np.max(scores)
exp = np.exp(shifted)
return exp / exp.sum()
class EndpointHandler:
def __init__(self, path: str = ""):
root = Path(path or ".")
report = json.loads((root / "export-report.json").read_text(encoding="utf8"))
self.labels = report["labels"]
self.input_normalizer = report.get("inputNormalizer", "unicode-codepoint-v1")
self.session = ort.InferenceSession(str(root / "classifier.onnx"), providers=["CPUExecutionProvider"])
def __call__(self, data: dict[str, Any]) -> list[dict[str, Any]]:
payload = data.get("inputs", data)
text = _input_text(payload)
if self.input_normalizer == "unicode-codepoint-v1":
text = _normalize_unicode_codepoint_v1(text)
outputs = self.session.run(None, {"text": np.array([[text]], dtype=object)})
raw_scores = np.asarray(outputs[1] if len(outputs) > 1 else outputs[0]).reshape(-1).astype(float)
scores = raw_scores if np.isclose(raw_scores.sum(), 1.0, atol=1e-3) else _softmax(raw_scores)
ranked = sorted(
(
{"label": self.labels[index] if index < len(self.labels) else "unknown", "score": float(score)}
for index, score in enumerate(scores[: len(self.labels)])
),
key=lambda item: item["score"],
reverse=True,
)
top_k = int(data.get("top_k", 5))
return ranked[:top_k]