-
Notifications
You must be signed in to change notification settings - Fork 321
provide event driven interface on top of google-assistant-grpc #358
Comments
Copying from #356 I would also consider the support for custom event callbacks/hooks. Ideally, I see two main usage patterns for the assistant: a synchronous one, where a generator approach works the best: with Assistant(**opts) as assistant:
for event in assistant:
print(event) and an asynchronous one, where the handlers might be executed in other threads, and in this kind of approach the callback pattern can really help keeping the code clean: with Assistant(on_conversation_start=on_conversation_start,
on_speech_recognized=on_speech_recognized,
..., **opts) as assistant:
# Do other stuff |
wouldn't the handlers need to dispatch the call the thread anyway using something like https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor.submit @blacklight curious about your thoughts about of callbacks vs inheritance + override? Another possibility could be have a
That could also enable us to unify the event handler with device actions and custom device actions:
What do you think? |
In my opinion the callback mechanism allows to handle interactions in a more flexible way than a synchronous class Assistant(threading.Thread):
def __init__(self, event_handler, *args, **kwargs):
self.event_handler = event_handler
def run():
while True:
event = self._poll_event()
if self.event_handler:
self.event_handler(assistant, event)
class MyClient:
def __init__(*args, **kwargs):
self.assistant = Assistant(event_handler=self.event_handler())
self.assistant.start()
def event_handler(self):
def wrapped_handler(assistant, event):
print(event)
return wrapped_handler With this pattern I can execute the event handler within the assistant thread, but the handler itself is within Thinking of it, however, I agree that in Python it's possible to implement better patterns than a callback approach borrowed from JS. I like the inheritance+override approach, I've also initially proposed it in #356. It feels a bit Java-ish to me though, as it would require the developer to define a new class and override all the needed handlers. Not that it's a bad thing, but it would make a quick-start a bit more verbose. Thinking of it I like the decorator approach the most, and it would indeed make things consistent with what's done with the |
It would be also nice to start audit project that uses the A few I'm aware of:
Filed #381 as it seems useful to audit further reverse dependencies of the project. |
Something else to consider would be a drastically higher level interface, with top-level module function that allow for a REPL-like experience:
|
I like the idea of using sounddevice to handle the audio, it also decouples audio management from the SDK. If we only use one method for all the interactions however I'm not sure of what the return type should be - maybe an It'd also still maintain the ability to optionally subscribe to assistant events, as in some applications it's neater to have some kind of event-based interface: from google import Assistant
import sounddevice as sd
def handler(assistant, event):
print('Received assistant event: {}'.format(event))
assistant = Assistant(event_handler=handler)
while True:
try:
assistant.assist(sd.rec(10))
except InterruptedError:
break |
Yep, we currently use sounddevice in
Python itself should be fine w/ a single method that returns different type, and the convention should be easy to understand: if you give a
Would the ability of passing raw |
I'm thinking of use case where you initialize the assistant "in the background", do other business logic (listen on a message queue, provide a web service etc.), and you only get notified on a custom event handler when something happens - where something can be a hotword event, a request event, a response event, a device event, an end of conversation event, etc. Of course wrapping the interface you proposed into a separate thread and implementing a basic pub/sub mechanism in Python would also be possible within a couple of lines of code, but I'd be nice if the new SDK also provided an event-based interface so the developers who prefer this pattern for the assistant don't have to reinvent the wheel (just my two cents). |
I wonder if this could composed wel with the synchronous style proposed in #358 (comment), where the caller would be responsible for triggering the assistant and handle the response as the return value. Maybe by combining it decorator approach proposed in #358 (comment)?
|
As shown by the our documentation https://developers.google.com/assistant/sdk/guides/service/integrate#implement_a_basic_conversation_dialog_with_the_assistant and the rather complicated https://github.com/googlesamples/assistant-sdk-python/blob/84995692f35be8e085de8dfa7032039a13ae3fab/google-assistant-sdk/googlesamples/assistant/grpc/pushtotalk.py example, creating a functional assistant on top of the generated gRPC bindings is not trivial, it currently requires developer to:
AssistRequest
messageEND_OF_CONVERSATION
event to stop audio captureAssistResponse
message and playback incoming assistant audio samplesSimpler example like https://github.com/googlesamples/assistant-sdk-python/blob/84995692f35be8e085de8dfa7032039a13ae3fab/google-assistant-sdk/googlesamples/assistant/grpc/audiofileinput.py do a better job at showing low-level gRPC integration into a Python application, but they only handle a simpler conversation turn.
As suggested in #356, developer could benefit from a event based interface on on top of google-assistant-grpc, such wrapper should:
google-assistant-library
One (pythonic) way to implement an event interface could be to do similar to the
google-assistant-library
, where event are surfaced using python generator, for example:That would have the added value of providing some level of familiarity to former users of the
google-assistant-library
The text was updated successfully, but these errors were encountered: