Views
No views yet
moderation detector for border, an embeddable library that inspects the text going into and coming out of an LLM and returns a structured decision plus an audit-grade evidence record.flowxai/moderation on the hub. It is one detector of 28, and it is not a general purpose moderation classifier: it was trained for this library's policy, is read at the operating point below, and reports through the evidence record rather than returning a bare score.cyber_intrusion, defamation, election_integrity, extremism, fraud_deception, hate_incitement, illicit_drugs, property_crime, self_harm, sexual_exploitation, violent_facilitation, weapons_cbrnonnx/model.int8.onnx, 535 MB, opset 17child_safety: must not be synthetically generated. The label covers sexualisation of minors and grooming, and no length band or framing makes generating either acceptable. It needs a vetted source with its provenance recorded, which is a decision about provenance rather than compute. Until then moderation is a twelve-label head and the thirteenth reports unavailable rather than clean.macro_f1 objective.pip install flowx-border1# policy.yaml
2policy_id: default
3version: 1
4
5detectors:
6 moderation:
7 enabled: true
8 on_fail: flag
9 threshold: 0.911from flowx_border import load_policy, scan_input, scan_output
2
3policy = load_policy("policy.yaml")
4
5decision = scan_input(user_text, policy)
6decision = scan_output(model_answer, policy)
7
8print(decision.verdict) # allow | flag | redact | block
9print([f.label for f in decision.findings if f.detector_id == "moderation"])
10print(decision.evidence.record_id)scan_input and scan_output is where it fires. It is T2, so it runs on the standard path and can be disabled per policy. Its budget is 150 ms at 87 tokens on one CPU thread.onnxruntime directly. Two things you then own yourself, and they are the reason the library exists: the operating point above is not in the graph, and neither is the chunking. Inputs longer than the trained window have to be split and recombined, or the scores past it are extrapolation.1import onnxruntime as ort
2from huggingface_hub import hf_hub_download
3from tokenizers import Tokenizer
4
5repo = "flowxai/moderation"
6session = ort.InferenceSession(hf_hub_download(repo, "onnx/model.int8.onnx"))
7tokenizer = Tokenizer.from_file(hf_hub_download(repo, "tokenizer.json"))| Label | Support | P | R | F1 |
|---|---|---|---|---|
cyber_intrusion | 119 | 0.846 | 0.874 | 0.860 |
defamation | 111 | 0.973 | 0.982 | 0.978 |
election_integrity | 116 | 0.991 | 0.966 | 0.978 |
extremism | 130 | 0.862 | 0.815 | 0.838 |
fraud_deception | 124 | 0.812 | 0.839 | 0.825 |
hate_incitement | 106 | 0.944 | 0.953 | 0.948 |
illicit_drugs | 141 | 0.961 | 0.872 | 0.914 |
property_crime | 129 | 0.861 | 0.814 | 0.837 |
self_harm | 99 | 0.830 | 0.939 | 0.881 |
sexual_exploitation | 119 | 0.983 | 0.950 | 0.966 |
violent_facilitation | 127 | 0.888 | 0.811 | 0.848 |
weapons_cbrn | 133 | 0.925 | 0.925 | 0.925 |
| Language | Support | P | R | F1 | Note |
|---|---|---|---|---|---|
bg Bulgarian | 52 | 1.000 | 1.000 | 1.000 | |
it Italian | 61 | 0.984 | 1.000 | 0.992 | |
pl Polish | 61 | 1.000 | 0.984 | 0.992 | |
sl Slovenian | 59 | 1.000 | 0.983 | 0.992 | |
el Greek | 57 | 1.000 | 0.983 | 0.991 | |
de German | 54 | 1.000 | 0.982 | 0.991 | |
lt Lithuanian | 52 | 1.000 | 0.981 | 0.990 | |
da Danish | 51 | 1.000 | 0.980 | 0.990 | |
en English | 62 | 0.984 | 0.984 | 0.984 | |
et Estonian | 59 | 1.000 | 0.966 | 0.983 | |
nl Dutch | 56 | 0.982 | 0.982 | 0.982 | |
fi Finnish | 50 | 1.000 | 0.960 | 0.980 | |
sk Slovak | 48 | 0.979 | 0.979 | 0.979 | |
hu Hungarian | 63 | 0.969 | 0.984 | 0.976 | |
ro Romanian | 63 | 1.000 | 0.952 | 0.976 | |
hr Croatian | 60 | 0.967 | 0.983 | 0.975 | |
fr French | 60 | 1.000 | 0.950 | 0.974 | |
lv Latvian | 55 | 1.000 | 0.946 | 0.972 | |
sv Swedish | 52 | 0.962 | 0.981 | 0.971 | |
pt Portuguese | 52 | 0.980 | 0.962 | 0.971 | |
cs Czech | 60 | 0.983 | 0.950 | 0.966 | |
es Spanish | 52 | 1.000 | 0.923 | 0.960 | |
tr Turkish | 60 | 0.936 | 0.983 | 0.959 | |
az Azerbaijani | 57 | 0.982 | 0.930 | 0.955 | |
ga Irish | 50 | 0.913 | 0.840 | 0.875 | |
mt Maltese | 48 | 0.872 | 0.854 | 0.863 | not in base model pretraining |
mt Maltese: F1 0.863 (absent from XLM-R pretraining, which is a base-model limit)ga Irish: F1 0.875az Azerbaijani: F1 0.955sigmoid_at_threshold. A quantised model that answers differently is a different detector, so this is measured rather than assumed.nsfw detector scored 0.000 in Maltese, was blamed on the base model, and went to 1.000 with perfect precision and recall when its corpus went from 2 positives per language to 10. Nothing about the model changed. So where a language scores badly here, read the support column first.