n8n-nodes-openai-analytics
Version:
n8n node for OpenAI Analytics
140 lines • 6.35 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.embeddingBasedClassify = void 0;
const simpleMemory_1 = require("./simpleMemory");
// 워크플로우 ID별 카테고리 임베딩을 저장하는 캐시
const categoryEmbeddingsCache = new Map();
/**
* 카테고리 목록의 해시값을 생성
* @param categories 카테고리 목록
* @returns 카테고리 목록의 해시값
*/
function getCategoriesHash(categories) {
return categories.map(c => c.category).sort().join('|');
}
/**
* 임베딩 기반 텍스트 분류기
* 텍스트와 카테고리의 임베딩 벡터 간 유사도 계산으로 분류
*/
async function embeddingBasedClassify(context) {
var _a;
const { openai, functionThis, i } = context;
// 대상 텍스트 가져오기
const targetText = functionThis.getNodeParameter('embeddingTargetText', i);
// 카테고리 목록 및 임베딩 가져오기
const categoriesCollection = functionThis.getNodeParameter('embeddingCategories', i);
if (!categoriesCollection || !categoriesCollection.values || categoriesCollection.values.length === 0) {
throw new Error('카테고리가 정의되지 않았습니다. 분류를 위한 카테고리를 추가해주세요.');
}
// 임베딩 모델 가져오기
let embeddingModel;
try {
embeddingModel = functionThis.getNodeParameter('embeddingModel', i);
}
catch (error) {
// embeddingModel 파라미터가 없으면 기본값 사용
console.log('embeddingModel 파라미터를 찾을 수 없습니다. 기본값을 사용합니다.');
embeddingModel = 'text-embedding-3-small';
}
// 워크플로우 ID 가져오기 (캐싱에 사용)
const workflowId = (((_a = functionThis.getNode().credentials) === null || _a === void 0 ? void 0 : _a.workflowId) ||
functionThis.getWorkflow().id ||
'default');
const cacheKey = `${workflowId}_${embeddingModel}`;
// 결과 카테고리 배열 초기화
let categories = [];
// 워크플로우에 대한 임베딩 캐시 가져오기 또는 새로 생성
if (!categoryEmbeddingsCache.has(cacheKey)) {
categoryEmbeddingsCache.set(cacheKey, new Map());
}
const workflowEmbeddingCache = categoryEmbeddingsCache.get(cacheKey);
// 카테고리별로 임베딩 처리
for (const item of categoriesCollection.values) {
let embedding = item.embedding;
const category = item.category;
// 임베딩이 없거나 빈 배열인 경우
if (!embedding || embedding.length === 0) {
// 캐시에 카테고리가 있는 경우 캐시 사용
if (workflowEmbeddingCache.has(category)) {
embedding = workflowEmbeddingCache.get(category);
console.log(`카테고리 '${category}'에 대해 캐시된 임베딩을 사용합니다.`);
}
else {
// 캐시에 없는 경우 새 임베딩 생성
console.log(`카테고리 '${category}'에 대한 새 임베딩을 생성합니다.`);
const embeddingResponse = await openai.embeddings.create({
model: embeddingModel,
input: category,
});
embedding = embeddingResponse.data[0].embedding;
// 생성된 임베딩 캐싱
workflowEmbeddingCache.set(category, embedding);
}
}
else {
// 사용자가 직접 임베딩을 제공한 경우 캐시 갱신
workflowEmbeddingCache.set(category, embedding);
}
categories.push({
category,
embedding,
});
}
// 캐시 통계 로깅
console.log(`워크플로우 '${workflowId}'에 대한 임베딩 캐시: ${workflowEmbeddingCache.size}개 카테고리`);
// 대상 텍스트 임베딩 생성
const embeddingResponse = await openai.embeddings.create({
model: embeddingModel,
input: targetText,
});
const targetEmbedding = embeddingResponse.data[0].embedding;
// 유사도 임계값 가져오기
const similarityThreshold = functionThis.getNodeParameter('embeddingThreshold', i, 0.7);
// 분류 타입 가져오기 (단일 또는 다중)
const classificationType = functionThis.getNodeParameter('embeddingClassificationType', i, 'single');
// 브랜칭 사용 여부 가져오기
const useBranching = functionThis.getNodeParameter('embeddingUseBranching', i, true);
// 각 카테고리와의 유사도 계산
const similarities = categories.map(category => ({
category: category.category,
similarity: (0, simpleMemory_1.simpleVectorSimilarity)(targetEmbedding, category.embedding),
}));
// 유사도가 높은 순으로 정렬
similarities.sort((a, b) => b.similarity - a.similarity);
// 결과 생성
let result;
if (classificationType === 'single') {
// 가장 유사한 카테고리 선택 (임계값 이상인 경우만)
const bestMatch = similarities[0] || { category: 'unclassified', similarity: 0 };
result = {
category: bestMatch.similarity >= similarityThreshold ? bestMatch.category : 'unclassified',
similarity: bestMatch.similarity,
text: targetText,
model: embeddingModel,
};
}
else {
// 임계값 이상인 모든 카테고리 선택
const matchingCategories = similarities.filter(item => item.similarity >= similarityThreshold);
result = {
category: matchingCategories.length > 0 ? matchingCategories[0].category : 'unclassified',
similarity: matchingCategories.length > 0 ? matchingCategories[0].similarity : 0,
categories: matchingCategories,
text: targetText,
model: embeddingModel,
};
}
// 브랜치 데이터 추가
if (useBranching) {
const branchData = {};
for (const category of categories) {
branchData[category.category] = category.category === result.category;
}
result._categoryBranches = branchData;
}
return {
json: result,
};
}
exports.embeddingBasedClassify = embeddingBasedClassify;
//# sourceMappingURL=embeddingBasedClassify.js.map