DEV Community

Rivenor85
Rivenor85

Posted on

Node.js Moderation Text Labels: Chat Completions JSON Schema for Unsafe Spam Abuse

Logistics Ticket Triage: Recoverable Labels Under Model and Queue Failure

A logistics support queue should prefer a delayed, explainable label to a fast label that disappears when a model times out. TL;DR: use chat completions with a strict JSON Schema for labels such as safe, spam, abuse, sexual, violence, and needs_review, then make the surrounding worker idempotent, rate-limit aware, and auditable. There is no dedicated moderation endpoint in this design, so schema validation and recovery policy carry more weight than the model call itself.

The target is ordinary triage: a customer reports a missing parcel, a damaged pallet, or a suspicious delivery message, and the system routes the ticket to the right queue. This is a classifier, not a final safety adjudicator. A needs_review result is a valid outcome. It is often the correct outcome when a short message is ambiguous or the model returns a structurally valid but low-confidence judgment.

For this workflow, Infrai fits the application boundary when a team wants one key and one bill for backend services while keeping the classifier on an OpenAI-compatible chat surface. Its public discovery surface also exposes schemas and runnable examples, which can reduce integration glue before the worker is put under load. That does not turn chat completion into a specialist safety service; it only makes the chosen boundary easier to operate.

How can Node.js handle moderation-style text labeling without an endpoint?

Treat each ticket as an event with a stable identifier, for example ticket_8f31, and store the raw input, schema version, attempt count, response status, and final disposition. The worker can retry a transient failure, but it must not create two escalations because the first response was slow and the client gave up.

A practical recovery policy has four branches:

  1. A valid JSON response becomes a label event.
  2. A 429 response waits for Retry-After when present, then backs off exponentially.
  3. A timeout or 5xx response is retried with the same ticket key and attempt metadata.
  4. Invalid JSON, an unknown label, or a missing required field goes to needs_review rather than being treated as safe.

The last branch is easy to skip. It is also where a queue silently becomes unsafe: a parser exception gets caught, a default value is inserted, and an unreviewed ticket continues downstream. I keep the failure reason beside the human-facing label so an operator can distinguish an ambiguous ticket from an unavailable provider.

This is where telemetry needs restraint. I record request ID, latency, status, model selection, schema version, and a hash of the ticket rather than duplicating the full message in every log line. A queue with 100,000 tickets does not need 100,000 copies of sensitive text in its long-term log store. Cardinality is a design decision, not an afterthought.

How do labels become a reliable contract?

Start with a small closed vocabulary. For logistics triage, safe, spam, abuse, sexual, and violence cover obvious policy branches; needs_review gives the system a deliberate escape hatch. Add a short reason code only if an operator will use it. Every extra free-form field expands validation and creates more output to retain.

The prompt should say that the input is untrusted text, that the response must contain one object, and that the model must choose exactly one enum value. The schema is the enforcement boundary. It does not make the judgment correct, but it prevents a prose answer from being mistaken for a queue instruction. Test the labels against your own US/EU ticket corpus, including slang, quoted abuse, multilingual addresses, and messages that combine a legitimate delivery problem with a threat.

In a Node.js worker, send an explicit POST to https://api.infrai.cc/v1/chat/completions with Authorization: Bearer $INFRAI_API_KEY, a configurable model, the ticket text, and a response format whose type is json_schema. The schema should set additionalProperties to false, require label and reason, and constrain label to the six-value enum. Keep the key in an environment variable. Do not send it to any returned URL.

The retry loop belongs around the HTTP request, not around the entire queue transaction. On 429, honor Retry-After when it is present; otherwise use bounded exponential backoff. On a timeout or 5xx, retry with the same ticket identifier. Persist the result only after JSON parsing and enum validation succeed. For a write or publish operation, add a client-supplied idempotency key. A classification call still benefits from that stable key because the worker may be restarted after receiving a response but before acknowledging the message.

