vtcode_commons/
reasoning.rs1use serde::{Deserialize, Deserializer, Serialize};
9use std::fmt;
10
11pub mod constants {
13 pub const NONE: &str = "none";
14 pub const MINIMAL: &str = "minimal";
15 pub const LOW: &str = "low";
16 pub const MEDIUM: &str = "medium";
17 pub const HIGH: &str = "high";
18 pub const XHIGH: &str = "xhigh";
19 pub const MAX: &str = "max";
20 pub const ALLOWED_LEVELS: &[&str] = &[MINIMAL, LOW, MEDIUM, HIGH, XHIGH, MAX];
21 pub const LABEL_LOW: &str = "Low";
22 pub const LABEL_MEDIUM: &str = "Medium";
23 pub const LABEL_HIGH: &str = "High";
24 pub const DESCRIPTION_LOW: &str = "Fast responses with lightweight reasoning.";
25 pub const DESCRIPTION_MEDIUM: &str = "Balanced depth and speed for general tasks. (Note: May not be fully available for all models including Gemini 3 Pro)";
26 pub const DESCRIPTION_HIGH: &str = "Maximum reasoning depth for complex problems.";
27}
28
29#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
35#[serde(rename_all = "lowercase")]
36#[derive(Default)]
37pub enum ReasoningEffortLevel {
38 None,
40 Minimal,
42 Low,
44 #[default]
46 Medium,
47 High,
49 XHigh,
51 Max,
53 Unknown,
55}
56
57impl ReasoningEffortLevel {
58 pub fn as_str(self) -> &'static str {
60 match self {
61 Self::None => constants::NONE,
62 Self::Minimal => constants::MINIMAL,
63 Self::Low => constants::LOW,
64 Self::Medium => constants::MEDIUM,
65 Self::High => constants::HIGH,
66 Self::XHigh => constants::XHIGH,
67 Self::Max => constants::MAX,
68 Self::Unknown => "unknown",
69 }
70 }
71
72 pub fn parse(value: &str) -> Option<Self> {
74 let normalized = value.trim();
75 if normalized.eq_ignore_ascii_case(constants::NONE) {
76 Some(Self::None)
77 } else if normalized.eq_ignore_ascii_case(constants::MINIMAL) {
78 Some(Self::Minimal)
79 } else if normalized.eq_ignore_ascii_case(constants::LOW) {
80 Some(Self::Low)
81 } else if normalized.eq_ignore_ascii_case(constants::MEDIUM) {
82 Some(Self::Medium)
83 } else if normalized.eq_ignore_ascii_case(constants::HIGH) {
84 Some(Self::High)
85 } else if normalized.eq_ignore_ascii_case(constants::XHIGH) {
86 Some(Self::XHigh)
87 } else if normalized.eq_ignore_ascii_case(constants::MAX) {
88 Some(Self::Max)
89 } else {
90 None
91 }
92 }
93
94 pub fn allowed_values() -> &'static [&'static str] {
96 constants::ALLOWED_LEVELS
97 }
98}
99
100impl fmt::Display for ReasoningEffortLevel {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 f.write_str(self.as_str())
103 }
104}
105
106impl<'de> Deserialize<'de> for ReasoningEffortLevel {
107 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
108 where
109 D: Deserializer<'de>,
110 {
111 let raw = String::deserialize(deserializer)?;
112 if let Some(parsed) = Self::parse(&raw) {
113 Ok(parsed)
114 } else {
115 Ok(Self::Unknown)
116 }
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_reasoning_effort_parse_and_allowed_values_include_max() {
126 assert_eq!(
127 ReasoningEffortLevel::parse("max"),
128 Some(ReasoningEffortLevel::Max)
129 );
130 assert_eq!(ReasoningEffortLevel::Max.as_str(), "max");
131 assert!(ReasoningEffortLevel::allowed_values().contains(&"max"));
132 }
133}