Spaces:
Sleeping
Sleeping
Commit ·
377f697
1
Parent(s): 423d8c5
feat: Enhance audio processing and transcription features
Browse files- app.py +24 -8
- backend/asr.py +9 -0
- backend/main.py +1 -1
- backend/systemprompt.py +6 -2
- backend/tools.py +10 -5
- backend/utils.py +6 -1
app.py
CHANGED
|
@@ -8,7 +8,7 @@ import numpy as np
|
|
| 8 |
import sys
|
| 9 |
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
| 10 |
from backend.tts import synthesize_text
|
| 11 |
-
from backend.asr import transcribe_audio
|
| 12 |
from backend.utils import preprocess_audio, is_valid_turn, preprocess_audio_simplified
|
| 13 |
from backend.main import stream_chat_response
|
| 14 |
import json
|
|
@@ -240,9 +240,26 @@ def response(audio: tuple[int, np.ndarray] | None, conversation_history):
|
|
| 240 |
print(f"Audio preprocessing failed: {audio_err}")
|
| 241 |
print(f"------------------------")
|
| 242 |
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
t0 = time.time()
|
| 245 |
-
transcription =
|
|
|
|
| 246 |
t_asr = time.time() - t0
|
| 247 |
print(f"ASR: {t_asr:.4f}s")
|
| 248 |
|
|
@@ -250,7 +267,6 @@ def response(audio: tuple[int, np.ndarray] | None, conversation_history):
|
|
| 250 |
print("No valid transcription; skipping response generation.")
|
| 251 |
print(f"------------------------")
|
| 252 |
return
|
| 253 |
-
|
| 254 |
user_turn = {"role": "user", "content": transcription}
|
| 255 |
print(f"User: {transcription}")
|
| 256 |
if is_valid_turn(user_turn):
|
|
@@ -434,13 +450,13 @@ with gr.Blocks(css=custom_css, theme=gr.themes.Soft(primary_hue="orange", second
|
|
| 434 |
fn=ReplyOnPause(
|
| 435 |
response,
|
| 436 |
algo_options=AlgoOptions(
|
| 437 |
-
audio_chunk_duration=1.
|
| 438 |
-
started_talking_threshold=0.
|
| 439 |
-
speech_threshold=0.
|
| 440 |
),
|
| 441 |
model_options=SileroVadOptions(
|
| 442 |
-
threshold=0.
|
| 443 |
-
min_speech_duration_ms=
|
| 444 |
max_speech_duration_s=float("inf"),
|
| 445 |
min_silence_duration_ms=1200,
|
| 446 |
),
|
|
|
|
| 8 |
import sys
|
| 9 |
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
| 10 |
from backend.tts import synthesize_text
|
| 11 |
+
from backend.asr import transcribe_audio, transcribe_typhoon
|
| 12 |
from backend.utils import preprocess_audio, is_valid_turn, preprocess_audio_simplified
|
| 13 |
from backend.main import stream_chat_response
|
| 14 |
import json
|
|
|
|
| 240 |
print(f"Audio preprocessing failed: {audio_err}")
|
| 241 |
print(f"------------------------")
|
| 242 |
return
|
| 243 |
+
|
| 244 |
+
silence_duration_s = 0.2
|
| 245 |
+
|
| 246 |
+
# Calculate the number of samples corresponding to the silence duration
|
| 247 |
+
silence_samples = int(16000 * silence_duration_s)
|
| 248 |
+
|
| 249 |
+
# Create a silent audio segment (an array of zeros)
|
| 250 |
+
# Ensure the dtype matches your processed audio for compatibility
|
| 251 |
+
leading_silence = np.zeros(silence_samples, dtype=np.float32)
|
| 252 |
|
| 253 |
+
# Prepend the silence to the beginning of your processed audio
|
| 254 |
+
audio_with_padding = np.concatenate([leading_silence, processed_audio])
|
| 255 |
+
|
| 256 |
+
print(f"Added {silence_duration_s}s of silence. New shape: {audio_with_padding.shape}")
|
| 257 |
+
|
| 258 |
+
file_name = "temp.wav"
|
| 259 |
+
sf.write(file_name, audio_with_padding, sr)
|
| 260 |
t0 = time.time()
|
| 261 |
+
transcription = transcribe_typhoon(file_name)
|
| 262 |
+
# transcription = transcribe_audio( "debug_processed.wav")
|
| 263 |
t_asr = time.time() - t0
|
| 264 |
print(f"ASR: {t_asr:.4f}s")
|
| 265 |
|
|
|
|
| 267 |
print("No valid transcription; skipping response generation.")
|
| 268 |
print(f"------------------------")
|
| 269 |
return
|
|
|
|
| 270 |
user_turn = {"role": "user", "content": transcription}
|
| 271 |
print(f"User: {transcription}")
|
| 272 |
if is_valid_turn(user_turn):
|
|
|
|
| 450 |
fn=ReplyOnPause(
|
| 451 |
response,
|
| 452 |
algo_options=AlgoOptions(
|
| 453 |
+
audio_chunk_duration=1.5,
|
| 454 |
+
started_talking_threshold=0.35,
|
| 455 |
+
speech_threshold=0.2
|
| 456 |
),
|
| 457 |
model_options=SileroVadOptions(
|
| 458 |
+
threshold=0.65,
|
| 459 |
+
min_speech_duration_ms=200,
|
| 460 |
max_speech_duration_s=float("inf"),
|
| 461 |
min_silence_duration_ms=1200,
|
| 462 |
),
|
backend/asr.py
CHANGED
|
@@ -170,6 +170,15 @@ def _transcribe_with_gpt(audio_array: np.ndarray) -> str:
|
|
| 170 |
|
| 171 |
_ASR_TYPHOON = _initialize_typhoon_pipeline()
|
| 172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
def transcribe_audio(audio_array: np.ndarray) -> str:
|
| 175 |
"""Transcribe user audio with the best available backend based on the current stage."""
|
|
|
|
| 170 |
|
| 171 |
_ASR_TYPHOON = _initialize_typhoon_pipeline()
|
| 172 |
|
| 173 |
+
def transcribe_typhoon(path: str) -> str:
|
| 174 |
+
text = _ASR_TYPHOON.transcribe(path)
|
| 175 |
+
if text[0].text:
|
| 176 |
+
return text[0].text
|
| 177 |
+
else :
|
| 178 |
+
print(text)
|
| 179 |
+
return ""
|
| 180 |
+
|
| 181 |
+
|
| 182 |
|
| 183 |
def transcribe_audio(audio_array: np.ndarray) -> str:
|
| 184 |
"""Transcribe user audio with the best available backend based on the current stage."""
|
backend/main.py
CHANGED
|
@@ -114,7 +114,7 @@ async def _stream_chat_async(history: List[Dict[str, str]], message: str) -> Asy
|
|
| 114 |
# limited_conversation = full_conversation[-7:]
|
| 115 |
# else:
|
| 116 |
# limited_conversation = full_conversation
|
| 117 |
-
|
| 118 |
response_generator = llm_analyzer.generate_normal_response(limited_conversation)
|
| 119 |
|
| 120 |
async for chunk in response_generator:
|
|
|
|
| 114 |
# limited_conversation = full_conversation[-7:]
|
| 115 |
# else:
|
| 116 |
# limited_conversation = full_conversation
|
| 117 |
+
limited_conversation = full_conversation
|
| 118 |
response_generator = llm_analyzer.generate_normal_response(limited_conversation)
|
| 119 |
|
| 120 |
async for chunk in response_generator:
|
backend/systemprompt.py
CHANGED
|
@@ -90,12 +90,16 @@ Stage 1: Consult
|
|
| 90 |
Stage 2: Buying and get info:
|
| 91 |
- This phrase when user comfirm that they want to buy the promotion or product.
|
| 92 |
- Ask how many product do they want.
|
| 93 |
-
- After get quantity, call the "summary_order" will return the promotion_id,quantity and total_price. Confirm user again that the info is correct. **Do not tell promotion_id to the user, map to the name of the promotion in the promotion description.
|
| 94 |
-
- After user confirm the order, now ask about personal data for the shipping [
|
|
|
|
| 95 |
- After get all the personal data, confirm user 1 last time that the info is correct.
|
| 96 |
Stage 3: Send order to the backend system
|
| 97 |
- After all the info correct, call "purchase_product" to send order to the system
|
| 98 |
- if successful, tell user that the order is successful, if not call "call_admin"
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
## Notes:
|
| 101 |
- Do not give a image or any link to the user.
|
|
|
|
| 90 |
Stage 2: Buying and get info:
|
| 91 |
- This phrase when user comfirm that they want to buy the promotion or product.
|
| 92 |
- Ask how many product do they want.
|
| 93 |
+
- After get quantity, call the "summary_order" will return the promotion_id,quantity and total_price. Confirm user again that the info is correct. **Do not tell promotion_id to the user, map to the name of the promotion in the promotion description. state the user that "เก็ยเงินปลายทาง" is the only payment method available.
|
| 94 |
+
- After user confirm the order, now ask about personal data for the shipping [name, surname, shipping address, postal code and customer telephone number].
|
| 95 |
+
- Ask step by step slowly start from name then surname then address then postal code then telephone number, Ask until get all the data. Note that the input is from the asr system so the address can be inaccurate, so u can make assumption(the address must be in Thailand), if not sure, you can ask the user back for the clarification.
|
| 96 |
- After get all the personal data, confirm user 1 last time that the info is correct.
|
| 97 |
Stage 3: Send order to the backend system
|
| 98 |
- After all the info correct, call "purchase_product" to send order to the system
|
| 99 |
- if successful, tell user that the order is successful, if not call "call_admin"
|
| 100 |
+
Stage 4: After sales service
|
| 101 |
+
- Thank user for buying the product, and ask them to wait for the product to arrive in 5-7 days.
|
| 102 |
+
- Then ask if they have any question about the product or the order.
|
| 103 |
|
| 104 |
## Notes:
|
| 105 |
- Do not give a image or any link to the user.
|
backend/tools.py
CHANGED
|
@@ -65,9 +65,13 @@ TOOL_DEFINITIONS =[
|
|
| 65 |
"type": "integer",
|
| 66 |
"description": "Quantity the user wants to buy.",
|
| 67 |
},
|
| 68 |
-
"
|
| 69 |
"type": "string",
|
| 70 |
-
"description": "Customer
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
},
|
| 72 |
"address": {
|
| 73 |
"type": "string",
|
|
@@ -78,7 +82,7 @@ TOOL_DEFINITIONS =[
|
|
| 78 |
"description": "Customer telephone number.",
|
| 79 |
},
|
| 80 |
},
|
| 81 |
-
"required": ["promotion_id", "quantity", "
|
| 82 |
},
|
| 83 |
},
|
| 84 |
},
|
|
@@ -186,7 +190,8 @@ def call_admin(cause: str, chat_history : Dict[str, str]):
|
|
| 186 |
def purchase_product(
|
| 187 |
promotion_id: str,
|
| 188 |
quantity: int,
|
| 189 |
-
|
|
|
|
| 190 |
address: str,
|
| 191 |
tel: str,
|
| 192 |
) -> Dict[str, Any]:
|
|
@@ -197,7 +202,7 @@ def purchase_product(
|
|
| 197 |
payload = {
|
| 198 |
"promotion_id": promotion_id,
|
| 199 |
"quantity": quantity,
|
| 200 |
-
"user_name":
|
| 201 |
"address": address,
|
| 202 |
"tel": tel
|
| 203 |
}
|
|
|
|
| 65 |
"type": "integer",
|
| 66 |
"description": "Quantity the user wants to buy.",
|
| 67 |
},
|
| 68 |
+
"name": {
|
| 69 |
"type": "string",
|
| 70 |
+
"description": "Customer name in Thai.",
|
| 71 |
+
},
|
| 72 |
+
"surname": {
|
| 73 |
+
"type": "string",
|
| 74 |
+
"description": "Customer surname in Thai.",
|
| 75 |
},
|
| 76 |
"address": {
|
| 77 |
"type": "string",
|
|
|
|
| 82 |
"description": "Customer telephone number.",
|
| 83 |
},
|
| 84 |
},
|
| 85 |
+
"required": ["promotion_id", "quantity", "name","surname", "address", "tel"],
|
| 86 |
},
|
| 87 |
},
|
| 88 |
},
|
|
|
|
| 190 |
def purchase_product(
|
| 191 |
promotion_id: str,
|
| 192 |
quantity: int,
|
| 193 |
+
name: str,
|
| 194 |
+
surname: str,
|
| 195 |
address: str,
|
| 196 |
tel: str,
|
| 197 |
) -> Dict[str, Any]:
|
|
|
|
| 202 |
payload = {
|
| 203 |
"promotion_id": promotion_id,
|
| 204 |
"quantity": quantity,
|
| 205 |
+
"user_name": name + " " + surname,
|
| 206 |
"address": address,
|
| 207 |
"tel": tel
|
| 208 |
}
|
backend/utils.py
CHANGED
|
@@ -98,7 +98,7 @@ def audiosegment_to_numpy(audio, target_sample_rate=16000):
|
|
| 98 |
return samples.astype(np.float32)
|
| 99 |
|
| 100 |
|
| 101 |
-
def preprocess_audio(audio, target_channels=1,
|
| 102 |
"""
|
| 103 |
Ensures the audio is mono, target sample rate, and normalized to [-1, 1].
|
| 104 |
|
|
@@ -107,7 +107,12 @@ def preprocess_audio(audio, target_channels=1, target_frame_rate=16000):
|
|
| 107 |
Returns:
|
| 108 |
tuple: (target_frame_rate, normalized_audio)
|
| 109 |
"""
|
|
|
|
|
|
|
| 110 |
sample_rate, audio_array = audio
|
|
|
|
|
|
|
|
|
|
| 111 |
print(audio_array)
|
| 112 |
print(audio_array[0])
|
| 113 |
print(len(audio_array[0]))
|
|
|
|
| 98 |
return samples.astype(np.float32)
|
| 99 |
|
| 100 |
|
| 101 |
+
def preprocess_audio(audio, target_channels=1, target_sr=16000):
|
| 102 |
"""
|
| 103 |
Ensures the audio is mono, target sample rate, and normalized to [-1, 1].
|
| 104 |
|
|
|
|
| 107 |
Returns:
|
| 108 |
tuple: (target_frame_rate, normalized_audio)
|
| 109 |
"""
|
| 110 |
+
target_frame_rate = target_sr
|
| 111 |
+
|
| 112 |
sample_rate, audio_array = audio
|
| 113 |
+
#save audio array for debug
|
| 114 |
+
with open("debug_audio_array.npy", "wb") as f:
|
| 115 |
+
np.save(f, audio_array)
|
| 116 |
print(audio_array)
|
| 117 |
print(audio_array[0])
|
| 118 |
print(len(audio_array[0]))
|