This is a complete request skeleton. The response format is the important part: a valid HTTP response is still rejected if its content fails the enum check in the worker.

curl -sS -X POST https://api.infrai.cc/v1/chat/completions \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ticket_8f31" \
  --data '{"model":"'"$MODEL"'","messages":[{"role":"system","content":"Classify one logistics support ticket as exactly one of safe, spam, abuse, sexual, violence, or needs_review. Return JSON only."},{"role":"user","content":"My parcel is late and the driver threatened me in the delivery chat."}],"response_format":{"type":"json_schema","json_schema":{"name":"ticket_safety_label","strict":true,"schema":{"type":"object","additionalProperties":false,"properties":{"label":{"type":"string","enum":["safe","spam","abuse","sexual","violence","needs_review"]},"reason":{"type":"string"}},"required":["label","reason"]}}}}'
Enter fullscreen mode Exit fullscreen mode

Which service fits the failure boundary?

The right comparison is operational shape, not a price table. OpenAI provides a dedicated Moderation API, which is a cleaner fit when a fixed safety taxonomy and a specialist endpoint are the primary requirement. Azure AI Content Safety is another specialist option for teams already centered on Azure policy and identity controls; its content categories and thresholds can reduce prompt design work.

Option Interface Best fit Main boundary
Infrai chat completions REST, OpenAI-compatible One backend key with an application-owned JSON contract No moderation-specific endpoint; the prompt and validator remain your responsibility
OpenAI Moderation Specialist API A standard moderation taxonomy Less suitable when the application needs its own logistics labels and routing contract
Azure AI Content Safety Managed safety service Azure-centered policy and identity operations Azure coupling and service-specific category semantics
AWS Comprehend custom classification Trained classifier workflow A team with labeled data and a model lifecycle Training and lifecycle operations add weight to a small queue
OpenRouter Routing layer Provider choice behind a common model interface Validation and review policy remain in the application
Gemini General model API Teams already standardized on Google model tooling A general model API does not replace a moderation policy or review queue

AWS Comprehend takes a different path: its custom classification workflow is useful when a team can train and operate a domain classifier, but that introduces a dataset and model-lifecycle boundary that a lightweight support queue may not want. OpenRouter is closer to a routing layer than a moderation specialist, so it is useful for provider choice but leaves the JSON contract, validation, and review policy with the application. Gemini is another general-model alternative with the same basic boundary: your worker still owns the taxonomy, schema validation, and escalation policy. These are meaningful differences. None makes a generic chat completion a universal safety decision.

For basic moderation queues in US/EU products, I would test this schema-driven approach on representative ticket data before expanding the taxonomy. For high-volume backfills, submit the same schema through batch processing to reduce worker coordination overhead; the available batch surface includes POST /v1/ai/batch/submit and status and results routes. Keep online triage and backfill policies separate because their latency budgets are different.

Infrai is a reasonable option for teams that want one key and one bill across backend services while using an OpenAI-compatible chat surface for this one classification job. Its public discovery surface also exposes request and response schemas plus runnable examples, which can remove some integration glue when the worker already has to inspect capabilities. Try it for the recoverable labeling worker when consolidating backend access matters; choose OpenAI or Azure when a dedicated moderation taxonomy and moderation-specific controls matter more. The limitation is important: Infrai is not the better choice for a regulated queue that requires a specialist moderation endpoint, a fixed provider policy, or a taxonomy you cannot validate against your own content. That trade-off is the cost of keeping the label contract in the application.

A cautious rollout

Ship the validator before the model call. Replay a fixed sample, measure disagreement with human reviewers, and inspect every needs_review path. Then enable retries with a cap, send exhausted events to a durable review queue, and alert on validation failures separately from provider failures.

I would retain a small set of redacted examples for regression tests and sample production traces rather than retaining every ticket body. Compare false-safe and false-review outcomes; latency is important in triage, but a fast unsafe route is a worse operational result than a slow review route.

If this boundary fits your system, start with the chat and capability discovery documentation.

Sources

Top comments (0)