UNPKG

@claude-vector/cli

Version:

CLI for Claude-integrated vector search

545 lines (448 loc) 14 kB
/** * AdaptiveThrottler - パラメトリック思考によるAPI制限・パフォーマンス適応制御 * * 多次元制御要因: * - API レート制限(OpenAI: 3 RPM for free tier, 500+ for paid) * - 同時リクエスト数制限 * - エラー率に基づく動的調整 * - システム負荷(CPU、メモリ) * - ネットワーク応答時間 * * 適応的制御アルゴリズム: * - バックプレッシャー制御 * - 指数バックオフ * - 動的バッチサイズ調整 */ import { EventEmitter } from 'events'; /** * API制限・パフォーマンス制御のデフォルト設定 */ const THROTTLE_DEFAULTS = { // OpenAI API 制限設定 baseDelay: 1000, // 基本待機時間(ms) maxDelay: 30000, // 最大待機時間(ms) maxConcurrent: 3, // 最大同時実行数 // 適応制御設定 errorThreshold: 0.1, // エラー率閾値(10%) successThreshold: 0.95, // 成功率閾値(95%) adaptationFactor: 1.5, // 適応係数 // バックオフ設定 exponentialBase: 2, // 指数バックオフ基数 maxRetries: 3, // 最大リトライ回数 jitterFactor: 0.1, // ジッター係数 // システム負荷制御 memoryThreshold: 500, // メモリ閾値(MB) cpuThreshold: 80, // CPU使用率閾値(%) // 統計収集設定 statisticsWindow: 100, // 統計計算ウィンドウサイズ cleanupInterval: 300000 // 統計クリーンアップ間隔(5分) }; export class AdaptiveThrottler extends EventEmitter { constructor(config = {}) { super(); this.config = { ...THROTTLE_DEFAULTS, ...config }; // 制御状態 this.state = { // 実行制御 activeCalls: 0, queuedCalls: 0, // 統計情報 totalCalls: 0, successfulCalls: 0, failedCalls: 0, // 動的制御 currentDelay: this.config.baseDelay, errorRate: 0, successRate: 1, avgResponseTime: 0, // システム状態 lastSystemCheck: 0, systemLoad: { memory: 0, cpu: 0 } }; // 統計履歴 this.statistics = { callHistory: [], responseTimeHistory: [], errorHistory: [] }; // タイマー this.cleanupTimer = setInterval(() => { this.cleanupStatistics(); }, this.config.cleanupInterval); // 待機キュー this.waitQueue = []; this.startTime = Date.now(); } /** * API呼び出しの制御実行(パラメトリック制御) */ async throttleApiCall(operation, metadata = {}) { const callId = this.generateCallId(); const startTime = Date.now(); try { // 制御前チェック await this.preCallControl(callId, metadata); // API呼び出し実行 const result = await this.executeWithTracking(operation, callId); // 成功時の統計更新 this.recordSuccess(callId, startTime, result); return result; } catch (error) { // 失敗時の統計更新 this.recordFailure(callId, startTime, error); throw error; } } /** * 呼び出し前制御(多次元要因考慮) */ async preCallControl(callId, metadata) { // 1. 同時実行数制御 await this.waitForSlot(); // 2. 適応的遅延制御 const delay = this.calculateAdaptiveDelay(metadata); if (delay > 0) { await this.wait(delay); } // 3. システム負荷チェック await this.checkSystemLoad(); // 4. API レート制限チェック await this.checkRateLimit(); this.state.activeCalls++; this.state.queuedCalls = Math.max(0, this.state.queuedCalls - 1); } /** * 実行スロット待機 */ async waitForSlot() { if (this.state.activeCalls >= this.config.maxConcurrent) { this.state.queuedCalls++; return new Promise((resolve) => { this.waitQueue.push(resolve); }); } } /** * 適応的遅延計算(パラメトリック思考) */ calculateAdaptiveDelay(metadata = {}) { let delay = this.state.currentDelay; // 要因1: エラー率ベース調整 if (this.state.errorRate > this.config.errorThreshold) { const errorMultiplier = 1 + (this.state.errorRate * this.config.adaptationFactor); delay *= errorMultiplier; } // 要因2: システム負荷ベース調整 const systemLoad = this.state.systemLoad; if (systemLoad.memory > this.config.memoryThreshold) { delay *= 1.2; // メモリ負荷補正 } if (systemLoad.cpu > this.config.cpuThreshold) { delay *= 1.3; // CPU負荷補正 } // 要因3: 応答時間ベース調整 if (this.state.avgResponseTime > 5000) { // 5秒以上 delay *= 1.1; // 応答時間補正 } // 要因4: メタデータベース調整 if (metadata.priority === 'low') { delay *= 1.5; } else if (metadata.priority === 'high') { delay *= 0.8; } // 要因5: ジッター追加(同期呼び出し回避) const jitter = Math.random() * this.config.jitterFactor * delay; delay += jitter; // 制限範囲内に調整 return Math.max(0, Math.min(delay, this.config.maxDelay)); } /** * 待機処理 */ async wait(ms) { if (ms <= 0) return; return new Promise(resolve => { setTimeout(resolve, ms); }); } /** * システム負荷チェック */ async checkSystemLoad() { const now = Date.now(); // 1秒間隔でシステム負荷をチェック if (now - this.state.lastSystemCheck > 1000) { const memUsage = process.memoryUsage(); this.state.systemLoad.memory = Math.round(memUsage.heapUsed / 1024 / 1024); // CPU使用率は簡易的な実装(実際の測定は複雑) this.state.systemLoad.cpu = Math.min(50, this.state.activeCalls * 10); this.state.lastSystemCheck = now; } } /** * API レート制限チェック */ async checkRateLimit() { // OpenAI の RPM 制限を考慮した制御 // 基本的には delay で制御されるが、追加チェック const recentCalls = this.getRecentCalls(60000); // 過去1分 const rpm = recentCalls.length; // 無料tier: 3 RPM, 有料tier: 500+ RPM const maxRpm = process.env.OPENAI_TIER === 'free' ? 3 : 100; if (rpm >= maxRpm) { const waitTime = 60000 / maxRpm; // 分あたりの間隔 await this.wait(waitTime); } } /** * 実行・追跡 */ async executeWithTracking(operation, callId) { const startTime = Date.now(); try { this.emit('call-start', { callId, startTime }); const result = await operation(); const endTime = Date.now(); const responseTime = endTime - startTime; this.emit('call-success', { callId, responseTime, result }); return result; } catch (error) { const endTime = Date.now(); const responseTime = endTime - startTime; this.emit('call-error', { callId, responseTime, error }); throw error; } finally { this.state.activeCalls--; this.releaseWaitingCall(); } } /** * 成功記録 */ recordSuccess(callId, startTime, result) { const responseTime = Date.now() - startTime; this.state.totalCalls++; this.state.successfulCalls++; // 統計履歴に追加 this.statistics.callHistory.push({ callId, startTime, responseTime, success: true, timestamp: Date.now() }); this.statistics.responseTimeHistory.push(responseTime); // 動的統計更新 this.updateStatistics(); // 成功時の制御調整 this.adjustControlOnSuccess(); } /** * 失敗記録 */ recordFailure(callId, startTime, error) { const responseTime = Date.now() - startTime; this.state.totalCalls++; this.state.failedCalls++; // 統計履歴に追加 this.statistics.callHistory.push({ callId, startTime, responseTime, success: false, error: error.message, timestamp: Date.now() }); this.statistics.errorHistory.push({ timestamp: Date.now(), error: error.message, type: this.categorizeError(error) }); // 動的統計更新 this.updateStatistics(); // 失敗時の制御調整 this.adjustControlOnFailure(error); } /** * 統計更新 */ updateStatistics() { if (this.state.totalCalls === 0) return; // エラー率計算 this.state.errorRate = this.state.failedCalls / this.state.totalCalls; this.state.successRate = this.state.successfulCalls / this.state.totalCalls; // 平均応答時間計算 const recentResponseTimes = this.statistics.responseTimeHistory.slice(-50); if (recentResponseTimes.length > 0) { this.state.avgResponseTime = recentResponseTimes.reduce((sum, time) => sum + time, 0) / recentResponseTimes.length; } } /** * 成功時の制御調整 */ adjustControlOnSuccess() { // 連続成功時は遅延を減少 if (this.state.successRate > this.config.successThreshold) { this.state.currentDelay = Math.max( this.config.baseDelay, this.state.currentDelay * 0.9 ); } } /** * 失敗時の制御調整 */ adjustControlOnFailure(error) { const errorType = this.categorizeError(error); // エラータイプに応じた調整 switch (errorType) { case 'rate_limit': this.state.currentDelay *= 2; // レート制限時は倍化 break; case 'timeout': this.state.currentDelay *= 1.3; // タイムアウト時は1.3倍 break; case 'server_error': this.state.currentDelay *= 1.5; // サーバーエラー時は1.5倍 break; default: this.state.currentDelay *= 1.2; // その他は1.2倍 } // 最大値制限 this.state.currentDelay = Math.min(this.state.currentDelay, this.config.maxDelay); } /** * エラー分類 */ categorizeError(error) { const message = error.message.toLowerCase(); if (message.includes('rate limit') || message.includes('429')) { return 'rate_limit'; } else if (message.includes('timeout') || message.includes('ECONNRESET')) { return 'timeout'; } else if (message.includes('500') || message.includes('502') || message.includes('503')) { return 'server_error'; } else if (message.includes('network') || message.includes('ENOTFOUND')) { return 'network_error'; } else { return 'unknown'; } } /** * 待機中呼び出しの解放 */ releaseWaitingCall() { if (this.waitQueue.length > 0) { const resolve = this.waitQueue.shift(); resolve(); } } /** * 最近の呼び出し取得 */ getRecentCalls(timeWindowMs) { const cutoff = Date.now() - timeWindowMs; return this.statistics.callHistory.filter(call => call.timestamp > cutoff); } /** * 統計クリーンアップ */ cleanupStatistics() { const cutoff = Date.now() - (3600000); // 1時間前 // 古い履歴を削除 this.statistics.callHistory = this.statistics.callHistory.filter( call => call.timestamp > cutoff ); this.statistics.errorHistory = this.statistics.errorHistory.filter( error => error.timestamp > cutoff ); // 応答時間履歴は最新200件のみ保持 if (this.statistics.responseTimeHistory.length > 200) { this.statistics.responseTimeHistory = this.statistics.responseTimeHistory.slice(-200); } } /** * 統計情報取得 */ getStatistics() { const uptime = Date.now() - this.startTime; const rpm = this.getRecentCalls(60000).length; return { // 基本統計 totalCalls: this.state.totalCalls, successfulCalls: this.state.successfulCalls, failedCalls: this.state.failedCalls, successRate: this.state.successRate, errorRate: this.state.errorRate, // パフォーマンス avgResponseTime: Math.round(this.state.avgResponseTime), currentDelay: this.state.currentDelay, activeCalls: this.state.activeCalls, queuedCalls: this.state.queuedCalls, rpm, // システム負荷 systemLoad: this.state.systemLoad, // 実行時間 uptime, // 設定 config: this.config }; } /** * コール ID 生成 */ generateCallId() { return `call_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * 設定更新 */ updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; this.emit('config-updated', this.config); } /** * 状態リセット */ reset() { this.state = { activeCalls: 0, queuedCalls: 0, totalCalls: 0, successfulCalls: 0, failedCalls: 0, currentDelay: this.config.baseDelay, errorRate: 0, successRate: 1, avgResponseTime: 0, lastSystemCheck: 0, systemLoad: { memory: 0, cpu: 0 } }; this.statistics = { callHistory: [], responseTimeHistory: [], errorHistory: [] }; this.waitQueue = []; this.startTime = Date.now(); this.emit('reset'); } /** * クリーンアップ */ destroy() { if (this.cleanupTimer) { clearInterval(this.cleanupTimer); this.cleanupTimer = null; } // 待機中の呼び出しをキャンセル this.waitQueue.forEach(resolve => { resolve(); // または reject with cancellation error }); this.waitQueue = []; this.emit('destroyed'); } }