agcodex_core/
model_family.rs

1/// A model family is a group of models that share certain characteristics.
2#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3pub struct ModelFamily {
4    /// The full model slug used to derive this model family, e.g.
5    /// "gpt-4.1-2025-04-14".
6    pub slug: String,
7
8    /// The model family name, e.g. "gpt-4.1". Note this should able to be used
9    /// with [`crate::openai_model_info::get_model_info`].
10    pub family: String,
11
12    /// True if the model needs additional instructions on how to use the
13    /// "virtual" `apply_patch` CLI.
14    pub needs_special_apply_patch_instructions: bool,
15
16    // Whether the `reasoning` field can be set when making a request to this
17    // model family. Note it has `effort` and `summary` subfields (though
18    // `summary` is optional).
19    pub supports_reasoning_summaries: bool,
20
21    // This should be set to true when the model expects a tool named
22    // "local_shell" to be provided. Its contract must be understood natively by
23    // the model such that its description can be omitted.
24    // See https://platform.openai.com/docs/guides/tools-local-shell
25    pub uses_local_shell_tool: bool,
26
27    /// True if the model performs better when `apply_patch` is provided as
28    /// a tool call instead of just a bash command.
29    pub uses_apply_patch_tool: bool,
30}
31
32macro_rules! model_family {
33    (
34        $slug:expr, $family:expr $(, $key:ident : $value:expr )* $(,)?
35    ) => {{
36        // defaults
37        let mut mf = ModelFamily {
38            slug: $slug.to_string(),
39            family: $family.to_string(),
40            needs_special_apply_patch_instructions: false,
41            supports_reasoning_summaries: false,
42            uses_local_shell_tool: false,
43            uses_apply_patch_tool: false,
44        };
45        // apply overrides
46        $(
47            mf.$key = $value;
48        )*
49        Some(mf)
50    }};
51}
52
53macro_rules! simple_model_family {
54    (
55        $slug:expr, $family:expr
56    ) => {{
57        Some(ModelFamily {
58            slug: $slug.to_string(),
59            family: $family.to_string(),
60            needs_special_apply_patch_instructions: false,
61            supports_reasoning_summaries: false,
62            uses_local_shell_tool: false,
63            uses_apply_patch_tool: false,
64        })
65    }};
66}
67
68/// Returns a `ModelFamily` for the given model slug, or `None` if the slug
69/// does not match any known model family.
70pub fn find_family_for_model(slug: &str) -> Option<ModelFamily> {
71    if slug.starts_with("o3") {
72        model_family!(
73            slug, "o3",
74            supports_reasoning_summaries: true,
75        )
76    } else if slug.starts_with("o4-mini") {
77        model_family!(
78            slug, "o4-mini",
79            supports_reasoning_summaries: true,
80        )
81    } else if slug.starts_with("agcodex-mini-latest") {
82        model_family!(
83            slug, "agcodex-mini-latest",
84            supports_reasoning_summaries: true,
85            uses_local_shell_tool: true,
86        )
87    } else if slug.starts_with("agcodex-") {
88        model_family!(
89            slug, slug,
90            supports_reasoning_summaries: true,
91        )
92    } else if slug.starts_with("gpt-4.1") {
93        model_family!(
94            slug, "gpt-4.1",
95            needs_special_apply_patch_instructions: true,
96        )
97    } else if slug.starts_with("gpt-oss") {
98        model_family!(slug, "gpt-oss", uses_apply_patch_tool: true)
99    } else if slug.starts_with("gpt-4o") {
100        simple_model_family!(slug, "gpt-4o")
101    } else if slug.starts_with("gpt-3.5") {
102        simple_model_family!(slug, "gpt-3.5")
103    } else if slug.starts_with("gpt-5") {
104        model_family!(
105            slug, "gpt-5",
106            supports_reasoning_summaries: true,
107        )
108    } else {
109        None
110    }
111}