Skip to content

Commit ef2fec9

Browse files
refactor: remove 9 low-usage providers and add retired-provider UX (#11297)
* 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 dd593e1. * 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
1 parent 7afa436 commit ef2fec9

138 files changed

Lines changed: 869 additions & 9949 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/cli/src/lib/utils/context-window.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,10 @@ function getModelIdForProvider(config: ProviderSettings): string | undefined {
4848
return config.requestyModelId
4949
case "litellm":
5050
return config.litellmModelId
51-
case "deepinfra":
52-
return config.deepInfraModelId
53-
case "huggingface":
54-
return config.huggingFaceModelId
55-
case "unbound":
56-
return config.unboundModelId
5751
case "vercel-ai-gateway":
5852
return config.vercelAiGatewayModelId
59-
case "io-intelligence":
60-
return config.ioIntelligenceModelId
6153
default:
62-
// For anthropic, bedrock, vertex, gemini, xai, groq, etc.
54+
// For anthropic, bedrock, vertex, gemini, xai, etc.
6355
return config.apiModelId
6456
}
6557
}

apps/web-evals/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"@radix-ui/react-tabs": "^1.1.3",
2828
"@radix-ui/react-tooltip": "^1.2.8",
2929
"@roo-code/evals": "workspace:^",
30-
"@roo-code/types": "^1.108.0",
30+
"@roo-code/types": "workspace:^",
3131
"@tanstack/react-query": "^5.69.0",
3232
"archiver": "^7.0.1",
3333
"class-variance-authority": "^0.7.1",

packages/types/src/global-settings.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -268,32 +268,23 @@ export const SECRET_STATE_KEYS = [
268268
"ollamaApiKey",
269269
"geminiApiKey",
270270
"openAiNativeApiKey",
271-
"cerebrasApiKey",
272271
"deepSeekApiKey",
273-
"doubaoApiKey",
274272
"moonshotApiKey",
275273
"mistralApiKey",
276274
"minimaxApiKey",
277-
"unboundApiKey",
278275
"requestyApiKey",
279276
"xaiApiKey",
280-
"groqApiKey",
281-
"chutesApiKey",
282277
"litellmApiKey",
283-
"deepInfraApiKey",
284278
"codeIndexOpenAiKey",
285279
"codeIndexQdrantApiKey",
286280
"codebaseIndexOpenAiCompatibleApiKey",
287281
"codebaseIndexGeminiApiKey",
288282
"codebaseIndexMistralApiKey",
289283
"codebaseIndexVercelAiGatewayApiKey",
290284
"codebaseIndexOpenRouterApiKey",
291-
"huggingFaceApiKey",
292285
"sambaNovaApiKey",
293286
"zaiApiKey",
294287
"fireworksApiKey",
295-
"featherlessApiKey",
296-
"ioIntelligenceApiKey",
297288
"vercelAiGatewayApiKey",
298289
"basetenApiKey",
299290
] as const

packages/types/src/provider-settings.ts

