Files
orcs-code/src/utils
emsanakhchivan b66633ea4d Feat/multi model provider support (#692)
* test: add tests for provider model env updates and multi-model profiles

Add comprehensive tests covering:
- OPENAI_MODEL/ANTHROPIC_MODEL env updates on provider activation
- Cross-provider type switches (openai ↔ anthropic) clearing stale env
- Multi-model profile activation using only the first model for env vars
- Model options cache population from comma-separated model lists
- getProfileModelOptions generating correct ModelOption arrays

* feat: multi-model provider support and model auto-switch

Support comma-separated model names in provider profiles (e.g.
"glm-4.7, glm-4.7-flash"). The first model is used as default on
activation; all models appear in the /model picker for easy switching.

When switching active providers, the session model now automatically
updates to the new provider's first model. The multi-model list is
preserved across switches and /model selections.

Changes:
- Add parseModelList, getPrimaryModel, hasMultipleModels utilities
  with full test coverage (19 tests)
- Use getPrimaryModel when applying profiles to process.env so only
  the primary model is set in OPENAI_MODEL/ANTHROPIC_MODEL
- Update ProviderManager UI to hint at multi-model syntax and show
  model count in provider list summaries
- Populate model options cache from multi-model profiles on activation
  so all models appear in /model picker regardless of base URL type
- Guard persistActiveProviderProfileModel against overwriting
  comma-separated lists: models already in the profile are session
  selections, not profile edits
- Set AppState.mainLoopModel to the actual model string on provider
  switch so Anthropic profiles use the configured model instead of
  falling back to the built-in default

* fix: only show profile models when provider profile env is applied

Guard the profile model picker options behind a
PROFILE_ENV_APPLIED check. getActiveProviderProfile() has a
?? profiles[0] fallback that returns the first profile even when
no profile is explicitly active, causing users with inactive
profiles to lose all standard model options (Opus, Haiku, etc.)
from the /model picker.

* fix: show all model names for profiles with 3 or fewer models

Instead of a summary format for multi-model profiles, display all
model names when there are 3 or fewer. Only use the "+ N more"
format for profiles with 4+ models.

* fix: preserve standard model options in picker alongside profile models

The previous implementation used an early return that replaced all
standard picker options (Opus, Haiku, Sonnet for Anthropic; Codex/GPT
models for OpenAI) with only the profile's custom models.

Changes:
- Collect profile models into a shared array instead of early returning
- Append profile models to firstParty path (Opus + Haiku + Sonnet + custom)
- Append profile models to PAYG 3P path (Codex + Sonnet + Opus + Haiku + custom)
- Guard collection behind PROFILE_ENV_APPLIED to avoid ?? profiles[0] fallback

Fixes review feedback: standard models are no longer hidden when a
provider profile with custom models is active. Users see both the
standard options and their profile's models.

---------

Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com>
2026-04-16 05:01:55 +08:00
..
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-04-12 15:26:14 +08:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-04-12 15:26:14 +08:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-04-01 23:32:38 +08:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-04-12 15:26:14 +08:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00
2026-03-31 03:34:03 -07:00