aiwf
Version:
AI Workflow Framework for Claude Code with multi-language support (Korean/English)
177 lines (155 loc) • 4.68 kB
JavaScript
import tiktoken from 'tiktoken';
import fs from 'fs';
import path from 'path';
/**
* 토큰 카운팅 및 분석을 위한 유틸리티 클래스
*/
export class TokenCounter {
constructor(model = 'gpt-4') {
this.model = model;
this.encoder = tiktoken.get_encoding('cl100k_base'); // GPT-4 기본 인코딩
}
/**
* 텍스트의 토큰 수를 계산합니다
* @param {string} text - 분석할 텍스트
* @returns {number} 토큰 수
*/
countTokens(text) {
if (!text || typeof text !== 'string') {
return 0;
}
try {
return this.encoder.encode(text).length;
} catch (error) {
console.error('토큰 카운팅 중 오류 발생:', error);
return 0;
}
}
/**
* 파일의 토큰 수를 계산합니다
* @param {string} filePath - 파일 경로
* @returns {number} 토큰 수
*/
countFileTokens(filePath) {
try {
if (!fs.existsSync(filePath)) {
return 0;
}
const content = fs.readFileSync(filePath, 'utf8');
return this.countTokens(content);
} catch (error) {
console.error(`파일 토큰 카운팅 중 오류 발생 (${filePath}):`, error);
return 0;
}
}
/**
* 디렉토리의 모든 마크다운 파일 토큰 수를 계산합니다
* @param {string} dirPath - 디렉토리 경로
* @returns {number} 총 토큰 수
*/
countDirectoryTokens(dirPath) {
try {
const files = this.getMarkdownFiles(dirPath);
return files.reduce((total, file) => {
return total + this.countFileTokens(file);
}, 0);
} catch (error) {
console.error(`디렉토리 토큰 카운팅 중 오류 발생 (${dirPath}):`, error);
return 0;
}
}
/**
* 디렉토리에서 마크다운 파일 목록을 가져옵니다
* @param {string} dirPath - 디렉토리 경로
* @returns {Array<string>} 마크다운 파일 경로 배열
*/
getMarkdownFiles(dirPath) {
const files = [];
try {
if (!fs.existsSync(dirPath)) {
return files;
}
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
if (entry.isDirectory()) {
files.push(...this.getMarkdownFiles(fullPath));
} else if (entry.isFile() && entry.name.endsWith('.md')) {
files.push(fullPath);
}
}
} catch (error) {
console.error(`마크다운 파일 탐색 중 오류 발생 (${dirPath}):`, error);
}
return files;
}
/**
* 콘텐츠의 토큰 분포를 분석합니다
* @param {string} content - 분석할 콘텐츠
* @returns {Array<Object>} 섹션별 토큰 분포 정보
*/
analyzeTokenDistribution(content) {
try {
const sections = this.parseSections(content);
const totalTokens = this.countTokens(content);
return sections.map(section => ({
name: section.name,
tokens: this.countTokens(section.content),
percentage: totalTokens > 0 ? ((this.countTokens(section.content) / totalTokens) * 100).toFixed(1) : 0,
content: section.content
}));
} catch (error) {
console.error('토큰 분포 분석 중 오류 발생:', error);
return [];
}
}
/**
* 마크다운 콘텐츠를 섹션별로 파싱합니다
* @param {string} content - 마크다운 콘텐츠
* @returns {Array<Object>} 섹션 정보 배열
*/
parseSections(content) {
const sections = [];
const lines = content.split('\n');
let currentSection = null;
let currentContent = [];
for (const line of lines) {
// 헤더 라인 감지 (# ## ### 등)
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
if (headerMatch) {
// 이전 섹션 저장
if (currentSection) {
sections.push({
name: currentSection,
content: currentContent.join('\n').trim()
});
}
// 새 섹션 시작
currentSection = headerMatch[2];
currentContent = [line];
} else {
// 현재 섹션에 라인 추가
if (currentSection) {
currentContent.push(line);
}
}
}
// 마지막 섹션 저장
if (currentSection) {
sections.push({
name: currentSection,
content: currentContent.join('\n').trim()
});
}
return sections;
}
/**
* 토큰 카운터 리소스를 정리합니다
*/
cleanup() {
if (this.encoder) {
this.encoder.free();
}
}
}
export default TokenCounter;