Merge branch 'main' into basalt-dirt

This commit is contained in:
Kirill Kalishev
2026-06-23 13:42:27 -04:00
committed by GitHub
8 changed files with 100 additions and 11 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"kilo-code": patch
"@kilocode/kilo-gateway": patch
---
Use the matching FIM model for chat autocomplete when Next Edit is selected.
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Preserve unchanged codebase indexes when extension or VS Code updates interrupt an incremental scan.
+18 -7
View File
@@ -1,7 +1,7 @@
export type AutocompleteProviderID = "kilo" | "mistral" | "inception"
export type DirectAutocompleteProviderID = Exclude<AutocompleteProviderID, "kilo">
export interface AutocompleteModelDef {
interface AutocompleteModelBase {
/** Stable combined value for internal comparisons. */
readonly id: string
/** Model value stored in settings and sent to the autocomplete API. */
@@ -18,14 +18,23 @@ export interface AutocompleteModelDef {
readonly directProvider?: DirectAutocompleteProviderID
/** Request temperature. */
readonly temperature: number
/**
* Which gateway endpoint this model targets. Defaults to "fim" if omitted
* (back-compat with existing entries). Models with `kind: "edit"` route
* through `/kilo/edit` and use Mercury's Next Edit pipeline.
*/
readonly kind?: "fim" | "edit"
}
export type AutocompleteModelDef = AutocompleteModelBase &
(
| {
/** Route through `/kilo/edit` using the Next Edit pipeline. */
readonly kind: "edit"
/** Stable combined ID of the FIM model used where Next Edit is unsupported. */
readonly fimModelID: string
}
| {
/** Route through the FIM endpoint. */
readonly kind?: "fim"
readonly fimModelID?: never
}
)
const models: AutocompleteModelDef[] = [
{
id: "kilo/mistralai/codestral-2508",
@@ -57,6 +66,7 @@ const models: AutocompleteModelDef[] = [
requestModel: "inception/mercury-edit-2",
temperature: 0,
kind: "edit",
fimModelID: "kilo/inception/mercury-edit-2",
},
{
id: "mistral/codestral-2508",
@@ -91,6 +101,7 @@ const models: AutocompleteModelDef[] = [
directProvider: "inception",
temperature: 0,
kind: "edit",
fimModelID: "inception/mercury-edit-2",
},
]
@@ -18,3 +18,15 @@ describe("DEFAULT_AUTOCOMPLETE_MODEL", () => {
expect(DEFAULT_AUTOCOMPLETE_MODEL.kind).toBe("edit")
})
})
describe("Next Edit FIM models", () => {
test("reference a FIM model from the same provider", () => {
for (const model of AUTOCOMPLETE_MODELS) {
if (model.kind !== "edit") continue
const sibling = AUTOCOMPLETE_MODELS.find((candidate) => candidate.id === model.fimModelID)
expect(sibling).toBeDefined()
expect(sibling?.kind).not.toBe("edit")
expect(sibling?.providerID).toBe(model.providerID)
}
})
})
@@ -277,6 +277,8 @@ export class CodeIndexOrchestrator {
private async _runScan(mode: IndexingTelemetryMode, trigger: IndexingTelemetryTrigger): Promise<void> {
if (this._cancelRequested) {
if (mode === "incremental") await this.vectorStore.markIndexingComplete()
this.stateManager.setSystemState("Standby", "Indexing cancelled.")
log.info("scan skipped: cancellation was requested", { workspacePath: this.workspacePath, mode })
return
}
@@ -319,6 +321,10 @@ export class CodeIndexOrchestrator {
})
if (this._cancelRequested || this.scanner.isCancelled) {
if (mode === "incremental" && result.stats.processed === 0 && batchErrors.length === 0) {
await this.vectorStore.markIndexingComplete()
log.info("preserved unchanged index after cancelled scan", { workspacePath: this.workspacePath })
}
this._isProcessing = false
if (this.stateManager.state !== "Error") {
this.stateManager.setSystemState("Standby", "Indexing cancelled.")
@@ -18,7 +18,9 @@ import { Emitter } from "../../../src/indexing/runtime"
class Store {
public clearCount = 0
public closeCount = 0
public completeCount = 0
public deleteCount = 0
public incompleteCount = 0
constructor(
private readonly existing: boolean,
@@ -57,8 +59,12 @@ class Store {
async hasIndexedData(): Promise<boolean> {
return this.existing
}
async markIndexingComplete(): Promise<void> {}
async markIndexingIncomplete(): Promise<void> {}
async markIndexingComplete(): Promise<void> {
this.completeCount += 1
}
async markIndexingIncomplete(): Promise<void> {
this.incompleteCount += 1
}
}
class Scanner {
@@ -270,6 +276,31 @@ describe("CodeIndexOrchestrator telemetry", () => {
expect(scanner.finished).toBe(true)
expect(store.closeCount).toBe(1)
expect(store.incompleteCount).toBe(1)
expect(store.completeCount).toBe(0)
})
test("preserves an unchanged index when an incremental scan is interrupted", async () => {
const scanner = new BlockingScanner()
const store = new Store(true)
const orchestrator = new CodeIndexOrchestrator(
createConfig(),
new CodeIndexStateManager(),
"/tmp/ws",
{ async clearCacheFile() {}, async flush() {} } as unknown as CacheManager,
store as unknown as IVectorStore,
scanner as unknown as DirectoryScanner,
new Watcher() as unknown as IFileWatcher,
)
const active = orchestrator.startIndexing("background")
await scanner.started.promise
await orchestrator.shutdown()
await active
expect(store.incompleteCount).toBe(1)
expect(store.completeCount).toBe(1)
expect(store.clearCount).toBe(0)
})
test("clears stale vectors and hashes before rebuilding an incomplete store", async () => {
@@ -7,7 +7,7 @@ import { VisibleCodeTracker } from "../context/VisibleCodeTracker"
import { FileIgnoreController } from "../shims/FileIgnoreController"
import type { KiloConnectionService } from "../../cli-backend"
import { generateFim, hasValidCredentials } from "../fim"
import { getAutocompleteModel } from "../../../shared/autocomplete-models"
import { getAutocompleteModel, getAutocompleteModelById } from "../../../shared/autocomplete-models"
import { finalizeChatSuggestion, buildChatPrefix } from "./chat-autocomplete-utils"
interface ChatCompletionRequestMessage {
@@ -20,6 +20,12 @@ interface ChatCompletionResponseSender {
postMessage(message: { type: "chatCompletionResult"; text: string; requestId: string }): void
}
export function getChatAutocompleteModel(provider?: string, model?: string) {
const info = getAutocompleteModel(provider, model)
if (info.kind !== "edit") return info
return getAutocompleteModelById(info.fimModelID)
}
/**
* Chat textarea autocomplete with cached per-request objects.
*
@@ -77,7 +83,7 @@ export class ChatTextAreaAutocomplete {
async getCompletion(userText: string, visibleCodeContext?: VisibleCodeContext): Promise<{ suggestion: string }> {
const cfg = vscode.workspace.getConfiguration("kilo-code.new.autocomplete")
const entry = getAutocompleteModel(cfg.get<string>("provider"), cfg.get<string>("model"))
const entry = getChatAutocompleteModel(cfg.get<string>("provider"), cfg.get<string>("model"))
const startTime = Date.now()
// Build context for telemetry
@@ -3,6 +3,7 @@ import {
finalizeChatSuggestion,
buildChatPrefix,
} from "../../src/services/autocomplete/chat-autocomplete/chat-autocomplete-utils"
import { getChatAutocompleteModel } from "../../src/services/autocomplete/chat-autocomplete/ChatTextAreaAutocomplete"
describe("finalizeChatSuggestion", () => {
it("returns empty string for empty input", () => {
@@ -98,3 +99,14 @@ describe("buildChatPrefix", () => {
expect(result).toContain("hi")
})
})
describe("getChatAutocompleteModel", () => {
it("uses the matching FIM model for Next Edit settings", () => {
expect(getChatAutocompleteModel("kilo", "inception/mercury-next-edit").id).toBe("kilo/inception/mercury-edit-2")
expect(getChatAutocompleteModel("inception", "mercury-next-edit").id).toBe("inception/mercury-edit-2")
})
it("keeps FIM settings unchanged", () => {
expect(getChatAutocompleteModel("kilo", "mistralai/codestral-2508").id).toBe("kilo/mistralai/codestral-2508")
})
})