diff --git a/.changeset/default-lancedb-search.md b/.changeset/default-lancedb-search.md new file mode 100644 index 00000000000..ef8a8153d86 --- /dev/null +++ b/.changeset/default-lancedb-search.md @@ -0,0 +1,7 @@ +--- +"@kilocode/cli": minor +"@kilocode/kilo-indexing": minor +"kilo-code": minor +--- + +Use embedded LanceDB as the default semantic search vector store so indexing works without a separate Qdrant server. Existing Qdrant users and Intel Mac users can select `qdrant` with `indexing.vectorStore`. diff --git a/packages/kilo-docs/pages/customize/context/codebase-indexing.md b/packages/kilo-docs/pages/customize/context/codebase-indexing.md index 819ae399d7b..034748782de 100644 --- a/packages/kilo-docs/pages/customize/context/codebase-indexing.md +++ b/packages/kilo-docs/pages/customize/context/codebase-indexing.md @@ -39,7 +39,7 @@ This enables natural language queries like "user authentication logic" or "datab 1. Open Kilo Code **Settings** → **Indexing**, or click the indexing indicator at the bottom of the prompt input panel. 2. Turn on **Global Enable** to index every workspace, or turn on **Enable for This Project** to index only the current workspace. Both toggles are off until explicitly enabled. 3. Pick an **Embedding Provider** and fill in its required fields. -4. Pick a **Vector Store** (`Qdrant` or `LanceDB`) and configure it. +4. Pick a **Vector Store** (`LanceDB` or `Qdrant`) and configure it. 5. Optionally adjust **Tuning Parameters** (search score, batch size, retries, max results). 6. Save to start the initial scan. @@ -74,8 +74,12 @@ You can also edit the `indexing` section in `kilo.jsonc` directly: ### Vector stores -- **Qdrant** (default) — external server. Recommended for team deployments and larger codebases. See [Setting Up Qdrant](#setting-up-qdrant). -- **LanceDB** — embedded, file-based. No server to run. Stores data under your Kilo data directory by default. +- **LanceDB** (default). Embedded and file-based, with no server to run. Stores data under your Kilo data directory by default. +- **Qdrant**. External server recommended for team deployments and larger codebases. See [Setting Up Qdrant](#setting-up-qdrant). + +{% callout type="warning" title="Intel Macs" %} +LanceDB does not support Intel Macs. Select **Qdrant** and configure a Qdrant server instead. +{% /callout %} {% callout type="tip" %} For a fully local, zero-cost setup, combine **Ollama** (embeddings) with **LanceDB** (vector store — no separate server needed). @@ -106,7 +110,7 @@ This opens an interactive configuration dialog where you can: - Choose an **Embedding Provider** and fill in provider settings (API key, base URL, AWS region, etc.) - Set the **Embedding Model** (blank = provider default) - Set the **Vector Dimension** (blank = auto-detect from the model) -- Choose a **Vector Store** (`Qdrant` or `LanceDB`) and configure its connection +- Choose a **Vector Store** (`LanceDB` or `Qdrant`) and configure its connection - Adjust **Tuning Parameters** (search threshold, batch size, retries, max results) All changes are written to your `kilo.jsonc` config and take effect immediately. @@ -120,14 +124,11 @@ You can also edit the `indexing` section directly. This is the full shape of the "provider": "voyage", "model": "voyage-code-3", "dimension": 1024, - "vectorStore": "qdrant", + "vectorStore": "lancedb", "voyage": { "apiKey": "pa-..." }, - "qdrant": { - "url": "http://localhost:6333", - "apiKey": "" - }, + "lancedb": {}, "searchMinScore": 0.4, "searchMaxResults": 50, "embeddingBatchSize": 60, @@ -152,8 +153,8 @@ You can also edit the `indexing` section directly. This is the full shape of the ### Vector stores -- `qdrant` — `{ url?, apiKey? }` (default). See [Setting Up Qdrant](#setting-up-qdrant). -- `lancedb` — `{ directory? }` — embedded, file-based. No server to run. Uses a default Kilo data directory when omitted. +- `lancedb` uses `{ directory? }` and is the default. It is embedded and file-based, with no server to run. Kilo uses its data directory when `directory` is omitted. +- `qdrant` uses `{ url?, apiKey? }`. See [Setting Up Qdrant](#setting-up-qdrant). {% callout type="tip" %} For a fully local, zero-cost setup, combine **Ollama** (embeddings) with **LanceDB** (vector store — no separate server needed). diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/indexing-kilo-catalog-loading-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/indexing-kilo-catalog-loading-chromium-linux.png index 6f601a9608e..5203e5d55fc 100644 --- a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/indexing-kilo-catalog-loading-chromium-linux.png +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/settings/indexing-kilo-catalog-loading-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d70e12fc78142601de74977fea330a91f3662851cc8f56023a6ec819724dbb8 -size 48958 +oid sha256:810d109acde3456d250df74029ac031929cb4b2fffe13a4c765cda9d2a62f175 +size 46116 diff --git a/packages/kilo-indexing/src/config.ts b/packages/kilo-indexing/src/config.ts index a4477388036..b68097af855 100644 --- a/packages/kilo-indexing/src/config.ts +++ b/packages/kilo-indexing/src/config.ts @@ -1,8 +1,11 @@ import { Schema } from "effect" import z from "zod" import type { IndexingConfigInput } from "./indexing/config-manager" +import { DEFAULT_VECTOR_STORE } from "./indexing/constants" import type { EmbedderProvider } from "./indexing/interfaces/manager" +export { DEFAULT_VECTOR_STORE } from "./indexing/constants" + const providers = [ "kilo", "openai", @@ -29,7 +32,7 @@ export const IndexingConfig = z .nullable() .optional() .describe("Override embedding vector dimension (auto-detected from model if omitted)"), - vectorStore: z.enum(stores).optional().describe("Vector store backend (default: qdrant)"), + vectorStore: z.enum(stores).optional().describe("Vector store backend (default: lancedb)"), kilo: z .object({ apiKey: z.string().optional(), @@ -147,7 +150,7 @@ export const IndexingSchema = Schema.Struct({ dimension: Schema.optional(Schema.NullOr(PositiveInt)).annotate({ description: "Override embedding vector dimension (auto-detected from model if omitted)", }), - vectorStore: Schema.optional(Store).annotate({ description: "Vector store backend (default: qdrant)" }), + vectorStore: Schema.optional(Store).annotate({ description: "Vector store backend (default: lancedb)" }), kilo: Schema.optional( Schema.Struct({ apiKey: Schema.optional(Schema.String), @@ -237,7 +240,7 @@ export function toIndexingConfigInput(cfg: IndexingConfig | undefined): Indexing return { enabled: cfg?.enabled ?? false, embedderProvider: provider, - vectorStoreProvider: cfg?.vectorStore, + vectorStoreProvider: cfg?.vectorStore ?? DEFAULT_VECTOR_STORE, modelId: cfg?.model ?? undefined, modelDimension: cfg?.dimension ?? undefined, lancedbVectorStoreDirectory: cfg?.lancedb?.directory, diff --git a/packages/kilo-indexing/src/indexing/config-manager.ts b/packages/kilo-indexing/src/indexing/config-manager.ts index fd8c63210d9..9b12556c5b8 100644 --- a/packages/kilo-indexing/src/indexing/config-manager.ts +++ b/packages/kilo-indexing/src/indexing/config-manager.ts @@ -1,6 +1,6 @@ import type { EmbedderProvider } from "./interfaces/manager" import type { CodeIndexConfig, PreviousConfigSnapshot } from "./interfaces/config" -import { DEFAULT_SEARCH_MIN_SCORE, DEFAULT_MAX_SEARCH_RESULTS } from "./constants" +import { DEFAULT_SEARCH_MIN_SCORE, DEFAULT_MAX_SEARCH_RESULTS, DEFAULT_VECTOR_STORE } from "./constants" import { getDefaultModelId, getModelDimension, getModelScoreThreshold } from "./model-registry" import { isEmbeddingProfileEqual, resolveEmbeddingProfile } from "./embedding-profile" @@ -50,7 +50,7 @@ export interface IndexingConfigInput { export class CodeIndexConfigManager { private enabled = false private embedderProvider: EmbedderProvider = "openai" - private vectorStoreProvider: "lancedb" | "qdrant" = "qdrant" + private vectorStoreProvider: "lancedb" | "qdrant" = DEFAULT_VECTOR_STORE private lancedbVectorStoreDirectory?: string private modelId?: string private modelDimension?: number @@ -88,7 +88,7 @@ export class CodeIndexConfigManager { private applyInput(input: IndexingConfigInput): void { this.enabled = input.enabled this.embedderProvider = input.embedderProvider - this.vectorStoreProvider = input.vectorStoreProvider ?? "qdrant" + this.vectorStoreProvider = input.vectorStoreProvider ?? DEFAULT_VECTOR_STORE this.lancedbVectorStoreDirectory = input.lancedbVectorStoreDirectory this.qdrantUrl = input.qdrantUrl ?? "http://localhost:6333" this.qdrantApiKey = input.qdrantApiKey @@ -196,7 +196,7 @@ export class CodeIndexConfigManager { if (prevProvider !== this.embedderProvider) return true // Vector store provider change - if ((prev.vectorStoreProvider ?? "qdrant") !== this.vectorStoreProvider) return true + if ((prev.vectorStoreProvider ?? DEFAULT_VECTOR_STORE) !== this.vectorStoreProvider) return true // LanceDB path change if ( @@ -258,7 +258,7 @@ export class CodeIndexConfigManager { return { isConfigured: this.isConfigured(), embedderProvider: this.embedderProvider, - vectorStoreProvider: this.vectorStoreProvider ?? "qdrant", + vectorStoreProvider: this.vectorStoreProvider, lancedbVectorStoreDirectoryPlaceholder: this.lancedbVectorStoreDirectory, modelId: this.modelId, modelDimension: this.modelDimension, diff --git a/packages/kilo-indexing/src/indexing/constants/index.ts b/packages/kilo-indexing/src/indexing/constants/index.ts index a381a6351c0..ef8a6666733 100644 --- a/packages/kilo-indexing/src/indexing/constants/index.ts +++ b/packages/kilo-indexing/src/indexing/constants/index.ts @@ -1,6 +1,8 @@ /** * Codebase Index Constants */ +export const DEFAULT_VECTOR_STORE = "lancedb" as const + export const CODEBASE_INDEX_DEFAULTS = { MIN_SEARCH_RESULTS: 10, MAX_SEARCH_RESULTS: 200, diff --git a/packages/kilo-indexing/src/indexing/manager.ts b/packages/kilo-indexing/src/indexing/manager.ts index 5fc017f18c5..3dbd8c3b428 100644 --- a/packages/kilo-indexing/src/indexing/manager.ts +++ b/packages/kilo-indexing/src/indexing/manager.ts @@ -2,7 +2,7 @@ import type { VectorStoreSearchResult } from "./interfaces" import type { IndexingState } from "./interfaces/manager" import type { IndexingTelemetryEvent, IndexingTelemetryMeta, IndexingTelemetryTrigger } from "./interfaces/telemetry" import { CodeIndexConfigManager, type IndexingConfigInput } from "./config-manager" -import { INITIAL_MANAGER_RECOVERY_DELAY_MS, MAX_MANAGER_RECOVERY_ATTEMPTS } from "./constants" +import { DEFAULT_VECTOR_STORE, INITIAL_MANAGER_RECOVERY_DELAY_MS, MAX_MANAGER_RECOVERY_ATTEMPTS } from "./constants" import { CodeIndexStateManager } from "./state-manager" import { CodeIndexServiceFactory } from "./service-factory" import { CodeIndexSearchService } from "./search-service" @@ -60,7 +60,7 @@ export class CodeIndexManager { const cfg = this._configManager.getConfig() return { provider: cfg.embedderProvider, - vectorStore: cfg.vectorStoreProvider ?? "qdrant", + vectorStore: cfg.vectorStoreProvider ?? DEFAULT_VECTOR_STORE, modelId: cfg.modelId, } } diff --git a/packages/kilo-indexing/src/indexing/orchestrator.ts b/packages/kilo-indexing/src/indexing/orchestrator.ts index f20805bfc10..06c1f97d41b 100644 --- a/packages/kilo-indexing/src/indexing/orchestrator.ts +++ b/packages/kilo-indexing/src/indexing/orchestrator.ts @@ -16,6 +16,7 @@ import type { CacheManager } from "./cache-manager" import type { Disposable } from "./runtime" import { Log } from "../util/log" import { sanitizeErrorMessage } from "./shared/validation-helpers" +import { DEFAULT_VECTOR_STORE } from "./constants" const log = Log.create({ service: "indexing-orchestrator" }) @@ -39,7 +40,7 @@ export class CodeIndexOrchestrator { const cfg = this.configManager.getConfig() return { provider: cfg.embedderProvider, - vectorStore: cfg.vectorStoreProvider ?? "qdrant", + vectorStore: cfg.vectorStoreProvider ?? DEFAULT_VECTOR_STORE, modelId: cfg.modelId, } } diff --git a/packages/kilo-indexing/src/indexing/service-factory.ts b/packages/kilo-indexing/src/indexing/service-factory.ts index aa8f14eef7e..9dbaac9c8a1 100644 --- a/packages/kilo-indexing/src/indexing/service-factory.ts +++ b/packages/kilo-indexing/src/indexing/service-factory.ts @@ -23,6 +23,7 @@ import type { CacheManager } from "./cache-manager" import type { IndexingTelemetryMeta, IndexingTelemetryReporter } from "./interfaces/telemetry" import { BATCH_SEGMENT_THRESHOLD, + DEFAULT_VECTOR_STORE, OLLAMA_EMBEDDER_REQUEST_TIMEOUT_MS, REMOTE_EMBEDDER_VALIDATION_TIMEOUT_MS, } from "./constants" @@ -55,7 +56,7 @@ export class CodeIndexServiceFactory { const cfg = this.configManager.getConfig() return { provider: cfg.embedderProvider, - vectorStore: cfg.vectorStoreProvider ?? "qdrant", + vectorStore: cfg.vectorStoreProvider ?? DEFAULT_VECTOR_STORE, modelId: cfg.modelId, } } diff --git a/packages/kilo-indexing/test/kilocode/indexing/config-manager.test.ts b/packages/kilo-indexing/test/kilocode/indexing/config-manager.test.ts index 4ba3338df49..9d80b1d759d 100644 --- a/packages/kilo-indexing/test/kilocode/indexing/config-manager.test.ts +++ b/packages/kilo-indexing/test/kilocode/indexing/config-manager.test.ts @@ -1,4 +1,5 @@ import { describe, expect, test } from "bun:test" +import { toIndexingConfigInput } from "../../../src/config" import { CodeIndexConfigManager, type IndexingConfigInput } from "../../../src/indexing/config-manager" function createInput(input: Partial = {}): IndexingConfigInput { @@ -25,9 +26,21 @@ describe("CodeIndexConfigManager", () => { expect(cfg.getConfig().ollamaOptions?.baseUrl).toBe("http://localhost:11434") }) - test("defaults vector store to qdrant when omitted", () => { + test("defaults vector store to LanceDB when omitted", () => { const cfg = new CodeIndexConfigManager(createInput({ vectorStoreProvider: undefined })) + expect(cfg.getConfig().vectorStoreProvider).toBe("lancedb") + }) + + test("normalizes omitted vector store config to LanceDB for hosts", () => { + expect(toIndexingConfigInput(undefined).vectorStoreProvider).toBe("lancedb") + }) + + test("preserves an explicit Qdrant override", () => { + const input = toIndexingConfigInput({ vectorStore: "qdrant" }) + const cfg = new CodeIndexConfigManager(input) + + expect(input.vectorStoreProvider).toBe("qdrant") expect(cfg.getConfig().vectorStoreProvider).toBe("qdrant") }) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/IndexingTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/IndexingTab.tsx index 188250c315c..02e3c7870cf 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/IndexingTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/IndexingTab.tsx @@ -1,5 +1,6 @@ import { Component, For, Show, createMemo, createSignal } from "solid-js" import { Card } from "@kilocode/kilo-ui/card" +import { DEFAULT_VECTOR_STORE } from "@kilocode/kilo-indexing/config" import { formatKiloEmbeddingModelLabel, getKiloEmbeddingModel } from "@kilocode/kilo-indexing/embedding-models" import { Select } from "@kilocode/kilo-ui/select" import { Switch } from "@kilocode/kilo-ui/switch" @@ -32,8 +33,8 @@ const allProviders: { value: ProviderId; label: string }[] = [ ] const stores: Option[] = [ - { value: "qdrant", label: "Qdrant (default)" }, - { value: "lancedb", label: "LanceDB" }, + { value: "lancedb", label: "LanceDB (default)" }, + { value: "qdrant", label: "Qdrant" }, ] const tuning: Array<{ key: TuningKey; label: string; placeholder: string }> = [ @@ -91,7 +92,7 @@ const IndexingTab: Component = () => { updateConfig({ indexing: { ...cfg(), ...partial } }) } - const vectorStore = () => cfg().vectorStore ?? "qdrant" + const vectorStore = () => cfg().vectorStore ?? DEFAULT_VECTOR_STORE const kiloDefault = () => getKiloEmbeddingModel(embeds.catalog().defaultModel, embeds.catalog())?.id ?? embeds.catalog().defaultModel const kiloModels = createMemo(() => diff --git a/packages/opencode/src/kilocode/components/dialog-indexing.tsx b/packages/opencode/src/kilocode/components/dialog-indexing.tsx index f1c43b4429b..793b2fd10c5 100644 --- a/packages/opencode/src/kilocode/components/dialog-indexing.tsx +++ b/packages/opencode/src/kilocode/components/dialog-indexing.tsx @@ -9,6 +9,7 @@ import { useDialog } from "@tui/ui/dialog" import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select" import { DialogPrompt } from "@tui/ui/dialog-prompt" +import { DEFAULT_VECTOR_STORE } from "@kilocode/kilo-indexing/config" import { formatKiloEmbeddingModelLabel } from "@kilocode/kilo-indexing/embedding-models" import { fetchKiloEmbeddingModelCatalog } from "@kilocode/kilo-gateway" import { useSync } from "@tui/context/sync" @@ -62,8 +63,8 @@ const PROVIDER_FIELDS: Record = { } const VECTOR_STORE_LABELS: Record = { - qdrant: "Qdrant (default)", - lancedb: "LanceDB", + lancedb: "LanceDB (default)", + qdrant: "Qdrant", } function maskSecret(value: string | undefined): string { @@ -316,14 +317,14 @@ function VectorStoreSelect(props: SubDialogProps) { const options: DialogSelectOption[] = Object.entries(VECTOR_STORE_LABELS).map(([value, title]) => ({ value, title, - description: value === (indexing.vectorStore ?? "qdrant") ? "(current)" : undefined, + description: value === (indexing.vectorStore ?? DEFAULT_VECTOR_STORE) ? "(current)" : undefined, })) return ( { const store = option.value as "lancedb" | "qdrant" if (store === "lancedb") { @@ -480,9 +481,8 @@ export function DialogIndexing(props: DialogIndexingProps) { const indexing = defaultIndexing(sync, globalCfg()) const providerLabel = indexing.provider ? PROVIDER_LABELS[indexing.provider] : "not set" - const storeLabel = indexing.vectorStore - ? (VECTOR_STORE_LABELS[indexing.vectorStore] ?? indexing.vectorStore) - : "Qdrant (default)" + const store = indexing.vectorStore ?? DEFAULT_VECTOR_STORE + const storeLabel = VECTOR_STORE_LABELS[store] ?? store const tuningCount = TUNING_PARAMS.filter((p) => indexing[p.key] !== undefined).length const tuningDesc = tuningCount > 0 ? `${tuningCount} customized` : "defaults" diff --git a/packages/opencode/src/kilocode/lancedb.ts b/packages/opencode/src/kilocode/lancedb.ts index d042b8ff7ec..7d8099b88c1 100644 --- a/packages/opencode/src/kilocode/lancedb.ts +++ b/packages/opencode/src/kilocode/lancedb.ts @@ -25,6 +25,11 @@ export namespace LanceDBRuntime { export async function ensure(store?: string) { if (store !== "lancedb") return if (process.env[env]) return + if (process.platform === "darwin" && process.arch === "x64") { + throw new Error( + 'LanceDB is not supported on Intel Macs. Set "indexing.vectorStore" to "qdrant" and configure a Qdrant server.', + ) + } if (box.ready) return box.ready box.ready = (async () => { diff --git a/packages/opencode/test/kilocode/lancedb-runtime.test.ts b/packages/opencode/test/kilocode/lancedb-runtime.test.ts index 9252ed986ef..db9d9bb496d 100644 --- a/packages/opencode/test/kilocode/lancedb-runtime.test.ts +++ b/packages/opencode/test/kilocode/lancedb-runtime.test.ts @@ -39,6 +39,24 @@ describe("LanceDBRuntime", () => { expect(process.env[env]).toBeUndefined() }) + test("guides Intel Mac users to Qdrant", async () => { + const { LanceDBRuntime } = await import("../../src/kilocode/lancedb") + const platform = Object.getOwnPropertyDescriptor(process, "platform")! + const arch = Object.getOwnPropertyDescriptor(process, "arch")! + Object.defineProperty(process, "platform", { ...platform, value: "darwin" }) + Object.defineProperty(process, "arch", { ...arch, value: "x64" }) + + try { + await expect(LanceDBRuntime.ensure("lancedb")).rejects.toThrow( + 'LanceDB is not supported on Intel Macs. Set "indexing.vectorStore" to "qdrant" and configure a Qdrant server.', + ) + expect(add).not.toHaveBeenCalled() + } finally { + Object.defineProperty(process, "platform", platform) + Object.defineProperty(process, "arch", arch) + } + }) + test("installs the pinned package and exports a file URL for lancedb", async () => { const { LanceDBRuntime } = await import("../../src/kilocode/lancedb")