Skip to main content

openai_tools/common/
role.rs

1use serde::{Deserialize, Serialize};
2
3/// The role of a message author.
4///
5/// # Forward compatibility
6///
7/// OpenAI adds roles over time - `developer` was introduced as the
8/// reasoning-model replacement for `system`. Unrecognised roles deserialize
9/// into [`Other`](Role::Other) instead of failing the whole response, and the
10/// enum is `#[non_exhaustive]` so future additions are not breaking changes.
11#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
12#[non_exhaustive]
13pub enum Role {
14    /// System instructions
15    #[serde(rename = "system")]
16    System,
17    /// Developer instructions - the reasoning-model replacement for `system`
18    #[serde(rename = "developer")]
19    Developer,
20    /// End-user input
21    #[serde(rename = "user")]
22    User,
23    /// Model output
24    #[serde(rename = "assistant")]
25    Assistant,
26    /// Legacy function result
27    #[serde(rename = "function")]
28    Function,
29    /// Tool call result
30    #[serde(rename = "tool")]
31    Tool,
32    /// A role this version of the library does not know about
33    #[serde(untagged)]
34    Other(String),
35}
36
37impl TryFrom<String> for Role {
38    type Error = &'static str;
39
40    /// Parses a caller-supplied role.
41    ///
42    /// This stays strict and rejects unknown values, unlike deserialization,
43    /// which has to tolerate whatever the API sends. Construct
44    /// [`Role::Other`] directly if you need to pass through an unrecognised
45    /// role.
46    fn try_from(role: String) -> Result<Self, Self::Error> {
47        let role = role.to_lowercase();
48        match role.as_str() {
49            "system" => Ok(Role::System),
50            "developer" => Ok(Role::Developer),
51            "user" => Ok(Role::User),
52            "assistant" => Ok(Role::Assistant),
53            "function" => Ok(Role::Function),
54            "tool" => Ok(Role::Tool),
55            _ => Err("Unknown role"),
56        }
57    }
58}
59
60impl Role {
61    /// Returns the wire representation of this role.
62    pub fn as_str(&self) -> &str {
63        match self {
64            Role::System => "system",
65            Role::Developer => "developer",
66            Role::User => "user",
67            Role::Assistant => "assistant",
68            Role::Function => "function",
69            Role::Tool => "tool",
70            Role::Other(role) => role.as_str(),
71        }
72    }
73}
74
75impl std::fmt::Display for Role {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        write!(f, "{}", self.as_str())
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    #[test]
85    fn test_role_conversion() {
86        assert_eq!(Role::try_from("system".to_string()).unwrap(), Role::System);
87        assert_eq!(Role::try_from("developer".to_string()).unwrap(), Role::Developer);
88        assert_eq!(Role::try_from("user".to_string()).unwrap(), Role::User);
89        assert_eq!(Role::try_from("assistant".to_string()).unwrap(), Role::Assistant);
90        assert_eq!(Role::try_from("function".to_string()).unwrap(), Role::Function);
91        assert_eq!(Role::try_from("tool".to_string()).unwrap(), Role::Tool);
92        assert!(Role::try_from("unknown".to_string()).is_err());
93    }
94
95    #[test]
96    fn test_role_as_str() {
97        assert_eq!(Role::System.as_str(), "system");
98        assert_eq!(Role::Developer.as_str(), "developer");
99        assert_eq!(Role::User.as_str(), "user");
100        assert_eq!(Role::Assistant.as_str(), "assistant");
101        assert_eq!(Role::Function.as_str(), "function");
102        assert_eq!(Role::Tool.as_str(), "tool");
103        assert_eq!(Role::Other("moderator".to_string()).as_str(), "moderator");
104    }
105}