CoPE-B-S1 — A Decision Model for Safety

CoPE-B-S1 is a policy-steerable decision model: you hand it a piece of state (text content) and one or more typed questions (policies), and it returns a calibrated decision for each in a single forward pass.

CoPE-B-S1 is purpose-built to be both accurate and fast. Furthermore, it is natively compatible with the System One spec, making it a drop-in replacement for anyone using Jev or other decision models.

While this model can serve as a general decision engine, it is tuned and battle-tested for safety, moderation, and LLM-guardrail use cases. The CoPE family of models have a long production pedigree as the real-time safety layer deployed by major platforms such as Runway ML and some of the largest websites in the world.

See the paper for technical details: "CoPE: A Small Language Model for Steerable and Scalable Content Labeling" (arXiv:2512.18027).


What's new vs. prior CoPE-B models

CoPE-B-S1 keeps CoPE's accuracy, speed, and steerability and adds three capabilities:

  1. Multi-label in a single pass, at near-constant inference cost. Ask up to 26 typed questions about the same state/content and the model answers them all from one prefill — the per-decision marginal cost is a cheap pooled read, not any extra output tokens. Controlling for prompt length, latency is ~flat in the number of questions.
  2. System One / Jev-native prompt format. The model is prompted with the structured System One JSON format — typed questions (noul boolean, choice, score) over a state — and read via pooled answer slots. This makes it a drop-in replacement for Jev or any other System One decision model.
  3. Improved, calibrated confidence. The returned probabilities are better-calibrated out of the box, with an optional per-question-type temperature for tightening ECE further. For the most meaningful confidence, we recommend calibrating according to your own dataset's distribution.
CoPE-B-A4B CoPE-B-S1
labels per call 1 (binary) up to 26, one pass
question types boolean noul / choice / score
prompt format free-text template System One JSON
read mechanism single-token generate pooled prefill (no generate)
calibrated confidence not by default yes (general default)

Model overview

  • Context: up to 256K; serve at the context you need for multi-question bundles.
  • Modality: text only (this version cannot process images).
  • Output: a decision per question; pooled read means zero output_tokens.
  • Base: google/gemma-4-26B-A4B-it (Mixture-of-Experts; ~3.8B active / 25.2B total).
  • License: Apache-2.0.

Prompting the model

CoPE-B-S1 natively implements the System One decision spec, taking in a JSON object with a state (the thing being judged) and a questions map. Each question has a type — noul (a true/false read), choice (pick one of N options), or score (an ordinal level) — so you can ask several different kinds of decision about the same state in one request. Because it follows the spec exactly, it can be swapped in for Jev or any other System One decision model.

The state is an arbitrary JSON object that you define — it is rendered into the prompt verbatim. There is no required schema: use {"content": "..."} for a single blob of text (as in the examples below), or any keys and nested structure that fit your data, e.g. {"prompt": "...", "response": "..."} to judge an LLM turn, or {"title": "...", "body": "...", "author": {...}} for a richer record. Your criteria should simply refer to whatever fields you supply.

Input format example

Here is a sample request that triages a customer-support message with all three question types at once:

{
  "questions": {
    "q1": { "type": "noul",
            "instructions": "Does the message report a problem the customer wants fixed?",
            "criteria": { "true":  "Reports a bug, defect, error, or broken behavior.",
                          "false": "Does not report a problem (praise, a question, or a feature request)." } },
    "q2": { "type": "choice",
            "instructions": "Which team should handle this message?",
            "criteria": { "billing":   "Payments, refunds, invoices, or subscription charges.",
                          "technical": "Bugs, errors, outages, or how-to problems.",
                          "account":   "Login, password, profile, or access issues." } },
    "q3": { "type": "score",
            "instructions": "How urgent is this message?",
            "criteria": [ "Not urgent — general feedback or a question.",
                          "Normal — a problem, but the customer can still work.",
                          "High — the customer is blocked.",
                          "Critical — data loss, outage, or a security issue." ] }
  },
  "state": { "content": "I was charged twice for my subscription this month and the refund page just shows an error. Please help!" },
  "answers": { "q1": {"noul": "-"}, "q2": {"choice": "-"}, "q3": {"score": "-"} }
}

