Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/types/src/codebase-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const codebaseIndexConfigSchema = z.object({
"vercel-ai-gateway",
"bedrock",
"openrouter",
"roo",
])
.optional(),
codebaseIndexEmbedderBaseUrl: z.string().optional(),
Expand Down Expand Up @@ -67,6 +68,7 @@ export const codebaseIndexModelsSchema = z.object({
"vercel-ai-gateway": z.record(z.string(), z.object({ dimension: z.number() })).optional(),
openrouter: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
bedrock: 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>
Expand Down
3 changes: 2 additions & 1 deletion packages/types/src/embedding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export type EmbedderProvider =
| "mistral"
| "vercel-ai-gateway"
| "bedrock"
| "openrouter" // Add other providers as needed.
| "openrouter"
| "roo" // Add other providers as needed.

export interface EmbeddingModelProfile {
dimension: number
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/vscode-extension-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,7 @@ export interface WebviewMessage {
| "vercel-ai-gateway"
| "bedrock"
| "openrouter"
| "roo"
codebaseIndexEmbedderBaseUrl?: string
codebaseIndexEmbedderModelId: string
codebaseIndexEmbedderModelDimension?: number // Generic dimension for all providers
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/en/embeddings.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"geminiConfigMissing": "Gemini configuration missing for embedder creation",
"mistralConfigMissing": "Mistral configuration missing for embedder creation",
"openRouterConfigMissing": "OpenRouter configuration missing for embedder creation",
"rooConfigMissing": "Roo Code Cloud authentication required for embedder creation. Please sign in to Roo Code Cloud.",
"vercelAiGatewayConfigMissing": "Vercel AI Gateway configuration missing for embedder creation",
"bedrockConfigMissing": "Amazon Bedrock configuration missing for embedder creation",
"invalidEmbedderType": "Invalid embedder type configured: {{embedderProvider}}",
Expand Down
70 changes: 70 additions & 0 deletions src/services/code-index/__tests__/config-manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ import { PreviousConfigSnapshot } from "../interfaces/config"
// Mock ContextProxy
vi.mock("../../../core/config/ContextProxy")

// Mock CloudService - use vi.hoisted so variables are available when vi.mock runs
const { mockIsAuthenticated, mockCloudHasInstance } = vi.hoisted(() => ({
mockIsAuthenticated: vi.fn().mockReturnValue(false),
mockCloudHasInstance: vi.fn().mockReturnValue(false),
}))
vi.mock("@roo-code/cloud", () => ({
CloudService: {
hasInstance: mockCloudHasInstance,
get instance() {
return {
isAuthenticated: mockIsAuthenticated,
authService: {
getSessionToken: vi.fn().mockReturnValue("test-session-token"),
},
}
},
},
}))

// Mock embeddingModels module
vi.mock("../../../shared/embeddingModels")

Expand Down Expand Up @@ -1684,6 +1703,57 @@ describe("CodeIndexConfigManager", () => {
expect(configManager.isConfigured()).toBe(false)
})

it("should return true when Roo provider is authenticated and Qdrant configured", () => {
mockCloudHasInstance.mockReturnValue(true)
mockIsAuthenticated.mockReturnValue(true)

mockContextProxy.getGlobalState.mockReturnValue({
codebaseIndexEnabled: true,
codebaseIndexEmbedderProvider: "roo",
codebaseIndexQdrantUrl: "http://localhost:6333",
})
mockContextProxy.getSecret.mockReturnValue(undefined)

configManager = new CodeIndexConfigManager(mockContextProxy)
expect(configManager.isConfigured()).toBe(true)

// Cleanup
mockCloudHasInstance.mockReturnValue(false)
mockIsAuthenticated.mockReturnValue(false)
})

it("should return false when Roo provider is not authenticated", () => {
mockCloudHasInstance.mockReturnValue(true)
mockIsAuthenticated.mockReturnValue(false)

mockContextProxy.getGlobalState.mockReturnValue({
codebaseIndexEnabled: true,
codebaseIndexEmbedderProvider: "roo",
codebaseIndexQdrantUrl: "http://localhost:6333",
})
mockContextProxy.getSecret.mockReturnValue(undefined)

configManager = new CodeIndexConfigManager(mockContextProxy)
expect(configManager.isConfigured()).toBe(false)

// Cleanup
mockCloudHasInstance.mockReturnValue(false)
})

it("should return false when Roo provider has no CloudService instance", () => {
mockCloudHasInstance.mockReturnValue(false)

mockContextProxy.getGlobalState.mockReturnValue({
codebaseIndexEnabled: true,
codebaseIndexEmbedderProvider: "roo",
codebaseIndexQdrantUrl: "http://localhost:6333",
})
mockContextProxy.getSecret.mockReturnValue(undefined)

configManager = new CodeIndexConfigManager(mockContextProxy)
expect(configManager.isConfigured()).toBe(false)
})

describe("currentModelDimension", () => {
beforeEach(() => {
vi.clearAllMocks()
Expand Down
18 changes: 18 additions & 0 deletions src/services/code-index/__tests__/service-factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { CodeIndexOllamaEmbedder } from "../embedders/ollama"
import { OpenAICompatibleEmbedder } from "../embedders/openai-compatible"
import { GeminiEmbedder } from "../embedders/gemini"
import { QdrantVectorStore } from "../vector-store/qdrant-client"
import { RooEmbedder } from "../embedders/roo"

// Mock the embedders and vector store
vitest.mock("../embedders/openai")
vitest.mock("../embedders/ollama")
vitest.mock("../embedders/openai-compatible")
vitest.mock("../embedders/gemini")
vitest.mock("../embedders/roo")
vitest.mock("../vector-store/qdrant-client")

// Mock the embedding models module
Expand All @@ -33,6 +35,7 @@ const MockedCodeIndexOllamaEmbedder = CodeIndexOllamaEmbedder as MockedClass<typ
const MockedOpenAICompatibleEmbedder = OpenAICompatibleEmbedder as MockedClass<typeof OpenAICompatibleEmbedder>
const MockedGeminiEmbedder = GeminiEmbedder as MockedClass<typeof GeminiEmbedder>
const MockedQdrantVectorStore = QdrantVectorStore as MockedClass<typeof QdrantVectorStore>
const MockedRooEmbedder = RooEmbedder as MockedClass<typeof RooEmbedder>

// Import the mocked functions
import { getDefaultModelId, getModelDimension } from "../../../shared/embeddingModels"
Expand Down Expand Up @@ -345,6 +348,21 @@ describe("CodeIndexServiceFactory", () => {
expect(() => factory.createEmbedder()).toThrow("serviceFactory.geminiConfigMissing")
})

it("should create RooEmbedder when using Roo provider", () => {
// Arrange
const testConfig = {
embedderProvider: "roo",
modelId: "text-embedding-3-small",
}
mockConfigManager.getConfig.mockReturnValue(testConfig as any)

// Act
factory.createEmbedder()

// Assert
expect(MockedRooEmbedder).toHaveBeenCalledWith("text-embedding-3-small")
})

it("should throw error for invalid embedder provider", () => {
// Arrange
const testConfig = {
Expand Down
10 changes: 10 additions & 0 deletions src/services/code-index/config-manager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { CloudService } from "@roo-code/cloud"

import { ApiHandlerOptions } from "../../shared/api"
import { ContextProxy } from "../../core/config/ContextProxy"
import { EmbedderProvider } from "./interfaces/manager"
Expand Down Expand Up @@ -120,6 +122,8 @@ export class CodeIndexConfigManager {
this.embedderProvider = "bedrock"
} else if (codebaseIndexEmbedderProvider === "openrouter") {
this.embedderProvider = "openrouter"
} else if (codebaseIndexEmbedderProvider === "roo") {
this.embedderProvider = "roo"
} else {
this.embedderProvider = "openai"
}
Expand Down Expand Up @@ -272,6 +276,12 @@ export class CodeIndexConfigManager {
const qdrantUrl = this.qdrantUrl
const isConfigured = !!(apiKey && qdrantUrl)
return isConfigured
} else if (this.embedderProvider === "roo") {
// Roo Code Router uses CloudService session token for auth.
// Use isAuthenticated() for a stable auth check (not raw session token parsing).
const qdrantUrl = this.qdrantUrl
const isAuthenticated = CloudService.hasInstance() && CloudService.instance.isAuthenticated()
return !!(isAuthenticated && qdrantUrl)
}
return false // Should not happen if embedderProvider is always set correctly
}
Expand Down
Loading
Loading