diff --git a/docs/models.md b/docs/models.md index 0c4c95d..d4dfa0b 100644 --- a/docs/models.md +++ b/docs/models.md @@ -34,6 +34,8 @@ The catalog also lists model ids by provider or endpoint. `/login` and `/model` Saved provider profiles carry an `extension` tag. Existing profiles without one migrate as `openai`. Codex profiles use `extension: "codex"` and keep OAuth credentials separately in `$ALBEDO_HOME/auth.json`; see [model authentication](auth.md). +Codex metadata lookups select the `openai` provider in models.dev explicitly, including its reasoning effort tiers and context limits. `/effort`, session defaults, and model switches use this same selection; another provider publishing the same model id cannot replace OpenAI's metadata. No authenticated Codex model catalog is fetched. + ## antigravity models Antigravity is not on models.dev, and its model ids and required `model_enum` labels change server-side. The `antigravity` extension therefore contributes its own catalog. It asks Cloud Code Assist `fetchAvailableModels` which models the account can use, keeps the ids that the real client's agent picker shows, and caches them with their limits, labels, and the renames of retired ids in `$ALBEDO_HOME/antigravity.json`. The cache refreshes in the background after 6 hours, and the first `/login` listing waits for discovery. A built-in table supplies each model's thinking control and serves as the list before the first discovery. A saved profile that names a retired id follows the backend's rename. The catalog answers lookups only for the Antigravity endpoint, so models.dev keeps answering for the same ids at other providers. diff --git a/src/albedo/daemon/server.gleam b/src/albedo/daemon/server.gleam index c5d3e71..726df0a 100644 --- a/src/albedo/daemon/server.gleam +++ b/src/albedo/daemon/server.gleam @@ -9,6 +9,7 @@ import albedo/daemon/images import albedo/daemon/mail import albedo/daemon/reaper import albedo/daemon/session +import albedo/daemon/session_provider import albedo/daemon/store import albedo/daemon/usage import albedo/harness/command @@ -157,14 +158,13 @@ fn handle(state: State, message: Message) { "" -> provider.model _ -> model } - let efforts = case runtime.global(state.host) { - Ok(extensions) -> - case extension.model_info(extensions, model, "") { - Some(m_info) -> m_info.efforts - None -> [] - } - Error(_) -> [] - } + let efforts = + session_provider.model_efforts( + state.host, + state.config.home, + provider.name, + model, + ) let effort = extension.default_effort(efforts) let info = conversation.Info( diff --git a/src/albedo/daemon/session.gleam b/src/albedo/daemon/session.gleam index 47ac18e..c0e42f5 100644 --- a/src/albedo/daemon/session.gleam +++ b/src/albedo/daemon/session.gleam @@ -173,14 +173,8 @@ pub fn start( let info = case info.effort { Some(_) -> info None -> { - let efforts = case runtime.global(host) { - Ok(extensions) -> - case extension.model_info(extensions, info.model, "") { - Some(m_info) -> m_info.efforts - None -> [] - } - Error(_) -> [] - } + let efforts = + session_provider.model_efforts(host, home, info.provider, info.model) case extension.default_effort(efforts) { Some(def) -> { let _ = diff --git a/src/albedo/daemon/session_provider.gleam b/src/albedo/daemon/session_provider.gleam index fb981a1..9c7f24e 100644 --- a/src/albedo/daemon/session_provider.gleam +++ b/src/albedo/daemon/session_provider.gleam @@ -57,9 +57,23 @@ pub fn efforts_for_model( state: session_state.State(message), model: String, ) -> List(String) { - case runtime.global(state.host) { + model_efforts(state.host, state.home, state.info.provider, model) +} + +pub fn model_efforts( + host: runtime.Runtime, + home: String, + provider: String, + model: String, +) -> List(String) { + let endpoint = case configuration.named(home, provider) { + Ok(configured) if configured.extension == "codex" -> + "https://chatgpt.com/backend-api" + _ -> "" + } + case runtime.global(host) { Ok(extensions) -> - case extension.model_info(extensions, model, "") { + case extension.model_info(extensions, model, endpoint) { option.Some(info) -> info.efforts option.None -> [] } @@ -188,7 +202,7 @@ pub fn select( #(configured.name, configured.protocol) }) }) - let efforts = efforts_for_model(state, model) + let efforts = model_efforts(state.host, state.home, provider, model) let new_effort = case state.info.effort { option.Some(current) -> case list.contains(efforts, current) { diff --git a/src/albedo/harness/extensions/models/albedo_models.erl b/src/albedo/harness/extensions/models/albedo_models.erl index 217d53e..328cc44 100644 --- a/src/albedo/harness/extensions/models/albedo_models.erl +++ b/src/albedo/harness/extensions/models/albedo_models.erl @@ -325,6 +325,13 @@ resolve(#{index := Index, providers := Providers}, Model, Host) -> %% The configured endpoint decides between providers that publish one model id. %% Without a host match, agreeing candidates still answer and conflicting ones do not. +%% Codex subscription models use OpenAI's metadata, even though models.dev +%% does not publish the ChatGPT endpoint (or an API URL for OpenAI). +select(Candidates, _Providers, <<"chatgpt.com">>) -> + case [E || {<<"openai">>, _} = E <- Candidates] of + [Entry | _] -> {ok, Entry, <<"provider identity">>}; + [] -> error + end; select(Candidates, Providers, Host) -> case [E || {Name, _} = E <- Candidates, Host =/= <<>>, element(1, maps:get(Name, Providers)) =:= Host] of