1use std::fmt;
7use std::str::FromStr;
8
9use serde::{Deserialize, Serialize};
10
11pub const HEAVY_MODEL: &str = "gpt-5.5";
13pub const HEAVY_REASONING_EFFORT: &str = "low";
15pub const MAIN_MODEL: &str = "gpt-5.4-mini";
17pub const MAIN_REASONING_EFFORT: &str = "low";
19pub const WORKER_MODEL: &str = "gpt-5.3-codex-spark";
21pub const WORKER_REASONING_EFFORT: &str = "medium";
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "snake_case")]
27pub enum Tier {
28 Heavy,
30 Main,
32 Worker,
34}
35
36impl Tier {
37 pub fn escalate(self) -> Option<Tier> {
39 match self {
40 Tier::Worker => Some(Tier::Main),
41 Tier::Main => Some(Tier::Heavy),
42 Tier::Heavy => None,
43 }
44 }
45
46 pub fn downgrade(self) -> Option<Tier> {
48 match self {
49 Tier::Heavy => Some(Tier::Main),
50 Tier::Main => Some(Tier::Worker),
51 Tier::Worker => None,
52 }
53 }
54
55 pub fn spec(self) -> TierSpec {
57 match self {
58 Tier::Heavy => TierSpec {
59 model_id: HEAVY_MODEL,
60 reasoning_effort: HEAVY_REASONING_EFFORT,
61 },
62 Tier::Main => TierSpec {
63 model_id: MAIN_MODEL,
64 reasoning_effort: MAIN_REASONING_EFFORT,
65 },
66 Tier::Worker => TierSpec {
67 model_id: WORKER_MODEL,
68 reasoning_effort: WORKER_REASONING_EFFORT,
69 },
70 }
71 }
72}
73
74impl fmt::Display for Tier {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 let value = match self {
77 Tier::Heavy => "heavy",
78 Tier::Main => "main",
79 Tier::Worker => "worker",
80 };
81 f.write_str(value)
82 }
83}
84
85impl FromStr for Tier {
86 type Err = String;
87
88 fn from_str(s: &str) -> Result<Self, Self::Err> {
89 match s {
90 "heavy" => Ok(Tier::Heavy),
91 "main" => Ok(Tier::Main),
92 "worker" => Ok(Tier::Worker),
93 other => Err(format!("invalid tier {other}: use heavy, main, or worker")),
94 }
95 }
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
100pub struct TierSpec {
101 pub model_id: &'static str,
103 pub reasoning_effort: &'static str,
105}