diff --git a/packages/kilo-gateway/src/api/models.ts b/packages/kilo-gateway/src/api/models.ts index 1a89769650d..b5bb9ee2467 100644 --- a/packages/kilo-gateway/src/api/models.ts +++ b/packages/kilo-gateway/src/api/models.ts @@ -30,6 +30,16 @@ const openRouterModelSchema = z.object({ top_provider: z.object({ max_completion_tokens: z.number().nullish() }).optional(), supported_parameters: z.array(z.string()).optional(), preferredIndex: z.number().optional(), + opencode: z + .object({ + family: z.string().optional(), + prompt: z + .enum(["codex", "gemini", "beast", "anthropic", "trinity", "anthropic_without_todo"]) + .optional() + .catch(undefined), + variants: z.record(z.string(), z.record(z.string(), z.any())).optional(), + }) + .optional(), }) const openRouterModelsResponseSchema = z.object({ @@ -143,11 +153,14 @@ function transformToModelDevFormat(model: OpenRouterModel): any { return { id: model.id, name: model.name, - family: model.id === "kilo/auto" ? "kilo/auto" : extractFamily(model.id), // kilocode_change + family: model.opencode?.family ?? extractFamily(model.id), release_date: new Date().toISOString().split("T")[0], // Default to today attachment: supportsImages, reasoning: supportsReasoning, temperature: supportsTemperature, + recommendedIndex: model.preferredIndex, + variants: model.opencode?.variants, + prompt: model.opencode?.prompt, tool_call: supportsTools, ...(inputPrice !== undefined && outputPrice !== undefined && { @@ -168,10 +181,6 @@ function transformToModelDevFormat(model: OpenRouterModel): any { output: mapModalities(outputModalities), }, }), - ...(model.preferredIndex !== undefined && { - recommended: true, - recommendedIndex: model.preferredIndex, - }), options: { ...(model.description && { description: model.description }), }, diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx index 189c3a26f9b..dce4b579282 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx @@ -86,7 +86,7 @@ export function DialogModel(props: { providerID?: string }) { : undefined, // kilocode_change start category: connected() - ? provider.id === "kilo" && info.recommended + ? provider.id === "kilo" && info.recommendedIndex !== undefined ? "Recommended" : provider.name : undefined, diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts index e88842ad394..5d61672458a 100644 --- a/packages/opencode/src/provider/models.ts +++ b/packages/opencode/src/provider/models.ts @@ -27,7 +27,10 @@ const normalizeKiloBaseURL = (baseURL: string | undefined, orgId: string | undef if (trimmed.includes("/openrouter")) return trimmed if (trimmed.endsWith("/api")) return `${trimmed}/openrouter` return `${trimmed}/api/openrouter` -} // kilocode_change end +} + +export const Prompt = z.enum(["codex", "gemini", "beast", "anthropic", "trinity", "anthropic_without_todo"]) +// kilocode_change end export namespace ModelsDev { const log = Log.create({ service: "models.dev" }) @@ -79,8 +82,12 @@ export namespace ModelsDev { output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])), }) .optional(), - recommended: z.boolean().optional(), // kilocode_change - recommendedIndex: z.number().optional(), // kilocode_change + + // kilocode_change start + recommendedIndex: z.number().optional(), + prompt: Prompt.optional().catch(undefined), + // kilocode_change end + experimental: z.boolean().optional(), status: z.enum(["alpha", "beta", "deprecated"]).optional(), options: z.record(z.string(), z.any()), diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index f0ccbdc1255..f5ed0917631 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -7,7 +7,10 @@ import { NoSuchModelError, type Provider as SDK } from "ai" import { Log } from "../util/log" import { BunProc } from "../bun" import { Plugin } from "../plugin" -import { ModelsDev } from "./models" +import { + ModelsDev, + Prompt, // kilocode_change +} from "./models" import { NamedError } from "@opencode-ai/util/error" import { Auth } from "../auth" import { Env } from "../env" @@ -663,8 +666,11 @@ export namespace Provider { headers: z.record(z.string(), z.string()), release_date: z.string(), variants: z.record(z.string(), z.record(z.string(), z.any())).optional(), - recommended: z.boolean().optional(), // kilocode_change - recommendedIndex: z.number().optional(), // kilocode_change + + // kilocode_change start + recommendedIndex: z.number().optional(), + prompt: Prompt.optional().catch(undefined), + // kilocode_change end }) .meta({ ref: "Model", @@ -745,9 +751,12 @@ export namespace Provider { interleaved: model.interleaved ?? false, }, release_date: model.release_date, - variants: {}, - recommended: model.recommended, // kilocode_change - recommendedIndex: model.recommendedIndex, // kilocode_change + + // kilocode_change start + variants: provider.id === "kilo" ? (model.variants ?? {}) : {}, + recommendedIndex: model.recommendedIndex, + prompt: model.prompt, + // kilocode_change end } m.variants = mapValues(ProviderTransform.variants(m), (v) => v) @@ -891,8 +900,11 @@ export namespace Provider { family: model.family ?? existingModel?.family ?? "", release_date: model.release_date ?? existingModel?.release_date ?? "", variants: {}, - recommended: model.recommended ?? existingModel?.recommended, // kilocode_change - recommendedIndex: model.recommendedIndex ?? existingModel?.recommendedIndex, // kilocode_change + + // kilocode_change start + recommendedIndex: model.recommendedIndex ?? existingModel?.recommendedIndex, + prompt: model.prompt ?? existingModel?.prompt, + // kilocode_change end } const merged = mergeDeep(ProviderTransform.variants(parsedModel), model.variants ?? {}) parsedModel.variants = mapValues( diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 7f6c44b858c..9bc9aa2abca 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -346,6 +346,12 @@ export namespace ProviderTransform { const OPENAI_EFFORTS = ["none", "minimal", ...WIDELY_SUPPORTED_EFFORTS, "xhigh"] export function variants(model: Provider.Model): Record> { + // kilocode_change start + if (model.api.npm === "@kilocode/kilo-gateway" && model.variants && Object.keys(model.variants).length > 0) { + return model.variants + } + // kilocode_change end + if (!model.capabilities.reasoning) return {} const id = model.id.toLowerCase() @@ -381,27 +387,11 @@ export namespace ProviderTransform { if (id.includes("grok")) return {} switch (model.api.npm) { + case "@kilocode/kilo-gateway": // kilocode_change case "@openrouter/ai-sdk-provider": if (!model.id.includes("gpt") && !model.id.includes("gemini-3") && !model.id.includes("claude")) return {} return Object.fromEntries(OPENAI_EFFORTS.map((effort) => [effort, { reasoning: { effort } }])) - // kilocode_change start - case "@kilocode/kilo-gateway": - if (model.id.includes("claude")) { - // for models that support adaptive thinking, effort is ignored - // for models that don't support adaptive thinking, effort is translated into a token budget - return { - none: { reasoning: { enabled: false } }, - low: { reasoning: { enabled: true, effort: "low" }, verbosity: "low" }, - medium: { reasoning: { enabled: true, effort: "medium" }, verbosity: "medium" }, - high: { reasoning: { enabled: true, effort: "high" }, verbosity: "high" }, - max: { reasoning: { enabled: true, effort: "xhigh" }, verbosity: "max" }, - } - } - if (!model.id.includes("gpt") && !model.id.includes("gemini-3")) return {} - return Object.fromEntries(OPENAI_EFFORTS.map((effort) => [effort, { reasoning: { effort } }])) - // kilocode_change end - // TODO: YOU CANNOT SET max_tokens if this is set!!! case "@ai-sdk/gateway": if (model.id.includes("anthropic")) { diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index 655500cc373..4c793c014b8 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -28,6 +28,23 @@ export namespace SystemPrompt { // kilocode_change end export function provider(model: Provider.Model) { + // kilocode_change start + switch (model.prompt) { + case "anthropic": + return [PROMPT_ANTHROPIC] + case "anthropic_without_todo": + return [PROMPT_ANTHROPIC_WITHOUT_TODO] + case "beast": + return [PROMPT_BEAST] + case "codex": + return [PROMPT_CODEX] + case "gemini": + return [PROMPT_GEMINI] + case "trinity": + return [PROMPT_TRINITY] + } + // kilocode_change end + if (model.api.id.includes("gpt-5")) return [PROMPT_CODEX] if (model.api.id.includes("gpt-") || model.api.id.includes("o1") || model.api.id.includes("o3")) return [PROMPT_BEAST] @@ -35,11 +52,6 @@ export namespace SystemPrompt { if (model.api.id.includes("claude")) return [PROMPT_ANTHROPIC] if (model.api.id.toLowerCase().includes("trinity")) return [PROMPT_TRINITY] - // kilocode_change start - // automodel is currently anthropic models - if (model.api.id == "kilo/auto") return [PROMPT_ANTHROPIC] - // kilocode_change end - return [PROMPT_ANTHROPIC_WITHOUT_TODO] }