mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: Add provider categorization and improved UI for model selection
- Add comprehensive provider metadata system with categories (cloud/local/hybrid) - Add pricing models (free/paid/freemium) and quality tiers - Create new CategorizedProviderSelect component with visual organization - Add icons and color-coded badges for better UX - Include warnings for deprecated/experimental providers - Support 50+ providers with detailed metadata Fixes #8618
This commit is contained in:
parent
3a47c55a2e
commit
bde80f807d
5 changed files with 1256 additions and 7 deletions
|
|
@ -13,6 +13,8 @@ export * from "./mcp.js"
|
|||
export * from "./message.js"
|
||||
export * from "./mode.js"
|
||||
export * from "./model.js"
|
||||
export * from "./provider-metadata.js"
|
||||
export * from "./provider-metadata-definitions.js"
|
||||
export * from "./provider-settings.js"
|
||||
export * from "./single-file-read-models.js"
|
||||
export * from "./task.js"
|
||||
|
|
|
|||
821
packages/types/src/provider-metadata-definitions.ts
Normal file
821
packages/types/src/provider-metadata-definitions.ts
Normal file
|
|
@ -0,0 +1,821 @@
|
|||
import { ProviderMetadataMap } from "./provider-metadata.js"
|
||||
|
||||
/**
|
||||
* Comprehensive provider metadata with categorization
|
||||
*/
|
||||
export const PROVIDER_METADATA: ProviderMetadataMap = {
|
||||
// Premium Cloud Providers (Paid, High Quality)
|
||||
"openai-native": {
|
||||
id: "openai-native",
|
||||
label: "OpenAI",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "premium",
|
||||
authentication: "api-key",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "Official OpenAI API with GPT-4, GPT-4 Turbo, and GPT-5 models",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: true,
|
||||
supportsPromptCaching: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
costInfo: {
|
||||
pricingUrl: "https://openai.com/pricing",
|
||||
},
|
||||
tags: ["official", "gpt", "premium"],
|
||||
},
|
||||
anthropic: {
|
||||
id: "anthropic",
|
||||
label: "Anthropic",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "premium",
|
||||
authentication: "api-key",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "Claude models with strong reasoning and coding capabilities",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: true,
|
||||
supportsPromptCaching: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
contextWindow: 200000,
|
||||
},
|
||||
costInfo: {
|
||||
pricingUrl: "https://www.anthropic.com/pricing",
|
||||
},
|
||||
tags: ["official", "claude", "premium"],
|
||||
},
|
||||
gemini: {
|
||||
id: "gemini",
|
||||
label: "Google Gemini",
|
||||
category: "cloud",
|
||||
pricing: "freemium",
|
||||
quality: "premium",
|
||||
authentication: "api-key",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "Google's Gemini models with large context windows",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
contextWindow: 1000000,
|
||||
},
|
||||
costInfo: {
|
||||
pricingUrl: "https://ai.google.dev/pricing",
|
||||
freeTokensPerMonth: 60,
|
||||
},
|
||||
tags: ["official", "google", "large-context"],
|
||||
},
|
||||
|
||||
// Enterprise Cloud Providers
|
||||
bedrock: {
|
||||
id: "bedrock",
|
||||
label: "Amazon Bedrock",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "premium",
|
||||
authentication: "cloud-account",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "AWS managed service for various AI models",
|
||||
setupDifficulty: "advanced",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: true,
|
||||
supportsPromptCaching: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["enterprise", "aws", "multi-model"],
|
||||
},
|
||||
vertex: {
|
||||
id: "vertex",
|
||||
label: "GCP Vertex AI",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "premium",
|
||||
authentication: "cloud-account",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "Google Cloud's enterprise AI platform",
|
||||
setupDifficulty: "advanced",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["enterprise", "gcp", "multi-model"],
|
||||
},
|
||||
|
||||
// Local Providers (Free, Self-hosted)
|
||||
ollama: {
|
||||
id: "ollama",
|
||||
label: "Ollama",
|
||||
category: "local",
|
||||
pricing: "free",
|
||||
quality: "standard",
|
||||
authentication: "none",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "Run AI models locally on your machine",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["local", "open-source", "privacy"],
|
||||
},
|
||||
lmstudio: {
|
||||
id: "lmstudio",
|
||||
label: "LM Studio",
|
||||
category: "local",
|
||||
pricing: "free",
|
||||
quality: "standard",
|
||||
authentication: "none",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "User-friendly local model runner with GUI",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["local", "user-friendly", "privacy"],
|
||||
},
|
||||
|
||||
// Router/Aggregator Services
|
||||
openrouter: {
|
||||
id: "openrouter",
|
||||
label: "OpenRouter",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "Access multiple AI providers through one API",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: true,
|
||||
supportsPromptCaching: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
costInfo: {
|
||||
pricingUrl: "https://openrouter.ai/pricing",
|
||||
},
|
||||
tags: ["router", "multi-provider", "flexible"],
|
||||
},
|
||||
litellm: {
|
||||
id: "litellm",
|
||||
label: "LiteLLM",
|
||||
category: "hybrid",
|
||||
pricing: "freemium",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Unified interface for multiple LLM providers",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["router", "proxy", "multi-provider"],
|
||||
},
|
||||
"vercel-ai-gateway": {
|
||||
id: "vercel-ai-gateway",
|
||||
label: "Vercel AI Gateway",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Vercel's unified AI gateway with caching",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["router", "vercel", "caching"],
|
||||
},
|
||||
|
||||
// Alternative/Specialized Providers
|
||||
deepseek: {
|
||||
id: "deepseek",
|
||||
label: "DeepSeek",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "Cost-effective Chinese AI provider with strong coding models",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["affordable", "coding", "chinese"],
|
||||
},
|
||||
groq: {
|
||||
id: "groq",
|
||||
label: "Groq",
|
||||
category: "cloud",
|
||||
pricing: "freemium",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "Ultra-fast inference with specialized hardware",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["fast", "free-tier", "specialized"],
|
||||
},
|
||||
cerebras: {
|
||||
id: "cerebras",
|
||||
label: "Cerebras",
|
||||
category: "cloud",
|
||||
pricing: "freemium",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Fast inference with Cerebras hardware",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["fast", "specialized-hardware"],
|
||||
},
|
||||
mistral: {
|
||||
id: "mistral",
|
||||
label: "Mistral",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "European AI provider with efficient models",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["european", "efficient"],
|
||||
},
|
||||
xai: {
|
||||
id: "xai",
|
||||
label: "xAI (Grok)",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Grok models from xAI",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["grok", "new"],
|
||||
},
|
||||
|
||||
// Specialized/Niche Providers
|
||||
"claude-code": {
|
||||
id: "claude-code",
|
||||
label: "Claude Code",
|
||||
category: "cloud",
|
||||
pricing: "subscription",
|
||||
quality: "premium",
|
||||
authentication: "oauth",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "Claude optimized for coding tasks",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["claude", "coding", "specialized"],
|
||||
},
|
||||
"qwen-code": {
|
||||
id: "qwen-code",
|
||||
label: "Qwen Code",
|
||||
category: "cloud",
|
||||
pricing: "free",
|
||||
quality: "standard",
|
||||
authentication: "oauth",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Alibaba's Qwen model for coding",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["alibaba", "coding", "chinese"],
|
||||
},
|
||||
moonshot: {
|
||||
id: "moonshot",
|
||||
label: "Moonshot",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Chinese AI provider with large context models",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["chinese", "large-context"],
|
||||
},
|
||||
doubao: {
|
||||
id: "doubao",
|
||||
label: "Doubao",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "ByteDance's AI models",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["bytedance", "chinese"],
|
||||
},
|
||||
|
||||
// Infrastructure/Development Providers
|
||||
deepinfra: {
|
||||
id: "deepinfra",
|
||||
label: "DeepInfra",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Serverless inference for open-source models",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["open-source", "serverless"],
|
||||
},
|
||||
huggingface: {
|
||||
id: "huggingface",
|
||||
label: "Hugging Face",
|
||||
category: "cloud",
|
||||
pricing: "freemium",
|
||||
quality: "experimental",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Access thousands of open-source models",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["open-source", "community", "experimental"],
|
||||
},
|
||||
sambanova: {
|
||||
id: "sambanova",
|
||||
label: "SambaNova",
|
||||
category: "cloud",
|
||||
pricing: "freemium",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Fast inference with SambaNova hardware",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["specialized-hardware", "free-tier"],
|
||||
},
|
||||
fireworks: {
|
||||
id: "fireworks",
|
||||
label: "Fireworks AI",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Fast and affordable AI inference",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["fast", "affordable"],
|
||||
},
|
||||
|
||||
// Custom/Compatible Providers
|
||||
openai: {
|
||||
id: "openai",
|
||||
label: "OpenAI Compatible",
|
||||
category: "hybrid",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Connect to any OpenAI-compatible API endpoint",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "medium",
|
||||
},
|
||||
warning: "Compatibility varies by provider",
|
||||
tags: ["flexible", "custom", "compatible"],
|
||||
},
|
||||
|
||||
// VSCode Integration
|
||||
"vscode-lm": {
|
||||
id: "vscode-lm",
|
||||
label: "VS Code LM API",
|
||||
category: "local",
|
||||
pricing: "free",
|
||||
quality: "standard",
|
||||
authentication: "none",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Use models provided by VS Code extensions",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["vscode", "integrated", "copilot"],
|
||||
},
|
||||
|
||||
// Lesser-known/Experimental Providers
|
||||
glama: {
|
||||
id: "glama",
|
||||
label: "Glama",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "experimental",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Multi-model AI gateway",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["gateway", "experimental"],
|
||||
},
|
||||
unbound: {
|
||||
id: "unbound",
|
||||
label: "Unbound",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "experimental",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Experimental AI provider",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "low",
|
||||
},
|
||||
tags: ["experimental"],
|
||||
},
|
||||
requesty: {
|
||||
id: "requesty",
|
||||
label: "Requesty",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "experimental",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "API gateway service",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "low",
|
||||
},
|
||||
tags: ["gateway", "experimental"],
|
||||
},
|
||||
chutes: {
|
||||
id: "chutes",
|
||||
label: "Chutes AI",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "experimental",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Experimental AI provider",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "low",
|
||||
},
|
||||
tags: ["experimental"],
|
||||
},
|
||||
featherless: {
|
||||
id: "featherless",
|
||||
label: "Featherless AI",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "experimental",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Serverless AI inference",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "low",
|
||||
},
|
||||
tags: ["serverless", "experimental"],
|
||||
},
|
||||
"io-intelligence": {
|
||||
id: "io-intelligence",
|
||||
label: "IO Intelligence",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "experimental",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Experimental AI provider",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "low",
|
||||
},
|
||||
tags: ["experimental"],
|
||||
},
|
||||
zai: {
|
||||
id: "zai",
|
||||
label: "Z AI",
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Zhipu AI models",
|
||||
setupDifficulty: "moderate",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "medium",
|
||||
reliability: "medium",
|
||||
},
|
||||
tags: ["chinese", "zhipu"],
|
||||
},
|
||||
|
||||
// Special/Internal Providers
|
||||
roo: {
|
||||
id: "roo",
|
||||
label: "Roo Code Cloud",
|
||||
category: "cloud",
|
||||
pricing: "subscription",
|
||||
quality: "premium",
|
||||
authentication: "cloud-account",
|
||||
recommended: true,
|
||||
deprecated: false,
|
||||
description: "Roo Code's managed cloud service",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: true,
|
||||
supportsImages: true,
|
||||
supportsTools: true,
|
||||
supportsPromptCaching: true,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
tags: ["official", "managed", "integrated"],
|
||||
},
|
||||
"human-relay": {
|
||||
id: "human-relay",
|
||||
label: "Human Relay",
|
||||
category: "local",
|
||||
pricing: "free",
|
||||
quality: "experimental",
|
||||
authentication: "none",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Manual responses for testing",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: false,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "slow",
|
||||
reliability: "low",
|
||||
},
|
||||
warning: "For testing only - requires manual intervention",
|
||||
tags: ["testing", "manual", "development"],
|
||||
},
|
||||
"fake-ai": {
|
||||
id: "fake-ai",
|
||||
label: "Fake AI",
|
||||
category: "local",
|
||||
pricing: "free",
|
||||
quality: "experimental",
|
||||
authentication: "none",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
description: "Mock AI for testing",
|
||||
setupDifficulty: "easy",
|
||||
features: {
|
||||
supportsStreaming: false,
|
||||
supportsImages: false,
|
||||
supportsTools: false,
|
||||
},
|
||||
performance: {
|
||||
speed: "fast",
|
||||
reliability: "high",
|
||||
},
|
||||
warning: "For testing only - returns mock responses",
|
||||
tags: ["testing", "mock", "development"],
|
||||
},
|
||||
}
|
||||
77
packages/types/src/provider-metadata.ts
Normal file
77
packages/types/src/provider-metadata.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Provider metadata for enhanced categorization and user guidance
|
||||
*/
|
||||
|
||||
export type ProviderCategory = "cloud" | "local" | "hybrid"
|
||||
|
||||
export type PricingModel = "free" | "paid" | "freemium" | "pay-per-use" | "subscription"
|
||||
|
||||
export type QualityTier = "premium" | "standard" | "experimental" | "deprecated"
|
||||
|
||||
export type AuthenticationMethod = "api-key" | "oauth" | "none" | "cloud-account"
|
||||
|
||||
export interface ProviderMetadata {
|
||||
/** Unique identifier for the provider */
|
||||
id: string
|
||||
|
||||
/** Display name for the provider */
|
||||
label: string
|
||||
|
||||
/** Category of the provider */
|
||||
category: ProviderCategory
|
||||
|
||||
/** Pricing model */
|
||||
pricing: PricingModel
|
||||
|
||||
/** Quality tier indicator */
|
||||
quality: QualityTier
|
||||
|
||||
/** Authentication method required */
|
||||
authentication: AuthenticationMethod
|
||||
|
||||
/** Whether this provider is recommended */
|
||||
recommended: boolean
|
||||
|
||||
/** Whether this provider should be avoided */
|
||||
deprecated: boolean
|
||||
|
||||
/** Short description of the provider */
|
||||
description?: string
|
||||
|
||||
/** Setup difficulty level */
|
||||
setupDifficulty?: "easy" | "moderate" | "advanced"
|
||||
|
||||
/** Link to documentation */
|
||||
documentationUrl?: string
|
||||
|
||||
/** Specific features or capabilities */
|
||||
features?: {
|
||||
supportsStreaming?: boolean
|
||||
supportsImages?: boolean
|
||||
supportsTools?: boolean
|
||||
supportsPromptCaching?: boolean
|
||||
supportsFinetuning?: boolean
|
||||
}
|
||||
|
||||
/** Performance characteristics */
|
||||
performance?: {
|
||||
speed: "fast" | "medium" | "slow"
|
||||
reliability: "high" | "medium" | "low"
|
||||
contextWindow?: number
|
||||
}
|
||||
|
||||
/** Cost information for paid providers */
|
||||
costInfo?: {
|
||||
pricingUrl?: string
|
||||
estimatedCostPer1kTokens?: number
|
||||
freeTokensPerMonth?: number
|
||||
}
|
||||
|
||||
/** Warning message if any */
|
||||
warning?: string
|
||||
|
||||
/** Tags for additional categorization */
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
export type ProviderMetadataMap = Record<string, ProviderMetadata>
|
||||
|
|
@ -56,11 +56,11 @@ import {
|
|||
SelectValue,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SearchableSelect,
|
||||
Collapsible,
|
||||
CollapsibleTrigger,
|
||||
CollapsibleContent,
|
||||
} from "@src/components/ui"
|
||||
import { CategorizedProviderSelect } from "./CategorizedProviderSelect"
|
||||
|
||||
import {
|
||||
Anthropic,
|
||||
|
|
@ -443,15 +443,11 @@ const ApiOptions = ({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<SearchableSelect
|
||||
<CategorizedProviderSelect
|
||||
value={selectedProvider}
|
||||
onValueChange={(value) => onProviderChange(value as ProviderName)}
|
||||
options={providerOptions}
|
||||
availableProviders={providerOptions}
|
||||
placeholder={t("settings:common.select")}
|
||||
searchPlaceholder={t("settings:providers.searchProviderPlaceholder")}
|
||||
emptyMessage={t("settings:providers.noProviderMatchFound")}
|
||||
className="w-full"
|
||||
data-testid="provider-select"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
353
webview-ui/src/components/settings/CategorizedProviderSelect.tsx
Normal file
353
webview-ui/src/components/settings/CategorizedProviderSelect.tsx
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
import React, { useMemo, useState } from "react"
|
||||
import { ChevronDownIcon, ChevronRightIcon } from "@radix-ui/react-icons"
|
||||
import { ProviderMetadata, PROVIDER_METADATA, ProviderCategory, PricingModel } from "@roo-code/types"
|
||||
import { cn } from "@src/lib/utils"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui"
|
||||
|
||||
interface CategorizedProviderSelectProps {
|
||||
value: string
|
||||
onValueChange: (value: string) => void
|
||||
availableProviders: Array<{ value: string; label: string }>
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
// Helper function to get badge styles based on pricing
|
||||
const getPricingBadgeStyles = (pricing: PricingModel) => {
|
||||
switch (pricing) {
|
||||
case "free":
|
||||
return "bg-green-500/20 text-green-700 dark:text-green-400"
|
||||
case "freemium":
|
||||
return "bg-blue-500/20 text-blue-700 dark:text-blue-400"
|
||||
case "pay-per-use":
|
||||
return "bg-orange-500/20 text-orange-700 dark:text-orange-400"
|
||||
case "subscription":
|
||||
return "bg-purple-500/20 text-purple-700 dark:text-purple-400"
|
||||
default:
|
||||
return "bg-gray-500/20 text-gray-700 dark:text-gray-400"
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to get badge styles based on quality
|
||||
const getQualityBadgeStyles = (quality: string) => {
|
||||
switch (quality) {
|
||||
case "premium":
|
||||
return "bg-yellow-500/20 text-yellow-700 dark:text-yellow-400"
|
||||
case "standard":
|
||||
return "bg-gray-500/20 text-gray-700 dark:text-gray-400"
|
||||
case "experimental":
|
||||
return "bg-red-500/20 text-red-700 dark:text-red-400"
|
||||
case "deprecated":
|
||||
return "bg-red-800/20 text-red-900 dark:text-red-300"
|
||||
default:
|
||||
return "bg-gray-500/20 text-gray-700 dark:text-gray-400"
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to get category icon
|
||||
const getCategoryIcon = (category: ProviderCategory) => {
|
||||
switch (category) {
|
||||
case "cloud":
|
||||
return "☁️"
|
||||
case "local":
|
||||
return "💻"
|
||||
case "hybrid":
|
||||
return "🔄"
|
||||
default:
|
||||
return "📦"
|
||||
}
|
||||
}
|
||||
|
||||
export const CategorizedProviderSelect: React.FC<CategorizedProviderSelectProps> = ({
|
||||
value,
|
||||
onValueChange,
|
||||
availableProviders,
|
||||
placeholder = "Select a provider",
|
||||
}) => {
|
||||
const [expandedCategories, setExpandedCategories] = useState<Set<string>>(
|
||||
new Set(["recommended", "cloud", "local"]),
|
||||
)
|
||||
|
||||
// Group providers by category
|
||||
const categorizedProviders = useMemo(() => {
|
||||
const groups: Record<string, Array<{ provider: ProviderMetadata; value: string }>> = {
|
||||
recommended: [],
|
||||
cloud: [],
|
||||
local: [],
|
||||
hybrid: [],
|
||||
experimental: [],
|
||||
deprecated: [],
|
||||
}
|
||||
|
||||
availableProviders.forEach(({ value: providerId }) => {
|
||||
const metadata = PROVIDER_METADATA[providerId]
|
||||
if (metadata) {
|
||||
const provider = { provider: metadata, value: providerId }
|
||||
|
||||
// Add to recommended section if applicable
|
||||
if (metadata.recommended) {
|
||||
groups.recommended.push(provider)
|
||||
}
|
||||
|
||||
// Add to quality-based sections
|
||||
if (metadata.deprecated || metadata.quality === "deprecated") {
|
||||
groups.deprecated.push(provider)
|
||||
} else if (metadata.quality === "experimental") {
|
||||
groups.experimental.push(provider)
|
||||
} else {
|
||||
// Add to category-based section
|
||||
groups[metadata.category]?.push(provider)
|
||||
}
|
||||
} else {
|
||||
// Fallback for providers without metadata
|
||||
groups.cloud.push({
|
||||
provider: {
|
||||
id: providerId,
|
||||
label: availableProviders.find((p) => p.value === providerId)?.label || providerId,
|
||||
category: "cloud",
|
||||
pricing: "pay-per-use",
|
||||
quality: "standard",
|
||||
authentication: "api-key",
|
||||
recommended: false,
|
||||
deprecated: false,
|
||||
},
|
||||
value: providerId,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Sort providers within each group
|
||||
Object.keys(groups).forEach((key) => {
|
||||
groups[key].sort((a, b) => a.provider.label.localeCompare(b.provider.label))
|
||||
})
|
||||
|
||||
// Remove empty groups
|
||||
return Object.entries(groups).filter(([_, providers]) => providers.length > 0)
|
||||
}, [availableProviders])
|
||||
|
||||
const toggleCategory = (category: string) => {
|
||||
setExpandedCategories((prev) => {
|
||||
const newSet = new Set(prev)
|
||||
if (newSet.has(category)) {
|
||||
newSet.delete(category)
|
||||
} else {
|
||||
newSet.add(category)
|
||||
}
|
||||
return newSet
|
||||
})
|
||||
}
|
||||
|
||||
const getCategoryLabel = (category: string) => {
|
||||
switch (category) {
|
||||
case "recommended":
|
||||
return "⭐ Recommended"
|
||||
case "cloud":
|
||||
return "☁️ Cloud Providers"
|
||||
case "local":
|
||||
return "💻 Local Providers"
|
||||
case "hybrid":
|
||||
return "🔄 Hybrid Providers"
|
||||
case "experimental":
|
||||
return "🧪 Experimental"
|
||||
case "deprecated":
|
||||
return "⚠️ Deprecated"
|
||||
default:
|
||||
return category
|
||||
}
|
||||
}
|
||||
|
||||
const selectedMetadata = PROVIDER_METADATA[value]
|
||||
const selectedLabel = selectedMetadata?.label || availableProviders.find((p) => p.value === value)?.label || value
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<Select value={value} onValueChange={onValueChange}>
|
||||
<SelectTrigger className="w-full" data-testid="categorized-provider-select">
|
||||
<SelectValue placeholder={placeholder}>
|
||||
<div className="flex items-center gap-2">
|
||||
{selectedMetadata && (
|
||||
<span className="text-sm">{getCategoryIcon(selectedMetadata.category)}</span>
|
||||
)}
|
||||
<span>{selectedLabel}</span>
|
||||
{selectedMetadata && (
|
||||
<div className="flex gap-1 ml-auto">
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs px-1.5 py-0.5 rounded",
|
||||
getPricingBadgeStyles(selectedMetadata.pricing),
|
||||
)}>
|
||||
{selectedMetadata.pricing}
|
||||
</span>
|
||||
{selectedMetadata.quality === "premium" && (
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs px-1.5 py-0.5 rounded",
|
||||
getQualityBadgeStyles(selectedMetadata.quality),
|
||||
)}>
|
||||
Premium
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent className="max-h-[400px] overflow-y-auto">
|
||||
{categorizedProviders.map(([category, providers]) => (
|
||||
<div key={category} className="mb-1">
|
||||
<div
|
||||
className="flex items-center gap-1 px-2 py-1.5 cursor-pointer hover:bg-vscode-list-hoverBackground text-sm font-medium"
|
||||
onClick={() => toggleCategory(category)}>
|
||||
{expandedCategories.has(category) ? (
|
||||
<ChevronDownIcon className="w-3 h-3" />
|
||||
) : (
|
||||
<ChevronRightIcon className="w-3 h-3" />
|
||||
)}
|
||||
<span>{getCategoryLabel(category)}</span>
|
||||
<span className="text-xs text-vscode-descriptionForeground ml-auto">
|
||||
({providers.length})
|
||||
</span>
|
||||
</div>
|
||||
{expandedCategories.has(category) && (
|
||||
<div className="ml-2">
|
||||
{providers.map(({ provider, value: providerId }) => (
|
||||
<SelectItem key={providerId} value={providerId} className="pl-4 pr-2 py-2">
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm">
|
||||
{getCategoryIcon(provider.category)}
|
||||
</span>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm">{provider.label}</span>
|
||||
{provider.description && (
|
||||
<span className="text-xs text-vscode-descriptionForeground">
|
||||
{provider.description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-1 ml-4">
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs px-1.5 py-0.5 rounded",
|
||||
getPricingBadgeStyles(provider.pricing),
|
||||
)}>
|
||||
{provider.pricing}
|
||||
</span>
|
||||
{provider.quality !== "standard" && (
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs px-1.5 py-0.5 rounded",
|
||||
getQualityBadgeStyles(provider.quality),
|
||||
)}>
|
||||
{provider.quality}
|
||||
</span>
|
||||
)}
|
||||
{provider.setupDifficulty === "advanced" && (
|
||||
<span className="text-xs px-1.5 py-0.5 rounded bg-yellow-500/20 text-yellow-700 dark:text-yellow-400">
|
||||
Advanced
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{/* Show metadata info for selected provider */}
|
||||
{selectedMetadata && (
|
||||
<div className="mt-2 p-2 rounded bg-vscode-editor-background border border-vscode-panel-border">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
{selectedMetadata.description && (
|
||||
<p className="text-sm text-vscode-descriptionForeground mb-2">
|
||||
{selectedMetadata.description}
|
||||
</p>
|
||||
)}
|
||||
{selectedMetadata.warning && (
|
||||
<p className="text-sm text-yellow-600 dark:text-yellow-500 mb-2">
|
||||
⚠️ {selectedMetadata.warning}
|
||||
</p>
|
||||
)}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs px-2 py-1 rounded",
|
||||
getPricingBadgeStyles(selectedMetadata.pricing),
|
||||
)}>
|
||||
{selectedMetadata.pricing === "free"
|
||||
? "Free"
|
||||
: selectedMetadata.pricing === "freemium"
|
||||
? "Free tier available"
|
||||
: selectedMetadata.pricing === "pay-per-use"
|
||||
? "Pay per use"
|
||||
: selectedMetadata.pricing === "subscription"
|
||||
? "Subscription"
|
||||
: selectedMetadata.pricing}
|
||||
</span>
|
||||
{selectedMetadata.quality !== "standard" && (
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs px-2 py-1 rounded",
|
||||
getQualityBadgeStyles(selectedMetadata.quality),
|
||||
)}>
|
||||
{selectedMetadata.quality === "premium"
|
||||
? "Premium Quality"
|
||||
: selectedMetadata.quality === "experimental"
|
||||
? "Experimental"
|
||||
: selectedMetadata.quality === "deprecated"
|
||||
? "Deprecated"
|
||||
: selectedMetadata.quality}
|
||||
</span>
|
||||
)}
|
||||
{selectedMetadata.setupDifficulty && (
|
||||
<span className="text-xs px-2 py-1 rounded bg-gray-500/20 text-gray-700 dark:text-gray-400">
|
||||
Setup: {selectedMetadata.setupDifficulty}
|
||||
</span>
|
||||
)}
|
||||
{selectedMetadata.performance && (
|
||||
<>
|
||||
<span className="text-xs px-2 py-1 rounded bg-blue-500/20 text-blue-700 dark:text-blue-400">
|
||||
Speed: {selectedMetadata.performance.speed}
|
||||
</span>
|
||||
<span className="text-xs px-2 py-1 rounded bg-purple-500/20 text-purple-700 dark:text-purple-400">
|
||||
Reliability: {selectedMetadata.performance.reliability}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{selectedMetadata.features && (
|
||||
<div className="mt-2 flex flex-wrap gap-1">
|
||||
{selectedMetadata.features.supportsStreaming && (
|
||||
<span className="text-xs px-1.5 py-0.5 rounded bg-green-500/20 text-green-700 dark:text-green-400">
|
||||
✓ Streaming
|
||||
</span>
|
||||
)}
|
||||
{selectedMetadata.features.supportsImages && (
|
||||
<span className="text-xs px-1.5 py-0.5 rounded bg-green-500/20 text-green-700 dark:text-green-400">
|
||||
✓ Images
|
||||
</span>
|
||||
)}
|
||||
{selectedMetadata.features.supportsTools && (
|
||||
<span className="text-xs px-1.5 py-0.5 rounded bg-green-500/20 text-green-700 dark:text-green-400">
|
||||
✓ Tools
|
||||
</span>
|
||||
)}
|
||||
{selectedMetadata.features.supportsPromptCaching && (
|
||||
<span className="text-xs px-1.5 py-0.5 rounded bg-green-500/20 text-green-700 dark:text-green-400">
|
||||
✓ Caching
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue