#include "VoiceModule.h" #include #include namespace aissia { VoiceModule::VoiceModule() { m_logger = spdlog::get("VoiceModule"); if (!m_logger) { m_logger = spdlog::stdout_color_mt("VoiceModule"); } m_config = std::make_unique("config"); } void VoiceModule::setConfiguration(const grove::IDataNode& configNode, grove::IIO* io, grove::ITaskScheduler* scheduler) { m_io = io; m_config = std::make_unique("config"); // TTS config auto* ttsNode = const_cast(configNode).getChildReadOnly("tts"); if (ttsNode) { m_ttsEnabled = ttsNode->getBool("enabled", true); } // STT config auto* sttNode = const_cast(configNode).getChildReadOnly("stt"); if (sttNode) { m_sttEnabled = sttNode->getBool("enabled", true); m_language = sttNode->getString("language", "fr"); } // Subscribe to topics if (m_io) { grove::SubscriptionConfig subConfig; m_io->subscribe("ai:response", subConfig); m_io->subscribe("ai:suggestion", subConfig); m_io->subscribe("notification:speak", subConfig); m_io->subscribe("voice:speaking_started", subConfig); m_io->subscribe("voice:speaking_ended", subConfig); m_io->subscribe("voice:transcription", subConfig); // Tool request handlers m_io->subscribe("voice:command", subConfig); } m_logger->info("VoiceModule configure (v2 - sans infrastructure): TTS={}, STT={}", m_ttsEnabled, m_sttEnabled); } const grove::IDataNode& VoiceModule::getConfiguration() { return *m_config; } void VoiceModule::process(const grove::IDataNode& input) { processMessages(); } void VoiceModule::processMessages() { if (!m_io) return; while (m_io->hasMessages() > 0) { auto msg = m_io->pullMessage(); if (msg.topic == "ai:response" && msg.data) { handleAIResponse(*msg.data); } else if (msg.topic == "ai:suggestion" && msg.data) { handleSuggestion(*msg.data); } else if (msg.topic == "notification:speak" && msg.data) { handleNotificationSpeak(*msg.data); } else if (msg.topic == "voice:speaking_started" && msg.data) { handleSpeakingStarted(*msg.data); } else if (msg.topic == "voice:speaking_ended" && msg.data) { handleSpeakingEnded(*msg.data); } else if (msg.topic == "voice:transcription" && msg.data) { handleTranscription(*msg.data); } // Tool command handler else if (msg.topic == "voice:command" && msg.data) { handleToolCommand(*msg.data); } } } void VoiceModule::handleToolCommand(const grove::IDataNode& request) { std::string action = request.getString("action", ""); if (action == "speak") { std::string text = request.getString("text", ""); std::string priority = request.getString("priority", "normal"); if (!text.empty()) { bool isPriority = (priority == "high"); requestSpeak(text, isPriority); m_logger->info("Tool speak: {} (priority={})", text.substr(0, 50), priority); } } } void VoiceModule::requestSpeak(const std::string& text, bool priority) { if (!m_io || !m_ttsEnabled || text.empty()) return; auto request = std::make_unique("speak"); request->setString("text", text); request->setBool("priority", priority); m_io->publish("voice:speak", std::move(request)); m_logger->debug("Speak request: {} (priority={})", text.size() > 50 ? text.substr(0, 50) + "..." : text, priority); } void VoiceModule::handleAIResponse(const grove::IDataNode& data) { if (!m_ttsEnabled) return; std::string text = data.getString("text", ""); if (!text.empty()) { requestSpeak(text, false); } } void VoiceModule::handleSuggestion(const grove::IDataNode& data) { if (!m_ttsEnabled) return; std::string message = data.getString("message", ""); if (!message.empty()) { // Suggestions are priority messages requestSpeak(message, true); } } void VoiceModule::handleNotificationSpeak(const grove::IDataNode& data) { if (!m_ttsEnabled) return; std::string message = data.getString("message", ""); if (!message.empty()) { requestSpeak(message, false); } } void VoiceModule::handleSpeakingStarted(const grove::IDataNode& data) { m_isSpeaking = true; m_totalSpoken++; std::string text = data.getString("text", ""); m_logger->debug("Speaking started: {}", text.size() > 30 ? text.substr(0, 30) + "..." : text); } void VoiceModule::handleSpeakingEnded(const grove::IDataNode& data) { m_isSpeaking = false; m_logger->debug("Speaking ended"); } void VoiceModule::handleTranscription(const grove::IDataNode& data) { std::string text = data.getString("text", ""); float confidence = data.getDouble("confidence", 0.0); if (!text.empty()) { m_totalTranscribed++; m_logger->info("Transcription: {} (conf={:.2f})", text, confidence); // Forward to AI module if (m_io) { auto event = std::make_unique("query"); event->setString("query", text); event->setString("source", "voice"); m_io->publish("ai:query", std::move(event)); } } } std::unique_ptr VoiceModule::getHealthStatus() { auto status = std::make_unique("status"); status->setString("status", "running"); status->setBool("ttsEnabled", m_ttsEnabled); status->setBool("sttEnabled", m_sttEnabled); status->setBool("isSpeaking", m_isSpeaking); status->setInt("totalSpoken", m_totalSpoken); status->setInt("totalTranscribed", m_totalTranscribed); return status; } void VoiceModule::shutdown() { m_logger->info("VoiceModule arrete. Spoken: {}, Transcribed: {}", m_totalSpoken, m_totalTranscribed); } std::unique_ptr VoiceModule::getState() { auto state = std::make_unique("state"); state->setInt("totalSpoken", m_totalSpoken); state->setInt("totalTranscribed", m_totalTranscribed); return state; } void VoiceModule::setState(const grove::IDataNode& state) { m_totalSpoken = state.getInt("totalSpoken", 0); m_totalTranscribed = state.getInt("totalTranscribed", 0); m_logger->info("Etat restore: spoken={}, transcribed={}", m_totalSpoken, m_totalTranscribed); } } // namespace aissia #ifndef AISSIA_TEST_BUILD extern "C" { grove::IModule* createModule() { return new aissia::VoiceModule(); } void destroyModule(grove::IModule* module) { delete module; } } #endif