agent_engine/runtime/openai/catalog/
xai.rs1use super::{
10 CatalogModel, CatalogProviderKind, CatalogSource, Modality, PricingSummary, ReasoningSupport,
11};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct XaiModelDescriptor {
15 pub id: &'static str,
16 pub label: &'static str,
17 pub context_tokens: Option<u64>,
18 pub reasoning: bool,
19}
20
21pub const XAI_TEXT_MODELS: &[XaiModelDescriptor] = &[
22 XaiModelDescriptor {
23 id: "grok-4.3",
24 label: "Grok 4.3",
25 context_tokens: None,
26 reasoning: true,
27 },
28 XaiModelDescriptor {
29 id: "grok-4.3-latest",
30 label: "Grok 4.3 (latest alias)",
31 context_tokens: None,
32 reasoning: true,
33 },
34 XaiModelDescriptor {
35 id: "grok-latest",
36 label: "Grok (latest alias)",
37 context_tokens: None,
38 reasoning: true,
39 },
40 XaiModelDescriptor {
41 id: "grok-4.20-0309-reasoning",
42 label: "Grok 4.20 Reasoning",
43 context_tokens: None,
44 reasoning: true,
45 },
46 XaiModelDescriptor {
47 id: "grok-4.20-0309-non-reasoning",
48 label: "Grok 4.20 Non-Reasoning",
49 context_tokens: None,
50 reasoning: false,
51 },
52 XaiModelDescriptor {
53 id: "grok-4.20-multi-agent-0309",
54 label: "Grok 4.20 Multi-Agent",
55 context_tokens: None,
56 reasoning: true,
57 },
58 XaiModelDescriptor {
59 id: "grok-4.5",
60 label: "Grok 4.5",
61 context_tokens: None,
62 reasoning: true,
63 },
64 XaiModelDescriptor {
65 id: "grok-4.5-latest",
66 label: "Grok 4.5 (latest alias)",
67 context_tokens: None,
68 reasoning: true,
69 },
70 XaiModelDescriptor {
71 id: "grok-4.6",
72 label: "Grok 4.6",
73 context_tokens: None,
74 reasoning: true,
75 },
76];
77
78pub fn xai_model(id: &str) -> Option<&'static XaiModelDescriptor> {
79 XAI_TEXT_MODELS.iter().find(|model| model.id == id)
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum XaiReasoningCapability {
95 Effort {
98 supported: &'static [agent_core::reasoning::ReasoningLevel],
99 default_level: Option<agent_core::reasoning::ReasoningLevel>,
100 can_disable: bool,
103 },
104 IntrinsicReasoning,
106 NonReasoning,
108}
109
110pub fn xai_static_capability(model_id: &str) -> Option<XaiReasoningCapability> {
113 use agent_core::reasoning::ReasoningLevel::*;
114 match model_id {
115 "grok-4.5" | "grok-4.5-latest" | "grok-4.6" => Some(XaiReasoningCapability::Effort {
116 supported: &[Low, Medium, High],
117 default_level: Some(High),
118 can_disable: false,
119 }),
120 "grok-4.20-multi-agent-0309" => Some(XaiReasoningCapability::Effort {
121 supported: &[Low, Medium, High, XHigh],
122 default_level: None,
124 can_disable: false,
125 }),
126 "grok-4.3" | "grok-4.3-latest" | "grok-latest" | "grok-4.20-0309-reasoning" => {
127 Some(XaiReasoningCapability::IntrinsicReasoning)
128 }
129 "grok-4.20-0309-non-reasoning" => Some(XaiReasoningCapability::NonReasoning),
130 _ => None,
131 }
132}
133
134pub fn xai_static_catalog_models() -> Vec<CatalogModel> {
135 XAI_TEXT_MODELS
136 .iter()
137 .map(|descriptor| CatalogModel {
138 provider_key: "xai-auth".into(),
139 provider_name: "xAI (Grok)".into(),
140 provider_kind: CatalogProviderKind::Generic {
141 key: "xai-auth".into(),
142 },
143 id: descriptor.id.into(),
144 label: Some(descriptor.label.into()),
145 context_tokens: descriptor.context_tokens,
146 max_output_tokens: None,
147 input_modalities: vec![Modality::Text],
148 pricing: PricingSummary::default(),
149 reasoning: if descriptor.reasoning {
150 ReasoningSupport::GenericOpenAi
151 } else {
152 ReasoningSupport::None
153 },
154 source: CatalogSource::StaticFallback,
155 })
156 .collect()
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn documented_selectable_public_responses_text_ids_are_exposed() {
165 assert_eq!(
166 XAI_TEXT_MODELS.iter().map(|m| m.id).collect::<Vec<_>>(),
167 vec![
168 "grok-4.3",
169 "grok-4.3-latest",
170 "grok-latest",
171 "grok-4.20-0309-reasoning",
172 "grok-4.20-0309-non-reasoning",
173 "grok-4.20-multi-agent-0309",
174 "grok-4.5",
175 "grok-4.5-latest",
176 "grok-4.6",
177 ]
178 );
179 }
180
181 #[test]
182 fn reasoning_metadata_is_limited_to_documented_reasoning_models() {
183 let reasoning_ids = xai_static_catalog_models()
184 .into_iter()
185 .filter(|model| model.reasoning != ReasoningSupport::None)
186 .map(|model| model.id)
187 .collect::<Vec<_>>();
188 assert_eq!(
189 reasoning_ids,
190 vec![
191 "grok-4.3",
192 "grok-4.3-latest",
193 "grok-latest",
194 "grok-4.20-0309-reasoning",
195 "grok-4.20-multi-agent-0309",
196 "grok-4.5",
197 "grok-4.5-latest",
198 "grok-4.6",
199 ]
200 );
201 }
202
203 #[test]
206 fn grok_45_and_46_family_effort_capability_is_exact() {
207 use agent_core::reasoning::ReasoningLevel::*;
208 for id in ["grok-4.5", "grok-4.5-latest", "grok-4.6"] {
209 match xai_static_capability(id) {
210 Some(XaiReasoningCapability::Effort {
211 supported,
212 default_level,
213 can_disable,
214 }) => {
215 assert_eq!(supported, &[Low, Medium, High], "{id}");
216 assert_eq!(default_level, Some(High), "{id}");
217 assert!(!can_disable, "{id}: reasoning cannot be disabled");
218 }
219 other => panic!("{id}: expected Effort capability, got {other:?}"),
220 }
221 }
222 }
223
224 #[test]
225 fn multi_agent_supports_xhigh_but_45_does_not() {
226 use agent_core::reasoning::ReasoningLevel::*;
227 match xai_static_capability("grok-4.20-multi-agent-0309") {
228 Some(XaiReasoningCapability::Effort {
229 supported,
230 default_level,
231 can_disable,
232 }) => {
233 assert_eq!(supported, &[Low, Medium, High, XHigh]);
234 assert_eq!(default_level, None, "no documented default");
235 assert!(!can_disable);
236 }
237 other => panic!("expected Effort capability, got {other:?}"),
238 }
239 for id in ["grok-4.5", "grok-4.6"] {
241 if let Some(XaiReasoningCapability::Effort { supported, .. }) =
242 xai_static_capability(id)
243 {
244 assert!(!supported.contains(&XHigh), "{id}");
245 }
246 }
247 }
248
249 #[test]
250 fn intrinsic_reasoning_models_have_no_named_effort() {
251 for id in [
252 "grok-4.3",
253 "grok-4.3-latest",
254 "grok-latest",
255 "grok-4.20-0309-reasoning",
256 ] {
257 assert_eq!(
258 xai_static_capability(id),
259 Some(XaiReasoningCapability::IntrinsicReasoning),
260 "{id}"
261 );
262 }
263 }
264
265 #[test]
266 fn non_reasoning_model_is_marked_non_reasoning() {
267 assert_eq!(
268 xai_static_capability("grok-4.20-0309-non-reasoning"),
269 Some(XaiReasoningCapability::NonReasoning)
270 );
271 }
272
273 #[test]
274 fn unknown_exact_ids_fail_closed() {
275 for id in ["grok-5", "grok-4.50", "grok-4.5x", "", "gpt-5.5"] {
276 assert_eq!(xai_static_capability(id), None, "{id}");
277 }
278 }
279
280 #[test]
281 fn catalog_is_text_only_and_excludes_unconfirmed_code_model() {
282 let models = xai_static_catalog_models();
283 assert!(models
284 .iter()
285 .all(|m| m.input_modalities == vec![Modality::Text]));
286 assert!(!models.iter().any(|m| m.id == "grok-build-0.1"));
287 assert!(!models.iter().any(|m| m.id == "grok-build-latest"));
288 assert!(!models.iter().any(|m| ["image", "video", "voice"]
289 .iter()
290 .any(|kind| m.id.contains(kind))));
291 }
292}