Skip to content

Commit 70ac27b

Browse files
committed
Use GAPIC with sync_recognize().
1 parent 7acd247 commit 70ac27b

4 files changed

Lines changed: 148 additions & 31 deletions

File tree

docs/speech-usage.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ Great Britian.
9191
... language_code='en-GB', max_alternatives=2)
9292
>>> for alternative in alternatives:
9393
... print('=' * 20)
94-
... print('transcript: ' + alternative['transcript'])
95-
... print('confidence: ' + alternative['confidence'])
94+
... print('transcript: ' + alternative.transcript)
95+
... print('confidence: ' + alternative.confidence)
9696
====================
9797
transcript: Hello, this is a test
9898
confidence: 0.81
@@ -113,8 +113,8 @@ Example of using the profanity filter.
113113
... profanity_filter=True)
114114
>>> for alternative in alternatives:
115115
... print('=' * 20)
116-
... print('transcript: ' + alternative['transcript'])
117-
... print('confidence: ' + alternative['confidence'])
116+
... print('transcript: ' + alternative.transcript)
117+
... print('confidence: ' + alternative.confidence)
118118
====================
119119
transcript: Hello, this is a f****** test
120120
confidence: 0.81
@@ -135,8 +135,8 @@ words to the vocabulary of the recognizer.
135135
... speech_context=hints)
136136
>>> for alternative in alternatives:
137137
... print('=' * 20)
138-
... print('transcript: ' + alternative['transcript'])
139-
... print('confidence: ' + alternative['confidence'])
138+
... print('transcript: ' + alternative.transcript)
139+
... print('confidence: ' + alternative.confidence)
140140
====================
141141
transcript: Hello, this is a test
142142
confidence: 0.81

speech/google/cloud/speech/client.py

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,17 @@
2525
from google.cloud.speech.encoding import Encoding
2626
from google.cloud.speech.operation import Operation
2727
from google.cloud.speech.sample import Sample
28+
from google.cloud.speech.transcript import Transcript
2829
from google.cloud.speech.streaming_response import StreamingSpeechResponse
2930

