This commit is contained in:
sebseb7
2026-04-04 15:13:02 +02:00
parent ab25981684
commit 27180aa2c3
4 changed files with 184 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const LOG_DIR = path.join(__dirname, '../../logs');
const LOG_FILE = path.join(LOG_DIR, 'extraction.log');
/**
* Ensures the log directory exists
*/
async function ensureLogDirectory() {
try {
await fs.mkdir(LOG_DIR, { recursive: true });
} catch (error) {
console.error('Failed to create log directory:', error);
throw error;
}
}
/**
* Formats a timestamp for logging
*/
function formatTimestamp() {
return new Date().toISOString();
}
/**
* Logs an extraction event with input and output
* @param {Object} input - The input data (search result from Exa)
* @param {string} output - The extracted text content
*/
export async function logExtraction(input, output) {
try {
await ensureLogDirectory();
const timestamp = formatTimestamp();
const logEntry = {
timestamp,
input: {
resultCount: input?.results?.length || 0,
results: input?.results?.map((r) => ({
title: r.title,
url: r.url,
textPreview: r.text?.substring(0, 200) + (r.text?.length > 200 ? '...' : ''),
summaryPreview: r.summary?.substring(0, 200) + (r.summary?.length > 200 ? '...' : ''),
highlightsCount: r.highlights?.length || 0,
publishedDate: r.publishedDate,
author: r.author,
})),
},
output: {
length: output.length,
preview: output.substring(0, 500) + (output.length > 500 ? '...' : ''),
},
};
const logLine = `${timestamp} | Input: ${JSON.stringify(logEntry.input)} | Output: [${logEntry.output.length} chars] ${logEntry.output.preview}\n`;
await fs.appendFile(LOG_FILE, logLine, 'utf8');
} catch (error) {
console.error('Failed to log extraction:', error);
// Don't throw - logging should not break the main functionality
}
}
/**
* Gets the path to the extraction log file
*/
export function getLogFilePath() {
return LOG_FILE;
}

View File

@@ -0,0 +1,102 @@
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const LOG_DIR = path.join(__dirname, '../../logs');
const LOG_FILE = path.join(LOG_DIR, 'openrouter.log');
/**
* Ensures the log directory exists
*/
async function ensureLogDirectory() {
try {
await fs.mkdir(LOG_DIR, { recursive: true });
} catch (error) {
console.error('Failed to create log directory:', error);
throw error;
}
}
/**
* Formats a timestamp for logging
*/
function formatTimestamp() {
return new Date().toISOString();
}
/**
* Safely truncates a string
*/
function truncate(str, maxLength = 1000) {
if (!str) return '';
return str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
}
/**
* Logs an OpenRouter API call with params and response
* @param {string} operation - The operation name (e.g., 'summarizeSources', 'summarizeFinalAnswer')
* @param {Object} params - The request parameters sent to OpenRouter
* @param {Object} response - The response from OpenRouter
* @param {Error} [error] - Optional error if the call failed
*/
export async function logOpenRouterCall(operation, params, response, error = null) {
try {
await ensureLogDirectory();
const timestamp = formatTimestamp();
// Sanitize and truncate params for logging
const sanitizedParams = {
model: params.model,
messages: params.messages?.map((msg) => ({
role: msg.role,
contentPreview: truncate(msg.content, 500),
})),
reasoning: params.reasoning,
responseFormat: params.responseFormat?.jsonSchema?.name,
stream: params.stream,
};
// Sanitize response
const sanitizedResponse = response
? {
choices: response.choices?.map((choice) => ({
messagePreview: truncate(choice.message?.content, 500),
finishReason: choice.finish_reason,
})),
model: response.model,
usage: response.usage,
}
: null;
const logEntry = {
timestamp,
operation,
params: sanitizedParams,
response: sanitizedResponse,
error: error
? {
name: error.name,
message: error.message,
}
: null,
};
const logLine = `${timestamp} | ${operation} | Params: ${JSON.stringify(logEntry.params)} | Response: ${JSON.stringify(logEntry.response)}${error ? ` | Error: ${error.message}` : ''}\n`;
await fs.appendFile(LOG_FILE, logLine, 'utf8');
} catch (logError) {
console.error('Failed to log OpenRouter call:', logError);
// Don't throw - logging should not break the main functionality
}
}
/**
* Gets the path to the OpenRouter log file
*/
export function getOpenRouterLogFilePath() {
return LOG_FILE;
}

View File

@@ -1,3 +1,5 @@
import { logOpenRouterCall } from './openRouterLogger.js';
function parseResponse(response) {
return JSON.parse(response.choices[0].message.content);
}
@@ -46,6 +48,7 @@ export async function summarizeSources({ openrouter, text, question }) {
};
const response = await openrouter.chat.send({ chatRequest: params });
await logOpenRouterCall('summarizeSources', params, response);
return parseResponse(response);
}
@@ -86,5 +89,6 @@ export async function summarizeFinalAnswer({ openrouter, text, question }) {
};
const response = await openrouter.chat.send({ chatRequest: params });
await logOpenRouterCall('summarizeFinalAnswer', params, response);
return parseResponse(response);
}

View File

@@ -1,4 +1,5 @@
import { extractContent } from './extractContent.js';
import { logExtraction } from './extractionLogger.js';
import { summarizeFinalAnswer, summarizeSources } from './openRouterService.js';
import { formatSummarySources } from './searchFormatter.js';
@@ -84,6 +85,9 @@ export function createSearchService({ exa, openrouter, broadcast }) {
const extractedContent = extractContent(result);
// Log the extraction
await logExtraction(result, extractedContent);
if (!extractedContent.trim()) {
throw new SearchServiceError('No content extracted from results');
}