voyage-and-consumption-mcp-server
Version:
Voyage and consumption management server handling vessel position tracking, ETA monitoring, fuel consumption, lube oil consumption, fresh water production and weather data
266 lines • 9.16 kB
JavaScript
import OpenAI from "openai";
import { logger } from "./logger.js";
import { config } from './config.js';
import axios from 'axios';
export class LLMClient {
constructor(apiKey) {
this.client = new OpenAI({ apiKey });
}
async ask(request) {
try {
const response = await this.client.chat.completions.create({
model: request.modelName,
messages: [
{ role: "system", content: request.systemPrompt },
{ role: "user", content: request.query }
],
response_format: request.jsonMode ? { type: "json_object" } : undefined,
temperature: request.temperature || 0
});
const content = response.choices[0]?.message?.content;
if (!content) {
throw new Error("No content in LLM response");
}
if (request.jsonMode) {
return JSON.parse(content);
}
return {
content,
topic: "",
flag: "",
importance: "medium"
};
}
catch (error) {
logger.error("Error in LLM request:", error);
throw error;
}
}
}
export async function parseDocumentToMarkdown(params) {
try {
const { file_path, json_output_path, md_output_path, parsing_instruction, api_key, vendor_model } = params;
// Read the file content
const fs = require('fs');
const fileContent = await fs.promises.readFile(file_path);
// Prepare the request to LlamaParse API
const response = await axios.post('https://api.llamaparse.com/v1/parse', {
file: fileContent,
output_format: 'markdown',
parsing_instruction: parsing_instruction || '',
model: vendor_model || 'default'
}, {
headers: {
'Authorization': `Bearer ${api_key}`,
'Content-Type': 'multipart/form-data'
}
});
if (response.status === 200 && response.data) {
// Save the markdown output
await fs.promises.writeFile(md_output_path, response.data.markdown);
// Save the JSON output if available
if (response.data.json) {
await fs.promises.writeFile(json_output_path, JSON.stringify(response.data.json, null, 2));
}
return true;
}
return false;
}
catch (error) {
logger.error('Error in parseDocumentToMarkdown:', error);
return false;
}
}
export async function extractMarkdownFromJson(jsonPath, mdOutputPath) {
try {
const fs = require('fs');
const jsonContent = await fs.promises.readFile(jsonPath, 'utf-8');
const jsonData = JSON.parse(jsonContent);
// Extract markdown content from JSON
// This is a basic implementation - adjust based on your JSON structure
const markdown = jsonData.markdown || jsonData.content || '';
// Save the markdown output
await fs.promises.writeFile(mdOutputPath, markdown);
return markdown;
}
catch (error) {
logger.error('Error in extractMarkdownFromJson:', error);
return null;
}
}
export async function callLLM(prompt, apiKey, model = 'default') {
try {
const response = await axios.post('https://api.llamaparse.com/v1/chat', {
prompt,
model
}, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
if (response.status === 200 && response.data) {
return {
content: response.data.content,
topic: "",
flag: "",
importance: "medium"
};
}
return {
content: "",
topic: "",
flag: "",
importance: "medium"
};
}
catch (error) {
logger.error('Error in callLLM:', error);
throw error;
}
}
// Preserve existing functionality
export async function getLLMResponse(prompt) {
try {
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const completion = await openai.chat.completions.create({
messages: [{ role: "user", content: prompt }],
model: "gpt-4",
});
const content = completion.choices[0]?.message?.content || "";
return {
content,
topic: "",
flag: "",
importance: "medium"
};
}
catch (error) {
logger.error("Error in getLLMResponse:", error);
return {
content: "",
topic: "",
flag: "",
importance: "medium"
};
}
}
export class EnhancedLLMClient {
constructor(apiKey) {
const key = apiKey || config().openai?.apiKey || process.env.OPENAI_API_KEY;
if (!key) {
throw new Error('OpenAI API key is required');
}
this.openai = new OpenAI({
apiKey: key
});
logger.info('Enhanced LLM client initialized successfully');
}
async ask(query, systemPrompt, modelName = 'gpt-4o', jsonMode = false, temperature = 0.1, maxTokens) {
try {
const messages = [];
if (systemPrompt) {
messages.push({
role: 'system',
content: systemPrompt
});
}
messages.push({
role: 'user',
content: query
});
const requestParams = {
model: modelName,
messages,
temperature,
max_tokens: maxTokens
};
if (jsonMode) {
requestParams.response_format = { type: 'json_object' };
}
logger.debug(`Making LLM request with model: ${modelName}`);
const response = await this.openai.chat.completions.create(requestParams);
const content = response.choices[0]?.message?.content;
if (!content) {
throw new Error('No content in LLM response');
}
// Parse JSON if json_mode is enabled
if (jsonMode) {
try {
return JSON.parse(content);
}
catch (parseError) {
logger.error('Failed to parse JSON response:', parseError);
throw new Error('Invalid JSON response from LLM');
}
}
return content;
}
catch (error) {
logger.error('LLM request failed:', error);
throw error;
}
}
async chatCompletion(messages, modelName = 'gpt-4o', temperature = 0.1, maxTokens) {
try {
const requestParams = {
model: modelName,
messages,
temperature,
max_tokens: maxTokens
};
logger.debug(`Making chat completion request with model: ${modelName}`);
const response = await this.openai.chat.completions.create(requestParams);
const content = response.choices[0]?.message?.content;
if (!content) {
throw new Error('No content in chat completion response');
}
return content;
}
catch (error) {
logger.error('Chat completion request failed:', error);
throw error;
}
}
async generateEmbedding(text, model = 'text-embedding-ada-002') {
try {
logger.debug(`Generating embedding for text of length: ${text.length}`);
const response = await this.openai.embeddings.create({
model,
input: text
});
return response.data[0].embedding;
}
catch (error) {
logger.error('Embedding generation failed:', error);
throw error;
}
}
async solveCaptcha(captchaImage, prompt = "This is a captcha image containing only numbers. Please extract and return only the numbers you see in the image, nothing else.") {
try {
const messages = [
{
role: 'user',
content: [
{ type: 'text', text: prompt },
{
type: 'image_url',
image_url: {
url: `data:image/jpeg;base64,${captchaImage}`
}
}
]
}
];
const result = await this.chatCompletion(messages, 'gpt-4o-mini');
return result.trim();
}
catch (error) {
logger.error('Captcha solving failed:', error);
throw error;
}
}
}
//# sourceMappingURL=llm.js.map