Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue/19 edge adapter #36

Merged
merged 6 commits into from
Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ clean:
rm -rf docs/_build

formatter:
isort -rc -q david tests
black david tests

lint:
Expand Down
22 changes: 19 additions & 3 deletions david/__main__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from flask import Flask, jsonify, request
from flask import Flask, abort, jsonify, make_response, request
from flask_cors import CORS

from david.assistant import Assistant
from david.dialog import fetch_dialog
from david.googleadap import GoogleWebHook
from david.registry import Registry

# from david.brain import fetch_model, fetch_know

Expand All @@ -13,6 +14,8 @@
assistant = Assistant()
googleWH = GoogleWebHook(assistant)

registry = Registry.get_instance()


@app.route("/")
def hi():
Expand All @@ -27,8 +30,21 @@ def train():

@app.route("/dialog", methods=["POST"])
def dialog():
data = request.get_json()
return jsonify(assistant.respond(data["input"]).__dict__)
requestData = request.get_json()

adapterName = request.args.get("adapter")
adapter = registry.getAdapter(adapterName)

if not adapter:
abort(400, "Invalid adapter")

if not adapter.validade_data(requestData):
abort(400, "Invalid input")

messageIn = adapter.input(requestData)
messageOut = assistant.respond(messageIn)
responseData = adapter.output(messageOut)
return jsonify(responseData)


@app.route("/google", methods=["POST"])
Expand Down
36 changes: 36 additions & 0 deletions david/adapters/adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import json
from typing import Dict, List, Text

from david.typing import Message


class Adapter:
@property
def name(self):
"""The name property is a function of the class - its __name__."""

return self.__class__.__name__

def validade_data(self, payload: Dict) -> bool:
raise NotImplementedError

def input(self, payload: Dict) -> Message:
raise NotImplementedError

def output(self, message: Message) -> Dict:
raise NotImplementedError


class MessageAdapter(Adapter):
@property
def name(self):
return "message"

def validade_data(self, payload: Dict) -> bool:
return "input" in payload and "text" in payload["input"]

def input(self, payload: Dict) -> Message:
return Message.build(payload=payload)

def output(self, message: Message) -> Dict:
return message.__dict__
13 changes: 10 additions & 3 deletions david/assistant.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from david.brain import Brain
from david.constants import CONTEXT_ATTRIBUTE, ENTITIES_ATTRIBUTE, INTENTS_ATTRIBUTE
from david.constants import (
CONTEXT_ATTRIBUTE,
ENTITIES_ATTRIBUTE,
INTENTS_ATTRIBUTE,
TEXT_ATTRIBUTE,
)
from david.dialog import Dialog


Expand All @@ -13,8 +18,10 @@ def train(self):
self.brain.train()
self.dialog.train()

def respond(self, input, context={}):
message = self.brain.process(input)
def respond(self, message, context={}):
text = message.get(TEXT_ATTRIBUTE)

message = self.brain.process(text)

intents = message.get(INTENTS_ATTRIBUTE)
entities = message.get(ENTITIES_ATTRIBUTE)
Expand Down
4 changes: 2 additions & 2 deletions david/components/nlu/simplenlu.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import david.util as util
from david.components import Component
from david.config import DavidConfig
from david.constants import INTENTS_ATTRIBUTE
from david.constants import INTENTS_ATTRIBUTE, TEXT_ATTRIBUTE
from david.typing import Message, TrainingData
from david.typing.model import Metadata

Expand Down Expand Up @@ -91,7 +91,7 @@ def persist(self, file_name: Text, model_dir: Text) -> Optional[Dict[Text, Any]]

def process(self, message: Message, **kwargs: Any) -> None:

input = message.text
input = message.get(TEXT_ATTRIBUTE)

tokens = util.tokenize(input)
# print ("tokens", tokens)
Expand Down
38 changes: 38 additions & 0 deletions david/registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from david.adapters.adapter import Adapter, MessageAdapter


# [TODO] refactory as generic registry
class Registry:

_instance = None

adapters = {}

def __init__(self, defaultAdapter=None):

messageAdapter = MessageAdapter()
self.registryAdapter(messageAdapter)

if defaultAdapter:
self.defaultAdapter = defaultAdapter
else:
self.defaultAdapter = messageAdapter.name

@classmethod
def get_instance(cls):
if not cls._instance:
cls._instance = Registry()
return cls._instance

def registryAdapter(self, adapter: Adapter):
self.adapters[adapter.name] = adapter

def getAdapter(self, adapterName=None):

if not adapterName:
adapterName = self.defaultAdapter

if adapterName in self.adapters:
return self.adapters[adapterName]

return
16 changes: 13 additions & 3 deletions david/typing/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

class Message:
def __init__(self, text: Text, context=None, data={}, time=None):
self.text = text
self.input = {}
self.input["text"] = text
self.context = context
self.time = time
self.data = data
Expand All @@ -26,19 +27,28 @@ def set(self, prop, info, add_to_output=False) -> None:

def get(self, prop, default=None) -> Any:
if prop == TEXT_ATTRIBUTE:
return self.text
return self.input["text"]
return self.data.get(prop, default)

@classmethod
def build(cls, text, intents=None, entities=None, context=None) -> "Message":
def build(
cls, text=None, intents=None, entities=None, context=None, payload=None
) -> "Message":
data = {}

if intents:
# split_intent, response_key = cls.separate_intent_response_key(intent)
data[INTENTS_ATTRIBUTE] = intents
# if response_key:
# data[RESPONSE_KEY_ATTRIBUTE] = response_key

if entities:
data[ENTITIES_ATTRIBUTE] = entities

if context:
data[CONTEXT_ATTRIBUTE] = context

if not text:
text = payload["input"]["text"]

return cls(text, data)