Spaces:
Running
Running
Oviya
commited on
Commit
Β·
58309bf
1
Parent(s):
28880f0
fix
Browse files- Findingword.py +32 -33
Findingword.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
| 1 |
import openai
|
| 2 |
from flask import Flask, jsonify, request, send_from_directory, send_file, Blueprint, current_app
|
| 3 |
import os
|
| 4 |
-
from google.cloud import texttospeech
|
| 5 |
from flask_cors import CORS
|
| 6 |
-
import io #
|
| 7 |
|
| 8 |
# Optional (only used in AWS mode)
|
| 9 |
try:
|
|
@@ -17,17 +16,15 @@ except Exception:
|
|
| 17 |
app = Flask(__name__)
|
| 18 |
CORS(app)
|
| 19 |
|
| 20 |
-
# ---
|
| 21 |
finding_bp = Blueprint("findingword", __name__)
|
| 22 |
|
| 23 |
# Directories for video, audio, and transcripts
|
| 24 |
VIDEO_FOLDER = 'static/videos'
|
| 25 |
AUDIO_FOLDER = 'static/audio' # Ensure this folder exists
|
| 26 |
TRANSCRIPT_FOLDER = 'static/transcripts'
|
|
|
|
| 27 |
|
| 28 |
-
# Set your OpenAI API key (left as-is per your request)
|
| 29 |
-
# openai.api_key = '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'
|
| 30 |
-
# openai.api_key = os.getenv("OPENAI_API_KEY", "")
|
| 31 |
# --- OpenAI key handling (same as vocab builder) ---
|
| 32 |
_OPENAI_API_KEY_FALLBACK = os.getenv("OPENAI_API_KEY", "")
|
| 33 |
|
|
@@ -36,13 +33,8 @@ def _ensure_openai_key():
|
|
| 36 |
api_key = (current_app.config.get("OPENAI_API_KEY") if current_app else None) or _OPENAI_API_KEY_FALLBACK
|
| 37 |
if api_key:
|
| 38 |
openai.api_key = api_key
|
| 39 |
-
# Initialize Google Cloud TTS client (local mode)
|
| 40 |
-
client = texttospeech.TextToSpeechClient()
|
| 41 |
-
|
| 42 |
-
# Ensure required folders exist (used in local mode)
|
| 43 |
-
os.makedirs(AUDIO_FOLDER, exist_ok=True)
|
| 44 |
|
| 45 |
-
# ---------------------- audio-mode helpers
|
| 46 |
def _is_aws_mode() -> bool:
|
| 47 |
"""
|
| 48 |
Switch to AWS Polly + S3 on Hugging Face / prod.
|
|
@@ -64,8 +56,11 @@ def _sanitize_filename(word: str) -> str:
|
|
| 64 |
|
| 65 |
@finding_bp.route('/generate-vocabulary', methods=['GET'])
|
| 66 |
def get_vocabulary_word_from_openai():
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
try:
|
| 71 |
_ensure_openai_key()
|
|
@@ -83,31 +78,30 @@ def get_vocabulary_word_from_openai():
|
|
| 83 |
if "Word:" in result and "Meaning:" in result:
|
| 84 |
parts = result.split("Meaning:")
|
| 85 |
word = parts[0].replace("Word:", "").strip()
|
| 86 |
-
|
| 87 |
-
word = word.rstrip('.')
|
| 88 |
meaning = parts[1].strip()
|
| 89 |
|
| 90 |
# Generate the sentence
|
| 91 |
sentence = generate_sentence(word, meaning)
|
| 92 |
|
| 93 |
# Generate audio file for the vocabulary word
|
| 94 |
-
audio_file_path_or_name = generate_audio(word) #
|
| 95 |
|
| 96 |
-
#
|
| 97 |
audio_url = f"/static/audio/{os.path.basename(audio_file_path_or_name)}"
|
| 98 |
|
| 99 |
return jsonify({
|
| 100 |
"word": word,
|
| 101 |
"meaning": meaning,
|
| 102 |
"sentence": sentence,
|
| 103 |
-
"audio_file_path": audio_url
|
| 104 |
})
|
| 105 |
|
| 106 |
else:
|
| 107 |
return jsonify({"response": result, "message": "Meaning not provided in the expected format"})
|
| 108 |
|
| 109 |
except Exception as e:
|
| 110 |
-
return jsonify({"error": str(e)})
|
| 111 |
|
| 112 |
|
| 113 |
def generate_sentence(word, meaning):
|
|
@@ -120,14 +114,13 @@ def generate_sentence(word, meaning):
|
|
| 120 |
{"role": "user", "content": prompt},
|
| 121 |
]
|
| 122 |
)
|
| 123 |
-
|
| 124 |
sentence = response.choices[0].message.content.strip()
|
| 125 |
return sentence
|
| 126 |
|
| 127 |
|
| 128 |
def generate_audio(word):
|
| 129 |
"""
|
| 130 |
-
Local (default): Google TTS β write MP3 to ./static/audio/<word>.mp3 β return full path
|
| 131 |
Hugging Face / AWS mode: Polly β upload to S3 (findingword/<word>.mp3) β return just the filename,
|
| 132 |
and let /static/audio/<filename> stream from S3 (see route below).
|
| 133 |
"""
|
|
@@ -171,18 +164,31 @@ def generate_audio(word):
|
|
| 171 |
# Return only the filename; /static/audio/<filename> will proxy from S3
|
| 172 |
return filename
|
| 173 |
|
| 174 |
-
# ---- Local Google TTS path (
|
| 175 |
os.makedirs(AUDIO_FOLDER, exist_ok=True)
|
| 176 |
audio_file_path = os.path.join(AUDIO_FOLDER, filename)
|
| 177 |
|
| 178 |
if not os.path.exists(audio_file_path):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
synthesis_input = texttospeech.SynthesisInput(text=word)
|
| 180 |
voice = texttospeech.VoiceSelectionParams(
|
| 181 |
language_code="en-US", ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL
|
| 182 |
)
|
| 183 |
audio_config = texttospeech.AudioConfig(audio_encoding=texttospeech.AudioEncoding.MP3)
|
| 184 |
|
| 185 |
-
response =
|
|
|
|
|
|
|
| 186 |
|
| 187 |
with open(audio_file_path, "wb") as out:
|
| 188 |
out.write(response.audio_content)
|
|
@@ -196,33 +202,27 @@ def generate_audio(word):
|
|
| 196 |
def validate_word():
|
| 197 |
try:
|
| 198 |
data = request.get_json()
|
| 199 |
-
print("π₯ Received data for validation:", data)
|
| 200 |
|
| 201 |
if not data or 'user_input' not in data or 'correct_word' not in data:
|
| 202 |
-
print("β Missing user_input or correct_word in request data.")
|
| 203 |
return jsonify({"error": "Invalid request, missing fields"}), 400
|
| 204 |
|
| 205 |
user_input = data.get('user_input', '').strip()
|
| 206 |
correct_word = data.get('correct_word', '').strip()
|
| 207 |
|
| 208 |
-
print(f"π Validating: User Input - '{user_input}' | Correct Word - '{correct_word}'")
|
| 209 |
-
|
| 210 |
if user_input.lower() == correct_word.lower():
|
| 211 |
-
print("β
Validation Success")
|
| 212 |
return jsonify({"status": "success", "message": "Correct! You typed the word correctly."})
|
| 213 |
else:
|
| 214 |
-
print("β Validation Failed")
|
| 215 |
return jsonify({"status": "failure", "message": f"Incorrect. The correct word was '{correct_word}'."})
|
| 216 |
|
| 217 |
except Exception as e:
|
| 218 |
-
print("β Exception in validate-word API:", str(e))
|
| 219 |
return jsonify({"error": str(e)}), 500
|
| 220 |
|
| 221 |
|
| 222 |
@finding_bp.route('/static/audio/<filename>')
|
| 223 |
def serve_audio(filename):
|
| 224 |
"""
|
| 225 |
-
Local: serve from disk
|
| 226 |
AWS mode (HF): fetch the object from S3 and stream it (no local storage).
|
| 227 |
"""
|
| 228 |
if _is_aws_mode():
|
|
@@ -240,7 +240,6 @@ def serve_audio(filename):
|
|
| 240 |
try:
|
| 241 |
obj = s3.get_object(Bucket=bucket, Key=key)
|
| 242 |
data = obj["Body"].read()
|
| 243 |
-
# Stream as an MP3 without saving locally
|
| 244 |
return send_file(
|
| 245 |
io.BytesIO(data),
|
| 246 |
mimetype="audio/mpeg",
|
|
|
|
| 1 |
import openai
|
| 2 |
from flask import Flask, jsonify, request, send_from_directory, send_file, Blueprint, current_app
|
| 3 |
import os
|
|
|
|
| 4 |
from flask_cors import CORS
|
| 5 |
+
import io # for streaming S3 bytes in HF/AWS mode
|
| 6 |
|
| 7 |
# Optional (only used in AWS mode)
|
| 8 |
try:
|
|
|
|
| 16 |
app = Flask(__name__)
|
| 17 |
CORS(app)
|
| 18 |
|
| 19 |
+
# --- Blueprint ---
|
| 20 |
finding_bp = Blueprint("findingword", __name__)
|
| 21 |
|
| 22 |
# Directories for video, audio, and transcripts
|
| 23 |
VIDEO_FOLDER = 'static/videos'
|
| 24 |
AUDIO_FOLDER = 'static/audio' # Ensure this folder exists
|
| 25 |
TRANSCRIPT_FOLDER = 'static/transcripts'
|
| 26 |
+
os.makedirs(AUDIO_FOLDER, exist_ok=True)
|
| 27 |
|
|
|
|
|
|
|
|
|
|
| 28 |
# --- OpenAI key handling (same as vocab builder) ---
|
| 29 |
_OPENAI_API_KEY_FALLBACK = os.getenv("OPENAI_API_KEY", "")
|
| 30 |
|
|
|
|
| 33 |
api_key = (current_app.config.get("OPENAI_API_KEY") if current_app else None) or _OPENAI_API_KEY_FALLBACK
|
| 34 |
if api_key:
|
| 35 |
openai.api_key = api_key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
+
# ---------------------- audio-mode helpers ----------------------
|
| 38 |
def _is_aws_mode() -> bool:
|
| 39 |
"""
|
| 40 |
Switch to AWS Polly + S3 on Hugging Face / prod.
|
|
|
|
| 56 |
|
| 57 |
@finding_bp.route('/generate-vocabulary', methods=['GET'])
|
| 58 |
def get_vocabulary_word_from_openai():
|
| 59 |
+
prompt = (
|
| 60 |
+
"Pick a simple vocabulary word suitable for children (ages 6β8) "
|
| 61 |
+
"and provide its meaning in very easy English. Do not repeat words from previous responses. "
|
| 62 |
+
"Format: 'Word: [word]. Meaning: [meaning].'"
|
| 63 |
+
)
|
| 64 |
|
| 65 |
try:
|
| 66 |
_ensure_openai_key()
|
|
|
|
| 78 |
if "Word:" in result and "Meaning:" in result:
|
| 79 |
parts = result.split("Meaning:")
|
| 80 |
word = parts[0].replace("Word:", "").strip()
|
| 81 |
+
word = word.rstrip('.') # avoid trailing dot
|
|
|
|
| 82 |
meaning = parts[1].strip()
|
| 83 |
|
| 84 |
# Generate the sentence
|
| 85 |
sentence = generate_sentence(word, meaning)
|
| 86 |
|
| 87 |
# Generate audio file for the vocabulary word
|
| 88 |
+
audio_file_path_or_name = generate_audio(word) # local path or just filename in AWS mode
|
| 89 |
|
| 90 |
+
# URL for frontend remains identical
|
| 91 |
audio_url = f"/static/audio/{os.path.basename(audio_file_path_or_name)}"
|
| 92 |
|
| 93 |
return jsonify({
|
| 94 |
"word": word,
|
| 95 |
"meaning": meaning,
|
| 96 |
"sentence": sentence,
|
| 97 |
+
"audio_file_path": audio_url
|
| 98 |
})
|
| 99 |
|
| 100 |
else:
|
| 101 |
return jsonify({"response": result, "message": "Meaning not provided in the expected format"})
|
| 102 |
|
| 103 |
except Exception as e:
|
| 104 |
+
return jsonify({"error": str(e)}), 500
|
| 105 |
|
| 106 |
|
| 107 |
def generate_sentence(word, meaning):
|
|
|
|
| 114 |
{"role": "user", "content": prompt},
|
| 115 |
]
|
| 116 |
)
|
|
|
|
| 117 |
sentence = response.choices[0].message.content.strip()
|
| 118 |
return sentence
|
| 119 |
|
| 120 |
|
| 121 |
def generate_audio(word):
|
| 122 |
"""
|
| 123 |
+
Local (default): Google TTS β write MP3 to ./static/audio/<word>.mp3 β return full path.
|
| 124 |
Hugging Face / AWS mode: Polly β upload to S3 (findingword/<word>.mp3) β return just the filename,
|
| 125 |
and let /static/audio/<filename> stream from S3 (see route below).
|
| 126 |
"""
|
|
|
|
| 164 |
# Return only the filename; /static/audio/<filename> will proxy from S3
|
| 165 |
return filename
|
| 166 |
|
| 167 |
+
# ---- Local Google TTS path (lazy import; no GCP on HF) ----
|
| 168 |
os.makedirs(AUDIO_FOLDER, exist_ok=True)
|
| 169 |
audio_file_path = os.path.join(AUDIO_FOLDER, filename)
|
| 170 |
|
| 171 |
if not os.path.exists(audio_file_path):
|
| 172 |
+
try:
|
| 173 |
+
# Import only in local mode to avoid HF credential errors
|
| 174 |
+
from google.cloud import texttospeech
|
| 175 |
+
gcp_client = texttospeech.TextToSpeechClient()
|
| 176 |
+
except Exception as e:
|
| 177 |
+
raise RuntimeError(
|
| 178 |
+
"Google TTS is required in local mode but missing. "
|
| 179 |
+
"Install google-cloud-texttospeech and set GOOGLE_APPLICATION_CREDENTIALS. "
|
| 180 |
+
f"Details: {e}"
|
| 181 |
+
)
|
| 182 |
+
|
| 183 |
synthesis_input = texttospeech.SynthesisInput(text=word)
|
| 184 |
voice = texttospeech.VoiceSelectionParams(
|
| 185 |
language_code="en-US", ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL
|
| 186 |
)
|
| 187 |
audio_config = texttospeech.AudioConfig(audio_encoding=texttospeech.AudioEncoding.MP3)
|
| 188 |
|
| 189 |
+
response = gcp_client.synthesize_speech(
|
| 190 |
+
input=synthesis_input, voice=voice, audio_config=audio_config
|
| 191 |
+
)
|
| 192 |
|
| 193 |
with open(audio_file_path, "wb") as out:
|
| 194 |
out.write(response.audio_content)
|
|
|
|
| 202 |
def validate_word():
|
| 203 |
try:
|
| 204 |
data = request.get_json()
|
| 205 |
+
print("π₯ Received data for validation:", data)
|
| 206 |
|
| 207 |
if not data or 'user_input' not in data or 'correct_word' not in data:
|
|
|
|
| 208 |
return jsonify({"error": "Invalid request, missing fields"}), 400
|
| 209 |
|
| 210 |
user_input = data.get('user_input', '').strip()
|
| 211 |
correct_word = data.get('correct_word', '').strip()
|
| 212 |
|
|
|
|
|
|
|
| 213 |
if user_input.lower() == correct_word.lower():
|
|
|
|
| 214 |
return jsonify({"status": "success", "message": "Correct! You typed the word correctly."})
|
| 215 |
else:
|
|
|
|
| 216 |
return jsonify({"status": "failure", "message": f"Incorrect. The correct word was '{correct_word}'."})
|
| 217 |
|
| 218 |
except Exception as e:
|
|
|
|
| 219 |
return jsonify({"error": str(e)}), 500
|
| 220 |
|
| 221 |
|
| 222 |
@finding_bp.route('/static/audio/<filename>')
|
| 223 |
def serve_audio(filename):
|
| 224 |
"""
|
| 225 |
+
Local: serve from disk.
|
| 226 |
AWS mode (HF): fetch the object from S3 and stream it (no local storage).
|
| 227 |
"""
|
| 228 |
if _is_aws_mode():
|
|
|
|
| 240 |
try:
|
| 241 |
obj = s3.get_object(Bucket=bucket, Key=key)
|
| 242 |
data = obj["Body"].read()
|
|
|
|
| 243 |
return send_file(
|
| 244 |
io.BytesIO(data),
|
| 245 |
mimetype="audio/mpeg",
|