Skip to content

Commit

Permalink
Merge pull request #7 from OpenVoiceOS/release-0.1.1a2
Browse files Browse the repository at this point in the history
Release 0.1.1a2
  • Loading branch information
JarbasAl authored Oct 14, 2024
2 parents 70538ba + 2b2f9d5 commit 29c1686
Show file tree
Hide file tree
Showing 13 changed files with 301 additions and 11 deletions.
13 changes: 5 additions & 8 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
branches:
- dev
paths-ignore:
- 'ovos_bus_client/version.py'
- 'ocp_pipeline/version.py'
- 'examples/**'
- '.github/**'
- '.gitignore'
Expand All @@ -17,7 +17,7 @@ on:
branches:
- master
paths-ignore:
- 'ovos_bus_client/version.py'
- 'ocp_pipeline/version.py'
- 'requirements/**'
- 'examples/**'
- '.github/**'
Expand All @@ -33,7 +33,7 @@ jobs:
unit_tests:
strategy:
matrix:
python-version: [ 3.7, 3.8, 3.9, '3.10']
python-version: [3.9]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
Expand All @@ -52,13 +52,10 @@ jobs:
pip install -e .
- name: Install test dependencies
run: |
pip install -r test/requirements.txt
pip install -r tests/requirements.txt
- name: Run unittests
run: |
pytest --cov=ovos_adapt --cov-report=xml test/unittests
# NOTE: additional pytest invocations should also add the --cov-append flag
# or they will overwrite previous invocations' coverage reports
# (for an example, see OVOS Skill Manager's workflow)
pytest --cov=ocp_pipeline --cov-report=xml ./tests
- name: Upload coverage
if: "${{ matrix.python-version == '3.9' }}"
uses: codecov/codecov-action@v3
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Changelog

## [0.1.1a2](https://github.com/OpenVoiceOS/ovos-ocp-pipeline-plugin/tree/0.1.1a2) (2024-10-14)

