Skip to content

fix: discover models from an allowlisted configured gateway - #873

Closed
seonghobae wants to merge 1 commit into
fix/gateway-default-chat-modelfrom
fix/configured-gateway-discovery
Closed

fix: discover models from an allowlisted configured gateway#873
seonghobae wants to merge 1 commit into
fix/gateway-default-chat-modelfrom
fix/configured-gateway-discovery

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Problem

LLM_GATEWAY_API_URL / LLM_GATEWAY_URL and LLM_GATEWAY_API_KEY in the trusted bootstrap environment were not connected to any ProviderModelSource. The live /v1/models endpoint listed seven chat models plus one embedding model, but discover-models ignored that gateway entirely.

The same live endpoint also exposes a Sectigo chain trusted by certifi but absent from this uv Python installation system CA file, so a correctly wired source initially failed TLS with unable to get local issuer certificate.

Fix

  • Build one OpenAI-compatible source only when the configured HTTPS hostname is present in CONTEXTUAL_ORCHESTRATOR_ALLOWED_PROVIDER_HOSTS.
  • Promote LLM_GATEWAY_API_KEY into the credential KV during the one-shot bootstrap boundary; runtime discovery reads it back through get_credential.
  • Keep the full paid/unknown/free chat catalog. --free-only still requires structured zero-price evidence; unknown pricing is never labeled free.
  • Retry only certificate-verification failures with certifi trust, keeping hostname and certificate verification enabled.

Evidence

  • Focused discovery/bootstrap suite: 54 passed.
  • Broader provider bootstrap suite before the TLS follow-up: 113 passed.
  • Live configured gateway: discovered_count=7, providers_with_errors=[]; models include gpt-4.1, gpt-4.1-mini, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, and gpt-5.4.
  • Live gateway supplies no structured pricing, so free_tier_count=0 and priced_count=0 are intentionally honest.

Stacked on #868 because that PR defines orchestrator/auto, orchestrator/free, gateway-default, and omitted-model serving semantics.


Open in Devin Review

@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a4d42777-2102-4945-bcf8-3c58bab59eba

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Closing and replacing this PR because an organization-specific hostname appeared in an initial test fixture. The replacement will contain only generic fixtures and corrected model-info pricing evidence.

@seonghobae seonghobae closed this Aug 26, 2026
@seonghobae
seonghobae deleted the fix/configured-gateway-discovery branch August 26, 2026 10:09

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Open in Devin Review

Comment on lines +105 to +113
allowed_hosts = {
host.strip().casefold()
for host in environ.get(
"CONTEXTUAL_ORCHESTRATOR_ALLOWED_PROVIDER_HOSTS", ""
).split(",")
if host.strip()
}
if parsed.hostname.casefold() not in allowed_hosts:
raise ValueError("LLM gateway host must be present in the provider allowlist")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Discovery and runtime use two different host allowlists

configured_gateway_source allowlists the gateway host from the CONTEXTUAL_ORCHESTRATOR_ALLOWED_PROVIDER_HOSTS env var, but runtime egress uses the --allowed-provider-host CLI list (contextual_orchestrator/orchestrator.py:1630). With --auto-discover-model-agents, discovered gateway agents are created enabled (main.py); a CLI list that omits the gateway host then refuses routing to them at request time. Setting only the CLI list makes startup abort with ValueError.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +85 to +93
values = {
value.strip().rstrip("/")
for name in ("LLM_GATEWAY_API_URL", "LLM_GATEWAY_URL")
if isinstance((value := environ.get(name)), str) and value.strip()
}
if not values:
return None
if len(values) != 1:
raise ValueError("LLM gateway URL settings must identify the same endpoint")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Differently-written but equivalent gateway URLs get rejected

The URL set is normalized only by stripping whitespace and a trailing slash before len(values) != 1 is enforced; the /v1 canonicalization happens afterward. Setting LLM_GATEWAY_API_URL=https://host and LLM_GATEWAY_URL=https://host/v1, which resolve to the same endpoint, produces two values and raises, blocking discovery.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread pyproject.toml
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"certifi>=2024.8.30",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: certifi added to pyproject without a lockfile regeneration in the diff

pyproject.toml promotes certifi>=2024.8.30 to a direct dependency. requirements.lock already pins certifi==2026.7.22 transitively and satisfies the constraint, so hash-locked installs still work and nothing breaks; only the direct-vs-transitive classification in the lock may be stale.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant