Self-hosted behavioral safety classifier for human–AI conversations. Reads signals across both sides of the dialogue — the shape of the conversation, not the surface of one turn — and returns one salience score per call with 12 axes of depth behind it.
Apache 2.0 · source ships inside the image at /app/.
What Ocular is → · Docs & API reference → · Commercial engagement
Download the Docker imageAPI at a glance
One HTTP endpoint: POST /classify. Single message or
full conversation; same response shape either way.
salience score in [0, 1] plus subject (self / other / unknown). Pick the cutoff that fits your action — reference
bands are watch ≥ 0.30, danger ≥ 0.60. This is
what your code keys off.level and a calibrated score, plus imminence. Surface in dashboards; explains the salience score, doesn't replace it.fiction and authenticity are already factored into
the salience score server-side — surfaced so you can see what shifted the call, not
for client-side filtering. Don't re-aggregate.Request
POST /classify
content-type: application/json
{
"messages": [
{"role": "user", "content": "I've been feeling hopeless for weeks and I don't know how much longer I can keep going."}
]
}Response
{
"salience": 0.3419,
"subject": "self",
"imminence": {"level": "minimal", "score": 0.0009},
"fiction": 0.0,
"authenticity": 0.9768,
"signals": {
"user": {
"suicide": {"level": "high", "score": 0.2692},
"self_harm": {"level": "minimal", "score": 0.0},
"harm_to_others": {"level": "minimal", "score": 0.0},
"abuse": {"level": "minimal", "score": 0.044},
"sexual_violence": {"level": "minimal", "score": 0.044},
"exploitation": {"level": "minimal", "score": 0.044},
"stalking": {"level": "minimal", "score": 0.044},
"self_neglect": {"level": "minimal", "score": 0.0}
},
"ai": {
"harm_provision": {"level": "minimal", "score": 0.0},
"emotional_failure": {"level": "minimal", "score": 0.0},
"manipulation": {"level": "minimal", "score": 0.0},
"safeguarding_failure": {"level": "minimal", "score": 0.0}
}
},
"meta": {"version": "1.0.0", "build": "c13d837", "inference_ms": 53}
}Full schema, fields, and salience semantics at the API reference.
Install + run
# After submitting the form below, you'll get the two tarballs:
zstd -d -c ocular-platform-oss-1.0.0.tar.zst | docker load
tar xzf customer-bundle-oss-1.0.0.tar.gz
cd ocular-oss-1.0.0
cp customer.env.example .env # then set OCULAR_VERSION=1.0.0 in .env
docker compose --env-file .env -f customer-compose.yml up -d
# Classify
curl http://localhost:8080/classify -H 'content-type: application/json' \
-d '{"messages":[{"role":"user","content":"..."}]}'Full walkthrough in deployment.md inside the bundle, mirrored at docs/deployment.md.
Requirements
Trajectory / throughput / tuning details at docs/deployment.md.
1.0.0verdict is replaced by a continuous salience score. The categorical verdict field is gone. The response now leads with salience — one score in [0, 1] you threshold
yourself (reference bands watch ≥ 0.30, danger ≥ 0.60). If you parsed verdict,
switch to salience.meta.version is the contract
version (1.0.0), meta.build the source
commit. A PATCH may shift scores on borderline traffic — pin the exact
tag for byte-stable scoring. See api-reference#versioning.signals.user / signals.ai, each with a level and calibrated score, plus imminence, fiction, and authenticity.USER_SUICIDE_HEAD_A, etc.) — opaque diagnostic
output, not part of the contract; do not key off them.Tell us who you are and the download links come back on the next screen. We share Ocular openly because we want to learn from how it's deployed — your context helps us calibrate, and we'll share back what we're seeing.
Ocular is a behavioral signal classifier — not predictive, not diagnostic, not a substitute for clinical judgment, human review, or emergency services. Operator sets thresholds and response policies. Coverage and calibration are still maturing; treat outputs as signal, not ground truth. See the model card for limits and intended use.