tbdavid2019 commited on
Commit
9dec988
·
1 Parent(s): 31112e4

分拆突破8192限制

Browse files
Files changed (1) hide show
  1. app.py +249 -48
app.py CHANGED
@@ -195,16 +195,21 @@ def generate_dialogue_via_requests(
195
  llm_api_key: str,
196
  api_base: str,
197
  edited_transcript: str = None,
198
- user_feedback: str = None
 
199
  ) -> str:
200
  """
201
  Generate dialogue by making a direct request to the LLM API.
202
- Includes retry logic for handling rate limits.
203
  """
204
  logger.info(f"準備生成對話,使用模型: {model}")
205
  logger.info(f"輸入文本長度: {len(pdf_text)} 字符")
206
 
207
- merged_content = f"""
 
 
 
 
208
  以下是從 PDF 中擷取的文字內容,請參考並納入對話:
209
  ================================
210
  {pdf_text}
@@ -227,63 +232,259 @@ def generate_dialogue_via_requests(
227
  "Content-Type": "application/json"
228
  }
229
 
230
- payload = {
231
- "model": model,
232
- "messages": [
233
- {
234
- "role": "user",
235
- "content": merged_content
236
- }
237
- ],
238
- "temperature": 0.7,
239
- "max_tokens": 8192 # 設置一個更合理的值
240
- }
241
-
242
  base_url = api_base.rstrip("/")
243
  url = f"{base_url}/chat/completions"
244
  logger.info(f"準備發送請求到 API: {url}")
 
 
 
245
 
246
  # 重試參數
247
  max_retries = 5
248
  retry_delay = 5 # 初始延遲秒數
249
 
250
- for attempt in range(max_retries):
251
- try:
252
- logger.info(f"發送 API 請求 (嘗試 {attempt+1}/{max_retries})...")
253
- response = requests.post(url, headers=headers, json=payload)
254
-
255
- # 處理速率限制錯誤
256
- if response.status_code == 429:
257
- # 獲取 Retry-After 頭信息,如果有的話
258
- retry_after = int(response.headers.get('Retry-After', retry_delay))
259
- logger.warning(f"速率限制錯誤 (429)。將在 {retry_after} 秒後重試。嘗試 {attempt+1}/{max_retries}")
260
- time.sleep(retry_after)
261
- # 增加下次重試的延遲(指數退避)
262
- retry_delay *= 2
263
- continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
- # 記錄其他錯誤狀態碼
266
- if response.status_code != 200:
267
- logger.error(f"API 請求失敗: 狀態碼 {response.status_code}, 原因: {response.reason}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
- response.raise_for_status()
270
- result = response.json()
271
- logger.info("API 請求成功,已收到回應")
272
- return result['choices'][0]['message']['content']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
274
- except requests.exceptions.RequestException as e:
275
- error_msg = f"請求失敗: {str(e)}"
276
- logger.error(error_msg)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
 
278
- if attempt < max_retries - 1:
279
- logger.info(f"將在 {retry_delay} 秒後重試。嘗試 {attempt+1}/{max_retries}")
280
- time.sleep(retry_delay)
281
- # 增加下次重試的延遲(指數退避)
282
- retry_delay *= 2
283
- else:
284
- final_error = f"在 {max_retries} 次嘗試後失敗: {str(e)}"
285
- logger.error(final_error)
286
- return f"Error after {max_retries} attempts: {str(e)}"
 
 
 
 
 
 
 
287
 
288
  def validate_and_generate_script(
289
  files,
 
195
  llm_api_key: str,
196
  api_base: str,
197
  edited_transcript: str = None,
198
+ user_feedback: str = None,
199
+ progress_callback=None
200
  ) -> str:
201
  """
202
  Generate dialogue by making a direct request to the LLM API.
