@trishchuk/ai-think-gate-mcp
Version:
Model Context Protocol (MCP) server that provides AI-powered thinking and code architecture tools
138 lines (137 loc) • 5.12 kB
JavaScript
import { ChatOpenAI } from "@langchain/openai";
import { logService } from '../../application/services/logging-service.js';
import { DEFAULT_TEMPERATURES, LLM_API_ENDPOINTS, LLM_API_KEYS, LLM_API_MODELS, MAX_TOKENS } from '../../domain/constants.js';
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
/**
* Base client for interacting with LLM through OpenAI-compatible API
*/
export class OpenAIClient {
/**
* OpenAI client constructor
*/
constructor(toolName) {
this.model = null;
this.toolName = toolName;
this.initialize();
}
/**
* Initialize OpenAI client with appropriate keys and settings
*/
initialize() {
// Determine API key according to tool or general
const apiKey = this.toolName
? process.env[LLM_API_KEYS[this.toolName]] || process.env[LLM_API_KEYS.DEFAULT]
: process.env[LLM_API_KEYS.DEFAULT];
// Determine model according to tool or general
this.modelName = this.toolName
? process.env[LLM_API_MODELS[this.toolName]] || process.env[LLM_API_MODELS.DEFAULT]
: process.env[LLM_API_MODELS.DEFAULT];
// Determine endpoint according to tool or general
this.apiEndpoint = this.toolName
? process.env[LLM_API_ENDPOINTS[this.toolName]] || process.env[LLM_API_ENDPOINTS.DEFAULT]
: process.env[LLM_API_ENDPOINTS.DEFAULT];
// Configure default temperature according to tool
const defaultTemperature = this.toolName
? DEFAULT_TEMPERATURES[this.toolName]
: 0.5;
if (apiKey) {
try {
this.model = new ChatOpenAI({
apiKey,
model: this.modelName,
configuration: this.apiEndpoint ? {
baseURL: this.apiEndpoint
} : undefined,
temperature: defaultTemperature,
});
logService.log(`OpenAI client initialized ${this.toolName ? `for ${this.toolName}` : ''}`);
logService.debug(`Using model: ${this.modelName}, endpoint: ${this.apiEndpoint || 'default'}`);
}
catch (error) {
logService.error(`Error initializing OpenAI client: ${error}`);
this.model = null;
}
}
else {
logService.warn(`No API key provided for OpenAI ${this.toolName ? `(${this.toolName})` : ''}, client disabled`);
}
}
/**
* Process content through LLM
*/
async process(systemPrompt, userContent, options) {
if (!this.model) {
throw new Error("OpenAI client not initialized. Please provide API key.");
}
try {
logService.debug(`Request to LLM ${this.toolName ? `(${this.toolName})` : ''}: ${userContent.substring(0, 100)}...`);
const modelConfig = {};
// Apply options if provided
if (options) {
if (options.temperature !== undefined) {
modelConfig.temperature = options.temperature;
}
if (options.maxTokens !== undefined) {
modelConfig.maxTokens = options.maxTokens;
}
else if (this.toolName) {
// Apply default max tokens for the tool
modelConfig.maxTokens = MAX_TOKENS[this.toolName];
}
if (options.stopSequences) {
modelConfig.stopSequences = options.stopSequences;
}
}
// Create temporary model with needed parameters or use existing
const modelToUse = Object.keys(modelConfig).length > 0
? this.model.bind(modelConfig)
: this.model;
// Use proper LangChain message objects
const response = await modelToUse.invoke([
new SystemMessage(systemPrompt),
new HumanMessage(userContent)
]);
return typeof response.content === 'string'
? response.content
: JSON.stringify(response.content);
}
catch (error) {
logService.error(`Error in OpenAI processing: ${error}`);
throw error;
}
}
/**
* Check if client is initialized
*/
isInitialized() {
return this.model !== null;
}
/**
* Get model name being used
*/
getModelName() {
return this.modelName;
}
/**
* Get provider name
*/
getProviderName() {
return "OpenAI-compatible";
}
}
/**
* Factory for creating LLM clients according to tool
*/
export class LLMClientFactory {
/**
* Get client for specific tool
*/
static getClient(toolName) {
const key = toolName || 'default';
if (!this.instances.has(key)) {
this.instances.set(key, new OpenAIClient(toolName));
}
return this.instances.get(key);
}
}
LLMClientFactory.instances = new Map();