diff --git a/README.md b/README.md index 227ed31..bb1f1b8 100644 --- a/README.md +++ b/README.md @@ -101,9 +101,10 @@ Use your `server.auth_token` if configured; otherwise `tinyllm` is a client placeholder. Choose models your account can access. The prefix is the provider table name; the rest is the upstream model ID, including any further slashes. Client reasoning effort and service tier override configured model defaults. -For a configured OpenAI GPT model, append `-fast` (for example, +For an OpenAI model ID beginning with `gpt-`, append `-fast` (for example, `openai/gpt-5.6-sol-fast`) to request the priority tier while sending the base model -upstream, overriding client and configured service tiers; subscription requests also +upstream, overriding client and configured service tiers; the base needs no model +table, matching how unlisted IDs are forwarded; subscription requests also carry Codex's routing hint. The suffix does not stack, and upstream decides what it delivers: responses may still report `default`. Fast/priority processing can consume more credits or cost more. diff --git a/src/providers/openai/compatible_tests.rs b/src/providers/openai/compatible_tests.rs index 4f8428b..9ebd788 100644 --- a/src/providers/openai/compatible_tests.rs +++ b/src/providers/openai/compatible_tests.rs @@ -301,7 +301,7 @@ fn replayed(details: &Value) -> Value { } #[test] -fn fast_suffix_resolves_only_configured_gpt_models() { +fn fast_suffix_resolves_gpt_models() { use super::super::models::{Config, ModelOptions, OpenAiAuth}; use crate::providers::Provider; let server = crate::config::Server::default(); @@ -319,15 +319,17 @@ fn fast_suffix_resolves_only_configured_gpt_models() { server, ) .unwrap(); - assert_eq!( - provider.synthetic_fast_base("gpt-6-astra-fast"), - Some("gpt-6-astra") - ); + for (native, base) in [ + ("gpt-6-astra-fast", "gpt-6-astra"), + // An unconfigured base still resolves; model tables are not an allowlist. + ("gpt-9-fast", "gpt-9"), + ] { + assert_eq!(provider.synthetic_fast_base(native), Some(base), "{native}"); + } for native in [ "gpt-5.4-fast", // configured upstream ID stays literal "gpt-5.4-fast-fast", // the suffix never stacks "o4-mini-fast", // not a gpt- model - "gpt-9-fast", // base is not configured "gpt-6-astra", ] { assert_eq!(provider.synthetic_fast_base(native), None, "{native}"); diff --git a/src/providers/openai/mod.rs b/src/providers/openai/mod.rs index 9302b59..3a98b13 100644 --- a/src/providers/openai/mod.rs +++ b/src/providers/openai/mod.rs @@ -47,10 +47,7 @@ impl OpenAiProvider { let base = native .strip_suffix("-fast") .filter(|b| !b.ends_with("-fast"))?; - (!self.config.models.contains_key(native) - && base.starts_with("gpt-") - && self.config.models.contains_key(base)) - .then_some(base) + (!self.config.models.contains_key(native) && base.starts_with("gpt-")).then_some(base) } fn model(&self, native: &str) -> Model { diff --git a/src/tests.rs b/src/tests.rs index 67d9dc1..04fe305 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -3269,7 +3269,7 @@ async fn openai_model_defaults_and_client_overrides_reach_each_endpoint() { ("gpt-5.4", "gpt-5.4", Some("low"), "low"), ("gpt-5.4", "gpt-5.4", None, "xhigh"), ("gpt-5.4-fast", "gpt-5.4", None, "xhigh"), - ("gpt-9-fast", "gpt-9-fast", Some("low"), "low"), + ("gpt-9-fast", "gpt-9", Some("low"), "low"), ] { let mut body = if path.ends_with("/responses") { json!({"input":"hello"}) @@ -3313,16 +3313,8 @@ async fn openai_model_defaults_and_client_overrides_reach_each_endpoint() { assert_eq!(response["service_tier"], "default"); } match native_model { - "gpt-5.4" => assert_eq!(upstream["service_tier"], "priority"), - // An unconfigured base forwards the name literally and keeps the client tier. - "gpt-9-fast" => assert_eq!( - upstream["service_tier"], - if path.starts_with("/anthropic") { - "default" - } else { - "flex" - } - ), + // An unconfigured base still resolves, so both take the fast tier. + "gpt-5.4" | "gpt-9" => assert_eq!(upstream["service_tier"], "priority"), _ => assert!(upstream.get("service_tier").is_none()), } } diff --git a/tinyllm.example.toml b/tinyllm.example.toml index 4e1fc09..2d41135 100644 --- a/tinyllm.example.toml +++ b/tinyllm.example.toml @@ -126,10 +126,10 @@ type = "Subscription" # not an allowlist. Unlisted native IDs are forwarded too. Quote IDs containing # dots or slashes. Model availability depends on the upstream account. # Clients select the main/small/subagent model; there are no configurable aliases. -# Each configured native ID beginning with gpt- also exposes a generated -fast -# name that sends the base model with priority. Exact configured names stay literal, -# so listing an upstream ID that itself ends in -fast keeps it verbatim; the suffix -# never stacks. +# Any native ID beginning with gpt- also accepts a generated -fast name that sends +# the base model with priority, configured or not; discovery lists it for configured +# IDs only. Exact configured names stay literal, so listing an upstream ID that itself +# ends in -fast keeps it verbatim; the suffix never stacks. [providers.openai.models."gpt-5.6-sol"] # Optional fallback when the client omits effort. Client reasoning controls win. # Accepted values: none, minimal, low, medium, high, xhigh, max. Providers map