203
+ Includes retry logic for handling rate limits and supports long content generation.
204
  """
205
  logger.info(f"準備生成對話,使用模型: {model}")
206
  logger.info(f"輸入文本長度: {len(pdf_text)} 字符")
207
 
208
+ # 檢查是否需要分批生成
209
+ use_continuation = "podcast" in podcast_dialog_instructions.lower() or len(pdf_text) > 50000
210
+
211
+ # 基本提示詞
212
+ base_prompt = f"""
213
  以下是從 PDF 中擷取的文字內容,請參考並納入對話:
214
  ================================
215
  {pdf_text}
 
232
  "Content-Type": "application/json"
233
  }
234
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  base_url = api_base.rstrip("/")
236
  url = f"{base_url}/chat/completions"
237
  logger.info(f"準備發送請求到 API: {url}")
238
+
239
+ if progress_callback:
240
+ progress_callback("正在發送請求到 LLM API...")
241
 
242
  # 重試參數
243
  max_retries = 5
244
  retry_delay = 5 # 初始延遲秒數
245
 
246
+ # 如果不需要分批生成,直接生成完整內容
247
+ if not use_continuation:
248
+ payload = {
249
+ "model": model,
250
+ "messages": [
251
+ {
252
+ "role": "user",
253
+ "content": base_prompt
254
+ }
255
+ ],
256
+ "temperature": 0.7,
257
+ "max_tokens": 8192 # 增加 token 限制
258
+ }
259
+
260
+ for attempt in range(max_retries):
261
+ try:
262
+ logger.info(f"發送 API 請求 (嘗試 {attempt+1}/{max_retries})...")
263
+ if progress_callback:
264
+ progress_callback(f"API 請求中 (嘗試 {attempt+1}/{max_retries})...")
265
+
266
+ response = requests.post(url, headers=headers, json=payload)
267
+
268
+ # 處理速率限制錯誤
269
+ if response.status_code == 429:
270
+ # 獲取 Retry-After 頭信息,如果有的話
271
+ retry_after = int(response.headers.get('Retry-After', retry_delay))
272
+ logger.warning(f"速率限制錯誤 (429)。將在 {retry_after} 秒後重試。嘗試 {attempt+1}/{max_retries}")
273
+ if progress_callback:
274
+ progress_callback(f"速率限制錯誤 (429)。將在 {retry_after} 秒後重試...")
275
+ time.sleep(retry_after)
276
+ # 增加下次重試的延遲(指數退避)
277
+ retry_delay *= 2
278
+ continue
279
+
280
+ # 記錄其他錯誤狀態碼
281
+ if response.status_code != 200:
282
+ logger.error(f"API 請求失敗: 狀態碼 {response.status_code}, 原因: {response.reason}")
283
+ if progress_callback:
284
+ progress_callback(f"API 錯誤: {response.status_code} {response.reason}")
285
 