Notice the answers map is fed in as part of the input prompt along with placeholder tokens so they can be properly prefilled at inference time.

Output calculation procedure

Important: CoPE-B-S1 does not generate any output. Instead, it reads each answer's probability directly from its prefilled slot, so every question is answered in the same forward pass. To use this capability:

  1. Render the request as a single prompt string, with the answers map included and each answer slot filled with a placeholder token. Concretely, the prompt should be a System One JSON string (questions-first, then state, then the answers map).
  2. Run a single forward pass (prefill only — max_new_tokens=0). Nothing is decoded.
  3. Read the decision from each answer slot's logits. At the position that predicts each slot's placeholder, take the model's next-token logits restricted to that question's candidate tokens and normalize:
    • noul → candidates " true" / " false" → P(true) = softmax([logit_false, logit_true])[1]
    • choice → candidate option letters (e.g., " A", " B", …) → argmax + per-option probs
    • score → candidate level tokens → expected value over levels

The "result" is something you calculate from the slot logits, such as:

q1 (noul)    →  P(true) = 0.97                              # yes — reports a problem
q2 (choice)  →  "billing"   (billing 0.88, technical 0.10, account 0.02)
q3 (score)   →  2           (expected value 2.1 over levels 0–3)   # customer is blocked

The answers JSON that our custom SGlang server returns at /v1/systemone (see "Option A" below) is simply this per-slot read packaged back into the request's keys for convenience.

Content-policy evaluation

The example above shows the model's full generality. But one common use is content labeling against a policy: given a piece of content and a policy, decide whether the content merits a label according to that policy. For this, use a noul question type and instantiate it according to the following convention:

Field Value
instructions "Does the content meet the criteria described in the following policy?\n\n" + your policy text
criteria.true "The content meets the policy's criteria."
criteria.false "The content does not meet the policy's criteria."
state.content the content being judged

So a single-policy evaluation request looks like this:

{
  "questions": {
    "q1": {
      "type": "noul",
      "instructions": "Does the content meet the criteria described in the following policy?\n\nNo coordination of illegal drug sales. This includes offering to sell, buy, or arrange the exchange of controlled substances, as well as sharing contact details or meeting logistics for such a transaction.",
      "criteria": {
        "true":  "The content meets the policy's criteria.",
        "false": "The content does not meet the policy's criteria."
      }
    }
  },
  "state": { "content": "Meet me behind the gym at 9, bring cash and I'll bring the stuff." },
  "answers": { "q1": {"noul": "-"} }
}

Read P(true) from the q1 slot (as above): true = the content merits a label, false = it does not. We highly recommend thresholding P(true) according to your use case (e.g., hard actions may need high thresholds and soft actions can be lower).

Multi-policy (one content, many policies). To screen one piece of content against an entire policy set at once, add one noul question per policy (q1, q2, …), each with the same fixed wrapper and its own policy text:

"questions": {
  "q1": { "type": "noul", "instructions": "Does the content meet ...?\n\n<drug-sales policy>",  "criteria": { "true": "The content meets the policy's criteria.", "false": "The content does not meet the policy's criteria." } },
  "q2": { "type": "noul", "instructions": "Does the content meet ...?\n\n<violence policy>",  "criteria": { "true": "...", "false": "..." } },
  "q3": { "type": "noul", "instructions": "Does the content meet ...?\n\n<harassment policy>",  "criteria": { "true": "...", "false": "..." } }
}

Pro Tip: For high-throughput labeling of a large volume of content against a fixed policy set, be sure to put questions (policies) before state (content) so the policy prefix is cached across contents.


Running the model

