Skip to main content

Output Controls

Human Oversight

human-oversight holds a model response for a person to review. The caller receives a queued escalation instead of the answer, and a reviewer records a decision in Inbox.

The control escalates every response that reaches it. Approval in the Inbox does not resume or replay the original request. Set up the review workflow before production traffic uses this control. With gateway 0.1.26 or later, a streamed request also escalates. The escalation occurs at the first output text, and the client gets no output text.

Configuration​

Because the control escalates everything it receives, decide which requests reach it with routes. A when condition on the chain entry has no effect on this control.

This example sends a request for review only when it has the header x-review-required: true. Apply it to a test gateway with the config-first workflow.

policy-config.yaml
pack:
  name: manual-review-route
  version: 1.0.0
  enabled: true
policies:
  chain:
    - audit-logger
    - human-oversight
policy:
  audit-logger: {}
  human-oversight:
    action: escalate
routes:
  - name: review-required
    path: /v1
    headers:
      x-review-required: "true"
    priority: 10
    chain:
      - audit-logger
      - human-oversight
  - name: no-review
    path: /v1
    priority: 0
    chain:
      - audit-logger

The global chain must list human-oversight, or lint fails. The catch-all no-review route then leaves it out for requests without the header.

The caller controls this header, which is fine for a demonstration. For review that must always happen, use a route condition that the caller cannot remove, such as a path or a consumer group.

Send a request for review​

Use the base URL and application token from the quickstart, and a model that your gateway serves:

curl --fail-with-body "$VERDICTAN_BASE_URL/chat/completions" \
  -H "Authorization: Bearer $VERDICTAN_CLIENT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "x-review-required: true" \
  --data '{"model":"gpt-4o-mini","stream":false,"messages":[{"role":"user","content":"Draft a short greeting."}]}'

The response is HTTP 200 with choices[0].message.content: null and verdictan.escalation.status: queued. Open Inbox and find the escalation with the same ID.

Send the request again without the header. This time, you get the model answer.

Fields​

FieldTypeDefaultDescription
actionstringSet it to escalate to turn the control on. Without it, the control does nothing. The loader rejects block.

The loader also rejects the removed fields require_human_for, confidence_threshold, default_assignee, and timeout_seconds. Use only action. The policy schema of gateway 0.1.26 or later accepts only action: escalate and has no default for it.

How it works​

  1. The gateway receives the provider response and runs the output controls of the active chain.
  2. If an earlier output control returned block or escalate, the stage stops before this control.
  3. With action: escalate, the control sets the verdict to escalate with reason code oversight.required.
  4. The gateway emits a decision event with policy_kind: human-oversight, verdict ESCALATE, and oversight.required.
  5. If the requested model has escalation routing, the gateway adds its team_id or user_id to the event.
  6. The caller gets HTTP 200 with choices[0].message.content: null, finish_reason: content_filter, and a verdictan.escalation object with the escalation ID and status queued. With gateway 0.1.26 or later, a buffered escalation of a Responses request gets a response object with status: incomplete, the content_filter reason, and no output. A Messages request gets a message with no content and the refusal stop reason.

Phase and verdicts​

ItemValue
Phaseoutput
Enforced pathChat Completions and Responses with stream: false. With gateway 0.1.26 or later, also streamed Chat Completions, Responses, and Messages.
Verdictsescalate
Reason codesoversight.required

The control never returns allow. Without action: escalate, it adds no result.

Limits and failure behavior​

  • The control has no threshold, category, or pattern. It escalates every response that reaches it.
  • Only the active chain decides whether it runs. Routes, consumer groups, and team or gateway targeting change the active chain. A when condition or a stage on the chain entry does not.
  • With stream: true and gateway 0.1.26 or later, the control escalates at the first output text. The stream stops with the error event of the route, with oversight.required, the verdict ESCALATE, and a verdictan.escalation object with status queued. See Output escalations in a stream.
  • A streamed escalation occurs at the first output text. Thus a later output control in the chain does not examine that output. With gateway 0.1.26 or later, the output result of this control has chain_stopped: true, and its not_evaluated list names the later output controls.
  • On escalation, the caller does not receive the model text. Only the decision event and the Inbox have it.
  • Reviewer assignment, categories, and timeouts are not part of this control. Configure them with escalation routing and your review workflow.
  • The control stores no evidence. Add audit-logger if the decision must show audit logging.

Next steps​