Lines changed: 30 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,9 @@ import {
66
anthropicModels,
77
basetenModels,
88
bedrockModels,
9-
cerebrasModels,
109
deepSeekModels,
11-
doubaoModels,
12-
featherlessModels,
1310
fireworksModels,
1411
geminiModels,
15-
groqModels,
16-
ioIntelligenceModels,
1712
mistralModels,
1813
moonshotModels,
1914
openAiCodexModels,
@@ -39,18 +34,7 @@ export const DEFAULT_CONSECUTIVE_MISTAKE_LIMIT = 3
3934
* Dynamic provider requires external API calls in order to get the model list.
4035
*/
4136

42-
export const dynamicProviders = [
43-
"openrouter",
44-
"vercel-ai-gateway",
45-
"huggingface",
46-
"litellm",
47-
"deepinfra",
48-
"io-intelligence",
49-
"requesty",
50-
"unbound",
51-
"roo",
52-
"chutes",
53-
] as const
37+
export const dynamicProviders = ["openrouter", "vercel-ai-gateway", "litellm", "requesty", "roo"] as const
5438

5539
export type DynamicProvider = (typeof dynamicProviders)[number]
5640

@@ -121,14 +105,10 @@ export const providerNames = [
121105
"anthropic",
122106
"bedrock",
123107
"baseten",
124-
"cerebras",
125-
"doubao",
126108
"deepseek",
127-
"featherless",
128109
"fireworks",
129110
"gemini",
130111
"gemini-cli",
131-
"groq",
132112
"mistral",
133113
"moonshot",
134114
"minimax",
@@ -149,14 +129,41 @@ export type ProviderName = z.infer<typeof providerNamesSchema>
149129
export const isProviderName = (key: unknown): key is ProviderName =>
150130
typeof key === "string" && providerNames.includes(key as ProviderName)
151131

132+
/**
133+
* RetiredProviderName
134+
*/
135+
136+
export const retiredProviderNames = [
137+
"cerebras",
138+
"chutes",
139+
"deepinfra",
140+
"doubao",
141+
"featherless",
142+
"groq",
143+
"huggingface",
144+
"io-intelligence",
145+
"unbound",
146+
] as const
147+
148+
export const retiredProviderNamesSchema = z.enum(retiredProviderNames)
149+
150+
export type RetiredProviderName = z.infer<typeof retiredProviderNamesSchema>
151+
152+
export const isRetiredProvider = (value: string): value is RetiredProviderName =>
153+
retiredProviderNames.includes(value as RetiredProviderName)
154+
155+
export const providerNamesWithRetiredSchema = z.union([providerNamesSchema, retiredProviderNamesSchema])
156+
157+
export type ProviderNameWithRetired = z.infer<typeof providerNamesWithRetiredSchema>
158+
152159
/**
153160
* ProviderSettingsEntry
154161
*/
155162

156163
export const providerSettingsEntrySchema = z.object({
157164
id: z.string(),
158165
name: z.string(),
159-
apiProvider: providerNamesSchema.optional(),
166+
apiProvider: providerNamesWithRetiredSchema.optional(),
160167
modelId: z.string().optional(),
161168
})
162169

@@ -300,17 +307,6 @@ const deepSeekSchema = apiModelIdProviderModelSchema.extend({
300307
deepSeekApiKey: z.string().optional(),
301308
})
302309

303-
const deepInfraSchema = apiModelIdProviderModelSchema.extend({
304-
deepInfraBaseUrl: z.string().optional(),
305-
deepInfraApiKey: z.string().optional(),
306-
deepInfraModelId: z.string().optional(),
307-
})
308-
309-
const doubaoSchema = apiModelIdProviderModelSchema.extend({
310-
doubaoBaseUrl: z.string().optional(),
311-
doubaoApiKey: z.string().optional(),
312-
})
313-
314310
const moonshotSchema = apiModelIdProviderModelSchema.extend({
315311
moonshotBaseUrl: z
316312
.union([z.literal("https://api.moonshot.ai/v1"), z.literal("https://api.moonshot.cn/v1")])
@@ -325,11 +321,6 @@ const minimaxSchema = apiModelIdProviderModelSchema.extend({
325321
minimaxApiKey: z.string().optional(),
326322
})
327323

328-
const unboundSchema = baseProviderSettingsSchema.extend({
329-
unboundApiKey: z.string().optional(),
330-
unboundModelId: z.string().optional(),
331-
})
332-
333324
const requestySchema = baseProviderSettingsSchema.extend({
334325
requestyBaseUrl: z.string().optional(),
335326
requestyApiKey: z.string().optional(),
@@ -344,31 +335,13 @@ const xaiSchema = apiModelIdProviderModelSchema.extend({
344335
xaiApiKey: z.string().optional(),
345336
})
346337

347-
const groqSchema = apiModelIdProviderModelSchema.extend({
348-
groqApiKey: z.string().optional(),
349-
})
350-
351-
const huggingFaceSchema = baseProviderSettingsSchema.extend({
352-
huggingFaceApiKey: z.string().optional(),
353-
huggingFaceModelId: z.string().optional(),
354-
huggingFaceInferenceProvider: z.string().optional(),
355-
})
356-
357-
const chutesSchema = apiModelIdProviderModelSchema.extend({
358-
chutesApiKey: z.string().optional(),
359-
})
360-
361338
const litellmSchema = baseProviderSettingsSchema.extend({
362339
litellmBaseUrl: z.string().optional(),
363340
litellmApiKey: z.string().optional(),
364341
litellmModelId: z.string().optional(),
365342
litellmUsePromptCache: z.boolean().optional(),
366343
})
367344

368-
const cerebrasSchema = apiModelIdProviderModelSchema.extend({
369-
cerebrasApiKey: z.string().optional(),
370-
})
371-
372345
const sambaNovaSchema = apiModelIdProviderModelSchema.extend({
373346
sambaNovaApiKey: z.string().optional(),
374347
})
@@ -386,15 +359,6 @@ const fireworksSchema = apiModelIdProviderModelSchema.extend({
386359
fireworksApiKey: z.string().optional(),
387360
})
388361

389-
const featherlessSchema = apiModelIdProviderModelSchema.extend({
390-
featherlessApiKey: z.string().optional(),
391-
})
392-
393-
const ioIntelligenceSchema = apiModelIdProviderModelSchema.extend({
394-
ioIntelligenceModelId: z.string().optional(),
395-
ioIntelligenceApiKey: z.string().optional(),
396-
})
397-
398362
const qwenCodeSchema = apiModelIdProviderModelSchema.extend({
399363
qwenCodeOauthPath: z.string().optional(),
400364
})
@@ -432,33 +396,24 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
432396
openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })),
433397
mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })),
434398
deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })),
435-
deepInfraSchema.merge(z.object({ apiProvider: z.literal("deepinfra") })),
436-
doubaoSchema.merge(z.object({ apiProvider: z.literal("doubao") })),
437399
moonshotSchema.merge(z.object({ apiProvider: z.literal("moonshot") })),
438400
minimaxSchema.merge(z.object({ apiProvider: z.literal("minimax") })),
439-
unboundSchema.merge(z.object({ apiProvider: z.literal("unbound") })),
440401
requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })),
441402
fakeAiSchema.merge(z.object({ apiProvider: z.literal("fake-ai") })),
442403
xaiSchema.merge(z.object({ apiProvider: z.literal("xai") })),
443-
groqSchema.merge(z.object({ apiProvider: z.literal("groq") })),
444404
basetenSchema.merge(z.object({ apiProvider: z.literal("baseten") })),
445-
huggingFaceSchema.merge(z.object({ apiProvider: z.literal("huggingface") })),
446-
chutesSchema.merge(z.object({ apiProvider: z.literal("chutes") })),
447405
litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })),
448-
cerebrasSchema.merge(z.object({ apiProvider: z.literal("cerebras") })),
449406
sambaNovaSchema.merge(z.object({ apiProvider: z.literal("sambanova") })),
450407
zaiSchema.merge(z.object({ apiProvider: z.literal("zai") })),
451408
fireworksSchema.merge(z.object({ apiProvider: z.literal("fireworks") })),
452-
featherlessSchema.merge(z.object({ apiProvider: z.literal("featherless") })),
453-
ioIntelligenceSchema.merge(z.object({ apiProvider: z.literal("io-intelligence") })),
454409
qwenCodeSchema.merge(z.object({ apiProvider: z.literal("qwen-code") })),
455410
rooSchema.merge(z.object({ apiProvider: z.literal("roo") })),
456411
vercelAiGatewaySchema.merge(z.object({ apiProvider: z.literal("vercel-ai-gateway") })),
457412
defaultSchema,
458413
])
459414