[Full Changelog](https://github.com/OpenVoiceOS/ovos-ocp-pipeline-plugin/compare/0.1.1a1...0.1.1a2)

**Merged pull requests:**

- pt-pt/translate [\#6](https://github.com/OpenVoiceOS/ovos-ocp-pipeline-plugin/pull/6) ([gitlocalize-app[bot]](https://github.com/apps/gitlocalize-app))
- add unittests [\#5](https://github.com/OpenVoiceOS/ovos-ocp-pipeline-plugin/pull/5) ([JarbasAl](https://github.com/JarbasAl))

## [0.1.1a1](https://github.com/OpenVoiceOS/ovos-ocp-pipeline-plugin/tree/0.1.1a1) (2024-10-14)

[Full Changelog](https://github.com/OpenVoiceOS/ovos-ocp-pipeline-plugin/compare/0.1.0...0.1.1a1)

**Merged pull requests:**

- accepted index translation [\#3](https://github.com/OpenVoiceOS/ovos-ocp-pipeline-plugin/pull/3) ([gitlocalize-app[bot]](https://github.com/apps/gitlocalize-app))



\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# OCP Pipeline

OVOS plugin for specialized media handling

> NOTE: this repository is a work in progress, it is currently being [extracted from ovos-core](https://github.com/OpenVoiceOS/ovos-core/pull/527) and not yet used in the wild
## Architecture

![image](https://github.com/user-attachments/assets/8b6fac59-0e25-4373-ac17-fc5c0b9752fd)

![image](https://github.com/user-attachments/assets/8d7fdf5c-bdd9-4f30-8634-c0a5a6f87359)
3 changes: 3 additions & 0 deletions ocp_pipeline/locale/it-it/no.media.skills.dialog
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Non posso riprodurre nessun flusso fino a che non installi delle competenze media
Scusa ma non ho nessuna competenza installata per questa azione
Scusa, ma per compiere questa azione devi installare delle competenze media
31 changes: 31 additions & 0 deletions ocp_pipeline/locale/nl-nl/global_stop.intent
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Annuleer alle lopende acties
Annuleer alle taken
Beëindig alle acties
Beëindig alle lopende acties
Beëindig alle processen
Rond alle activiteiten af
Stop alle activiteiten
Stop alle huidige taken
Stop alle lopende acties
Stop alle lopende processen
Stop alle lopende processen
Stop met alle acties
Stop nu met alles
Stop onmiddellijk alle acties
Voltooi alle openstaande taken
alles annuleren
alles beëindigen
alles beëindigen
alles stoppen
alles stoppen
alles stoppen
alles stoppen
alles stoppen
alles stopzetten
annuleer alles
beëindig alles
beëindig alles
stop alles
stop alles
stop alles
stop met alles
17 changes: 17 additions & 0 deletions ocp_pipeline/locale/nl-nl/stop.intent
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
(kun|kan) je nu stoppen
Annuleer de huidige taak
Beëindig de huidige actie
Beëindig de huidige taak
Beëindig the current action
Ga niet verder
Hou daar alsjeblieft mee op
Maak er een einde aan
Stop de huidige actie
Stop de huidige actie
Stop het lopende proces
Stop met het uitvoeren van de huidige opdracht
Stop met het uitvoeren van die taak
Stop waar je mee bezig bent
stop
stop dit
stop ermee
3 changes: 3 additions & 0 deletions ocp_pipeline/locale/pt-pt/no.media.skills.dialog
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Desculpe, não tenho nenhuma habilidade de mídia instalada
Não consigo reproduzir nada até que algumas habilidades de mídia sejam instaladas
Preciso instalar algumas habilidades de mídia antes de poder reproduzir algo
4 changes: 2 additions & 2 deletions ocp_pipeline/version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# START_VERSION_BLOCK
VERSION_MAJOR = 0
VERSION_MINOR = 1
VERSION_BUILD = 0
VERSION_ALPHA = 0
VERSION_BUILD = 1
VERSION_ALPHA = 2
# END_VERSION_BLOCK
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
ovos-workshop
ovos-workshop
ovos-classifiers
5 changes: 5 additions & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
coveralls>=1.8.2
flake8>=3.7.9
pytest>=5.2.4
pytest-cov>=2.8.1
cov-core>=1.15.0
190 changes: 190 additions & 0 deletions tests/test_ocp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import os.path
import unittest
from unittest.mock import patch, Mock

from ovos_classifiers.skovos.features import ClassifierProbaVectorizer
from sklearn.pipeline import FeatureUnion
import ocp_pipeline.opm
from ovos_bus_client.message import Message
from ovos_utils.ocp import MediaType
from ocp_pipeline.opm import OCPFeaturizer, OCPPipelineMatcher
from ovos_utils.log import LOG


class TestOCPFeaturizer(unittest.TestCase):

def setUp(self):
self.featurizer = OCPFeaturizer()

@patch('os.path.isfile', return_value=True)
@patch('ovos_classifiers.skovos.features.KeywordFeaturesVectorizer.load_entities')
@patch.object(LOG, 'info')
def test_load_csv_with_existing_file(self, mock_log_info, mock_load_entities, mock_isfile):
csv_path = "existing_file.csv"
self.featurizer.load_csv([csv_path])
mock_isfile.assert_called_with(csv_path)
mock_load_entities.assert_called_with(csv_path)
mock_log_info.assert_called_with(f"Loaded OCP keywords: {csv_path}")

@patch.object(LOG, 'error')
def test_load_csv_with_nonexistent_file(self, mock_log_error):
csv_path = "nonexistent_file.csv"
self.featurizer.load_csv([csv_path])
mock_log_error.assert_called_with(f"Requested OCP entities file does not exist? {csv_path}")

@patch.object(FeatureUnion, 'transform', return_value='mock_transform_result')
def test_transform(self, mock_transform):
self.featurizer.clf_feats = Mock(spec=ClassifierProbaVectorizer)
result = self.featurizer.transform(["example_text"])
mock_transform.assert_called_with(["example_text"])
self.assertEqual(result, 'mock_transform_result')


class TestOCPPipelineNoClassifierMatcher(unittest.TestCase):

def setUp(self):
config = {
"experimental_media_classifier": False,
"experimental_binary_classifier": False,
"entity_csvs": [
os.path.dirname(ocp_pipeline.opm.__file__) + "/models/ocp_entities_v0.csv"
]}
self.ocp = OCPPipelineMatcher(config=config)

def test_match_high(self):
result = self.ocp.match_high(["play metallica"], "en-us")
self.assertIsNotNone(result)
self.assertEqual(result.intent_service, 'OCP_intents')
self.assertEqual(result.intent_type, 'ocp:play')

def test_match_high_with_invalid_input(self):
result = self.ocp.match_high(["put on some music"], "en-us")
self.assertIsNone(result)

def test_match_medium(self):
result = self.ocp.match_medium(["put on some movie"], "en-us")
self.assertIsNotNone(result)
self.assertEqual(result.intent_service, 'OCP_media')
self.assertEqual(result.intent_type, 'ocp:play')

def test_match_medium_with_invalid_input(self):
result = self.ocp.match_medium(["i wanna hear metallica"], "en-us")
self.assertIsNone(result)

def test_match_fallback(self):
result = self.ocp.match_fallback(["i want music"], "en-us")
self.assertIsNotNone(result)
self.assertEqual(result.intent_service, 'OCP_fallback')
self.assertEqual(result.intent_type, 'ocp:play')

def test_match_fallback_with_invalid_input(self):
result = self.ocp.match_fallback(["do the thing"], "en-us")
self.assertIsNone(result)

def test_predict(self):
self.assertTrue(self.ocp.is_ocp_query("play a song", "en-us")[0])
self.assertTrue(self.ocp.is_ocp_query("play a movie", "en-us")[0])
self.assertTrue(self.ocp.is_ocp_query("play a podcast", "en-us")[0])
self.assertFalse(self.ocp.is_ocp_query("tell me a joke", "en-us")[0])
self.assertFalse(self.ocp.is_ocp_query("who are you", "en-us")[0])
self.assertFalse(self.ocp.is_ocp_query("you suck", "en-us")[0])

def test_predict_prob(self):
noise = "hglisjerhksrtjhdgsf"
self.assertEqual(self.ocp.classify_media(f"play {noise} music", "en-us")[0], MediaType.MUSIC)
self.assertIsInstance(self.ocp.classify_media(f"play music {noise}", "en-us")[1], float)
self.assertEqual(self.ocp.classify_media(f"play {noise} movie soundtrack", "en-us")[0], MediaType.MUSIC)
self.assertEqual(self.ocp.classify_media(f"play movie {noise}", "en-us")[0], MediaType.MOVIE)
self.assertEqual(self.ocp.classify_media(f"play silent {noise} movie", "en-us")[0], MediaType.SILENT_MOVIE)
self.assertEqual(self.ocp.classify_media(f"play {noise} black and white movie", "en-us")[0],
MediaType.BLACK_WHITE_MOVIE)
self.assertEqual(self.ocp.classify_media(f"play short {noise} film", "en-us")[0], MediaType.SHORT_FILM)
self.assertEqual(self.ocp.classify_media(f"play cartoons {noise}", "en-us")[0], MediaType.CARTOON)
self.assertEqual(self.ocp.classify_media(f"play {noise} episode", "en-us")[0], MediaType.VIDEO_EPISODES)
self.assertEqual(self.ocp.classify_media(f"play {noise} podcast", "en-us")[0], MediaType.PODCAST)
self.assertEqual(self.ocp.classify_media(f"play {noise} book", "en-us")[0], MediaType.AUDIOBOOK)
self.assertEqual(self.ocp.classify_media(f"play radio {noise} FM", "en-us")[0], MediaType.RADIO)
self.assertEqual(self.ocp.classify_media(f"read {noise}", "en-us")[0], MediaType.AUDIOBOOK)


class TestOCPPipelineMatcher(unittest.TestCase):

def setUp(self):
config = {
"experimental_media_classifier": True,
"experimental_binary_classifier": True,
"entity_csvs": [
os.path.dirname(ocp_pipeline.opm.__file__) + "/models/ocp_entities_v0.csv"
]}
self.ocp = OCPPipelineMatcher(config=config)

def test_match_high(self):
result = self.ocp.match_high(["play metallica"], "en-us")
self.assertIsNotNone(result)
self.assertEqual(result.intent_service, 'OCP_intents')
self.assertEqual(result.intent_type, 'ocp:play')

def test_match_high_with_invalid_input(self):
result = self.ocp.match_high(["put on some metallica"], "en-us")
self.assertIsNone(result)

def test_match_medium(self):
result = self.ocp.match_medium(["put on some metallica"], "en-us")
self.assertIsNotNone(result)
self.assertEqual(result.intent_service, 'OCP_media')
self.assertEqual(result.intent_type, 'ocp:play')

def test_match_medium_with_invalid_input(self):
result = self.ocp.match_medium(["i wanna hear metallica"], "en-us")
self.assertIsNone(result)

def test_match_fallback(self):
result = self.ocp.match_fallback(["i wanna hear metallica"], "en-us")
print(result)
self.assertIsNotNone(result)
self.assertEqual(result.intent_service, 'OCP_fallback')
self.assertEqual(result.intent_type, 'ocp:play')

def test_match_fallback_with_invalid_input(self):
result = self.ocp.match_fallback(["do the thing"], "en-us")
self.assertIsNone(result)

def test_predict(self):
self.assertTrue(self.ocp.is_ocp_query("play a song", "en-us")[0])
self.assertTrue(self.ocp.is_ocp_query("play my morning jams", "en-us")[0])
self.assertTrue(self.ocp.is_ocp_query("i want to watch the matrix", "en-us")[0])
self.assertFalse(self.ocp.is_ocp_query("tell me a joke", "en-us")[0])
self.assertFalse(self.ocp.is_ocp_query("who are you", "en-us")[0])
self.assertFalse(self.ocp.is_ocp_query("you suck", "en-us")[0])

def test_predict_prob(self):
# "metallica" in csv dataset
self.ocp.config["classifier_threshold"] = 0.2
self.assertEqual(self.ocp.classify_media("play metallica", "en-us")[0], MediaType.MUSIC)
self.assertIsInstance(self.ocp.classify_media("play metallica", "en-us")[1], float)
self.ocp.config["classifier_threshold"] = 0.5
self.assertEqual(self.ocp.classify_media("play metallica", "en-us")[0], MediaType.GENERIC)
self.assertIsInstance(self.ocp.classify_media("play metallica", "en-us")[1], float)

@unittest.skip("TODO - classifiers needs retraining")
def test_predict_prob_with_unknown_entity(self):
# "klownevilus" not in the csv dataset
self.ocp.config["classifier_threshold"] = 0.2
self.assertEqual(self.ocp.classify_media("play klownevilus", "en-us")[0], MediaType.MUSIC)
self.assertIsInstance(self.ocp.classify_media("play klownevilus", "en-us")[1], float)
self.ocp.config["classifier_threshold"] = 0.5
self.assertEqual(self.ocp.classify_media("play klownevilus", "en-us")[0], MediaType.GENERIC)

self.ocp.config["classifier_threshold"] = 0.1
self.ocp.handle_skill_keyword_register(Message("", {
"skill_id": "fake",
"label": "movie_name",
"media_type": MediaType.MOVIE,
"samples": ["klownevilus"]
}))
# should be MOVIE not MUSIC TODO fix me
self.assertEqual(self.ocp.classify_media("play klownevilus", "en-us")[0], MediaType.MOVIE)


if __name__ == '__main__':
unittest.main()
5 changes: 5 additions & 0 deletions translations/it-it/dialogs.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"play.what.dialog": [
"Cosa vuoi ascoltare dopo?"
],
"no.media.skills.dialog": [
"Scusa ma non ho nessuna competenza installata per questa azione",
"Scusa, ma per compiere questa azione devi installare delle competenze media",
"Non posso riprodurre nessun flusso fino a che non installi delle competenze media"
],
"just.one.moment.dialog": [
"Aspetta",
"Un momentino",
Expand Down
5 changes: 5 additions & 0 deletions translations/pt-pt/dialogs.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"play.what.dialog": [
"O que devo (reproduzir|tocar) a seguir?"
],
"no.media.skills.dialog": [
"Desculpe, não tenho nenhuma habilidade de mídia instalada",
"Preciso instalar algumas habilidades de mídia antes de poder reproduzir algo",
"Não consigo reproduzir nada até que algumas habilidades de mídia sejam instaladas"
],
"just.one.moment.dialog": [
"Dê-me um momento",
"Só um segundo",
Expand Down

0 comments on commit 29c1686

Please sign in to comment.