Using the Moderations CLI¶
The dr-moderation CLI lets you manage guards and test moderation pipelines from the terminal — no Python code required.
インストール¶
End-user — the dr-moderation binary lands on your PATH automatically:
pip install 'datarobot-moderations[all]'
dr-moderation --help
備考
Python 3.10 – 3.12 is required.
Developer / contributor — Poetry places the binary inside .venv/bin/, which is not on your PATH until the venv is active. Pick one:
poetry shell # Option A: activate for the session
poetry run dr-moderation --help # Option B: one-off prefix
make cli ARGS="evaluate --help" # Option C: Makefile shortcut
認証¶
Commands that call the DataRobot API need credentials. Set them once per session:
export DATAROBOT_ENDPOINT="https://app.datarobot.com/api/v2"
export DATAROBOT_API_TOKEN="your-api-token"
Or pass them as global flags (flags take precedence over env vars):
dr-moderation --endpoint <url> --token <token> <command>
Commands¶
evaluate¶
Evaluate a prompt and/or response through the local ModerationPipeline. Supports every guard type including LLM Gateway (llm_type: llmGateway) — no deployment required.
The config file must use the Python SDK snake_case schema (see Moderations guardrails for the full field reference).
dr-moderation evaluate [OPTIONS]
| オプション | 必須 | デフォルト | 説明 |
|---|---|---|---|
--config-file FILE |
はい | — | Moderation config YAML (snake_case SDK format) |
--prompt TEXT |
No * | — | Prompt text; evaluated against prescore guards |
--response TEXT |
No * | — | Response text; evaluated against postscore guards. Also pass --prompt for guards that need both (e.g. faithfulness, task_adherence) |
--as-json |
いいえ | false | Emit results as JSON — useful for scripting |
* At least one of --prompt or --response is required.
Example output (human-readable):
── Prescore (prompt) ──────────────────────────────
Blocked : False
Metrics :
Prompts_token_count: 4
Latency : 0.05s
Evaluate-examples¶
Token-count guard on a prompt:
dr-moderation evaluate \
--config-file docs/examples/token_count_config.yaml \
--prompt "Hello, world!"
LLM Gateway task-adherence guard:
dr-moderation evaluate \
--config-file docs/examples/llm_gateway_config.yaml \
--prompt "What is DataRobot?" \
--response "DataRobot is an AI platform."
Evaluate both, emit JSON, pipe to jq:
dr-moderation evaluate \
--config-file docs/examples/llm_gateway_config.yaml \
--prompt "What is DataRobot?" \
--response "DataRobot is an AI platform." \
--as-json | jq '.postscore.metrics'
Ready-made configs in docs/examples/:
token_count_config.yaml— Prompt + response token-count guardsllm_gateway_config.yaml— token-count prompt guard + LLM Gatewaytask_adherence
add-guard¶
Add guards to an existing DataRobot custom model. Creates a new custom model version with the guards attached and prints the version ID to stdout.
How it works:
- You create and register a custom model (your LLM) in DataRobot — this gives you a
customModelId. - You define guards in a camelCase YAML file.
add-guardPOSTs the config to/guardConfigurations/toNewCustomModelVersion/. DataRobot creates a new version of the model with the guards and returns thecustomModelVersionId.- Deploy that new version — it will now enforce your guards on every prompt/response.
dr-moderation add-guard [OPTIONS]
| オプション | 必須 | デフォルト | 説明 |
|---|---|---|---|
--custom-model-id TEXT |
はい | — | ID of the custom model (find it in the DataRobot UI under Model Workshop → Custom Models) |
--config-file FILE |
はい | — | YAML list of guard configurations (camelCase API format) |
--timeout-sec INTEGER |
いいえ | 60 | Per-guard timeout in seconds |
--timeout-action [score\ | block] |
いいえ | スコア | Action on timeout: score passes through; block rejects |
Example output:
6797abc123def456789abcde
The printed ID is the new customModelVersionId — pass it to subsequent API or SDK calls to deploy the version.
例:
# Add guards, capture the new version ID
VERSION_ID=$(dr-moderation add-guard \
--custom-model-id 6793e6b2114f17240fa2194c \
--config-file docs/examples/add_guard_config.yaml)
echo "New version: ${VERSION_ID}"
# Block if any guard exceeds 30 s
dr-moderation add-guard \
--custom-model-id 6793e6b2114f17240fa2194c \
--config-file docs/examples/add_guard_config.yaml \
--timeout-sec 30 \
--timeout-action block
agent a2a connect¶
Verify connectivity to a remote A2A agent by fetching its agent card from /.well-known/agent.json.
dr-moderation agent a2a connect [OPTIONS]
| オプション | 必須 | 説明 |
|---|---|---|
--url TEXT |
はい | Base URL of the remote A2A agent |
--deployment-id TEXT |
いいえ | DataRobot deployment ID to verify alongside the agent |
例:
# 1. Start a one-line A2A mock (serves /.well-known/agent.json on port 8765)
python3 - << 'EOF'
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
CARD = {"name": "My Agent", "version": "1.0.0", "capabilities": ["moderation"]}
class H(BaseHTTPRequestHandler):
def do_GET(self):
body = json.dumps(CARD).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(body)
def log_message(self, *_): pass
HTTPServer(("localhost", 8765), H).serve_forever()
EOF &
# 2. Connect to it
dr-moderation agent a2a connect --url http://localhost:8765
Production examples:
# Verify a remote A2A agent is reachable
dr-moderation agent a2a connect --url https://my-agent.example.com
# Also verify the backing DataRobot deployment
dr-moderation agent a2a connect \
--url https://my-agent.example.com \
--deployment-id 6793e6b2114f17240fa2194c
serve¶
Start a JSON-RPC 2.0 server so that non-Python applications (Java, Go, C#, …) can evaluate prompts and responses through the full moderation pipeline without HTTP/REST overhead or a Python runtime in their own process.
Two transports are available:
| Transport | 仕組み | 最適な用途 |
|---|---|---|
stdio (default) |
Caller spawns dr-moderation serve as a subprocess; newline-delimited JSON on stdin/stdout |
Single-caller, zero network setup |
ws |
aiohttp WebSocket server; multiple callers share one long-running instance | Containerised / multi-caller deployments |
dr-moderation serve [OPTIONS]
| オプション | 必須 | デフォルト | 説明 |
|---|---|---|---|
--transport [stdio\ | ws] |
いいえ | stdio |
Transport backend |
--config-file FILE |
いいえ | — | Pre-load a pipeline YAML at startup. For ws this pipeline is shared across all connections; for stdio the caller can still send initialize to override it |
--host TEXT |
いいえ | 127.0.0.1 |
Bind address (ws only) |
--port INTEGER |
いいえ | 9000 |
Bind port (ws only) |
--log-level [debug\ | info\ | warning\ | error] |
いいえ | warning |
Logging verbosity — all output goes to stderr, never stdout |
All diagnostic output goes to stderr. The stdout stream carries only JSON-RPC messages so callers can parse it without noise.
Wire format¶
Messages are newline-delimited JSON (one complete JSON object per line, \n-terminated). Both requests and responses follow JSON-RPC 2.0.
Request (caller → server):
{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"config_path": "/path/to/config.yaml"}}
Response (server → caller):
{"jsonrpc": "2.0", "id": 1, "result": {"ok": true}}
方法¶
| 方法 | Call order | params keys |
説明 |
|---|---|---|---|
initialize |
Before evaluate_* |
config_path (string, required) |
Load the moderation pipeline from a YAML file. Must be called before any evaluate_* method unless --config-file was passed at startup. Returns {"ok": true} |
evaluate_prompt |
After initialize |
prompt (string, required) |
Run prescore guards and return an EvaluationResult |
evaluate_response |
After initialize |
response (string, required); prompt (string, optional); pipeline_interactions (string, optional) |
Run postscore guards and return an EvaluationResult |
shutdown |
Any time | (なし) | Signal the server to stop and return {"ok": true}. stdio: server exits after sending the response. ws: closes the current connection; the server process keeps running |
Complete response example (evaluate_prompt)¶
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"blocked": false,
"blocked_message": null,
"replaced": false,
"replacement": null,
"metrics": {
"Prompts_token_count": 4
},
"latency_sec": 0.012345
}
}
When a guard blocks content blocked is true, blocked_message holds the guard's configured message, and latency_sec is always present. When a replace-action guard fires, replaced is true and replacement holds the sanitized text.
例¶
Bash (stdio — interactive test):
# Pre-load a config, then evaluate a prompt
dr-moderation serve --config-file moderation_config.yaml --transport stdio <<'EOF'
{"jsonrpc":"2.0","id":1,"method":"evaluate_prompt","params":{"prompt":"Hello, world!"}}
{"jsonrpc":"2.0","id":2,"method":"shutdown","params":{}}
EOF
Python (subprocess, stdio):
import json
import subprocess
import sys
proc = subprocess.Popen(
[sys.executable, "-m", "datarobot_dome.cli", "serve",
"--transport", "stdio",
"--config-file", "moderation_config.yaml"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, # discard diagnostics; redirect to sys.stderr to surface them
text=True,
bufsize=1,
)
def rpc(method, params, *, req_id):
msg = json.dumps({"jsonrpc": "2.0", "id": req_id, "method": method, "params": params})
proc.stdin.write(msg + "\n")
proc.stdin.flush()
# Skip any stdout lines that are not valid JSON (startup messages, warnings).
while True:
line = proc.stdout.readline()
try:
return json.loads(line)
except json.JSONDecodeError:
continue
result = rpc("evaluate_prompt", {"prompt": "Hello, world!"}, req_id=1)
print(result["result"])
rpc("shutdown", {}, req_id=2)
proc.wait()
Go (stdio):
package main
import (
"bufio"
"encoding/json"
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("dr-moderation", "serve",
"--transport", "stdio",
"--config-file", "moderation_config.yaml")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
_ = cmd.Start()
scanner := bufio.NewScanner(stdout)
send := func(req any) {
b, _ := json.Marshal(req)
fmt.Fprintln(stdin, string(b))
}
recv := func() map[string]any {
// Skip non-JSON lines (startup messages, log output on stdout)
for scanner.Scan() {
var m map[string]any
if err := json.Unmarshal(scanner.Bytes(), &m); err == nil {
return m
}
}
return nil
}
send(map[string]any{"jsonrpc": "2.0", "id": 1, "method": "evaluate_prompt",
"params": map[string]any{"prompt": "Hello, world!"}})
resp := recv()
fmt.Println(resp["result"])
send(map[string]any{"jsonrpc": "2.0", "id": 2, "method": "shutdown", "params": map[string]any{}})
cmd.Wait()
}
Java (stdio):
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.*;
import java.util.Map;
public class ModerationClient {
public static void main(String[] args) throws Exception {
ProcessBuilder pb = new ProcessBuilder(
"dr-moderation", "serve",
"--transport", "stdio",
"--config-file", "moderation_config.yaml");
pb.redirectError(ProcessBuilder.Redirect.DISCARD);
Process proc = pb.start();
ObjectMapper mapper = new ObjectMapper();
var writer = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(proc.getOutputStream())), true);
var reader = new BufferedReader(
new InputStreamReader(proc.getInputStream()));
// Send request
String req = mapper.writeValueAsString(Map.of(
"jsonrpc", "2.0", "id", 1,
"method", "evaluate_prompt",
"params", Map.of("prompt", "Hello, world!")));
writer.println(req);
// Read response — skip non-JSON lines
String line;
while ((line = reader.readLine()) != null) {
try {
var resp = mapper.readValue(line, Map.class);
System.out.println(resp.get("result"));
break;
} catch (Exception ignored) {}
}
writer.println(mapper.writeValueAsString(Map.of(
"jsonrpc", "2.0", "id", 2, "method", "shutdown", "params", Map.of())));
proc.waitFor();
}
}
C# (stdio):
using System.Diagnostics;
using System.Text.Json;
var proc = new Process {
StartInfo = new ProcessStartInfo("dr-moderation") {
Arguments = "serve --transport stdio --config-file moderation_config.yaml",
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
}
};
proc.Start();
_ = proc.StandardError.ReadToEndAsync(); // drain stderr on a background task
void Send(object req) => proc.StandardInput.WriteLine(JsonSerializer.Serialize(req));
JsonElement Recv() {
// Skip non-JSON lines (startup messages, warnings)
while (true) {
var line = proc.StandardOutput.ReadLine() ?? throw new EndOfStreamException();
try { return JsonDocument.Parse(line).RootElement; } catch { }
}
}
Send(new { jsonrpc = "2.0", id = 1, method = "evaluate_prompt",
@params = new { prompt = "Hello, world!" } });
var resp = Recv();
Console.WriteLine(resp.GetProperty("result"));
Send(new { jsonrpc = "2.0", id = 2, method = "shutdown", @params = new { } });
proc.WaitForExit();
WebSocket (ws transport):
# Start the server (runs until killed)
dr-moderation serve --transport ws --host 127.0.0.1 --port 9000 \
--config-file moderation_config.yaml
# In another terminal — connect with any WebSocket client (e.g. websocat)
echo '{"jsonrpc":"2.0","id":1,"method":"evaluate_prompt","params":{"prompt":"Hello"}}' \
| websocat ws://127.0.0.1:9000
YAML schema quick reference¶
The two commands use different schemas — they are not interchangeable:
| Command | 形式 | Key fields |
|---|---|---|
add-guard |
DataRobot API — camelCase | ootbType, stages (list), intervention |
evaluate |
Python SDK — snake_case | ootb_type, stage (string or list), llm_type, llm_gateway_model_id |
add-guard config (camelCase)¶
Sent directly to /guardConfigurations/toNewCustomModelVersion/. The file must be a YAML list.
- name: Prompt Token Count
type: ootb
ootbType: token_count
stages: [prompt]
intervention:
action: report
allowedActions: [report, block]
message: " "
sendNotification: false
conditions: []
| フィールド | 必須 | 備考 |
|---|---|---|
name |
はい | Unique per config |
type |
はい | ootb · guardModel · userModel · nemo |
stages |
はい | List: [prompt], [response], or [prompt, response] |
ootbType |
When type: ootb |
token_count, faithfulness, rouge_1, etc. |
modelInfo |
When type: guardModel |
inputColumnName, outputColumnName, targetType, classNames |
intervention |
いいえ | action, conditions, message; omit to measure only |
evaluate config (snake_case)¶
Consumed by ModerationPipeline.from_yaml. For the full field reference see Moderations guardrails.
The key difference from add-guard: use llm_type: llmGateway with llm_gateway_model_id — no deployment_id needed:
- name: Task Adherence
type: ootb
ootb_type: task_adherence
stage: response
llm_type: llmGateway
llm_gateway_model_id: "azure/gpt-4o-2024-11-20"
intervention:
action: block
message: "Response does not address the task."
conditions:
- comparator: lessThan
comparand: 0.5
Exit codes¶
| コード | Meaning |
|---|---|
0 |
成功 |
1 |
Runtime error (API error, bad YAML, connection refused) |
2 |
Invalid CLI usage (missing required option, unknown value) |
Non-zero exits write a descriptive message to stderr.