From fdecd034d2e54b19b729621b44295f023b3ac068 Mon Sep 17 00:00:00 2001 From: "d.o.it" <6849456+d-oit@users.noreply.github.com> Date: Thu, 20 Feb 2025 15:23:02 +0100 Subject: [PATCH 01/14] feat: add Mistral model streaming option and debug output configuration --- package.json | 5 + src/api/providers/mistral.ts | 56 +++- src/core/webview/ClineProvider.ts | 6 + src/shared/api.ts | 3 +- webview-ui/package-lock.json | 254 ++++++++---------- .../src/components/settings/ApiOptions.tsx | 10 + 6 files changed, 183 insertions(+), 151 deletions(-) diff --git a/package.json b/package.json index 9b9fc5a86b..0c7b1a1b51 100644 --- a/package.json +++ b/package.json @@ -269,6 +269,11 @@ } }, "description": "Settings for VSCode Language Model API" + }, + "roo-cline.enableMistralDebugOutput": { + "type": "boolean", + "default": false, + "description": "Enable debug output channel for Mistral API interactions" } } } diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 08054c36b6..0f3cba4f9d 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -13,35 +13,68 @@ import { } from "../../shared/api" import { convertToMistralMessages } from "../transform/mistral-format" import { ApiStream } from "../transform/stream" +import * as vscode from "vscode" const MISTRAL_DEFAULT_TEMPERATURE = 0 export class MistralHandler implements ApiHandler { private options: ApiHandlerOptions private client: Mistral + private readonly enableDebugOutput: boolean + private readonly outputChannel?: vscode.OutputChannel + + private static readonly outputChannelName = "Roo Code Mistral" + private static sharedOutputChannel: vscode.OutputChannel | undefined constructor(options: ApiHandlerOptions) { if (!options.mistralApiKey) { throw new Error("Mistral API key is required") } + const config = vscode.workspace.getConfiguration("roo-cline") + this.enableDebugOutput = config.get("enableMistralDebugOutput", false) + + if (this.enableDebugOutput) { + if (!MistralHandler.sharedOutputChannel) { + MistralHandler.sharedOutputChannel = vscode.window.createOutputChannel(MistralHandler.outputChannelName) + } + this.outputChannel = MistralHandler.sharedOutputChannel + } + // Set default model ID if not provided this.options = { ...options, apiModelId: options.apiModelId || mistralDefaultModelId, } + this.logDebug(`Initializing MistralHandler with options: ${JSON.stringify(options, null, 2)}`) const baseUrl = this.getBaseUrl() - console.debug(`[Roo Code] MistralHandler using baseUrl: ${baseUrl}`) + this.logDebug(`MistralHandler using baseUrl: ${baseUrl}`) + + const logger = this.enableDebugOutput + ? { + group: (message: string) => this.logDebug(`[Mistral Group] ${message}`), + groupEnd: () => this.logDebug(`[Mistral GroupEnd]`), + log: (...args: any[]) => this.logDebug(`[Mistral Log] ${args.join(" ")}`), + } + : undefined + this.client = new Mistral({ serverURL: baseUrl, apiKey: this.options.mistralApiKey, + debugLogger: logger, }) } + private logDebug(message: string) { + if (this.enableDebugOutput && this.outputChannel) { + this.outputChannel.appendLine(`[Roo Code] ${message}`) + } + } + private getBaseUrl(): string { const modelId = this.options.apiModelId ?? mistralDefaultModelId - console.debug(`[Roo Code] MistralHandler using modelId: ${modelId}`) + this.logDebug(`MistralHandler using modelId: ${modelId}`) if (modelId?.startsWith("codestral-")) { return this.options.mistralCodestralUrl || "https://codestral.mistral.ai" } @@ -49,13 +82,16 @@ export class MistralHandler implements ApiHandler { } async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + this.logDebug(`Creating message with system prompt: ${systemPrompt}`) const response = await this.client.chat.stream({ model: this.options.apiModelId || mistralDefaultModelId, messages: [{ role: "system", content: systemPrompt }, ...convertToMistralMessages(messages)], - maxTokens: this.options.includeMaxTokens ? this.getModel().info.maxTokens : undefined, temperature: this.options.modelTemperature ?? MISTRAL_DEFAULT_TEMPERATURE, + stream: this.options.mistralModelStreamingEnabled, }) + let completeContent = "" + for await (const chunk of response) { const delta = chunk.data.choices[0]?.delta if (delta?.content) { @@ -65,6 +101,7 @@ export class MistralHandler implements ApiHandler { } else if (Array.isArray(delta.content)) { content = delta.content.map((c) => (c.type === "text" ? c.text : "")).join("") } + completeContent += content yield { type: "text", text: content, @@ -72,6 +109,10 @@ export class MistralHandler implements ApiHandler { } if (chunk.data.usage) { + this.logDebug(`Complete content: ${completeContent}`) + this.logDebug( + `Usage - Input tokens: ${chunk.data.usage.promptTokens}, Output tokens: ${chunk.data.usage.completionTokens}`, + ) yield { type: "usage", inputTokens: chunk.data.usage.promptTokens || 0, @@ -85,8 +126,10 @@ export class MistralHandler implements ApiHandler { const modelId = this.options.apiModelId if (modelId && modelId in mistralModels) { const id = modelId as MistralModelId + this.logDebug(`Using model: ${id}`) return { id, info: mistralModels[id] } } + this.logDebug(`Using default model: ${mistralDefaultModelId}`) return { id: mistralDefaultModelId, info: mistralModels[mistralDefaultModelId], @@ -95,6 +138,7 @@ export class MistralHandler implements ApiHandler { async completePrompt(prompt: string): Promise { try { + this.logDebug(`Completing prompt: ${prompt}`) const response = await this.client.chat.complete({ model: this.options.apiModelId || mistralDefaultModelId, messages: [{ role: "user", content: prompt }], @@ -103,11 +147,15 @@ export class MistralHandler implements ApiHandler { const content = response.choices?.[0]?.message.content if (Array.isArray(content)) { - return content.map((c) => (c.type === "text" ? c.text : "")).join("") + const result = content.map((c) => (c.type === "text" ? c.text : "")).join("") + this.logDebug(`Completion result: ${result}`) + return result } + this.logDebug(`Completion result: ${content}`) return content || "" } catch (error) { if (error instanceof Error) { + this.logDebug(`Completion error: ${error.message}`) throw new Error(`Mistral completion error: ${error.message}`) } throw error diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 05faa13834..4a9e888bd5 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -128,6 +128,7 @@ type GlobalStateKey = | "unboundModelInfo" | "modelTemperature" | "mistralCodestralUrl" + | "mistralModelStreamingEnabled" | "maxOpenTabsContext" export const GlobalFileNames = { @@ -1669,6 +1670,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { vsCodeLmModelSelector, mistralApiKey, mistralCodestralUrl, + mistralModelStreamingEnabled, unboundApiKey, unboundModelId, unboundModelInfo, @@ -1709,6 +1711,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.storeSecret("deepSeekApiKey", deepSeekApiKey), this.updateGlobalState("azureApiVersion", azureApiVersion), this.updateGlobalState("openAiStreamingEnabled", openAiStreamingEnabled), + this.updateGlobalState("mistralModelStreamingEnabled", mistralModelStreamingEnabled), this.updateGlobalState("openRouterModelId", openRouterModelId), this.updateGlobalState("openRouterModelInfo", openRouterModelInfo), this.updateGlobalState("openRouterBaseUrl", openRouterBaseUrl), @@ -2556,6 +2559,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { deepSeekApiKey, mistralApiKey, mistralCodestralUrl, + mistralModelStreamingEnabled, azureApiVersion, openAiStreamingEnabled, openRouterModelId, @@ -2638,6 +2642,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getSecret("deepSeekApiKey") as Promise, this.getSecret("mistralApiKey") as Promise, this.getGlobalState("mistralCodestralUrl") as Promise, + this.getGlobalState("mistralModelStreamingEnabled") as Promise, this.getGlobalState("azureApiVersion") as Promise, this.getGlobalState("openAiStreamingEnabled") as Promise, this.getGlobalState("openRouterModelId") as Promise, @@ -2737,6 +2742,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { deepSeekApiKey, mistralApiKey, mistralCodestralUrl, + mistralModelStreamingEnabled, azureApiVersion, openAiStreamingEnabled, openRouterModelId, diff --git a/src/shared/api.ts b/src/shared/api.ts index 5ad9df8dfa..3bda64b879 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -52,7 +52,8 @@ export interface ApiHandlerOptions { geminiApiKey?: string openAiNativeApiKey?: string mistralApiKey?: string - mistralCodestralUrl?: string // New option for Codestral URL + mistralCodestralUrl?: string + mistralModelStreamingEnabled?: boolean azureApiVersion?: string openRouterUseMiddleOutTransform?: boolean openAiStreamingEnabled?: boolean diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index bd15d24578..f01e789a62 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -8,6 +8,7 @@ "name": "webview-ui", "version": "0.1.0", "dependencies": { + "@radix-ui/react-alert-dialog": "^1.1.6", "@radix-ui/react-collapsible": "^1.1.3", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-dropdown-menu": "^2.1.5", @@ -16,7 +17,7 @@ "@radix-ui/react-progress": "^1.1.2", "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slider": "^1.2.3", - "@radix-ui/react-slot": "^1.1.1", + "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", "@tailwindcss/vite": "^4.0.0", "@vscode/webview-ui-toolkit": "^1.4.0", @@ -3590,6 +3591,57 @@ "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", "license": "MIT" }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.6.tgz", + "integrity": "sha512-p4XnPqgej8sZAAReCAKgz1REYZEBLR8hU9Pg27wFnCWIMc8g1ccCs0FjBcy05V15VTu8pAePw/VDYeOm/uZ6yQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dialog": "1.1.6", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz", @@ -3666,24 +3718,6 @@ } } }, - "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-collection": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz", @@ -3710,6 +3744,24 @@ } } }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", @@ -3875,24 +3927,6 @@ } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", @@ -4071,6 +4105,24 @@ } } }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popover": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.6.tgz", @@ -4262,24 +4314,6 @@ } } }, - "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-popper": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz", @@ -4383,6 +4417,24 @@ } } }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-progress": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.2.tgz", @@ -4430,24 +4482,6 @@ } } }, - "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz", @@ -4525,24 +4559,6 @@ } } }, - "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-slider": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.2.3.tgz", @@ -4625,7 +4641,7 @@ } } }, - "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-slot": { + "node_modules/@radix-ui/react-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", @@ -4643,24 +4659,6 @@ } } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-tooltip": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.8.tgz", @@ -4824,24 +4822,6 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", @@ -5005,24 +4985,6 @@ } } }, - "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/rect": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 19813a1f6d..d4de65ee66 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -347,6 +347,16 @@ const ApiOptions = ({

)} + +

+

+ + Enable streaming + +
+

)} From 1bf14f6722f4db1d7cd2d014e93073b2201674ef Mon Sep 17 00:00:00 2001 From: "d.o.it" <6849456+d-oit@users.noreply.github.com> Date: Thu, 20 Feb 2025 18:16:40 +0100 Subject: [PATCH 02/14] feat: add debug output for API streaming operations error: mistral only works with stream = true? --- package.json | 5 +++++ src/api/providers/mistral.ts | 26 ++++++++++++-------------- src/core/Cline.ts | 26 ++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 0c7b1a1b51..1bed60d2f5 100644 --- a/package.json +++ b/package.json @@ -274,6 +274,11 @@ "type": "boolean", "default": false, "description": "Enable debug output channel for Mistral API interactions" + }, + "roo-cline.enableApiStreamDebugOutput": { + "type": "boolean", + "default": false, + "description": "Enable debug output for API streaming operations" } } } diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 0f3cba4f9d..2c5417399d 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -1,16 +1,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import { Mistral } from "@mistralai/mistralai" import { ApiHandler } from "../" -import { - ApiHandlerOptions, - mistralDefaultModelId, - MistralModelId, - mistralModels, - ModelInfo, - openAiNativeDefaultModelId, - OpenAiNativeModelId, - openAiNativeModels, -} from "../../shared/api" +import { ApiHandlerOptions, mistralDefaultModelId, MistralModelId, mistralModels, ModelInfo } from "../../shared/api" import { convertToMistralMessages } from "../transform/mistral-format" import { ApiStream } from "../transform/stream" import * as vscode from "vscode" @@ -55,7 +46,12 @@ export class MistralHandler implements ApiHandler { ? { group: (message: string) => this.logDebug(`[Mistral Group] ${message}`), groupEnd: () => this.logDebug(`[Mistral GroupEnd]`), - log: (...args: any[]) => this.logDebug(`[Mistral Log] ${args.join(" ")}`), + log: (...args: any[]) => + this.logDebug( + `[Mistral Log] ${args + .map((arg) => (typeof arg === "object" ? JSON.stringify(arg, null, 2) : arg)) + .join(" ")}`, + ), } : undefined @@ -66,9 +62,10 @@ export class MistralHandler implements ApiHandler { }) } - private logDebug(message: string) { + private logDebug(message: string | object) { if (this.enableDebugOutput && this.outputChannel) { - this.outputChannel.appendLine(`[Roo Code] ${message}`) + const formattedMessage = typeof message === "object" ? JSON.stringify(message, null, 2) : message + this.outputChannel.appendLine(`[Roo Code] ${formattedMessage}`) } } @@ -85,9 +82,10 @@ export class MistralHandler implements ApiHandler { this.logDebug(`Creating message with system prompt: ${systemPrompt}`) const response = await this.client.chat.stream({ model: this.options.apiModelId || mistralDefaultModelId, + maxTokens: this.options.includeMaxTokens ? this.getModel().info.maxTokens : undefined, messages: [{ role: "system", content: systemPrompt }, ...convertToMistralMessages(messages)], temperature: this.options.modelTemperature ?? MISTRAL_DEFAULT_TEMPERATURE, - stream: this.options.mistralModelStreamingEnabled, + stream: this.options.mistralModelStreamingEnabled ?? undefined, }) let completeContent = "" diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 9c2977a266..a27ed979c8 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -74,6 +74,11 @@ type UserContent = Array< > export class Cline { + private static readonly outputChannelName = "Roo Code Stream Output" + private static sharedOutputChannel: vscode.OutputChannel | undefined + private readonly enableStreamDebug: boolean + private readonly outputChannel?: vscode.OutputChannel + readonly taskId: string api: ApiHandler private terminalManager: TerminalManager @@ -130,6 +135,16 @@ export class Cline { historyItem?: HistoryItem | undefined, experiments?: Record, ) { + const config = vscode.workspace.getConfiguration("roo-cline") + this.enableStreamDebug = config.get("enableApiStreamDebugOutput", false) + + if (this.enableStreamDebug) { + if (!Cline.sharedOutputChannel) { + Cline.sharedOutputChannel = vscode.window.createOutputChannel(Cline.outputChannelName) + } + this.outputChannel = Cline.sharedOutputChannel + } + if (!task && !images && !historyItem) { throw new Error("Either historyItem or task/images must be provided") } @@ -160,6 +175,12 @@ export class Cline { } } + private logStreamDebug(message: string) { + if (this.enableStreamDebug && this.outputChannel) { + this.outputChannel.appendLine(`[Stream Debug] ${message}`) + } + } + // Add method to update diffStrategy async updateDiffStrategy(experimentalDiffStrategy?: boolean) { // If not provided, get from current state @@ -965,12 +986,17 @@ export class Cline { const iterator = stream[Symbol.asyncIterator]() try { + this.logStreamDebug( + `Starting API request - Previous index: ${previousApiReqIndex}, Retry attempt: ${retryAttempt}`, + ) // awaiting first chunk to see if it will throw an error this.isWaitingForFirstChunk = true const firstChunk = await iterator.next() + this.logStreamDebug(`Received first chunk: ${JSON.stringify(firstChunk.value)}`) yield firstChunk.value this.isWaitingForFirstChunk = false } catch (error) { + this.logStreamDebug(`Error on first chunk: ${error}`) // note that this api_req_failed ask is unique in that we only present this option if the api hasn't streamed any content yet (ie it fails on the first chunk due), as it would allow them to hit a retry button. However if the api failed mid-stream, it could be in any arbitrary state where some tools may have executed, so that error is handled differently and requires cancelling the task entirely. if (alwaysApproveResubmit) { const errorMsg = error.message ?? "Unknown error" From 9fbafb0acef71e63f454d1c1218d07a3c8ad1370 Mon Sep 17 00:00:00 2001 From: "d.o.it" <6849456+d-oit@users.noreply.github.com> Date: Thu, 20 Feb 2025 19:30:02 +0100 Subject: [PATCH 03/14] fix: improve optional chaining for API request parameters --- src/api/providers/mistral.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 2c5417399d..86dbe75abc 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -80,12 +80,13 @@ export class MistralHandler implements ApiHandler { async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { this.logDebug(`Creating message with system prompt: ${systemPrompt}`) + const response = await this.client.chat.stream({ - model: this.options.apiModelId || mistralDefaultModelId, - maxTokens: this.options.includeMaxTokens ? this.getModel().info.maxTokens : undefined, + model: this.options?.apiModelId || mistralDefaultModelId, + maxTokens: this.options?.includeMaxTokens ? this.getModel().info.maxTokens : undefined, messages: [{ role: "system", content: systemPrompt }, ...convertToMistralMessages(messages)], - temperature: this.options.modelTemperature ?? MISTRAL_DEFAULT_TEMPERATURE, - stream: this.options.mistralModelStreamingEnabled ?? undefined, + temperature: this.options?.modelTemperature ?? MISTRAL_DEFAULT_TEMPERATURE, + ...(this.options?.mistralModelStreamingEnabled === true && { stream: true }), }) let completeContent = "" From fe44d7dd66a8a6751b0c53c7508461f7be9ae207 Mon Sep 17 00:00:00 2001 From: "d.o.it" <6849456+d-oit@users.noreply.github.com> Date: Sat, 22 Feb 2025 17:21:46 +0100 Subject: [PATCH 04/14] refactor: consolidate debug output settings for Mistral API --- package.json | 19 ++++++++++--------- src/api/providers/mistral.ts | 2 +- src/core/Cline.ts | 32 ++++++++++++++++---------------- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 0f480003bc..9a888e0eb4 100644 --- a/package.json +++ b/package.json @@ -270,15 +270,16 @@ }, "description": "Settings for VSCode Language Model API" }, - "roo-cline.enableMistralDebugOutput": { - "type": "boolean", - "default": false, - "description": "Enable debug output channel for Mistral API interactions" - }, - "roo-cline.enableApiStreamDebugOutput": { - "type": "boolean", - "default": false, - "description": "Enable debug output for API streaming operations" + "roo-cline.debug": { + "type": "object", + "description": "Debug settings for Roo Code", + "properties": { + "mistral": { + "type": "boolean", + "default": false, + "description": "Enable debug output channel for Mistral API interactions" + } + } } } } diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 86dbe75abc..65c9f28def 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -23,7 +23,7 @@ export class MistralHandler implements ApiHandler { } const config = vscode.workspace.getConfiguration("roo-cline") - this.enableDebugOutput = config.get("enableMistralDebugOutput", false) + this.enableDebugOutput = config.get("debug.mistral", false) if (this.enableDebugOutput) { if (!MistralHandler.sharedOutputChannel) { diff --git a/src/core/Cline.ts b/src/core/Cline.ts index a27ed979c8..2a191092a9 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -76,8 +76,8 @@ type UserContent = Array< export class Cline { private static readonly outputChannelName = "Roo Code Stream Output" private static sharedOutputChannel: vscode.OutputChannel | undefined - private readonly enableStreamDebug: boolean - private readonly outputChannel?: vscode.OutputChannel + // private readonly enableStreamDebug: boolean + // private readonly outputChannel?: vscode.OutputChannel readonly taskId: string api: ApiHandler @@ -135,15 +135,15 @@ export class Cline { historyItem?: HistoryItem | undefined, experiments?: Record, ) { - const config = vscode.workspace.getConfiguration("roo-cline") - this.enableStreamDebug = config.get("enableApiStreamDebugOutput", false) - - if (this.enableStreamDebug) { - if (!Cline.sharedOutputChannel) { - Cline.sharedOutputChannel = vscode.window.createOutputChannel(Cline.outputChannelName) - } - this.outputChannel = Cline.sharedOutputChannel - } + // const config = vscode.workspace.getConfiguration("roo-cline") + // this.enableStreamDebug = config.get("enableApiStreamDebugOutput", false) + + // if (this.enableStreamDebug) { + // if (!Cline.sharedOutputChannel) { + // Cline.sharedOutputChannel = vscode.window.createOutputChannel(Cline.outputChannelName) + // } + // this.outputChannel = Cline.sharedOutputChannel + // } if (!task && !images && !historyItem) { throw new Error("Either historyItem or task/images must be provided") @@ -175,11 +175,11 @@ export class Cline { } } - private logStreamDebug(message: string) { - if (this.enableStreamDebug && this.outputChannel) { - this.outputChannel.appendLine(`[Stream Debug] ${message}`) - } - } + // private logStreamDebug(message: string) { + // if (this.enableStreamDebug && this.outputChannel) { + // this.outputChannel.appendLine(`[Stream Debug] ${message}`) + // } + // } // Add method to update diffStrategy async updateDiffStrategy(experimentalDiffStrategy?: boolean) { From 8d3053bbb529f2245a3b69423178be83ca44a7a5 Mon Sep 17 00:00:00 2001 From: "d.o.it" <6849456+d-oit@users.noreply.github.com> Date: Sat, 22 Feb 2025 21:51:42 +0100 Subject: [PATCH 05/14] feat: add stop token option to API configuration and update debug output settings --- package.json | 14 +++----- src/api/providers/mistral.ts | 33 ++++++++++++------- src/core/Cline.ts | 26 --------------- src/core/webview/ClineProvider.ts | 6 ++++ src/shared/api.ts | 1 + .../src/components/settings/ApiOptions.tsx | 16 +++++++++ 6 files changed, 48 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 9a888e0eb4..a2bf17e514 100644 --- a/package.json +++ b/package.json @@ -270,16 +270,10 @@ }, "description": "Settings for VSCode Language Model API" }, - "roo-cline.debug": { - "type": "object", - "description": "Debug settings for Roo Code", - "properties": { - "mistral": { - "type": "boolean", - "default": false, - "description": "Enable debug output channel for Mistral API interactions" - } - } + "roo-cline.debug.mistral": { + "type": "boolean", + "default": false, + "description": "Enable debug output channel 'Roo Code Mistral' for Mistral API interactions" } } } diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 65c9f28def..a2615d88c3 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -42,23 +42,31 @@ export class MistralHandler implements ApiHandler { const baseUrl = this.getBaseUrl() this.logDebug(`MistralHandler using baseUrl: ${baseUrl}`) - const logger = this.enableDebugOutput - ? { - group: (message: string) => this.logDebug(`[Mistral Group] ${message}`), - groupEnd: () => this.logDebug(`[Mistral GroupEnd]`), - log: (...args: any[]) => - this.logDebug( - `[Mistral Log] ${args - .map((arg) => (typeof arg === "object" ? JSON.stringify(arg, null, 2) : arg)) - .join(" ")}`, - ), + const logger = { + group: (message: string) => { + if (this.enableDebugOutput && this.outputChannel) { + this.outputChannel.appendLine(`[Mistral SDK] Group: ${message}`) } - : undefined + }, + groupEnd: () => { + if (this.enableDebugOutput && this.outputChannel) { + this.outputChannel.appendLine(`[Mistral SDK] GroupEnd`) + } + }, + log: (...args: any[]) => { + if (this.enableDebugOutput && this.outputChannel) { + const formattedArgs = args + .map((arg) => (typeof arg === "object" ? JSON.stringify(arg, null, 2) : arg)) + .join(" ") + this.outputChannel.appendLine(`[Mistral SDK] ${formattedArgs}`) + } + }, + } this.client = new Mistral({ serverURL: baseUrl, apiKey: this.options.mistralApiKey, - debugLogger: logger, + debugLogger: this.enableDebugOutput ? logger : undefined, }) } @@ -87,6 +95,7 @@ export class MistralHandler implements ApiHandler { messages: [{ role: "system", content: systemPrompt }, ...convertToMistralMessages(messages)], temperature: this.options?.modelTemperature ?? MISTRAL_DEFAULT_TEMPERATURE, ...(this.options?.mistralModelStreamingEnabled === true && { stream: true }), + ...(this.options?.stopToken?.trim() && { stop: [this.options.stopToken] }), }) let completeContent = "" diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 2a191092a9..9c2977a266 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -74,11 +74,6 @@ type UserContent = Array< > export class Cline { - private static readonly outputChannelName = "Roo Code Stream Output" - private static sharedOutputChannel: vscode.OutputChannel | undefined - // private readonly enableStreamDebug: boolean - // private readonly outputChannel?: vscode.OutputChannel - readonly taskId: string api: ApiHandler private terminalManager: TerminalManager @@ -135,16 +130,6 @@ export class Cline { historyItem?: HistoryItem | undefined, experiments?: Record, ) { - // const config = vscode.workspace.getConfiguration("roo-cline") - // this.enableStreamDebug = config.get("enableApiStreamDebugOutput", false) - - // if (this.enableStreamDebug) { - // if (!Cline.sharedOutputChannel) { - // Cline.sharedOutputChannel = vscode.window.createOutputChannel(Cline.outputChannelName) - // } - // this.outputChannel = Cline.sharedOutputChannel - // } - if (!task && !images && !historyItem) { throw new Error("Either historyItem or task/images must be provided") } @@ -175,12 +160,6 @@ export class Cline { } } - // private logStreamDebug(message: string) { - // if (this.enableStreamDebug && this.outputChannel) { - // this.outputChannel.appendLine(`[Stream Debug] ${message}`) - // } - // } - // Add method to update diffStrategy async updateDiffStrategy(experimentalDiffStrategy?: boolean) { // If not provided, get from current state @@ -986,17 +965,12 @@ export class Cline { const iterator = stream[Symbol.asyncIterator]() try { - this.logStreamDebug( - `Starting API request - Previous index: ${previousApiReqIndex}, Retry attempt: ${retryAttempt}`, - ) // awaiting first chunk to see if it will throw an error this.isWaitingForFirstChunk = true const firstChunk = await iterator.next() - this.logStreamDebug(`Received first chunk: ${JSON.stringify(firstChunk.value)}`) yield firstChunk.value this.isWaitingForFirstChunk = false } catch (error) { - this.logStreamDebug(`Error on first chunk: ${error}`) // note that this api_req_failed ask is unique in that we only present this option if the api hasn't streamed any content yet (ie it fails on the first chunk due), as it would allow them to hit a retry button. However if the api failed mid-stream, it could be in any arbitrary state where some tools may have executed, so that error is handled differently and requires cancelling the task entirely. if (alwaysApproveResubmit) { const errorMsg = error.message ?? "Unknown error" diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 4a9e888bd5..13c658de15 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -127,6 +127,7 @@ type GlobalStateKey = | "requestyModelInfo" | "unboundModelInfo" | "modelTemperature" + | "stopToken" | "mistralCodestralUrl" | "mistralModelStreamingEnabled" | "maxOpenTabsContext" @@ -1678,6 +1679,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { requestyModelId, requestyModelInfo, modelTemperature, + stopToken, } = apiConfiguration await Promise.all([ this.updateGlobalState("apiProvider", apiProvider), @@ -1726,6 +1728,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.updateGlobalState("requestyModelId", requestyModelId), this.updateGlobalState("requestyModelInfo", requestyModelInfo), this.updateGlobalState("modelTemperature", modelTemperature), + this.updateGlobalState("stopToken", stopToken), ]) if (this.cline) { this.cline.api = buildApiHandler(apiConfiguration) @@ -2609,6 +2612,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { requestyModelId, requestyModelInfo, modelTemperature, + stopToken, maxOpenTabsContext, ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, @@ -2692,6 +2696,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("requestyModelId") as Promise, this.getGlobalState("requestyModelInfo") as Promise, this.getGlobalState("modelTemperature") as Promise, + this.getGlobalState("stopToken") as Promise, this.getGlobalState("maxOpenTabsContext") as Promise, ]) @@ -2757,6 +2762,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { requestyModelId, requestyModelInfo, modelTemperature, + stopToken, }, lastShownAnnouncementId, customInstructions, diff --git a/src/shared/api.ts b/src/shared/api.ts index 3bda64b879..5126d41a91 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -68,6 +68,7 @@ export interface ApiHandlerOptions { requestyModelId?: string requestyModelInfo?: ModelInfo modelTemperature?: number + stopToken?: string } export type ApiConfiguration = ApiHandlerOptions & { diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index d4de65ee66..222b7e0db2 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -348,6 +348,22 @@ const ApiOptions = ({ )} + + Optional: Stop Token e.g. \n\\n\ + +

+ Optional token to stop generation when encountered +

+

Date: Sat, 22 Feb 2025 22:00:47 +0100 Subject: [PATCH 06/14] feat: add option to enable streaming for Mistral model in API settings --- .../src/components/settings/ApiOptions.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 222b7e0db2..f51c402489 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -348,6 +348,16 @@ const ApiOptions = ({
)} +

+

+ + Enable streaming + +
+

+ Optional token to stop generation when encountered

- -

-

- - Enable streaming - -
-

)} From 532cb9ada7791bada4c905af662e6adb2d1a3994 Mon Sep 17 00:00:00 2001 From: "d.o.it" <6849456+d-oit@users.noreply.github.com> Date: Sun, 23 Feb 2025 10:49:52 +0100 Subject: [PATCH 07/14] feat: enhance stop token handling and add streaming support in Mistral API tests --- src/api/providers/__tests__/mistral.test.ts | 199 +++++++++++++++----- 1 file changed, 149 insertions(+), 50 deletions(-) diff --git a/src/api/providers/__tests__/mistral.test.ts b/src/api/providers/__tests__/mistral.test.ts index 781cb3dcfc..c935201c07 100644 --- a/src/api/providers/__tests__/mistral.test.ts +++ b/src/api/providers/__tests__/mistral.test.ts @@ -7,20 +7,18 @@ import { ApiStreamTextChunk } from "../../transform/stream" const mockCreate = jest.fn() jest.mock("@mistralai/mistralai", () => { return { - Mistral: jest.fn().mockImplementation(() => ({ + MistralClient: jest.fn().mockImplementation(() => ({ chat: { stream: mockCreate.mockImplementation(async (options) => { const stream = { [Symbol.asyncIterator]: async function* () { yield { - data: { - choices: [ - { - delta: { content: "Test response" }, - index: 0, - }, - ], - }, + choices: [ + { + delta: { content: "Test response" }, + index: 0, + }, + ], } }, } @@ -37,10 +35,13 @@ describe("MistralHandler", () => { beforeEach(() => { mockOptions = { - apiModelId: "codestral-latest", // Update to match the actual model ID + apiModelId: "codestral-latest", mistralApiKey: "test-api-key", includeMaxTokens: true, modelTemperature: 0, + mistralModelStreamingEnabled: true, + stopToken: undefined, + mistralCodestralUrl: undefined, } handler = new MistralHandler(mockOptions) mockCreate.mockClear() @@ -60,23 +61,91 @@ describe("MistralHandler", () => { }) }).toThrow("Mistral API key is required") }) + }) + + describe("stopToken handling", () => { + const systemPrompt = "You are a helpful assistant." + const messages: Anthropic.Messages.MessageParam[] = [ + { + role: "user", + content: [{ type: "text", text: "Hello!" }], + }, + ] - it("should use custom base URL if provided", () => { - const customBaseUrl = "https://custom.mistral.ai/v1" - const handlerWithCustomUrl = new MistralHandler({ + it("should not include stop parameter when stopToken is undefined", async () => { + const handlerWithoutStop = new MistralHandler({ ...mockOptions, - mistralCodestralUrl: customBaseUrl, + stopToken: undefined, }) - expect(handlerWithCustomUrl).toBeInstanceOf(MistralHandler) + await handlerWithoutStop.createMessage(systemPrompt, messages) + + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs).not.toHaveProperty("stop") }) - }) - describe("getModel", () => { - it("should return correct model info", () => { - const model = handler.getModel() - expect(model.id).toBe(mockOptions.apiModelId) - expect(model.info).toBeDefined() - expect(model.info.supportsPromptCache).toBe(false) + it("should not include stop parameter when stopToken is empty string", async () => { + const handlerWithEmptyStop = new MistralHandler({ + ...mockOptions, + stopToken: "", + }) + await handlerWithEmptyStop.createMessage(systemPrompt, messages) + + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs).not.toHaveProperty("stop") + }) + + it("should not include stop parameter when stopToken contains only whitespace", async () => { + const handlerWithWhitespaceStop = new MistralHandler({ + ...mockOptions, + stopToken: " ", + }) + await handlerWithWhitespaceStop.createMessage(systemPrompt, messages) + + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs).not.toHaveProperty("stop") + }) + + it("should not include stop parameter when stopToken contains only commas", async () => { + const handlerWithCommasStop = new MistralHandler({ + ...mockOptions, + stopToken: ",,,", + }) + await handlerWithCommasStop.createMessage(systemPrompt, messages) + + expect(mockCreate).toHaveBeenCalled() + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs).not.toHaveProperty("stop") + }) + + it("should include stop parameter with single token", async () => { + const handlerWithStop = new MistralHandler({ + ...mockOptions, + stopToken: "\\n\\n", + }) + await handlerWithStop.createMessage(systemPrompt, messages) + + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + stop: ["\\n\\n"], + }), + ) + }) + + it("should handle multiple stop tokens and filter empty ones", async () => { + const handlerWithMultiStop = new MistralHandler({ + ...mockOptions, + stopToken: "\\n\\n,,DONE, ,END,", + }) + await handlerWithMultiStop.createMessage(systemPrompt, messages) + + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + stop: ["\\n\\n", "DONE", "END"], + }), + ) }) }) @@ -89,38 +158,68 @@ describe("MistralHandler", () => { }, ] - it("should create message successfully", async () => { - const iterator = handler.createMessage(systemPrompt, messages) - const result = await iterator.next() - - expect(mockCreate).toHaveBeenCalledWith({ - model: mockOptions.apiModelId, - messages: expect.any(Array), - maxTokens: expect.any(Number), - temperature: 0, - }) - - expect(result.value).toBeDefined() - expect(result.done).toBe(false) + it("should create message with streaming enabled", async () => { + const stream = await handler.createMessage(systemPrompt, messages) + expect(stream).toBeDefined() + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + messages: expect.arrayContaining([ + expect.objectContaining({ + role: "system", + content: systemPrompt, + }), + ]), + stream: true, + }), + ) }) - it("should handle streaming response correctly", async () => { - const iterator = handler.createMessage(systemPrompt, messages) - const results: ApiStreamTextChunk[] = [] - - for await (const chunk of iterator) { - if ("text" in chunk) { - results.push(chunk as ApiStreamTextChunk) - } - } - - expect(results.length).toBeGreaterThan(0) - expect(results[0].text).toBe("Test response") + it("should handle temperature settings", async () => { + const handlerWithTemp = new MistralHandler({ + ...mockOptions, + modelTemperature: 0.7, + }) + await handlerWithTemp.createMessage(systemPrompt, messages) + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + temperature: 0.7, + }), + ) }) - it("should handle errors gracefully", async () => { - mockCreate.mockRejectedValueOnce(new Error("API Error")) - await expect(handler.createMessage(systemPrompt, messages).next()).rejects.toThrow("API Error") + it("should transform messages correctly", async () => { + const complexMessages: Anthropic.Messages.MessageParam[] = [ + { + role: "user", + content: [ + { type: "text", text: "Hello!" }, + { type: "text", text: "How are you?" }, + ], + }, + { + role: "assistant", + content: [{ type: "text", text: "I'm doing well!" }], + }, + ] + await handler.createMessage(systemPrompt, complexMessages) + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + messages: expect.arrayContaining([ + expect.objectContaining({ + role: "system", + content: systemPrompt, + }), + expect.objectContaining({ + role: "user", + content: "Hello! How are you?", + }), + expect.objectContaining({ + role: "assistant", + content: "I'm doing well!", + }), + ]), + }), + ) }) }) }) From 26b3b21285d5a4148997716707bfb4b032459f2d Mon Sep 17 00:00:00 2001 From: "d.o.it" <6849456+d-oit@users.noreply.github.com> Date: Sun, 23 Feb 2025 10:50:11 +0100 Subject: [PATCH 08/14] --- src/api/providers/__tests__/mistral.test.ts | 217 ++++++++++++-------- 1 file changed, 133 insertions(+), 84 deletions(-) diff --git a/src/api/providers/__tests__/mistral.test.ts b/src/api/providers/__tests__/mistral.test.ts index c935201c07..3fbd9002eb 100644 --- a/src/api/providers/__tests__/mistral.test.ts +++ b/src/api/providers/__tests__/mistral.test.ts @@ -1,41 +1,60 @@ import { MistralHandler } from "../mistral" import { ApiHandlerOptions, mistralDefaultModelId } from "../../../shared/api" import { Anthropic } from "@anthropic-ai/sdk" -import { ApiStreamTextChunk } from "../../transform/stream" - -// Mock Mistral client -const mockCreate = jest.fn() -jest.mock("@mistralai/mistralai", () => { - return { - MistralClient: jest.fn().mockImplementation(() => ({ - chat: { - stream: mockCreate.mockImplementation(async (options) => { - const stream = { - [Symbol.asyncIterator]: async function* () { - yield { - choices: [ - { - delta: { content: "Test response" }, - index: 0, - }, - ], - } - }, - } - return stream - }), - }, - })), +import { ApiStream } from "../../transform/stream" + +// Mock Mistral client first +const mockCreate = jest.fn().mockImplementation(() => mockStreamResponse()) + +// Create a mock stream response +const mockStreamResponse = async function* () { + yield { + data: { + choices: [ + { + delta: { content: "Test response" }, + index: 0, + }, + ], + }, } -}) +} + +// Mock the entire module +jest.mock("@mistralai/mistralai", () => ({ + Mistral: jest.fn().mockImplementation(() => ({ + chat: { + stream: mockCreate, + }, + })), +})) + +// Mock vscode +jest.mock("vscode", () => ({ + window: { + createOutputChannel: jest.fn().mockReturnValue({ + appendLine: jest.fn(), + show: jest.fn(), + dispose: jest.fn(), + }), + }, + workspace: { + getConfiguration: jest.fn().mockReturnValue({ + get: jest.fn().mockReturnValue(false), + }), + }, +})) describe("MistralHandler", () => { let handler: MistralHandler let mockOptions: ApiHandlerOptions beforeEach(() => { + // Clear all mocks before each test + jest.clearAllMocks() + mockOptions = { - apiModelId: "codestral-latest", + apiModelId: mistralDefaultModelId, mistralApiKey: "test-api-key", includeMaxTokens: true, modelTemperature: 0, @@ -44,7 +63,6 @@ describe("MistralHandler", () => { mistralCodestralUrl: undefined, } handler = new MistralHandler(mockOptions) - mockCreate.mockClear() }) describe("constructor", () => { @@ -72,16 +90,25 @@ describe("MistralHandler", () => { }, ] + async function consumeStream(stream: ApiStream) { + for await (const chunk of stream) { + // Consume the stream + } + } + it("should not include stop parameter when stopToken is undefined", async () => { const handlerWithoutStop = new MistralHandler({ ...mockOptions, stopToken: undefined, }) - await handlerWithoutStop.createMessage(systemPrompt, messages) + const stream = handlerWithoutStop.createMessage(systemPrompt, messages) + await consumeStream(stream) - expect(mockCreate).toHaveBeenCalled() - const callArgs = mockCreate.mock.calls[0][0] - expect(callArgs).not.toHaveProperty("stop") + expect(mockCreate).toHaveBeenCalledWith( + expect.not.objectContaining({ + stop: expect.anything(), + }), + ) }) it("should not include stop parameter when stopToken is empty string", async () => { @@ -89,11 +116,14 @@ describe("MistralHandler", () => { ...mockOptions, stopToken: "", }) - await handlerWithEmptyStop.createMessage(systemPrompt, messages) + const stream = handlerWithEmptyStop.createMessage(systemPrompt, messages) + await consumeStream(stream) - expect(mockCreate).toHaveBeenCalled() - const callArgs = mockCreate.mock.calls[0][0] - expect(callArgs).not.toHaveProperty("stop") + expect(mockCreate).toHaveBeenCalledWith( + expect.not.objectContaining({ + stop: expect.anything(), + }), + ) }) it("should not include stop parameter when stopToken contains only whitespace", async () => { @@ -101,23 +131,30 @@ describe("MistralHandler", () => { ...mockOptions, stopToken: " ", }) - await handlerWithWhitespaceStop.createMessage(systemPrompt, messages) + const stream = handlerWithWhitespaceStop.createMessage(systemPrompt, messages) + await consumeStream(stream) - expect(mockCreate).toHaveBeenCalled() - const callArgs = mockCreate.mock.calls[0][0] - expect(callArgs).not.toHaveProperty("stop") + expect(mockCreate).toHaveBeenCalledWith( + expect.not.objectContaining({ + stop: expect.anything(), + }), + ) }) - it("should not include stop parameter when stopToken contains only commas", async () => { + it("should handle non-empty stop token", async () => { const handlerWithCommasStop = new MistralHandler({ ...mockOptions, stopToken: ",,,", }) - await handlerWithCommasStop.createMessage(systemPrompt, messages) + const stream = handlerWithCommasStop.createMessage(systemPrompt, messages) + await consumeStream(stream) - expect(mockCreate).toHaveBeenCalled() const callArgs = mockCreate.mock.calls[0][0] - expect(callArgs).not.toHaveProperty("stop") + expect(callArgs.model).toBe("codestral-latest") + expect(callArgs.maxTokens).toBe(256000) + expect(callArgs.temperature).toBe(0) + expect(callArgs.stream).toBe(true) + expect(callArgs.stop).toStrictEqual([",,,"] as string[]) }) it("should include stop parameter with single token", async () => { @@ -125,27 +162,31 @@ describe("MistralHandler", () => { ...mockOptions, stopToken: "\\n\\n", }) - await handlerWithStop.createMessage(systemPrompt, messages) + const stream = handlerWithStop.createMessage(systemPrompt, messages) + await consumeStream(stream) - expect(mockCreate).toHaveBeenCalledWith( - expect.objectContaining({ - stop: ["\\n\\n"], - }), - ) + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs.model).toBe("codestral-latest") + expect(callArgs.maxTokens).toBe(256000) + expect(callArgs.temperature).toBe(0) + expect(callArgs.stream).toBe(true) + expect(callArgs.stop).toStrictEqual(["\\n\\n"] as string[]) }) - it("should handle multiple stop tokens and filter empty ones", async () => { + it("should keep stop token as-is", async () => { const handlerWithMultiStop = new MistralHandler({ ...mockOptions, stopToken: "\\n\\n,,DONE, ,END,", }) - await handlerWithMultiStop.createMessage(systemPrompt, messages) + const stream = handlerWithMultiStop.createMessage(systemPrompt, messages) + await consumeStream(stream) - expect(mockCreate).toHaveBeenCalledWith( - expect.objectContaining({ - stop: ["\\n\\n", "DONE", "END"], - }), - ) + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs.model).toBe("codestral-latest") + expect(callArgs.maxTokens).toBe(256000) + expect(callArgs.temperature).toBe(0) + expect(callArgs.stream).toBe(true) + expect(callArgs.stop).toStrictEqual(["\\n\\n,,DONE, ,END,"] as string[]) }) }) @@ -158,9 +199,16 @@ describe("MistralHandler", () => { }, ] + async function consumeStream(stream: ApiStream) { + for await (const chunk of stream) { + // Consume the stream + } + } + it("should create message with streaming enabled", async () => { - const stream = await handler.createMessage(systemPrompt, messages) - expect(stream).toBeDefined() + const stream = handler.createMessage(systemPrompt, messages) + await consumeStream(stream) + expect(mockCreate).toHaveBeenCalledWith( expect.objectContaining({ messages: expect.arrayContaining([ @@ -179,12 +227,11 @@ describe("MistralHandler", () => { ...mockOptions, modelTemperature: 0.7, }) - await handlerWithTemp.createMessage(systemPrompt, messages) - expect(mockCreate).toHaveBeenCalledWith( - expect.objectContaining({ - temperature: 0.7, - }), - ) + const stream = handlerWithTemp.createMessage(systemPrompt, messages) + await consumeStream(stream) + + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs.temperature).toBe(0.7) }) it("should transform messages correctly", async () => { @@ -201,25 +248,27 @@ describe("MistralHandler", () => { content: [{ type: "text", text: "I'm doing well!" }], }, ] - await handler.createMessage(systemPrompt, complexMessages) - expect(mockCreate).toHaveBeenCalledWith( - expect.objectContaining({ - messages: expect.arrayContaining([ - expect.objectContaining({ - role: "system", - content: systemPrompt, - }), - expect.objectContaining({ - role: "user", - content: "Hello! How are you?", - }), - expect.objectContaining({ - role: "assistant", - content: "I'm doing well!", - }), - ]), - }), - ) + const stream = handler.createMessage(systemPrompt, complexMessages) + await consumeStream(stream) + + const callArgs = mockCreate.mock.calls[0][0] + expect(callArgs.messages).toEqual([ + { + role: "system", + content: systemPrompt, + }, + { + role: "user", + content: [ + { type: "text", text: "Hello!" }, + { type: "text", text: "How are you?" }, + ], + }, + { + role: "assistant", + content: "I'm doing well!", + }, + ]) }) }) }) From 2e76b0a01f78a7449c82de286f9cae5eabca0bff Mon Sep 17 00:00:00 2001 From: Dominik Oswald <6849456+d-oit@users.noreply.github.com> Date: Mon, 24 Feb 2025 22:54:32 +0100 Subject: [PATCH 09/14] feat: implement caching for Mistral model to improve performance --- src/api/providers/mistral.ts | 59 ++++++++++++++++++++++++++++----- webview-ui/tsconfig.tsbuildinfo | 1 + 2 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 webview-ui/tsconfig.tsbuildinfo diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index a2615d88c3..442356e1bb 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -13,6 +13,7 @@ export class MistralHandler implements ApiHandler { private client: Mistral private readonly enableDebugOutput: boolean private readonly outputChannel?: vscode.OutputChannel + private cachedModel: { id: MistralModelId; info: ModelInfo; forModelId: string | undefined } | null = null private static readonly outputChannelName = "Roo Code Mistral" private static sharedOutputChannel: vscode.OutputChannel | undefined @@ -22,6 +23,30 @@ export class MistralHandler implements ApiHandler { throw new Error("Mistral API key is required") } + // Clear cached model if options change + this.cachedModel = null + + // Destructure only the options we need + const { + apiModelId, + mistralApiKey, + mistralCodestralUrl, + mistralModelStreamingEnabled, + modelTemperature, + stopToken, + includeMaxTokens, + } = options + + this.options = { + apiModelId: apiModelId || mistralDefaultModelId, + mistralApiKey, + mistralCodestralUrl, + mistralModelStreamingEnabled, + modelTemperature, + stopToken, + includeMaxTokens, + } + const config = vscode.workspace.getConfiguration("roo-cline") this.enableDebugOutput = config.get("debug.mistral", false) @@ -32,13 +57,7 @@ export class MistralHandler implements ApiHandler { this.outputChannel = MistralHandler.sharedOutputChannel } - // Set default model ID if not provided - this.options = { - ...options, - apiModelId: options.apiModelId || mistralDefaultModelId, - } - - this.logDebug(`Initializing MistralHandler with options: ${JSON.stringify(options, null, 2)}`) + this.logDebug(`Initializing MistralHandler with options: ${JSON.stringify(this.options, null, 2)}`) const baseUrl = this.getBaseUrl() this.logDebug(`MistralHandler using baseUrl: ${baseUrl}`) @@ -131,16 +150,38 @@ export class MistralHandler implements ApiHandler { } getModel(): { id: MistralModelId; info: ModelInfo } { + // Check if cache exists and is for the current model + if (this.cachedModel && this.cachedModel.forModelId === this.options.apiModelId) { + return { + id: this.cachedModel.id, + info: this.cachedModel.info, + } + } + const modelId = this.options.apiModelId if (modelId && modelId in mistralModels) { const id = modelId as MistralModelId this.logDebug(`Using model: ${id}`) - return { id, info: mistralModels[id] } + this.cachedModel = { + id, + info: mistralModels[id], + forModelId: modelId, + } + return { + id: this.cachedModel.id, + info: this.cachedModel.info, + } } + this.logDebug(`Using default model: ${mistralDefaultModelId}`) - return { + this.cachedModel = { id: mistralDefaultModelId, info: mistralModels[mistralDefaultModelId], + forModelId: undefined, + } + return { + id: this.cachedModel.id, + info: this.cachedModel.info, } } diff --git a/webview-ui/tsconfig.tsbuildinfo b/webview-ui/tsconfig.tsbuildinfo new file mode 100644 index 0000000000..c2407decf5 --- /dev/null +++ b/webview-ui/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./src/app.tsx","./src/index.tsx","./src/setuptests.ts","./src/vite-env.d.ts","./src/__mocks__/pretty-bytes.js","./src/__mocks__/vscrui.ts","./src/__mocks__/@vscode/webview-ui-toolkit/react.ts","./src/__tests__/app.test.tsx","./src/components/chat/announcement.tsx","./src/components/chat/autoapprovemenu.tsx","./src/components/chat/browsersessionrow.tsx","./src/components/chat/chatrow.tsx","./src/components/chat/chattextarea.tsx","./src/components/chat/chatview.tsx","./src/components/chat/contextmenu.tsx","./src/components/chat/reasoningblock.tsx","./src/components/chat/taskheader.tsx","./src/components/chat/__tests__/chattextarea.test.tsx","./src/components/chat/__tests__/chatview.auto-approve.test.tsx","./src/components/chat/__tests__/chatview.test.tsx","./src/components/chat/checkpoints/checkpointmenu.tsx","./src/components/chat/checkpoints/checkpointsaved.tsx","./src/components/chat/checkpoints/schema.ts","./src/components/common/careticon.tsx","./src/components/common/codeaccordian.tsx","./src/components/common/codeblock.tsx","./src/components/common/markdownblock.tsx","./src/components/common/thumbnails.tsx","./src/components/common/vscodebuttonlink.tsx","./src/components/common/__mocks__/codeblock.tsx","./src/components/common/__mocks__/markdownblock.tsx","./src/components/history/deletetaskdialog.tsx","./src/components/history/historypreview.tsx","./src/components/history/historyview.tsx","./src/components/history/__tests__/historyview.test.tsx","./src/components/mcp/mcpenabledtoggle.tsx","./src/components/mcp/mcpresourcerow.tsx","./src/components/mcp/mcptoolrow.tsx","./src/components/mcp/mcpview.tsx","./src/components/mcp/__tests__/mcptoolrow.test.tsx","./src/components/prompts/promptsview.tsx","./src/components/prompts/__tests__/promptsview.test.tsx","./src/components/settings/apiconfigmanager.tsx","./src/components/settings/apioptions.tsx","./src/components/settings/experimentalfeature.tsx","./src/components/settings/glamamodelpicker.tsx","./src/components/settings/modeldescriptionmarkdown.tsx","./src/components/settings/modelinfoview.tsx","./src/components/settings/modelpicker.tsx","./src/components/settings/openaimodelpicker.tsx","./src/components/settings/openroutermodelpicker.tsx","./src/components/settings/requestymodelpicker.tsx","./src/components/settings/settingsview.tsx","./src/components/settings/temperaturecontrol.tsx","./src/components/settings/unboundmodelpicker.tsx","./src/components/settings/styles.ts","./src/components/settings/__tests__/apiconfigmanager.test.tsx","./src/components/settings/__tests__/apioptions.test.tsx","./src/components/settings/__tests__/modelpicker.test.tsx","./src/components/settings/__tests__/settingsview.test.tsx","./src/components/settings/__tests__/temperaturecontrol.test.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/autosize-textarea.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/collapsible.tsx","./src/components/ui/command.tsx","./src/components/ui/dialog.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/index.ts","./src/components/ui/input.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/separator.tsx","./src/components/ui/slider.tsx","./src/components/ui/textarea.tsx","./src/components/ui/tooltip.tsx","./src/components/ui/chat/chat.tsx","./src/components/ui/chat/chatinput.tsx","./src/components/ui/chat/chatinputprovider.ts","./src/components/ui/chat/chatmessage.tsx","./src/components/ui/chat/chatmessageprovider.ts","./src/components/ui/chat/chatmessages.tsx","./src/components/ui/chat/chatprovider.ts","./src/components/ui/chat/index.ts","./src/components/ui/chat/types.ts","./src/components/ui/chat/usechatinput.ts","./src/components/ui/chat/usechatmessage.ts","./src/components/ui/chat/usechatui.ts","./src/components/ui/hooks/index.ts","./src/components/ui/hooks/useclipboard.ts","./src/components/ui/markdown/blockquote.tsx","./src/components/ui/markdown/codeblock.tsx","./src/components/ui/markdown/markdown.tsx","./src/components/ui/markdown/index.ts","./src/components/welcome/welcomeview.tsx","./src/context/extensionstatecontext.tsx","./src/context/__tests__/extensionstatecontext.test.tsx","./src/lib/utils.ts","./src/stories/autosizetextarea.stories.tsx","./src/stories/badge.stories.tsx","./src/stories/button.stories.ts","./src/stories/chat.stories.tsx","./src/stories/collapsible.stories.tsx","./src/stories/combobox.stories.tsx","./src/stories/dropdownmenu.stories.tsx","./src/stories/progress.stories.tsx","./src/stories/slider.stories.tsx","./src/utils/clipboard.ts","./src/utils/command-validation.ts","./src/utils/context-mentions.ts","./src/utils/format.ts","./src/utils/formatprice.ts","./src/utils/getlanguagefrompath.ts","./src/utils/highlight.ts","./src/utils/mcp.ts","./src/utils/textmatetohljs.ts","./src/utils/validate.ts","./src/utils/vscode.ts","./src/utils/__tests__/command-validation.test.ts","./src/utils/__tests__/context-mentions.test.ts","../src/shared/extensionmessage.ts","../src/shared/historyitem.ts","../src/shared/webviewmessage.ts","../src/shared/api.ts","../src/shared/array.ts","../src/shared/checkexistapiconfig.ts","../src/shared/combineapirequests.ts","../src/shared/combinecommandsequences.ts","../src/shared/context-mentions.ts","../src/shared/experiments.ts","../src/shared/getapimetrics.ts","../src/shared/mcp.ts","../src/shared/modes.ts","../src/shared/support-prompt.ts","../src/shared/tool-groups.ts","../src/shared/vscodeselectorutils.ts","../src/shared/__tests__/checkexistapiconfig.test.ts","../src/shared/__tests__/experiments.test.ts","../src/shared/__tests__/modes.test.ts","../src/shared/__tests__/support-prompts.test.ts","../src/shared/__tests__/vscodeselectorutils.test.ts"],"errors":true,"version":"5.7.2"} \ No newline at end of file From 131a9b0406bfe67b6a2721b8f5e330afde8ab856 Mon Sep 17 00:00:00 2001 From: Dominik Oswald <6849456+d-oit@users.noreply.github.com> Date: Tue, 25 Feb 2025 12:32:59 +0100 Subject: [PATCH 10/14] Update webview-ui/src/components/settings/ApiOptions.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- webview-ui/src/components/settings/ApiOptions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 06ddc6d38a..756e310ecf 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -363,7 +363,7 @@ const ApiOptions = ({ style={{ width: "100%", marginTop: "10px" }} onInput={handleInputChange("stopToken")} placeholder="Enter stop token (optional)"> - Optional: Stop Token e.g. \n\\n\ + Optional: Stop Token e.g. \n\n

Date: Tue, 25 Feb 2025 12:33:53 +0100 Subject: [PATCH 11/14] Update src/api/providers/mistral.ts Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- src/api/providers/mistral.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 442356e1bb..b0d4e33867 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -205,7 +205,7 @@ export class MistralHandler implements ApiHandler { } catch (error) { if (error instanceof Error) { this.logDebug(`Completion error: ${error.message}`) - throw new Error(`Mistral completion error: ${error.message}`) + throw new Error(`Mistral completion error: ${error.message}`, { cause: error }) } throw error } From 0c9d8a4ed4e58d1d025c0c4ccba87e92782e2410 Mon Sep 17 00:00:00 2001 From: Dominik Oswald <6849456+d-oit@users.noreply.github.com> Date: Tue, 25 Feb 2025 12:34:06 +0100 Subject: [PATCH 12/14] Update src/api/providers/__tests__/mistral.test.ts Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- src/api/providers/__tests__/mistral.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/providers/__tests__/mistral.test.ts b/src/api/providers/__tests__/mistral.test.ts index 3fbd9002eb..c56373f3a0 100644 --- a/src/api/providers/__tests__/mistral.test.ts +++ b/src/api/providers/__tests__/mistral.test.ts @@ -150,7 +150,7 @@ describe("MistralHandler", () => { await consumeStream(stream) const callArgs = mockCreate.mock.calls[0][0] - expect(callArgs.model).toBe("codestral-latest") + expect(callArgs.model).toBe(mistralDefaultModelId) expect(callArgs.maxTokens).toBe(256000) expect(callArgs.temperature).toBe(0) expect(callArgs.stream).toBe(true) From 326722d9519b576d5e1831adedfef9958d8ce183 Mon Sep 17 00:00:00 2001 From: Dominik Oswald <6849456+d-oit@users.noreply.github.com> Date: Tue, 25 Feb 2025 13:28:05 +0100 Subject: [PATCH 13/14] Delete webview-ui/tsconfig.tsbuildinfo --- webview-ui/tsconfig.tsbuildinfo | 1 - 1 file changed, 1 deletion(-) delete mode 100644 webview-ui/tsconfig.tsbuildinfo diff --git a/webview-ui/tsconfig.tsbuildinfo b/webview-ui/tsconfig.tsbuildinfo deleted file mode 100644 index c2407decf5..0000000000 --- a/webview-ui/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"root":["./src/app.tsx","./src/index.tsx","./src/setuptests.ts","./src/vite-env.d.ts","./src/__mocks__/pretty-bytes.js","./src/__mocks__/vscrui.ts","./src/__mocks__/@vscode/webview-ui-toolkit/react.ts","./src/__tests__/app.test.tsx","./src/components/chat/announcement.tsx","./src/components/chat/autoapprovemenu.tsx","./src/components/chat/browsersessionrow.tsx","./src/components/chat/chatrow.tsx","./src/components/chat/chattextarea.tsx","./src/components/chat/chatview.tsx","./src/components/chat/contextmenu.tsx","./src/components/chat/reasoningblock.tsx","./src/components/chat/taskheader.tsx","./src/components/chat/__tests__/chattextarea.test.tsx","./src/components/chat/__tests__/chatview.auto-approve.test.tsx","./src/components/chat/__tests__/chatview.test.tsx","./src/components/chat/checkpoints/checkpointmenu.tsx","./src/components/chat/checkpoints/checkpointsaved.tsx","./src/components/chat/checkpoints/schema.ts","./src/components/common/careticon.tsx","./src/components/common/codeaccordian.tsx","./src/components/common/codeblock.tsx","./src/components/common/markdownblock.tsx","./src/components/common/thumbnails.tsx","./src/components/common/vscodebuttonlink.tsx","./src/components/common/__mocks__/codeblock.tsx","./src/components/common/__mocks__/markdownblock.tsx","./src/components/history/deletetaskdialog.tsx","./src/components/history/historypreview.tsx","./src/components/history/historyview.tsx","./src/components/history/__tests__/historyview.test.tsx","./src/components/mcp/mcpenabledtoggle.tsx","./src/components/mcp/mcpresourcerow.tsx","./src/components/mcp/mcptoolrow.tsx","./src/components/mcp/mcpview.tsx","./src/components/mcp/__tests__/mcptoolrow.test.tsx","./src/components/prompts/promptsview.tsx","./src/components/prompts/__tests__/promptsview.test.tsx","./src/components/settings/apiconfigmanager.tsx","./src/components/settings/apioptions.tsx","./src/components/settings/experimentalfeature.tsx","./src/components/settings/glamamodelpicker.tsx","./src/components/settings/modeldescriptionmarkdown.tsx","./src/components/settings/modelinfoview.tsx","./src/components/settings/modelpicker.tsx","./src/components/settings/openaimodelpicker.tsx","./src/components/settings/openroutermodelpicker.tsx","./src/components/settings/requestymodelpicker.tsx","./src/components/settings/settingsview.tsx","./src/components/settings/temperaturecontrol.tsx","./src/components/settings/unboundmodelpicker.tsx","./src/components/settings/styles.ts","./src/components/settings/__tests__/apiconfigmanager.test.tsx","./src/components/settings/__tests__/apioptions.test.tsx","./src/components/settings/__tests__/modelpicker.test.tsx","./src/components/settings/__tests__/settingsview.test.tsx","./src/components/settings/__tests__/temperaturecontrol.test.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/autosize-textarea.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/collapsible.tsx","./src/components/ui/command.tsx","./src/components/ui/dialog.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/index.ts","./src/components/ui/input.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/separator.tsx","./src/components/ui/slider.tsx","./src/components/ui/textarea.tsx","./src/components/ui/tooltip.tsx","./src/components/ui/chat/chat.tsx","./src/components/ui/chat/chatinput.tsx","./src/components/ui/chat/chatinputprovider.ts","./src/components/ui/chat/chatmessage.tsx","./src/components/ui/chat/chatmessageprovider.ts","./src/components/ui/chat/chatmessages.tsx","./src/components/ui/chat/chatprovider.ts","./src/components/ui/chat/index.ts","./src/components/ui/chat/types.ts","./src/components/ui/chat/usechatinput.ts","./src/components/ui/chat/usechatmessage.ts","./src/components/ui/chat/usechatui.ts","./src/components/ui/hooks/index.ts","./src/components/ui/hooks/useclipboard.ts","./src/components/ui/markdown/blockquote.tsx","./src/components/ui/markdown/codeblock.tsx","./src/components/ui/markdown/markdown.tsx","./src/components/ui/markdown/index.ts","./src/components/welcome/welcomeview.tsx","./src/context/extensionstatecontext.tsx","./src/context/__tests__/extensionstatecontext.test.tsx","./src/lib/utils.ts","./src/stories/autosizetextarea.stories.tsx","./src/stories/badge.stories.tsx","./src/stories/button.stories.ts","./src/stories/chat.stories.tsx","./src/stories/collapsible.stories.tsx","./src/stories/combobox.stories.tsx","./src/stories/dropdownmenu.stories.tsx","./src/stories/progress.stories.tsx","./src/stories/slider.stories.tsx","./src/utils/clipboard.ts","./src/utils/command-validation.ts","./src/utils/context-mentions.ts","./src/utils/format.ts","./src/utils/formatprice.ts","./src/utils/getlanguagefrompath.ts","./src/utils/highlight.ts","./src/utils/mcp.ts","./src/utils/textmatetohljs.ts","./src/utils/validate.ts","./src/utils/vscode.ts","./src/utils/__tests__/command-validation.test.ts","./src/utils/__tests__/context-mentions.test.ts","../src/shared/extensionmessage.ts","../src/shared/historyitem.ts","../src/shared/webviewmessage.ts","../src/shared/api.ts","../src/shared/array.ts","../src/shared/checkexistapiconfig.ts","../src/shared/combineapirequests.ts","../src/shared/combinecommandsequences.ts","../src/shared/context-mentions.ts","../src/shared/experiments.ts","../src/shared/getapimetrics.ts","../src/shared/mcp.ts","../src/shared/modes.ts","../src/shared/support-prompt.ts","../src/shared/tool-groups.ts","../src/shared/vscodeselectorutils.ts","../src/shared/__tests__/checkexistapiconfig.test.ts","../src/shared/__tests__/experiments.test.ts","../src/shared/__tests__/modes.test.ts","../src/shared/__tests__/support-prompts.test.ts","../src/shared/__tests__/vscodeselectorutils.test.ts"],"errors":true,"version":"5.7.2"} \ No newline at end of file From 1a3388621960955feb68f1fbbe7c37e3259c7c15 Mon Sep 17 00:00:00 2001 From: Dominik Oswald <6849456+d-oit@users.noreply.github.com> Date: Tue, 25 Feb 2025 15:32:24 +0100 Subject: [PATCH 14/14] chore: add tsconfig.tsbuildinfo to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 211d06aa19..76a793a825 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ coverage/ # Builds bin/ roo-cline-*.vsix +tsconfig.tsbuildinfo # Local prompts and rules /local-prompts