From 3c989d3591ef83f30ed81bfa90dc8e30c398a309 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 26 Nov 2025 00:08:39 -0500 Subject: [PATCH] Revert "Add support for Roo Code Cloud as an embeddings provider" (#9602) --- packages/types/src/codebase-index.ts | 3 +- src/i18n/locales/ca/embeddings.json | 3 +- src/i18n/locales/de/embeddings.json | 3 +- src/i18n/locales/en/embeddings.json | 3 +- src/i18n/locales/es/embeddings.json | 3 +- src/i18n/locales/fr/embeddings.json | 3 +- src/i18n/locales/hi/embeddings.json | 3 +- src/i18n/locales/id/embeddings.json | 3 +- src/i18n/locales/it/embeddings.json | 3 +- src/i18n/locales/ja/embeddings.json | 3 +- src/i18n/locales/ko/embeddings.json | 3 +- src/i18n/locales/nl/embeddings.json | 3 +- src/i18n/locales/pl/embeddings.json | 3 +- src/i18n/locales/pt-BR/embeddings.json | 3 +- src/i18n/locales/ru/embeddings.json | 3 +- src/i18n/locales/tr/embeddings.json | 3 +- src/i18n/locales/vi/embeddings.json | 3 +- src/i18n/locales/zh-CN/embeddings.json | 3 +- src/i18n/locales/zh-TW/embeddings.json | 3 +- .../__tests__/config-manager.spec.ts | 64 +-- src/services/code-index/config-manager.ts | 24 +- .../embedders/__tests__/roo.spec.ts | 319 -------------- src/services/code-index/embedders/roo.ts | 415 ------------------ .../code-index/interfaces/embedder.ts | 1 - src/services/code-index/interfaces/manager.ts | 1 - src/services/code-index/service-factory.ts | 4 - src/shared/WebviewMessage.ts | 1 - src/shared/embeddingModels.ts | 23 +- .../src/components/chat/CodeIndexPopover.tsx | 66 +-- webview-ui/src/i18n/locales/ca/settings.json | 2 - webview-ui/src/i18n/locales/de/settings.json | 2 - webview-ui/src/i18n/locales/en/settings.json | 2 - webview-ui/src/i18n/locales/es/settings.json | 2 - webview-ui/src/i18n/locales/fr/settings.json | 2 - webview-ui/src/i18n/locales/hi/settings.json | 2 - webview-ui/src/i18n/locales/id/settings.json | 2 - webview-ui/src/i18n/locales/it/settings.json | 2 - webview-ui/src/i18n/locales/ja/settings.json | 2 - webview-ui/src/i18n/locales/ko/settings.json | 2 - webview-ui/src/i18n/locales/nl/settings.json | 2 - webview-ui/src/i18n/locales/pl/settings.json | 2 - .../src/i18n/locales/pt-BR/settings.json | 2 - webview-ui/src/i18n/locales/ru/settings.json | 2 - webview-ui/src/i18n/locales/tr/settings.json | 2 - webview-ui/src/i18n/locales/vi/settings.json | 2 - .../src/i18n/locales/zh-CN/settings.json | 2 - .../src/i18n/locales/zh-TW/settings.json | 2 - 47 files changed, 40 insertions(+), 971 deletions(-) delete mode 100644 src/services/code-index/embedders/__tests__/roo.spec.ts delete mode 100644 src/services/code-index/embedders/roo.ts diff --git a/packages/types/src/codebase-index.ts b/packages/types/src/codebase-index.ts index 40ad5bffd6..8ad66cbb68 100644 --- a/packages/types/src/codebase-index.ts +++ b/packages/types/src/codebase-index.ts @@ -22,7 +22,7 @@ export const codebaseIndexConfigSchema = z.object({ codebaseIndexEnabled: z.boolean().optional(), codebaseIndexQdrantUrl: z.string().optional(), codebaseIndexEmbedderProvider: z - .enum(["openai", "ollama", "openai-compatible", "gemini", "mistral", "vercel-ai-gateway", "openrouter", "roo"]) + .enum(["openai", "ollama", "openai-compatible", "gemini", "mistral", "vercel-ai-gateway", "openrouter"]) .optional(), codebaseIndexEmbedderBaseUrl: z.string().optional(), codebaseIndexEmbedderModelId: z.string().optional(), @@ -52,7 +52,6 @@ export const codebaseIndexModelsSchema = z.object({ mistral: z.record(z.string(), z.object({ dimension: z.number() })).optional(), "vercel-ai-gateway": z.record(z.string(), z.object({ dimension: z.number() })).optional(), openrouter: z.record(z.string(), z.object({ dimension: z.number() })).optional(), - roo: z.record(z.string(), z.object({ dimension: z.number() })).optional(), }) export type CodebaseIndexModels = z.infer diff --git a/src/i18n/locales/ca/embeddings.json b/src/i18n/locales/ca/embeddings.json index 9597060808..c00e336ee5 100644 --- a/src/i18n/locales/ca/embeddings.json +++ b/src/i18n/locales/ca/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Model no vàlid. Comproveu la vostra configuració de model.", "invalidResponse": "Resposta no vàlida del servei d'incrustació. Comproveu la vostra configuració.", "apiKeyRequired": "Es requereix una clau d'API per a aquest incrustador", - "baseUrlRequired": "Es requereix una URL base per a aquest incrustador", - "rooAuthenticationRequired": "Es requereix autenticació de Roo Code Cloud. Si us plau, inicieu sessió per utilitzar el proveïdor d'incrustacions Roo." + "baseUrlRequired": "Es requereix una URL base per a aquest incrustador" }, "serviceFactory": { "openAiConfigMissing": "Falta la configuració d'OpenAI per crear l'embedder", diff --git a/src/i18n/locales/de/embeddings.json b/src/i18n/locales/de/embeddings.json index 1194adfeab..e0c50e0a3d 100644 --- a/src/i18n/locales/de/embeddings.json +++ b/src/i18n/locales/de/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Ungültiges Modell. Bitte überprüfe deine Modellkonfiguration.", "invalidResponse": "Ungültige Antwort vom Embedder-Dienst. Bitte überprüfe deine Konfiguration.", "apiKeyRequired": "API-Schlüssel ist für diesen Embedder erforderlich", - "baseUrlRequired": "Basis-URL ist für diesen Embedder erforderlich", - "rooAuthenticationRequired": "Roo Code Cloud-Authentifizierung erforderlich. Bitte melde dich an, um den Roo Embeddings-Anbieter zu verwenden." + "baseUrlRequired": "Basis-URL ist für diesen Embedder erforderlich" }, "serviceFactory": { "openAiConfigMissing": "OpenAI-Konfiguration fehlt für die Erstellung des Embedders", diff --git a/src/i18n/locales/en/embeddings.json b/src/i18n/locales/en/embeddings.json index 764c81adfb..5cf0322584 100644 --- a/src/i18n/locales/en/embeddings.json +++ b/src/i18n/locales/en/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Invalid model. Please check your model configuration.", "invalidResponse": "Invalid response from embedder service. Please check your configuration.", "apiKeyRequired": "API key is required for this embedder", - "baseUrlRequired": "Base URL is required for this embedder", - "rooAuthenticationRequired": "Roo Code Cloud authentication required. Please sign in to use the Roo embeddings provider." + "baseUrlRequired": "Base URL is required for this embedder" }, "serviceFactory": { "openAiConfigMissing": "OpenAI configuration missing for embedder creation", diff --git a/src/i18n/locales/es/embeddings.json b/src/i18n/locales/es/embeddings.json index ec10600b19..76cd5cf53a 100644 --- a/src/i18n/locales/es/embeddings.json +++ b/src/i18n/locales/es/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Modelo no válido. Comprueba la configuración de tu modelo.", "invalidResponse": "Respuesta no válida del servicio de embedder. Comprueba tu configuración.", "apiKeyRequired": "Se requiere una clave de API para este embedder", - "baseUrlRequired": "Se requiere una URL base para este embedder", - "rooAuthenticationRequired": "Se requiere autenticación de Roo Code Cloud. Inicia sesión para usar el proveedor de embeddings de Roo." + "baseUrlRequired": "Se requiere una URL base para este embedder" }, "serviceFactory": { "openAiConfigMissing": "Falta la configuración de OpenAI para crear el incrustador", diff --git a/src/i18n/locales/fr/embeddings.json b/src/i18n/locales/fr/embeddings.json index 8965768ebd..8bb97735a8 100644 --- a/src/i18n/locales/fr/embeddings.json +++ b/src/i18n/locales/fr/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Modèle invalide. Veuillez vérifier votre configuration de modèle.", "invalidResponse": "Réponse invalide du service d'embedder. Veuillez vérifier votre configuration.", "apiKeyRequired": "Une clé API est requise pour cet embedder.", - "baseUrlRequired": "Une URL de base est requise pour cet embedder", - "rooAuthenticationRequired": "Authentification Roo Code Cloud requise. Connecte-toi pour utiliser le fournisseur d'embeddings Roo." + "baseUrlRequired": "Une URL de base est requise pour cet embedder" }, "serviceFactory": { "openAiConfigMissing": "Configuration OpenAI manquante pour la création de l'embedder", diff --git a/src/i18n/locales/hi/embeddings.json b/src/i18n/locales/hi/embeddings.json index 457c702354..26f9326e30 100644 --- a/src/i18n/locales/hi/embeddings.json +++ b/src/i18n/locales/hi/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "अमान्य मॉडल। कृपया अपनी मॉडल कॉन्फ़िगरेशन जांचें।", "invalidResponse": "एम्बेडर सेवा से अमान्य प्रतिक्रिया। कृपया अपनी कॉन्फ़िगरेशन जांचें।", "apiKeyRequired": "इस एम्बेडर के लिए API कुंजी आवश्यक है।", - "baseUrlRequired": "इस एम्बेडर के लिए बेस यूआरएल आवश्यक है", - "rooAuthenticationRequired": "Roo Code Cloud प्रमाणीकरण आवश्यक है। Roo एम्बेडिंग प्रदाता का उपयोग करने के लिए कृपया साइन इन करें।" + "baseUrlRequired": "इस एम्बेडर के लिए बेस यूआरएल आवश्यक है" }, "serviceFactory": { "openAiConfigMissing": "एम्बेडर बनाने के लिए OpenAI कॉन्फ़िगरेशन गायब है", diff --git a/src/i18n/locales/id/embeddings.json b/src/i18n/locales/id/embeddings.json index 97a60e6e9b..b7cbf96851 100644 --- a/src/i18n/locales/id/embeddings.json +++ b/src/i18n/locales/id/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Model tidak valid. Silakan periksa konfigurasi model Anda.", "invalidResponse": "Respons tidak valid dari layanan embedder. Silakan periksa konfigurasi Anda.", "apiKeyRequired": "Kunci API diperlukan untuk embedder ini", - "baseUrlRequired": "URL dasar diperlukan untuk embedder ini", - "rooAuthenticationRequired": "Autentikasi Roo Code Cloud diperlukan. Silakan masuk untuk menggunakan penyedia embeddings Roo." + "baseUrlRequired": "URL dasar diperlukan untuk embedder ini" }, "serviceFactory": { "openAiConfigMissing": "Konfigurasi OpenAI tidak ada untuk membuat embedder", diff --git a/src/i18n/locales/it/embeddings.json b/src/i18n/locales/it/embeddings.json index 9e975548f1..220b902f2c 100644 --- a/src/i18n/locales/it/embeddings.json +++ b/src/i18n/locales/it/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Modello non valido. Controlla la configurazione del tuo modello.", "invalidResponse": "Risposta non valida dal servizio embedder. Controlla la tua configurazione.", "apiKeyRequired": "È richiesta una chiave API per questo embedder", - "baseUrlRequired": "È richiesto un URL di base per questo embedder", - "rooAuthenticationRequired": "È richiesta l'autenticazione Roo Code Cloud. Accedi per utilizzare il provider di embeddings Roo." + "baseUrlRequired": "È richiesto un URL di base per questo embedder" }, "serviceFactory": { "openAiConfigMissing": "Configurazione OpenAI mancante per la creazione dell'embedder", diff --git a/src/i18n/locales/ja/embeddings.json b/src/i18n/locales/ja/embeddings.json index 5a3b6a536c..e74fef4138 100644 --- a/src/i18n/locales/ja/embeddings.json +++ b/src/i18n/locales/ja/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "無効なモデルです。モデル構成を確認してください。", "invalidResponse": "エンベッダーサービスからの無効な応答です。設定を確認してください。", "apiKeyRequired": "このエンベッダーにはAPIキーが必要です。", - "baseUrlRequired": "このエンベッダーにはベースURLが必要です", - "rooAuthenticationRequired": "Roo Code Cloud認証が必要です。Roo埋め込みプロバイダーを使用するにはサインインしてください。" + "baseUrlRequired": "このエンベッダーにはベースURLが必要です" }, "serviceFactory": { "openAiConfigMissing": "エンベッダー作成のためのOpenAI設定がありません", diff --git a/src/i18n/locales/ko/embeddings.json b/src/i18n/locales/ko/embeddings.json index 53bad563f1..31c73fa5f2 100644 --- a/src/i18n/locales/ko/embeddings.json +++ b/src/i18n/locales/ko/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "잘못된 모델입니다. 모델 구성을 확인하세요.", "invalidResponse": "임베더 서비스에서 잘못된 응답이 왔습니다. 구성을 확인하세요.", "apiKeyRequired": "이 임베더에는 API 키가 필요합니다", - "baseUrlRequired": "이 임베더에는 기본 URL이 필요합니다", - "rooAuthenticationRequired": "Roo Code Cloud 인증이 필요합니다. Roo 임베딩 제공업체를 사용하려면 로그인하세요." + "baseUrlRequired": "이 임베더에는 기본 URL이 필요합니다" }, "serviceFactory": { "openAiConfigMissing": "임베더 생성을 위한 OpenAI 구성이 누락되었습니다", diff --git a/src/i18n/locales/nl/embeddings.json b/src/i18n/locales/nl/embeddings.json index 229844f9ab..aa6d242f1f 100644 --- a/src/i18n/locales/nl/embeddings.json +++ b/src/i18n/locales/nl/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Ongeldig model. Controleer je modelconfiguratie.", "invalidResponse": "Ongeldige reactie van embedder-service. Controleer je configuratie.", "apiKeyRequired": "API-sleutel is vereist voor deze embedder", - "baseUrlRequired": "Basis-URL is vereist voor deze embedder", - "rooAuthenticationRequired": "Roo Code Cloud-authenticatie is vereist. Meld je aan om de Roo embeddings-provider te gebruiken." + "baseUrlRequired": "Basis-URL is vereist voor deze embedder" }, "serviceFactory": { "openAiConfigMissing": "OpenAI-configuratie ontbreekt voor het maken van embedder", diff --git a/src/i18n/locales/pl/embeddings.json b/src/i18n/locales/pl/embeddings.json index 28cf002ae6..88543ede38 100644 --- a/src/i18n/locales/pl/embeddings.json +++ b/src/i18n/locales/pl/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Nieprawidłowy model. Sprawdź konfigurację modelu.", "invalidResponse": "Nieprawidłowa odpowiedź z usługi embedder. Sprawdź swoją konfigurację.", "apiKeyRequired": "Klucz API jest wymagany dla tego embeddera", - "baseUrlRequired": "Podstawowy adres URL jest wymagany dla tego embeddera", - "rooAuthenticationRequired": "Wymagana jest autentykacja Roo Code Cloud. Zaloguj się, aby używać dostawcy embeddings Roo." + "baseUrlRequired": "Podstawowy adres URL jest wymagany dla tego embeddera" }, "serviceFactory": { "openAiConfigMissing": "Brak konfiguracji OpenAI do utworzenia embeddera", diff --git a/src/i18n/locales/pt-BR/embeddings.json b/src/i18n/locales/pt-BR/embeddings.json index cdcdff41db..c67d0df686 100644 --- a/src/i18n/locales/pt-BR/embeddings.json +++ b/src/i18n/locales/pt-BR/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Modelo inválido. Verifique a configuração do seu modelo.", "invalidResponse": "Resposta inválida do serviço de embedder. Verifique sua configuração.", "apiKeyRequired": "A chave de API é necessária para este embedder", - "baseUrlRequired": "A URL base é necessária para este embedder", - "rooAuthenticationRequired": "Autenticação Roo Code Cloud necessária. Faça login para usar o provedor de embeddings Roo." + "baseUrlRequired": "A URL base é necessária para este embedder" }, "serviceFactory": { "openAiConfigMissing": "Configuração do OpenAI ausente para criação do embedder", diff --git a/src/i18n/locales/ru/embeddings.json b/src/i18n/locales/ru/embeddings.json index ad55e1baf1..7e48af3d59 100644 --- a/src/i18n/locales/ru/embeddings.json +++ b/src/i18n/locales/ru/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Неверная модель. Проверьте конфигурацию модели.", "invalidResponse": "Неверный ответ от службы embedder. Проверьте вашу конфигурацию.", "apiKeyRequired": "Для этого встраивателя требуется ключ API", - "baseUrlRequired": "Для этого встраивателя требуется базовый URL", - "rooAuthenticationRequired": "Требуется аутентификация Roo Code Cloud. Войдите в систему, чтобы использовать провайдер embeddings Roo." + "baseUrlRequired": "Для этого встраивателя требуется базовый URL" }, "serviceFactory": { "openAiConfigMissing": "Отсутствует конфигурация OpenAI для создания эмбеддера", diff --git a/src/i18n/locales/tr/embeddings.json b/src/i18n/locales/tr/embeddings.json index ce5b897060..36efc466e3 100644 --- a/src/i18n/locales/tr/embeddings.json +++ b/src/i18n/locales/tr/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Geçersiz model. Lütfen model yapılandırmanızı kontrol edin.", "invalidResponse": "Embedder hizmetinden geçersiz yanıt. Lütfen yapılandırmanızı kontrol edin.", "apiKeyRequired": "Bu gömücü için API anahtarı gereklidir", - "baseUrlRequired": "Bu gömücü için temel URL gereklidir", - "rooAuthenticationRequired": "Roo Code Cloud kimlik doğrulaması gerekli. Roo embeddings sağlayıcısını kullanmak için lütfen giriş yap." + "baseUrlRequired": "Bu gömücü için temel URL gereklidir" }, "serviceFactory": { "openAiConfigMissing": "Gömücü oluşturmak için OpenAI yapılandırması eksik", diff --git a/src/i18n/locales/vi/embeddings.json b/src/i18n/locales/vi/embeddings.json index 4d9af13345..96496083ca 100644 --- a/src/i18n/locales/vi/embeddings.json +++ b/src/i18n/locales/vi/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "Mô hình không hợp lệ. Vui lòng kiểm tra cấu hình mô hình của bạn.", "invalidResponse": "Phản hồi không hợp lệ từ dịch vụ embedder. Vui lòng kiểm tra cấu hình của bạn.", "apiKeyRequired": "Cần có khóa API cho trình nhúng này", - "baseUrlRequired": "Cần có URL cơ sở cho trình nhúng này", - "rooAuthenticationRequired": "Yêu cầu xác thực Roo Code Cloud. Vui lòng đăng nhập để sử dụng nhà cung cấp embeddings Roo." + "baseUrlRequired": "Cần có URL cơ sở cho trình nhúng này" }, "serviceFactory": { "openAiConfigMissing": "Thiếu cấu hình OpenAI để tạo embedder", diff --git a/src/i18n/locales/zh-CN/embeddings.json b/src/i18n/locales/zh-CN/embeddings.json index 3a9af38eeb..dfc591391e 100644 --- a/src/i18n/locales/zh-CN/embeddings.json +++ b/src/i18n/locales/zh-CN/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "模型无效。请检查您的模型配置。", "invalidResponse": "嵌入服务响应无效。请检查您的配置。", "apiKeyRequired": "此嵌入器需要 API 密钥", - "baseUrlRequired": "此嵌入器需要基础 URL", - "rooAuthenticationRequired": "需要 Roo Code Cloud 身份验证。请登录以使用 Roo 嵌入提供商。" + "baseUrlRequired": "此嵌入器需要基础 URL" }, "serviceFactory": { "openAiConfigMissing": "创建嵌入器缺少 OpenAI 配置", diff --git a/src/i18n/locales/zh-TW/embeddings.json b/src/i18n/locales/zh-TW/embeddings.json index b203fa2b9f..24ed519096 100644 --- a/src/i18n/locales/zh-TW/embeddings.json +++ b/src/i18n/locales/zh-TW/embeddings.json @@ -39,8 +39,7 @@ "invalidModel": "無效的模型。請檢查您的模型組態。", "invalidResponse": "內嵌服務回應無效。請檢查您的組態。", "apiKeyRequired": "此嵌入器需要 API 金鑰", - "baseUrlRequired": "此嵌入器需要基礎 URL", - "rooAuthenticationRequired": "需要 Roo Code Cloud 身份驗證。請登入以使用 Roo 嵌入提供商。" + "baseUrlRequired": "此嵌入器需要基礎 URL" }, "serviceFactory": { "openAiConfigMissing": "建立嵌入器缺少 OpenAI 設定", diff --git a/src/services/code-index/__tests__/config-manager.spec.ts b/src/services/code-index/__tests__/config-manager.spec.ts index 1f8f7b1212..089e039ff8 100644 --- a/src/services/code-index/__tests__/config-manager.spec.ts +++ b/src/services/code-index/__tests__/config-manager.spec.ts @@ -9,21 +9,6 @@ vi.mock("../../../core/config/ContextProxy") // Mock embeddingModels module vi.mock("../../../shared/embeddingModels") -// Mock CloudService -vi.mock("@roo-code/cloud", () => ({ - CloudService: { - hasInstance: vi.fn(() => false), - instance: { - authService: { - getSessionToken: vi.fn(() => undefined), - }, - }, - }, -})) - -import { CloudService } from "@roo-code/cloud" -const mockedCloudService = vi.mocked(CloudService) - // Import mocked functions import { getDefaultModelId, getModelDimension, getModelScoreThreshold } from "../../../shared/embeddingModels" @@ -69,7 +54,7 @@ describe("CodeIndexConfigManager", () => { it("should initialize with ContextProxy", () => { expect(configManager).toBeDefined() expect(configManager.isFeatureEnabled).toBe(true) - expect(configManager.currentEmbedderProvider).toBe("roo") + expect(configManager.currentEmbedderProvider).toBe("openai") }) }) @@ -113,41 +98,17 @@ describe("CodeIndexConfigManager", () => { const result = await configManager.loadConfiguration() - // Roo is the default provider but requires authentication to be configured - // Since there's no session token in the test environment, isConfigured is false - expect(result.currentConfig.isConfigured).toBe(false) - expect(result.currentConfig.embedderProvider).toBe("roo") - expect(result.currentConfig.modelId).toBeUndefined() - expect(result.currentConfig.openAiOptions).toEqual({ openAiNativeApiKey: "" }) - expect(result.currentConfig.ollamaOptions).toEqual({ ollamaBaseUrl: "" }) - expect(result.currentConfig.qdrantUrl).toBe("http://localhost:6333") - expect(result.currentConfig.qdrantApiKey).toBe("") - expect(result.currentConfig.searchMinScore).toBe(0.4) - expect(result.requiresRestart).toBe(false) - }) - - it("should return isConfigured=true for Roo provider when authenticated", async () => { - // Mock CloudService to return an authenticated session - mockedCloudService.hasInstance.mockReturnValue(true) - ;(mockedCloudService.instance.authService?.getSessionToken as ReturnType).mockReturnValue( - "valid-session-token", - ) - - mockContextProxy.getGlobalState.mockReturnValue({ - codebaseIndexEnabled: true, - codebaseIndexQdrantUrl: "http://localhost:6333", - codebaseIndexEmbedderProvider: "roo", + expect(result.currentConfig).toEqual({ + isConfigured: false, + embedderProvider: "openai", + modelId: undefined, + openAiOptions: { openAiNativeApiKey: "" }, + ollamaOptions: { ollamaBaseUrl: "" }, + qdrantUrl: "http://localhost:6333", + qdrantApiKey: "", + searchMinScore: 0.4, }) - mockContextProxy.getSecret.mockReturnValue(undefined) - - configManager = new CodeIndexConfigManager(mockContextProxy) - const result = await configManager.loadConfiguration() - - expect(result.currentConfig.isConfigured).toBe(true) - expect(result.currentConfig.embedderProvider).toBe("roo") - - // Reset the mock - mockedCloudService.hasInstance.mockReturnValue(false) + expect(result.requiresRestart).toBe(false) }) it("should load configuration from globalState and secrets", async () => { @@ -1663,7 +1624,6 @@ describe("CodeIndexConfigManager", () => { expect(config).toHaveProperty("isConfigured") expect(config).toHaveProperty("embedderProvider") - // Provider is "openai" as set in the mock, not "roo" expect(config.embedderProvider).toBe("openai") }) }) @@ -1819,7 +1779,7 @@ describe("CodeIndexConfigManager", () => { configManager = new CodeIndexConfigManager(mockContextProxy) await configManager.loadConfiguration() - // Should use default model ID for the configured provider (openai) + // Should use default model ID expect(configManager.currentModelDimension).toBe(1536) expect(mockedGetDefaultModelId).toHaveBeenCalledWith("openai") expect(mockedGetModelDimension).toHaveBeenCalledWith("openai", "text-embedding-3-small") diff --git a/src/services/code-index/config-manager.ts b/src/services/code-index/config-manager.ts index bd8c7978ab..5bc00b6ce3 100644 --- a/src/services/code-index/config-manager.ts +++ b/src/services/code-index/config-manager.ts @@ -4,7 +4,6 @@ import { EmbedderProvider } from "./interfaces/manager" import { CodeIndexConfig, PreviousConfigSnapshot } from "./interfaces/config" import { DEFAULT_SEARCH_MIN_SCORE, DEFAULT_MAX_SEARCH_RESULTS } from "./constants" import { getDefaultModelId, getModelDimension, getModelScoreThreshold } from "../../shared/embeddingModels" -import { CloudService } from "@roo-code/cloud" /** * Manages configuration state and validation for the code indexing feature. @@ -12,7 +11,7 @@ import { CloudService } from "@roo-code/cloud" */ export class CodeIndexConfigManager { private codebaseIndexEnabled: boolean = true - private embedderProvider: EmbedderProvider = "roo" + private embedderProvider: EmbedderProvider = "openai" private modelId?: string private modelDimension?: number private openAiOptions?: ApiHandlerOptions @@ -48,7 +47,7 @@ export class CodeIndexConfigManager { const codebaseIndexConfig = this.contextProxy?.getGlobalState("codebaseIndexConfig") ?? { codebaseIndexEnabled: true, codebaseIndexQdrantUrl: "http://localhost:6333", - codebaseIndexEmbedderProvider: "roo", + codebaseIndexEmbedderProvider: "openai", codebaseIndexEmbedderBaseUrl: "", codebaseIndexEmbedderModelId: "", codebaseIndexSearchMinScore: undefined, @@ -101,9 +100,7 @@ export class CodeIndexConfigManager { this.openAiOptions = { openAiNativeApiKey: openAiKey } // Set embedder provider with support for openai-compatible - if (codebaseIndexEmbedderProvider === "openai") { - this.embedderProvider = "openai" - } else if (codebaseIndexEmbedderProvider === "ollama") { + if (codebaseIndexEmbedderProvider === "ollama") { this.embedderProvider = "ollama" } else if (codebaseIndexEmbedderProvider === "openai-compatible") { this.embedderProvider = "openai-compatible" @@ -115,10 +112,8 @@ export class CodeIndexConfigManager { this.embedderProvider = "vercel-ai-gateway" } else if (codebaseIndexEmbedderProvider === "openrouter") { this.embedderProvider = "openrouter" - } else if (codebaseIndexEmbedderProvider === "roo") { - this.embedderProvider = "roo" } else { - this.embedderProvider = "roo" + this.embedderProvider = "openai" } this.modelId = codebaseIndexEmbedderModelId || undefined @@ -252,15 +247,6 @@ export class CodeIndexConfigManager { const qdrantUrl = this.qdrantUrl const isConfigured = !!(apiKey && qdrantUrl) return isConfigured - } else if (this.embedderProvider === "roo") { - // Roo Code Cloud uses CloudService session token, so we need to check authentication - const qdrantUrl = this.qdrantUrl - const sessionToken = CloudService.hasInstance() - ? CloudService.instance.authService?.getSessionToken() - : undefined - const isAuthenticated = sessionToken && sessionToken !== "unauthenticated" - const isConfigured = !!(qdrantUrl && isAuthenticated) - return isConfigured } return false // Should not happen if embedderProvider is always set correctly } @@ -287,7 +273,7 @@ export class CodeIndexConfigManager { // Handle null/undefined values safely const prevEnabled = prev?.enabled ?? false const prevConfigured = prev?.configured ?? false - const prevProvider = prev?.embedderProvider ?? "roo" + const prevProvider = prev?.embedderProvider ?? "openai" const prevOpenAiKey = prev?.openAiKey ?? "" const prevOllamaBaseUrl = prev?.ollamaBaseUrl ?? "" const prevOpenAiCompatibleBaseUrl = prev?.openAiCompatibleBaseUrl ?? "" diff --git a/src/services/code-index/embedders/__tests__/roo.spec.ts b/src/services/code-index/embedders/__tests__/roo.spec.ts deleted file mode 100644 index 773733fd7b..0000000000 --- a/src/services/code-index/embedders/__tests__/roo.spec.ts +++ /dev/null @@ -1,319 +0,0 @@ -// npx vitest run src/services/code-index/embedders/__tests__/roo.spec.ts - -import { RooEmbedder } from "../roo" -import { OpenAI } from "openai" -import { CloudService } from "@roo-code/cloud" - -// Mock OpenAI -vi.mock("openai", () => ({ - OpenAI: vi.fn(), -})) - -// Mock CloudService -vi.mock("@roo-code/cloud", () => ({ - CloudService: { - hasInstance: vi.fn(), - instance: { - authService: { - getSessionToken: vi.fn(), - }, - }, - }, -})) - -// Mock the TelemetryService -vi.mock("@roo-code/telemetry", () => ({ - TelemetryService: { - instance: { - captureEvent: vi.fn(), - }, - }, -})) - -// Mock handleOpenAIError -vi.mock("../../../../api/providers/utils/openai-error-handler", () => ({ - handleOpenAIError: vi.fn((error) => error), -})) - -const MockedOpenAI = vi.mocked(OpenAI) -const MockedCloudService = vi.mocked(CloudService) - -describe("RooEmbedder", () => { - let embedder: RooEmbedder - let mockEmbeddingsCreate: ReturnType - - beforeEach(() => { - vi.clearAllMocks() - - // Set up CloudService mock to return a valid session token - MockedCloudService.hasInstance.mockReturnValue(true) - ;(MockedCloudService.instance.authService!.getSessionToken as ReturnType).mockReturnValue( - "test-session-token", - ) - - // Set up OpenAI mock - mockEmbeddingsCreate = vi.fn() - MockedOpenAI.mockImplementation( - () => - ({ - embeddings: { - create: mockEmbeddingsCreate, - }, - apiKey: "test-session-token", - }) as any, - ) - }) - - describe("constructor", () => { - it("should create RooEmbedder with default model", () => { - // Act - embedder = new RooEmbedder() - - // Assert - expect(MockedOpenAI).toHaveBeenCalledWith( - expect.objectContaining({ - baseURL: "https://api.roocode.com/proxy/v1", - apiKey: "test-session-token", - defaultHeaders: { - "HTTP-Referer": "https://github.com/RooCodeInc/Roo-Code", - "X-Title": "Roo Code", - }, - }), - ) - }) - - it("should create RooEmbedder with custom model", () => { - // Arrange - const customModel = "openai/text-embedding-3-small" - - // Act - embedder = new RooEmbedder(customModel) - - // Assert - expect(MockedOpenAI).toHaveBeenCalled() - // The embedder should store the custom model - expect(embedder.embedderInfo.name).toBe("roo") - }) - - it("should handle unauthenticated state", () => { - // Arrange - MockedCloudService.hasInstance.mockReturnValue(false) - - // Act - embedder = new RooEmbedder() - - // Assert - Should use "unauthenticated" as apiKey - expect(MockedOpenAI).toHaveBeenCalledWith( - expect.objectContaining({ - apiKey: "unauthenticated", - }), - ) - }) - }) - - describe("createEmbeddings", () => { - beforeEach(() => { - embedder = new RooEmbedder() - }) - - it("should create embeddings for text input", async () => { - // Arrange - const texts = ["test text 1", "test text 2"] - const base64Embedding1 = Buffer.from(new Float32Array([0.1, 0.2, 0.3]).buffer).toString("base64") - const base64Embedding2 = Buffer.from(new Float32Array([0.4, 0.5, 0.6]).buffer).toString("base64") - - mockEmbeddingsCreate.mockResolvedValue({ - data: [{ embedding: base64Embedding1 }, { embedding: base64Embedding2 }], - usage: { prompt_tokens: 10, total_tokens: 10 }, - }) - - // Act - const result = await embedder.createEmbeddings(texts) - - // Assert - expect(mockEmbeddingsCreate).toHaveBeenCalledWith({ - input: texts, - model: "openai/text-embedding-3-large", - encoding_format: "base64", - }) - expect(result.embeddings).toHaveLength(2) - expect(result.usage?.promptTokens).toBe(10) - expect(result.usage?.totalTokens).toBe(10) - }) - - it("should use custom model when provided", async () => { - // Arrange - const texts = ["test text"] - const customModel = "google/gemini-embedding-001" - const base64Embedding = Buffer.from(new Float32Array([0.1, 0.2]).buffer).toString("base64") - - mockEmbeddingsCreate.mockResolvedValue({ - data: [{ embedding: base64Embedding }], - usage: { prompt_tokens: 5, total_tokens: 5 }, - }) - - // Act - const result = await embedder.createEmbeddings(texts, customModel) - - // Assert - expect(mockEmbeddingsCreate).toHaveBeenCalledWith({ - input: texts, - model: customModel, - encoding_format: "base64", - }) - expect(result.embeddings).toHaveLength(1) - }) - - it("should handle batch processing for large inputs", async () => { - // Arrange - // Create texts that would exceed batch limits - const texts = Array(100).fill("test text") - const base64Embedding = Buffer.from(new Float32Array([0.1, 0.2]).buffer).toString("base64") - - mockEmbeddingsCreate.mockResolvedValue({ - data: texts.map(() => ({ embedding: base64Embedding })), - usage: { prompt_tokens: 500, total_tokens: 500 }, - }) - - // Act - const result = await embedder.createEmbeddings(texts) - - // Assert - expect(result.embeddings).toHaveLength(100) - }) - - it("should skip texts exceeding token limit", async () => { - // Arrange - // Create a very long text that exceeds MAX_ITEM_TOKENS - const longText = "a".repeat(100000) // Way more than 8191 tokens - const normalText = "normal text" - const texts = [longText, normalText] - const base64Embedding = Buffer.from(new Float32Array([0.1, 0.2]).buffer).toString("base64") - - mockEmbeddingsCreate.mockResolvedValue({ - data: [{ embedding: base64Embedding }], - usage: { prompt_tokens: 5, total_tokens: 5 }, - }) - - // Act - const result = await embedder.createEmbeddings(texts) - - // Assert - Only the normal text should be processed - expect(mockEmbeddingsCreate).toHaveBeenCalled() - expect(result.embeddings).toHaveLength(1) - }) - - it("should handle API errors", async () => { - // Arrange - const texts = ["test text"] - mockEmbeddingsCreate.mockRejectedValue(new Error("API error")) - - // Act & Assert - await expect(embedder.createEmbeddings(texts)).rejects.toThrow() - }) - }) - - describe("validateConfiguration", () => { - beforeEach(() => { - embedder = new RooEmbedder() - }) - - it("should return valid when authenticated and API works", async () => { - // Arrange - const base64Embedding = Buffer.from(new Float32Array([0.1]).buffer).toString("base64") - mockEmbeddingsCreate.mockResolvedValue({ - data: [{ embedding: base64Embedding }], - usage: { prompt_tokens: 1, total_tokens: 1 }, - }) - - // Act - const result = await embedder.validateConfiguration() - - // Assert - expect(result.valid).toBe(true) - expect(result.error).toBeUndefined() - }) - - it("should return invalid when not authenticated", async () => { - // Arrange - Reset and set up unauthenticated state - MockedCloudService.hasInstance.mockReturnValue(false) - embedder = new RooEmbedder() - - // Act - const result = await embedder.validateConfiguration() - - // Assert - expect(result.valid).toBe(false) - expect(result.error).toBe("embeddings:validation.rooAuthenticationRequired") - }) - - it("should return invalid when API call fails", async () => { - // Arrange - mockEmbeddingsCreate.mockRejectedValue(new Error("API error")) - - // Act - const result = await embedder.validateConfiguration() - - // Assert - expect(result.valid).toBe(false) - }) - - it("should return invalid when response is empty", async () => { - // Arrange - mockEmbeddingsCreate.mockResolvedValue({ - data: [], - usage: { prompt_tokens: 0, total_tokens: 0 }, - }) - - // Act - const result = await embedder.validateConfiguration() - - // Assert - expect(result.valid).toBe(false) - expect(result.error).toBe("embeddings:validation.invalidResponse") - }) - }) - - describe("embedderInfo", () => { - it("should return correct embedder info", () => { - // Arrange - embedder = new RooEmbedder() - - // Act - const info = embedder.embedderInfo - - // Assert - expect(info).toEqual({ - name: "roo", - }) - }) - }) - - describe("rate limiting", () => { - beforeEach(() => { - embedder = new RooEmbedder() - }) - - it("should handle 429 rate limit errors with retry", async () => { - // Arrange - const texts = ["test text"] - const rateLimitError = new Error("Rate limited") as any - rateLimitError.status = 429 - - const base64Embedding = Buffer.from(new Float32Array([0.1]).buffer).toString("base64") - - // First call fails with 429, second succeeds - mockEmbeddingsCreate.mockRejectedValueOnce(rateLimitError).mockResolvedValueOnce({ - data: [{ embedding: base64Embedding }], - usage: { prompt_tokens: 1, total_tokens: 1 }, - }) - - // Act - const result = await embedder.createEmbeddings(texts) - - // Assert - expect(mockEmbeddingsCreate).toHaveBeenCalledTimes(2) - expect(result.embeddings).toHaveLength(1) - }) - }) -}) diff --git a/src/services/code-index/embedders/roo.ts b/src/services/code-index/embedders/roo.ts deleted file mode 100644 index 5296992890..0000000000 --- a/src/services/code-index/embedders/roo.ts +++ /dev/null @@ -1,415 +0,0 @@ -import { OpenAI } from "openai" -import { IEmbedder, EmbeddingResponse, EmbedderInfo } from "../interfaces/embedder" -import { - MAX_BATCH_TOKENS, - MAX_ITEM_TOKENS, - MAX_BATCH_RETRIES as MAX_RETRIES, - INITIAL_RETRY_DELAY_MS as INITIAL_DELAY_MS, -} from "../constants" -import { getDefaultModelId, getModelQueryPrefix } from "../../../shared/embeddingModels" -import { t } from "../../../i18n" -import { withValidationErrorHandling, HttpError, formatEmbeddingError } from "../shared/validation-helpers" -import { TelemetryEventName } from "@roo-code/types" -import { TelemetryService } from "@roo-code/telemetry" -import { Mutex } from "async-mutex" -import { handleOpenAIError } from "../../../api/providers/utils/openai-error-handler" -import { CloudService } from "@roo-code/cloud" - -interface EmbeddingItem { - embedding: string | number[] - [key: string]: any -} - -interface RooEmbeddingResponse { - data: EmbeddingItem[] - usage?: { - prompt_tokens?: number - total_tokens?: number - } -} - -function getSessionToken(): string { - const token = CloudService.hasInstance() ? CloudService.instance.authService?.getSessionToken() : undefined - return token ?? "unauthenticated" -} - -/** - * Roo Code Cloud implementation of the embedder interface with batching and rate limiting. - * Roo Code Cloud provides access to embedding models through a unified proxy endpoint. - */ -export class RooEmbedder implements IEmbedder { - private embeddingsClient: OpenAI - private readonly defaultModelId: string - private readonly maxItemTokens: number - private readonly baseUrl: string - - // Global rate limiting state shared across all instances - private static globalRateLimitState = { - isRateLimited: false, - rateLimitResetTime: 0, - consecutiveRateLimitErrors: 0, - lastRateLimitError: 0, - // Mutex to ensure thread-safe access to rate limit state - mutex: new Mutex(), - } - - /** - * Creates a new Roo Code Cloud embedder - * @param modelId Optional model identifier (defaults to "openai/text-embedding-3-large") - * @param maxItemTokens Optional maximum tokens per item (defaults to MAX_ITEM_TOKENS) - */ - constructor(modelId?: string, maxItemTokens?: number) { - const sessionToken = getSessionToken() - - this.baseUrl = process.env.ROO_CODE_PROVIDER_URL ?? "https://api.roocode.com/proxy" - - // Ensure baseURL ends with /v1 for OpenAI client, but don't duplicate it - const baseURL = !this.baseUrl.endsWith("/v1") ? `${this.baseUrl}/v1` : this.baseUrl - - // Wrap OpenAI client creation to handle invalid API key characters - try { - this.embeddingsClient = new OpenAI({ - baseURL, - apiKey: sessionToken, - defaultHeaders: { - "HTTP-Referer": "https://github.com/RooCodeInc/Roo-Code", - "X-Title": "Roo Code", - }, - }) - } catch (error) { - // Use the error handler to transform ByteString conversion errors - throw handleOpenAIError(error, "Roo Code Cloud") - } - - this.defaultModelId = modelId || getDefaultModelId("roo") - this.maxItemTokens = maxItemTokens || MAX_ITEM_TOKENS - } - - /** - * Creates embeddings for the given texts with batching and rate limiting - * @param texts Array of text strings to embed - * @param model Optional model identifier - * @returns Promise resolving to embedding response - */ - async createEmbeddings(texts: string[], model?: string): Promise { - const modelToUse = model || this.defaultModelId - - // Apply model-specific query prefix if required - const queryPrefix = getModelQueryPrefix("roo", modelToUse) - const processedTexts = queryPrefix - ? texts.map((text, index) => { - // Prevent double-prefixing - if (text.startsWith(queryPrefix)) { - return text - } - const prefixedText = `${queryPrefix}${text}` - const estimatedTokens = Math.ceil(prefixedText.length / 4) - if (estimatedTokens > MAX_ITEM_TOKENS) { - console.warn( - t("embeddings:textWithPrefixExceedsTokenLimit", { - index, - estimatedTokens, - maxTokens: MAX_ITEM_TOKENS, - }), - ) - // Return original text if adding prefix would exceed limit - return text - } - return prefixedText - }) - : texts - - const allEmbeddings: number[][] = [] - const usage = { promptTokens: 0, totalTokens: 0 } - const remainingTexts = [...processedTexts] - - while (remainingTexts.length > 0) { - const currentBatch: string[] = [] - let currentBatchTokens = 0 - const processedIndices: number[] = [] - - for (let i = 0; i < remainingTexts.length; i++) { - const text = remainingTexts[i] - const itemTokens = Math.ceil(text.length / 4) - - if (itemTokens > this.maxItemTokens) { - console.warn( - t("embeddings:textExceedsTokenLimit", { - index: i, - itemTokens, - maxTokens: this.maxItemTokens, - }), - ) - processedIndices.push(i) - continue - } - - if (currentBatchTokens + itemTokens <= MAX_BATCH_TOKENS) { - currentBatch.push(text) - currentBatchTokens += itemTokens - processedIndices.push(i) - } else { - break - } - } - - // Remove processed items from remainingTexts (in reverse order to maintain correct indices) - for (let i = processedIndices.length - 1; i >= 0; i--) { - remainingTexts.splice(processedIndices[i], 1) - } - - if (currentBatch.length > 0) { - const batchResult = await this._embedBatchWithRetries(currentBatch, modelToUse) - allEmbeddings.push(...batchResult.embeddings) - usage.promptTokens += batchResult.usage.promptTokens - usage.totalTokens += batchResult.usage.totalTokens - } - } - - return { embeddings: allEmbeddings, usage } - } - - /** - * Helper method to handle batch embedding with retries and exponential backoff - * @param batchTexts Array of texts to embed in this batch - * @param model Model identifier to use - * @returns Promise resolving to embeddings and usage statistics - */ - private async _embedBatchWithRetries( - batchTexts: string[], - model: string, - ): Promise<{ embeddings: number[][]; usage: { promptTokens: number; totalTokens: number } }> { - for (let attempts = 0; attempts < MAX_RETRIES; attempts++) { - // Check global rate limit before attempting request - await this.waitForGlobalRateLimit() - - // Update API key before each request to ensure we use the latest session token - this.embeddingsClient.apiKey = getSessionToken() - - try { - const response = (await this.embeddingsClient.embeddings.create({ - input: batchTexts, - model: model, - // OpenAI package (as of v4.78.1) has a parsing issue that truncates embedding dimensions to 256 - // when processing numeric arrays, which breaks compatibility with models using larger dimensions. - // By requesting base64 encoding, we bypass the package's parser and handle decoding ourselves. - encoding_format: "base64", - })) as RooEmbeddingResponse - - // Convert base64 embeddings to float32 arrays - const processedEmbeddings = response.data.map((item: EmbeddingItem) => { - if (typeof item.embedding === "string") { - const buffer = Buffer.from(item.embedding, "base64") - - // Create Float32Array view over the buffer - const float32Array = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4) - - return { - ...item, - embedding: Array.from(float32Array), - } - } - return item - }) - - // Replace the original data with processed embeddings - response.data = processedEmbeddings - - const embeddings = response.data.map((item) => item.embedding as number[]) - - return { - embeddings: embeddings, - usage: { - promptTokens: response.usage?.prompt_tokens || 0, - totalTokens: response.usage?.total_tokens || 0, - }, - } - } catch (error) { - // Capture telemetry before error is reformatted - TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, { - error: error instanceof Error ? error.message : String(error), - stack: error instanceof Error ? error.stack : undefined, - location: "RooEmbedder:_embedBatchWithRetries", - attempt: attempts + 1, - }) - - const hasMoreAttempts = attempts < MAX_RETRIES - 1 - - // Check if it's a rate limit error - const httpError = error as HttpError - if (httpError?.status === 429) { - // Update global rate limit state - await this.updateGlobalRateLimitState(httpError) - - if (hasMoreAttempts) { - // Calculate delay based on global rate limit state - const baseDelay = INITIAL_DELAY_MS * Math.pow(2, attempts) - const globalDelay = await this.getGlobalRateLimitDelay() - const delayMs = Math.max(baseDelay, globalDelay) - - console.warn( - t("embeddings:rateLimitRetry", { - delayMs, - attempt: attempts + 1, - maxRetries: MAX_RETRIES, - }), - ) - await new Promise((resolve) => setTimeout(resolve, delayMs)) - continue - } - } - - // Log the error for debugging - console.error(`Roo Code Cloud embedder error (attempt ${attempts + 1}/${MAX_RETRIES}):`, error) - - // Format and throw the error - throw formatEmbeddingError(error, MAX_RETRIES) - } - } - - throw new Error(t("embeddings:failedMaxAttempts", { attempts: MAX_RETRIES })) - } - - /** - * Validates the Roo Code Cloud embedder configuration by testing API connectivity - * @returns Promise resolving to validation result with success status and optional error message - */ - async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { - return withValidationErrorHandling(async () => { - // Check if we have a valid session token - const sessionToken = getSessionToken() - if (!sessionToken || sessionToken === "unauthenticated") { - return { - valid: false, - error: "embeddings:validation.rooAuthenticationRequired", - } - } - - try { - // Update API key before validation - this.embeddingsClient.apiKey = sessionToken - - // Test with a minimal embedding request - const testTexts = ["test"] - const modelToUse = this.defaultModelId - - const response = (await this.embeddingsClient.embeddings.create({ - input: testTexts, - model: modelToUse, - encoding_format: "base64", - })) as RooEmbeddingResponse - - // Check if we got a valid response - if (!response?.data || response.data.length === 0) { - return { - valid: false, - error: "embeddings:validation.invalidResponse", - } - } - - return { valid: true } - } catch (error) { - // Capture telemetry for validation errors - TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, { - error: error instanceof Error ? error.message : String(error), - stack: error instanceof Error ? error.stack : undefined, - location: "RooEmbedder:validateConfiguration", - }) - throw error - } - }, "roo") - } - - /** - * Returns information about this embedder - */ - get embedderInfo(): EmbedderInfo { - return { - name: "roo", - } - } - - /** - * Waits if there's an active global rate limit - */ - private async waitForGlobalRateLimit(): Promise { - const release = await RooEmbedder.globalRateLimitState.mutex.acquire() - let mutexReleased = false - - try { - const state = RooEmbedder.globalRateLimitState - - if (state.isRateLimited && state.rateLimitResetTime > Date.now()) { - const waitTime = state.rateLimitResetTime - Date.now() - // Silent wait - no logging to prevent flooding - release() - mutexReleased = true - await new Promise((resolve) => setTimeout(resolve, waitTime)) - return - } - - // Reset rate limit if time has passed - if (state.isRateLimited && state.rateLimitResetTime <= Date.now()) { - state.isRateLimited = false - state.consecutiveRateLimitErrors = 0 - } - } finally { - // Only release if we haven't already - if (!mutexReleased) { - release() - } - } - } - - /** - * Updates global rate limit state when a 429 error occurs - */ - private async updateGlobalRateLimitState(error: HttpError): Promise { - const release = await RooEmbedder.globalRateLimitState.mutex.acquire() - try { - const state = RooEmbedder.globalRateLimitState - const now = Date.now() - - // Increment consecutive rate limit errors - if (now - state.lastRateLimitError < 60000) { - // Within 1 minute - state.consecutiveRateLimitErrors++ - } else { - state.consecutiveRateLimitErrors = 1 - } - - state.lastRateLimitError = now - - // Calculate exponential backoff based on consecutive errors - const baseDelay = 5000 // 5 seconds base - const maxDelay = 300000 // 5 minutes max - const exponentialDelay = Math.min(baseDelay * Math.pow(2, state.consecutiveRateLimitErrors - 1), maxDelay) - - // Set global rate limit - state.isRateLimited = true - state.rateLimitResetTime = now + exponentialDelay - - // Silent rate limit activation - no logging to prevent flooding - } finally { - release() - } - } - - /** - * Gets the current global rate limit delay - */ - private async getGlobalRateLimitDelay(): Promise { - const release = await RooEmbedder.globalRateLimitState.mutex.acquire() - try { - const state = RooEmbedder.globalRateLimitState - - if (state.isRateLimited && state.rateLimitResetTime > Date.now()) { - return state.rateLimitResetTime - Date.now() - } - - return 0 - } finally { - release() - } - } -} diff --git a/src/services/code-index/interfaces/embedder.ts b/src/services/code-index/interfaces/embedder.ts index 5895f7bae2..7a3aa91ad9 100644 --- a/src/services/code-index/interfaces/embedder.ts +++ b/src/services/code-index/interfaces/embedder.ts @@ -36,7 +36,6 @@ export type AvailableEmbedders = | "mistral" | "vercel-ai-gateway" | "openrouter" - | "roo" export interface EmbedderInfo { name: AvailableEmbedders diff --git a/src/services/code-index/interfaces/manager.ts b/src/services/code-index/interfaces/manager.ts index afaddc6535..9a6e4031ab 100644 --- a/src/services/code-index/interfaces/manager.ts +++ b/src/services/code-index/interfaces/manager.ts @@ -78,7 +78,6 @@ export type EmbedderProvider = | "mistral" | "vercel-ai-gateway" | "openrouter" - | "roo" export interface IndexProgressUpdate { systemStatus: IndexingState diff --git a/src/services/code-index/service-factory.ts b/src/services/code-index/service-factory.ts index 39fd831ad4..56ee1cff9f 100644 --- a/src/services/code-index/service-factory.ts +++ b/src/services/code-index/service-factory.ts @@ -6,7 +6,6 @@ import { GeminiEmbedder } from "./embedders/gemini" import { MistralEmbedder } from "./embedders/mistral" import { VercelAiGatewayEmbedder } from "./embedders/vercel-ai-gateway" import { OpenRouterEmbedder } from "./embedders/openrouter" -import { RooEmbedder } from "./embedders/roo" import { EmbedderProvider, getDefaultModelId, getModelDimension } from "../../shared/embeddingModels" import { QdrantVectorStore } from "./vector-store/qdrant-client" import { codeParser, DirectoryScanner, FileWatcher } from "./processors" @@ -86,9 +85,6 @@ export class CodeIndexServiceFactory { throw new Error(t("embeddings:serviceFactory.openRouterConfigMissing")) } return new OpenRouterEmbedder(config.openRouterOptions.apiKey, config.modelId) - } else if (provider === "roo") { - // Roo Code Cloud uses session token from CloudService, no API key required - return new RooEmbedder(config.modelId) } throw new Error( diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 33a80a9863..b4c6580e1b 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -237,7 +237,6 @@ export interface WebviewMessage { | "mistral" | "vercel-ai-gateway" | "openrouter" - | "roo" codebaseIndexEmbedderBaseUrl?: string codebaseIndexEmbedderModelId: string codebaseIndexEmbedderModelDimension?: number // Generic dimension for all providers diff --git a/src/shared/embeddingModels.ts b/src/shared/embeddingModels.ts index 8bae4d18bb..9f83e6ae59 100644 --- a/src/shared/embeddingModels.ts +++ b/src/shared/embeddingModels.ts @@ -9,8 +9,7 @@ export type EmbedderProvider = | "gemini" | "mistral" | "vercel-ai-gateway" - | "openrouter" - | "roo" // Add other providers as needed + | "openrouter" // Add other providers as needed export interface EmbeddingModelProfile { dimension: number @@ -93,23 +92,6 @@ export const EMBEDDING_MODEL_PROFILES: EmbeddingModelProfiles = { "qwen/qwen3-embedding-4b": { dimension: 2560, scoreThreshold: 0.4 }, "qwen/qwen3-embedding-8b": { dimension: 4096, scoreThreshold: 0.4 }, }, - roo: { - // OpenAI models via Roo Code Cloud - "openai/text-embedding-3-small": { dimension: 1536, scoreThreshold: 0.4 }, - "openai/text-embedding-3-large": { dimension: 3072, scoreThreshold: 0.4 }, - "openai/text-embedding-ada-002": { dimension: 1536, scoreThreshold: 0.4 }, - // Cohere models via Roo Code Cloud - "cohere/embed-v4.0": { dimension: 1024, scoreThreshold: 0.4 }, - // Google models via Roo Code Cloud - "google/gemini-embedding-001": { dimension: 3072, scoreThreshold: 0.4 }, - "google/text-embedding-005": { dimension: 768, scoreThreshold: 0.4 }, - "google/text-multilingual-embedding-002": { dimension: 768, scoreThreshold: 0.4 }, - // Amazon models via Roo Code Cloud - "amazon/titan-embed-text-v2": { dimension: 1024, scoreThreshold: 0.4 }, - // Mistral models via Roo Code Cloud - "mistral/codestral-embed": { dimension: 1536, scoreThreshold: 0.4 }, - "mistral/mistral-embed": { dimension: 1024, scoreThreshold: 0.4 }, - }, } /** @@ -206,9 +188,6 @@ export function getDefaultModelId(provider: EmbedderProvider): string { case "openrouter": return "openai/text-embedding-3-large" - case "roo": - return "openai/text-embedding-3-large" - default: // Fallback for unknown providers console.warn(`Unknown provider for default model ID: ${provider}. Falling back to OpenAI default.`) diff --git a/webview-ui/src/components/chat/CodeIndexPopover.tsx b/webview-ui/src/components/chat/CodeIndexPopover.tsx index 1efa11cc68..70a1337730 100644 --- a/webview-ui/src/components/chat/CodeIndexPopover.tsx +++ b/webview-ui/src/components/chat/CodeIndexPopover.tsx @@ -161,14 +161,6 @@ const createValidationSchema = (provider: EmbedderProvider, t: any) => { .min(1, t("settings:codeIndex.validation.modelSelectionRequired")), }) - case "roo": - // Roo Code Cloud uses session token from CloudService - no API key required - return baseSchema.extend({ - codebaseIndexEmbedderModelId: z - .string() - .min(1, t("settings:codeIndex.validation.modelSelectionRequired")), - }) - default: return baseSchema } @@ -180,7 +172,7 @@ export const CodeIndexPopover: React.FC = ({ }) => { const SECRET_PLACEHOLDER = "••••••••••••••••" const { t } = useAppTranslation() - const { codebaseIndexConfig, codebaseIndexModels, cwd, cloudIsAuthenticated } = useExtensionState() + const { codebaseIndexConfig, codebaseIndexModels, cwd } = useExtensionState() const [open, setOpen] = useState(false) const [isAdvancedSettingsOpen, setIsAdvancedSettingsOpen] = useState(false) const [isSetupSettingsOpen, setIsSetupSettingsOpen] = useState(false) @@ -201,7 +193,7 @@ export const CodeIndexPopover: React.FC = ({ const getDefaultSettings = (): LocalCodeIndexSettings => ({ codebaseIndexEnabled: true, codebaseIndexQdrantUrl: "", - codebaseIndexEmbedderProvider: "roo", + codebaseIndexEmbedderProvider: "openai", codebaseIndexEmbedderBaseUrl: "", codebaseIndexEmbedderModelId: "", codebaseIndexEmbedderModelDimension: undefined, @@ -234,7 +226,7 @@ export const CodeIndexPopover: React.FC = ({ const settings = { codebaseIndexEnabled: codebaseIndexConfig.codebaseIndexEnabled ?? true, codebaseIndexQdrantUrl: codebaseIndexConfig.codebaseIndexQdrantUrl || "", - codebaseIndexEmbedderProvider: codebaseIndexConfig.codebaseIndexEmbedderProvider || "roo", + codebaseIndexEmbedderProvider: codebaseIndexConfig.codebaseIndexEmbedderProvider || "openai", codebaseIndexEmbedderBaseUrl: codebaseIndexConfig.codebaseIndexEmbedderBaseUrl || "", codebaseIndexEmbedderModelId: codebaseIndexConfig.codebaseIndexEmbedderModelId || "", codebaseIndexEmbedderModelDimension: @@ -703,9 +695,6 @@ export const CodeIndexPopover: React.FC = ({ {t("settings:codeIndex.openRouterProvider")} - - {t("settings:codeIndex.rooProvider")} - @@ -1233,55 +1222,6 @@ export const CodeIndexPopover: React.FC = ({ )} - {currentSettings.codebaseIndexEmbedderProvider === "roo" && ( - <> - {!cloudIsAuthenticated && ( -
- {t("settings:codeIndex.rooCloudAuthNote")} -
- )} - -
- - - updateSetting("codebaseIndexEmbedderModelId", e.target.value) - } - className={cn("w-full", { - "border-red-500": formErrors.codebaseIndexEmbedderModelId, - })}> - - {t("settings:codeIndex.selectModel")} - - {getAvailableModels().map((modelId) => { - const model = - codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider - ]?.[modelId] - return ( - - {modelId}{" "} - {model - ? t("settings:codeIndex.modelDimensions", { - dimension: model.dimension, - }) - : ""} - - ) - })} - - {formErrors.codebaseIndexEmbedderModelId && ( -

- {formErrors.codebaseIndexEmbedderModelId} -

- )} -
- - )} - {/* Qdrant Settings */}