Because the read is pooled (we read a decision from an interior slot's prefill, not by generating tokens), inference is not a standard LLM generate() call.

Why a custom inference path? vLLM and SGLang's built-in "decision model" / structured-output / guided- decoding features wrap a standard LLM in a Decisions API, which generates an answer per prompted question. This means inference time scales linearly with each question/label. We instead take a pooled-read approach which is what lets us extract many decisions in essentially constant time (i.e., no per-decision decode).

You have several options for easily taking advantage of this mode of inference:

Option A — Custom SGLang engine (highest perf)

For self-hosting, the turnkey path is to use our containerized decision server. It is a prebuilt SGLang engine that implements /v1/systemone with RadixAttention prefix caching + continuous batching for high performance.

docker run --gpus all -p 80:80 \
  -e MODEL=zentropi-ai/cope-b-s1 \
  zentropiai/cope-s1-sglang

curl -s http://localhost:80/v1/systemone -H "Content-Type: application/json" \
  -d '{"questions":{"q1":{"type":"noul","instructions":"Is this funny?"}},"state":{"content":"Never trust atoms. They make up everything."}}'

Pass in your request according to the /v1/systemone spec. In contrast to direct prompting, note you do not need to provide an answers parameter to your API request as the server will construct one for you automatically.

The server response is also System One compliant: an answers map keyed by your question ids (each tagged with its type), plus usage. The request above might produce:

{
  "model": "cope-b-s1",
  "answers": {
    "q1": { "type": "noul", "noul": 0.93 }
  },
  "usage": { "input_tokens": 41, "output_tokens": 0 }
}

Each answer's shape depends on its type:

  • noul → noul: the probability the content meets the criteria (P(true)).
  • choice → choice: the selected option key, plus confidence (its probability) and the full probabilities distribution over options.
  • score → score: the expected value over the ordinal levels (a float), plus confidence, the per-level probabilities, and a legend mapping level keys to labels.

So the multi-type support-triage request earlier comes back as:

{
  "model": "cope-b-s1",
  "answers": {
    "q1": { "type": "noul", "noul": 0.997 },
    "q2": { "type": "choice", "choice": "billing", "confidence": 0.608,
            "probabilities": { "billing": 0.608, "technical": 0.269, "account": 0.123 } },
    "q3": { "type": "score", "score": 2.14, "confidence": 0.735,
            "probabilities": { "0": 0.002, "1": 0.059, "2": 0.735, "3": 0.205 },
            "legend": { "0": "Not urgent — general feedback or a question.",
                        "1": "Normal — a problem, but the customer can still work.",
                        "2": "High — the customer is blocked.",
                        "3": "Critical — data loss, outage, or a security issue." } }
  },
  "usage": { "input_tokens": 234, "output_tokens": 0 }
}

To run the model effectively, we recommend a minimum 80 GB GPU (e.g., A100).

Option B — Zentropi API (simplest for safety teams)

A Zentropi-hosted endpoint (free tier available) is also open to the public. It uses Zentropi's /v1/label spec that's specialized for content labeling so the parameters are simpler than the System One spec. Just set model=cope-b-s1.

The API integrates with Zentropi's policy hub for easy discovery and creation of policies. To get up and running the most quickly, install the Zentropi Agent Skill which allows your agent to drive Zentropi directly.


Usage modes: accuracy vs. throughput

You can run CoPE-B-S1 in two modes, either optimizing for accuracy or for throughput:

  • Max-accuracy mode — one decision per request. Ask a single question per forward pass. This is the most accurate setting and the right default for high-stakes or borderline decisions.
  • Max-throughput mode — many decisions per request. Bundle up to 26 questions into one prefill and read all their slots at once. The per-decision cost collapses to a cheap pooled read (latency ~flat in the number of questions, controlling for prompt length).

Two tips to get the best of both:

  • Bundle conceptually-separate decisions. Multi-decision accuracy is highest when the questions are about distinct concepts (e.g., hate + drugs + self-harm bundle cleanly). Bundling several overlapping shades of the same concept (e.g., two subtly different "violence" policies) can incur a small accuracy cost, so split those across separate requests when accuracy is paramount.
  • Screen broadly, then confirm. A good production pattern is to screen many policies at once in max-throughput mode, then re-run any low-confidence or mission-critical hits as single-question (max-accuracy) requests for a sharper read. You pay the single-decision cost only on the small fraction of cases that need it.

Calibration temperature (optional)

Returned probabilities may not be closely calibrated to your dataset out of the box. For best results, an optional per-question-type temperature rescales them (softmax(log p / T)).

Here are some recommended starting points that were fitted on our own held-out data, but you should re-fit on your own traffic: noul ≈ 1.3, choice ≈ 1.0, score ≈ 1.0.

Leave then all unset (i.e., empty or 1.0) to get raw probabilities. Because the rescale is monotonic, these affect confidence only — never which decision is returned.

If you are using the cope-s1-sglang engine, pass one per question type as environment variables when you launch the container (they default to 1.0, i.e. raw probabilities). The server applies them before returning, so downstream thresholds see calibrated values with no client-side changes:

docker run --gpus all -p 80:80 \
  -e MODEL=zentropi-ai/cope-b-s1 \
  -e TEMP_NOUL=1.3 \
  -e TEMP_CHOICE=1.0 \
  -e TEMP_SCORE=1.0 \
  zentropiai/cope-s1-sglang

Equivalently, you may use CLI flags --temp-noul / --temp-choice / --temp-score. Each defaults to 1.0 = off.


Evaluation

Steerability

The thing CoPE is built for is following your criteria, not a fixed taxonomy. We measure this on Zentropi's Policy Steerability Benchmark, which quantifies if a model correctly labels content when evaluated based on pairs of contrastive policies.

CoPE-B-S1 scores 0.766 — ahead of every other open model we’ve tested, including all prior versions of CoPE. It is also the only top model that is also small and self-hostable.

Model Steerability Self-hostable Small (< 10B active)
gpt-5.4 (no reasoning) 0.777
CoPE-B-S1 (this model) 0.766 ✓ ✓
gpt-5.4 (default reasoning) 0.756
gpt-oss-120b 0.724 ✓
CoPE-B-A4B-mm (prior CoPE) 0.691 ✓ ✓
gemma-4-31b-it 0.656 ✓
CoPE-B-A4B (prior CoPE) 0.634 ✓ ✓
Jev-1.14 0.622 ?
claude-opus-4-6 0.516
cope-a-9b (prior CoPE) 0.435 ✓ ✓
llama-guard-4-12b 0.045 ✓ ✓

Generalization

CoPE-B-S1 generalizes beyond Zentropi's own policy data. Here are its scores on a few categories of DecisionIndex— third-party benchmarks whose data the model has never seen during training. Jev is provided for reference and was evaluated using byte-identical system one API calls.

Benchmark (metric) CoPE-B-S1 Jev-1.14 Δ
PhishNChips — phishing detection (acc) 0.938 0.638 +0.30
FinEntity — financial sentiment (macro-F1) 0.900 0.850 +0.05
MuSR — multistep reasoning (acc) 0.690 0.687 +0.00
VAST — stance detection (macro-F1) 0.610 0.617 −0.01
RAGTruth — hallucination detection (F1) 0.756 0.778 −0.02

Broadly speaking, we see CoPE-B-S1 particularly excel on decision tasks that are either safety-focused (like phishing detection) or safety-adjacent.


Intended uses

Primary Use Cases

  1. LLM Guardrails

    • Input prompt risk assessment and output answer risk assessment
    • Screening a single turn against your entire policy set in one call, at near-constant cost
    • NB: not yet optimized for agentic / multi-turn tool-use patterns
  2. Content Labeling

    • Real-time content moderation and batch processing at scale
    • Policy-driven classification against your own rules — many policies evaluated per item in a single pass
    • Severity and type assessment via score and choice questions alongside binary noul checks
  3. Scoring & Signals

    • Feature generation for social feed ranking
    • Language-model training-data filtering
    • Content quality assessment and measurement
  4. General structured decisions over text

    • Any general decision — safety is its sharpest edge, but the System One interface is domain-agnostic
    • Examples: Classification, NLI-style entailment, and extraction-as-choice via the typed-question interface
    • Try playing Tetris! :-)

