otari/types/moderation.rs
1//! Moderation types.
2//!
3//! These mirror the OpenAI `/v1/moderations` request/response shape and
4//! are used by the gateway provider's inherent `moderation` method.
5
6use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10/// Input to a moderation request.
11///
12/// Serialized as untagged JSON so the body matches the OpenAI-compatible
13/// shape: either a single string, an array of strings, or an array of
14/// multimodal content parts.
15#[derive(Debug, Clone, Serialize, Deserialize)]
16#[serde(untagged)]
17pub enum ModerationInput {
18 /// A single text string.
19 Text(String),
20 /// A batch of text strings.
21 Batch(Vec<String>),
22 /// Multimodal content parts (OpenAI `omni-moderation-*` only).
23 Parts(Vec<ModerationContentPart>),
24}
25
26impl Default for ModerationInput {
27 fn default() -> Self {
28 Self::Text(String::new())
29 }
30}
31
32/// A single multimodal content part for moderation input.
33#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(tag = "type", rename_all = "snake_case")]
35pub enum ModerationContentPart {
36 /// A text part.
37 Text {
38 /// The text content.
39 text: String,
40 },
41 /// An image referenced by URL.
42 ImageUrl {
43 /// The image URL descriptor.
44 image_url: ModerationImageUrl,
45 },
46}
47
48/// An image URL reference for a moderation content part.
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct ModerationImageUrl {
51 /// The URL of the image (HTTP(S) or `data:` URI).
52 pub url: String,
53}
54
55/// Parameters for a moderation request.
56///
57/// `include_raw` is intentionally not serialized into the HTTP body; it
58/// is emitted as the `?include_raw=true` query string by the gateway
59/// client when `true`.
60#[derive(Debug, Clone, Serialize, Deserialize, Default)]
61pub struct ModerationParams {
62 /// Namespaced model identifier (e.g. `openai:omni-moderation-latest`).
63 pub model: String,
64
65 /// The input to moderate.
66 pub input: ModerationInput,
67
68 /// Optional end-user identifier for abuse monitoring.
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub user: Option<String>,
71
72 /// When `true`, the gateway should echo the upstream raw response
73 /// alongside its normalized output. Serialized as a query parameter
74 /// (`?include_raw=true`), not in the body.
75 #[serde(skip_serializing)]
76 pub include_raw: bool,
77}
78
79impl ModerationParams {
80 /// Create a new moderation request for the given model and input.
81 pub fn new(model: impl Into<String>, input: ModerationInput) -> Self {
82 Self {
83 model: model.into(),
84 input,
85 user: None,
86 include_raw: false,
87 }
88 }
89
90 /// Attach an end-user identifier.
91 #[must_use]
92 pub fn with_user(mut self, user: impl Into<String>) -> Self {
93 self.user = Some(user.into());
94 self
95 }
96
97 /// Ask the gateway to include the upstream provider's raw response.
98 #[must_use]
99 pub fn with_include_raw(mut self, include_raw: bool) -> Self {
100 self.include_raw = include_raw;
101 self
102 }
103}
104
105/// A single moderation result.
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct ModerationResult {
108 /// Whether the input was flagged by any category.
109 pub flagged: bool,
110
111 /// Map of category name to whether it tripped.
112 #[serde(default)]
113 pub categories: HashMap<String, bool>,
114
115 /// Map of category name to model confidence score.
116 #[serde(default)]
117 pub category_scores: HashMap<String, f64>,
118
119 /// Which input modalities contributed to each category (OpenAI
120 /// omni-moderation only).
121 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub category_applied_input_types: Option<HashMap<String, Vec<String>>>,
123
124 /// The upstream provider's raw payload when `include_raw=true` was
125 /// requested.
126 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub provider_raw: Option<serde_json::Value>,
128}
129
130/// Top-level moderation response (OpenAI-compatible shape).
131#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct ModerationResponse {
133 /// Opaque response identifier.
134 pub id: String,
135 /// Echoed model identifier.
136 pub model: String,
137 /// One result per input item.
138 pub results: Vec<ModerationResult>,
139}