460415
export const providerSettingsSchema = z.object({
461-
apiProvider: providerNamesSchema.optional(),
416+
apiProvider: providerNamesWithRetiredSchema.optional(),
462417
...anthropicSchema.shape,
463418
...openRouterSchema.shape,
464419
...bedrockSchema.shape,
@@ -473,25 +428,16 @@ export const providerSettingsSchema = z.object({
473428
...openAiNativeSchema.shape,
474429
...mistralSchema.shape,
475430
...deepSeekSchema.shape,
476-
...deepInfraSchema.shape,
477-
...doubaoSchema.shape,
478431
...moonshotSchema.shape,
479432
...minimaxSchema.shape,
480-
...unboundSchema.shape,
481433
...requestySchema.shape,
482434
...fakeAiSchema.shape,
483435
...xaiSchema.shape,
484-
...groqSchema.shape,
485436
...basetenSchema.shape,
486-
...huggingFaceSchema.shape,
487-
...chutesSchema.shape,
488437
...litellmSchema.shape,
489-
...cerebrasSchema.shape,
490438
...sambaNovaSchema.shape,
491439
...zaiSchema.shape,
492440
...fireworksSchema.shape,
493-
...featherlessSchema.shape,
494-
...ioIntelligenceSchema.shape,
495441
...qwenCodeSchema.shape,
496442
...rooSchema.shape,
497443
...vercelAiGatewaySchema.shape,
@@ -521,13 +467,9 @@ export const modelIdKeys = [
521467
"ollamaModelId",
522468
"lmStudioModelId",
523469
"lmStudioDraftModelId",
524-
"unboundModelId",
525470
"requestyModelId",
526471
"litellmModelId",
527-
"huggingFaceModelId",
528-
"ioIntelligenceModelId",
529472
"vercelAiGatewayModelId",
530-
"deepInfraModelId",
531473
] as const satisfies readonly (keyof ProviderSettings)[]
532474

533475
export type ModelIdKey = (typeof modelIdKeys)[number]
@@ -561,23 +503,14 @@ export const modelIdKeysByProvider: Record<TypicalProvider, ModelIdKey> = {
561503
moonshot: "apiModelId",
562504
minimax: "apiModelId",
563505
deepseek: "apiModelId",
564-
deepinfra: "deepInfraModelId",
565-
doubao: "apiModelId",
566506
"qwen-code": "apiModelId",
567-
unbound: "unboundModelId",
568507
requesty: "requestyModelId",
569508
xai: "apiModelId",
570-
groq: "apiModelId",
571509
baseten: "apiModelId",
572-
chutes: "apiModelId",
573510
litellm: "litellmModelId",
574-
huggingface: "huggingFaceModelId",
575-
cerebras: "apiModelId",
576511
sambanova: "apiModelId",
577512
zai: "apiModelId",
578513
fireworks: "apiModelId",
579-
featherless: "apiModelId",
580-
"io-intelligence": "ioIntelligenceModelId",
581514
roo: "apiModelId",
582515
"vercel-ai-gateway": "vercelAiGatewayModelId",
583516
}
@@ -629,22 +562,11 @@ export const MODELS_BY_PROVIDER: Record<
629562
label: "Amazon Bedrock",
630563
models: Object.keys(bedrockModels),
631564
},
632-
cerebras: {
633-
id: "cerebras",
634-
label: "Cerebras",
635-
models: Object.keys(cerebrasModels),
636-
},
637565
deepseek: {
638566
id: "deepseek",
639567
label: "DeepSeek",
640568
models: Object.keys(deepSeekModels),
641569
},
642-
doubao: { id: "doubao", label: "Doubao", models: Object.keys(doubaoModels) },
643-
featherless: {
644-
id: "featherless",
645-
label: "Featherless",
646-
models: Object.keys(featherlessModels),
647-
},
648570
fireworks: {
649571
id: "fireworks",
650572
label: "Fireworks",
@@ -655,12 +577,6 @@ export const MODELS_BY_PROVIDER: Record<
655577
label: "Google Gemini",
656578
models: Object.keys(geminiModels),
657579
},
658-
groq: { id: "groq", label: "Groq", models: Object.keys(groqModels) },
659-
"io-intelligence": {
660-
id: "io-intelligence",
661-
label: "IO Intelligence",
662-
models: Object.keys(ioIntelligenceModels),
663-
},
664580
mistral: {
665581
id: "mistral",
666582
label: "Mistral",
@@ -708,14 +624,10 @@ export const MODELS_BY_PROVIDER: Record<
708624
baseten: { id: "baseten", label: "Baseten", models: Object.keys(basetenModels) },
709625

710626
// Dynamic providers; models pulled from remote APIs.
711-
huggingface: { id: "huggingface", label: "Hugging Face", models: [] },
712627
litellm: { id: "litellm", label: "LiteLLM", models: [] },
713628
openrouter: { id: "openrouter", label: "OpenRouter", models: [] },
714629
requesty: { id: "requesty", label: "Requesty", models: [] },
715-
unbound: { id: "unbound", label: "Unbound", models: [] },
716-
deepinfra: { id: "deepinfra", label: "DeepInfra", models: [] },
717630
"vercel-ai-gateway": { id: "vercel-ai-gateway", label: "Vercel AI Gateway", models: [] },
718-
chutes: { id: "chutes", label: "Chutes AI", models: [] },
719631

720632
// Local providers; models discovered from localhost endpoints.
721633
lmstudio: { id: "lmstudio", label: "LM Studio", models: [] },

0 commit comments

Comments
 (0)