See also these case studies for how other organizations use CoPE's classification capabilities to advance their work.

Discouraged Uses

While the Apache 2.0 license permits broad use, the following fall outside the intended scope and may produce poor or unsafe results:

  • Surveillance applications
  • Zero-shot use without human review for high-stakes decisions
  • Use cases beyond the stated technical limitations (see below)
  • Decisions requiring external knowledge not supplied in the state (e.g., fact-checking against world knowledge)

Training methodology

CoPE-B-S1 inherits CoPE's two core techniques (see paper for detail):

  • Contradictory-example training: identical content is labeled differently under different policies, forcing the model to interpret the policy rather than memorize content patterns — this is what makes it steerable.
  • Binocular labeling: policies are refined via dual LLM labeling until independent readers agree, yielding deterministic policies suitable for contradictory training.

On top of that, CoPE-B-S1 is produced by an augmented training procedure relative to earlier versions of CoPE:

  1. Enhanced data coverage — higher data quality across additional safety domains.
  2. Multi-label adaptation — K-question prefills with minimized slot interdependence.
  3. Confidence-based calibration — additional self-distillation to tune probabilities.

Data integrity: As with all CoPE models, training and evaluation sets are disjoint on both content and policy so scores measure decision generalization, not memorization.


Limitations

  • ≤ 26 questions per read. Questions are mapped internally to single letters A–Z, so a single prompt supports at most 26 decisions.
  • Text only. No image/audio input (in this version).
  • Calibration is distribution-specific. The recommended temperatures are tuned for safety-oriented traffic; re-fit on a labeled sample of your own traffic if it looks different.
  • Knowledge constraints: Cannot make classifications requiring external knowledge (e.g., misinformation) unless explicitly enumerated in state.
  • Pooled inference requires the custom read path or turnkey server — it is not a drop-in generate() LLM call.

