refactor(gateway): own autocomplete model catalog

This commit is contained in:
Mark IJbema
2026-05-25 17:16:00 +02:00
parent 2bf49bddb6
commit 25e89db233
7 changed files with 118 additions and 101 deletions
+76
View File
@@ -0,0 +1,76 @@
export interface AutocompleteModelDef {
/** Stable setting value. */
readonly id: string
/** Model ID displayed under the selector provider group. */
readonly modelID: string
/** Human-readable label shown in settings. */
readonly label: string
/** Provider ID used by the selector group. */
readonly providerID: string
/** Provider display name for status bar / telemetry. */
readonly provider: string
/** Full model ID sent to the FIM API. */
readonly requestModel: string
/** Provider key to use for direct BYOK FIM. Empty means Kilo Gateway. */
readonly directProvider?: "mistral" | "inception"
/** FIM request temperature. */
readonly temperature: number
}
const models: AutocompleteModelDef[] = [
{
id: "kilo/mistralai/codestral-2508",
modelID: "mistralai/codestral-2508",
label: "Codestral",
providerID: "kilo",
provider: "Kilo Gateway",
requestModel: "mistralai/codestral-2508",
temperature: 0.2,
},
{
id: "kilo/inception/mercury-edit-2",
modelID: "inception/mercury-edit-2",
label: "Mercury Edit 2",
providerID: "kilo",
provider: "Kilo Gateway",
requestModel: "inception/mercury-edit-2",
temperature: 0,
},
{
id: "mistral/codestral-2508",
modelID: "codestral-2508",
label: "Codestral",
providerID: "mistral",
provider: "Mistral",
requestModel: "codestral-2508",
directProvider: "mistral",
temperature: 0.2,
},
{
id: "inception/mercury-edit-2",
modelID: "mercury-edit-2",
label: "Mercury Edit 2",
providerID: "inception",
provider: "Inception",
requestModel: "mercury-edit-2",
directProvider: "inception",
temperature: 0,
},
]
export const AUTOCOMPLETE_MODELS: readonly AutocompleteModelDef[] = models
export const DEFAULT_AUTOCOMPLETE_MODEL: AutocompleteModelDef = models[0]!
const aliases: Record<string, string> = {
"mistralai/codestral-2508": "kilo/mistralai/codestral-2508",
"inception/mercury-edit": "kilo/inception/mercury-edit-2",
}
export function getAutocompleteModel(id: string): AutocompleteModelDef {
const resolved = aliases[id] ?? id
for (const m of models) {
if (m.id === resolved) return m
}
return DEFAULT_AUTOCOMPLETE_MODEL
}
+6
View File
@@ -41,6 +41,12 @@ export {
type KiloEmbeddingModelCatalog,
} from "./api/embedding-models.js"
export { resolveKiloGatewayBaseUrl, resolveKiloOpenRouterBaseUrl } from "./api/url.js"
export {
AUTOCOMPLETE_MODELS,
DEFAULT_AUTOCOMPLETE_MODEL,
getAutocompleteModel,
type AutocompleteModelDef,
} from "./autocomplete.js"
export {
fetchOrganizationModes,
clearModesCache,
+7 -5
View File
@@ -1,4 +1,5 @@
import { HEADER_FEATURE, KILO_API_BASE } from "../api/constants.js"
import { getAutocompleteModel } from "../autocomplete.js"
import { buildKiloHeaders } from "../headers.js"
type Auth = any
@@ -18,13 +19,14 @@ const CODESTRAL_FIM_URL = "https://codestral.mistral.ai/v1/fim/completions"
const INCEPTION_FIM_URL = "https://api.inceptionlabs.ai/v1/fim/completions"
export function resolveFimTarget(model?: string): FimTarget {
if (model === "mistral/codestral-2508") {
return { provider: "mistral", model: "codestral-2508", urls: [MISTRAL_FIM_URL, CODESTRAL_FIM_URL] }
const info = getAutocompleteModel(model ?? "")
if (info.directProvider === "mistral") {
return { provider: "mistral", model: info.requestModel, urls: [MISTRAL_FIM_URL, CODESTRAL_FIM_URL] }
}
if (model === "inception-direct/mercury-edit-2") {
return { provider: "inception", model: "mercury-edit-2", urls: [INCEPTION_FIM_URL] }
if (info.directProvider === "inception") {
return { provider: "inception", model: info.requestModel, urls: [INCEPTION_FIM_URL] }
}
return { provider: "kilo", model: model ?? "mistralai/codestral-2501", urls: [KILO_FIM_URL] }
return { provider: "kilo", model: info.requestModel, urls: [KILO_FIM_URL] }
}
async function getProxyAuth(Auth: Auth) {
+16 -3
View File
@@ -3,12 +3,12 @@ import { resolveFimTarget } from "../src/server/fim"
describe("FIM target resolution", () => {
test("keeps gateway autocomplete models on Kilo Gateway", () => {
expect(resolveFimTarget("mistralai/codestral-2508")).toEqual({
expect(resolveFimTarget("kilo/mistralai/codestral-2508")).toEqual({
provider: "kilo",
model: "mistralai/codestral-2508",
urls: ["https://api.kilo.ai/api/fim/completions"],
})
expect(resolveFimTarget("inception/mercury-edit-2")).toEqual({
expect(resolveFimTarget("kilo/inception/mercury-edit-2")).toEqual({
provider: "kilo",
model: "inception/mercury-edit-2",
urls: ["https://api.kilo.ai/api/fim/completions"],
@@ -21,10 +21,23 @@ describe("FIM target resolution", () => {
model: "codestral-2508",
urls: ["https://api.mistral.ai/v1/fim/completions", "https://codestral.mistral.ai/v1/fim/completions"],
})
expect(resolveFimTarget("inception-direct/mercury-edit-2")).toEqual({
expect(resolveFimTarget("inception/mercury-edit-2")).toEqual({
provider: "inception",
model: "mercury-edit-2",
urls: ["https://api.inceptionlabs.ai/v1/fim/completions"],
})
})
test("maps legacy gateway IDs to explicit Kilo Gateway targets", () => {
expect(resolveFimTarget("mistralai/codestral-2508")).toEqual({
provider: "kilo",
model: "mistralai/codestral-2508",
urls: ["https://api.kilo.ai/api/fim/completions"],
})
expect(resolveFimTarget("inception/mercury-edit")).toEqual({
provider: "kilo",
model: "inception/mercury-edit-2",
urls: ["https://api.kilo.ai/api/fim/completions"],
})
})
})
+3 -3
View File
@@ -752,10 +752,10 @@
"kilo-code.new.autocomplete.model": {
"type": "string",
"enum": [
"mistralai/codestral-2508",
"inception/mercury-edit-2",
"kilo/mistralai/codestral-2508",
"kilo/inception/mercury-edit-2",
"mistral/codestral-2508",
"inception-direct/mercury-edit-2"
"inception/mercury-edit-2"
],
"enumDescriptions": [
"Codestral via Kilo Gateway (default)",
@@ -34,21 +34,21 @@ describe("autocomplete settings", () => {
it("defaults to codestral when no model is set", async () => {
const { buildAutocompleteSettingsMessage } = await import("../settings")
expect(buildAutocompleteSettingsMessage().settings.model).toBe("mistralai/codestral-2508")
expect(buildAutocompleteSettingsMessage().settings.model).toBe("kilo/mistralai/codestral-2508")
})
it("defaults to codestral when stored model is no longer supported", async () => {
state.set("model", "some/removed-model")
const { buildAutocompleteSettingsMessage } = await import("../settings")
expect(buildAutocompleteSettingsMessage().settings.model).toBe("mistralai/codestral-2508")
expect(buildAutocompleteSettingsMessage().settings.model).toBe("kilo/mistralai/codestral-2508")
})
it("maps legacy inception/mercury-edit to inception/mercury-edit-2", async () => {
it("maps legacy inception/mercury-edit to Kilo Gateway Mercury", async () => {
state.set("model", "inception/mercury-edit")
const { buildAutocompleteSettingsMessage } = await import("../settings")
expect(buildAutocompleteSettingsMessage().settings.model).toBe("inception/mercury-edit-2")
expect(buildAutocompleteSettingsMessage().settings.model).toBe("kilo/inception/mercury-edit-2")
})
it("validates supported model updates", async () => {
@@ -1,86 +1,6 @@
/**
* Single source of truth for autocomplete FIM model definitions.
*
* Shared between extension code (src/) and webview code (webview-ui/).
* When adding a new model, update ONLY this file and package.json's
* `kilo-code.new.autocomplete.model` enum.
*/
export interface AutocompleteModelDef {
/** Stable setting value. */
readonly id: string
/** Model ID displayed under the selector provider group. */
readonly modelID: string
/** Human-readable label shown in the settings dropdown */
readonly label: string
/** Provider ID used by the selector group. */
readonly providerID: string
/** Provider display name for status bar / telemetry */
readonly provider: string
/** Full model ID sent to the FIM API. */
readonly requestModel: string
/** Provider key to use for direct BYOK FIM. Empty means Kilo Gateway. */
readonly directProvider?: "mistral" | "inception"
/** FIM request temperature */
readonly temperature: number
}
const models: AutocompleteModelDef[] = [
{
id: "mistralai/codestral-2508",
modelID: "mistralai/codestral-2508",
label: "Codestral",
providerID: "kilo",
provider: "Kilo Gateway",
requestModel: "mistralai/codestral-2508",
temperature: 0.2,
},
{
id: "inception/mercury-edit-2",
modelID: "inception/mercury-edit-2",
label: "Mercury Edit 2",
providerID: "kilo",
provider: "Kilo Gateway",
requestModel: "inception/mercury-edit-2",
temperature: 0,
},
{
id: "mistral/codestral-2508",
modelID: "codestral-2508",
label: "Codestral",
providerID: "mistral",
provider: "Mistral",
requestModel: "codestral-2508",
directProvider: "mistral",
temperature: 0.2,
},
{
id: "inception-direct/mercury-edit-2",
modelID: "mercury-edit-2",
label: "Mercury Edit 2",
providerID: "inception",
provider: "Inception",
requestModel: "mercury-edit-2",
directProvider: "inception",
temperature: 0,
},
]
export const AUTOCOMPLETE_MODELS: readonly AutocompleteModelDef[] = models
export const DEFAULT_AUTOCOMPLETE_MODEL: AutocompleteModelDef = models[0]!
// Map inception/mercury-edit to inception/mercury-edit-2 so users who
// already have the old id saved in settings.json keep working without a
// silent fallback to Codestral. Not exposed in the dropdown.
const aliases: Record<string, string> = {
"inception/mercury-edit": "inception/mercury-edit-2",
}
export function getAutocompleteModel(id: string): AutocompleteModelDef {
const resolved = aliases[id] ?? id
for (const m of models) {
if (m.id === resolved) return m
}
return DEFAULT_AUTOCOMPLETE_MODEL
}
export {
AUTOCOMPLETE_MODELS,
DEFAULT_AUTOCOMPLETE_MODEL,
getAutocompleteModel,
type AutocompleteModelDef,
} from "@kilocode/kilo-gateway"