286
+ response.raise_for_status()
287
+ result = response.json()
288
+ logger.info("API 請求成功,已收到回應")
289
+ if progress_callback:
290
+ progress_callback("已成功從 LLM 獲取回應")
291
+ return result['choices'][0]['message']['content']
292
+
293
+ except requests.exceptions.RequestException as e:
294
+ error_msg = f"請求失敗: {str(e)}"
295
+ logger.error(error_msg)
296
+
297
+ if attempt < max_retries - 1:
298
+ retry_msg = f"將在 {retry_delay} 秒後重試。嘗試 {attempt+1}/{max_retries}"
299
+ logger.info(retry_msg)
300
+ if progress_callback:
301
+ progress_callback(f"{error_msg} {retry_msg}")
302
+ time.sleep(retry_delay)
303
+ # 增加下次重試的延遲(指數退避)
304
+ retry_delay *= 2
305
+ else:
306
+ final_error = f"在 {max_retries} 次嘗試後失敗: {str(e)}"
307
+ logger.error(final_error)
308
+ if progress_callback:
309
+ progress_callback(final_error)
310
+ return f"Error after {max_retries} attempts: {str(e)}"
311
+
312
+ # 分批生成長對話
313
+ else:
314
+ logger.info("檢測到需要生成長對話,將使用分批生成方式")
315
+ if progress_callback:
316
+ progress_callback("檢測到需要生成長對話,將使用分批生成方式...")
317
+
318
+ # 第一部分:生成開場白和前幾輪對話
319
+ first_prompt = base_prompt + "\n請生成對話的開場白和前10輪對話。確保對話開始符合要求,並且內容連貫。"
320
+
321
+ payload = {
322
+ "model": model,
323
+ "messages": [
324
+ {
325
+ "role": "user",
326
+ "content": first_prompt
327
+ }
328
+ ],
329
+ "temperature": 0.7,
330
+ "max_tokens": 8192
331
+ }
332
+
333
+ # 獲取第一部分
334
+ first_part = ""
335
+ for attempt in range(max_retries):
336
+ try:
337
+ logger.info(f"發送第一部分 API 請求 (嘗試 {attempt+1}/{max_retries})...")
338
+ if progress_callback:
339
+ progress_callback(f"生成對話第一部分 (嘗試 {attempt+1}/{max_retries})...")
340
+
341
+ response = requests.post(url, headers=headers, json=payload)
342
+
343
+ if response.status_code == 429:
344
+ retry_after = int(response.headers.get('Retry-After', retry_delay))
345
+ logger.warning(f"速率限制錯誤 (429)。將在 {retry_after} 秒後重試。嘗試 {attempt+1}/{max_retries}")
346
+ if progress_callback:
347
+ progress_callback(f"速率限制錯誤 (429)。將在 {retry_after} 秒後重試...")
348
+ time.sleep(retry_after)
349
+ retry_delay *= 2
350
+ continue
351
+
352
+ if response.status_code != 200:
353
+ logger.error(f"API 請求失敗: 狀態碼 {response.status_code}, 原因: {response.reason}")
354
+
355
+ response.raise_for_status()
356
+ result = response.json()
357
+ first_part = result['choices'][0]['message']['content']
358
+ logger.info("成功獲取對話第一部分")
359
+ if progress_callback:
360
+ progress_callback("成功獲取對話第一部分")
361
+ break
362
 
363
+ except requests.exceptions.RequestException as e:
364
+ logger.error(f"請求失敗: {str(e)}")
365
+ if attempt < max_retries - 1:
366
+ logger.info(f"將在 {retry_delay} 秒後重試。嘗試 {attempt+1}/{max_retries}")
367
+ time.sleep(retry_delay)
368
+ retry_delay *= 2
369
+ else:
370
+ return f"Error generating first part after {max_retries} attempts: {str(e)}"
371
+
372
+ # 第二部分:生成中間部分對話
373
+ second_prompt = base_prompt + f"\n以下是已生成的對話開頭,請繼續生成接下來的20輪對話,保持內容連貫:\n\n{first_part}"
374
+
375
+ payload = {
376
+ "model": model,
377
+ "messages": [
378
+ {
379
+ "role": "user",
380
+ "content": second_prompt
381
+ }
382
+ ],
383
+ "temperature": 0.7,
384
+ "max_tokens": 8192
385
+ }
386
+
387
+ # 獲取第二部分
388
+ second_part = ""
389
+ for attempt in range(max_retries):
390
+ try:
391
+ logger.info(f"發送第二部分 API 請求 (嘗試 {attempt+1}/{max_retries})...")
392
+ if progress_callback:
393
+ progress_callback(f"生成對話第二部分 (嘗試 {attempt+1}/{max_retries})...")
394
+
395
+ response = requests.post(url, headers=headers, json=payload)
396
+
397
+ if response.status_code == 429:
398
+ retry_after = int(response.headers.get('Retry-After', retry_delay))
399
+ logger.warning(f"速率限制錯誤 (429)。將在 {retry_after} 秒後重試。嘗試 {attempt+1}/{max_retries}")
400
+ if progress_callback:
401
+ progress_callback(f"速率限制錯誤 (429)。將在 {retry_after} 秒後重試...")
402
+ time.sleep(retry_after)
403
+ retry_delay *= 2
404
+ continue
405
+
406
+ if response.status_code != 200:
407
+ logger.error(f"API 請求失敗: 狀態碼 {response.status_code}, 原因: {response.reason}")
408
+
409
+ response.raise_for_status()
410
+ result = response.json()
411
+ second_part = result['choices'][0]['message']['content']
412
+ logger.info("成功獲取對話第二部分")
413
+ if progress_callback:
414
+ progress_callback("成功獲取對話第二部分")
415
+ break
416
 
