27
27
import google .auth .transport .requests
28
28
import google .oauth2 .credentials
29
29
30
- from google .assistant .embedded .v1alpha1 import (
30
+ from google .assistant .embedded .v1alpha2 import (
31
31
embedded_assistant_pb2 ,
32
32
embedded_assistant_pb2_grpc
33
33
)
34
- from google .rpc import code_pb2
35
34
from tenacity import retry , stop_after_attempt , retry_if_exception
36
35
37
36
try :
47
46
48
47
49
48
ASSISTANT_API_ENDPOINT = 'embeddedassistant.googleapis.com'
50
- END_OF_UTTERANCE = embedded_assistant_pb2 .ConverseResponse .END_OF_UTTERANCE
51
- DIALOG_FOLLOW_ON = embedded_assistant_pb2 .ConverseResult .DIALOG_FOLLOW_ON
52
- CLOSE_MICROPHONE = embedded_assistant_pb2 .ConverseResult .CLOSE_MICROPHONE
49
+ END_OF_UTTERANCE = embedded_assistant_pb2 .AssistResponse .END_OF_UTTERANCE
50
+ DIALOG_FOLLOW_ON = embedded_assistant_pb2 .DialogStateOut .DIALOG_FOLLOW_ON
51
+ CLOSE_MICROPHONE = embedded_assistant_pb2 .DialogStateOut .CLOSE_MICROPHONE
53
52
DEFAULT_GRPC_DEADLINE = 60 * 3 + 5
54
53
55
54
@@ -67,16 +66,18 @@ class SampleAssistant(object):
67
66
device_handler: callback for device actions.
68
67
"""
69
68
70
- def __init__ (self , device_model_id , device_id , conversation_stream ,
69
+ def __init__ (self , language_code , device_model_id , device_id ,
70
+ conversation_stream ,
71
71
channel , deadline_sec , device_handler ):
72
+ self .language_code = language_code
72
73
self .device_model_id = device_model_id
73
74
self .device_id = device_id
74
75
self .conversation_stream = conversation_stream
75
76
76
- # Opaque blob provided in ConverseResponse that,
77
- # when provided in a follow-up ConverseRequest ,
77
+ # Opaque blob provided in AssistResponse that,
78
+ # when provided in a follow-up AssistRequest ,
78
79
# gives the Assistant a context marker within the current state
79
- # of the multi-Converse ()-RPC "conversation".
80
+ # of the multi-Assist ()-RPC "conversation".
80
81
# This value, along with MicrophoneMode, supports a more natural
81
82
# "conversation" with the Assistant.
82
83
self .conversation_state = None
@@ -106,7 +107,7 @@ def is_grpc_error_unavailable(e):
106
107
107
108
@retry (reraise = True , stop = stop_after_attempt (3 ),
108
109
retry = retry_if_exception (is_grpc_error_unavailable ))
109
- def converse (self ):
110
+ def assist (self ):
110
111
"""Send a voice request to the Assistant and playback the response.
111
112
112
113
Returns: True if conversation should continue.
@@ -117,46 +118,39 @@ def converse(self):
117
118
self .conversation_stream .start_recording ()
118
119
logging .info ('Recording audio request.' )
119
120
120
- def iter_converse_requests ():
121
- for c in self .gen_converse_requests ():
122
- assistant_helpers .log_converse_request_without_audio (c )
121
+ def iter_assist_requests ():
122
+ for c in self .gen_assist_requests ():
123
+ assistant_helpers .log_assist_request_without_audio (c )
123
124
yield c
124
125
self .conversation_stream .start_playback ()
125
126
126
- # This generator yields ConverseResponse proto messages
127
+ # This generator yields AssistResponse proto messages
127
128
# received from the gRPC Google Assistant API.
128
- for resp in self .assistant .Converse (iter_converse_requests (),
129
- self .deadline ):
130
- assistant_helpers .log_converse_response_without_audio (resp )
131
- if resp .error .code != code_pb2 .OK :
132
- logging .error ('server error: %s' , resp .error .message )
133
- break
129
+ for resp in self .assistant .Assist (iter_assist_requests (),
130
+ self .deadline ):
131
+ assistant_helpers .log_assist_response_without_audio (resp )
134
132
if resp .event_type == END_OF_UTTERANCE :
135
133
logging .info ('End of audio request detected' )
136
134
self .conversation_stream .stop_recording ()
137
- if resp .result . spoken_request_text :
135
+ if resp .speech_results :
138
136
logging .info ('Transcript of user request: "%s".' ,
139
- resp .result .spoken_request_text )
137
+ ' ' .join (r .transcript
138
+ for r in resp .speech_results ))
140
139
logging .info ('Playing assistant response.' )
141
140
if len (resp .audio_out .audio_data ) > 0 :
142
141
self .conversation_stream .write (resp .audio_out .audio_data )
143
- if resp .result .spoken_response_text :
144
- logging .info (
145
- 'Transcript of TTS response '
146
- '(only populated from IFTTT): "%s".' ,
147
- resp .result .spoken_response_text )
148
- if resp .result .conversation_state :
149
- self .conversation_state = resp .result .conversation_state
150
- if resp .result .volume_percentage != 0 :
151
- logging .info ('Setting volume to %s%%' ,
152
- resp .result .volume_percentage )
153
- self .conversation_stream .volume_percentage = (
154
- resp .result .volume_percentage
155
- )
156
- if resp .result .microphone_mode == DIALOG_FOLLOW_ON :
142
+ if resp .dialog_state_out .conversation_state :
143
+ conversation_state = resp .dialog_state_out .conversation_state
144
+ logging .debug ('Updating conversation state.' )
145
+ self .conversation_state = conversation_state
146
+ if resp .dialog_state_out .volume_percentage != 0 :
147
+ volume_percentage = resp .dialog_state_out .volume_percentage
148
+ logging .info ('Setting volume to %s%%' , volume_percentage )
149
+ self .conversation_stream .volume_percentage = volume_percentage
150
+ if resp .dialog_state_out .microphone_mode == DIALOG_FOLLOW_ON :
157
151
continue_conversation = True
158
152
logging .info ('Expecting follow-on query from user.' )
159
- elif resp .result .microphone_mode == CLOSE_MICROPHONE :
153
+ elif resp .dialog_state_out .microphone_mode == CLOSE_MICROPHONE :
160
154
continue_conversation = False
161
155
if resp .device_action .device_request_json :
162
156
device_request = json .loads (
@@ -174,17 +168,17 @@ def iter_converse_requests():
174
168
self .conversation_stream .stop_playback ()
175
169
return continue_conversation
176
170
177
- def gen_converse_requests (self ):
178
- """Yields: ConverseRequest messages to send to the API."""
171
+ def gen_assist_requests (self ):
172
+ """Yields: AssistRequest messages to send to the API."""
179
173
180
- converse_state = None
181
- if self .conversation_state :
182
- logging .debug ('Sending converse_state: %s' ,
183
- self .conversation_state )
184
- converse_state = embedded_assistant_pb2 .ConverseState (
185
- conversation_state = self .conversation_state ,
174
+ dialog_state_in = embedded_assistant_pb2 .DialogStateIn (
175
+ language_code = self .language_code ,
176
+ conversation_state = b''
186
177
)
187
- config = embedded_assistant_pb2 .ConverseConfig (
178
+ if self .conversation_state :
179
+ logging .debug ('Sending conversation state.' )
180
+ dialog_state_in .conversation_state = self .conversation_state
181
+ config = embedded_assistant_pb2 .AssistConfig (
188
182
audio_in_config = embedded_assistant_pb2 .AudioInConfig (
189
183
encoding = 'LINEAR16' ,
190
184
sample_rate_hertz = self .conversation_stream .sample_rate ,
@@ -194,18 +188,18 @@ def gen_converse_requests(self):
194
188
sample_rate_hertz = self .conversation_stream .sample_rate ,
195
189
volume_percentage = self .conversation_stream .volume_percentage ,
196
190
),
197
- converse_state = converse_state ,
191
+ dialog_state_in = dialog_state_in ,
198
192
device_config = embedded_assistant_pb2 .DeviceConfig (
199
- device_model_id = self .device_model_id ,
200
193
device_id = self .device_id ,
194
+ device_model_id = self .device_model_id ,
201
195
)
202
196
)
203
- # The first ConverseRequest must contain the ConverseConfig
197
+ # The first AssistRequest must contain the AssistConfig
204
198
# and no audio data.
205
- yield embedded_assistant_pb2 .ConverseRequest (config = config )
199
+ yield embedded_assistant_pb2 .AssistRequest (config = config )
206
200
for data in self .conversation_stream :
207
201
# Subsequent requests need audio data, but not config.
208
- yield embedded_assistant_pb2 .ConverseRequest (audio_in = data )
202
+ yield embedded_assistant_pb2 .AssistRequest (audio_in = data )
209
203
210
204
211
205
@click .command ()
@@ -237,6 +231,10 @@ def gen_converse_requests(self):
237
231
click .get_app_dir ('googlesamples-assistant' ),
238
232
'device_config.json' ),
239
233
help = 'Path to save and restore the device configuration' )
234
+ @click .option ('--lang' , show_default = True ,
235
+ metavar = '<language code>' ,
236
+ default = 'en-US' ,
237
+ help = 'Language code of the Assistant' )
240
238
@click .option ('--verbose' , '-v' , is_flag = True , default = False ,
241
239
help = 'Verbose logging.' )
242
240
@click .option ('--input-audio-file' , '-i' ,
@@ -275,7 +273,7 @@ def gen_converse_requests(self):
275
273
@click .option ('--once' , default = False , is_flag = True ,
276
274
help = 'Force termination after a single conversation.' )
277
275
def main (api_endpoint , credentials , project ,
278
- device_model_id , device_id , device_config , verbose ,
276
+ device_model_id , device_id , device_config , lang , verbose ,
279
277
input_audio_file , output_audio_file ,
280
278
audio_sample_rate , audio_sample_width ,
281
279
audio_iter_size , audio_block_size , audio_flush_size ,
@@ -398,13 +396,14 @@ def onoff(on):
398
396
with open (device_config , 'w' ) as f :
399
397
json .dump (payload , f )
400
398
401
- with SampleAssistant (device_model_id , device_id , conversation_stream ,
399
+ with SampleAssistant (lang , device_model_id , device_id ,
400
+ conversation_stream ,
402
401
grpc_channel , grpc_deadline ,
403
402
device_handler ) as assistant :
404
403
# If file arguments are supplied:
405
404
# exit after the first turn of the conversation.
406
405
if input_audio_file or output_audio_file :
407
- assistant .converse ()
406
+ assistant .assist ()
408
407
return
409
408
410
409
# If no file arguments supplied:
@@ -415,7 +414,7 @@ def onoff(on):
415
414
while True :
416
415
if wait_for_user_trigger :
417
416
click .pause (info = 'Press Enter to send a new request...' )
418
- continue_conversation = assistant .converse ()
417
+ continue_conversation = assistant .assist ()
419
418
# wait for user trigger if there is no follow-up turn in
420
419
# the conversation.
421
420
wait_for_user_trigger = not continue_conversation
0 commit comments