mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Add Baseten provider support
- Add Baseten model definitions (10 models) in packages/types/src/providers/baseten.ts - Implement BasetenHandler extending BaseOpenAiCompatibleProvider - Add Baseten UI component for API key input - Add basetenApiKey to SECRET_STATE_KEYS (critical fix for welcome screen) - Register Baseten in provider settings, API handlers, and UI components - Add validation and translation support - Baseten appears last in provider dropdown as requested
This commit is contained in:
parent
5c5a893b50
commit
6eb26eccfa
14 changed files with 296 additions and 0 deletions
|
|
@ -246,6 +246,7 @@ export const SECRET_STATE_KEYS = [
|
|||
"featherlessApiKey",
|
||||
"ioIntelligenceApiKey",
|
||||
"vercelAiGatewayApiKey",
|
||||
"basetenApiKey",
|
||||
] as const
|
||||
|
||||
// Global secrets that are part of GlobalSettings (not ProviderSettings)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { modelInfoSchema, reasoningEffortSettingSchema, verbosityLevelsSchema, s
|
|||
import { codebaseIndexProviderSchema } from "./codebase-index.js"
|
||||
import {
|
||||
anthropicModels,
|
||||
basetenModels,
|
||||
bedrockModels,
|
||||
cerebrasModels,
|
||||
claudeCodeModels,
|
||||
|
|
@ -120,6 +121,7 @@ export const providerNames = [
|
|||
...fauxProviders,
|
||||
"anthropic",
|
||||
"bedrock",
|
||||
"baseten",
|
||||
"cerebras",
|
||||
"claude-code",
|
||||
"doubao",
|
||||
|
|
@ -424,6 +426,10 @@ const vercelAiGatewaySchema = baseProviderSettingsSchema.extend({
|
|||
vercelAiGatewayModelId: z.string().optional(),
|
||||
})
|
||||
|
||||
const basetenSchema = apiModelIdProviderModelSchema.extend({
|
||||
basetenApiKey: z.string().optional(),
|
||||
})
|
||||
|
||||
const defaultSchema = z.object({
|
||||
apiProvider: z.undefined(),
|
||||
})
|
||||
|
|
@ -454,6 +460,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
|
|||
fakeAiSchema.merge(z.object({ apiProvider: z.literal("fake-ai") })),
|
||||
xaiSchema.merge(z.object({ apiProvider: z.literal("xai") })),
|
||||
groqSchema.merge(z.object({ apiProvider: z.literal("groq") })),
|
||||
basetenSchema.merge(z.object({ apiProvider: z.literal("baseten") })),
|
||||
huggingFaceSchema.merge(z.object({ apiProvider: z.literal("huggingface") })),
|
||||
chutesSchema.merge(z.object({ apiProvider: z.literal("chutes") })),
|
||||
litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })),
|
||||
|
|
@ -496,6 +503,7 @@ export const providerSettingsSchema = z.object({
|
|||
...fakeAiSchema.shape,
|
||||
...xaiSchema.shape,
|
||||
...groqSchema.shape,
|
||||
...basetenSchema.shape,
|
||||
...huggingFaceSchema.shape,
|
||||
...chutesSchema.shape,
|
||||
...litellmSchema.shape,
|
||||
|
|
@ -583,6 +591,7 @@ export const modelIdKeysByProvider: Record<TypicalProvider, ModelIdKey> = {
|
|||
requesty: "requestyModelId",
|
||||
xai: "apiModelId",
|
||||
groq: "apiModelId",
|
||||
baseten: "apiModelId",
|
||||
chutes: "apiModelId",
|
||||
litellm: "litellmModelId",
|
||||
huggingface: "huggingFaceModelId",
|
||||
|
|
@ -715,6 +724,7 @@ export const MODELS_BY_PROVIDER: Record<
|
|||
},
|
||||
xai: { id: "xai", label: "xAI (Grok)", models: Object.keys(xaiModels) },
|
||||
zai: { id: "zai", label: "Zai", models: Object.keys(internationalZAiModels) },
|
||||
baseten: { id: "baseten", label: "BaseTen", models: Object.keys(basetenModels) },
|
||||
|
||||
// Dynamic providers; models pulled from remote APIs.
|
||||
glama: { id: "glama", label: "Glama", models: [] },
|
||||
|
|
|
|||
127
packages/types/src/providers/baseten.ts
Normal file
127
packages/types/src/providers/baseten.ts
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import type { ModelInfo } from "../model.js"
|
||||
|
||||
// Baseten
|
||||
// https://baseten.co/products/model-apis/
|
||||
|
||||
// Extended ModelInfo to include supportedFeatures, like tools
|
||||
export interface BasetenModelInfo extends ModelInfo {
|
||||
supportedFeatures?: string[]
|
||||
}
|
||||
|
||||
export const basetenModels = {
|
||||
"moonshotai/Kimi-K2-Thinking": {
|
||||
maxTokens: 163_800,
|
||||
contextWindow: 262_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.6,
|
||||
outputPrice: 2.5,
|
||||
cacheWritesPrice: 0,
|
||||
cacheReadsPrice: 0,
|
||||
description: "Kimi K2 Thinking - A model with enhanced reasoning capabilities from Kimi K2",
|
||||
},
|
||||
"zai-org/GLM-4.6": {
|
||||
maxTokens: 200_000,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.6,
|
||||
outputPrice: 2.2,
|
||||
cacheWritesPrice: 0,
|
||||
cacheReadsPrice: 0,
|
||||
description: "Frontier open model with advanced agentic, reasoning and coding capabilities",
|
||||
},
|
||||
"deepseek-ai/DeepSeek-R1": {
|
||||
maxTokens: 131_072,
|
||||
contextWindow: 163_840,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 2.55,
|
||||
outputPrice: 5.95,
|
||||
cacheWritesPrice: 0,
|
||||
cacheReadsPrice: 0,
|
||||
description: "DeepSeek's first-generation reasoning model",
|
||||
},
|
||||
"deepseek-ai/DeepSeek-R1-0528": {
|
||||
maxTokens: 131_072,
|
||||
contextWindow: 163_840,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 2.55,
|
||||
outputPrice: 5.95,
|
||||
cacheWritesPrice: 0,
|
||||
cacheReadsPrice: 0,
|
||||
description: "The latest revision of DeepSeek's first-generation reasoning model",
|
||||
},
|
||||
"deepseek-ai/DeepSeek-V3-0324": {
|
||||
maxTokens: 131_072,
|
||||
contextWindow: 163_840,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.77,
|
||||
outputPrice: 0.77,
|
||||
cacheWritesPrice: 0,
|
||||
cacheReadsPrice: 0,
|
||||
description: "Fast general-purpose LLM with enhanced reasoning capabilities",
|
||||
},
|
||||
"deepseek-ai/DeepSeek-V3.1": {
|
||||
maxTokens: 131_072,
|
||||
contextWindow: 163_840,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.5,
|
||||
outputPrice: 1.5,
|
||||
cacheWritesPrice: 0,
|
||||
cacheReadsPrice: 0,
|
||||
description:
|
||||
"Extremely capable general-purpose LLM with hybrid reasoning capabilities and advanced tool calling",
|
||||
},
|
||||
"Qwen/Qwen3-235B-A22B-Instruct-2507": {
|
||||
maxTokens: 262_144,
|
||||
contextWindow: 262_144,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.22,
|
||||
outputPrice: 0.8,
|
||||
cacheWritesPrice: 0,
|
||||
cacheReadsPrice: 0,
|
||||
description: "Mixture-of-experts LLM with math and reasoning capabilities",
|
||||
},
|
||||
"Qwen/Qwen3-Coder-480B-A35B-Instruct": {
|
||||
maxTokens: 262_144,
|
||||
contextWindow: 262_144,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.38,
|
||||
outputPrice: 1.53,
|
||||
cacheWritesPrice: 0,
|
||||
cacheReadsPrice: 0,
|
||||
description: "Mixture-of-experts LLM with advanced coding and reasoning capabilities",
|
||||
},
|
||||
"openai/gpt-oss-120b": {
|
||||
maxTokens: 128_072,
|
||||
contextWindow: 128_072,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.1,
|
||||
outputPrice: 0.5,
|
||||
cacheWritesPrice: 0,
|
||||
cacheReadsPrice: 0,
|
||||
description: "Extremely capable general-purpose LLM with strong, controllable reasoning capabilities",
|
||||
},
|
||||
"moonshotai/Kimi-K2-Instruct-0905": {
|
||||
maxTokens: 168_000,
|
||||
contextWindow: 262_000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.6,
|
||||
outputPrice: 2.5,
|
||||
cacheWritesPrice: 0,
|
||||
cacheReadsPrice: 0,
|
||||
description: "State of the art language model for agentic and coding tasks. Septemeber Update.",
|
||||
},
|
||||
} as const satisfies Record<string, ModelInfo>
|
||||
|
||||
export type BasetenModelId = keyof typeof basetenModels
|
||||
|
||||
export const basetenDefaultModelId = "zai-org/GLM-4.6" satisfies BasetenModelId
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
export * from "./anthropic.js"
|
||||
export * from "./baseten.js"
|
||||
export * from "./bedrock.js"
|
||||
export * from "./cerebras.js"
|
||||
export * from "./chutes.js"
|
||||
|
|
@ -33,6 +34,7 @@ export * from "./deepinfra.js"
|
|||
export * from "./minimax.js"
|
||||
|
||||
import { anthropicDefaultModelId } from "./anthropic.js"
|
||||
import { basetenDefaultModelId } from "./baseten.js"
|
||||
import { bedrockDefaultModelId } from "./bedrock.js"
|
||||
import { cerebrasDefaultModelId } from "./cerebras.js"
|
||||
import { chutesDefaultModelId } from "./chutes.js"
|
||||
|
|
@ -93,6 +95,8 @@ export function getProviderDefaultModelId(
|
|||
return "meta-llama/Llama-3.3-70B-Instruct"
|
||||
case "chutes":
|
||||
return chutesDefaultModelId
|
||||
case "baseten":
|
||||
return basetenDefaultModelId
|
||||
case "bedrock":
|
||||
return bedrockDefaultModelId
|
||||
case "vertex":
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import {
|
|||
VercelAiGatewayHandler,
|
||||
DeepInfraHandler,
|
||||
MiniMaxHandler,
|
||||
BasetenHandler,
|
||||
} from "./providers"
|
||||
import { NativeOllamaHandler } from "./providers/native-ollama"
|
||||
|
||||
|
|
@ -190,6 +191,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
|
|||
return new VercelAiGatewayHandler(options)
|
||||
case "minimax":
|
||||
return new MiniMaxHandler(options)
|
||||
case "baseten":
|
||||
return new BasetenHandler(options)
|
||||
default:
|
||||
apiProvider satisfies "gemini-cli" | undefined
|
||||
return new AnthropicHandler(options)
|
||||
|
|
|
|||
75
src/api/providers/baseten.ts
Normal file
75
src/api/providers/baseten.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import { type BasetenModelId, basetenDefaultModelId, basetenModels } from "@roo-code/types"
|
||||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import OpenAI from "openai"
|
||||
|
||||
import type { ApiHandlerOptions } from "../../shared/api"
|
||||
import type { ApiHandlerCreateMessageMetadata } from "../index"
|
||||
import { ApiStream } from "../transform/stream"
|
||||
import { calculateApiCostOpenAI } from "../../shared/cost"
|
||||
|
||||
import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
|
||||
|
||||
export class BasetenHandler extends BaseOpenAiCompatibleProvider<BasetenModelId> {
|
||||
constructor(options: ApiHandlerOptions) {
|
||||
super({
|
||||
...options,
|
||||
providerName: "Baseten",
|
||||
baseURL: "https://inference.baseten.co/v1",
|
||||
apiKey: options.basetenApiKey,
|
||||
defaultProviderModelId: basetenDefaultModelId,
|
||||
providerModels: basetenModels,
|
||||
defaultTemperature: 0.5,
|
||||
})
|
||||
}
|
||||
|
||||
override async *createMessage(
|
||||
systemPrompt: string,
|
||||
messages: Anthropic.Messages.MessageParam[],
|
||||
metadata?: ApiHandlerCreateMessageMetadata,
|
||||
): ApiStream {
|
||||
const stream = await this.createStream(systemPrompt, messages, metadata)
|
||||
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
|
||||
if (delta?.content) {
|
||||
yield {
|
||||
type: "text",
|
||||
text: delta.content,
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk.usage) {
|
||||
// Check if usage has cached token details (some models support this)
|
||||
const usage = chunk.usage as any
|
||||
const inputTokens = usage?.prompt_tokens || 0
|
||||
const outputTokens = usage?.completion_tokens || 0
|
||||
|
||||
// Check for cached tokens in various possible locations
|
||||
const cacheReadTokens =
|
||||
usage?.prompt_tokens_details?.cached_tokens || usage?.prompt_cache_hit_tokens || 0
|
||||
|
||||
// Baseten currently doesn't track cache writes
|
||||
const cacheWriteTokens = 0
|
||||
|
||||
// Calculate cost using OpenAI-compatible cost calculation
|
||||
const { totalCost } = calculateApiCostOpenAI(
|
||||
this.getModel().info,
|
||||
inputTokens,
|
||||
outputTokens,
|
||||
cacheWriteTokens,
|
||||
cacheReadTokens,
|
||||
)
|
||||
|
||||
yield {
|
||||
type: "usage",
|
||||
inputTokens,
|
||||
outputTokens,
|
||||
cacheWriteTokens,
|
||||
cacheReadTokens,
|
||||
totalCost,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,3 +35,4 @@ export { FeatherlessHandler } from "./featherless"
|
|||
export { VercelAiGatewayHandler } from "./vercel-ai-gateway"
|
||||
export { DeepInfraHandler } from "./deepinfra"
|
||||
export { MiniMaxHandler } from "./minimax"
|
||||
export { BasetenHandler } from "./baseten"
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
groqDefaultModelId,
|
||||
cerebrasDefaultModelId,
|
||||
chutesDefaultModelId,
|
||||
basetenDefaultModelId,
|
||||
bedrockDefaultModelId,
|
||||
vertexDefaultModelId,
|
||||
sambaNovaDefaultModelId,
|
||||
|
|
@ -67,6 +68,7 @@ import {
|
|||
|
||||
import {
|
||||
Anthropic,
|
||||
Baseten,
|
||||
Bedrock,
|
||||
Cerebras,
|
||||
Chutes,
|
||||
|
|
@ -274,6 +276,7 @@ const ApiOptions = ({
|
|||
|
||||
const selectedProviderModels = useMemo(() => {
|
||||
const models = MODELS_BY_PROVIDER[selectedProvider]
|
||||
|
||||
if (!models) return []
|
||||
|
||||
const filteredModels = filterModels(models, selectedProvider, organizationAllowList)
|
||||
|
|
@ -355,6 +358,7 @@ const ApiOptions = ({
|
|||
xai: { field: "apiModelId", default: xaiDefaultModelId },
|
||||
groq: { field: "apiModelId", default: groqDefaultModelId },
|
||||
chutes: { field: "apiModelId", default: chutesDefaultModelId },
|
||||
baseten: { field: "apiModelId", default: basetenDefaultModelId },
|
||||
bedrock: { field: "apiModelId", default: bedrockDefaultModelId },
|
||||
vertex: { field: "apiModelId", default: vertexDefaultModelId },
|
||||
sambanova: { field: "apiModelId", default: sambaNovaDefaultModelId },
|
||||
|
|
@ -564,6 +568,10 @@ const ApiOptions = ({
|
|||
<Mistral apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
|
||||
)}
|
||||
|
||||
{selectedProvider === "baseten" && (
|
||||
<Baseten apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
|
||||
)}
|
||||
|
||||
{selectedProvider === "bedrock" && (
|
||||
<Bedrock
|
||||
apiConfiguration={apiConfiguration}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import {
|
|||
fireworksModels,
|
||||
featherlessModels,
|
||||
minimaxModels,
|
||||
basetenModels,
|
||||
} from "@roo-code/types"
|
||||
|
||||
export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, ModelInfo>>> = {
|
||||
|
|
@ -42,6 +43,7 @@ export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, Mod
|
|||
fireworks: fireworksModels,
|
||||
featherless: featherlessModels,
|
||||
minimax: minimaxModels,
|
||||
baseten: basetenModels,
|
||||
}
|
||||
|
||||
export const PROVIDERS = [
|
||||
|
|
@ -80,4 +82,5 @@ export const PROVIDERS = [
|
|||
{ value: "roo", label: "Roo Code Cloud" },
|
||||
{ value: "vercel-ai-gateway", label: "Vercel AI Gateway" },
|
||||
{ value: "minimax", label: "MiniMax" },
|
||||
{ value: "baseten", label: "Baseten" },
|
||||
].sort((a, b) => a.label.localeCompare(b.label))
|
||||
|
|
|
|||
50
webview-ui/src/components/settings/providers/Baseten.tsx
Normal file
50
webview-ui/src/components/settings/providers/Baseten.tsx
Normal file
|
|
@ -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 BasetenProps = {
|
||||
apiConfiguration: ProviderSettings
|
||||
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
|
||||
}
|
||||
|
||||
export const Baseten = ({ apiConfiguration, setApiConfigurationField }: BasetenProps) => {
|
||||
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?.basetenApiKey || ""}
|
||||
type="password"
|
||||
onInput={handleInputChange("basetenApiKey")}
|
||||
placeholder={t("settings:placeholders.apiKey")}
|
||||
className="w-full">
|
||||
<label className="block font-medium mb-1">{t("settings:providers.basetenApiKey")}</label>
|
||||
</VSCodeTextField>
|
||||
<div className="text-sm text-vscode-descriptionForeground -mt-2">
|
||||
{t("settings:providers.apiKeyStorageNotice")}
|
||||
</div>
|
||||
{!apiConfiguration?.basetenApiKey && (
|
||||
<VSCodeButtonLink href="https://app.baseten.co/settings/api_keys" appearance="secondary">
|
||||
{t("settings:providers.getBasetenApiKey")}
|
||||
</VSCodeButtonLink>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -32,3 +32,4 @@ export { Featherless } from "./Featherless"
|
|||
export { VercelAiGateway } from "./VercelAiGateway"
|
||||
export { DeepInfra } from "./DeepInfra"
|
||||
export { MiniMax } from "./MiniMax"
|
||||
export { Baseten } from "./Baseten"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
fireworksModels,
|
||||
featherlessModels,
|
||||
ioIntelligenceModels,
|
||||
basetenModels,
|
||||
qwenCodeModels,
|
||||
BEDROCK_1M_CONTEXT_MODEL_IDS,
|
||||
isDynamicProvider,
|
||||
|
|
@ -196,6 +197,11 @@ function getSelectedModel({
|
|||
const info = routerModels.chutes?.[id]
|
||||
return { id, info }
|
||||
}
|
||||
case "baseten": {
|
||||
const id = apiConfiguration.apiModelId ?? defaultModelId
|
||||
const info = basetenModels[id as keyof typeof basetenModels]
|
||||
return { id, info }
|
||||
}
|
||||
case "bedrock": {
|
||||
const id = apiConfiguration.apiModelId ?? defaultModelId
|
||||
const baseInfo = bedrockModels[id as keyof typeof bedrockModels]
|
||||
|
|
|
|||
|
|
@ -284,6 +284,8 @@
|
|||
"anthropic1MContextBetaDescription": "Extends context window to 1 million tokens for Claude Sonnet 4",
|
||||
"awsBedrock1MContextBetaLabel": "Enable 1M context window (Beta)",
|
||||
"awsBedrock1MContextBetaDescription": "Extends context window to 1 million tokens for Claude Sonnet 4",
|
||||
"basetenApiKey": "Baseten API Key",
|
||||
"getBasetenApiKey": "Get Baseten API Key",
|
||||
"cerebrasApiKey": "Cerebras API Key",
|
||||
"getCerebrasApiKey": "Get Cerebras API Key",
|
||||
"chutesApiKey": "Chutes API Key",
|
||||
|
|
|
|||
|
|
@ -156,6 +156,11 @@ function validateModelsAndKeysProvided(apiConfiguration: ProviderSettings): stri
|
|||
return i18next.t("settings:validation.apiKey")
|
||||
}
|
||||
break
|
||||
case "baseten":
|
||||
if (!apiConfiguration.basetenApiKey) {
|
||||
return i18next.t("settings:validation.apiKey")
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return undefined
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue