mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Revert "Add support for Roo Code Cloud as an embeddings provider" (#9602)
This commit is contained in:
parent
71e761e21b
commit
3c989d3591
47 changed files with 40 additions and 971 deletions
|
|
@ -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<typeof codebaseIndexModelsSchema>
|
||||
|
|
|
|||
3
src/i18n/locales/ca/embeddings.json
generated
3
src/i18n/locales/ca/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/de/embeddings.json
generated
3
src/i18n/locales/de/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/es/embeddings.json
generated
3
src/i18n/locales/es/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/fr/embeddings.json
generated
3
src/i18n/locales/fr/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/hi/embeddings.json
generated
3
src/i18n/locales/hi/embeddings.json
generated
|
|
@ -39,8 +39,7 @@
|
|||
"invalidModel": "अमान्य मॉडल। कृपया अपनी मॉडल कॉन्फ़िगरेशन जांचें।",
|
||||
"invalidResponse": "एम्बेडर सेवा से अमान्य प्रतिक्रिया। कृपया अपनी कॉन्फ़िगरेशन जांचें।",
|
||||
"apiKeyRequired": "इस एम्बेडर के लिए API कुंजी आवश्यक है।",
|
||||
"baseUrlRequired": "इस एम्बेडर के लिए बेस यूआरएल आवश्यक है",
|
||||
"rooAuthenticationRequired": "Roo Code Cloud प्रमाणीकरण आवश्यक है। Roo एम्बेडिंग प्रदाता का उपयोग करने के लिए कृपया साइन इन करें।"
|
||||
"baseUrlRequired": "इस एम्बेडर के लिए बेस यूआरएल आवश्यक है"
|
||||
},
|
||||
"serviceFactory": {
|
||||
"openAiConfigMissing": "एम्बेडर बनाने के लिए OpenAI कॉन्फ़िगरेशन गायब है",
|
||||
|
|
|
|||
3
src/i18n/locales/id/embeddings.json
generated
3
src/i18n/locales/id/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/it/embeddings.json
generated
3
src/i18n/locales/it/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/ja/embeddings.json
generated
3
src/i18n/locales/ja/embeddings.json
generated
|
|
@ -39,8 +39,7 @@
|
|||
"invalidModel": "無効なモデルです。モデル構成を確認してください。",
|
||||
"invalidResponse": "エンベッダーサービスからの無効な応答です。設定を確認してください。",
|
||||
"apiKeyRequired": "このエンベッダーにはAPIキーが必要です。",
|
||||
"baseUrlRequired": "このエンベッダーにはベースURLが必要です",
|
||||
"rooAuthenticationRequired": "Roo Code Cloud認証が必要です。Roo埋め込みプロバイダーを使用するにはサインインしてください。"
|
||||
"baseUrlRequired": "このエンベッダーにはベースURLが必要です"
|
||||
},
|
||||
"serviceFactory": {
|
||||
"openAiConfigMissing": "エンベッダー作成のためのOpenAI設定がありません",
|
||||
|
|
|
|||
3
src/i18n/locales/ko/embeddings.json
generated
3
src/i18n/locales/ko/embeddings.json
generated
|
|
@ -39,8 +39,7 @@
|
|||
"invalidModel": "잘못된 모델입니다. 모델 구성을 확인하세요.",
|
||||
"invalidResponse": "임베더 서비스에서 잘못된 응답이 왔습니다. 구성을 확인하세요.",
|
||||
"apiKeyRequired": "이 임베더에는 API 키가 필요합니다",
|
||||
"baseUrlRequired": "이 임베더에는 기본 URL이 필요합니다",
|
||||
"rooAuthenticationRequired": "Roo Code Cloud 인증이 필요합니다. Roo 임베딩 제공업체를 사용하려면 로그인하세요."
|
||||
"baseUrlRequired": "이 임베더에는 기본 URL이 필요합니다"
|
||||
},
|
||||
"serviceFactory": {
|
||||
"openAiConfigMissing": "임베더 생성을 위한 OpenAI 구성이 누락되었습니다",
|
||||
|
|
|
|||
3
src/i18n/locales/nl/embeddings.json
generated
3
src/i18n/locales/nl/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/pl/embeddings.json
generated
3
src/i18n/locales/pl/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/pt-BR/embeddings.json
generated
3
src/i18n/locales/pt-BR/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/ru/embeddings.json
generated
3
src/i18n/locales/ru/embeddings.json
generated
|
|
@ -39,8 +39,7 @@
|
|||
"invalidModel": "Неверная модель. Проверьте конфигурацию модели.",
|
||||
"invalidResponse": "Неверный ответ от службы embedder. Проверьте вашу конфигурацию.",
|
||||
"apiKeyRequired": "Для этого встраивателя требуется ключ API",
|
||||
"baseUrlRequired": "Для этого встраивателя требуется базовый URL",
|
||||
"rooAuthenticationRequired": "Требуется аутентификация Roo Code Cloud. Войдите в систему, чтобы использовать провайдер embeddings Roo."
|
||||
"baseUrlRequired": "Для этого встраивателя требуется базовый URL"
|
||||
},
|
||||
"serviceFactory": {
|
||||
"openAiConfigMissing": "Отсутствует конфигурация OpenAI для создания эмбеддера",
|
||||
|
|
|
|||
3
src/i18n/locales/tr/embeddings.json
generated
3
src/i18n/locales/tr/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/vi/embeddings.json
generated
3
src/i18n/locales/vi/embeddings.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
3
src/i18n/locales/zh-CN/embeddings.json
generated
3
src/i18n/locales/zh-CN/embeddings.json
generated
|
|
@ -39,8 +39,7 @@
|
|||
"invalidModel": "模型无效。请检查您的模型配置。",
|
||||
"invalidResponse": "嵌入服务响应无效。请检查您的配置。",
|
||||
"apiKeyRequired": "此嵌入器需要 API 密钥",
|
||||
"baseUrlRequired": "此嵌入器需要基础 URL",
|
||||
"rooAuthenticationRequired": "需要 Roo Code Cloud 身份验证。请登录以使用 Roo 嵌入提供商。"
|
||||
"baseUrlRequired": "此嵌入器需要基础 URL"
|
||||
},
|
||||
"serviceFactory": {
|
||||
"openAiConfigMissing": "创建嵌入器缺少 OpenAI 配置",
|
||||
|
|
|
|||
3
src/i18n/locales/zh-TW/embeddings.json
generated
3
src/i18n/locales/zh-TW/embeddings.json
generated
|
|
@ -39,8 +39,7 @@
|
|||
"invalidModel": "無效的模型。請檢查您的模型組態。",
|
||||
"invalidResponse": "內嵌服務回應無效。請檢查您的組態。",
|
||||
"apiKeyRequired": "此嵌入器需要 API 金鑰",
|
||||
"baseUrlRequired": "此嵌入器需要基礎 URL",
|
||||
"rooAuthenticationRequired": "需要 Roo Code Cloud 身份驗證。請登入以使用 Roo 嵌入提供商。"
|
||||
"baseUrlRequired": "此嵌入器需要基礎 URL"
|
||||
},
|
||||
"serviceFactory": {
|
||||
"openAiConfigMissing": "建立嵌入器缺少 OpenAI 設定",
|
||||
|
|
|
|||
|
|
@ -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<typeof vi.fn>).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")
|
||||
|
|
|
|||
|
|
@ -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 ?? ""
|
||||
|
|
|
|||
|
|
@ -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<typeof vi.fn>
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Set up CloudService mock to return a valid session token
|
||||
MockedCloudService.hasInstance.mockReturnValue(true)
|
||||
;(MockedCloudService.instance.authService!.getSessionToken as ReturnType<typeof vi.fn>).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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -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<EmbeddingResponse> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<number> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,7 +36,6 @@ export type AvailableEmbedders =
|
|||
| "mistral"
|
||||
| "vercel-ai-gateway"
|
||||
| "openrouter"
|
||||
| "roo"
|
||||
|
||||
export interface EmbedderInfo {
|
||||
name: AvailableEmbedders
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ export type EmbedderProvider =
|
|||
| "mistral"
|
||||
| "vercel-ai-gateway"
|
||||
| "openrouter"
|
||||
| "roo"
|
||||
|
||||
export interface IndexProgressUpdate {
|
||||
systemStatus: IndexingState
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -237,7 +237,6 @@ export interface WebviewMessage {
|
|||
| "mistral"
|
||||
| "vercel-ai-gateway"
|
||||
| "openrouter"
|
||||
| "roo"
|
||||
codebaseIndexEmbedderBaseUrl?: string
|
||||
codebaseIndexEmbedderModelId: string
|
||||
codebaseIndexEmbedderModelDimension?: number // Generic dimension for all providers
|
||||
|
|
|
|||
|
|
@ -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.`)
|
||||
|
|
|
|||
|
|
@ -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<CodeIndexPopoverProps> = ({
|
|||
}) => {
|
||||
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<CodeIndexPopoverProps> = ({
|
|||
const getDefaultSettings = (): LocalCodeIndexSettings => ({
|
||||
codebaseIndexEnabled: true,
|
||||
codebaseIndexQdrantUrl: "",
|
||||
codebaseIndexEmbedderProvider: "roo",
|
||||
codebaseIndexEmbedderProvider: "openai",
|
||||
codebaseIndexEmbedderBaseUrl: "",
|
||||
codebaseIndexEmbedderModelId: "",
|
||||
codebaseIndexEmbedderModelDimension: undefined,
|
||||
|
|
@ -234,7 +226,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
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<CodeIndexPopoverProps> = ({
|
|||
<SelectItem value="openrouter">
|
||||
{t("settings:codeIndex.openRouterProvider")}
|
||||
</SelectItem>
|
||||
<SelectItem value="roo">
|
||||
{t("settings:codeIndex.rooProvider")}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
|
@ -1233,55 +1222,6 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
</>
|
||||
)}
|
||||
|
||||
{currentSettings.codebaseIndexEmbedderProvider === "roo" && (
|
||||
<>
|
||||
{!cloudIsAuthenticated && (
|
||||
<div className="p-3 mb-2 bg-vscode-editorWarning-background text-vscode-editorWarning-foreground rounded text-sm">
|
||||
{t("settings:codeIndex.rooCloudAuthNote")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">
|
||||
{t("settings:codeIndex.modelLabel")}
|
||||
</label>
|
||||
<VSCodeDropdown
|
||||
value={currentSettings.codebaseIndexEmbedderModelId}
|
||||
onChange={(e: any) =>
|
||||
updateSetting("codebaseIndexEmbedderModelId", e.target.value)
|
||||
}
|
||||
className={cn("w-full", {
|
||||
"border-red-500": formErrors.codebaseIndexEmbedderModelId,
|
||||
})}>
|
||||
<VSCodeOption value="" className="p-2">
|
||||
{t("settings:codeIndex.selectModel")}
|
||||
</VSCodeOption>
|
||||
{getAvailableModels().map((modelId) => {
|
||||
const model =
|
||||
codebaseIndexModels?.[
|
||||
currentSettings.codebaseIndexEmbedderProvider
|
||||
]?.[modelId]
|
||||
return (
|
||||
<VSCodeOption key={modelId} value={modelId} className="p-2">
|
||||
{modelId}{" "}
|
||||
{model
|
||||
? t("settings:codeIndex.modelDimensions", {
|
||||
dimension: model.dimension,
|
||||
})
|
||||
: ""}
|
||||
</VSCodeOption>
|
||||
)
|
||||
})}
|
||||
</VSCodeDropdown>
|
||||
{formErrors.codebaseIndexEmbedderModelId && (
|
||||
<p className="text-xs text-vscode-errorForeground mt-1 mb-0">
|
||||
{formErrors.codebaseIndexEmbedderModelId}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Qdrant Settings */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/ca/settings.json
generated
2
webview-ui/src/i18n/locales/ca/settings.json
generated
|
|
@ -81,8 +81,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "Clau de l'API d'OpenRouter",
|
||||
"openRouterApiKeyPlaceholder": "Introduïu la vostra clau de l'API d'OpenRouter",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud utilitza l'autenticació del teu compte. Inicia sessió a Roo Code Cloud per utilitzar aquest proveïdor.",
|
||||
"openaiCompatibleProvider": "Compatible amb OpenAI",
|
||||
"openAiKeyLabel": "Clau API OpenAI",
|
||||
"openAiKeyPlaceholder": "Introduïu la vostra clau API OpenAI",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/de/settings.json
generated
2
webview-ui/src/i18n/locales/de/settings.json
generated
|
|
@ -83,8 +83,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "OpenRouter API-Schlüssel",
|
||||
"openRouterApiKeyPlaceholder": "Gib deinen OpenRouter API-Schlüssel ein",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud verwendet deine Kontoauthentifizierung. Melde dich bei Roo Code Cloud an, um diesen Anbieter zu verwenden.",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "API-Schlüssel:",
|
||||
"mistralApiKeyPlaceholder": "Gib deinen Mistral-API-Schlüssel ein",
|
||||
|
|
|
|||
|
|
@ -92,8 +92,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "OpenRouter API Key",
|
||||
"openRouterApiKeyPlaceholder": "Enter your OpenRouter API key",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud uses your account authentication. Sign in to Roo Code Cloud to use this provider.",
|
||||
"openaiCompatibleProvider": "OpenAI Compatible",
|
||||
"openAiKeyLabel": "OpenAI API Key",
|
||||
"openAiKeyPlaceholder": "Enter your OpenAI API key",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/es/settings.json
generated
2
webview-ui/src/i18n/locales/es/settings.json
generated
|
|
@ -83,8 +83,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "Clave de API de OpenRouter",
|
||||
"openRouterApiKeyPlaceholder": "Introduce tu clave de API de OpenRouter",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud utiliza la autenticación de tu cuenta. Inicia sesión en Roo Code Cloud para usar este proveedor.",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "Clave API:",
|
||||
"mistralApiKeyPlaceholder": "Introduce tu clave de API de Mistral",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/fr/settings.json
generated
2
webview-ui/src/i18n/locales/fr/settings.json
generated
|
|
@ -83,8 +83,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "Clé d'API OpenRouter",
|
||||
"openRouterApiKeyPlaceholder": "Entrez votre clé d'API OpenRouter",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud utilise l'authentification de ton compte. Connecte-toi à Roo Code Cloud pour utiliser ce fournisseur.",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "Clé d'API:",
|
||||
"mistralApiKeyPlaceholder": "Entrez votre clé d'API Mistral",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/hi/settings.json
generated
2
webview-ui/src/i18n/locales/hi/settings.json
generated
|
|
@ -78,8 +78,6 @@
|
|||
"openRouterProvider": "ओपनराउटर",
|
||||
"openRouterApiKeyLabel": "ओपनराउटर एपीआई कुंजी",
|
||||
"openRouterApiKeyPlaceholder": "अपनी ओपनराउटर एपीआई कुंजी दर्ज करें",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud आपके खाते के प्रमाणीकरण का उपयोग करता है। इस प्रदाता का उपयोग करने के लिए Roo Code Cloud में साइन इन करें।",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "API कुंजी:",
|
||||
"mistralApiKeyPlaceholder": "अपनी मिस्ट्रल एपीआई कुंजी दर्ज करें",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/id/settings.json
generated
2
webview-ui/src/i18n/locales/id/settings.json
generated
|
|
@ -78,8 +78,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "Kunci API OpenRouter",
|
||||
"openRouterApiKeyPlaceholder": "Masukkan kunci API OpenRouter Anda",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud menggunakan autentikasi akun Anda. Masuk ke Roo Code Cloud untuk menggunakan penyedia ini.",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "Kunci API:",
|
||||
"mistralApiKeyPlaceholder": "Masukkan kunci API Mistral Anda",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/it/settings.json
generated
2
webview-ui/src/i18n/locales/it/settings.json
generated
|
|
@ -78,8 +78,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "Chiave API OpenRouter",
|
||||
"openRouterApiKeyPlaceholder": "Inserisci la tua chiave API OpenRouter",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud utilizza l'autenticazione del tuo account. Accedi a Roo Code Cloud per utilizzare questo provider.",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "Chiave API:",
|
||||
"mistralApiKeyPlaceholder": "Inserisci la tua chiave API Mistral",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/ja/settings.json
generated
2
webview-ui/src/i18n/locales/ja/settings.json
generated
|
|
@ -78,8 +78,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "OpenRouter APIキー",
|
||||
"openRouterApiKeyPlaceholder": "OpenRouter APIキーを入力してください",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloudはアカウント認証を使用します。このプロバイダーを使用するにはRoo Code Cloudにサインインしてください。",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "APIキー:",
|
||||
"mistralApiKeyPlaceholder": "Mistral APIキーを入力してください",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/ko/settings.json
generated
2
webview-ui/src/i18n/locales/ko/settings.json
generated
|
|
@ -81,8 +81,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "OpenRouter API 키",
|
||||
"openRouterApiKeyPlaceholder": "OpenRouter API 키를 입력하세요",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud는 계정 인증을 사용합니다. 이 제공업체를 사용하려면 Roo Code Cloud에 로그인하세요.",
|
||||
"openaiCompatibleProvider": "OpenAI 호환",
|
||||
"openAiKeyLabel": "OpenAI API 키",
|
||||
"openAiKeyPlaceholder": "OpenAI API 키를 입력하세요",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/nl/settings.json
generated
2
webview-ui/src/i18n/locales/nl/settings.json
generated
|
|
@ -78,8 +78,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "OpenRouter API-sleutel",
|
||||
"openRouterApiKeyPlaceholder": "Voer uw OpenRouter API-sleutel in",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud gebruikt je accountauthenticatie. Meld je aan bij Roo Code Cloud om deze provider te gebruiken.",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "API-sleutel:",
|
||||
"mistralApiKeyPlaceholder": "Voer uw Mistral API-sleutel in",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/pl/settings.json
generated
2
webview-ui/src/i18n/locales/pl/settings.json
generated
|
|
@ -81,8 +81,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "Klucz API OpenRouter",
|
||||
"openRouterApiKeyPlaceholder": "Wprowadź swój klucz API OpenRouter",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud używa uwierzytelnienia twojego konta. Zaloguj się do Roo Code Cloud, aby używać tego dostawcy.",
|
||||
"openaiCompatibleProvider": "Kompatybilny z OpenAI",
|
||||
"openAiKeyLabel": "Klucz API OpenAI",
|
||||
"openAiKeyPlaceholder": "Wprowadź swój klucz API OpenAI",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
2
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
|
|
@ -78,8 +78,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "Chave de API do OpenRouter",
|
||||
"openRouterApiKeyPlaceholder": "Digite sua chave de API do OpenRouter",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud usa a autenticação da sua conta. Faça login no Roo Code Cloud para usar este provedor.",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "Chave de API:",
|
||||
"mistralApiKeyPlaceholder": "Digite sua chave de API da Mistral",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/ru/settings.json
generated
2
webview-ui/src/i18n/locales/ru/settings.json
generated
|
|
@ -78,8 +78,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "Ключ API OpenRouter",
|
||||
"openRouterApiKeyPlaceholder": "Введите свой ключ API OpenRouter",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud использует аутентификацию твоего аккаунта. Войди в Roo Code Cloud, чтобы использовать этого провайдера.",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "Ключ API:",
|
||||
"mistralApiKeyPlaceholder": "Введите свой API-ключ Mistral",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/tr/settings.json
generated
2
webview-ui/src/i18n/locales/tr/settings.json
generated
|
|
@ -81,8 +81,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "OpenRouter API Anahtarı",
|
||||
"openRouterApiKeyPlaceholder": "OpenRouter API anahtarınızı girin",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud hesap kimlik doğrulamanı kullanır. Bu sağlayıcıyı kullanmak için Roo Code Cloud'a giriş yap.",
|
||||
"openaiCompatibleProvider": "OpenAI Uyumlu",
|
||||
"openAiKeyLabel": "OpenAI API Anahtarı",
|
||||
"openAiKeyPlaceholder": "OpenAI API anahtarınızı girin",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/vi/settings.json
generated
2
webview-ui/src/i18n/locales/vi/settings.json
generated
|
|
@ -81,8 +81,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "Khóa API OpenRouter",
|
||||
"openRouterApiKeyPlaceholder": "Nhập khóa API OpenRouter của bạn",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud sử dụng xác thực tài khoản của bạn. Đăng nhập vào Roo Code Cloud để sử dụng nhà cung cấp này.",
|
||||
"openaiCompatibleProvider": "Tương thích OpenAI",
|
||||
"openAiKeyLabel": "Khóa API OpenAI",
|
||||
"openAiKeyPlaceholder": "Nhập khóa API OpenAI của bạn",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
2
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
|
|
@ -83,8 +83,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "OpenRouter API 密钥",
|
||||
"openRouterApiKeyPlaceholder": "输入您的 OpenRouter API 密钥",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud 使用您的帐户认证。请登录 Roo Code Cloud 以使用此提供商。",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "API 密钥:",
|
||||
"mistralApiKeyPlaceholder": "输入您的 Mistral API 密钥",
|
||||
|
|
|
|||
2
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
2
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
|
|
@ -78,8 +78,6 @@
|
|||
"openRouterProvider": "OpenRouter",
|
||||
"openRouterApiKeyLabel": "OpenRouter API 金鑰",
|
||||
"openRouterApiKeyPlaceholder": "輸入您的 OpenRouter API 金鑰",
|
||||
"rooProvider": "Roo Code Cloud",
|
||||
"rooCloudAuthNote": "Roo Code Cloud 使用您的帳戶認證。請登入 Roo Code Cloud 以使用此提供者。",
|
||||
"mistralProvider": "Mistral",
|
||||
"mistralApiKeyLabel": "API 金鑰:",
|
||||
"mistralApiKeyPlaceholder": "輸入您的 Mistral API 金鑰",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue