Skip to content

Commit fdecd03

Browse files
committed
feat: add Mistral model streaming option and debug output configuration
1 parent bfd5024 commit fdecd03

File tree

6 files changed

+183
-151
lines changed

6 files changed

+183
-151
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@
269269
}
270270
},
271271
"description": "Settings for VSCode Language Model API"
272+
},
273+
"roo-cline.enableMistralDebugOutput": {
274+
"type": "boolean",
275+
"default": false,
276+
"description": "Enable debug output channel for Mistral API interactions"
272277
}
273278
}
274279
}

src/api/providers/mistral.ts

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,49 +13,85 @@ import {
1313
} from "../../shared/api"
1414
import { convertToMistralMessages } from "../transform/mistral-format"
1515
import { ApiStream } from "../transform/stream"
16+
import * as vscode from "vscode"
1617

1718
const MISTRAL_DEFAULT_TEMPERATURE = 0
1819

1920
export class MistralHandler implements ApiHandler {
2021
private options: ApiHandlerOptions
2122
private client: Mistral
23+
private readonly enableDebugOutput: boolean
24+
private readonly outputChannel?: vscode.OutputChannel
25+
26+
private static readonly outputChannelName = "Roo Code Mistral"
27+
private static sharedOutputChannel: vscode.OutputChannel | undefined
2228

2329
constructor(options: ApiHandlerOptions) {
2430
if (!options.mistralApiKey) {
2531
throw new Error("Mistral API key is required")
2632
}
2733

34+
const config = vscode.workspace.getConfiguration("roo-cline")
35+
this.enableDebugOutput = config.get<boolean>("enableMistralDebugOutput", false)
36+
37+
if (this.enableDebugOutput) {
38+
if (!MistralHandler.sharedOutputChannel) {
39+
MistralHandler.sharedOutputChannel = vscode.window.createOutputChannel(MistralHandler.outputChannelName)
40+
}
41+
this.outputChannel = MistralHandler.sharedOutputChannel
42+
}
43+
2844
// Set default model ID if not provided
2945
this.options = {
3046
...options,
3147
apiModelId: options.apiModelId || mistralDefaultModelId,
3248
}
3349

50+
this.logDebug(`Initializing MistralHandler with options: ${JSON.stringify(options, null, 2)}`)
3451
const baseUrl = this.getBaseUrl()
35-
console.debug(`[Roo Code] MistralHandler using baseUrl: ${baseUrl}`)
52+
this.logDebug(`MistralHandler using baseUrl: ${baseUrl}`)
53+
54+
const logger = this.enableDebugOutput
55+
? {
56+
group: (message: string) => this.logDebug(`[Mistral Group] ${message}`),
57+
groupEnd: () => this.logDebug(`[Mistral GroupEnd]`),
58+
log: (...args: any[]) => this.logDebug(`[Mistral Log] ${args.join(" ")}`),
59+
}
60+
: undefined
61+
3662
this.client = new Mistral({
3763
serverURL: baseUrl,
3864
apiKey: this.options.mistralApiKey,
65+
debugLogger: logger,
3966
})
4067
}
4168

69+
private logDebug(message: string) {
70+
if (this.enableDebugOutput && this.outputChannel) {
71+
this.outputChannel.appendLine(`[Roo Code] ${message}`)
72+
}
73+
}
74+
4275
private getBaseUrl(): string {
4376
const modelId = this.options.apiModelId ?? mistralDefaultModelId
44-
console.debug(`[Roo Code] MistralHandler using modelId: ${modelId}`)
77+
this.logDebug(`MistralHandler using modelId: ${modelId}`)
4578
if (modelId?.startsWith("codestral-")) {
4679
return this.options.mistralCodestralUrl || "https://codestral.mistral.ai"
4780
}
4881
return "https://api.mistral.ai"
4982
}
5083

5184
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
85+
this.logDebug(`Creating message with system prompt: ${systemPrompt}`)
5286
const response = await this.client.chat.stream({
5387
model: this.options.apiModelId || mistralDefaultModelId,
5488
messages: [{ role: "system", content: systemPrompt }, ...convertToMistralMessages(messages)],
55-
maxTokens: this.options.includeMaxTokens ? this.getModel().info.maxTokens : undefined,
5689
temperature: this.options.modelTemperature ?? MISTRAL_DEFAULT_TEMPERATURE,
90+
stream: this.options.mistralModelStreamingEnabled,
5791
})
5892

93+
let completeContent = ""
94+
5995
for await (const chunk of response) {
6096
const delta = chunk.data.choices[0]?.delta
6197
if (delta?.content) {
@@ -65,13 +101,18 @@ export class MistralHandler implements ApiHandler {
65101
} else if (Array.isArray(delta.content)) {
66102
content = delta.content.map((c) => (c.type === "text" ? c.text : "")).join("")
67103
}
104+
completeContent += content
68105
yield {
69106
type: "text",
70107
text: content,
71108
}
72109
}
73110

74111
if (chunk.data.usage) {
112+
this.logDebug(`Complete content: ${completeContent}`)
113+
this.logDebug(
114+
`Usage - Input tokens: ${chunk.data.usage.promptTokens}, Output tokens: ${chunk.data.usage.completionTokens}`,
115+
)
75116
yield {
76117
type: "usage",
77118
inputTokens: chunk.data.usage.promptTokens || 0,
@@ -85,8 +126,10 @@ export class MistralHandler implements ApiHandler {
85126
const modelId = this.options.apiModelId
86127
if (modelId && modelId in mistralModels) {
87128
const id = modelId as MistralModelId
129+
this.logDebug(`Using model: ${id}`)
88130
return { id, info: mistralModels[id] }
89131
}
132+
this.logDebug(`Using default model: ${mistralDefaultModelId}`)
90133
return {
91134
id: mistralDefaultModelId,
92135
info: mistralModels[mistralDefaultModelId],
@@ -95,6 +138,7 @@ export class MistralHandler implements ApiHandler {
95138

96139
async completePrompt(prompt: string): Promise<string> {
97140
try {
141+
this.logDebug(`Completing prompt: ${prompt}`)
98142
const response = await this.client.chat.complete({
99143
model: this.options.apiModelId || mistralDefaultModelId,
100144
messages: [{ role: "user", content: prompt }],
@@ -103,11 +147,15 @@ export class MistralHandler implements ApiHandler {
103147

104148
const content = response.choices?.[0]?.message.content
105149
if (Array.isArray(content)) {
106-
return content.map((c) => (c.type === "text" ? c.text : "")).join("")
150+
const result = content.map((c) => (c.type === "text" ? c.text : "")).join("")
151+
this.logDebug(`Completion result: ${result}`)
152+
return result
107153
}
154+
this.logDebug(`Completion result: ${content}`)
108155
return content || ""
109156
} catch (error) {
110157
if (error instanceof Error) {
158+
this.logDebug(`Completion error: ${error.message}`)
111159
throw new Error(`Mistral completion error: ${error.message}`)
112160
}
113161
throw error

src/core/webview/ClineProvider.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ type GlobalStateKey =
128128
| "unboundModelInfo"
129129
| "modelTemperature"
130130
| "mistralCodestralUrl"
131+
| "mistralModelStreamingEnabled"
131132
| "maxOpenTabsContext"
132133

133134
export const GlobalFileNames = {
@@ -1669,6 +1670,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
16691670
vsCodeLmModelSelector,
16701671
mistralApiKey,
16711672
mistralCodestralUrl,
1673+
mistralModelStreamingEnabled,
16721674
unboundApiKey,
16731675
unboundModelId,
16741676
unboundModelInfo,
@@ -1709,6 +1711,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
17091711
this.storeSecret("deepSeekApiKey", deepSeekApiKey),
17101712
this.updateGlobalState("azureApiVersion", azureApiVersion),
17111713
this.updateGlobalState("openAiStreamingEnabled", openAiStreamingEnabled),
1714+
this.updateGlobalState("mistralModelStreamingEnabled", mistralModelStreamingEnabled),
17121715
this.updateGlobalState("openRouterModelId", openRouterModelId),
17131716
this.updateGlobalState("openRouterModelInfo", openRouterModelInfo),
17141717
this.updateGlobalState("openRouterBaseUrl", openRouterBaseUrl),
@@ -2556,6 +2559,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
25562559
deepSeekApiKey,
25572560
mistralApiKey,
25582561
mistralCodestralUrl,
2562+
mistralModelStreamingEnabled,
25592563
azureApiVersion,
25602564
openAiStreamingEnabled,
25612565
openRouterModelId,
@@ -2638,6 +2642,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
26382642
this.getSecret("deepSeekApiKey") as Promise<string | undefined>,
26392643
this.getSecret("mistralApiKey") as Promise<string | undefined>,
26402644
this.getGlobalState("mistralCodestralUrl") as Promise<string | undefined>,
2645+
this.getGlobalState("mistralModelStreamingEnabled") as Promise<boolean | undefined>,
26412646
this.getGlobalState("azureApiVersion") as Promise<string | undefined>,
26422647
this.getGlobalState("openAiStreamingEnabled") as Promise<boolean | undefined>,
26432648
this.getGlobalState("openRouterModelId") as Promise<string | undefined>,
@@ -2737,6 +2742,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
27372742
deepSeekApiKey,
27382743
mistralApiKey,
27392744
mistralCodestralUrl,
2745+
mistralModelStreamingEnabled,
27402746
azureApiVersion,
27412747
openAiStreamingEnabled,
27422748
openRouterModelId,

src/shared/api.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ export interface ApiHandlerOptions {
5252
geminiApiKey?: string
5353
openAiNativeApiKey?: string
5454
mistralApiKey?: string
55-
mistralCodestralUrl?: string // New option for Codestral URL
55+
mistralCodestralUrl?: string
56+
mistralModelStreamingEnabled?: boolean
5657
azureApiVersion?: string
5758
openRouterUseMiddleOutTransform?: boolean
5859
openAiStreamingEnabled?: boolean

0 commit comments

Comments
 (0)