Text Classification
Transformers
Safetensors
English
nli
cross-encoder
qwen3.5
reranker
image-text-to-text
Instructions to use AlexWortega/openjev with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use AlexWortega/openjev with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="AlexWortega/openjev")# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("AlexWortega/openjev", device_map="auto") - Notebooks
- Google Colab
- Kaggle
| """Image JevBench bridge and reproducible public-example runner. | |
| The published benchmark runner/sealed corpus are not in the public repository. | |
| This bridge consumes its public example schema without passing alt text or gold | |
| to inference. Output probabilities are explicitly normalized NLI entailment, | |
| not calibrated forecasts. No temperature is fitted on benchmark examples. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import base64 | |
| import hashlib | |
| import json | |
| import math | |
| from pathlib import Path | |
| import time | |
| import httpx | |
| import numpy as np | |
| class OpenJevImageAdapter: | |
| def __init__(self, endpoint, model, timeout=180): | |
| self.endpoint = endpoint.rstrip("/") | |
| self.model = model | |
| self.client = httpx.Client(timeout=timeout) | |
| def close(self): | |
| self.client.close() | |
| def build_request(self, item, image_bytes): | |
| options = item["options"] | |
| criteria = {o["label"]: o["text"] for o in options} | |
| if not criteria or len(criteria) != len(options): | |
| raise ValueError("Options must have unique labels") | |
| return {"model": self.model, "state": "An image: <<IMG>>", | |
| "image_data": base64.b64encode(image_bytes).decode("ascii"), | |
| "questions": {"decision": {"type": "choice", "instructions": item["question"], | |
| "criteria": criteria}}} | |
| def run(self, item, image_bytes): | |
| body = self.build_request(item, image_bytes) | |
| start = time.perf_counter() | |
| r = self.client.post(self.endpoint + "/v1/systemone", json=body) | |
| latency = time.perf_counter() - start | |
| r.raise_for_status() | |
| raw = r.json() | |
| answer = raw["answers"]["decision"] | |
| p = answer["probabilities"] | |
| labels = list(body["questions"]["decision"]["criteria"]) | |
| if set(p) != set(labels) or any(isinstance(v, bool) or not isinstance(v, (int, float)) | |
| or not math.isfinite(v) or not 0 <= v <= 1 for v in p.values()): | |
| raise ValueError("Invalid categorical probabilities") | |
| total = sum(p.values()) | |
| if abs(total - 1) > 0.005: | |
| raise ValueError("Probabilities do not sum to one") | |
| # Remove API decimal rounding error, preserving score order. | |
| p = {label: p[label] / total for label in labels} | |
| choice = answer["choice"] | |
| if choice not in p or p[choice] < max(p.values()): | |
| raise ValueError("Choice disagrees with distribution") | |
| return {"id": item.get("sourceItemId", item.get("key")), "choice": choice, | |
| "probabilities": p, "probability_method": raw.get("probability_method", "unknown"), | |
| "latency_s": latency, "image_sha256": hashlib.sha256(image_bytes).hexdigest(), | |
| "request_sha256": hashlib.sha256(json.dumps(body, sort_keys=True).encode()).hexdigest(), | |
| "raw": raw} | |
| def summarize(rows): | |
| good = [r for r in rows if "probabilities" in r] | |
| labelled = [r for r in good if r.get("gold") is not None] | |
| result = {"attempted": len(rows), "completed": len(good), "errors": len(rows)-len(good), | |
| "scope": "published resized public examples only; not the full benchmark", | |
| "probability_method": "normalized_entailment_v1; no fitted calibration", | |
| "cost": None} | |
| if good: | |
| result.update({"p50_s": float(np.percentile([r["latency_s"] for r in good], 50)), | |
| "p95_s": float(np.percentile([r["latency_s"] for r in good], 95))}) | |
| if labelled: | |
| correct = [r["choice"] == r["gold"] for r in labelled] | |
| conf = [r["probabilities"][r["choice"]] for r in labelled] | |
| ece = 0.0 | |
| for i in range(10): | |
| indices = [j for j, c in enumerate(conf) if min(int(c*10), 9) == i] | |
| if indices: | |
| ece += abs(sum(conf[j] - correct[j] for j in indices)) / len(labelled) | |
| result.update({"scored": len(labelled), "correct": sum(correct), "accuracy": sum(correct)/len(labelled), | |
| "top_label_ece_10_bins": ece, | |
| "brier_multiclass": float(np.mean([sum((p-(k==r["gold"]))**2 | |
| for k, p in r["probabilities"].items()) for r in labelled]))}) | |
| return result | |
| def main(): | |
| ap = argparse.ArgumentParser(__doc__) | |
| ap.add_argument("--items", type=Path, required=True, help="JSON array exported from PUBLIC_IMAGE_JEV_EXAMPLES") | |
| ap.add_argument("--assets-root", type=Path, required=True, help="Repository public/ directory") | |
| ap.add_argument("--endpoint", required=True) | |
| ap.add_argument("--model", required=True) | |
| ap.add_argument("--output", type=Path, required=True) | |
| args = ap.parse_args() | |
| items = json.loads(args.items.read_text()) | |
| args.output.mkdir(parents=True, exist_ok=True) | |
| adapter = OpenJevImageAdapter(args.endpoint, args.model) | |
| rows = [] | |
| try: | |
| with (args.output / "predictions.jsonl").open("x") as f: | |
| for item in items: | |
| try: | |
| root = args.assets_root.resolve() | |
| path = (root / item["image"].lstrip("/")).resolve() | |
| if not path.is_relative_to(root): | |
| raise ValueError("Image escapes assets root") | |
| row = adapter.run(item, path.read_bytes()) | |
| row["gold"] = item.get("correctLabel") | |
| if row["gold"] is not None and row["gold"] not in row["probabilities"]: | |
| raise ValueError("Gold is not an option") | |
| except Exception as e: | |
| row = {"id": item.get("sourceItemId", item.get("key")), "error": str(e)} | |
| rows.append(row) | |
| f.write(json.dumps(row) + "\n") | |
| f.flush() | |
| print(json.dumps({k: v for k, v in row.items() if k != "raw"}), flush=True) | |
| finally: | |
| adapter.close() | |
| report = summarize(rows) | |
| (args.output / "summary.json").write_text(json.dumps(report, indent=2) + "\n") | |
| print(json.dumps(report, indent=2)) | |
| if report["errors"]: | |
| raise SystemExit(1) | |
| if __name__ == "__main__": | |
| main() | |