Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions libs/code/deepagents_code/tui/widgets/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
ProviderAuthState,
ProviderAuthStatus,
clear_caches,
get_available_models,
get_base_url_env_var,
get_base_url_env_vars,
get_credential_env_var,
Expand Down Expand Up @@ -1879,19 +1878,28 @@ def _build_options_with_warning(self) -> tuple[list[Option], str | None]:
name for name, cfg in config.providers.items() if cfg.get("api_key_env")
}

# Only show well-known providers whose LangChain package is actually
# installed. `get_available_models` returns providers it could
# successfully import profiles for, so it doubles as an install
# gate. Stored and config-defined providers are always shown — even
# if the package was later uninstalled — so a stale credential can
# still be cleaned up and an explicitly-declared provider stays
# visible.
installed = set(get_available_models().keys())
well_known_installed = set(PROVIDER_API_KEY_ENV) & installed
# Show well-known providers whenever their LangChain package is
# installed, even if none of the package's model profiles pass dcode's
# tool-calling filter. Stored and config-defined providers are always
# shown too, so stale credentials can still be cleaned up and an
# explicitly-declared provider stays visible.
from deepagents_code.config_manifest import is_provider_package_installed

well_known_installed = {
provider
for provider in PROVIDER_API_KEY_ENV
if config.is_provider_enabled(provider)
and is_provider_package_installed(provider)
}
# `openai_codex` is gated on `langchain-openai` being installed (we
# surface it whenever `openai` was discovered) rather than on
# surface it whenever `openai` is available) rather than on
# `PROVIDER_API_KEY_ENV`, since it has no env var of its own.
codex_installed = {CODEX_PROVIDER} if "openai" in installed else set()
codex_installed = (
{CODEX_PROVIDER}
if "openai" in well_known_installed
and config.is_provider_enabled(CODEX_PROVIDER)
else set()
)
Comment thread
open-swe[bot] marked this conversation as resolved.

