diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index 288f6c2118c..fe9c3fb181b 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -276,6 +276,7 @@ export const SECRET_STATE_KEYS = [ "codebaseIndexVercelAiGatewayApiKey", "codebaseIndexOpenRouterApiKey", "sambaNovaApiKey", + "kymaApiKey", "zaiApiKey", "fireworksApiKey", "vercelAiGatewayApiKey", diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 43135577e16..385aefab1ed 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -20,6 +20,7 @@ import { xaiModels, internationalZAiModels, minimaxModels, + kymaModels, } from "./providers/index.js" /** @@ -125,6 +126,7 @@ export const providerNames = [ "qwen-code", "roo", "sambanova", + "kyma", "vertex", "xai", "zai", @@ -363,6 +365,10 @@ const sambaNovaSchema = apiModelIdProviderModelSchema.extend({ sambaNovaApiKey: z.string().optional(), }) +const kymaSchema = apiModelIdProviderModelSchema.extend({ + kymaApiKey: z.string().optional(), +}) + export const zaiApiLineSchema = z.enum(["international_coding", "china_coding", "international_api", "china_api"]) export type ZaiApiLine = z.infer @@ -423,6 +429,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv basetenSchema.merge(z.object({ apiProvider: z.literal("baseten") })), litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })), sambaNovaSchema.merge(z.object({ apiProvider: z.literal("sambanova") })), + kymaSchema.merge(z.object({ apiProvider: z.literal("kyma") })), zaiSchema.merge(z.object({ apiProvider: z.literal("zai") })), fireworksSchema.merge(z.object({ apiProvider: z.literal("fireworks") })), qwenCodeSchema.merge(z.object({ apiProvider: z.literal("qwen-code") })), @@ -457,6 +464,7 @@ export const providerSettingsSchema = z.object({ ...basetenSchema.shape, ...litellmSchema.shape, ...sambaNovaSchema.shape, + ...kymaSchema.shape, ...zaiSchema.shape, ...fireworksSchema.shape, ...qwenCodeSchema.shape, @@ -533,6 +541,7 @@ export const modelIdKeysByProvider: Record = { baseten: "apiModelId", litellm: "litellmModelId", sambanova: "apiModelId", + kyma: "apiModelId", zai: "apiModelId", fireworks: "apiModelId", roo: "apiModelId", @@ -633,6 +642,11 @@ export const MODELS_BY_PROVIDER: Record< label: "SambaNova", models: Object.keys(sambaNovaModels), }, + kyma: { + id: "kyma", + label: "Kyma API", + models: Object.keys(kymaModels), + }, vertex: { id: "vertex", label: "GCP Vertex AI", diff --git a/packages/types/src/providers/index.ts b/packages/types/src/providers/index.ts index 6c180d5dda4..0a128a61a90 100644 --- a/packages/types/src/providers/index.ts +++ b/packages/types/src/providers/index.ts @@ -25,6 +25,7 @@ export * from "./xai.js" export * from "./vercel-ai-gateway.js" export * from "./zai.js" export * from "./minimax.js" +export * from "./kyma.js" import { anthropicDefaultModelId } from "./anthropic.js" import { basetenDefaultModelId } from "./baseten.js" @@ -49,6 +50,7 @@ import { xaiDefaultModelId } from "./xai.js" import { vercelAiGatewayDefaultModelId } from "./vercel-ai-gateway.js" import { internationalZAiDefaultModelId, mainlandZAiDefaultModelId } from "./zai.js" import { minimaxDefaultModelId } from "./minimax.js" +import { kymaDefaultModelId } from "./kyma.js" // Import the ProviderName type from provider-settings to avoid duplication import type { ProviderName } from "../provider-settings.js" @@ -115,6 +117,8 @@ export function getProviderDefaultModelId( return unboundDefaultModelId case "vercel-ai-gateway": return vercelAiGatewayDefaultModelId + case "kyma": + return kymaDefaultModelId case "anthropic": case "gemini-cli": case "fake-ai": diff --git a/packages/types/src/providers/kyma.ts b/packages/types/src/providers/kyma.ts new file mode 100644 index 00000000000..d2623980eb8 --- /dev/null +++ b/packages/types/src/providers/kyma.ts @@ -0,0 +1,89 @@ +import type { ModelInfo } from "../model.js" + +// https://kymaapi.com — OpenAI-compatible LLM gateway with 20+ open-source models +export type KymaModelId = + | "qwen-3.6-plus" + | "deepseek-v3" + | "deepseek-r1" + | "kimi-k2.5" + | "gemma-4-31b" + | "llama-3.3-70b" + | "qwen-3-32b" + | "minimax-m2.5" + +export const kymaDefaultModelId: KymaModelId = "qwen-3.6-plus" + +export const kymaModels = { + "qwen-3.6-plus": { + maxTokens: 8192, + contextWindow: 128000, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.4, + outputPrice: 1.6, + description: "Qwen 3.6 Plus — top-tier open model, best overall quality.", + }, + "deepseek-v3": { + maxTokens: 8192, + contextWindow: 128000, + supportsImages: false, + supportsPromptCache: true, + inputPrice: 0.26, + outputPrice: 0.38, + description: "DeepSeek V3 — GPT-4-class coding and reasoning at very low cost.", + }, + "deepseek-r1": { + maxTokens: 8192, + contextWindow: 128000, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.5, + outputPrice: 2.18, + description: "DeepSeek R1 — strong open reasoning model, 96% cheaper than o1.", + }, + "kimi-k2.5": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 1.0, + outputPrice: 3.0, + description: "Kimi K2.5 — multimodal agentic model optimized for tool use.", + }, + "gemma-4-31b": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0.0, + outputPrice: 0.0, + description: "Gemma 4 31B — Google's latest multimodal open model, free tier.", + }, + "llama-3.3-70b": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.23, + outputPrice: 0.4, + description: "Llama 3.3 70B — most popular open model, great balance of speed and quality.", + }, + "qwen-3-32b": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.2, + outputPrice: 0.6, + description: "Qwen 3 32B — top coding model, fast and accurate.", + }, + "minimax-m2.5": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.8, + outputPrice: 2.0, + description: "MiniMax M2.5 — SWE-bench 80.2%, strong coding and agentic tasks.", + }, +} as const satisfies Record diff --git a/src/api/index.ts b/src/api/index.ts index 1891113c03b..218e517206e 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -34,6 +34,7 @@ import { VercelAiGatewayHandler, MiniMaxHandler, BasetenHandler, + KymaHandler, } from "./providers" import { NativeOllamaHandler } from "./providers/native-ollama" @@ -177,6 +178,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { return new MiniMaxHandler(options) case "baseten": return new BasetenHandler(options) + case "kyma": + return new KymaHandler(options) case "poe": return new PoeHandler(options) default: diff --git a/src/api/providers/index.ts b/src/api/providers/index.ts index 41aff953d43..4a6fd2de7fb 100644 --- a/src/api/providers/index.ts +++ b/src/api/providers/index.ts @@ -28,3 +28,4 @@ export { RooHandler } from "./roo" export { VercelAiGatewayHandler } from "./vercel-ai-gateway" export { MiniMaxHandler } from "./minimax" export { BasetenHandler } from "./baseten" +export { KymaHandler } from "./kyma" diff --git a/src/api/providers/kyma.ts b/src/api/providers/kyma.ts new file mode 100644 index 00000000000..77bc6d83881 --- /dev/null +++ b/src/api/providers/kyma.ts @@ -0,0 +1,18 @@ +import { type KymaModelId, kymaDefaultModelId, kymaModels } from "@roo-code/types" + +import type { ApiHandlerOptions } from "../../shared/api" + +import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider" + +export class KymaHandler extends BaseOpenAiCompatibleProvider { + constructor(options: ApiHandlerOptions) { + super({ + ...options, + providerName: "Kyma API", + baseURL: "https://kymaapi.com/v1", + apiKey: options.kymaApiKey, + defaultProviderModelId: kymaDefaultModelId, + providerModels: kymaModels, + }) + } +} diff --git a/src/shared/ProfileValidator.ts b/src/shared/ProfileValidator.ts index 7246a90177a..3f5b5ed7e56 100644 --- a/src/shared/ProfileValidator.ts +++ b/src/shared/ProfileValidator.ts @@ -62,6 +62,7 @@ export class ProfileValidator { case "deepseek": case "xai": case "sambanova": + case "kyma": case "fireworks": return profile.apiModelId case "litellm": diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index a6e4cc3f5f6..75a7e93c314 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -26,6 +26,7 @@ import { bedrockDefaultModelId, vertexDefaultModelId, sambaNovaDefaultModelId, + kymaDefaultModelId, internationalZAiDefaultModelId, mainlandZAiDefaultModelId, fireworksDefaultModelId, @@ -94,6 +95,7 @@ import { Fireworks, VercelAiGateway, MiniMax, + Kyma, } from "./providers" import { MODELS_BY_PROVIDER, PROVIDERS } from "./constants" @@ -352,6 +354,7 @@ const ApiOptions = ({ bedrock: { field: "apiModelId", default: bedrockDefaultModelId }, vertex: { field: "apiModelId", default: vertexDefaultModelId }, sambanova: { field: "apiModelId", default: sambaNovaDefaultModelId }, + kyma: { field: "apiModelId", default: kymaDefaultModelId }, zai: { field: "apiModelId", default: @@ -686,6 +689,10 @@ const ApiOptions = ({ /> )} + {selectedProvider === "kyma" && ( + + )} + {selectedProvider === "zai" && ( )} diff --git a/webview-ui/src/components/settings/constants.ts b/webview-ui/src/components/settings/constants.ts index 14f04cb5b22..bb0874d44e5 100644 --- a/webview-ui/src/components/settings/constants.ts +++ b/webview-ui/src/components/settings/constants.ts @@ -17,6 +17,7 @@ import { fireworksModels, minimaxModels, basetenModels, + kymaModels, } from "@roo-code/types" export const MODELS_BY_PROVIDER: Partial>> = { @@ -32,6 +33,7 @@ export const MODELS_BY_PROVIDER: Partial void +} + +export const Kyma = ({ apiConfiguration, setApiConfigurationField }: KymaProps) => { + const { t } = useAppTranslation() + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + return ( + <> + + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ {!apiConfiguration?.kymaApiKey && ( + + {t("settings:providers.getKymaApiKey")} + + )} + + ) +} diff --git a/webview-ui/src/components/settings/providers/index.ts b/webview-ui/src/components/settings/providers/index.ts index 4a64ce9586b..12a441e0da9 100644 --- a/webview-ui/src/components/settings/providers/index.ts +++ b/webview-ui/src/components/settings/providers/index.ts @@ -25,3 +25,4 @@ export { Fireworks } from "./Fireworks" export { VercelAiGateway } from "./VercelAiGateway" export { MiniMax } from "./MiniMax" export { Baseten } from "./Baseten" +export { Kyma } from "./Kyma" diff --git a/webview-ui/src/components/settings/utils/providerModelConfig.ts b/webview-ui/src/components/settings/utils/providerModelConfig.ts index 59f76862b45..f860a3ed1b6 100644 --- a/webview-ui/src/components/settings/utils/providerModelConfig.ts +++ b/webview-ui/src/components/settings/utils/providerModelConfig.ts @@ -11,6 +11,7 @@ import { vertexDefaultModelId, xaiDefaultModelId, sambaNovaDefaultModelId, + kymaDefaultModelId, internationalZAiDefaultModelId, mainlandZAiDefaultModelId, fireworksDefaultModelId, @@ -37,6 +38,7 @@ export const PROVIDER_SERVICE_CONFIG: Partial> = vertex: vertexDefaultModelId, xai: xaiDefaultModelId, sambanova: sambaNovaDefaultModelId, + kyma: kymaDefaultModelId, zai: internationalZAiDefaultModelId, fireworks: fireworksDefaultModelId, minimax: minimaxDefaultModelId, diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 7192d9d4ee4..84fb4989d0f 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -19,6 +19,7 @@ import { vscodeLlmDefaultModelId, openAiCodexModels, sambaNovaModels, + kymaModels, internationalZAiModels, mainlandZAiModels, fireworksModels, @@ -307,6 +308,11 @@ function getSelectedModel({ const info = sambaNovaModels[id as keyof typeof sambaNovaModels] return { id, info } } + case "kyma": { + const id = apiConfiguration.apiModelId ?? defaultModelId + const info = kymaModels[id as keyof typeof kymaModels] + return { id, info } + } case "fireworks": { const id = apiConfiguration.apiModelId ?? defaultModelId const info = fireworksModels[id as keyof typeof fireworksModels] diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 183cd663e31..445ef381b7e 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -463,6 +463,8 @@ "geminiApiKey": "Gemini API Key", "getSambaNovaApiKey": "Get SambaNova API Key", "sambaNovaApiKey": "SambaNova API Key", + "getKymaApiKey": "Get Kyma API Key", + "kymaApiKey": "Kyma API Key", "getGeminiApiKey": "Get Gemini API Key", "openAiApiKey": "OpenAI API Key", "apiKey": "API Key",