1use super::{
8 DEFAULT_ARCEE_BASE_URL, DEFAULT_ARCEE_MODEL, DEFAULT_ATLASCLOUD_BASE_URL,
9 DEFAULT_ATLASCLOUD_MODEL, DEFAULT_DEEPINFRA_BASE_URL, DEFAULT_DEEPINFRA_MODEL,
10 DEFAULT_DEEPSEEK_ANTHROPIC_BASE_URL, DEFAULT_DEEPSEEK_ANTHROPIC_MODEL,
11 DEFAULT_DEEPSEEK_BASE_URL, DEFAULT_DEEPSEEK_MODEL, DEFAULT_FIREWORKS_BASE_URL,
12 DEFAULT_FIREWORKS_MODEL, DEFAULT_HUGGINGFACE_BASE_URL, DEFAULT_HUGGINGFACE_MODEL,
13 DEFAULT_MINIMAX_BASE_URL, DEFAULT_MINIMAX_MODEL, DEFAULT_MOONSHOT_BASE_URL,
14 DEFAULT_MOONSHOT_MODEL, DEFAULT_NOVITA_BASE_URL, DEFAULT_NOVITA_MODEL,
15 DEFAULT_NVIDIA_NIM_BASE_URL, DEFAULT_NVIDIA_NIM_MODEL, DEFAULT_OLLAMA_BASE_URL,
16 DEFAULT_OLLAMA_MODEL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_CODEX_BASE_URL,
17 DEFAULT_OPENAI_CODEX_MODEL, DEFAULT_OPENAI_MODEL, DEFAULT_OPENROUTER_BASE_URL,
18 DEFAULT_OPENROUTER_MODEL, DEFAULT_QIANFAN_BASE_URL, DEFAULT_QIANFAN_MODEL,
19 DEFAULT_SGLANG_BASE_URL, DEFAULT_SGLANG_MODEL, DEFAULT_SILICONFLOW_BASE_URL,
20 DEFAULT_SILICONFLOW_CN_BASE_URL, DEFAULT_SILICONFLOW_MODEL, DEFAULT_STEPFUN_BASE_URL,
21 DEFAULT_STEPFUN_MODEL, DEFAULT_TOGETHER_BASE_URL, DEFAULT_TOGETHER_MODEL,
22 DEFAULT_VLLM_BASE_URL, DEFAULT_VLLM_MODEL, DEFAULT_VOLCENGINE_BASE_URL,
23 DEFAULT_VOLCENGINE_MODEL, DEFAULT_WANJIE_ARK_BASE_URL, DEFAULT_WANJIE_ARK_MODEL,
24 DEFAULT_XIAOMI_MIMO_BASE_URL, DEFAULT_XIAOMI_MIMO_MODEL, DEFAULT_ZAI_BASE_URL,
25 DEFAULT_ZAI_MODEL, ProviderKind,
26};
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum WireFormat {
32 ChatCompletions,
34 Responses,
36 AnthropicMessages,
38}
39
40pub trait Provider: Send + Sync {
42 fn kind(&self) -> ProviderKind;
44
45 fn id(&self) -> &'static str {
47 self.kind().as_str()
48 }
49
50 fn display_name(&self) -> &'static str;
52
53 fn default_base_url(&self) -> &'static str;
55
56 fn default_model(&self) -> &'static str;
58
59 fn env_vars(&self) -> &'static [&'static str];
61
62 fn provider_config_key(&self) -> &'static str;
64
65 fn aliases(&self) -> &'static [&'static str] {
67 &[]
68 }
69
70 fn wire(&self) -> WireFormat {
72 WireFormat::ChatCompletions
73 }
74}
75
76macro_rules! provider {
77 (
78 $struct_name:ident,
79 $kind:ident,
80 $id:literal,
81 $display_name:literal,
82 $base_url:ident,
83 $model:ident,
84 [$($env_var:literal),* $(,)?],
85 $config_key:literal,
86 aliases: [$($alias:literal),* $(,)?]
87 ) => {
88 pub struct $struct_name;
90
91 impl Provider for $struct_name {
92 fn id(&self) -> &'static str {
93 $id
94 }
95
96 fn kind(&self) -> ProviderKind {
97 ProviderKind::$kind
98 }
99
100 fn display_name(&self) -> &'static str {
101 $display_name
102 }
103
104 fn default_base_url(&self) -> &'static str {
105 $base_url
106 }
107
108 fn default_model(&self) -> &'static str {
109 $model
110 }
111
112 fn env_vars(&self) -> &'static [&'static str] {
113 &[$($env_var),*]
114 }
115
116 fn provider_config_key(&self) -> &'static str {
117 $config_key
118 }
119
120 fn aliases(&self) -> &'static [&'static str] {
121 &[$($alias),*]
122 }
123 }
124 };
125}
126
127provider!(
128 Deepseek,
129 Deepseek,
130 "deepseek",
131 "DeepSeek",
132 DEFAULT_DEEPSEEK_BASE_URL,
133 DEFAULT_DEEPSEEK_MODEL,
134 ["DEEPSEEK_API_KEY"],
135 "deepseek",
136 aliases: ["deep-seek", "deepseek-cn", "deepseek_china", "deepseekcn", "deepseek-china"]
137);
138
139pub struct DeepseekAnthropic;
141
142impl Provider for DeepseekAnthropic {
143 fn id(&self) -> &'static str {
144 "deepseek-anthropic"
145 }
146
147 fn kind(&self) -> ProviderKind {
148 ProviderKind::DeepseekAnthropic
149 }
150
151 fn display_name(&self) -> &'static str {
152 "DeepSeek (Anthropic-compatible)"
153 }
154
155 fn default_base_url(&self) -> &'static str {
156 DEFAULT_DEEPSEEK_ANTHROPIC_BASE_URL
157 }
158
159 fn default_model(&self) -> &'static str {
160 DEFAULT_DEEPSEEK_ANTHROPIC_MODEL
161 }
162
163 fn env_vars(&self) -> &'static [&'static str] {
164 &["DEEPSEEK_API_KEY"]
165 }
166
167 fn provider_config_key(&self) -> &'static str {
168 "deepseek_anthropic"
169 }
170
171 fn aliases(&self) -> &'static [&'static str] {
172 &["deepseek_anthropic", "deepseek-claude", "deepseek_claude"]
173 }
174
175 fn wire(&self) -> WireFormat {
176 WireFormat::AnthropicMessages
177 }
178}
179provider!(
180 NvidiaNim,
181 NvidiaNim,
182 "nvidia-nim",
183 "NVIDIA NIM",
184 DEFAULT_NVIDIA_NIM_BASE_URL,
185 DEFAULT_NVIDIA_NIM_MODEL,
186 ["NVIDIA_API_KEY", "NVIDIA_NIM_API_KEY", "DEEPSEEK_API_KEY"],
187 "nvidia_nim",
188 aliases: ["nvidia", "nvidia_nim", "nim"]
189);
190provider!(
191 Openai,
192 Openai,
193 "openai",
194 "OpenAI-compatible",
195 DEFAULT_OPENAI_BASE_URL,
196 DEFAULT_OPENAI_MODEL,
197 ["OPENAI_API_KEY"],
198 "openai",
199 aliases: ["open-ai"]
200);
201provider!(
202 Atlascloud,
203 Atlascloud,
204 "atlascloud",
205 "AtlasCloud",
206 DEFAULT_ATLASCLOUD_BASE_URL,
207 DEFAULT_ATLASCLOUD_MODEL,
208 ["ATLASCLOUD_API_KEY"],
209 "atlascloud",
210 aliases: ["atlas-cloud", "atlas_cloud", "atlas"]
211);
212provider!(
213 WanjieArk,
214 WanjieArk,
215 "wanjie-ark",
216 "Wanjie Ark",
217 DEFAULT_WANJIE_ARK_BASE_URL,
218 DEFAULT_WANJIE_ARK_MODEL,
219 [
220 "WANJIE_ARK_API_KEY",
221 "WANJIE_API_KEY",
222 "WANJIE_MAAS_API_KEY"
223 ],
224 "wanjie_ark",
225 aliases: ["wanjie", "wanjie_ark", "ark-wanjie", "ark_wanjie", "wanjieark", "wanjie-maas", "wanjie_maas", "wanjiemaas"]
226);
227provider!(
228 Volcengine,
229 Volcengine,
230 "volcengine",
231 "Volcengine Ark",
232 DEFAULT_VOLCENGINE_BASE_URL,
233 DEFAULT_VOLCENGINE_MODEL,
234 [
235 "VOLCENGINE_API_KEY",
236 "VOLCENGINE_ARK_API_KEY",
237 "ARK_API_KEY"
238 ],
239 "volcengine",
240 aliases: ["volcengine-ark", "volcengine_ark", "ark", "volc-ark", "volcengineark"]
241);
242provider!(
243 Openrouter,
244 Openrouter,
245 "openrouter",
246 "OpenRouter",
247 DEFAULT_OPENROUTER_BASE_URL,
248 DEFAULT_OPENROUTER_MODEL,
249 ["OPENROUTER_API_KEY"],
250 "openrouter",
251 aliases: ["open_router"]
252);
253provider!(
254 XiaomiMimo,
255 XiaomiMimo,
256 "xiaomi-mimo",
257 "Xiaomi MiMo",
258 DEFAULT_XIAOMI_MIMO_BASE_URL,
259 DEFAULT_XIAOMI_MIMO_MODEL,
260 [
261 "XIAOMI_MIMO_TOKEN_PLAN_API_KEY",
262 "MIMO_TOKEN_PLAN_API_KEY",
263 "XIAOMI_MIMO_API_KEY",
264 "XIAOMI_API_KEY",
265 "MIMO_API_KEY",
266 ],
267 "xiaomi_mimo",
268 aliases: ["xiaomi_mimo", "xiaomimimo", "mimo", "xiaomi"]
269);
270provider!(
271 Novita,
272 Novita,
273 "novita",
274 "Novita AI",
275 DEFAULT_NOVITA_BASE_URL,
276 DEFAULT_NOVITA_MODEL,
277 ["NOVITA_API_KEY"],
278 "novita",
279 aliases: []
280);
281provider!(
282 Fireworks,
283 Fireworks,
284 "fireworks",
285 "Fireworks AI",
286 DEFAULT_FIREWORKS_BASE_URL,
287 DEFAULT_FIREWORKS_MODEL,
288 ["FIREWORKS_API_KEY"],
289 "fireworks",
290 aliases: ["fireworks-ai"]
291);
292provider!(
293 Siliconflow,
294 Siliconflow,
295 "siliconflow",
296 "SiliconFlow",
297 DEFAULT_SILICONFLOW_BASE_URL,
298 DEFAULT_SILICONFLOW_MODEL,
299 ["SILICONFLOW_API_KEY"],
300 "siliconflow",
301 aliases: ["silicon-flow", "silicon_flow"]
302);
303provider!(
304 SiliconflowCN,
305 SiliconflowCN,
306 "siliconflow-CN",
307 "SiliconFlow (China)",
308 DEFAULT_SILICONFLOW_CN_BASE_URL,
309 DEFAULT_SILICONFLOW_MODEL,
310 ["SILICONFLOW_API_KEY"],
311 "siliconflow_cn",
312 aliases: [
313 "silicon-flow-cn",
314 "silicon-flow-CN",
315 "silicon_flow_cn",
316 "silicon_flow_CN",
317 "siliconflow-china",
318 ]
319);
320provider!(
321 Arcee,
322 Arcee,
323 "arcee",
324 "Arcee AI",
325 DEFAULT_ARCEE_BASE_URL,
326 DEFAULT_ARCEE_MODEL,
327 ["ARCEE_API_KEY"],
328 "arcee",
329 aliases: ["arcee-ai", "arcee_ai"]
330);
331provider!(
332 Moonshot,
333 Moonshot,
334 "moonshot",
335 "Moonshot/Kimi",
336 DEFAULT_MOONSHOT_BASE_URL,
337 DEFAULT_MOONSHOT_MODEL,
338 ["MOONSHOT_API_KEY", "KIMI_API_KEY"],
339 "moonshot",
340 aliases: ["moonshot-ai", "kimi", "kimi-k2"]
341);
342provider!(
343 Sglang,
344 Sglang,
345 "sglang",
346 "SGLang",
347 DEFAULT_SGLANG_BASE_URL,
348 DEFAULT_SGLANG_MODEL,
349 ["SGLANG_API_KEY"],
350 "sglang",
351 aliases: ["sg-lang"]
352);
353provider!(
354 Vllm,
355 Vllm,
356 "vllm",
357 "vLLM",
358 DEFAULT_VLLM_BASE_URL,
359 DEFAULT_VLLM_MODEL,
360 ["VLLM_API_KEY"],
361 "vllm",
362 aliases: ["v-llm"]
363);
364provider!(
365 Ollama,
366 Ollama,
367 "ollama",
368 "Ollama",
369 DEFAULT_OLLAMA_BASE_URL,
370 DEFAULT_OLLAMA_MODEL,
371 ["OLLAMA_API_KEY"],
372 "ollama",
373 aliases: ["ollama-local"]
374);
375provider!(
376 Huggingface,
377 Huggingface,
378 "huggingface",
379 "Hugging Face",
380 DEFAULT_HUGGINGFACE_BASE_URL,
381 DEFAULT_HUGGINGFACE_MODEL,
382 ["HUGGINGFACE_API_KEY", "HF_TOKEN"],
383 "huggingface",
384 aliases: ["hugging-face", "hugging_face", "hf"]
385);
386provider!(
387 Together,
388 Together,
389 "together",
390 "Together AI",
391 DEFAULT_TOGETHER_BASE_URL,
392 DEFAULT_TOGETHER_MODEL,
393 ["TOGETHER_API_KEY"],
394 "together",
395 aliases: ["together-ai", "together_ai"]
396);
397provider!(
398 Qianfan,
399 Qianfan,
400 "qianfan",
401 "Baidu Qianfan",
402 DEFAULT_QIANFAN_BASE_URL,
403 DEFAULT_QIANFAN_MODEL,
404 ["QIANFAN_API_KEY", "BAIDU_QIANFAN_API_KEY"],
405 "qianfan",
406 aliases: ["baidu-qianfan", "baidu_qianfan", "baidu"]
407);
408
409pub struct OpenaiCodex;
411
412impl Provider for OpenaiCodex {
413 fn id(&self) -> &'static str {
414 "openai-codex"
415 }
416
417 fn kind(&self) -> ProviderKind {
418 ProviderKind::OpenaiCodex
419 }
420
421 fn display_name(&self) -> &'static str {
422 "OpenAI Codex (ChatGPT)"
423 }
424
425 fn default_base_url(&self) -> &'static str {
426 DEFAULT_OPENAI_CODEX_BASE_URL
427 }
428
429 fn default_model(&self) -> &'static str {
430 DEFAULT_OPENAI_CODEX_MODEL
431 }
432
433 fn env_vars(&self) -> &'static [&'static str] {
434 &["OPENAI_CODEX_ACCESS_TOKEN", "CODEX_ACCESS_TOKEN"]
435 }
436
437 fn provider_config_key(&self) -> &'static str {
438 "openai_codex"
439 }
440
441 fn aliases(&self) -> &'static [&'static str] {
442 &[
443 "openai_codex",
444 "openaicodex",
445 "codex",
446 "chatgpt",
447 "chatgpt-codex",
448 "chatgpt_codex",
449 "chatgptcodex",
450 ]
451 }
452
453 fn wire(&self) -> WireFormat {
454 WireFormat::Responses
455 }
456}
457
458pub struct Anthropic;
460
461impl Provider for Anthropic {
462 fn id(&self) -> &'static str {
463 "anthropic"
464 }
465
466 fn kind(&self) -> ProviderKind {
467 ProviderKind::Anthropic
468 }
469
470 fn display_name(&self) -> &'static str {
471 "Anthropic"
472 }
473
474 fn default_base_url(&self) -> &'static str {
475 crate::DEFAULT_ANTHROPIC_BASE_URL
476 }
477
478 fn default_model(&self) -> &'static str {
479 crate::DEFAULT_ANTHROPIC_MODEL
480 }
481
482 fn env_vars(&self) -> &'static [&'static str] {
483 &["ANTHROPIC_API_KEY"]
484 }
485
486 fn provider_config_key(&self) -> &'static str {
487 "anthropic"
488 }
489
490 fn wire(&self) -> WireFormat {
491 WireFormat::AnthropicMessages
492 }
493}
494
495provider!(
496 Zai,
497 Zai,
498 "zai",
499 "Zhipu AI / Z.ai",
500 DEFAULT_ZAI_BASE_URL,
501 DEFAULT_ZAI_MODEL,
502 ["ZAI_API_KEY", "Z_AI_API_KEY", "ZHIPU_API_KEY", "GLM_API_KEY"],
503 "zai",
504 aliases: ["z-ai", "z_ai", "z.ai", "zhipu", "zhipuai", "bigmodel", "big-model"]
505);
506
507provider!(
508 Stepfun,
509 Stepfun,
510 "stepfun",
511 "StepFun / StepFlash",
512 DEFAULT_STEPFUN_BASE_URL,
513 DEFAULT_STEPFUN_MODEL,
514 ["STEPFUN_API_KEY", "STEP_API_KEY"],
515 "stepfun",
516 aliases: ["step-fun", "step_fun", "stepflash", "step-flash", "step_flash"]
517);
518
519provider!(
520 Minimax,
521 Minimax,
522 "minimax",
523 "MiniMax",
524 DEFAULT_MINIMAX_BASE_URL,
525 DEFAULT_MINIMAX_MODEL,
526 ["MINIMAX_API_KEY"],
527 "minimax",
528 aliases: ["mini-max", "mini_max"]
529);
530
531provider!(
532 Deepinfra,
533 Deepinfra,
534 "deepinfra",
535 "DeepInfra",
536 DEFAULT_DEEPINFRA_BASE_URL,
537 DEFAULT_DEEPINFRA_MODEL,
538 ["DEEPINFRA_API_KEY", "DEEPINFRA_TOKEN"],
539 "deepinfra",
540 aliases: ["deep-infra", "deep_infra"]
541);
542
543pub struct Custom;
554
555impl Provider for Custom {
556 fn id(&self) -> &'static str {
557 "custom"
558 }
559
560 fn kind(&self) -> ProviderKind {
561 ProviderKind::Custom
562 }
563
564 fn display_name(&self) -> &'static str {
565 "Custom (OpenAI-compatible)"
566 }
567
568 fn default_base_url(&self) -> &'static str {
569 "http://localhost/v1"
573 }
574
575 fn default_model(&self) -> &'static str {
576 "custom-model"
579 }
580
581 fn env_vars(&self) -> &'static [&'static str] {
582 &[]
585 }
586
587 fn provider_config_key(&self) -> &'static str {
588 "custom"
589 }
590
591 fn wire(&self) -> WireFormat {
592 WireFormat::ChatCompletions
593 }
594}
595
596static DEEPSEEK: Deepseek = Deepseek;
597static DEEPSEEK_ANTHROPIC: DeepseekAnthropic = DeepseekAnthropic;
598static NVIDIA_NIM: NvidiaNim = NvidiaNim;
599static OPENAI: Openai = Openai;
600static ATLASCLOUD: Atlascloud = Atlascloud;
601static WANJIE_ARK: WanjieArk = WanjieArk;
602static VOLCENGINE: Volcengine = Volcengine;
603static OPENROUTER: Openrouter = Openrouter;
604static XIAOMI_MIMO: XiaomiMimo = XiaomiMimo;
605static NOVITA: Novita = Novita;
606static FIREWORKS: Fireworks = Fireworks;
607static SILICONFLOW: Siliconflow = Siliconflow;
608static SILICONFLOW_CN: SiliconflowCN = SiliconflowCN;
609static ARCEE: Arcee = Arcee;
610static MOONSHOT: Moonshot = Moonshot;
611static SGLANG: Sglang = Sglang;
612static VLLM: Vllm = Vllm;
613static OLLAMA: Ollama = Ollama;
614static HUGGINGFACE: Huggingface = Huggingface;
615static TOGETHER: Together = Together;
616static QIANFAN: Qianfan = Qianfan;
617static OPENAI_CODEX: OpenaiCodex = OpenaiCodex;
618static ANTHROPIC: Anthropic = Anthropic;
619static ZAI: Zai = Zai;
620static STEPFUN: Stepfun = Stepfun;
621static MINIMAX: Minimax = Minimax;
622static DEEPINFRA: Deepinfra = Deepinfra;
623static CUSTOM: Custom = Custom;
624
625static PROVIDER_REGISTRY: [&dyn Provider; 28] = [
626 &DEEPSEEK,
627 &DEEPSEEK_ANTHROPIC,
628 &NVIDIA_NIM,
629 &OPENAI,
630 &ATLASCLOUD,
631 &WANJIE_ARK,
632 &VOLCENGINE,
633 &OPENROUTER,
634 &XIAOMI_MIMO,
635 &NOVITA,
636 &FIREWORKS,
637 &SILICONFLOW,
638 &ARCEE,
639 &SILICONFLOW_CN,
640 &MOONSHOT,
641 &SGLANG,
642 &VLLM,
643 &OLLAMA,
644 &HUGGINGFACE,
645 &TOGETHER,
646 &QIANFAN,
647 &OPENAI_CODEX,
648 &ANTHROPIC,
649 &ZAI,
650 &STEPFUN,
651 &MINIMAX,
652 &DEEPINFRA,
653 &CUSTOM,
654];
655
656#[must_use]
662pub fn all_providers() -> &'static [&'static dyn Provider] {
663 &PROVIDER_REGISTRY
664}
665
666#[must_use]
682pub fn providers_sorted_for_display() -> Vec<&'static dyn Provider> {
683 let mut providers = all_providers().to_vec();
684 providers.sort_by(|a, b| {
685 a.display_name()
686 .to_ascii_lowercase()
687 .cmp(&b.display_name().to_ascii_lowercase())
688 });
689 providers
690}
691
692#[must_use]
694pub fn lookup_provider(id: &str) -> Option<&'static dyn Provider> {
695 let id = id.trim();
696 all_providers()
697 .iter()
698 .copied()
699 .find(|provider| provider.id() == id)
700}
701
702#[must_use]
704pub fn resolve_provider(id_or_alias: &str) -> Option<&'static dyn Provider> {
705 ProviderKind::parse(id_or_alias).map(provider_for_kind)
706}
707
708#[must_use]
710pub fn provider_for_kind(kind: ProviderKind) -> &'static dyn Provider {
711 PROVIDER_REGISTRY
712 .iter()
713 .find(|p| p.kind() == kind)
714 .copied()
715 .expect("ProviderKind variant missing from PROVIDER_REGISTRY")
716}
717
718#[cfg(test)]
719mod tests {
720 use super::*;
721
722 #[test]
723 fn display_order_is_alphabetical_by_display_name() {
724 let display = providers_sorted_for_display();
725 let names: Vec<String> = display
726 .iter()
727 .map(|p| p.display_name().to_ascii_lowercase())
728 .collect();
729 let mut sorted = names.clone();
730 sorted.sort();
731 assert_eq!(
732 names, sorted,
733 "providers_sorted_for_display must be alphabetical (case-insensitive) by display name"
734 );
735 }
736
737 #[test]
738 fn display_order_differs_from_internal_all_order() {
739 let display_ids: Vec<&str> = providers_sorted_for_display()
742 .iter()
743 .map(|p| p.id())
744 .collect();
745 let internal_ids: Vec<&str> = all_providers().iter().map(|p| p.id()).collect();
746 assert_ne!(
747 display_ids, internal_ids,
748 "display order should not match internal ALL order"
749 );
750 }
751
752 #[test]
753 fn display_order_is_complete_and_unique() {
754 let display = providers_sorted_for_display();
756 assert_eq!(
757 display.len(),
758 all_providers().len(),
759 "display order must include every built-in provider"
760 );
761 let mut ids: Vec<&str> = display.iter().map(|p| p.id()).collect();
762 ids.sort_unstable();
763 let before = ids.len();
764 ids.dedup();
765 assert_eq!(
766 before,
767 ids.len(),
768 "display order must not contain duplicates"
769 );
770 }
771
772 #[test]
773 fn deepseek_is_present_but_not_first_in_display_order() {
774 let display = providers_sorted_for_display();
777 assert_eq!(
778 all_providers()[0].kind(),
779 ProviderKind::Deepseek,
780 "DeepSeek is expected to remain first in the stable internal order"
781 );
782 assert!(
783 display.iter().any(|p| p.kind() == ProviderKind::Deepseek),
784 "DeepSeek must remain present in display order"
785 );
786 assert_ne!(
787 display[0].kind(),
788 ProviderKind::Deepseek,
789 "DeepSeek must not be hard-coded first in display order"
790 );
791 assert_eq!(
794 display[0].display_name(),
795 "Anthropic",
796 "alphabetical display order should lead with Anthropic"
797 );
798 }
799}