3031
try:
3132
from google.cloud.gapic.speech.v1beta1.speech_api import SpeechApi
33+
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
34+
SpeechContext)
3235
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
3336
RecognitionConfig)
37+
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
38+
RecognitionAudio)
3439
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
3540
StreamingRecognitionConfig)
3641
from google.cloud.grpc.speech.v1beta1.cloud_speech_pb2 import (
@@ -211,17 +216,21 @@ def sync_recognize(self, sample, language_code=None,
211216
* ``confidence``: The confidence in language detection, float
212217
between 0 and 1.
213218
"""
219+
if _USE_GAX:
220+
config = RecognitionConfig(
221+
encoding=sample.encoding, sample_rate=sample.sample_rate,
222+
language_code=language_code, max_alternatives=max_alternatives,
223+
profanity_filter=profanity_filter,
224+
speech_context=SpeechContext(phrases=speech_context))
214225

215-
data = _build_request_data(sample, language_code, max_alternatives,
216-
profanity_filter, speech_context)
217-
218-
api_response = self.connection.api_request(
219-
method='POST', path='speech:syncrecognize', data=data)
226+
audio = RecognitionAudio(content=sample.content,
227+
uri=sample.source_uri)
220228

221-
if len(api_response['results']) == 1:
222-
return api_response['results'][0]['alternatives']
229+
return self._sync_recognize(config, audio)
223230
else:
224-
raise ValueError('result in api should have length 1')
231+
data = _build_request_data(sample, language_code, max_alternatives,
232+
profanity_filter, speech_context)
233+
return self._sync_recognize(data=data)
225234

226235
def stream_recognize(self, sample, language_code=None,
227236
max_alternatives=None, profanity_filter=None,
@@ -315,6 +324,37 @@ def speech_api(self):
315324
self._speech_api = SpeechApi()
316325
return self._speech_api
317326

327+
def _sync_recognize(self, config=None, audio=None, data=None):
328+
"""Handler for sync_recognize requests with or without GAPIC.
329+
330+
:type config: :class:`~RecognitionConfig
331+
:param config: Instance of ``RecognitionConfig`` with recognition
332+
settings.
333+
334+
:type audio: :class:`~RecognitionAudio`
335+
:param audio: Instance of ``RecognitionAudio`` with audio source data.
336+
337+
:type data: dict
338+
:param data: Mapped configuration paramters for the request.
339+
340+
:rtype: list of :class:`~transcript.Transcript`
341+
:returns: List of ``Transcript`` with recognition results.
342+
"""
343+
if config and audio and _USE_GAX:
344+
api_response = self.speech_api.sync_recognize(config=config,
345+
audio=audio)
346+
results = api_response.results.pop()
347+
alternatives = results.alternatives
348+
return [Transcript.from_pb(alternative)
349+
for alternative in alternatives]
350+
elif data:
351+
api_response = self.connection.api_request(
352+
method='POST', path='speech:syncrecognize', data=data)
353+
354+
return [Transcript.from_api_repr(alternative)
355+
for alternative
356+
in api_response['results'][0]['alternatives']]
357+
318358

319359
def _build_request_data(sample, language_code=None, max_alternatives=None,
320360
profanity_filter=None, speech_context=None):

speech/google/cloud/speech/transcript.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,22 @@ def __init__(self, transcript, confidence):
2828
self._transcript = transcript
2929
self._confidence = confidence
3030

31+
@classmethod
32+
def from_api_repr(cls, transcript):
33+
"""Factory: construct ``Transcript`` from JSON response.
34+
35+
:type transcript: :class:`~SpeechRecognitionAlternative`
36+
:param transcript: Instance of ``SpeechRecognitionAlternative``
37+
from protobuf.
38+
39+
:rtype: :class:`~Transcript`
40+
:returns: Instance of ``Transcript``.
41+
"""
42+
return cls(transcript['transcript'], transcript['confidence'])
43+
3144
@classmethod
3245
def from_pb(cls, transcript):
33-
"""Factory: construct ``Transcript`` from protobuf response
46+
"""Factory: construct ``Transcript`` from protobuf response.
3447
3548
:type transcript: :class:`~SpeechRecognitionAlternative`
3649
:param transcript: Instance of ``SpeechRecognitionAlternative``

speech/unit_tests/test_client.py

Lines changed: 80 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,11 @@ def test_sync_recognize_content_with_optional_parameters(self):
6565
from google.cloud._helpers import _to_bytes
6666
from google.cloud._helpers import _bytes_to_unicode
6767

68+
from google.cloud._testing import _Monkey
69+
from google.cloud.speech import client as MUT
6870
from google.cloud.speech.encoding import Encoding
6971
from google.cloud.speech.sample import Sample
72+
from google.cloud.speech.transcript import Transcript
7073
from unit_tests._fixtures import SYNC_RECOGNIZE_RESPONSE
7174
_AUDIO_CONTENT = _to_bytes(self.AUDIO_CONTENT)
7275
_B64_AUDIO_CONTENT = _bytes_to_unicode(b64encode(_AUDIO_CONTENT))
@@ -96,25 +99,30 @@ def test_sync_recognize_content_with_optional_parameters(self):
9699

97100
sample = Sample(content=self.AUDIO_CONTENT, encoding=encoding,
98101
sample_rate=self.SAMPLE_RATE)
99-
response = client.sync_recognize(sample,
100-
language_code='EN',
101-
max_alternatives=2,
102-
profanity_filter=True,
103-
speech_context=self.HINTS)
102+
with _Monkey(MUT, _USE_GAX=False):
103+
response = client.sync_recognize(sample,
104+
language_code='EN',
105+
max_alternatives=2,
106+
profanity_filter=True,
107+
speech_context=self.HINTS)
104108

105109
self.assertEqual(len(client.connection._requested), 1)
106110
req = client.connection._requested[0]
107111
self.assertEqual(len(req), 3)
108112
self.assertEqual(req['data'], REQUEST)
109113
self.assertEqual(req['method'], 'POST')
110114
self.assertEqual(req['path'], 'speech:syncrecognize')
111-
112-
expected = SYNC_RECOGNIZE_RESPONSE['results'][0]['alternatives']
113-
self.assertEqual(response, expected)
115+
alternative = SYNC_RECOGNIZE_RESPONSE['results'][0]['alternatives'][0]
116+
expected = [Transcript.from_api_repr(alternative)]
117+
self.assertEqual(response[0].transcript, expected[0].transcript)
118+
self.assertEqual(response[0].confidence, expected[0].confidence)
114119

115120
def test_sync_recognize_source_uri_without_optional_parameters(self):
121+
from google.cloud._testing import _Monkey
122+
from google.cloud.speech import client as MUT
116123
from google.cloud.speech.encoding import Encoding
117124
from google.cloud.speech.sample import Sample
125+
from google.cloud.speech.transcript import Transcript
118126
from unit_tests._fixtures import SYNC_RECOGNIZE_RESPONSE
119127

120128
RETURNED = SYNC_RECOGNIZE_RESPONSE
@@ -135,7 +143,8 @@ def test_sync_recognize_source_uri_without_optional_parameters(self):
135143

136144
sample = Sample(source_uri=self.AUDIO_SOURCE_URI, encoding=encoding,
137145
sample_rate=self.SAMPLE_RATE)
138-
response = client.sync_recognize(sample)
146+
with _Monkey(MUT, _USE_GAX=False):
147+
response = client.sync_recognize(sample)
139148

140149
self.assertEqual(len(client.connection._requested), 1)
141150
req = client.connection._requested[0]
@@ -144,10 +153,14 @@ def test_sync_recognize_source_uri_without_optional_parameters(self):
144153
self.assertEqual(req['method'], 'POST')
145154
self.assertEqual(req['path'], 'speech:syncrecognize')
146155

147-
expected = SYNC_RECOGNIZE_RESPONSE['results'][0]['alternatives']
148-
self.assertEqual(response, expected)
156+
expected = [Transcript.from_api_repr(
157+
SYNC_RECOGNIZE_RESPONSE['results'][0]['alternatives'][0])]
158+
self.assertEqual(response[0].transcript, expected[0].transcript)
159+
self.assertEqual(response[0].confidence, expected[0].confidence)
149160

150161
def test_sync_recognize_with_empty_results(self):
162+
from google.cloud._testing import _Monkey
163+
from google.cloud.speech import client as MUT
151164
from google.cloud.speech.encoding import Encoding
152165
from google.cloud.speech.sample import Sample
153166
from unit_tests._fixtures import SYNC_RECOGNIZE_EMPTY_RESPONSE
@@ -156,11 +169,32 @@ def test_sync_recognize_with_empty_results(self):
156169
client = self._makeOne(credentials=credentials)
157170
client.connection = _Connection(SYNC_RECOGNIZE_EMPTY_RESPONSE)
158171

159-
with self.assertRaises(ValueError):
160-
sample = Sample(source_uri=self.AUDIO_SOURCE_URI,
161-
encoding=Encoding.FLAC,
162-
sample_rate=self.SAMPLE_RATE)
163-
client.sync_recognize(sample)
172+
with self.assertRaises(IndexError):
173+
with _Monkey(MUT, _USE_GAX=False):
174+
sample = Sample(source_uri=self.AUDIO_SOURCE_URI,
175+
encoding=Encoding.FLAC,
176+
sample_rate=self.SAMPLE_RATE)
177+
client.sync_recognize(sample)
178+
179+
def test_sync_recognize_with_gapic(self):
180+
from google.cloud.speech import client as MUT
181+
from google.cloud.speech import Encoding
182+
from google.cloud._testing import _Monkey
183+
creds = _Credentials()
184+
client = self._makeOne(credentials=creds)
185+
client.connection = _Connection()
186+
187+
client._speech_api = _MockGAPICSpeechAPI()
188+
client._speech_api._responses = []
189+
190+
with _Monkey(MUT, _USE_GAX=True, RecognitionConfig=_RecognitionConfig,
191+
RecognitionAudio=_RecognitionAudio):
192+
sample = client.sample(source_uri=self.AUDIO_SOURCE_URI,
193+
encoding=Encoding.FLAC,
194+
sample_rate=self.SAMPLE_RATE)
195+
results = client.sync_recognize(sample)
196+
self.assertEqual(results[0].transcript, 'testing 1 2 3')
197+
self.assertEqual(results[0].confidence, 0.95234356)
164198

165199
def test_async_supported_encodings(self):
166200
from google.cloud.speech.encoding import Encoding
@@ -260,6 +294,31 @@ def test_stream_recognize(self):
260294
self.assertEqual(len(requests), 2)
261295

262296

297+
class _RecognitionConfig(object):
298+
def __init__(self, *args, **kwargs):
299+
self.args = args
300+
self.kwargs = kwargs
301+
302+
303+
class _RecognitionAudio(object):
304+
def __init__(self, content, uri):
305+
self.content = content
306+
self.uri = uri
307+
308+
309+
class _MockGAPICAlternative(object):
310+
transcript = 'testing 1 2 3'
311+
confidence = 0.95234356
312+
313+
314+
class _MockGAPICSyncResult(object):
315+
alternatives = [_MockGAPICAlternative()]
316+
317+
318+
class _MockGAPICSyncSpeechResponse(object):
319+
results = [_MockGAPICSyncResult()]
320+
321+
263322
class _MockGAPICSpeechResponse(object):
264323
error = None
265324
endpointer_type = None
@@ -275,6 +334,11 @@ def streaming_recognize(self, requests):
275334
self._requests = requests
276335
return self._responses
277336

337+
def sync_recognize(self, config, audio):
338+
self.config = config
339+
self.audio = audio
340+
return _MockGAPICSyncSpeechResponse()
341+
278342

279343
class _Credentials(object):
280344

0 commit comments

Comments
 (0)