Skip to content

Commit

Permalink
feat:intents (#6)
Browse files Browse the repository at this point in the history
* feat:intents

* feat:intents
  • Loading branch information
JarbasAl authored Nov 13, 2024
1 parent 1015fa3 commit 9a2efb1
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 171 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/sync_tx.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Run script on merge to dev by gitlocalize-app

on:
workflow_dispatch:
push:
branches:
- dev

jobs:
run-script:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2
with:
ref: dev
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
- name: Setup Python
uses: actions/setup-python@v1
with:
python-version: 3.9

- name: Run script if merged by gitlocalize-app[bot]
if: github.event_name == 'push' && github.event.head_commit.author.username == 'gitlocalize-app[bot]'
run: |
python scripts/sync_translations.py
- name: Commit to dev
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Update translations
branch: dev
113 changes: 106 additions & 7 deletions ovos_persona/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
import os
from os.path import dirname
from os.path import join, dirname
from typing import Optional, Dict, List, Union

from ovos_bus_client.client import MessageBusClient
Expand All @@ -9,10 +9,12 @@
from ovos_config.locations import get_xdg_config_save_path
from ovos_plugin_manager.persona import find_persona_plugins
from ovos_plugin_manager.solvers import find_question_solver_plugins
from ovos_plugin_manager.templates.pipeline import PipelineStageMatcher, IntentHandlerMatch, ConfidenceMatcherPipeline
from ovos_plugin_manager.templates.pipeline import PipelineStageConfidenceMatcher, IntentHandlerMatch
from ovos_utils.fakebus import FakeBus
from ovos_utils.lang import standardize_lang_tag, get_language_dir
from ovos_utils.log import LOG
from ovos_workshop.app import OVOSAbstractApplication
from padacioso import IntentContainer

from ovos_persona.solvers import QuestionSolversService

Expand Down Expand Up @@ -46,18 +48,57 @@ def chat(self, messages: list = None, lang: str = None) -> str:
return self.solvers.spoken_answer(prompt, lang)


class PersonaService(PipelineStageMatcher, OVOSAbstractApplication):
class PersonaService(PipelineStageConfidenceMatcher, OVOSAbstractApplication):
intents = ["ask.intent", "summon.intent"]
intent_matchers = {}

def __init__(self, bus: Optional[Union[MessageBusClient, FakeBus]] = None,
config: Optional[Dict] = None):
config = config or Configuration().get("persona", {})
OVOSAbstractApplication.__init__(
self, bus=bus or FakeBus(), skill_id="persona.openvoiceos",
resources_dir=f"{dirname(__file__)}")
PipelineStageMatcher.__init__(self, bus, config)
PipelineStageConfidenceMatcher.__init__(self, bus, config)
self.personas = {}
self.blacklist = self.config.get("persona_blacklist") or []
self.load_personas(self.config.get("personas_path"))
self.active_persona = None
self.add_event('persona:answer', self.handle_persona_answer)
self.add_event('persona:summon', self.handle_persona_summon)
self.add_event('persona:release', self.handle_persona_release)

@classmethod
def load_resource_files(cls):
intents = {}
langs = Configuration().get('secondary_langs', []) + [Configuration().get('lang', "en-US")]
langs = set([standardize_lang_tag(l) for l in langs])
for lang in langs:
intents[lang] = {}
locale_folder = get_language_dir(join(dirname(__file__), "locale"), lang)
if locale_folder is not None:
for f in os.listdir(locale_folder):
path = join(locale_folder, f)
if f in cls.intents:
with open(path) as intent:
samples = intent.read().split("\n")
for idx, s in enumerate(samples):
samples[idx] = s.replace("{{", "{").replace("}}", "}")
intents[lang][f] = samples
return intents

@classmethod
def load_intent_files(cls):
intent_files = cls.load_resource_files()

for lang, intent_data in intent_files.items():
lang = standardize_lang_tag(lang)
cls.intent_matchers[lang] = IntentContainer()
for intent_name in cls.intents:
samples = intent_data.get(intent_name)
if samples:
LOG.debug(f"registering OCP intent: {intent_name}")
cls.intent_matchers[lang].add_intent(
intent_name.replace(".intent", ""), samples)

@property
def default_persona(self) -> Optional[str]:
Expand Down Expand Up @@ -96,15 +137,63 @@ def deregister_persona(self, name):

# Chatbot API
def chatbox_ask(self, prompt: str, persona: Optional[str] = None, lang: Optional[str] = None) -> Optional[str]:
persona = persona or self.default_persona
persona = persona or self.active_persona or self.default_persona
if persona not in self.personas:
LOG.error(f"unknown persona, choose one of {self.personas.keys()}")
return None
messages = [{"role": "user", "content": prompt}]
return self.personas[persona].chat(messages, lang)

def match(self, utterances: List[str], lang: Optional[str] = None, message: Optional[Message] = None) -> Optional[IntentHandlerMatch]:
# Abstract methods
def match_high(self, utterances: List[str], lang: Optional[str] = None,
message: Optional[Message] = None) -> Optional[IntentHandlerMatch]:
"""
Recommended before common query
Args:
utterances (list): list of utterances
lang (string): 4 letter ISO language code
message (Message): message to use to generate reply
Returns:
IntentMatch if handled otherwise None.
"""
match = self.intent_matchers[lang].calc_intent(utterances[0].lower())

if match["name"]:
LOG.info(f"Persona exact match: {match}")
persona = match["entities"].pop("persona")
if match["name"] == "summon":
return IntentHandlerMatch(match_type='persona:summon',
match_data={"persona": persona},
skill_id="persona.openvoiceos",
utterance=utterances[0])
elif match["name"] == "ask":
utterance = match["entities"].pop("query")
ans = self.chatbox_ask(utterance,
lang=lang,
persona=persona)
if ans:
return IntentHandlerMatch(match_type='persona:answer',
match_data={"answer": ans,
"persona": persona},
skill_id="persona.openvoiceos",
utterance=utterances[0])

if self.active_persona and self.voc_match(utterances[0], "Release", lang):
return IntentHandlerMatch(match_type='persona:release',
match_data={"persona": self.active_persona},
skill_id="persona.openvoiceos",
utterance=utterances[0])

def match_medium(self, utterances: List[str], lang: str, message: Message) -> None:
return self.match_high(utterances, lang, message)

def match_low(self, utterances: List[str], lang: Optional[str] = None,
message: Optional[Message] = None) -> Optional[IntentHandlerMatch]:
"""
Recommended before fallback low
Args:
utterances (list): list of utterances
lang (string): 4 letter ISO language code
Expand All @@ -124,13 +213,23 @@ def handle_persona_answer(self, message):
utt = message.data["answer"]
self.speak(utt)

def handle_persona_summon(self, message):
persona = message.data["persona"]
if persona not in self.personas:
self.speak_dialog("unknown_persona")
else:
self.active_persona = persona

def handle_persona_release(self, message):
self.active_persona = None


if __name__ == "__main__":
b = PersonaService(FakeBus(),
config={"personas_path": "/home/miro/PycharmProjects/ovos-persona/personas"})
print(b.personas)

print(b.match(["what is the speed of light"]))
print(b.match_low(["what is the speed of light"]))

# The speed of light has a value of about 300 million meters per second
# The telephone was invented by Alexander Graham Bell
Expand Down
69 changes: 11 additions & 58 deletions ovos_persona/locale/en-us/Release.voc
Original file line number Diff line number Diff line change
@@ -1,61 +1,14 @@
stop talking
shut up
go away
go back to your grave
disable persona
deactivate persona
stop talking
be quiet
end the conversation
go silent
cease communication
pause
terminate
halt
finish
conclude
shush
quiet down
stop chatting
halt the interaction
close the dialogue
end the discussion
wrap it up
cease the conversation
quiet
discontinue
shut your mouth
stop responding
cease your response
hush
stop the chatter
keep quiet
stop communicating
enough talking
go away
(finish|close|cease|exit|end|quit|abort|terminate|discontinue|disengage|disable|deactivate) (chatbot|persona|AI|LLM) activity
(cease|finish|exit|close|end|quit|abort|terminate|discontinue|disengage|disable|deactivate) (the|) (interaction|conversation|discussion)
go (away|back to your grave|dormant|into hibernation)
halt (the|) (interaction|conversation|discussion)
leave me alone
exit the conversation
step back
enough talking
put (chatbot|persona|AI|LLM) to sleep
(release|remove|halt) (chatbot|persona|AI|LLM)
retreat
withdraw
disengage
go back to your grave
return to the abyss
go dormant
go into hibernation
disable persona
deactivate persona
turn off persona
switch off persona
put persona to sleep
remove persona
end persona's activity
release persona
disable chatbot
deactivate chatbot
turn off chatbot
switch off chatbot
put chatbot to sleep
remove chatbot
end chatbot's activity
release chatbot
shut (up|your mouth)
stop (chatting|communicating|responding|talking)
(turn|switch) off (chatbot|persona|AI|LLM)
terminate
55 changes: 8 additions & 47 deletions ovos_persona/locale/en-us/ask.intent
Original file line number Diff line number Diff line change
@@ -1,47 +1,8 @@
ask {name} about {utterance}
what does {name} (say|think) about {utterance}
ask {name} about {utterance}
what does {name} say about {utterance}
what does {name} think about {utterance}
get {name}'s opinion on {utterance}
ask {name} for their thoughts on {utterance}
inquire {name} about {utterance}
find out what {name} thinks about {utterance}
ask {name} their perspective on {utterance}
request {name}'s view on {utterance}
seek {name}'s opinion on {utterance}
question {name} regarding {utterance}
interrogate {name} about {utterance}
discover {name}'s thoughts on {utterance}
probe {name} for their thoughts on {utterance}
ask {name} for insights on {utterance}
pose a question to {name} about {utterance}
query {name} about {utterance}
explore {name}'s viewpoint on {utterance}
inquire of {name} their opinion on {utterance}
ask {name} what they think about {utterance}
request {name}'s perspective on {utterance}
ask {name} for their take on {utterance}
seek {name}'s viewpoint on {utterance}
question {name} on {utterance}
ask {name} for their thoughts regarding {utterance}
ask {name} to share their opinion on {utterance}
find out what {name} says about {utterance}
get {name}'s take on {utterance}
ask {name} for their viewpoint on {utterance}
inquire {name} for their perspective on {utterance}
request {name}'s stance on {utterance}
seek {name}'s thoughts on {utterance}
ask {name} for their reaction to {utterance}
ask {name} for their interpretation of {utterance}
ask {name} what they have to say about {utterance}
ask {name} for their analysis of {utterance}
question {name} for their views on {utterance}
ask {name} for their impression of {utterance}
inquire {name} for their thoughts on {utterance}
ask {name} for their standpoint on {utterance}
request {name}'s feedback on {utterance}
seek {name}'s interpretation of {utterance}
ask {name} for their comment on {utterance}
ask {name} for their understanding of {utterance}
ask {name} for their perception of {utterance}
ask {persona} about {utterance}
ask {persona} for (their|) (insights|analysis|comment|impression|interpretation|perception|reaction|standpoint|take|thoughts|understanding|viewpoint) (on|of) {utterance}
ask {persona} (about|to share|) their (perspective|opinion) (about|of|on) {utterance}
ask {persona} what they (have to say|think) about {utterance}
what does {persona} (think|say) about {utterance}
(query|probe|request|seek|question|ask|inquire|interrogate) {persona} for their views on {utterance}
(query|probe|request|seek|question|ask|inquire|interrogate|pose a question to) {persona} (regarding|about) {utterance}
(query|probe|request|seek|question|ask|inquire|get) {persona} (perspective|opinion|thoughts|viewpoint|interpretation|feedback|stance|view) (of|on) {utterance}
Loading

0 comments on commit 9a2efb1

Please sign in to comment.