mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-08 03:07:53 +00:00
* refactor: remove 9 low-usage providers (Phase 0)
Remove Cerebras, Chutes, DeepInfra, Doubao, Featherless, Groq,
Hugging Face, IO Intelligence, and Unbound providers from the codebase.
Each provider removal includes: handler, tests, model definitions,
type schemas, UI settings components, fetchers, i18n references,
and all wiring in shared registration/config files.
- Delete 42 provider-specific files (handlers, tests, fetchers, UI components)
- Remove @ai-sdk/cerebras and @ai-sdk/groq npm dependencies
- Clean provider references from 68 shared files across src/, packages/types/,
webview-ui/, and apps/cli/
- Remove ~490 dead i18n translation keys across 36 locale files
- Add docs/ai-sdk-migration-guide.md with updated migration status
- All TypeScript checks pass, 6505 tests pass with 0 failures
* feat: show retired-provider message for removed provider profiles
Preserve API profiles that reference removed providers instead of
silently stripping their apiProvider. When a user selects a profile
configured for a retired provider, the settings UI now shows an
empathetic message explaining the removal instead of the provider
configuration form.
- Add retiredProviderNames array and isRetiredProvider() helper to
packages/types/src/provider-settings.ts
- Update ProviderSettingsManager sanitization to preserve retired
providers (only strip truly unknown values)
- Update ContextProxy sanitization to preserve retired providers
- Render retired-provider message in ApiOptions.tsx when selected
provider is in the retired list
- Add tests for sanitization, ContextProxy, and UI behavior
* feat: add retired-provider warning banner in chat view
* Revert "feat: add retired-provider warning banner in chat view"
This reverts commit dd593e1056.
* feat: show retired-provider message as inline chat response
* fix: show retired provider warning on home screen
Move WarningRow outside {task && ...} conditional so it renders
regardless of task state. Preserve user input on retired provider
intercept so text isn't lost when switching providers.
- Move showRetiredProviderWarning WarningRow to unconditional render
area near ProfileViolationWarning
- Remove setInputValue/setSelectedImages clearing from retired
provider early return in handleSendMessage
- Delete unused RetiredProviderWarning.tsx (dead code)
* fix: address PR review — passthrough retired-provider fields and i18n strings
- Use passthrough() in saveConfig() and load() so legacy provider-specific
fields (e.g. groqApiKey, deepInfraModelId) are preserved instead of
silently stripped by strict Zod parse()
- Move hardcoded English strings in ApiOptions.tsx and ChatView.tsx to
i18n translation keys (settings:providers.retiredProviderMessage,
chat:retiredProvider.{title,message,openSettings})
- Update tests to assert legacy provider-specific fields survive
save and load round-trips
* i18n: add retired-provider translations for all 17 locales
Translate providers.retiredProviderMessage (settings) and
retiredProvider.{title,message,openSettings} (chat) into ca, de, es,
fr, hi, id, it, ja, ko, nl, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW.
* test: update ApiOptions retired-provider test to expect i18n key
303 lines
8.1 KiB
TypeScript
303 lines
8.1 KiB
TypeScript
// npx vitest run src/shared/__tests__/ProfileValidator.spec.ts
|
|
|
|
import type { ProviderSettings, OrganizationAllowList } from "@roo-code/types"
|
|
|
|
import { ProfileValidator } from "../ProfileValidator"
|
|
|
|
describe("ProfileValidator", () => {
|
|
describe("isProfileAllowed", () => {
|
|
it("should allow any profile when allowAll is true", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: true,
|
|
providers: {},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "openai",
|
|
openAiModelId: "gpt-4",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
it("should reject profiles without an apiProvider", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
openai: { allowAll: true },
|
|
},
|
|
}
|
|
const profile: Partial<ProviderSettings> = {}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile as ProviderSettings, allowList)).toBe(false)
|
|
})
|
|
|
|
it("should reject profiles with provider not in allow list", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
anthropic: { allowAll: true },
|
|
gemini: { allowAll: false, models: ["gemini-pro"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "openai",
|
|
openAiModelId: "gpt-4",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(false)
|
|
})
|
|
|
|
it("should allow providers with allowAll=true regardless of model", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
openai: { allowAll: true },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "openai",
|
|
openAiModelId: "any-model-id",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
it("should reject if provider exists but model ID is missing", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
openai: { allowAll: false, models: ["gpt-4"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "openai",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(false)
|
|
})
|
|
|
|
it("should allow if model is in the allowed models list", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
openai: { allowAll: false, models: ["gpt-3.5-turbo", "gpt-4"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "openai",
|
|
openAiModelId: "gpt-4",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
it("should reject if model is not in the allowed models list", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
openai: { allowAll: false, models: ["gpt-3.5-turbo"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "openai",
|
|
openAiModelId: "gpt-4",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(false)
|
|
})
|
|
|
|
it("should handle undefined models array in provider config", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
openai: { allowAll: false },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "openai",
|
|
openAiModelId: "gpt-4",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(false)
|
|
})
|
|
|
|
it("should extract openAiModelId for openai provider", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
openai: { allowAll: false, models: ["gpt-4"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "openai",
|
|
openAiModelId: "gpt-4",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
it("should extract apiModelId for anthropic provider", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
anthropic: { allowAll: false, models: ["claude-3-opus"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "anthropic",
|
|
apiModelId: "claude-3-opus",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
it("should extract ollamaModelId for ollama provider", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
ollama: { allowAll: false, models: ["llama3"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "ollama",
|
|
ollamaModelId: "llama3",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
// Test specific providers that use apiModelId
|
|
const apiModelProviders = [
|
|
"anthropic",
|
|
"openai-native",
|
|
"bedrock",
|
|
"vertex",
|
|
"gemini",
|
|
"mistral",
|
|
"deepseek",
|
|
"xai",
|
|
"sambanova",
|
|
"fireworks",
|
|
]
|
|
|
|
apiModelProviders.forEach((provider) => {
|
|
it(`should extract apiModelId for ${provider} provider`, () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
[provider]: { allowAll: false, models: ["test-model"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: provider as any, // Type assertion needed here
|
|
apiModelId: "test-model",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
})
|
|
|
|
// Test for litellm provider which uses litellmModelId
|
|
it(`should extract litellmModelId for litellm provider`, () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
litellm: { allowAll: false, models: ["test-model"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "litellm" as any,
|
|
litellmModelId: "test-model",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
it("should extract vsCodeLmModelSelector.id for vscode-lm provider", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
"vscode-lm": { allowAll: false, models: ["copilot-gpt-3.5"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "vscode-lm",
|
|
vsCodeLmModelSelector: { id: "copilot-gpt-3.5" },
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
it("should extract lmStudioModelId for lmstudio provider", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
lmstudio: { allowAll: false, models: ["lmstudio-model"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "lmstudio",
|
|
lmStudioModelId: "lmstudio-model",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
it("should extract openRouterModelId for openrouter provider", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
openrouter: { allowAll: false, models: ["openrouter-model"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "openrouter",
|
|
openRouterModelId: "openrouter-model",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
it("should extract requestyModelId for requesty provider", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
requesty: { allowAll: false, models: ["requesty-model"] },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "requesty",
|
|
requestyModelId: "requesty-model",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
|
})
|
|
|
|
it("should handle providers with undefined models list gracefully", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {
|
|
"fake-ai": { allowAll: false },
|
|
},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "fake-ai",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(false)
|
|
})
|
|
|
|
it("should handle empty providers object", () => {
|
|
const allowList: OrganizationAllowList = {
|
|
allowAll: false,
|
|
providers: {},
|
|
}
|
|
const profile: ProviderSettings = {
|
|
apiProvider: "openai",
|
|
openAiModelId: "gpt-4",
|
|
}
|
|
|
|
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(false)
|
|
})
|
|
})
|
|
})
|