Skip to content

Commit 0d840bb

Browse files
committed
Make it run in production.
1 parent 054b77c commit 0d840bb

File tree

4 files changed

+64
-39
lines changed

4 files changed

+64
-39
lines changed

Procfile

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: gunicorn -k gevent -b 0.0.0.0:$PORT asr:app

asr/__init__.py

+43-39
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1+
import gevent.monkey
2+
gevent.monkey.patch_all()
3+
import base64
14
from email.mime.multipart import MIMEMultipart
25
from email.message import Message
36
import json
47
import struct
8+
import os
59

6-
from flask import Flask, request, Response
7-
from google.cloud import speech
8-
10+
import requests
11+
from flask import Flask, request, Response, abort
912

1013
app = Flask(__name__)
1114

15+
AUTH_URL = "https://auth.rebble.io"
16+
API_KEY = os.environ['SPEECH_API_KEY']
17+
1218

1319
# We know gunicorn does this, but it doesn't *say* it does this, so we must signal it manually.
1420
@app.before_request
@@ -18,7 +24,6 @@ def handle_chunking():
1824

1925
def parse_chunks(stream):
2026
boundary = b'--' + request.headers['content-type'].split(';')[1].split('=')[1].encode('utf-8').strip() # super lazy/brittle parsing.
21-
print("Boundary: " + boundary.decode('utf-8'))
2227
this_frame = b''
2328
while True:
2429
content = stream.read(4096)
@@ -28,52 +33,52 @@ def parse_chunks(stream):
2833
frame = this_frame[:end]
2934
if frame != b'':
3035
header, content = frame.split(b'\r\n\r\n', 1)
31-
print(content)
3236
yield content[:-2]
3337
this_frame = this_frame[end + len(boundary):]
3438
if content == b'':
3539
print("End of input.")
3640
break
3741

3842

39-
def parse_data():
40-
boundary = b'--' + request.headers['content-type'].split(';')[1].split('=')[1].encode('utf-8').strip() # super lazy/brittle parsing.
41-
parts = request.data.split(boundary)
42-
for part in parts:
43-
if part == b'':
44-
continue
45-
yield part.split(b'\r\n\r\n', 1)[1][:-2]
46-
47-
4843
@app.route('/NmspServlet/', methods=["POST"])
4944
def recognise():
50-
51-
client = speech.SpeechClient()
5245
stream = request.stream
46+
47+
access_token, part1, part2 = request.host.split('.', 1)[0].split('-', 3)
48+
lang = f"{part1}-{part2.upper()}"
49+
50+
auth_req = requests.get(f"{AUTH_URL}/api/v1/me/token", headers={'Authorization': f"Bearer {access_token}"})
51+
if not auth_req.ok:
52+
abort(401)
53+
5354
chunks = iter(list(parse_chunks(stream)))
5455
content = next(chunks).decode('utf-8')
55-
print(content)
56-
57-
config = speech.types.RecognitionConfig(
58-
encoding='SPEEX_WITH_HEADER_BYTE',
59-
language_code='en-US',
60-
sample_rate_hertz=16000,
61-
)
62-
print('beginning request')
63-
responses = client.streaming_recognize(
64-
config=speech.types.StreamingRecognitionConfig(config=config),
65-
requests=(
66-
speech.types.StreamingRecognizeRequest(audio_content=struct.pack('B', len(x)) + x)
67-
for x in chunks))
68-
print('finished request')
56+
57+
body = {
58+
'config': {
59+
'encoding': 'SPEEX_WITH_HEADER_BYTE',
60+
'language_code': lang,
61+
'sample_rate_hertz': 16000,
62+
'max_alternatives': 1,
63+
# 'metadata': {
64+
# 'interaction_type': 'DICTATION',
65+
# 'microphone_distance': 'NEARFIELD',
66+
# },
67+
},
68+
'audio': {
69+
'content': base64.b64encode(b''.join((struct.pack('B', len(x)) + x for x in chunks))).decode('utf-8'),
70+
},
71+
}
72+
result = requests.post(f'https://speech.googleapis.com/v1/speech:recognize?key={API_KEY}', json=body)
73+
result.raise_for_status()
74+
6975
words = []
70-
for response in responses:
71-
if response.results:
72-
for result in response.results:
73-
words.extend({
74-
'word': x,
75-
'confidence': result.alternatives[0].confidence
76-
} for x in result.alternatives[0].transcript.split(' '))
76+
if 'results' in result.json():
77+
for result in result.json()['results']:
78+
words.extend({
79+
'word': x,
80+
'confidence': result['alternatives'][0]['confidence']
81+
} for x in result['alternatives'][0]['transcript'].split(' '))
7782

7883
# Now for some reason we also need to give back a mime/multipart message...
7984
parts = MIMEMultipart()
@@ -83,6 +88,7 @@ def recognise():
8388
if len(words) > 0:
8489
response_part.add_header('Content-Disposition', 'form-data; name="QueryResult"')
8590
words[0]['word'] += '\\*no-space-before'
91+
words[0]['word'] = words[0]['word'][0].upper() + words[0]['word'][1:]
8692
response_part.set_payload(json.dumps({
8793
'words': [words],
8894
}))
@@ -96,10 +102,8 @@ def recognise():
96102
"Prompt": "Sorry, speech not recognized. Please try again."
97103
}))
98104
parts.attach(response_part)
99-
print(parts.as_string())
100105

101106
response = Response(parts.as_string().split("\n", 3)[3])
102107
response.headers['Content-Type'] = f'multipart/form-data; boundary={parts.get_boundary()}'
103-
response.headers['Connection'] = 'close'
104108
return response
105109

requirements.txt

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
certifi==2018.4.16
2+
chardet==3.0.4
3+
click==6.7
4+
Flask==1.0.2
5+
gevent==1.3.4
6+
greenlet==0.4.13
7+
gunicorn==19.8.1
8+
idna==2.7
9+
itsdangerous==0.24
10+
Jinja2==2.10
11+
MarkupSafe==1.0
12+
pyasn1==0.4.3
13+
pyasn1-modules==0.2.2
14+
pytz==2018.4
15+
requests==2.19.1
16+
rsa==3.4.2
17+
six==1.11.0
18+
urllib3==1.23
19+
Werkzeug==0.14.1

runtime.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
python-3.6.6

0 commit comments

Comments
 (0)