mirror of
https://github.com/aljazceru/mentor-mcp-server.git
synced 2025-12-17 05:54:26 +01:00
feat: upgrade to deepseek-reasoner with Chain of Thought
- Switch to deepseek-reasoner model for improved analysis - Add Chain of Thought (CoT) reasoning capabilities - Update types to support reasoning_content in responses - Update second-opinion tool to showcase CoT reasoning - Add max_tokens configuration for response length control - Update documentation and examples The deepseek-reasoner model provides step-by-step reasoning before generating final responses, improving the quality and transparency of the analysis. The reasoning is now exposed in tool responses under the 'reasoning' field.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
# Deepseek API Configuration
|
||||
DEEPSEEK_API_KEY=your_deepseek_api_key_here
|
||||
DEEPSEEK_API_BASE_URL=https://api.deepseek.com
|
||||
DEEPSEEK_MODEL=deepseek-chat
|
||||
|
||||
# Deepseek Reasoner Settings
|
||||
# Maximum length of the final response after Chain of Thought (4K-8K)
|
||||
DEEPSEEK_MAX_TOKENS=4096
|
||||
DEEPSEEK_MAX_RETRIES=3
|
||||
DEEPSEEK_TIMEOUT=30000
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import OpenAI from 'openai';
|
||||
import type { LLMResponse } from '../../types/index.js';
|
||||
import type { LLMResponse, ChatMessage } from '../../types/index.js';
|
||||
import { config } from '../../config.js';
|
||||
import { sanitizeInput } from '../../utils/prompt.js';
|
||||
|
||||
@@ -42,7 +42,7 @@ class RateLimiter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deepseek API client class using OpenAI SDK
|
||||
* Deepseek API client class using OpenAI SDK with deepseek-reasoner model
|
||||
*/
|
||||
class DeepseekClient {
|
||||
private readonly client: OpenAI;
|
||||
@@ -52,8 +52,7 @@ class DeepseekClient {
|
||||
this.client = new OpenAI({
|
||||
baseURL: config.api.baseUrl || 'https://api.deepseek.com',
|
||||
apiKey: config.api.apiKey,
|
||||
defaultQuery: { model: config.api.model || 'deepseek-chat' },
|
||||
defaultHeaders: { 'api-key': config.api.apiKey }
|
||||
defaultQuery: { model: 'deepseek-reasoner' }
|
||||
});
|
||||
|
||||
this.rateLimiter = new RateLimiter(
|
||||
@@ -90,11 +89,12 @@ class DeepseekClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a call to the Deepseek API
|
||||
* Makes a call to the Deepseek API using the reasoner model
|
||||
*/
|
||||
public async makeApiCall(
|
||||
prompt: string,
|
||||
systemPrompt: string = 'You are a helpful AI assistant.'
|
||||
systemPrompt: string = 'You are a helpful AI assistant.',
|
||||
previousMessages: ChatMessage[] = []
|
||||
): Promise<LLMResponse> {
|
||||
// Check rate limit
|
||||
if (!this.rateLimiter.tryConsume()) {
|
||||
@@ -110,22 +110,33 @@ class DeepseekClient {
|
||||
const sanitizedPrompt = sanitizeInput(prompt);
|
||||
const sanitizedSystemPrompt = sanitizeInput(systemPrompt);
|
||||
|
||||
// Prepare messages, filtering out any previous reasoning_content
|
||||
const messages = [
|
||||
{ role: 'system' as const, content: sanitizedSystemPrompt },
|
||||
...previousMessages.map(msg => ({
|
||||
role: msg.role,
|
||||
content: msg.content
|
||||
})),
|
||||
{ role: 'user' as const, content: sanitizedPrompt }
|
||||
];
|
||||
|
||||
const response = await this.retryWithExponentialBackoff(async () => {
|
||||
const completion = await this.client.chat.completions.create({
|
||||
messages: [
|
||||
{ role: 'system', content: sanitizedSystemPrompt },
|
||||
{ role: 'user', content: sanitizedPrompt }
|
||||
],
|
||||
model: config.api.model || 'deepseek-chat',
|
||||
temperature: 0.7,
|
||||
max_tokens: 2048
|
||||
model: 'deepseek-reasoner',
|
||||
messages,
|
||||
max_tokens: config.api.maxTokens || 4096
|
||||
});
|
||||
|
||||
return completion;
|
||||
});
|
||||
|
||||
// Extract both the reasoning and final content
|
||||
const reasoningContent = (response.choices[0]?.message as any)?.reasoning_content || '';
|
||||
const finalContent = response.choices[0]?.message?.content || '';
|
||||
|
||||
return {
|
||||
text: response.choices[0]?.message?.content || '',
|
||||
text: finalContent,
|
||||
reasoning: reasoningContent,
|
||||
isError: false,
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -158,7 +169,10 @@ class DeepseekClient {
|
||||
export const deepseekClient = new DeepseekClient();
|
||||
|
||||
// Export the main interface functions
|
||||
export const makeDeepseekAPICall = (prompt: string, systemPrompt?: string) =>
|
||||
deepseekClient.makeApiCall(prompt, systemPrompt);
|
||||
export const makeDeepseekAPICall = (
|
||||
prompt: string,
|
||||
systemPrompt?: string,
|
||||
previousMessages: ChatMessage[] = []
|
||||
) => deepseekClient.makeApiCall(prompt, systemPrompt, previousMessages);
|
||||
|
||||
export const checkRateLimit = () => deepseekClient.checkRateLimit();
|
||||
@@ -15,9 +15,10 @@ function requireEnv(name: string): string {
|
||||
const apiConfig: APIConfig = {
|
||||
apiKey: requireEnv('DEEPSEEK_API_KEY'),
|
||||
baseUrl: process.env.DEEPSEEK_API_BASE_URL || 'https://api.deepseek.com',
|
||||
model: process.env.DEEPSEEK_MODEL || 'deepseek-chat',
|
||||
model: 'deepseek-reasoner', // Always use the reasoner model
|
||||
maxRetries: parseInt(process.env.DEEPSEEK_MAX_RETRIES || '3', 10),
|
||||
timeout: parseInt(process.env.DEEPSEEK_TIMEOUT || '30000', 10),
|
||||
maxTokens: parseInt(process.env.DEEPSEEK_MAX_TOKENS || '4096', 10), // Default to 4K tokens for final response
|
||||
};
|
||||
|
||||
export const config: ServerConfig = {
|
||||
|
||||
@@ -6,11 +6,19 @@ import { createPrompt, PromptTemplate } from '../../utils/prompt.js';
|
||||
* System prompt for the second opinion tool
|
||||
*/
|
||||
const SYSTEM_PROMPT = `You are an expert mentor providing second opinions on user requests.
|
||||
Your role is to analyze requests and identify critical considerations that might be overlooked.
|
||||
Focus on modern practices, potential pitfalls, and important factors for success.
|
||||
Your role is to analyze requests and identify critical considerations that might be overlooked.
|
||||
|
||||
Format your response as a clear, non-numbered list of points, focusing on what's most relevant
|
||||
to the specific request. Each point should be concise but informative.`;
|
||||
First, reason through the request step by step:
|
||||
1. Understand the core request and its implications
|
||||
2. Consider the context and domain
|
||||
3. Identify potential challenges and pitfalls
|
||||
4. Think about prerequisites and dependencies
|
||||
5. Evaluate resource requirements
|
||||
6. Consider maintenance and scalability
|
||||
7. Think about security and performance implications
|
||||
|
||||
Then, provide a concise list of critical considerations based on your reasoning.
|
||||
Focus on modern practices, potential pitfalls, and important factors for success.`;
|
||||
|
||||
/**
|
||||
* Prompt template for generating second opinions
|
||||
@@ -18,18 +26,17 @@ to the specific request. Each point should be concise but informative.`;
|
||||
const PROMPT_TEMPLATE: PromptTemplate = {
|
||||
template: `User Request: {user_request}
|
||||
|
||||
Task: List the critical considerations for this user request:
|
||||
Please analyze this request carefully. Consider:
|
||||
- Core problem/concept to address
|
||||
- Common pitfalls or edge cases
|
||||
- Security/performance implications (if applicable)
|
||||
- Prerequisites or dependencies
|
||||
- Resource constraints and requirements to consider
|
||||
- Prerequisites and dependencies
|
||||
- Resource constraints and requirements
|
||||
- Advanced topics that could add value
|
||||
- Maintenance/scalability factors
|
||||
|
||||
Reminder: You are not fulfilling the user request, only generating a plain text, non-numbered list of non-obvious points of consideration.
|
||||
|
||||
Format: Brief, clear points in plain text. Focus on what's most relevant to the specific request.`,
|
||||
First, reason through your analysis step by step.
|
||||
Then, provide a clear, non-numbered list of critical considerations.`,
|
||||
systemPrompt: SYSTEM_PROMPT
|
||||
};
|
||||
|
||||
@@ -55,9 +62,9 @@ export const definition: ToolDefinition = {
|
||||
* Handles the execution of the second opinion tool
|
||||
*
|
||||
* @param args - Tool arguments containing the user request
|
||||
* @returns Tool response containing the generated second opinion
|
||||
* @returns Tool response containing the generated second opinion with reasoning
|
||||
*/
|
||||
export async function handler(args: SecondOpinionArgs) {
|
||||
export async function handler(args: unknown) {
|
||||
// Check rate limit first
|
||||
if (!checkRateLimit()) {
|
||||
return {
|
||||
@@ -67,13 +74,30 @@ export async function handler(args: SecondOpinionArgs) {
|
||||
text: 'Rate limit exceeded. Please try again later.',
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// Create the complete prompt using the template
|
||||
// Type guard for SecondOpinionArgs
|
||||
if (!args || typeof args !== 'object' || !('user_request' in args) ||
|
||||
typeof args.user_request !== 'string') {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Missing or invalid user_request parameter.',
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
const typedArgs = args as SecondOpinionArgs;
|
||||
|
||||
// Create the complete prompt
|
||||
const prompt = createPrompt(PROMPT_TEMPLATE, {
|
||||
user_request: args.user_request
|
||||
user_request: typedArgs.user_request
|
||||
});
|
||||
|
||||
// Make the API call
|
||||
@@ -87,17 +111,27 @@ export async function handler(args: SecondOpinionArgs) {
|
||||
text: `Error generating second opinion: ${response.errorMessage || 'Unknown error'}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
// Format the response
|
||||
// Return both the reasoning and the final response
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `<internal_thoughts>\n${response.text}\n</internal_thoughts>`,
|
||||
text: response.text,
|
||||
},
|
||||
],
|
||||
// Include the Chain of Thought reasoning if available
|
||||
...(response.reasoning ? {
|
||||
reasoning: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `<reasoning>\n${response.reasoning}\n</reasoning>`,
|
||||
},
|
||||
],
|
||||
} : {}),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Second opinion tool error:', error);
|
||||
@@ -108,6 +142,7 @@ export async function handler(args: SecondOpinionArgs) {
|
||||
text: `Error processing request: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,13 @@ export interface ToolContent {
|
||||
|
||||
export interface ToolResponse {
|
||||
content: ToolContent[];
|
||||
reasoning?: ToolContent[]; // Added to expose reasoning content
|
||||
isError?: boolean;
|
||||
}
|
||||
|
||||
export interface LLMResponse {
|
||||
text: string;
|
||||
reasoning?: string; // Added for Chain of Thought content
|
||||
isError: boolean;
|
||||
errorMessage?: string;
|
||||
}
|
||||
@@ -38,6 +40,7 @@ export interface APIConfig {
|
||||
model: string;
|
||||
maxRetries: number;
|
||||
timeout: number;
|
||||
maxTokens?: number; // Added for deepseek-reasoner max_tokens parameter
|
||||
}
|
||||
|
||||
export interface ServerConfig {
|
||||
@@ -111,4 +114,14 @@ export function isBrainstormEnhancementsArgs(args: unknown): args is BrainstormE
|
||||
const a = args as Record<string, unknown>;
|
||||
|
||||
return 'concept' in a && typeof a.concept === 'string';
|
||||
}
|
||||
|
||||
// Message types for OpenAI/Deepseek chat
|
||||
export interface ChatMessage {
|
||||
role: 'system' | 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ChatHistory {
|
||||
messages: ChatMessage[];
|
||||
}
|
||||
Reference in New Issue
Block a user