diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index fca3a1e32b..d5f8261174 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -199,9 +199,6 @@ export const SECRET_STATE_KEYS = [ "codebaseIndexMistralApiKey", "codebaseIndexVercelAiGatewayApiKey", "huggingFaceApiKey", - "watsonxApiKey", - "codebaseIndexWatsonxApiKey", - "codebaseIndexWatsonxProjectId", "sambaNovaApiKey", "zaiApiKey", "fireworksApiKey", diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 4dc056d2c5..c19dd95a16 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -139,7 +139,6 @@ export const providerNames = [ "vertex", "xai", "zai", - "watsonx", ] as const export const providerNamesSchema = z.enum(providerNames) @@ -457,7 +456,6 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv huggingFaceSchema.merge(z.object({ apiProvider: z.literal("huggingface") })), chutesSchema.merge(z.object({ apiProvider: z.literal("chutes") })), litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })), - watsonxSchema.merge(z.object({ apiProvider: z.literal("watsonx") })), cerebrasSchema.merge(z.object({ apiProvider: z.literal("cerebras") })), sambaNovaSchema.merge(z.object({ apiProvider: z.literal("sambanova") })), zaiSchema.merge(z.object({ apiProvider: z.literal("zai") })), diff --git a/packages/types/src/providers/watsonx.ts b/packages/types/src/providers/watsonx.ts index 5846125c65..9a22cac030 100644 --- a/packages/types/src/providers/watsonx.ts +++ b/packages/types/src/providers/watsonx.ts @@ -5,21 +5,15 @@ export const watsonxAiDefaultModelId = "" // Common model properties export const baseModelInfo: ModelInfo = { - maxTokens: 131072, + maxTokens: 8192, contextWindow: 131072, supportsImages: false, supportsPromptCache: false, - supportsReasoningEffort: true, - supportsReasoningBudget: false, - requiredReasoningBudget: false, - inputPrice: 5.22, - outputPrice: 5.22, } export const watsonxAiModels = { // IBM Granite model "ibm/granite-3-3-8b-instruct": { ...baseModelInfo, - description: "", }, } as const satisfies Record diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c51754426d..f5105ae229 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -618,8 +618,8 @@ importers: specifier: ^1.0.0 version: 1.3.0(@modelcontextprotocol/sdk@1.12.0) '@ibm-cloud/watsonx-ai': - specifier: ^1.6.8 - version: 1.6.10 + specifier: ^1.6.13 + version: 1.6.13 '@lmstudio/sdk': specifier: ^1.1.1 version: 1.2.0 @@ -855,6 +855,9 @@ importers: '@types/lodash': specifier: ^4.14.201 version: 4.17.17 + '@types/lodash.debounce': + specifier: ^4.0.9 + version: 4.0.9 '@types/mocha': specifier: ^10.0.10 version: 10.0.10 @@ -1919,8 +1922,8 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@ibm-cloud/watsonx-ai@1.6.10': - resolution: {integrity: sha512-aZV50/s8VZc7w0t/qcaBw3RLT3WDsAeZUJlP8EbG/csZJF3a8F7alihbGOM4lJFM7R4Z81Lucz3nfHi2KR7J4Q==} + '@ibm-cloud/watsonx-ai@1.6.13': + resolution: {integrity: sha512-INaaD7EKpycwQg/tsLm3QM5uvDF5mWLPQCj6GTk44gEZhgx1depvVG5bxwjfqkx1tbJMFuozz2p6VHOE21S+8g==} engines: {node: '>=18.0.0'} '@iconify/types@2.0.0': @@ -4028,6 +4031,9 @@ packages: '@types/katex@0.16.7': resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@types/lodash.debounce@4.0.9': + resolution: {integrity: sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==} + '@types/lodash@4.17.17': resolution: {integrity: sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==} @@ -11400,10 +11406,11 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@ibm-cloud/watsonx-ai@1.6.10': + '@ibm-cloud/watsonx-ai@1.6.13': dependencies: '@types/node': 18.19.100 extend: 3.0.2 + form-data: 4.0.4 ibm-cloud-sdk-core: 5.4.3 transitivePeerDependencies: - supports-color @@ -13639,6 +13646,10 @@ snapshots: '@types/katex@0.16.7': {} + '@types/lodash.debounce@4.0.9': + dependencies: + '@types/lodash': 4.17.17 + '@types/lodash@4.17.17': {} '@types/mdast@3.0.15': diff --git a/src/api/providers/fetchers/watsonx.ts b/src/api/providers/fetchers/watsonx.ts index 14e06b4828..8b95cea913 100644 --- a/src/api/providers/fetchers/watsonx.ts +++ b/src/api/providers/fetchers/watsonx.ts @@ -68,8 +68,8 @@ export async function getWatsonxModels( if (Array.isArray(modelsList) && modelsList.length > 0) { for (const model of modelsList) { const modelId = model.id || model.name || model.model_id - const contextWindow = model.context_length || model.max_input_tokens || 8192 - const maxTokens = model.max_output_tokens || Math.floor(contextWindow / 2) + const contextWindow = model.model_limits.max_sequence_length || 131072 + const maxTokens = model.model_limits.max_output_tokens || Math.floor(contextWindow / 2) let description = "" if (model.long_description) { @@ -77,19 +77,10 @@ export async function getWatsonxModels( } else if (model.short_description) { description = model.short_description } - const supportsImages = model.modality === "multimodal" || model.modality === "vision" || false - const inputPrice = model.pricing?.input_price || 0 - const outputPrice = model.pricing?.output_price || 0 knownModels[modelId] = { contextWindow, maxTokens, supportsPromptCache: false, - supportsImages, - supportsReasoningEffort: false, - supportsReasoningBudget: false, - requiredReasoningBudget: false, - inputPrice, - outputPrice, description, } } diff --git a/src/api/providers/watsonx.ts b/src/api/providers/watsonx.ts index 5ddb197828..0e397c457e 100644 --- a/src/api/providers/watsonx.ts +++ b/src/api/providers/watsonx.ts @@ -8,7 +8,6 @@ import { BaseProvider } from "./base-provider" import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { WatsonXAI } from "@ibm-cloud/watsonx-ai" import { convertToWatsonxAiMessages } from "../transform/watsonxai-format" -import { calculateApiCostOpenAI } from "../../shared/cost" export class WatsonxAIHandler extends BaseProvider implements SingleCompletionHandler { private options: ApiHandlerOptions @@ -127,7 +126,6 @@ export class WatsonxAIHandler extends BaseProvider implements SingleCompletionHa const params = this.createTextChatParams(this.projectId!, modelId, watsonxMessages) let responseText = "" - let usageInfo: any = null // Call the IBM watsonx API using textChat (non-streaming); can be changed to streaming.. const response = await this.service.textChat(params) @@ -142,20 +140,6 @@ export class WatsonxAIHandler extends BaseProvider implements SingleCompletionHa type: "text", text: responseText, } - - usageInfo = response.result.usage || {} - const outputTokens = usageInfo.completion_tokens - - const inputTokens = usageInfo?.prompt_tokens || 0 - const modelInfo = this.getModel().info - const totalCost = calculateApiCostOpenAI(modelInfo, inputTokens, outputTokens) - - yield { - type: "usage", - inputTokens: inputTokens, - outputTokens, - totalCost: totalCost, - } } catch (error) { await vscode.window.showErrorMessage(error.message) yield { diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 760d245114..8f37795501 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -2530,12 +2530,6 @@ export const webviewMessageHandler = async ( settings.codebaseIndexMistralApiKey, ) } - if (settings.codebaseIndexVercelAiGatewayApiKey !== undefined) { - await provider.contextProxy.storeSecret( - "codebaseIndexVercelAiGatewayApiKey", - settings.codebaseIndexVercelAiGatewayApiKey, - ) - } if (settings.codebaseIndexWatsonxApiKey !== undefined) { await provider.contextProxy.storeSecret( "codebaseIndexWatsonxApiKey", @@ -2548,6 +2542,12 @@ export const webviewMessageHandler = async ( settings.codebaseIndexWatsonxProjectId, ) } + if (settings.codebaseIndexVercelAiGatewayApiKey !== undefined) { + await provider.contextProxy.storeSecret( + "codebaseIndexVercelAiGatewayApiKey", + settings.codebaseIndexVercelAiGatewayApiKey, + ) + } // Send success response first - settings are saved regardless of validation await provider.postMessageToWebview({ type: "codeIndexSettingsSaved", diff --git a/src/package.json b/src/package.json index 2c78cefdeb..eccb0528cb 100644 --- a/src/package.json +++ b/src/package.json @@ -442,7 +442,7 @@ "@aws-sdk/client-bedrock-runtime": "^3.848.0", "@aws-sdk/credential-providers": "^3.848.0", "@google/genai": "^1.0.0", - "@ibm-cloud/watsonx-ai": "^1.6.8", + "@ibm-cloud/watsonx-ai": "^1.6.13", "@lmstudio/sdk": "^1.1.1", "@mistralai/mistralai": "^1.9.18", "@modelcontextprotocol/sdk": "1.12.0", @@ -522,6 +522,7 @@ "@types/diff": "^5.2.1", "@types/diff-match-patch": "^1.0.36", "@types/glob": "^8.1.0", + "@types/lodash.debounce": "^4.0.9", "@types/lodash": "^4.14.201", "@types/mocha": "^10.0.10", "@types/node": "20.x", diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 3ee20786c3..acb6fd43a0 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -285,6 +285,7 @@ export interface WebviewMessage { | "gemini" | "mistral" | "vercel-ai-gateway" + | "watsonx" codebaseIndexEmbedderBaseUrl?: string codebaseIndexEmbedderModelId: string codebaseIndexEmbedderModelDimension?: number // Generic dimension for all providers diff --git a/webview-ui/src/components/chat/CodeIndexPopover.tsx b/webview-ui/src/components/chat/CodeIndexPopover.tsx index 8933b2b089..56a97aac9e 100644 --- a/webview-ui/src/components/chat/CodeIndexPopover.tsx +++ b/webview-ui/src/components/chat/CodeIndexPopover.tsx @@ -408,17 +408,14 @@ export const CodeIndexPopover: React.FC = ({ if (!prev.codebaseIndexMistralApiKey || prev.codebaseIndexMistralApiKey === SECRET_PLACEHOLDER) { updated.codebaseIndexMistralApiKey = secretStatus.hasMistralApiKey ? SECRET_PLACEHOLDER : "" } - if (!prev.codebaseIndexWatsonxApiKey || prev.codebaseIndexWatsonxApiKey === SECRET_PLACEHOLDER) { - updated.codebaseIndexWatsonxApiKey = secretStatus.hasWatsonxApiKey ? SECRET_PLACEHOLDER : "" - } - if ( !prev.codebaseIndexVercelAiGatewayApiKey || prev.codebaseIndexVercelAiGatewayApiKey === SECRET_PLACEHOLDER ) { updated.codebaseIndexVercelAiGatewayApiKey = secretStatus.hasVercelAiGatewayApiKey + ? SECRET_PLACEHOLDER + : "" } - if (!prev.codebaseIndexWatsonxApiKey || prev.codebaseIndexWatsonxApiKey === SECRET_PLACEHOLDER) { updated.codebaseIndexWatsonxApiKey = secretStatus.hasWatsonxApiKey ? SECRET_PLACEHOLDER : "" } @@ -430,16 +427,6 @@ export const CodeIndexPopover: React.FC = ({ ? SECRET_PLACEHOLDER : "" } - - if ( - !prev.codebaseIndexWatsonxProjectId || - prev.codebaseIndexWatsonxProjectId === SECRET_PLACEHOLDER - ) { - updated.codebaseIndexWatsonxProjectId = secretStatus.hasWatsonxProjectId - ? SECRET_PLACEHOLDER - : "" - } - return updated } diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 94e50dec10..8d265603e8 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -621,15 +621,6 @@ const ApiOptions = ({ /> )} - {selectedProvider === "watsonx" && ( - - )} - {selectedProvider === "sambanova" && ( )} diff --git a/webview-ui/src/components/settings/providers/WatsonxAI.tsx b/webview-ui/src/components/settings/providers/WatsonxAI.tsx index 9de6f3e9c9..37400cd9b5 100644 --- a/webview-ui/src/components/settings/providers/WatsonxAI.tsx +++ b/webview-ui/src/components/settings/providers/WatsonxAI.tsx @@ -1,6 +1,6 @@ import { useCallback, useState, useEffect, useRef } from "react" import { VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ModelInfo, OrganizationAllowList, watsonxAiDefaultModelId, type ProviderSettings } from "@roo-code/types" +import { ModelInfo, watsonxAiDefaultModelId, type OrganizationAllowList, type ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { vscode } from "@src/utils/vscode" diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index f8f6e373fc..a9da2092d0 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -352,11 +352,11 @@ function getSelectedModel({ return { id, info } } case "watsonx": { - const id = apiConfiguration.apiModelId ?? watsonxAiDefaultModelId + const id = apiConfiguration.watsonxModelId ?? watsonxAiDefaultModelId const info = watsonxAiModels[id as keyof typeof watsonxAiModels] return { id, - info: info || undefined, + info: info, } } // case "anthropic":