Reject unknown gateway provider ids and surface unavailable alias reasons - #599
Conversation
…sons Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
Tested this end-to-end at the CLI level against a fresh gateway root: Invalid provider id now fails closed Canonical Partial-failure startup diagnosability (two openai-compatible providers, one with a missing credential env, one alias granted on each): JSON receipt now names the broken alias and exact reason: {"operation":"gateway.run","status":"ready","engine":"rust", ...,
"unavailable_aliases":[{"alias":"brokenalias","reason":"connection credential environment variable 'MISSING_KEY_XYZ' is not set and no stored credential exists for connection 'broken'"}]}Human startup prints the warning: Regression: healthy alias still servesAll assertions passed. |
Greptile SummaryThe PR rejects provider identifiers that cannot produce gateway deployments and exposes partially unavailable aliases in startup diagnostics.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| exp/runtime/gateway/management.py | Adds shared fail-closed validation against the registry-supported, gateway-servable provider set. |
| exp/runtime/gateway/sqlite/platform.py | Applies the shared provider validation to public platform upserts while preserving disable behavior. |
| exp/common/models/gateway_catalog.py | Exports and reuses the provider exclusion set so admission and normalization agree about Tinker. |
| exp/runtime/gateway/lifecycle.py | Retains unavailable alias reasons in loaded authority state and propagates them through local gateway composition. |
| exp/cli/gateway/serve.py | Adds unavailable alias details to both engine receipts and emits literal, markup-disabled human warnings. |
| exp/runtime/models/registry.py | Promotes the runtime-supported provider set to a public constant without changing registry membership. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Provider mutation] --> B{Provider is registry-supported and gateway-servable?}
B -- No --> C[Reject configuration]
B -- Yes --> D[Persist provider authority]
D --> E[Load granted aliases]
E --> F{Alias loads successfully?}
F -- Yes --> G[Publish ready route]
F -- No, while another route is ready --> H[Publish unavailable alias reason]
F -- No routes ready --> I[Fail gateway startup]
Reviews (4): Last reviewed commit: "Keep the platform adapter within the han..." | Re-trigger Greptile
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Summary
exp config gateway provider add NAME --provider openai_compatible(underscore) previously stored the bogus provider id,alias createsnapshotted a catalog referencing it, and the alias then failed silently at gateway startup: every request answered503 {"code":"unavailable_route"}with no hint of the misconfiguration.Two changes, both driven by the runtime registry's single authoritative provider set:
_SUPPORTED_PROVIDERSis now exported asSUPPORTED_PROVIDERSfromexp.runtime.models, and provider admission fails closed on any id the gateway cannot serve, listing every servable provider. Servable meansSUPPORTED_PROVIDERS - GATEWAY_EXCLUDED_PROVIDERS, where the exclusion set ({"tinker"}) is exported fromexp.common.models.gateway_catalogand reused bynormalize_gateway_catalog, so admission and deployment normalization cannot drift. The check lives in a sharedrequire_gateway_servable_providerinmanagement.pyand is enforced at both mutation seams:GatewayManagement.upsert_provider_connection(coversprovider add,provider update, and other management callers) andSQLiteGatewayPlatform.mutate_provider_connection(the exported platform API). The CLI's existingusage_error(ValueError, ...)context turns theGatewayStoreErrorinto a clear parameter error:_load_alias_statekeeps its(alias, reason)failures on_AliasAuthorityState.unavailable_aliases, which flows toLocalGatewayComponents.unavailable_aliasesandLocalGatewayRuntime.unavailable_aliases.expserve (both python and rust engines) emits each failed alias in the JSON startup receipt asunavailable_aliases: [{alias, reason}]and in human output as a literal (markup-disabled) warning naming the alias, its503 unavailable_routebehavior, and the exact reason. The all-aliases-failed path still raisesGatewayLifecycleErroras before.lifecycle.pyandplatform.pysit at the 999-line hand-authored limit, so a few adjacent blocks were condensed (shared_authority_keyhelper, single-line message joins, tightened docstrings) without behavior change.Regression tests:
provider_test.py(CLI add/update rejection and accept path),management_test.py(fail-closed upsert, error lists every servable provider, tinker rejected),platform_test.py(platform mutation rejects tinker and persists nothing),lifecycle_test.py(partial startup exposes each failed alias with its reason),serve_test.py(receipt shaping and human warning output).Note:
terminal_tasks_live_pipeline_test.pyandrelease_test.py::test_installed_wheel_no_spend_release_evidencefail in this dev environment on clean main too (localAZURE_OPENAI_ENDPOINTbinding); unrelated to this change. All other gates pass: ruff check, ruff format, ty, pytest.Link to Devin session: https://app.devin.ai/sessions/8ffbe9ab84124f65a44b7eceb5f3879b
Requested by: @kfallah