Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ export const SECRET_STATE_KEYS = [
"codebaseIndexVercelAiGatewayApiKey",
"codebaseIndexOpenRouterApiKey",
"sambaNovaApiKey",
"kymaApiKey",
"zaiApiKey",
"fireworksApiKey",
"vercelAiGatewayApiKey",
Expand Down
14 changes: 14 additions & 0 deletions packages/types/src/provider-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
xaiModels,
internationalZAiModels,
minimaxModels,
kymaModels,
} from "./providers/index.js"

/**
Expand Down Expand Up @@ -125,6 +126,7 @@ export const providerNames = [
"qwen-code",
"roo",
"sambanova",
"kyma",
"vertex",
"xai",
"zai",
Expand Down Expand Up @@ -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<typeof zaiApiLineSchema>
Expand Down Expand Up @@ -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") })),
Expand Down Expand Up @@ -457,6 +464,7 @@ export const providerSettingsSchema = z.object({
...basetenSchema.shape,
...litellmSchema.shape,
...sambaNovaSchema.shape,
...kymaSchema.shape,
...zaiSchema.shape,
...fireworksSchema.shape,
...qwenCodeSchema.shape,
Expand Down Expand Up @@ -533,6 +541,7 @@ export const modelIdKeysByProvider: Record<TypicalProvider, ModelIdKey> = {
baseten: "apiModelId",
litellm: "litellmModelId",
sambanova: "apiModelId",
kyma: "apiModelId",
zai: "apiModelId",
fireworks: "apiModelId",
roo: "apiModelId",
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions packages/types/src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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":
Expand Down
89 changes: 89 additions & 0 deletions packages/types/src/providers/kyma.ts
Original file line number Diff line number Diff line change
@@ -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<string, ModelInfo>
3 changes: 3 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
VercelAiGatewayHandler,
MiniMaxHandler,
BasetenHandler,
KymaHandler,
} from "./providers"
import { NativeOllamaHandler } from "./providers/native-ollama"

Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions src/api/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
18 changes: 18 additions & 0 deletions src/api/providers/kyma.ts
Original file line number Diff line number Diff line change
@@ -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<KymaModelId> {
constructor(options: ApiHandlerOptions) {
super({
...options,
providerName: "Kyma API",
baseURL: "https://kymaapi.com/v1",
apiKey: options.kymaApiKey,
defaultProviderModelId: kymaDefaultModelId,
providerModels: kymaModels,
})
}
}
1 change: 1 addition & 0 deletions src/shared/ProfileValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class ProfileValidator {
case "deepseek":
case "xai":
case "sambanova":
case "kyma":
case "fireworks":
return profile.apiModelId
case "litellm":
Expand Down
7 changes: 7 additions & 0 deletions webview-ui/src/components/settings/ApiOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
bedrockDefaultModelId,
vertexDefaultModelId,
sambaNovaDefaultModelId,
kymaDefaultModelId,
internationalZAiDefaultModelId,
mainlandZAiDefaultModelId,
fireworksDefaultModelId,
Expand Down Expand Up @@ -94,6 +95,7 @@ import {
Fireworks,
VercelAiGateway,
MiniMax,
Kyma,
} from "./providers"

import { MODELS_BY_PROVIDER, PROVIDERS } from "./constants"
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -686,6 +689,10 @@ const ApiOptions = ({
/>
)}

{selectedProvider === "kyma" && (
<Kyma apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}

{selectedProvider === "zai" && (
<ZAi apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/components/settings/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
fireworksModels,
minimaxModels,
basetenModels,
kymaModels,
} from "@roo-code/types"

export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, ModelInfo>>> = {
Expand All @@ -32,6 +33,7 @@ export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, Mod
vertex: vertexModels,
xai: xaiModels,
sambanova: sambaNovaModels,
kyma: kymaModels,
zai: internationalZAiModels,
fireworks: fireworksModels,
minimax: minimaxModels,
Expand All @@ -58,6 +60,7 @@ export const PROVIDERS = [
{ value: "xai", label: "xAI (Grok)", proxy: false },
{ value: "litellm", label: "LiteLLM", proxy: true },
{ value: "sambanova", label: "SambaNova", proxy: false },
{ value: "kyma", label: "Kyma API", proxy: false },
{ value: "zai", label: "Z.ai", proxy: false },
{ value: "fireworks", label: "Fireworks AI", proxy: false },
{ value: "roo", label: "Roo Code Router", proxy: false },
Expand Down
50 changes: 50 additions & 0 deletions webview-ui/src/components/settings/providers/Kyma.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useCallback } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"

import type { ProviderSettings } from "@roo-code/types"

import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"

import { inputEventTransform } from "../transforms"

type KymaProps = {
apiConfiguration: ProviderSettings
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
}

export const Kyma = ({ apiConfiguration, setApiConfigurationField }: KymaProps) => {
const { t } = useAppTranslation()

const handleInputChange = useCallback(
<K extends keyof ProviderSettings, E>(
field: K,
transform: (event: E) => ProviderSettings[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)

return (
<>
<VSCodeTextField
value={apiConfiguration?.kymaApiKey || ""}
type="password"
onInput={handleInputChange("kymaApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.kymaApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.kymaApiKey && (
<VSCodeButtonLink href="https://kymaapi.com/signup" appearance="secondary">
{t("settings:providers.getKymaApiKey")}
</VSCodeButtonLink>
)}
</>
)
}
1 change: 1 addition & 0 deletions webview-ui/src/components/settings/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ export { Fireworks } from "./Fireworks"
export { VercelAiGateway } from "./VercelAiGateway"
export { MiniMax } from "./MiniMax"
export { Baseten } from "./Baseten"
export { Kyma } from "./Kyma"
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
vertexDefaultModelId,
xaiDefaultModelId,
sambaNovaDefaultModelId,
kymaDefaultModelId,
internationalZAiDefaultModelId,
mainlandZAiDefaultModelId,
fireworksDefaultModelId,
Expand All @@ -37,6 +38,7 @@ export const PROVIDER_SERVICE_CONFIG: Partial<Record<ProviderName, ProviderServi
vertex: { serviceName: "GCP Vertex AI", serviceUrl: "https://console.cloud.google.com/vertex-ai" },
xai: { serviceName: "xAI", serviceUrl: "https://x.ai" },
sambanova: { serviceName: "SambaNova", serviceUrl: "https://sambanova.ai" },
kyma: { serviceName: "Kyma API", serviceUrl: "https://kymaapi.com" },
zai: { serviceName: "Z.ai", serviceUrl: "https://z.ai" },
fireworks: { serviceName: "Fireworks AI", serviceUrl: "https://fireworks.ai" },
minimax: { serviceName: "MiniMax", serviceUrl: "https://minimax.chat" },
Expand All @@ -61,6 +63,7 @@ export const PROVIDER_DEFAULT_MODEL_IDS: Partial<Record<ProviderName, string>> =
vertex: vertexDefaultModelId,
xai: xaiDefaultModelId,
sambanova: sambaNovaDefaultModelId,
kyma: kymaDefaultModelId,
zai: internationalZAiDefaultModelId,
fireworks: fireworksDefaultModelId,
minimax: minimaxDefaultModelId,
Expand Down
6 changes: 6 additions & 0 deletions webview-ui/src/components/ui/hooks/useSelectedModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
vscodeLlmDefaultModelId,
openAiCodexModels,
sambaNovaModels,
kymaModels,
internationalZAiModels,
mainlandZAiModels,
fireworksModels,
Expand Down Expand Up @@ -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]
Expand Down
Loading
Loading