* 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>
34 lines
1005 B
TypeScript
34 lines
1005 B
TypeScript
/**
|
|
* Utility functions for parsing comma-separated model names in provider profiles.
|
|
*
|
|
* Example: "glm-4.7, glm-4.7-flash" -> ["glm-4.7", "glm-4.7-flash"]
|
|
* Single model: "llama3.1:8b" -> ["llama3.1:8b"]
|
|
*/
|
|
|
|
/**
|
|
* Splits a comma-separated model field into an array of trimmed model names,
|
|
* filtering out any empty entries.
|
|
*/
|
|
export function parseModelList(modelField: string): string[] {
|
|
return modelField
|
|
.split(',')
|
|
.map((part) => part.trim())
|
|
.filter((part) => part.length > 0)
|
|
}
|
|
|
|
/**
|
|
* Returns the first (primary) model from a comma-separated model field.
|
|
* Falls back to the original string if parsing yields no results.
|
|
*/
|
|
export function getPrimaryModel(modelField: string): string {
|
|
const models = parseModelList(modelField)
|
|
return models.length > 0 ? models[0] : modelField
|
|
}
|
|
|
|
/**
|
|
* Returns true if the model field contains more than one model.
|
|
*/
|
|
export function hasMultipleModels(modelField: string): boolean {
|
|
return parseModelList(modelField).length > 1
|
|
}
|