claude_agent/client/messages/
config.rs

1//! Configuration types for message requests.
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "lowercase")]
7pub enum EffortLevel {
8    Low,
9    Medium,
10    High,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(tag = "type", rename_all = "snake_case")]
15pub enum ToolChoice {
16    Auto,
17    Any,
18    Tool { name: String },
19    None,
20}
21
22impl ToolChoice {
23    pub fn tool(name: impl Into<String>) -> Self {
24        Self::Tool { name: name.into() }
25    }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct OutputConfig {
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub effort: Option<EffortLevel>,
32}
33
34impl OutputConfig {
35    pub fn with_effort(level: EffortLevel) -> Self {
36        Self {
37            effort: Some(level),
38        }
39    }
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct ThinkingConfig {
44    #[serde(rename = "type")]
45    pub thinking_type: ThinkingType,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub budget_tokens: Option<u32>,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(rename_all = "snake_case")]
52pub enum ThinkingType {
53    Enabled,
54    Disabled,
55}
56
57impl ThinkingConfig {
58    pub fn enabled(budget_tokens: u32) -> Self {
59        Self {
60            thinking_type: ThinkingType::Enabled,
61            budget_tokens: Some(budget_tokens),
62        }
63    }
64
65    pub fn disabled() -> Self {
66        Self {
67            thinking_type: ThinkingType::Disabled,
68            budget_tokens: None,
69        }
70    }
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74#[serde(tag = "type", rename_all = "snake_case")]
75pub enum OutputFormat {
76    JsonSchema {
77        #[serde(skip_serializing_if = "Option::is_none")]
78        name: Option<String>,
79        schema: serde_json::Value,
80        #[serde(skip_serializing_if = "Option::is_none")]
81        description: Option<String>,
82    },
83}
84
85impl OutputFormat {
86    pub fn json_schema(schema: serde_json::Value) -> Self {
87        Self::JsonSchema {
88            name: None,
89            schema,
90            description: None,
91        }
92    }
93
94    pub fn json_schema_named(name: impl Into<String>, schema: serde_json::Value) -> Self {
95        Self::JsonSchema {
96            name: Some(name.into()),
97            schema,
98            description: None,
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_thinking_config_enabled() {
109        let config = ThinkingConfig::enabled(10000);
110        assert_eq!(config.thinking_type, ThinkingType::Enabled);
111        assert_eq!(config.budget_tokens, Some(10000));
112    }
113
114    #[test]
115    fn test_thinking_config_disabled() {
116        let config = ThinkingConfig::disabled();
117        assert_eq!(config.thinking_type, ThinkingType::Disabled);
118        assert_eq!(config.budget_tokens, None);
119    }
120
121    #[test]
122    fn test_thinking_config_serialization() {
123        let config = ThinkingConfig::enabled(5000);
124        let json = serde_json::to_string(&config).unwrap();
125        assert!(json.contains("\"type\":\"enabled\""));
126        assert!(json.contains("\"budget_tokens\":5000"));
127    }
128
129    #[test]
130    fn test_output_format_json_schema() {
131        let schema = serde_json::json!({
132            "type": "object",
133            "properties": {
134                "name": {"type": "string"}
135            }
136        });
137        let format = OutputFormat::json_schema(schema);
138        let json = serde_json::to_string(&format).unwrap();
139        assert!(json.contains("\"type\":\"json_schema\""));
140        assert!(json.contains("\"schema\""));
141    }
142
143    #[test]
144    fn test_output_format_named_schema() {
145        let schema = serde_json::json!({"type": "string"});
146        let format = OutputFormat::json_schema_named("PersonName", schema);
147        let json = serde_json::to_string(&format).unwrap();
148        assert!(json.contains("\"name\":\"PersonName\""));
149    }
150
151    #[test]
152    fn test_effort_level_serialization() {
153        let effort = EffortLevel::Low;
154        let json = serde_json::to_string(&effort).unwrap();
155        assert_eq!(json, "\"low\"");
156
157        let effort = EffortLevel::Medium;
158        let json = serde_json::to_string(&effort).unwrap();
159        assert_eq!(json, "\"medium\"");
160
161        let effort = EffortLevel::High;
162        let json = serde_json::to_string(&effort).unwrap();
163        assert_eq!(json, "\"high\"");
164    }
165
166    #[test]
167    fn test_output_config_serialization() {
168        let config = OutputConfig::with_effort(EffortLevel::High);
169        let json = serde_json::to_string(&config).unwrap();
170        assert!(json.contains("\"effort\":\"high\""));
171    }
172
173    #[test]
174    fn test_tool_choice_auto() {
175        let choice = ToolChoice::Auto;
176        let json = serde_json::to_string(&choice).unwrap();
177        assert_eq!(json, r#"{"type":"auto"}"#);
178    }
179
180    #[test]
181    fn test_tool_choice_any() {
182        let choice = ToolChoice::Any;
183        let json = serde_json::to_string(&choice).unwrap();
184        assert_eq!(json, r#"{"type":"any"}"#);
185    }
186
187    #[test]
188    fn test_tool_choice_none() {
189        let choice = ToolChoice::None;
190        let json = serde_json::to_string(&choice).unwrap();
191        assert_eq!(json, r#"{"type":"none"}"#);
192    }
193
194    #[test]
195    fn test_tool_choice_tool() {
196        let choice = ToolChoice::tool("Bash");
197        let json = serde_json::to_string(&choice).unwrap();
198        assert_eq!(json, r#"{"type":"tool","name":"Bash"}"#);
199    }
200}