n8n-nodes-sap-ai-core
Version:
n8n nodes for SAP AI Core LLM and embeddings integration
169 lines • 7.48 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.N8nLlmTracing = void 0;
// N8nLlmTracing.ts
const base_1 = require("@langchain/core/callbacks/base");
const n8n_workflow_1 = require("n8n-workflow");
const TIKTOKEN_ESTIMATE_MODEL = 'gpt-4o';
// Simple pick function to avoid lodash dependency
function pick(object, keys) {
const result = {};
for (const key of keys) {
if (key in object) {
result[key] = object[key];
}
}
return result;
}
class N8nLlmTracing extends base_1.BaseCallbackHandler {
constructor(executionFunctions, options) {
super();
this.executionFunctions = executionFunctions;
this.name = 'N8nLlmTracing';
this.awaitHandlers = true;
this.connectionType = "ai_languageModel" /* NodeConnectionType.AiLanguageModel */;
this.promptTokensEstimate = 0;
this.completionTokensEstimate = 0;
this.runsMap = {};
this.options = {
// Default(OpenAI format) parser
tokensUsageParser: (result) => {
var _a, _b, _c, _d, _e, _f;
const completionTokens = (_c = (_b = (_a = result === null || result === void 0 ? void 0 : result.llmOutput) === null || _a === void 0 ? void 0 : _a.tokenUsage) === null || _b === void 0 ? void 0 : _b.completionTokens) !== null && _c !== void 0 ? _c : 0;
const promptTokens = (_f = (_e = (_d = result === null || result === void 0 ? void 0 : result.llmOutput) === null || _d === void 0 ? void 0 : _d.tokenUsage) === null || _e === void 0 ? void 0 : _e.promptTokens) !== null && _f !== void 0 ? _f : 0;
return {
completionTokens,
promptTokens,
totalTokens: completionTokens + promptTokens,
};
},
errorDescriptionMapper: (error) => error.description,
...options,
};
}
async estimateTokensFromGeneration(generations) {
const messages = generations.flatMap((gen) => gen.map((g) => g.text));
return await this.estimateTokensFromStringList(messages);
}
async estimateTokensFromStringList(list) {
// Simple token estimation - you might want to implement actual tiktoken
const text = list.join(' ');
return Math.ceil(text.length / 4); // Rough estimate: 4 chars per token
}
async handleLLMEnd(output, runId) {
var _a;
const runDetails = (_a = this.runsMap[runId]) !== null && _a !== void 0 ? _a : {
index: Object.keys(this.runsMap).length,
options: {},
messages: []
};
output.generations = output.generations.map((gen) => gen.map((g) => pick(g, ['text', 'generationInfo'])));
const tokenUsageEstimate = {
completionTokens: 0,
promptTokens: 0,
totalTokens: 0,
};
const tokenUsage = this.options.tokensUsageParser(output);
if (output.generations.length > 0) {
tokenUsageEstimate.completionTokens = await this.estimateTokensFromGeneration(output.generations);
tokenUsageEstimate.promptTokens = this.promptTokensEstimate;
tokenUsageEstimate.totalTokens =
tokenUsageEstimate.completionTokens + this.promptTokensEstimate;
}
const response = {
response: { generations: output.generations },
};
if (tokenUsage.completionTokens > 0) {
response.tokenUsage = tokenUsage;
}
else {
response.tokenUsageEstimate = tokenUsageEstimate;
}
const parsedMessages = typeof runDetails.messages === 'string'
? runDetails.messages
: runDetails.messages.map((message) => {
if (typeof message === 'string')
return message;
if (typeof (message === null || message === void 0 ? void 0 : message.toJSON) === 'function')
return message.toJSON();
return message;
});
const sourceNodeRunIndex = this.parentRunIndex !== undefined
? this.parentRunIndex + runDetails.index
: undefined;
// Fixed: Use correct number of parameters for addOutputData
this.executionFunctions.addOutputData(this.connectionType, runDetails.index, [[{ json: { ...response } }]]);
// Log AI event (simplified - you might want to implement actual logging)
console.log('AI LLM generated output', {
messages: parsedMessages,
options: runDetails.options,
response,
});
}
async handleLLMStart(llm, prompts, runId) {
var _a;
const estimatedTokens = await this.estimateTokensFromStringList(prompts);
// Fixed: Create a simple index instead of using getNextRunIndex
const currentIndex = Object.keys(this.runsMap).length;
const sourceNodeRunIndex = this.parentRunIndex !== undefined
? this.parentRunIndex + currentIndex
: undefined;
const options = llm.type === 'constructor' ? llm.kwargs : llm;
// Fixed: Use addInputData with correct parameters
const inputResult = this.executionFunctions.addInputData(this.connectionType, [
[
{
json: {
messages: prompts,
estimatedTokens,
options,
},
},
],
]);
this.runsMap[runId] = {
index: (_a = inputResult === null || inputResult === void 0 ? void 0 : inputResult.index) !== null && _a !== void 0 ? _a : currentIndex,
options,
messages: prompts,
};
this.promptTokensEstimate = estimatedTokens;
}
async handleLLMError(error, runId, parentRunId) {
var _a;
const runDetails = (_a = this.runsMap[runId]) !== null && _a !== void 0 ? _a : {
index: Object.keys(this.runsMap).length,
options: {},
messages: []
};
if (typeof error === 'object' && (error === null || error === void 0 ? void 0 : error.hasOwnProperty('headers'))) {
const errorWithHeaders = error;
Object.keys(errorWithHeaders.headers).forEach((key) => {
if (!key.startsWith('x-')) {
delete errorWithHeaders.headers[key];
}
});
}
if (error instanceof n8n_workflow_1.NodeError) {
if (this.options.errorDescriptionMapper) {
error.description = this.options.errorDescriptionMapper(error);
}
this.executionFunctions.addOutputData(this.connectionType, runDetails.index, error);
}
else {
this.executionFunctions.addOutputData(this.connectionType, runDetails.index, new n8n_workflow_1.NodeOperationError(this.executionFunctions.getNode(), error, {
functionality: 'configuration-node',
}));
}
// Log AI event (simplified)
console.log('AI LLM errored', {
error: Object.keys(error).length === 0 ? error.toString() : error,
runId,
parentRunId,
});
}
setParentRunIndex(runIndex) {
this.parentRunIndex = runIndex;
}
}
exports.N8nLlmTracing = N8nLlmTracing;
//# sourceMappingURL=N8nLlmTracing.js.map