Add localAIManager
This commit is contained in:
parent
6ece74737b
commit
9359b32c01
639
src/features/common/services/localAIManager.js
Normal file
639
src/features/common/services/localAIManager.js
Normal file
@ -0,0 +1,639 @@
|
|||||||
|
const { EventEmitter } = require('events');
|
||||||
|
const ollamaService = require('./ollamaService');
|
||||||
|
const whisperService = require('./whisperService');
|
||||||
|
|
||||||
|
|
||||||
|
//Central manager for managing Ollama and Whisper services
|
||||||
|
class LocalAIManager extends EventEmitter {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// service map
|
||||||
|
this.services = {
|
||||||
|
ollama: ollamaService,
|
||||||
|
whisper: whisperService
|
||||||
|
};
|
||||||
|
|
||||||
|
// unified state management
|
||||||
|
this.state = {
|
||||||
|
ollama: {
|
||||||
|
installed: false,
|
||||||
|
running: false,
|
||||||
|
models: []
|
||||||
|
},
|
||||||
|
whisper: {
|
||||||
|
installed: false,
|
||||||
|
initialized: false,
|
||||||
|
models: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// setup event listeners
|
||||||
|
this.setupEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// subscribe to events from each service and re-emit as unified events
|
||||||
|
setupEventListeners() {
|
||||||
|
// ollama events
|
||||||
|
ollamaService.on('install-progress', (data) => {
|
||||||
|
this.emit('install-progress', 'ollama', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
ollamaService.on('installation-complete', () => {
|
||||||
|
this.emit('installation-complete', 'ollama');
|
||||||
|
this.updateServiceState('ollama');
|
||||||
|
});
|
||||||
|
|
||||||
|
ollamaService.on('error', (error) => {
|
||||||
|
this.emit('error', { service: 'ollama', ...error });
|
||||||
|
});
|
||||||
|
|
||||||
|
ollamaService.on('model-pull-complete', (data) => {
|
||||||
|
this.emit('model-ready', { service: 'ollama', ...data });
|
||||||
|
this.updateServiceState('ollama');
|
||||||
|
});
|
||||||
|
|
||||||
|
ollamaService.on('state-changed', (state) => {
|
||||||
|
this.emit('state-changed', 'ollama', state);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Whisper 이벤트
|
||||||
|
whisperService.on('install-progress', (data) => {
|
||||||
|
this.emit('install-progress', 'whisper', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
whisperService.on('installation-complete', () => {
|
||||||
|
this.emit('installation-complete', 'whisper');
|
||||||
|
this.updateServiceState('whisper');
|
||||||
|
});
|
||||||
|
|
||||||
|
whisperService.on('error', (error) => {
|
||||||
|
this.emit('error', { service: 'whisper', ...error });
|
||||||
|
});
|
||||||
|
|
||||||
|
whisperService.on('model-download-complete', (data) => {
|
||||||
|
this.emit('model-ready', { service: 'whisper', ...data });
|
||||||
|
this.updateServiceState('whisper');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 서비스 설치
|
||||||
|
*/
|
||||||
|
async installService(serviceName, options = {}) {
|
||||||
|
const service = this.services[serviceName];
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(`Unknown service: ${serviceName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (serviceName === 'ollama') {
|
||||||
|
return await service.handleInstall();
|
||||||
|
} else if (serviceName === 'whisper') {
|
||||||
|
// Whisper는 자동 설치
|
||||||
|
await service.initialize();
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.emit('error', {
|
||||||
|
service: serviceName,
|
||||||
|
errorType: 'installation-failed',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 서비스 상태 조회
|
||||||
|
*/
|
||||||
|
async getServiceStatus(serviceName) {
|
||||||
|
const service = this.services[serviceName];
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(`Unknown service: ${serviceName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceName === 'ollama') {
|
||||||
|
return await service.getStatus();
|
||||||
|
} else if (serviceName === 'whisper') {
|
||||||
|
const installed = await service.isInstalled();
|
||||||
|
const running = await service.isServiceRunning();
|
||||||
|
const models = await service.getInstalledModels();
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
installed,
|
||||||
|
running,
|
||||||
|
models
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 서비스 시작
|
||||||
|
*/
|
||||||
|
async startService(serviceName) {
|
||||||
|
const service = this.services[serviceName];
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(`Unknown service: ${serviceName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await service.startService();
|
||||||
|
await this.updateServiceState(serviceName);
|
||||||
|
return { success: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 서비스 중지
|
||||||
|
*/
|
||||||
|
async stopService(serviceName) {
|
||||||
|
const service = this.services[serviceName];
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(`Unknown service: ${serviceName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result;
|
||||||
|
if (serviceName === 'ollama') {
|
||||||
|
result = await service.shutdown(false);
|
||||||
|
} else if (serviceName === 'whisper') {
|
||||||
|
result = await service.stopService();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 서비스 중지 후 상태 업데이트
|
||||||
|
await this.updateServiceState(serviceName);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모델 설치/다운로드
|
||||||
|
*/
|
||||||
|
async installModel(serviceName, modelId, options = {}) {
|
||||||
|
const service = this.services[serviceName];
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(`Unknown service: ${serviceName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceName === 'ollama') {
|
||||||
|
return await service.pullModel(modelId);
|
||||||
|
} else if (serviceName === 'whisper') {
|
||||||
|
return await service.downloadModel(modelId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 설치된 모델 목록 조회
|
||||||
|
*/
|
||||||
|
async getInstalledModels(serviceName) {
|
||||||
|
const service = this.services[serviceName];
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(`Unknown service: ${serviceName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceName === 'ollama') {
|
||||||
|
return await service.getAllModelsWithStatus();
|
||||||
|
} else if (serviceName === 'whisper') {
|
||||||
|
return await service.getInstalledModels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모델 워밍업 (Ollama 전용)
|
||||||
|
*/
|
||||||
|
async warmUpModel(modelName, forceRefresh = false) {
|
||||||
|
return await ollamaService.warmUpModel(modelName, forceRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 자동 워밍업 (Ollama 전용)
|
||||||
|
*/
|
||||||
|
async autoWarmUp() {
|
||||||
|
return await ollamaService.autoWarmUpSelectedModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 진단 실행
|
||||||
|
*/
|
||||||
|
async runDiagnostics(serviceName) {
|
||||||
|
const service = this.services[serviceName];
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(`Unknown service: ${serviceName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const diagnostics = {
|
||||||
|
service: serviceName,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
checks: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 설치 상태 확인
|
||||||
|
diagnostics.checks.installation = {
|
||||||
|
check: 'Installation',
|
||||||
|
status: await service.isInstalled() ? 'pass' : 'fail',
|
||||||
|
details: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. 서비스 실행 상태
|
||||||
|
diagnostics.checks.running = {
|
||||||
|
check: 'Service Running',
|
||||||
|
status: await service.isServiceRunning() ? 'pass' : 'fail',
|
||||||
|
details: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. 포트 연결 테스트 및 상세 health check (Ollama)
|
||||||
|
if (serviceName === 'ollama') {
|
||||||
|
try {
|
||||||
|
// Use comprehensive health check
|
||||||
|
const health = await service.healthCheck();
|
||||||
|
diagnostics.checks.health = {
|
||||||
|
check: 'Service Health',
|
||||||
|
status: health.healthy ? 'pass' : 'fail',
|
||||||
|
details: health
|
||||||
|
};
|
||||||
|
|
||||||
|
// Legacy port check for compatibility
|
||||||
|
diagnostics.checks.port = {
|
||||||
|
check: 'Port Connectivity',
|
||||||
|
status: health.checks.apiResponsive ? 'pass' : 'fail',
|
||||||
|
details: { connected: health.checks.apiResponsive }
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
diagnostics.checks.health = {
|
||||||
|
check: 'Service Health',
|
||||||
|
status: 'fail',
|
||||||
|
details: { error: error.message }
|
||||||
|
};
|
||||||
|
diagnostics.checks.port = {
|
||||||
|
check: 'Port Connectivity',
|
||||||
|
status: 'fail',
|
||||||
|
details: { error: error.message }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 모델 목록
|
||||||
|
if (diagnostics.checks.running.status === 'pass') {
|
||||||
|
try {
|
||||||
|
const models = await service.getInstalledModels();
|
||||||
|
diagnostics.checks.models = {
|
||||||
|
check: 'Installed Models',
|
||||||
|
status: 'pass',
|
||||||
|
details: { count: models.length, models: models.map(m => m.name) }
|
||||||
|
};
|
||||||
|
|
||||||
|
// 5. 워밍업 상태
|
||||||
|
const warmupStatus = await service.getWarmUpStatus();
|
||||||
|
diagnostics.checks.warmup = {
|
||||||
|
check: 'Model Warm-up',
|
||||||
|
status: 'pass',
|
||||||
|
details: warmupStatus
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
diagnostics.checks.models = {
|
||||||
|
check: 'Installed Models',
|
||||||
|
status: 'fail',
|
||||||
|
details: { error: error.message }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Whisper 특화 진단
|
||||||
|
if (serviceName === 'whisper') {
|
||||||
|
// 바이너리 확인
|
||||||
|
diagnostics.checks.binary = {
|
||||||
|
check: 'Whisper Binary',
|
||||||
|
status: service.whisperPath ? 'pass' : 'fail',
|
||||||
|
details: { path: service.whisperPath }
|
||||||
|
};
|
||||||
|
|
||||||
|
// 모델 디렉토리
|
||||||
|
diagnostics.checks.modelDir = {
|
||||||
|
check: 'Model Directory',
|
||||||
|
status: service.modelsDir ? 'pass' : 'fail',
|
||||||
|
details: { path: service.modelsDir }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 전체 진단 결과
|
||||||
|
const allChecks = Object.values(diagnostics.checks);
|
||||||
|
diagnostics.summary = {
|
||||||
|
total: allChecks.length,
|
||||||
|
passed: allChecks.filter(c => c.status === 'pass').length,
|
||||||
|
failed: allChecks.filter(c => c.status === 'fail').length,
|
||||||
|
overallStatus: allChecks.every(c => c.status === 'pass') ? 'healthy' : 'unhealthy'
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
diagnostics.error = error.message;
|
||||||
|
diagnostics.summary = {
|
||||||
|
overallStatus: 'error'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return diagnostics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 서비스 복구
|
||||||
|
*/
|
||||||
|
async repairService(serviceName) {
|
||||||
|
const service = this.services[serviceName];
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(`Unknown service: ${serviceName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[LocalAIManager] Starting repair for ${serviceName}...`);
|
||||||
|
const repairLog = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 진단 실행
|
||||||
|
repairLog.push('Running diagnostics...');
|
||||||
|
const diagnostics = await this.runDiagnostics(serviceName);
|
||||||
|
|
||||||
|
if (diagnostics.summary.overallStatus === 'healthy') {
|
||||||
|
repairLog.push('Service is already healthy, no repair needed');
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
repairLog,
|
||||||
|
diagnostics
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 설치 문제 해결
|
||||||
|
if (diagnostics.checks.installation?.status === 'fail') {
|
||||||
|
repairLog.push('Installation missing, attempting to install...');
|
||||||
|
try {
|
||||||
|
await this.installService(serviceName);
|
||||||
|
repairLog.push('Installation completed');
|
||||||
|
} catch (error) {
|
||||||
|
repairLog.push(`Installation failed: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 서비스 재시작
|
||||||
|
if (diagnostics.checks.running?.status === 'fail') {
|
||||||
|
repairLog.push('Service not running, attempting to start...');
|
||||||
|
|
||||||
|
// 종료 시도
|
||||||
|
try {
|
||||||
|
await this.stopService(serviceName);
|
||||||
|
repairLog.push('Stopped existing service');
|
||||||
|
} catch (error) {
|
||||||
|
repairLog.push('Service was not running');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 잠시 대기
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
|
||||||
|
// 시작
|
||||||
|
try {
|
||||||
|
await this.startService(serviceName);
|
||||||
|
repairLog.push('Service started successfully');
|
||||||
|
} catch (error) {
|
||||||
|
repairLog.push(`Failed to start service: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 포트 문제 해결 (Ollama)
|
||||||
|
if (serviceName === 'ollama' && diagnostics.checks.port?.status === 'fail') {
|
||||||
|
repairLog.push('Port connectivity issue detected');
|
||||||
|
|
||||||
|
// 프로세스 강제 종료
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
try {
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
const execAsync = promisify(exec);
|
||||||
|
await execAsync('pkill -f ollama');
|
||||||
|
repairLog.push('Killed stale Ollama processes');
|
||||||
|
} catch (error) {
|
||||||
|
repairLog.push('No stale processes found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (process.platform === 'win32') {
|
||||||
|
try {
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
const execAsync = promisify(exec);
|
||||||
|
await execAsync('taskkill /F /IM ollama.exe');
|
||||||
|
repairLog.push('Killed stale Ollama processes');
|
||||||
|
} catch (error) {
|
||||||
|
repairLog.push('No stale processes found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (process.platform === 'linux') {
|
||||||
|
try {
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
const execAsync = promisify(exec);
|
||||||
|
await execAsync('pkill -f ollama');
|
||||||
|
repairLog.push('Killed stale Ollama processes');
|
||||||
|
} catch (error) {
|
||||||
|
repairLog.push('No stale processes found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// 재시작
|
||||||
|
await this.startService(serviceName);
|
||||||
|
repairLog.push('Restarted service after port cleanup');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Whisper 특화 복구
|
||||||
|
if (serviceName === 'whisper') {
|
||||||
|
// 세션 정리
|
||||||
|
if (diagnostics.checks.running?.status === 'pass') {
|
||||||
|
repairLog.push('Cleaning up Whisper sessions...');
|
||||||
|
await service.cleanup();
|
||||||
|
repairLog.push('Sessions cleaned up');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 초기화
|
||||||
|
if (!service.installState.isInitialized) {
|
||||||
|
repairLog.push('Re-initializing Whisper...');
|
||||||
|
await service.initialize();
|
||||||
|
repairLog.push('Whisper re-initialized');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 최종 상태 확인
|
||||||
|
repairLog.push('Verifying repair...');
|
||||||
|
const finalDiagnostics = await this.runDiagnostics(serviceName);
|
||||||
|
|
||||||
|
const success = finalDiagnostics.summary.overallStatus === 'healthy';
|
||||||
|
repairLog.push(success ? 'Repair successful!' : 'Repair failed - manual intervention may be required');
|
||||||
|
|
||||||
|
// 성공 시 상태 업데이트
|
||||||
|
if (success) {
|
||||||
|
await this.updateServiceState(serviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success,
|
||||||
|
repairLog,
|
||||||
|
diagnostics: finalDiagnostics
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
repairLog.push(`Repair error: ${error.message}`);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
repairLog,
|
||||||
|
error: error.message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 상태 업데이트
|
||||||
|
*/
|
||||||
|
async updateServiceState(serviceName) {
|
||||||
|
try {
|
||||||
|
const status = await this.getServiceStatus(serviceName);
|
||||||
|
this.state[serviceName] = status;
|
||||||
|
|
||||||
|
// 상태 변경 이벤트 발행
|
||||||
|
this.emit('state-changed', serviceName, status);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[LocalAIManager] Failed to update ${serviceName} state:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 전체 상태 조회
|
||||||
|
*/
|
||||||
|
async getAllServiceStates() {
|
||||||
|
const states = {};
|
||||||
|
|
||||||
|
for (const serviceName of Object.keys(this.services)) {
|
||||||
|
try {
|
||||||
|
states[serviceName] = await this.getServiceStatus(serviceName);
|
||||||
|
} catch (error) {
|
||||||
|
states[serviceName] = {
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 주기적 상태 동기화 시작
|
||||||
|
*/
|
||||||
|
startPeriodicSync(interval = 30000) {
|
||||||
|
if (this.syncInterval) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.syncInterval = setInterval(async () => {
|
||||||
|
for (const serviceName of Object.keys(this.services)) {
|
||||||
|
await this.updateServiceState(serviceName);
|
||||||
|
}
|
||||||
|
}, interval);
|
||||||
|
|
||||||
|
// 각 서비스의 주기적 동기화도 시작
|
||||||
|
ollamaService.startPeriodicSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 주기적 상태 동기화 중지
|
||||||
|
*/
|
||||||
|
stopPeriodicSync() {
|
||||||
|
if (this.syncInterval) {
|
||||||
|
clearInterval(this.syncInterval);
|
||||||
|
this.syncInterval = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 각 서비스의 주기적 동기화도 중지
|
||||||
|
ollamaService.stopPeriodicSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 전체 종료
|
||||||
|
*/
|
||||||
|
async shutdown() {
|
||||||
|
this.stopPeriodicSync();
|
||||||
|
|
||||||
|
const results = {};
|
||||||
|
for (const [serviceName, service] of Object.entries(this.services)) {
|
||||||
|
try {
|
||||||
|
if (serviceName === 'ollama') {
|
||||||
|
results[serviceName] = await service.shutdown(false);
|
||||||
|
} else if (serviceName === 'whisper') {
|
||||||
|
await service.cleanup();
|
||||||
|
results[serviceName] = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
results[serviceName] = false;
|
||||||
|
console.error(`[LocalAIManager] Failed to shutdown ${serviceName}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 에러 처리
|
||||||
|
*/
|
||||||
|
async handleError(serviceName, errorType, details = {}) {
|
||||||
|
console.error(`[LocalAIManager] Error in ${serviceName}: ${errorType}`, details);
|
||||||
|
|
||||||
|
// 서비스별 에러 처리
|
||||||
|
switch(errorType) {
|
||||||
|
case 'installation-failed':
|
||||||
|
// 설치 실패 시 이벤트 발생
|
||||||
|
this.emit('error-occurred', {
|
||||||
|
service: serviceName,
|
||||||
|
errorType,
|
||||||
|
error: details.error || 'Installation failed',
|
||||||
|
canRetry: true
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'model-pull-failed':
|
||||||
|
case 'model-download-failed':
|
||||||
|
// 모델 다운로드 실패
|
||||||
|
this.emit('error-occurred', {
|
||||||
|
service: serviceName,
|
||||||
|
errorType,
|
||||||
|
model: details.model,
|
||||||
|
error: details.error || 'Model download failed',
|
||||||
|
canRetry: true
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'service-not-responding':
|
||||||
|
// 서비스 반응 없음
|
||||||
|
console.log(`[LocalAIManager] Attempting to repair ${serviceName}...`);
|
||||||
|
const repairResult = await this.repairService(serviceName);
|
||||||
|
|
||||||
|
this.emit('error-occurred', {
|
||||||
|
service: serviceName,
|
||||||
|
errorType,
|
||||||
|
error: details.error || 'Service not responding',
|
||||||
|
repairAttempted: true,
|
||||||
|
repairSuccessful: repairResult.success
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// 기타 에러
|
||||||
|
this.emit('error-occurred', {
|
||||||
|
service: serviceName,
|
||||||
|
errorType,
|
||||||
|
error: details.error || `Unknown error: ${errorType}`,
|
||||||
|
canRetry: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 싱글톤
|
||||||
|
const localAIManager = new LocalAIManager();
|
||||||
|
module.exports = localAIManager;
|
Loading…
x
Reference in New Issue
Block a user