Files
roshan bf62f4bf65 feat(admin): add opt-in Decision Models configuration (#16276)
## Summary

Add Admin > Decision Models for TypeSafe AI, OpenAI, and OpenRouter.
Decision-model selection starts unset and never falls back to the chat
default.

Decision providers have dedicated save and credential-test endpoints.
Their request accepts connection fields and explicit
`selected_model_names`. It rejects chat settings. The database operation
changes decision selections while preserving shared chat settings,
visibility, defaults, and access.

The dedicated decision form separates catalog capability from saved
selection. Catalog refresh and auto-mode synchronization do not select
additional decision models. Clear the selected workspace decision model
before removing its model or provider.

Remove decision-scope flags and branches from the shared chat provider
forms and updates. Shared model settings remain on Language Models. No
classification or review workflow ships in this stack.

## Validation

- 247 related backend unit tests passed, including stored-capability
preservation.
- 15 tests against isolated PostgreSQL passed.
- 24 frontend component tests and full web type checks passed.
- Live requests through the frontend proxy verified selection changes,
chat preservation, and rejection of chat settings.
- All applicable pre-commit hooks passed, including Python and
TypeScript types, Ruff, oxlint, oxfmt, secret scanning, and feature-map
integrity.
- The standard local Playwright setup requires paid group APIs
unavailable on this local deployment. The browser test did not reach its
assertions.

## Stack testing (`29eec61fb4`)

Tested the full stack at the top commit on a local dev stack, desktop
and 390×844 mobile. OpenAI decisions and chat used a real key. TypeSafe,
Zoom, OpenRouter, and Vercel AI Gateway used local mock upstreams; Onyx
auth, validation, persistence, encryption, and UI were real.

- Passed: decision selection starts **Not configured**, independent of
the chat default.
- Passed: live OpenAI decision validation saved `gpt-6-luna`. OpenRouter
discovery and validation worked; bad keys fail visibly.
- Passed: decision saves keep shared chat settings, visibility,
defaults, and access (DB before/after compared).
- Passed: save/test endpoints reject chat settings (422). Catalog
refresh and chat auto-mode do not add decision selections.
- Passed: model and provider removal are blocked until the workspace
selection is cleared.
- Passed (regression): live GPT-5 Mini chat works; decision-only models
are absent from the chat picker.
- **Fixed (`37c248ec04`):** two UI bugs found here. See *Fix retest*
below.

![Decision Models desktop and
mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS82ZTdiZjg3My1kNWYwLTQyOTMtYjk0ZS03NWM1OTFiMDdhMTYiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJkZWNpc2lvbi1tb2RlbHMtZGVza3RvcC1tb2JpbGUucG5nIn0.y0LxVR9yzp-2FviQg6sUUSc79oyekW7PhO9jd1sA2U0)

![OpenAI decision
provider](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9lYmNlMWNiMC1lZmVkLTQyNDQtYWU5Zi1hNjQ0NWM4MDAzYjciLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJvcGVuYWktZGVza3RvcC1tb2JpbGUucG5nIn0.viITouz1WwL-KPGjLAOOxhO2O8OH78a0-f9Ww6IhJHg)

![OpenRouter decision
provider](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8wMTQzMzRlYS1iNjE0LTQ1ZjktOGM3MC1hMWE5ZDAyMWU2NWUiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJvcGVucm91dGVyLWRlc2t0b3AtbW9iaWxlLnBuZyJ9.-k_vhxArLC0sn-efnND-4PrdBaCXCbvXIyODhgYM2O0)

![Live OpenAI decision
flow](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81NjEyY2VlZC0zMDEyLTRhNDUtYjhiZi1jMWUwNTVjN2EwNDgiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJvcGVuYWktZGVjaXNpb24tZmxvdy53ZWJwIn0.wnYGxw94Kj9NClKTgrANKSLib0ToOs1eROCqesZAdfs)

![Removal
guard](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS82NGUxM2JjOC04YjMwLTRlNmEtYjNhYy00ZDcxOWIxZDMyYTIiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJkZXNrdG9wLWRlbGV0ZS1ndWFyZC5wbmcifQ.QD54xAub9H33NzJCgbJhnJkKI36XdfjvRPFRyu4VUqo)

![Live chat
regression](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9lODJlNGM5Zi0xZjBiLTQxYzAtYTNjZS00M2E0YTFhNmU4ZjMiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJyZWdyZXNzaW9uLWxpdmUtb3BlbmFpLWNoYXQucG5nIn0._YvK1bOI35c1BUkJ8Ubdlzxyx2K7FQs_CfPhqWs44Vo)

## Fix retest (`78cf4ccb12`)

`37c248ec04` fixes two bugs found by the stack test:
- Decision Models showed a deleted provider (e.g. Zoom removed on Voice)
as Configure/Delete until a full reload. `useAdminLanguageModels` now
takes `{ revalidateIfStale }`; Decision Models passes `true`, so the
cached provider list refetches on mount.
- Configure headings rendered literal asterisks (`Configure *Vercel AI
Gateway*`). `DecisionProviderModal` now wraps the title in `markdown()`,
like the shared LLM modal.

Retested in the UI, no page reloads:
- Passed: Zoom connected on Voice and Decision Models, selection
cleared, Zoom disconnected on Voice. Sidebar navigation to Decision
Models showed **Connect**.
- Passed: a Gateway connection made on Language Models showed
**Configure** on Decision Models.
- Passed: Gateway and OpenAI Configure headings show no asterisks.
- Passed: both pages load, and display-name edits save and persist.
- Jest: new regression test for a stale cached provider list, and a
heading assertion.

![Zoom card before and after Voice
disconnect](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS82MzUxZWYwMi0zN2Q3LTRiYWItOWE5My01NWM1NTcyMDQ2ZDYiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ6b29tLWNhY2hlLWJlZm9yZS1hZnRlci5wbmcifQ.WFkOy2ZxZz4NccAYlxpAcloWWl2DQx-GtsyP2by5PFw)

![Cache navigation
recording](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9hN2QwMzhjMS04YzQwLTQ1ZWQtYjZhOC1mYzViNjM1ODc1NjQiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjYWNoZS1uYXZpZ2F0aW9uLndlYnAifQ.PKq-o2BIeHa7Y9wjSDZ3BkNdMKHR0kK_EtNztu_8s0M)

![Configure
headings](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81NTU2NzMzZC02NjBiLTQ4YjItOTdmOC05MzFmYWFiMWU0ODMiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjb25maWd1cmUtaGVhZGluZ3MucG5nIn0.8UqmY8ZjvoxXFml-rLNrfj1lCrqb_1MpRql8Zvyb2WQ)

## Stack

PR 2 of 4: #16262 → **#16276** → #16280 → #16284.

Link to Devin session:
https://app.devin.ai/sessions/39d77bde91c34b75922bb05c02b93b90
Open in Devin Desktop:
https://app.devin.ai/desktop/session/39d77bde91c34b75922bb05c02b93b90?variant=devin
Requested by: @rohoswagger
2026-10-21 23:51:27 +00:00
..