shown = well_known_installed | codex_installed | stored | config_providers
# Surface well-known providers whose package isn't installed yet as
Expand Down
142 changes: 52 additions & 90 deletions libs/code/tests/unit_tests/tui/widgets/test_auth_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2038,10 +2038,6 @@ async def test_configured_providers_sort_to_top(
"DEEPAGENTS_CODE_ANTHROPIC_API_KEY",
):
monkeypatch.delenv(var, raising=False)
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"], "anthropic": ["claude-opus-4-7"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda _provider: True,
Expand All @@ -2068,10 +2064,6 @@ async def test_configured_services_sort_with_configured_providers(
"DEEPAGENTS_CODE_TAVILY_API_KEY",
):
monkeypatch.delenv(var, raising=False)
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"anthropic": ["claude-opus-4-7"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda _provider: True,
Expand Down Expand Up @@ -2109,10 +2101,6 @@ async def test_uninstalled_providers_stay_below_configured(
"DEEPAGENTS_CODE_GROQ_API_KEY",
):
monkeypatch.delenv(var, raising=False)
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"], "anthropic": ["claude-opus-4-7"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider != "groq",
Expand Down Expand Up @@ -2148,10 +2136,6 @@ async def test_refresh_options_resorts_after_saving_key(
"DEEPAGENTS_CODE_ANTHROPIC_API_KEY",
):
monkeypatch.delenv(var, raising=False)
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"], "anthropic": ["claude-opus-4-7"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda _provider: True,
Expand Down Expand Up @@ -2199,15 +2183,6 @@ async def test_configured_group_preserves_alphabetical_order(
"DEEPAGENTS_CODE_MISTRAL_API_KEY",
):
monkeypatch.delenv(var, raising=False)
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {
"openai": ["gpt-5.4"],
"anthropic": ["claude-opus-4-7"],
"cohere": ["command"],
"mistralai": ["mistral-large"],
},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda _provider: True,
Expand Down Expand Up @@ -2244,10 +2219,6 @@ async def test_env_configured_provider_floats(
"DEEPAGENTS_CODE_ANTHROPIC_API_KEY",
):
monkeypatch.delenv(var, raising=False)
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"], "anthropic": ["claude-opus-4-7"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda _provider: True,
Expand Down Expand Up @@ -2343,34 +2314,27 @@ async def test_only_installed_well_known_providers_listed(
) -> None:
"""Installed providers show as live rows; uninstalled ones grey out.

When `openai` is "installed", `openai_codex` rides along — it shares
the same `langchain-openai` package, so the manager surfaces the
OAuth-backed twin alongside the API-key entry. With every known
package reported installed, no greyed install-on-select rows appear,
so the listing equals the installed set.
When `openai` is installed, `openai_codex` rides along because it shares
the same `langchain-openai` package. Every other known provider remains
a greyed install-on-select row below the live entries.
"""
# Pretend only `openai` and `anthropic` are installed.
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"], "anthropic": ["claude-opus-4-7"]},
)
# Report every known package installed so no greyed-out
# install-on-select rows are appended to the listing.
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda _provider: True,
lambda provider: provider in {"openai", "anthropic"},
)
app = _AuthHostApp()
async with app.run_test() as pilot:
app.show_manager()
await pilot.pause()
options = app.screen.query_one("#auth-manager-options", OptionList)
screen = cast("AuthManagerScreen", app.screen)
options = screen.query_one("#auth-manager-options", OptionList)
ids = {
options.get_option_at_index(i).id for i in range(options.option_count)
}
live_ids = ids - set(screen._install_extras)
# Non-model services (Tavily search, LangSmith tracing) are always
# listed for key entry.
assert ids == {
assert live_ids == {
"openai",
"openai_codex",
"anthropic",
Expand All @@ -2387,8 +2351,8 @@ async def test_selecting_service_opens_prompt_for_its_env_var(
would look up a credential env var the service doesn't have.
"""
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"]},
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider == "openai",
)
app = _AuthHostApp()
async with app.run_test() as pilot:
Expand All @@ -2413,8 +2377,8 @@ async def test_service_row_shows_stored_badge(
"""
auth_store.set_stored_key("tavily", "k")
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"]},
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider == "openai",
)
app = _AuthHostApp()
async with app.run_test() as pilot:
Expand All @@ -2438,8 +2402,8 @@ async def test_stored_provider_shown_even_when_uninstalled(
"""
auth_store.set_stored_key("groq", "k")
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"]},
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider == "openai",
)
app = _AuthHostApp()
async with app.run_test() as pilot:
Expand All @@ -2461,10 +2425,6 @@ async def test_uninstalled_known_provider_shown_greyed(
so `groq` is surfaced as a `[not installed]` install-on-select entry
for discoverability rather than being hidden.
"""
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"], "anthropic": ["claude-opus-4-7"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider in {"openai", "anthropic"},
Expand Down Expand Up @@ -2492,10 +2452,6 @@ async def test_selecting_uninstalled_provider_prompts_install(
InstallProviderConfirmScreen,
)

monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider == "openai",
Expand All @@ -2514,10 +2470,6 @@ async def test_confirming_install_records_extra_and_dismisses(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Confirming the install records the extra and dismisses the manager."""
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider == "openai",
Expand All @@ -2534,40 +2486,34 @@ async def test_confirming_install_records_extra_and_dismisses(
assert screen.pending_install_extra == "groq"
assert screen.pending_install_provider == "groq"

async def test_reopening_manager_highlights_initial_provider(
async def test_reopening_highlights_installed_provider_without_compatible_models(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Reopening after an install highlights the just-installed provider.
"""An installed provider stays live even when its profiles are filtered out.

Simulates the post-install reopen: `groq` is now installed and listed,
and passing it as `initial_provider` lands the cursor on its row rather
than resetting to index 0.
Perplexity currently declares no tool-calling models, so model discovery
excludes it. The auth manager must use package availability instead and
highlight it when reopening after the extra install.
"""
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"], "groq": ["llama-3"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider in {"openai", "groq"},
lambda provider: provider in {"openai", "perplexity"},
)
app = _AuthHostApp()
async with app.run_test() as pilot:
app.show_manager(initial_provider="groq")
app.show_manager(initial_provider="perplexity")
await pilot.pause()
screen = cast("AuthManagerScreen", app.screen)
options = screen.query_one("#auth-manager-options", OptionList)
assert options.highlighted is not None
assert options.get_option_at_index(options.highlighted).id == "groq"
selected = options.get_option_at_index(options.highlighted)
assert selected.id == "perplexity"
assert "not installed" not in str(selected.prompt)

async def test_reopening_manager_ignores_unknown_initial_provider(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""An initial provider absent from the list leaves the cursor at the top."""
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider == "openai",
Expand All @@ -2584,10 +2530,6 @@ async def test_cancelling_install_leaves_manager_without_pending_extra(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Declining the install records nothing and keeps the user on the manager."""
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider == "openai",
Expand Down Expand Up @@ -2621,10 +2563,6 @@ async def test_disabled_known_provider_not_offered_for_install(
""")
monkeypatch.setattr(model_config, "DEFAULT_CONFIG_PATH", config_path)
model_config.clear_caches()
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"], "anthropic": ["claude-opus-4-7"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider in {"openai", "anthropic"},
Expand All @@ -2651,10 +2589,6 @@ async def test_known_provider_without_extra_not_offered_for_install(
so the provider must not appear as an install-on-select row pointing at
a `None` extra.
"""
monkeypatch.setattr(
"deepagents_code.tui.widgets.auth.get_available_models",
lambda: {"openai": ["gpt-5.4"]},
)
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider != "groq",
Expand Down Expand Up @@ -2737,6 +2671,34 @@ async def test_codex_option_visible_when_openai_installed(self) -> None:
}
assert "openai_codex" in ids

async def test_disabled_codex_option_hidden(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""A disabled `openai_codex` provider stays out of the auth manager."""
config_path = tmp_path / "config.toml"
config_path.write_text("""
[models.providers.openai_codex]
enabled = false
""")
monkeypatch.setattr(model_config, "DEFAULT_CONFIG_PATH", config_path)
model_config.clear_caches()
monkeypatch.setattr(
"deepagents_code.config_manifest.is_provider_package_installed",
lambda provider: provider == "openai",
)

app = _AuthHostApp()
async with app.run_test() as pilot:
app.show_manager()
await pilot.pause()
options = app.screen.query_one("#auth-manager-options", OptionList)
ids = {
options.get_option_at_index(i).id for i in range(options.option_count)
}

assert "openai" in ids
assert "openai_codex" not in ids

async def test_codex_badge_reflects_signed_out_state(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
Expand Down
Loading