Ethical considerations

Due to the model's high steerability, decisions generally reflect the criteria you supply. Please monitor for demographic bias, keep a human in the loop for consequential actions, and audit regularly. Criteria wording materially affects outcomes, so treat criteria authoring as part of your overall workflow. Zentropi provides tools to help author high quality criteria.


Migrating from CoPE-B-A4B

  1. New prompt format: switch from the free-text INSTRUCTIONS/POLICY/CONTENT/ANSWER template to the /v1/systemone JSON with typed questions.
  2. New inference path: the answer is a pooled read, not a generated token — use the provided server (Option A) for best results, not vllm serve ... generate.
  3. Batch your policies: what used to be N separate binary calls can now be one multi-label request. Note: Total inference time will still be a function of overall prompt length.
  4. Recalibrate thresholds: as with any new model, confidence thresholds must be recalibrated.

Community and Support

For any technical questions or comments, please join our HuggingFace community forum or the Roost model community. You can share your feedback, suggest new areas, or pick our brains about anything. If you'd prefer a more private discussion, you can also email us at info@zentropi.ai.


About the Developer

CoPE is developed and maintained by Zentropi, a public benefit company focused on making content classification simple and powerful. Our tools advance the state of the art in online safety technology.


Citation

@article{cope2025,
  title={CoPE: A Small Language Model for Steerable and Scalable Content Labeling},
  year={2025},
  journal={arXiv preprint arXiv:2512.18027}
}
Downloads last month
79
Safetensors
Model size
25B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for zentropi-ai/cope-b-s1

Finetuned
(219)
this model

Collection including zentropi-ai/cope-b-s1

Paper for zentropi-ai/cope-b-s1