Hamed744 commited on
Commit
5f5b8e1
·
verified ·
1 Parent(s): 66e43ac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +73 -99
app.py CHANGED
@@ -91,37 +91,38 @@ def parse_audio_mime_type(mime_type: str) -> dict[str, int]:
91
  def smart_text_split(text, max_size=3800):
92
  if len(text) <= max_size: return [text]
93
  chunks, current_chunk = [], ""
94
- # بهبود regex برای پشتیبانی بهتر از نقطه ویرگول فارسی و انگلیسی و سایر علائم
95
- sentences = re.split(r'(?<=[.!?؟؛;])\s+', text)
96
  for sentence in sentences:
97
  if len(current_chunk) + len(sentence) + 1 > max_size: # +1 for space
98
  if current_chunk: chunks.append(current_chunk.strip())
99
  current_chunk = sentence
100
- # اگر یک جمله به تنهایی بزرگتر از max_size باشد، آن را بیشتر خرد کن
101
- while len(current_chunk) > max_size:
102
- # تلاش برای شکستن از روی کاما، نقطه ویرگول، یا فاصله در محدوده‌ی معقول
103
  split_idx = -1
104
- # ابتدا سعی کن نزدیک به انتها بشکنی
105
- for i in range(max_size - 1, int(max_size * 0.6), -1): # تا 60% طول ماکسیمم به عقب برو
106
- if current_chunk[i] in ['،', ',', '؛', ';', ':', ' ']:
107
- split_idx = i
108
- break
 
 
 
109
 
110
- part_to_add = ""
111
  if split_idx != -1:
112
- part_to_add = current_chunk[:split_idx+1]
113
  current_chunk = current_chunk[split_idx+1:]
114
- else: # اگر نقطه مناسبی پیدا نشد، به زور بشکن
115
- part_to_add = current_chunk[:max_size]
116
  current_chunk = current_chunk[max_size:]
117
- chunks.append(part_to_add.strip())
118
  else:
119
- if current_chunk and sentence: # اضافه کردن فاصله فقط اگر هر دو وجود دارند
120
  current_chunk += " " + sentence
121
- else: # اگر یکی از آنها خالی است، بدون فاصله اضافه کن
122
- current_chunk += sentence
123
  if current_chunk: chunks.append(current_chunk.strip())
124
- final_chunks = [c for c in chunks if c] # حذف قطعات خالی
125
  return final_chunks
126
 
127
  def merge_audio_files_func(file_paths, output_path):
@@ -129,12 +130,7 @@ def merge_audio_files_func(file_paths, output_path):
129
  try:
130
  combined = AudioSegment.empty()
131
  for i, fp in enumerate(file_paths):
132
- if os.path.exists(fp):
133
- segment = AudioSegment.from_file(fp)
134
- combined += segment
135
- # اضافه کردن سکوت کوتاه بین قطعات، مگر اینکه آخرین قطعه باشد
136
- if i < len(file_paths) - 1:
137
- combined += AudioSegment.silent(duration=150) # 150 میلی‌ثانیه سکوت
138
  else: _log(f"⚠️ فایل برای ادغام پیدا نشد: {fp}")
139
  combined.export(output_path, format="wav")
140
  return True
