feat(ai): add ai_model_release feature flag
What this does
Defines the generic ai_model_release feature flag, default off, and pushes it to the AI Gateway from the request paths that resolve models: Duo Chat, Code Suggestions, Duo Workflow, and the model-definitions request that feeds Model Selection.
This is Phase 0 item 4 of the day-zero model release work described in ai-assist#2522. The AI Gateway side is ai-assist!6499, merged 2026-08-25. Until this MR lands, that mechanism is inert — the gateway looks for a flag nothing sends.
How it works
The gateway reads ai_model_release per request from the x-gitlab-enabled-feature-flags header to decide whether env-injected model definitions (AIGW_MODEL_SELECTION__MODEL_RELEASES) are resolvable.
- Flag off: env-injected models are invisible.
- Flag on: they resolve and appear in Model Selection.
The check is per request because the gateway's ModelSelectionConfig is a process-wide cached singleton, so a load-time check would need a restart to take effect.
Model resolution happens on two kinds of request, and both need the flag:
- Inference — Duo Chat, Code Suggestions, Duo Workflow. Without the flag here, selecting the model raises
ValueErrorin the gateway. - Model list —
FetchModelDefinitionsService, which serves Model Selection. Without the flag here the model never appears in the UI.
How to test
The gateway side is already on main, so this is testable end to end against a local AI Gateway.
Start the gateway with a placeholder model injected:
export AIGW_AUTH__BYPASS_EXTERNAL=true
export AIGW_FASTAPI__METRICS_PORT=8099
export AIGW_MODEL_SELECTION__MODEL_RELEASES='{"models":[{"name":"Embargo Test Model","gitlab_identifier":"test_embargo_model","model_class_provider":"litellm","max_context_tokens":200000,"cost_indicator":"$$","description":"Placeholder.","params":{"model":"fake_provider/fake-model-001","custom_llm_provider":"vertex_ai"}}],"feature_attachments":{"duo_chat":{"selectable_models":["test_embargo_model"],"beta_models":[],"default_models":[]}}}'
poetry run uvicorn "ai_gateway.app:get_app" --factory --port 5099Then, with Feature.enable(:ai_model_release) on and off, confirm test_embargo_model appears in and disappears from Model Selection.
The gateway gate is per request, so the gateway itself needs no restart between the two checks. On the monolith side the model-definitions response is cached for 30 minutes, so cache_key includes the flag state — toggling the flag lands on a different key and takes effect immediately rather than waiting for expiry.
Rollout
Flag is default_enabled: false, type gitlab_com_derisk. Rollout issue: #626874.
Note for whoever runs the first release: ai_model_release is already listed in the gateway's AIGW_FEATURE_FLAGS__DISALLOWED_FLAGS for the self-managed realm, so enabling it does not expose an embargoed model to self-managed instances. That only gates the pre-launch env-injection path — post-launch the model moves into models.yml and is served ungated.