417
+ except requests.exceptions.RequestException as e:
418
+ logger.error(f"請求失敗: {str(e)}")
419
+ if attempt < max_retries - 1:
420
+ logger.info(f"將在 {retry_delay} 秒後重試。嘗試 {attempt+1}/{max_retries}")
421
+ time.sleep(retry_delay)
422
+ retry_delay *= 2
423
+ else:
424
+ return f"{first_part}\n\nError generating second part after {max_retries} attempts: {str(e)}"
425
+
426
+ # 第三部分:生成結尾部分對話
427
+ combined_parts = first_part + "\n\n" + second_part
428
+ third_prompt = base_prompt + f"\n以下是已生成的對話前兩部分,請生成最後10輪對話並提供總結,確保對話自然結束:\n\n{combined_parts[-8000:]}"
429
+
430
+ payload = {
431
+ "model": model,
432
+ "messages": [
433
+ {
434
+ "role": "user",
435
+ "content": third_prompt
436
+ }
437
+ ],
438
+ "temperature": 0.7,
439
+ "max_tokens": 8192
440
+ }
441
+
442
+ # 獲取第三部分
443
+ third_part = ""
444
+ for attempt in range(max_retries):
445
+ try:
446
+ logger.info(f"發送第三部分 API 請求 (嘗試 {attempt+1}/{max_retries})...")
447
+ if progress_callback:
448
+ progress_callback(f"生成對話結尾部分 (嘗試 {attempt+1}/{max_retries})...")
449
+
450
+ response = requests.post(url, headers=headers, json=payload)
451
+
452
+ if response.status_code == 429:
453
+ retry_after = int(response.headers.get('Retry-After', retry_delay))
454
+ logger.warning(f"速率限制錯誤 (429)。將在 {retry_after} 秒後重試。嘗試 {attempt+1}/{max_retries}")
455
+ if progress_callback:
456
+ progress_callback(f"速率限制錯誤 (429)。將在 {retry_after} 秒後重試...")
457
+ time.sleep(retry_after)
458
+ retry_delay *= 2
459
+ continue
460
+
461
+ if response.status_code != 200:
462
+ logger.error(f"API 請求失敗: 狀態碼 {response.status_code}, 原因: {response.reason}")
463
+
464
+ response.raise_for_status()
465
+ result = response.json()
466
+ third_part = result['choices'][0]['message']['content']
467
+ logger.info("成功獲取對話結尾部分")
468
+ if progress_callback:
469
+ progress_callback("成功獲取對話結尾部分")
470
+ break
471
 
472
+ except requests.exceptions.RequestException as e:
473
+ logger.error(f"請求失敗: {str(e)}")
474
+ if attempt < max_retries - 1:
475
+ logger.info(f"將在 {retry_delay} 秒後重試。嘗試 {attempt+1}/{max_retries}")
476
+ time.sleep(retry_delay)
477
+ retry_delay *= 2
478
+ else:
479
+ return f"{combined_parts}\n\nError generating third part after {max_retries} attempts: {str(e)}"
480
+
481
+ # 合併所有部分
482
+ full_dialogue = first_part + "\n\n" + second_part + "\n\n" + third_part
483
+ logger.info("成功生成完整對話,總長度: " + str(len(full_dialogue)) + " 字符")
484
+ if progress_callback:
485
+ progress_callback("成功生成完整對話!")
486
+
487
+ return full_dialogue
488
 
489
  def validate_and_generate_script(
490
  files,