@@ -171,15 +167,20 @@ def core_generate_audio(text_input, prompt_input, selected_voice, temperature_va
171
 
172
  generated_files = []
173
  for i, chunk in enumerate(text_chunks):
174
- final_text = ""
175
  if prompt_input and prompt_input.strip():
176
- # --- تغییر کلیدی برای مشکل خواندن پرامپت ---
177
- # به جای: final_text = f'"{prompt_input.strip()}"\n{chunk}'
178
- # از پرانتز استفاده می‌کنیم تا به مدل بفهمانیم این بخش دستورالعمل است.
179
- final_text = f"({prompt_input.strip()})\n{chunk}"
 
 
180
  else:
181
- final_text = chunk
182
-
 
 
 
183
  contents = [types.Content(role="user", parts=[types.Part.from_text(text=final_text)])]
184
  config = types.GenerateContentConfig(temperature=temperature_val, response_modalities=["audio"],
185
  speech_config=types.SpeechConfig(voice_config=types.VoiceConfig(
@@ -197,105 +198,79 @@ def core_generate_audio(text_input, prompt_input, selected_voice, temperature_va
197
  if fpath: generated_files.append(fpath)
198
  else: _log(f"⚠️ پاسخ API برای قطعه {i+1} بدون داده صوتی بود (با کلید شماره {key_idx_display}).")
199
  except Exception as e:
200
- # نمایش خطای دقیق‌تر از API اگر در دسترس باشد
201
- error_message = str(e)
202
- if hasattr(e, 'message'): # برای انواع خاصی از خطاها در کتابخانه google-generativeai
203
- error_message = e.message
204
- elif hasattr(e, 'args') and e.args:
205
- error_message = str(e.args[0])
206
-
207
- # گاهی اوقات خطا شامل جزئیات بیشتری است، مانند خطاهای مربوط به محتوای نامناسب
208
- # if "blocked" in error_message.lower() or "safety" in error_message.lower():
209
- # _log(f"❌ محتوای قطعه {i+1} ممکن است توسط فیلترهای ایمنی مسدود شده باشد. جزئیات: {error_message}")
210
- # else:
211
- _log(f"❌ خطا در تولید قطعه {i+1} با کلید شماره {key_idx_display}: {error_message}")
212
- continue # ادامه بده به قطعه بعدی اگر یکی خطا داد
213
-
214
- if i < len(text_chunks) - 1 and len(text_chunks) > 1:
215
- # _log(f"⏱️ انتظار {sleep_time} ثانیه قبل از درخواست بعدی...") # لاگ کمتر
216
- time.sleep(sleep_time)
217
 
218
  if not generated_files:
219
  _log(f"❌ هیچ فایل صوتی با کلید شماره {key_idx_display} تولید نشد.")
220
  return None
221
 
222
  final_audio_file = None
223
- final_output_path_base = f"{output_base_name}_final" # نام پایه فایل نهایی
224
-
225
- # ایجاد یک timestamp برای نام فایل نهایی برای جلوگیری از کش شدن توسط مرورگر
226
- # timestamp = int(time.time())
227
- # final_output_path_base_ts = f"{final_output_path_base}_{timestamp}"
228
- # فعلا از timestamp استفاده نمی‌کنیم تا نام فایل ساده بماند. Gradio معمولا کش نمی‌کند اگر محتوا تغییر کند.
229
 
230
  if len(generated_files) > 1:
231
  if PYDUB_AVAILABLE:
232
- merged_fn = f"{final_output_path_base}.wav" # همیشه خروجی ادغام شده WAV است
233
- if os.path.exists(merged_fn): # حذف فایل قبلی اگر وجود دارد تا جایگزین شود
234
- try: os.remove(merged_fn)
235
- except OSError as e_rem: _log(f"⚠️ نتوانست فایل قبلی ادغام شده را حذف کند: {merged_fn}, خطا: {e_rem}")
236
-
237
  if merge_audio_files_func(generated_files, merged_fn):
238
  final_audio_file = merged_fn
239
  else:
240
- _log("⚠️ ادغام فایل‌ها ناموفق بود. اولین قطعه به عنوان خروجی ارائه می‌شود.")
241
- if generated_files: # اگر حداقل یک فایل تولید شده باشد
242
  try:
243
- original_first_chunk_path = generated_files[0]
244
- target_ext = os.path.splitext(original_first_chunk_path)[1]
245
  renamed_first_chunk = f"{final_output_path_base}{target_ext}"
246
  if os.path.exists(renamed_first_chunk): os.remove(renamed_first_chunk)
247
- os.rename(original_first_chunk_path, renamed_first_chunk)
248
  final_audio_file = renamed_first_chunk
249
  except Exception as e_rename:
250
  _log(f"خطا در تغییر نام فایل اولین قطعه (پس از ادغام ناموفق): {e_rename}")
251
- final_audio_file = generated_files[0] # بازگشت به مسیر اصلی
252
 
253
- # پاک کردن فایل‌های جزئی (چه ادغام موفق بوده چه ناموفق، به جز فایل نهایی)
254
  for fp_cleanup in generated_files:
255
- # اگر فایل جزئی همان فایل نهایی است (مثلا در صورت ادغام ناموفق و تغییر نام اولین قطعه) آن را پاک نکن
256
  if final_audio_file and os.path.abspath(fp_cleanup) == os.path.abspath(final_audio_file):
257
  continue
258
- try:
259
- if os.path.exists(fp_cleanup): os.remove(fp_cleanup)
260
- except OSError as e_del_part: _log(f"⚠️ نتوانست فایل جزئی را حذف کند: {fp_cleanup}, خطا: {e_del_part}")
261
  else:
262
- _log("⚠️ pydub در دسترس نیست. اولین قطعه صوتی ارائه می‌شود چون امکان ادغام وجود ندارد.")
263
  if generated_files:
264
  try:
265
- original_first_chunk_path = generated_files[0]
266
- target_ext = os.path.splitext(original_first_chunk_path)[1]
267
  renamed_first_chunk = f"{final_output_path_base}{target_ext}"
268
  if os.path.exists(renamed_first_chunk): os.remove(renamed_first_chunk)
269
- os.rename(original_first_chunk_path, renamed_first_chunk)
270
  final_audio_file = renamed_first_chunk
271
- # پاک کردن بقیه فایل‌های جزئی
272
- for i_gf in range(1, len(generated_files)):
273
- try:
274
- if os.path.exists(generated_files[i_gf]): os.remove(generated_files[i_gf])
275
- except OSError as e_del_other_part: _log(f"⚠️ نتوانست فایل جزئی دیگر را حذف کند: {generated_files[i_gf]}, خطا: {e_del_other_part}")
276
  except Exception as e_rename_single:
277
  _log(f"خطا در تغییر نام فایل اولین قطعه (بدون pydub): {e_rename_single}")
278
- final_audio_file = generated_files[0] # بازگشت به مسیر اصلی
279
-
280
  elif len(generated_files) == 1:
281
  try:
282
- original_single_file_path = generated_files[0]
283
- target_ext = os.path.splitext(original_single_file_path)[1]
284
  final_single_fn = f"{final_output_path_base}{target_ext}"
285
- if os.path.exists(final_single_fn): os.remove(final_single_fn) # حذف فایل قبلی اگر وجود دارد
286
- os.rename(original_single_file_path, final_single_fn)
287
  final_audio_file = final_single_fn
288
  except Exception as e_rename_single_final:
289
  _log(f"خطا در تغییر نام فایل تکی نهایی: {e_rename_single_final}")
290
- final_audio_file = generated_files[0] # بازگشت به مسیر اصلی اگر تغییر نام ناموفق بود
291
 
292
  if final_audio_file and os.path.exists(final_audio_file):
293
  _log(f"✅ فایل صوتی نهایی با موفقیت با کلید شماره {key_idx_display} تولید شد: {os.path.basename(final_audio_file)}")
294
- elif final_audio_file: # اگر متغیر مقدار دارد اما فایل وجود ندارد
295
  _log(f"⚠️ فایل نهایی '{final_audio_file}' پس از پردازش وجود ندارد! (با کلید شماره {key_idx_display})")
296
- return None # صریحاً None برگردان تا Gradio خطا ندهد برای فایلی که نیست
297
- else: # اگر final_audio_file از ابتدا None مانده (هیچ فایلی تولید نشده یا خطایی بوده)
298
- # لاگ قبلی در مورد عدم تولید فایل کافی است
 
299
  return None
300
 
301
  return final_audio_file
@@ -305,11 +280,10 @@ def gradio_tts_interface(use_file_input, uploaded_file, text_to_speak, speech_pr
305
  if use_file_input:
306
  if uploaded_file:
307
  try:
308
- # اطمینان از اینکه uploaded_file.name مسیر معتبر فایل است
309
- file_path = uploaded_file.name if hasattr(uploaded_file, 'name') else uploaded_file
310
- with open(file_path, 'r', encoding='utf-8') as f: actual_text = f.read().strip()
311
  if not actual_text: _log("❌ فایل آپلود شده خالی است یا خوانده نشد."); return None
312
- except Exception as e: _log(f"❌ خطا در خواندن فایل آپلود شده '{file_path if 'file_path' in locals() else 'نامشخص'}': {e}"); return None
313
  else: _log("❌ گزینه استفاده از فایل انتخاب شده اما فایلی آپلود نشده."); return None
314
  else:
315
  actual_text = text_to_speak
@@ -406,14 +380,14 @@ with gr.Blocks(theme=gr.themes.Base(font=[gr.themes.GoogleFont("Vazirmatn")]), c
406
  SPEAKER_VOICES, label="انتخاب گوینده و لهجه", value="Charon", elem_id="speaker_voice_alpha_v3"
407
  )
408
  temperature_slider = gr.Slider(
409
- minimum=0.0, maximum=1.5, step=0.05, value=0.9, label="میزان خلاقیت صدا", # مینیمم دما می‌تواند 0 باشد
410
  elem_id="temperature_slider_alpha_v3"
411
  )
412
  gr.Markdown("<p class='temp_description_class_alpha_v3'>مقادیر بالاتر = تنوع بیشتر، مقادیر پایین‌تر = یکنواختی بیشتر.</p>")
413
 
414
  generate_button = gr.Button("🚀 تولید و پخش صدا", elem_classes=["generate-button-final"], elem_id="generate_button_alpha_v3")
415
 
416
- output_audio = gr.Audio(label=" ", type="filepath", elem_id="output_audio_player_alpha_v3")
417
 
418
  generate_button.click(
419
  fn=gradio_tts_interface,
@@ -426,12 +400,12 @@ with gr.Blocks(theme=gr.themes.Base(font=[gr.themes.GoogleFont("Vazirmatn")]), c
426
  examples=[
427
  [False, None, "سلام بر شما، امیدوارم روز خوبی داشته باشید.", "با لحنی گرم و صمیمی.", "Zephyr", 0.85],
428
  [False, None, "این یک آزمایش برای بررسی کیفیت صدای تولید شده توسط هوش مصنوعی آلفا است.", "با صدایی طبیعی و روان.", "Charon", 0.9],
429
- [False, None, "هوا فردا در تهران آفتابی و دلپذیر خواهد بود.", "", "Achird", 0.7], # نمونه بدون سبک گفتار
430
  ],
431
  inputs=[ use_file_input_cb, uploaded_file_input, text_to_speak_tb, speech_prompt_tb, speaker_voice_dd, temperature_slider ],
432
  outputs=[output_audio],
433
  fn=gradio_tts_interface,
434
- cache_examples=os.environ.get("GRADIO_CACHE_EXAMPLES", "false").lower() == "true" # کنترل کش با متغیر محیطی
435
  )
436
  gr.Markdown("<p class='app-footer-final'>Alpha Language Learning © 2024</p>")
437
 
 
91
  def smart_text_split(text, max_size=3800):
92
  if len(text) <= max_size: return [text]
93
  chunks, current_chunk = [], ""
94
+ # بهبود regex برای پشتیبانی بهتر از جداکننده‌های فارسی و انگلیسی
95
+ sentences = re.split(r'(?<=[.!?؟۔])\s+', text)
96
  for sentence in sentences:
97
  if len(current_chunk) + len(sentence) + 1 > max_size: # +1 for space
98
  if current_chunk: chunks.append(current_chunk.strip())
99
  current_chunk = sentence
100
+ while len(current_chunk) > max_size: # If a single sentence is too long
101
+ # Try to split at sensible places like comma, semicolon, or space
 
102
  split_idx = -1
103
+ # Prefer splitting at punctuation, then space
104
+ for char_to_find in ['،', ',', ';', ':', ' ']:
105
+ try:
106
+ # Search backwards from max_size towards middle
107
+ split_idx = current_chunk.rindex(char_to_find, max_size // 2, max_size)
108
+ break
109
+ except ValueError:
110
+ continue
111
 
 
112
  if split_idx != -1:
113
+ part = current_chunk[:split_idx+1]
114
  current_chunk = current_chunk[split_idx+1:]
115
+ else: # Force split if no ideal character found
116
+ part = current_chunk[:max_size]
117
  current_chunk = current_chunk[max_size:]
118
+ chunks.append(part.strip())
119
  else:
120
+ if current_chunk: # Add space if it's not the first part of the chunk
121
  current_chunk += " " + sentence
122
+ else:
123
+ current_chunk = sentence
124
  if current_chunk: chunks.append(current_chunk.strip())
125
+ final_chunks = [c for c in chunks if c]
126
  return final_chunks
127
 
128
  def merge_audio_files_func(file_paths, output_path):
 
130
  try:
131
  combined = AudioSegment.empty()
132
  for i, fp in enumerate(file_paths):
133
+ if os.path.exists(fp): combined += AudioSegment.from_file(fp) + (AudioSegment.silent(duration=150) if i < len(file_paths) - 1 else AudioSegment.empty())
 
 
 
 
 
134
  else: _log(f"⚠️ فایل برای ادغام پیدا نشد: {fp}")
135
  combined.export(output_path, format="wav")
136
  return True
 
167
 
168
  generated_files = []
169
  for i, chunk in enumerate(text_chunks):
170
+ # --- START: تغییر نحوه ترکیب prompt و chunk ---
171
  if prompt_input and prompt_input.strip():
172
+ processed_prompt = prompt_input.strip()
173
+ # اگر سبک گفتار با نقطه گذاری تمام نمی‌شود، یک ویرگول یا نقطه اضافه می‌کنیم
174
+ # این به مدل کمک می‌کند تا آن را به عنوان یک عبارت راهنما قبل از متن اصلی تشخیص دهد
175
+ if not re.search(r'[.!?؟،:۔]$', processed_prompt):
176
+ processed_prompt += "،" # افزودن ویرگول فارسی به عنوان جداکننده ملایم
177
+ final_text = f"{processed_prompt} {chunk.strip()}"
178
  else:
179
+ final_text = chunk.strip()
180
+ # --- END: تغییر نحوه ترکیب prompt و chunk ---
181
+
182
+ _log(f" متن ارسالی به API (قطعه {i+1}): '{final_text[:100]}...'") # نمایش بخش کوچکی از متن نهایی برای دیباگ
183
+
184
  contents = [types.Content(role="user", parts=[types.Part.from_text(text=final_text)])]
185
  config = types.GenerateContentConfig(temperature=temperature_val, response_modalities=["audio"],
186
  speech_config=types.SpeechConfig(voice_config=types.VoiceConfig(
 
198
  if fpath: generated_files.append(fpath)
199
  else: _log(f"⚠️ پاسخ API برای قطعه {i+1} بدون داده صوتی بود (با کلید شماره {key_idx_display}).")
200
  except Exception as e:
201
+ _log(f"❌ خطا در تولید قطعه {i+1} با کلید شماره {key_idx_display}: {e}")
202
+ # بررسی جزئیات خطا از Gemini، اگر موجود باشد
203
+ if hasattr(e, 'message') and "API key" in e.message:
204
+ _log(f" این خطا ممکن است مربوط به کلید API (شماره {key_idx_display}) یا محدودیت‌های آن باشد.")
205
+ elif hasattr(e, 'message') and "resource has been exhausted" in e.message.lower():
206
+ _log(f" احتمالاً به محدودیت استفاده از کلید API (شماره {key_idx_display}) رسیده‌اید.")
207
+ continue
208
+ if i < len(text_chunks) - 1 and len(text_chunks) > 1: time.sleep(sleep_time)
 
 
 
 
 
 
 
 
 
209
 
210
  if not generated_files:
211
  _log(f"❌ هیچ فایل صوتی با کلید شماره {key_idx_display} تولید نشد.")
212
  return None
213
 
214
  final_audio_file = None
215
+ final_output_path_base = f"{output_base_name}_final"
 
 
 
 
 
216
 
217
  if len(generated_files) > 1:
218
  if PYDUB_AVAILABLE:
219
+ merged_fn = f"{final_output_path_base}.wav"
220
+ if os.path.exists(merged_fn): os.remove(merged_fn)
 
 
 
221
  if merge_audio_files_func(generated_files, merged_fn):
222
  final_audio_file = merged_fn
223
  else:
224
+ if generated_files:
 
225
  try:
226
+ target_ext = os.path.splitext(generated_files[0])[1]
 
227
  renamed_first_chunk = f"{final_output_path_base}{target_ext}"
228
  if os.path.exists(renamed_first_chunk): os.remove(renamed_first_chunk)
229
+ os.rename(generated_files[0], renamed_first_chunk)
230
  final_audio_file = renamed_first_chunk
231
  except Exception as e_rename:
232
  _log(f"خطا در تغییر نام فایل اولین قطعه (پس از ادغام ناموفق): {e_rename}")
233
+ final_audio_file = generated_files[0]
234
 
 
235
  for fp_cleanup in generated_files:
 
236
  if final_audio_file and os.path.abspath(fp_cleanup) == os.path.abspath(final_audio_file):
237
  continue
238
+ try: os.remove(fp_cleanup)
239
+ except: pass
 
240
  else:
241
+ _log("⚠️ pydub در دسترس نیست. اولین قطعه صوتی ارائه می‌شود.")
242
  if generated_files:
243
  try:
244
+ target_ext = os.path.splitext(generated_files[0])[1]
 
245
  renamed_first_chunk = f"{final_output_path_base}{target_ext}"
246
  if os.path.exists(renamed_first_chunk): os.remove(renamed_first_chunk)
247
+ os.rename(generated_files[0], renamed_first_chunk)
248
  final_audio_file = renamed_first_chunk
249
+ for i_gf in range(1, len(generated_files)):
250
+ try: os.remove(generated_files[i_gf])
251
+ except: pass
 
 
252
  except Exception as e_rename_single:
253
  _log(f"خطا در تغییر نام فایل اولین قطعه (بدون pydub): {e_rename_single}")
254
+ final_audio_file = generated_files[0]
 
255
  elif len(generated_files) == 1:
256
  try:
257
+ target_ext = os.path.splitext(generated_files[0])[1]
 
258
  final_single_fn = f"{final_output_path_base}{target_ext}"
259
+ if os.path.exists(final_single_fn): os.remove(final_single_fn)
260
+ os.rename(generated_files[0], final_single_fn)
261
  final_audio_file = final_single_fn
262
  except Exception as e_rename_single_final:
263
  _log(f"خطا در تغییر نام فایل تکی نهایی: {e_rename_single_final}")
264
+ final_audio_file = generated_files[0]
265
 
266
  if final_audio_file and os.path.exists(final_audio_file):
267
  _log(f"✅ فایل صوتی نهایی با موفقیت با کلید شماره {key_idx_display} تولید شد: {os.path.basename(final_audio_file)}")
268
+ elif final_audio_file:
269
  _log(f"⚠️ فایل نهایی '{final_audio_file}' پس از پردازش وجود ندارد! (با کلید شماره {key_idx_display})")
270
+ return None
271
+ else:
272
+ # این حالت نباید رخ دهد اگر generated_files خالی نباشد و خطایی در تغییر نام رخ ندهد
273
+ _log(f"❓ وضعیت نامشخص برای فایل نهایی پس از پردازش تمام قطعات. (با کلید شماره {key_idx_display})")
274
  return None
275
 
276
  return final_audio_file
 
280
  if use_file_input:
281
  if uploaded_file:
282
  try:
283
+ # استفاده از uploaded_file.name که مسیر فایل موقت در Gradio است
284
+ with open(uploaded_file.name, 'r', encoding='utf-8') as f: actual_text = f.read().strip()
 
285
  if not actual_text: _log("❌ فایل آپلود شده خالی است یا خوانده نشد."); return None
286
+ except Exception as e: _log(f"❌ خطا در خواندن فایل آپلود شده: {e}"); return None
287
  else: _log("❌ گزینه استفاده از فایل انتخاب شده اما فایلی آپلود نشده."); return None
288
  else:
289
  actual_text = text_to_speak
 
380
  SPEAKER_VOICES, label="انتخاب گوینده و لهجه", value="Charon", elem_id="speaker_voice_alpha_v3"
381
  )
382
  temperature_slider = gr.Slider(
383
+ minimum=0.1, maximum=1.5, step=0.05, value=0.9, label="میزان خلاقیت صدا",
384
  elem_id="temperature_slider_alpha_v3"
385
  )
386
  gr.Markdown("<p class='temp_description_class_alpha_v3'>مقادیر بالاتر = تنوع بیشتر، مقادیر پایین‌تر = یکنواختی بیشتر.</p>")
387
 
388
  generate_button = gr.Button("🚀 تولید و پخش صدا", elem_classes=["generate-button-final"], elem_id="generate_button_alpha_v3")
389
 
390
+ output_audio = gr.Audio(label=" ", type="filepath", elem_id="output_audio_player_alpha_v3") # لیبل خالی برای تطابق با ظاهر
391
 
392
  generate_button.click(
393
  fn=gradio_tts_interface,
 
400
  examples=[
401
  [False, None, "سلام بر شما، امیدوارم روز خوبی داشته باشید.", "با لحنی گرم و صمیمی.", "Zephyr", 0.85],
402
  [False, None, "این یک آزمایش برای بررسی کیفیت صدای تولید شده توسط هوش مصنوعی آلفا است.", "با صدایی طبیعی و روان.", "Charon", 0.9],
403
+ [False, None, "آیا می‌توانم سوالی از شما بپرسم؟", "با کنجکاوی", "Puck", 0.95],
404
  ],
405
  inputs=[ use_file_input_cb, uploaded_file_input, text_to_speak_tb, speech_prompt_tb, speaker_voice_dd, temperature_slider ],
406
  outputs=[output_audio],
407
  fn=gradio_tts_interface,
408
+ cache_examples=False # برای اینکه همیشه با API تماس بگیرد و از کش استفاده نکند
409
  )
410
  gr.Markdown("<p class='app-footer-final'>Alpha Language Learning © 2024</p>")
411