Text Classification
ONNX
English
multilingual
pico-type
pico_type
byte-level
content-classification
multi-head
classifier
edge-ai
code-detection
language-detection
clipboard
tiny
open-source
onnx-runtime
content-moderation
risk-detection
secrets-detection
prompt-injection-detection
pii-detection
pico-type-arth
Eval Results (legacy)
File size: 5,401 Bytes
c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d 3d57a4d c5f9f0d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | """pico-type CLI: classify content from stdin/file/clipboard."""
from __future__ import annotations
import argparse
import json
import os
import sys
import numpy as np
from .labels import (
ALL_HEADS,
COARSE_LABELS,
CODE_LANG_LABELS,
FILE_MIME_LABELS,
MODALITY_LABELS,
RISK_LABELS,
SUBTYPE_LABELS,
TEXT_LANG_LABELS,
)
LABEL_TABLES = {
"coarse": COARSE_LABELS,
"modality": MODALITY_LABELS,
"subtype": SUBTYPE_LABELS,
"code_lang": CODE_LANG_LABELS,
"text_lang": TEXT_LANG_LABELS,
"file_mime": FILE_MIME_LABELS,
"risk": RISK_LABELS,
}
def load_onnx_model(tier: str = "base", model_dir: str = "checkpoints"):
import onnxruntime as ort
path = os.path.join(model_dir, f"picotype_{tier}.onnx")
if not os.path.exists(path):
raise FileNotFoundError(f"ONNX model not found: {path}")
session = ort.InferenceSession(path)
return session
def load_torch_model(tier: str = "base", checkpoint: str = ""):
import torch
from .arch import PicoType, PicoTypeConfig
cfg = PicoTypeConfig(max_bytes=1024)
model = PicoType(cfg)
ckpt = torch.load(checkpoint, map_location="cpu")
model.load_state_dict(ckpt.get("model_state_dict", ckpt))
model.eval()
return model, tier
def run_onnx(session, data: bytes, max_bytes: int = 1024) -> dict:
ids = np.frombuffer(data[:max_bytes], dtype=np.uint8).astype(np.int64)
seq_len = len(ids)
if seq_len > max_bytes:
ids = ids[:max_bytes]
seq_len = max_bytes
padded = np.zeros(max_bytes, dtype=np.int64)
padded[:seq_len] = ids
mask = np.zeros(max_bytes, dtype=np.bool_)
mask[:seq_len] = True
feed = {
"input_ids": padded[None, :],
"attention_mask": mask[None, :],
}
outs = session.run(None, feed)
result = {}
for name, logits in zip(ALL_HEADS, outs):
probs = _softmax(logits[0])
if name == "risk":
result[name] = {LABEL_TABLES[name][i]: float(probs[i]) for i in range(len(probs))}
else:
idx = int(np.argmax(probs))
label = LABEL_TABLES[name][idx]
result[name] = {"label": label, "confidence": float(probs[idx]), "index": idx}
return result
def _softmax(x):
e = np.exp(x - np.max(x))
return e / e.sum()
def run_torch(model, tier: str, data: bytes, max_bytes: int = 1024) -> dict:
import torch
model = model[0] if isinstance(model, tuple) else model
ids = torch.tensor([list(data[:max_bytes])], dtype=torch.long)
mask = torch.ones(1, ids.shape[1], dtype=torch.bool)
with torch.no_grad():
logits_dict = model(ids, mask)
out = {}
for head in ALL_HEADS:
logits = logits_dict[head]
tier_logits = logits[tier] if isinstance(logits, dict) else logits
if isinstance(tier_logits, dict):
tier_logits = tier_logits[tier]
probs = torch.softmax(tier_logits[0], dim=-1)
if head == "risk":
out[head] = {LABEL_TABLES[head][i]: float(probs[i]) for i in range(len(probs))}
else:
idx = int(torch.argmax(probs).item())
out[head] = {"label": LABEL_TABLES[head][idx], "confidence": float(probs[idx]), "index": idx}
return out
def read_input(args) -> bytes:
if args.text:
return args.text.encode("utf-8")
if args.file:
with open(args.file, "rb") as f:
return f.read()
if args.clip:
import subprocess
return subprocess.check_output(["pbpaste"]).rstrip(b"\n")
if not sys.stdin.isatty():
return sys.stdin.buffer.read()
raise ValueError("No input provided. Use --text, --file, --clip, or pipe content.")
def build_parser():
p = argparse.ArgumentParser(prog="picotype", description="Classify content type and risk")
p.add_argument("--text", "-t", help="Text string to classify")
p.add_argument("--file", "-f", help="File path to classify")
p.add_argument("--clip", "-c", action="store_true", help="Classify clipboard content")
p.add_argument("--tier", default="base", choices=["tiny", "small", "base", "pro"], help="Model tier")
p.add_argument("--model-dir", default="checkpoints", help="Directory with ONNX models")
p.add_argument("--checkpoint", help="PyTorch checkpoint (fallback if no ONNX)")
p.add_argument("--pretty", "-p", action="store_true", help="Pretty-print JSON output")
return p
def main():
args = build_parser().parse_args()
try:
data = read_input(args)
except ValueError as e:
print(e, file=sys.stderr)
sys.exit(1)
if not data.strip():
print('{"error": "empty input"}')
sys.exit(0)
onnx_path = os.path.join(args.model_dir, f"picotype_{args.tier}.onnx")
if os.path.exists(onnx_path):
session = load_onnx_model(args.tier, args.model_dir)
result = run_onnx(session, data)
elif args.checkpoint:
model = load_torch_model(args.tier, args.checkpoint)
result = run_torch(model, args.tier, data)
else:
print(f"ONNX model not found at {onnx_path}. Use --checkpoint to use PyTorch.", file=sys.stderr)
sys.exit(1)
result["text_length"] = len(data)
result["tier"] = args.tier
indent = 2 if args.pretty else None
json.dump(result, sys.stdout, indent=indent, ensure_ascii=False)
print()
if __name__ == "__main__":
main()
|