diff --git a/ovos_core/intent_services/__init__.py b/ovos_core/intent_services/__init__.py index 28ca78e70be..0fce03d6afc 100644 --- a/ovos_core/intent_services/__init__.py +++ b/ovos_core/intent_services/__init__.py @@ -12,29 +12,30 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import warnings from collections import defaultdict -from typing import Tuple, Callable, Union +from typing import Tuple, Callable, Union, List +import requests from ovos_adapt.opm import AdaptPipeline -from ovos_commonqa.opm import CommonQAService +from ovos_config.config import Configuration +from ovos_config.locale import get_valid_languages from ocp_pipeline.opm import OCPPipelineMatcher from ovos_bus_client.message import Message from ovos_bus_client.session import SessionManager from ovos_bus_client.util import get_message_lang -from ovos_config.config import Configuration -from ovos_config.locale import get_valid_languages +from ovos_commonqa.opm import CommonQAService from ovos_core.intent_services.converse_service import ConverseService from ovos_core.intent_services.fallback_service import FallbackService from ovos_core.intent_services.stop_service import StopService from ovos_core.transformers import MetadataTransformersService, UtteranceTransformersService +from ovos_persona import PersonaService from ovos_plugin_manager.templates.pipeline import PipelineMatch, IntentHandlerMatch from ovos_utils.lang import standardize_lang_tag from ovos_utils.log import LOG, log_deprecation, deprecated from ovos_utils.metrics import Stopwatch from padacioso.opm import PadaciosoPipeline as PadaciosoService -from ovos_persona import PersonaService -import warnings class IntentService: @@ -342,6 +343,47 @@ def _emit_match_message(self, match: Union[IntentHandlerMatch, PipelineMatch], m # finally emit reply message self.bus.emit(reply) + # upload intent metrics if enabled + self._upload_match_data(match.utterance, + match.skill_id if isinstance(match, PipelineMatch) else match.match_type, + lang, + match.match_data) + else: + self._upload_match_data(match.utterance, + "complete_intent_failure", + lang, + match.match_data) + + + def _upload_match_data(self, utterance, intent, lang, match_data): + """if enabled upload the intent match data to a server, allowing users and developers + to collect metrics/datasets to improve the pipeline plugins and skills. + + There isn't a default server to upload things too, users needs to explicitly configure one + eg. https://github.com/TigreGotico/metrics-server-docker + """ + config = Configuration().get("open_data", {}) + endpoints: List[str] = config.get("intent_urls", []) #eg. "http://localhost:8000/intents" + if not endpoints: + return # user didn't configure any endpoints to upload metrics to + if isinstance(endpoints, str): + endpoints = [endpoints] + headers = {"Content-Type": "application/x-www-form-urlencoded", + "User-Agent": config.get("user_agent", "ovos-metrics")} + data = { + "utterance": utterance, + "intent": intent, + "lang": lang, + "match_data": match_data + } + for url in endpoints: + try: + # Add a timeout to prevent hanging + response = requests.post(url, data=data, headers=headers, timeout=3) + LOG.info(f"Uploaded intent metrics to '{url}' - Response: {response.status_code}") + except Exception as e: + LOG.warning(f"Failed to upload metrics: {e}") + def send_cancel_event(self, message): """ Emit events and play a sound when an utterance is canceled.