Skip to main content

openai_fork/
moderations.rs

1//! Given a input text, outputs if the model classifies it as violating OpenAI's content policy.
2
3use super::{openai_post, ApiResponseOrError};
4use derive_builder::Builder;
5use serde::{Deserialize, Serialize};
6
7#[derive(Deserialize, Clone, Debug)]
8pub struct Moderation {
9    pub id: String,
10    pub model: String,
11    pub results: Vec<ModerationResult>,
12}
13
14#[derive(Deserialize, Clone, Debug)]
15pub struct ModerationResult {
16    pub flagged: bool,
17    pub categories: Categories,
18    pub category_scores: CategoryScores,
19}
20
21#[derive(Deserialize, Clone, Copy, Debug)]
22pub struct Categories {
23    pub hate: bool,
24    #[serde(rename = "hate/threatening")]
25    pub hate_threatening: bool,
26    #[serde(rename = "self-harm")]
27    pub self_harm: bool,
28    pub sexual: bool,
29    #[serde(rename = "sexual/minors")]
30    pub sexual_minors: bool,
31    pub violence: bool,
32    #[serde(rename = "violence/graphic")]
33    pub violence_graphic: bool,
34}
35
36#[derive(Deserialize, Clone, Debug)]
37pub struct CategoryScores {
38    pub hate: f64,
39    #[serde(rename = "hate/threatening")]
40    pub hate_threatening: f64,
41    #[serde(rename = "self-harm")]
42    pub self_harm: f64,
43    pub sexual: f64,
44    #[serde(rename = "sexual/minors")]
45    pub sexual_minors: f64,
46    pub violence: f64,
47    #[serde(rename = "violence/graphic")]
48    pub violence_graphic: f64,
49}
50
51#[derive(Serialize, Builder, Debug, Clone)]
52#[builder(pattern = "owned")]
53#[builder(name = "ModerationBuilder")]
54#[builder(setter(strip_option, into))]
55pub struct ModerationRequest {
56    /// The input text to classify.
57    pub input: String,
58    /// ID of the model to use.
59    /// Two content moderations models are available: `text-moderation-stable` and `text-moderation-latest`.
60    #[serde(skip_serializing_if = "Option::is_none")]
61    #[builder(default)]
62    pub model: Option<String>,
63}
64
65impl Moderation {
66    async fn create(request: &ModerationRequest) -> ApiResponseOrError<Self> {
67        openai_post("moderations", request).await
68    }
69
70    pub fn builder(input: impl Into<String>) -> ModerationBuilder {
71        ModerationBuilder::create_empty().input(input)
72    }
73}
74
75impl ModerationBuilder {
76    pub async fn create(self) -> ApiResponseOrError<Moderation> {
77        Moderation::create(&self.build().unwrap()).await
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::set_key;
85    use dotenvy::dotenv;
86    use std::env;
87
88    #[tokio::test]
89    async fn moderations() {
90        dotenv().ok();
91        set_key(env::var("OPENAI_KEY").unwrap());
92
93        let moderation = Moderation::builder("I want to kill them.")
94            .model("text-moderation-latest")
95            .create()
96            .await
97            .unwrap();
98
99        assert_eq!(
100            moderation.results.first().unwrap().categories.violence,
101            true
102        );
103        assert_eq!(moderation.results.first().unwrap().flagged, true);
104    }
105}