Skip to content

Commit b3700ae

Browse files
authored
Merge pull request #458 from kaovilai/thiknagain
Extends #445 to other providers which many provides deepseek
2 parents 1d81229 + 566a9b1 commit b3700ae

File tree

11 files changed

+143
-34
lines changed

11 files changed

+143
-34
lines changed

src/engine/anthropic.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import axios from 'axios';
88
import chalk from 'chalk';
99
import { OpenAI } from 'openai';
1010
import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff';
11+
import { removeContentTags } from '../utils/removeContentTags';
1112
import { tokenCount } from '../utils/tokenCount';
1213
import { AiEngine, AiEngineConfig } from './Engine';
1314

@@ -54,8 +55,8 @@ export class AnthropicEngine implements AiEngine {
5455
const data = await this.client.messages.create(params);
5556

5657
const message = data?.content[0].text;
57-
58-
return message;
58+
let content = message;
59+
return removeContentTags(content, 'think');
5960
} catch (error) {
6061
const err = error as Error;
6162
outro(`${chalk.red('✖')} ${err?.message || err}`);

src/engine/azure.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import axios from 'axios';
77
import chalk from 'chalk';
88
import { OpenAI } from 'openai';
99
import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff';
10+
import { removeContentTags } from '../utils/removeContentTags';
1011
import { tokenCount } from '../utils/tokenCount';
1112
import { AiEngine, AiEngineConfig } from './Engine';
1213

@@ -52,7 +53,9 @@ export class AzureEngine implements AiEngine {
5253
if (message?.content === null) {
5354
return undefined;
5455
}
55-
return message?.content;
56+
57+
let content = message?.content;
58+
return removeContentTags(content, 'think');
5659
} catch (error) {
5760
outro(`${chalk.red('✖')} ${this.config.model}`);
5861

src/engine/deepseek.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import axios from 'axios';
22
import { OpenAI } from 'openai';
33
import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff';
4+
import { removeContentTags } from '../utils/removeContentTags';
45
import { tokenCount } from '../utils/tokenCount';
56
import { OpenAiEngine, OpenAiConfig } from './openAi';
67

@@ -41,8 +42,8 @@ export class DeepseekEngine extends OpenAiEngine {
4142
const completion = await this.client.chat.completions.create(params);
4243

4344
const message = completion.choices[0].message;
44-
45-
return message?.content;
45+
let content = message?.content;
46+
return removeContentTags(content, 'think');
4647
} catch (error) {
4748
const err = error as Error;
4849
if (

src/engine/flowise.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import axios, { AxiosInstance } from 'axios';
22
import { OpenAI } from 'openai';
3+
import { removeContentTags } from '../utils/removeContentTags';
34
import { AiEngine, AiEngineConfig } from './Engine';
45

56
interface FlowiseAiConfig extends AiEngineConfig {}
@@ -36,7 +37,8 @@ export class FlowiseEngine implements AiEngine {
3637
try {
3738
const response = await this.client.post('', payload);
3839
const message = response.data;
39-
return message?.text;
40+
let content = message?.text;
41+
return removeContentTags(content, 'think');
4042
} catch (err: any) {
4143
const message = err.response?.data?.error ?? err.message;
4244
throw new Error('local model issues. details: ' + message);

src/engine/gemini.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from '@google/generative-ai';
88
import axios from 'axios';
99
import { OpenAI } from 'openai';
10+
import { removeContentTags } from '../utils/removeContentTags';
1011
import { AiEngine, AiEngineConfig } from './Engine';
1112

1213
interface GeminiConfig extends AiEngineConfig {}
@@ -71,7 +72,8 @@ export class GeminiEngine implements AiEngine {
7172
}
7273
});
7374

74-
return result.response.text();
75+
const content = result.response.text();
76+
return removeContentTags(content, 'think');
7577
} catch (error) {
7678
const err = error as Error;
7779
if (

src/engine/mistral.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
import axios from 'axios';
2-
import { Mistral } from '@mistralai/mistralai';
32
import { OpenAI } from 'openai';
43
import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff';
4+
import { removeContentTags } from '../utils/removeContentTags';
55
import { tokenCount } from '../utils/tokenCount';
66
import { AiEngine, AiEngineConfig } from './Engine';
7-
import {
8-
AssistantMessage as MistralAssistantMessage,
9-
SystemMessage as MistralSystemMessage,
10-
ToolMessage as MistralToolMessage,
11-
UserMessage as MistralUserMessage
12-
} from '@mistralai/mistralai/models/components';
137

8+
// Using any for Mistral types to avoid type declaration issues
149
export interface MistralAiConfig extends AiEngineConfig {}
15-
export type MistralCompletionMessageParam = Array<
16-
| (MistralSystemMessage & { role: "system" })
17-
| (MistralUserMessage & { role: "user" })
18-
| (MistralAssistantMessage & { role: "assistant" })
19-
| (MistralToolMessage & { role: "tool" })
20-
>
10+
export type MistralCompletionMessageParam = Array<any>;
11+
12+
// Import Mistral dynamically to avoid TS errors
13+
// eslint-disable-next-line @typescript-eslint/no-var-requires
14+
const Mistral = require('@mistralai/mistralai').Mistral;
2115

2216
export class MistralAiEngine implements AiEngine {
2317
config: MistralAiConfig;
24-
client: Mistral;
18+
client: any; // Using any type for Mistral client to avoid TS errors
2519

2620
constructor(config: MistralAiConfig) {
2721
this.config = config;
@@ -64,7 +58,8 @@ export class MistralAiEngine implements AiEngine {
6458
if (!message || !message.content)
6559
throw Error('No completion choice available.')
6660

67-
return message.content as string;
61+
let content = message.content as string;
62+
return removeContentTags(content, 'think');
6863
} catch (error) {
6964
const err = error as Error;
7065
if (

src/engine/mlx.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import axios, { AxiosInstance } from 'axios';
22
import { OpenAI } from 'openai';
3+
import { removeContentTags } from '../utils/removeContentTags';
34
import { AiEngine, AiEngineConfig } from './Engine';
4-
import { chown } from 'fs';
55

66
interface MLXConfig extends AiEngineConfig {}
77

@@ -37,11 +37,11 @@ export class MLXEngine implements AiEngine {
3737

3838
const choices = response.data.choices;
3939
const message = choices[0].message;
40-
41-
return message?.content;
40+
let content = message?.content;
41+
return removeContentTags(content, 'think');
4242
} catch (err: any) {
4343
const message = err.response?.data?.error ?? err.message;
4444
throw new Error(`MLX provider error: ${message}`);
4545
}
4646
}
47-
}
47+
}

src/engine/ollama.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import axios, { AxiosInstance } from 'axios';
22
import { OpenAI } from 'openai';
3+
import { removeContentTags } from '../utils/removeContentTags';
34
import { AiEngine, AiEngineConfig } from './Engine';
45

56
interface OllamaConfig extends AiEngineConfig {}
@@ -35,12 +36,7 @@ export class OllamaEngine implements AiEngine {
3536

3637
const { message } = response.data;
3738
let content = message?.content;
38-
39-
if (content && content.includes('<think>')) {
40-
return content.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
41-
}
42-
43-
return content;
39+
return removeContentTags(content, 'think');
4440
} catch (err: any) {
4541
const message = err.response?.data?.error ?? err.message;
4642
throw new Error(`Ollama provider error: ${message}`);

src/engine/openAi.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import axios from 'axios';
22
import { OpenAI } from 'openai';
33
import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff';
4+
import { removeContentTags } from '../utils/removeContentTags';
45
import { tokenCount } from '../utils/tokenCount';
56
import { AiEngine, AiEngineConfig } from './Engine';
67

@@ -45,8 +46,8 @@ export class OpenAiEngine implements AiEngine {
4546
const completion = await this.client.chat.completions.create(params);
4647

4748
const message = completion.choices[0].message;
48-
49-
return message?.content;
49+
let content = message?.content;
50+
return removeContentTags(content, 'think');
5051
} catch (error) {
5152
const err = error as Error;
5253
if (

src/utils/removeContentTags.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Removes content wrapped in specified tags from a string
3+
* @param content The content string to process
4+
* @param tag The tag name without angle brackets (e.g., 'think' for '<think></think>')
5+
* @returns The content with the specified tags and their contents removed, and trimmed
6+
*/
7+
export function removeContentTags<T extends string | null | undefined>(content: T, tag: string): T {
8+
if (!content || typeof content !== 'string') {
9+
return content;
10+
}
11+
12+
// Dynamic implementation for other cases
13+
const openTag = `<${tag}>`;
14+
const closeTag = `</${tag}>`;
15+
16+
// Parse the content and remove tags
17+
let result = '';
18+
let skipUntil: number | null = null;
19+
let depth = 0;
20+
21+
for (let i = 0; i < content.length; i++) {
22+
// Check for opening tag
23+
if (content.substring(i, i + openTag.length) === openTag) {
24+
depth++;
25+
if (depth === 1) {
26+
skipUntil = content.indexOf(closeTag, i + openTag.length);
27+
i = i + openTag.length - 1; // Skip the opening tag
28+
continue;
29+
}
30+
}
31+
// Check for closing tag
32+
else if (content.substring(i, i + closeTag.length) === closeTag && depth > 0) {
33+
depth--;
34+
if (depth === 0) {
35+
i = i + closeTag.length - 1; // Skip the closing tag
36+
skipUntil = null;
37+
continue;
38+
}
39+
}
40+
41+
// Only add character if not inside a tag
42+
if (skipUntil === null) {
43+
result += content[i];
44+
}
45+
}
46+
47+
// Normalize spaces (replace multiple spaces with a single space)
48+
result = result.replace(/\s+/g, ' ').trim();
49+
50+
return result as unknown as T;
51+
}

0 commit comments

Comments
 (0)