From 0974f640bb9b933c3892796b24f70f8be5a96669 Mon Sep 17 00:00:00 2001 From: Jacob Date: Wed, 30 Sep 2026 02:34:36 -0400 Subject: [PATCH] feat: specify reasoning effort in model ID, or use a default --- Cargo.lock | 365 ++++++++++++++++++++++++++++++++-- Cargo.toml | 3 + src/openai/chat/completion.rs | 9 +- src/server.rs | 82 +++++++- 4 files changed, 428 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97021f3..f630bfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,15 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "1.0.0" @@ -282,6 +291,12 @@ version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.13.2" @@ -318,6 +333,15 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bumpalo" version = "3.20.3" @@ -359,6 +383,18 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + [[package]] name = "clap" version = "4.6.7" @@ -587,8 +623,18 @@ version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + +[[package]] +name = "darling" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" +dependencies = [ + "darling_core 0.24.1", + "darling_macro 0.24.1", ] [[package]] @@ -604,28 +650,86 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "darling_core" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 3.0.6", +] + [[package]] name = "darling_macro" version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ - "darling_core", + "darling_core 0.21.3", "quote", "syn 2.0.119", ] +[[package]] +name = "darling_macro" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" +dependencies = [ + "darling_core 0.24.1", + "quote", + "syn 3.0.6", +] + [[package]] name = "data-encoding" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + [[package]] name = "deranged" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] [[package]] name = "digest" @@ -735,7 +839,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bd536557b58c682b217b8fb199afdff47cd3eff260623f19e77074eb073d63a" dependencies = [ - "darling", + "darling 0.21.3", "proc-macro2", "quote", "syn 2.0.119", @@ -950,13 +1054,19 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap", + "indexmap 2.14.2", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.17.1" @@ -993,6 +1103,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hickory-net" version = "0.26.3" @@ -1169,6 +1285,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.3.0" @@ -1279,6 +1419,17 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.14.2" @@ -1286,7 +1437,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.17.1", + "serde", + "serde_core", ] [[package]] @@ -1326,12 +1479,75 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1baf72f08796de0260609515130699b890ac25f30e610ad894bc5856cafdb" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" +dependencies = [ + "defmt", + "log", +] + +[[package]] +name = "jiff-static" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "378268a1116ad67ae6228701118ac9f491d78fda38a40a1f1a9e1348de6f7212" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jni" version = "0.22.4" @@ -1415,16 +1631,19 @@ dependencies = [ "enumset", "futures", "headers", - "indexmap", + "indexmap 2.14.2", + "itertools 0.15.0", "mitsein", "rand 0.10.3", "reqwest", "reqwest-sse", "rustls", - "schemars", + "schemars 1.2.2", "secrecy", "serde", "serde_json", + "serde_with", + "strum", "tokio", "tracing", "tracing-subscriber", @@ -1537,9 +1756,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a49aef75c004fb496a23f175395415ecfe2a630e4c6175ae6f7d9cc1a19332e" dependencies = [ "either", - "indexmap", - "itertools", - "schemars", + "indexmap 2.14.2", + "itertools 0.14.0", + "schemars 1.2.2", "serde", "smallvec", ] @@ -1689,6 +1908,15 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" +[[package]] +name = "portable-atomic-util" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.6" @@ -1822,7 +2050,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.13.2", ] [[package]] @@ -2004,7 +2232,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2055,6 +2283,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schemars" version = "1.2.2" @@ -2062,7 +2302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ "dyn-clone", - "indexmap", + "indexmap 2.14.2", "ref-cast", "schemars_derive", "serde", @@ -2103,7 +2343,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", + "bitflags 2.13.2", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -2174,7 +2414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" dependencies = [ "form_urlencoded", - "indexmap", + "indexmap 2.14.2", "itoa", "ryu", "serde_core", @@ -2216,6 +2456,39 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9adc193c780ef8f159aee8b61e2d5801aaa555e6eb0947fe45530ec506296f" +dependencies = [ + "base64 0.23.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.2", + "jiff", + "schemars 0.9.0", + "schemars 1.2.2", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e17bbc68e28663bbbb90df47e058aa7eda4fb445b89fe70457bb94fbccf6e49" +dependencies = [ + "darling 0.24.1", + "proc-macro2", + "quote", + "syn 3.0.6", +] + [[package]] name = "sha1" version = "0.10.7" @@ -2304,6 +2577,27 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "subtle" version = "2.6.1" @@ -2358,7 +2652,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags", + "bitflags 2.13.2", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -2550,7 +2844,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "async-compression", - "bitflags", + "bitflags 2.13.2", "bytes", "futures-core", "futures-util", @@ -2890,6 +3184,41 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "windows-link" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index f82d555..8be79b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ enumset = { version = "1.1.14", features = ["serde"] } futures = "0.3.34" headers = "0.4.2" indexmap = "2.14.2" +itertools = "0.15.0" mitsein = { version = "0.9.0", features = ["indexmap"] } rand = "0.10.3" reqwest = { version = "0.13.5", features = ["brotli", "cookies", "deflate", "form", "gzip", "hickory-dns", "json", "multipart", "query", "rustls-no-provider", "socks", "stream", "zstd"], default-features = false } @@ -24,6 +25,8 @@ schemars = "1.2.2" secrecy = { version = "0.10.3", features = ["serde"] } serde = { version = "1.0.229", features = ["derive"] } serde_json = "1.0.151" +serde_with = "3.24.0" +strum = { version = "0.28.0", features = ["derive"] } tokio = { version = "1.53.1", features = ["macros", "rt-multi-thread"] } tracing = "0.1.44" tracing-subscriber = { version = "0.3.23", features = ["env-filter"] } diff --git a/src/openai/chat/completion.rs b/src/openai/chat/completion.rs index 855db17..ade639a 100644 --- a/src/openai/chat/completion.rs +++ b/src/openai/chat/completion.rs @@ -1,4 +1,6 @@ use serde::{Deserialize, Serialize}; +use serde_with::{DeserializeFromStr, SerializeDisplay}; +use strum::{EnumString, VariantArray}; use std::collections::HashMap; // ============================================================================ @@ -916,8 +918,8 @@ pub enum PromptCacheRetention { } /// Constrains reasoning effort for reasoning models. -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(rename_all = "lowercase")] +#[derive(Clone, Debug, EnumString, DeserializeFromStr, strum::Display, SerializeDisplay, VariantArray)] +#[strum(serialize_all = "lowercase")] pub enum ReasoningEffort { /// Disable reasoning. None, @@ -930,7 +932,8 @@ pub enum ReasoningEffort { /// High reasoning effort. High, /// Extra high reasoning effort. - Xhigh, + #[strum(serialize = "xhigh")] + ExtraHigh, /// Maximum reasoning effort. Max, } diff --git a/src/server.rs b/src/server.rs index 969248c..9a77d4d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -10,9 +10,9 @@ use axum::response::Sse; use axum_extra::routing::RouterExt; use axum_extra::routing::TypedPath; use ecow::EcoString; -use enumset::{EnumSet, EnumSetType}; use headers::Header; use headers::authorization::Bearer; +use itertools::iproduct; use mitsein::vec1::Vec1; use rand::Rng; use rand::RngExt; @@ -21,7 +21,14 @@ use secrecy::ExposeSecret; use secrecy::SecretString; use serde::Deserialize; use serde::Serialize; +use serde_with::DeserializeFromStr; +use serde_with::SerializeDisplay; +use strum::EnumString; +use strum::VariantArray; use std::collections::VecDeque; +use std::convert::Infallible; +use std::fmt::Display; +use std::str::FromStr; use std::sync::Arc; use typed_builder::TypedBuilder; use url::Url; @@ -51,8 +58,8 @@ enum Provider { OpenRouter, } -#[derive(Debug, Serialize, Deserialize, EnumSetType)] -#[serde(rename_all = "lowercase")] +#[derive(Clone, Debug, EnumString, DeserializeFromStr, strum::Display, SerializeDisplay, VariantArray)] +#[strum(serialize_all = "lowercase")] enum Shortcut { XS, S, @@ -61,6 +68,43 @@ enum Shortcut { XL, } + +#[derive(Debug, Clone, DeserializeFromStr, SerializeDisplay)] +struct ShortcutAndMaybeReasoning(Shortcut, Option); + +impl FromStr for ShortcutAndMaybeReasoning { + type Err = Infallible; // TODO + + fn from_str(s: &str) -> Result { + if let Some((model, reasoning)) = s.split_once(":") { + let model = model.parse().expect("TODO"); + let reasoning = reasoning.parse().expect("TODO"); + + Ok(Self(model, Some(reasoning))) + } else { + let model = s.parse().expect("TODO"); + let reasoning = None; + + Ok(Self(model, reasoning)) + } + } +} + +impl Display for ShortcutAndMaybeReasoning { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self(model, reasoning) = self; + + write!(f, "{model}")?; + if let Some(reasoning) = reasoning { + write!(f, ":")?; + write!(f, "{reasoning}")?; + } + + Ok(()) + } +} + + impl Shortcut { fn suggestions(&self) -> Vec<(Provider, EcoString)> { use Provider as P; @@ -133,6 +177,19 @@ impl Shortcut { ], } } + + fn default_reasoning(&self) -> openai::chat::completion::ReasoningEffort { + use Shortcut as S; + use openai::chat::completion::ReasoningEffort as R; + + match self { + S::XS => R::None, + S::S => R::Minimal, + S::M => R::Low, + S::L => R::Medium, + S::XL => R::High, + } + } } fn take_random_by_swapping(mut vec1: Vec1, mut rng: impl Rng) -> (T, Option>) { @@ -202,12 +259,12 @@ impl IntoResponse for ChatCompletionError { async fn create_chat_completion( _: V1ChatCompletionsPath, State(state): State, - Json(request): Json>, + Json(request): Json>, ) -> Result { - let suggestions = request.model.suggestions(); + let ShortcutAndMaybeReasoning(shortcut, reasoning_in_id) = request.model.clone(); let mut model_upstreams = VecDeque::new(); - for (provider, model) in suggestions { + for (provider, model) in shortcut.suggestions() { let upstreams = match provider { Provider::CloudflareWorkersAi => vec![], // TODO Provider::GoogleAiStudio => state @@ -329,6 +386,8 @@ async fn create_chat_completion( web_search_options, } = request.clone(); + let reasoning_effort = Some(reasoning_effort.or(reasoning_in_id.clone()).unwrap_or(shortcut.default_reasoning())); + let request = openai::chat::completion::CreateChatCompletionRequest { model, messages, @@ -455,11 +514,14 @@ async fn create_chat_completion( struct V1ModelsPath; #[tracing::instrument] -async fn list_models(_: V1ModelsPath) -> Json> { - let models = EnumSet::all() +async fn list_models(_: V1ModelsPath) -> Json> { + let shortcuts = Shortcut::VARIANTS.into_iter().cloned(); + let reasoning_efforts = openai::chat::completion::ReasoningEffort::VARIANTS.into_iter().cloned().map(Some).chain(None); + + let models = iproduct!(shortcuts, reasoning_efforts) .into_iter() - .map(|shortcut| openai::models::Model { - id: shortcut, + .map(|(shortcut, reasoning)| openai::models::Model { + id: ShortcutAndMaybeReasoning(shortcut, reasoning), created: 0, object: openai::models::ModelObject::Model, owned_by: "nobody".into(),