featureBridge init
This commit is contained in:
parent
2cf71f1034
commit
bf20d002ba
@ -56,7 +56,23 @@ module.exports = {
|
||||
ipcMain.handle('ollama:get-warm-up-status', async () => await ollamaService.handleGetWarmUpStatus());
|
||||
ipcMain.handle('ollama:shutdown', async (event, force = false) => await ollamaService.handleShutdown(event, force));
|
||||
|
||||
// ModelStateService
|
||||
// Ask
|
||||
ipcMain.handle('ask:sendMessage', async (event, userPrompt, conversationHistoryRaw = []) => await askService.sendMessage(userPrompt, conversationHistoryRaw));
|
||||
|
||||
// Listen
|
||||
ipcMain.handle('send-audio-content', async (event, { data, mimeType }) => await listenService.handleSendAudioContent(data, mimeType));
|
||||
ipcMain.handle('send-system-audio-content', async (event, { data, mimeType }) => {
|
||||
const result = await listenService.sttService.sendSystemAudioContent(data, mimeType);
|
||||
if(result.success) {
|
||||
listenService.sendToRenderer('system-audio-data', { data });
|
||||
}
|
||||
return result;
|
||||
});
|
||||
ipcMain.handle('start-macos-audio', async () => await listenService.handleStartMacosAudio());
|
||||
ipcMain.handle('stop-macos-audio', async () => await listenService.handleStopMacosAudio());
|
||||
ipcMain.handle('update-google-search-setting', async (event, enabled) => await listenService.handleUpdateGoogleSearchSetting(enabled));
|
||||
|
||||
// ModelStateService
|
||||
ipcMain.handle('model:validate-key', async (e, { provider, key }) => await modelStateService.handleValidateKey(provider, key));
|
||||
ipcMain.handle('model:get-all-keys', () => modelStateService.getAllApiKeys());
|
||||
ipcMain.handle('model:set-api-key', async (e, { provider, key }) => await modelStateService.setApiKey(provider, key));
|
||||
|
@ -1,4 +1,4 @@
|
||||
const { ipcMain, BrowserWindow } = require('electron');
|
||||
const { BrowserWindow } = require('electron');
|
||||
const { createStreamingLLM } = require('../common/ai/factory');
|
||||
const { getCurrentModelInfo, windowPool, captureScreenshot } = require('../../window/windowManager');
|
||||
const sessionRepository = require('../common/repositories/session');
|
||||
@ -17,17 +17,6 @@ class AskService {
|
||||
console.log('[AskService] Service instance created.');
|
||||
}
|
||||
|
||||
/**
|
||||
* IPC 리스너를 등록하여 렌더러 프로세스로부터의 요청을 처리합니다.
|
||||
* Electron 애플리케이션의 메인 프로세스에서 한 번만 호출되어야 합니다.
|
||||
*/
|
||||
initialize() {
|
||||
ipcMain.handle('ask:sendMessage', async (event, userPrompt, conversationHistoryRaw=[]) => {
|
||||
return this.sendMessage(userPrompt, conversationHistoryRaw);
|
||||
});
|
||||
console.log('[AskService] Initialized and ready.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 대화 기록 배열을 프롬프트에 적합한 단일 문자열로 변환합니다.
|
||||
* @param {string[]} conversationTexts - 대화 내용 문자열의 배열
|
||||
|
@ -36,15 +36,17 @@ class ModelStateService {
|
||||
console.log(`[ModelStateService] Current Selection -> LLM: ${llmModel || 'None'} (Provider: ${llmProvider}), STT: ${sttModel || 'None'} (Provider: ${sttProvider})`);
|
||||
}
|
||||
|
||||
_autoSelectAvailableModels() {
|
||||
console.log('[ModelStateService] Running auto-selection for models...');
|
||||
_autoSelectAvailableModels(forceReselectionForTypes = []) {
|
||||
console.log(`[ModelStateService] Running auto-selection for models. Force re-selection for: [${forceReselectionForTypes.join(', ')}]`);
|
||||
const types = ['llm', 'stt'];
|
||||
|
||||
types.forEach(type => {
|
||||
const currentModelId = this.state.selectedModels[type];
|
||||
let isCurrentModelValid = false;
|
||||
|
||||
if (currentModelId) {
|
||||
const forceReselection = forceReselectionForTypes.includes(type);
|
||||
|
||||
if (currentModelId && !forceReselection) {
|
||||
const provider = this.getProviderForModel(type, currentModelId);
|
||||
const apiKey = this.getApiKey(provider);
|
||||
// For Ollama, 'local' is a valid API key
|
||||
@ -54,7 +56,7 @@ class ModelStateService {
|
||||
}
|
||||
|
||||
if (!isCurrentModelValid) {
|
||||
console.log(`[ModelStateService] No valid ${type.toUpperCase()} model selected. Finding an alternative...`);
|
||||
console.log(`[ModelStateService] No valid ${type.toUpperCase()} model selected or re-selection forced. Finding an alternative...`);
|
||||
const availableModels = this.getAvailableModels(type);
|
||||
if (availableModels.length > 0) {
|
||||
// Prefer API providers over local providers for auto-selection
|
||||
@ -331,7 +333,16 @@ class ModelStateService {
|
||||
async setApiKey(provider, key) {
|
||||
if (provider in this.state.apiKeys) {
|
||||
this.state.apiKeys[provider] = key;
|
||||
this._autoSelectAvailableModels();
|
||||
|
||||
const supportedTypes = [];
|
||||
if (PROVIDERS[provider]?.llmModels.length > 0 || provider === 'ollama') {
|
||||
supportedTypes.push('llm');
|
||||
}
|
||||
if (PROVIDERS[provider]?.sttModels.length > 0 || provider === 'whisper') {
|
||||
supportedTypes.push('stt');
|
||||
}
|
||||
|
||||
this._autoSelectAvailableModels(supportedTypes);
|
||||
await this._saveState();
|
||||
return true;
|
||||
}
|
||||
@ -395,6 +406,8 @@ class ModelStateService {
|
||||
areProvidersConfigured() {
|
||||
if (this.isLoggedInWithFirebase()) return true;
|
||||
|
||||
console.log('[DEBUG] Checking configured providers with apiKeys state:', JSON.stringify(this.state.apiKeys, (key, value) => (value ? '***' : null), 2));
|
||||
|
||||
// LLM과 STT 모델을 제공하는 Provider 중 하나라도 API 키가 설정되었는지 확인
|
||||
const hasLlmKey = Object.entries(this.state.apiKeys).some(([provider, key]) => {
|
||||
if (provider === 'ollama') {
|
||||
|
@ -1,4 +1,4 @@
|
||||
const { ipcMain, BrowserWindow } = require('electron');
|
||||
const { BrowserWindow } = require('electron');
|
||||
const SttService = require('./stt/sttService');
|
||||
const SummaryService = require('./summary/summaryService');
|
||||
const authService = require('../common/services/authService');
|
||||
@ -46,11 +46,6 @@ class ListenService {
|
||||
});
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.setupIpcHandlers();
|
||||
console.log('[ListenService] Initialized and ready.');
|
||||
}
|
||||
|
||||
async handleTranscriptionComplete(speaker, text) {
|
||||
console.log(`[ListenService] Transcription complete: ${speaker} - ${text}`);
|
||||
|
||||
@ -222,70 +217,57 @@ class ListenService {
|
||||
return this.summaryService.getConversationHistory();
|
||||
}
|
||||
|
||||
setupIpcHandlers() {
|
||||
ipcMain.handle('send-audio-content', async (event, { data, mimeType }) => {
|
||||
_createHandler(asyncFn, successMessage, errorMessage) {
|
||||
return async (...args) => {
|
||||
try {
|
||||
await this.sendAudioContent(data, mimeType);
|
||||
return { success: true };
|
||||
const result = await asyncFn.apply(this, args);
|
||||
if (successMessage) console.log(successMessage);
|
||||
// `startMacOSAudioCapture`는 성공 시 { success, error } 객체를 반환하지 않으므로,
|
||||
// 핸들러가 일관된 응답을 보내도록 여기서 success 객체를 반환합니다.
|
||||
// 다른 함수들은 이미 success 객체를 반환합니다.
|
||||
return result && typeof result.success !== 'undefined' ? result : { success: true };
|
||||
} catch (e) {
|
||||
console.error('Error sending user audio:', e);
|
||||
console.error(errorMessage, e);
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
ipcMain.handle('send-system-audio-content', async (event, { data, mimeType }) => {
|
||||
try {
|
||||
await this.sttService.sendSystemAudioContent(data, mimeType);
|
||||
|
||||
// Send system audio data back to renderer for AEC reference (like macOS does)
|
||||
this.sendToRenderer('system-audio-data', { data });
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error sending system audio:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
// `_createHandler`를 사용하여 핸들러들을 동적으로 생성합니다.
|
||||
handleSendAudioContent = this._createHandler(
|
||||
this.sendAudioContent,
|
||||
null,
|
||||
'Error sending user audio:'
|
||||
);
|
||||
|
||||
ipcMain.handle('start-macos-audio', async () => {
|
||||
handleStartMacosAudio = this._createHandler(
|
||||
async () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
return { success: false, error: 'macOS audio capture only available on macOS' };
|
||||
}
|
||||
if (this.sttService.isMacOSAudioRunning?.()) {
|
||||
return { success: false, error: 'already_running' };
|
||||
}
|
||||
await this.startMacOSAudioCapture();
|
||||
return { success: true, error: null };
|
||||
},
|
||||
'macOS audio capture started.',
|
||||
'Error starting macOS audio capture:'
|
||||
);
|
||||
|
||||
handleStopMacosAudio = this._createHandler(
|
||||
this.stopMacOSAudioCapture,
|
||||
'macOS audio capture stopped.',
|
||||
'Error stopping macOS audio capture:'
|
||||
);
|
||||
|
||||
try {
|
||||
const success = await this.startMacOSAudioCapture();
|
||||
return { success, error: null };
|
||||
} catch (error) {
|
||||
console.error('Error starting macOS audio capture:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('stop-macos-audio', async () => {
|
||||
try {
|
||||
this.stopMacOSAudioCapture();
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error stopping macOS audio capture:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('update-google-search-setting', async (event, enabled) => {
|
||||
try {
|
||||
console.log('Google Search setting updated to:', enabled);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error updating Google Search setting:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
console.log('✅ Listen service IPC handlers registered');
|
||||
}
|
||||
handleUpdateGoogleSearchSetting = this._createHandler(
|
||||
async (enabled) => {
|
||||
console.log('Google Search setting updated to:', enabled);
|
||||
},
|
||||
null,
|
||||
'Error updating Google Search setting:'
|
||||
);
|
||||
}
|
||||
|
||||
const listenService = new ListenService();
|
||||
|
@ -41,6 +41,17 @@ class SttService {
|
||||
});
|
||||
}
|
||||
|
||||
async handleSendSystemAudioContent(data, mimeType) {
|
||||
try {
|
||||
await this.sendSystemAudioContent(data, mimeType);
|
||||
this.sendToRenderer('system-audio-data', { data });
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Error sending system audio:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
flushMyCompletion() {
|
||||
const finalText = (this.myCompletionBuffer + this.myCurrentUtterance).trim();
|
||||
if (!this.modelInfo || !finalText) return;
|
||||
|
@ -197,8 +197,6 @@ app.whenReady().then(async () => {
|
||||
await modelStateService.initialize();
|
||||
//////// after_modelStateService ////////
|
||||
|
||||
listenService.initialize();
|
||||
askService.initialize();
|
||||
featureBridge.initialize(); // 추가: featureBridge 초기화
|
||||
setupWebDataHandlers();
|
||||
|
||||
|
@ -1541,7 +1541,7 @@ export class ApiKeyHeader extends LitElement {
|
||||
this.classList.remove("sliding-out");
|
||||
this.classList.add("hidden");
|
||||
|
||||
console.log('[ApiKeyHeader] handleAnimationEnd: Animation completed, transitioning to next state...');
|
||||
console.log('[ApiKeyHeader] handleAnimationEnd: Transition completed, transitioning to next state...');
|
||||
|
||||
if (!window.require) {
|
||||
console.error('[ApiKeyHeader] handleAnimationEnd: window.require not available');
|
||||
@ -1585,7 +1585,8 @@ export class ApiKeyHeader extends LitElement {
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
this.addEventListener("animationend", this.handleAnimationEnd)
|
||||
// this.addEventListener("animationend", this.handleAnimationEnd)
|
||||
this.addEventListener("transitionend", this.handleAnimationEnd)
|
||||
}
|
||||
|
||||
handleMessageFadeEnd(e) {
|
||||
@ -1603,8 +1604,8 @@ export class ApiKeyHeader extends LitElement {
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback()
|
||||
this.removeEventListener("animationend", this.handleAnimationEnd)
|
||||
|
||||
// this.removeEventListener("animationend", this.handleAnimationEnd)
|
||||
this.removeEventListener("transitionend", this.handleAnimationEnd)
|
||||
// Professional cleanup of all resources
|
||||
this._performCompleteCleanup();
|
||||
}
|
||||
|
@ -96,8 +96,12 @@ class HeaderTransitionManager {
|
||||
|
||||
//////// after_modelStateService ////////
|
||||
async handleStateUpdate(userState) {
|
||||
console.log('[HeaderController DEBUG] handleStateUpdate called with userState:', userState);
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
|
||||
console.log('[HeaderController DEBUG] Invoking "model:are-providers-configured"...');
|
||||
const isConfigured = await ipcRenderer.invoke('model:are-providers-configured');
|
||||
console.log('[HeaderController DEBUG] "model:are-providers-configured" returned:', isConfigured);
|
||||
|
||||
if (isConfigured) {
|
||||
const { isLoggedIn } = userState;
|
||||
|
Loading…
x
Reference in New Issue
Block a user