Spaces:
Sleeping
Sleeping
Commit
·
1c3d8d3
1
Parent(s):
d3aaa95
feat: 更新對話生成邏輯,增加輸入文本檢查及模板化系統,移除舊版提示詞欄位
Browse files- __pycache__/prompts.cpython-313.pyc +0 -0
- app.py +98 -96
- prompts.py +1 -38
__pycache__/prompts.cpython-313.pyc
ADDED
|
Binary file (7.89 kB). View file
|
|
|
app.py
CHANGED
|
@@ -16,7 +16,7 @@ import ebooklib
|
|
| 16 |
from ebooklib import epub
|
| 17 |
|
| 18 |
# 導入自定義模組
|
| 19 |
-
from prompts import
|
| 20 |
from quality_control import DialogueQualityChecker, validate_dialogue_structure, suggest_improvements
|
| 21 |
from content_planner import ContentPlanner, SmartContentSplitter, create_adaptive_prompts
|
| 22 |
|
|
@@ -60,15 +60,9 @@ def fetch_models(api_key, api_base=None):
|
|
| 60 |
|
| 61 |
def generate_dialogue_via_requests(
|
| 62 |
pdf_text: str,
|
| 63 |
-
intro_instructions: str,
|
| 64 |
-
text_instructions: str,
|
| 65 |
-
scratch_pad_instructions: str,
|
| 66 |
-
prelude_dialog: str,
|
| 67 |
-
podcast_dialog_instructions: str,
|
| 68 |
model: str,
|
| 69 |
llm_api_key: str,
|
| 70 |
api_base: str,
|
| 71 |
-
edited_transcript: str = None,
|
| 72 |
user_feedback: str = None,
|
| 73 |
num_parts: int = 3,
|
| 74 |
max_input_length: int = 1000000,
|
|
@@ -82,6 +76,14 @@ def generate_dialogue_via_requests(
|
|
| 82 |
"""
|
| 83 |
logger.info(f"準備生成對話,使用模型: {model}")
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
# 限制輸入文本長度
|
| 86 |
original_length = len(pdf_text)
|
| 87 |
if len(pdf_text) > max_input_length:
|
|
@@ -90,15 +92,17 @@ def generate_dialogue_via_requests(
|
|
| 90 |
|
| 91 |
logger.info(f"輸入文本長度: {len(pdf_text)} 字符")
|
| 92 |
|
| 93 |
-
#
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
-
#
|
| 97 |
if user_feedback:
|
| 98 |
base_prompt += f"\n\n【額外要求】\n{user_feedback}"
|
| 99 |
-
|
| 100 |
-
if edited_transcript:
|
| 101 |
-
base_prompt += f"\n\n【參考文稿】\n{edited_transcript}"
|
| 102 |
|
| 103 |
headers = {
|
| 104 |
"Authorization": f"Bearer {llm_api_key}",
|
|
@@ -174,6 +178,11 @@ def generate_dialogue_via_requests(
|
|
| 174 |
generated_content.strip().endswith(('在', '的', '了', '是', '會', '但', '因為', '所以', '這', '那'))
|
| 175 |
)
|
| 176 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
if is_truncated:
|
| 178 |
logger.warning("檢測到內容可能被截斷,嘗試分批生成...")
|
| 179 |
if progress_callback:
|
|
@@ -242,8 +251,7 @@ def generate_summary(
|
|
| 242 |
|
| 243 |
# 從 prompts 模組獲取摘要模板
|
| 244 |
try:
|
| 245 |
-
|
| 246 |
-
prompt = summary_template.format(content=script_content)
|
| 247 |
except KeyError:
|
| 248 |
return f"錯誤:未找到摘要模板 '{summary_type}'"
|
| 249 |
|
|
@@ -295,6 +303,11 @@ def _generate_in_batches(pdf_text, base_prompt, headers, url, model, num_parts,
|
|
| 295 |
分批生成的備用機制,只在單次生成被截斷時使用
|
| 296 |
"""
|
| 297 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
logger.info(f"開始分批生成,共 {num_parts} 個部分")
|
| 299 |
|
| 300 |
# 生成內容大綱(簡化版)
|
|
@@ -342,6 +355,7 @@ def _generate_in_batches(pdf_text, base_prompt, headers, url, model, num_parts,
|
|
| 342 |
- 按照正常格式開場:speaker-1: 歡迎收聽 David888 Podcast,我是 David...
|
| 343 |
- speaker-2 首次發言時自我介紹為 Cordelia
|
| 344 |
- 討論前面的主題
|
|
|
|
| 345 |
- **不要結束對話**,在一個開放的討論點停止
|
| 346 |
- 必須使用繁體中文,格式為 speaker-1: 和 speaker-2:
|
| 347 |
"""
|
|
@@ -361,7 +375,8 @@ def _generate_in_batches(pdf_text, base_prompt, headers, url, model, num_parts,
|
|
| 361 |
請:
|
| 362 |
1. **不要重複開場**,直接繼續前面的對話
|
| 363 |
2. 完成剩餘主題的討論
|
| 364 |
-
3.
|
|
|
|
| 365 |
|
| 366 |
**必須使用繁體中文,格式為 speaker-1: 和 speaker-2:**
|
| 367 |
"""
|
|
@@ -381,17 +396,18 @@ def _generate_in_batches(pdf_text, base_prompt, headers, url, model, num_parts,
|
|
| 381 |
請:
|
| 382 |
1. **不要重複開場**,直接繼續前面的對話
|
| 383 |
2. ��論相應的主題
|
| 384 |
-
3.
|
|
|
|
| 385 |
|
| 386 |
**必須使用繁體中文,格式為 speaker-1: 和 speaker-2:**
|
| 387 |
"""
|
| 388 |
|
| 389 |
-
#
|
| 390 |
part_payload = {
|
| 391 |
"model": model,
|
| 392 |
"messages": [{"role": "user", "content": part_prompt}],
|
| 393 |
"temperature": 0.7,
|
| 394 |
-
"max_tokens":
|
| 395 |
}
|
| 396 |
|
| 397 |
for attempt in range(max_retries):
|
|
@@ -436,12 +452,7 @@ def validate_and_generate_script(
|
|
| 436 |
openai_api_key,
|
| 437 |
text_model,
|
| 438 |
api_base_value,
|
| 439 |
-
|
| 440 |
-
text_instructions,
|
| 441 |
-
scratch_pad_instructions,
|
| 442 |
-
prelude_dialog,
|
| 443 |
-
podcast_dialog_instructions,
|
| 444 |
-
edited_transcript,
|
| 445 |
user_feedback,
|
| 446 |
num_parts=3,
|
| 447 |
max_input_length=1000000,
|
|
@@ -514,21 +525,44 @@ def validate_and_generate_script(
|
|
| 514 |
progress_callback(f"處理 EPUB 文件: {os.path.basename(filename)}")
|
| 515 |
|
| 516 |
book = epub.read_epub(file.name)
|
| 517 |
-
|
|
|
|
| 518 |
processed_count = 0
|
| 519 |
|
| 520 |
-
|
| 521 |
-
|
|
|
|
|
|
|
|
|
|
| 522 |
try:
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 532 |
|
| 533 |
logger.info(f"EPUB 處理完成: {filename}, 共處理 {processed_count} 個項目")
|
| 534 |
if progress_callback:
|
|
@@ -538,6 +572,12 @@ def validate_and_generate_script(
|
|
| 538 |
logger.error(error_msg)
|
| 539 |
if progress_callback:
|
| 540 |
progress_callback(error_msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 541 |
else:
|
| 542 |
logger.warning(f"跳過不支持的文件格式: {filename}")
|
| 543 |
if progress_callback:
|
|
@@ -548,6 +588,14 @@ def validate_and_generate_script(
|
|
| 548 |
if progress_callback:
|
| 549 |
progress_callback(f"所有文件處理完成,合併文本長度: {text_length} 字符")
|
| 550 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
# 生成對話腳本
|
| 552 |
logger.info("開始生成腳本...")
|
| 553 |
if progress_callback:
|
|
@@ -555,21 +603,15 @@ def validate_and_generate_script(
|
|
| 555 |
|
| 556 |
script = generate_dialogue_via_requests(
|
| 557 |
pdf_text=combined_text,
|
| 558 |
-
intro_instructions=intro_instructions,
|
| 559 |
-
text_instructions=text_instructions,
|
| 560 |
-
scratch_pad_instructions=scratch_pad_instructions,
|
| 561 |
-
prelude_dialog=prelude_dialog,
|
| 562 |
-
podcast_dialog_instructions=podcast_dialog_instructions,
|
| 563 |
model=text_model,
|
| 564 |
llm_api_key=openai_api_key,
|
| 565 |
api_base=api_base_value,
|
| 566 |
-
edited_transcript=edited_transcript,
|
| 567 |
user_feedback=user_feedback,
|
| 568 |
num_parts=num_parts,
|
| 569 |
max_input_length=max_input_length,
|
| 570 |
max_output_tokens=max_output_tokens,
|
| 571 |
progress_callback=progress_callback,
|
| 572 |
-
template_type=
|
| 573 |
)
|
| 574 |
|
| 575 |
logger.info("腳本生成完成")
|
|
@@ -632,44 +674,14 @@ with gr.Blocks(title="Script Generator", css="""
|
|
| 632 |
interactive=True
|
| 633 |
)
|
| 634 |
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
lines=5,
|
| 638 |
-
value=INSTRUCTION_TEMPLATES["podcast"]["intro"],
|
| 639 |
-
interactive=True,
|
| 640 |
-
visible=False # 隱藏此欄位
|
| 641 |
-
)
|
| 642 |
-
|
| 643 |
-
text_instructions = gr.Textbox(
|
| 644 |
-
label="文本分析提示詞 | Text Instructions",
|
| 645 |
-
lines=5,
|
| 646 |
-
value=INSTRUCTION_TEMPLATES["podcast"]["text_instructions"],
|
| 647 |
-
interactive=True,
|
| 648 |
-
visible=False # 隱藏此欄位
|
| 649 |
-
)
|
| 650 |
-
|
| 651 |
-
scratch_pad = gr.Textbox(
|
| 652 |
-
label="腦力激盪提示詞 | Scratch Pad",
|
| 653 |
-
lines=5,
|
| 654 |
-
value=INSTRUCTION_TEMPLATES["podcast"]["scratch_pad"],
|
| 655 |
-
interactive=True,
|
| 656 |
-
visible=False # 隱藏此欄位
|
| 657 |
-
)
|
| 658 |
-
|
| 659 |
-
prelude = gr.Textbox(
|
| 660 |
-
label="前導提示詞 | Prelude",
|
| 661 |
-
lines=5,
|
| 662 |
-
value=INSTRUCTION_TEMPLATES["podcast"]["prelude"],
|
| 663 |
-
interactive=True,
|
| 664 |
-
visible=False # 隱藏此欄位
|
| 665 |
-
)
|
| 666 |
-
|
| 667 |
dialog = gr.Textbox(
|
| 668 |
-
label="
|
| 669 |
lines=8,
|
| 670 |
-
value=
|
| 671 |
-
interactive=
|
| 672 |
-
info="
|
| 673 |
)
|
| 674 |
|
| 675 |
custom_prompt = gr.Textbox(
|
|
@@ -764,17 +776,12 @@ with gr.Blocks(title="Script Generator", css="""
|
|
| 764 |
def update_template(template):
|
| 765 |
logger.info(f"切換模板至: {template}")
|
| 766 |
try:
|
| 767 |
-
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
template_data["text_instructions"],
|
| 771 |
-
template_data["scratch_pad"],
|
| 772 |
-
template_data["prelude"],
|
| 773 |
-
template_data["dialog"]
|
| 774 |
-
]
|
| 775 |
except KeyError:
|
| 776 |
logger.error(f"模板 {template} 不存在")
|
| 777 |
-
return
|
| 778 |
|
| 779 |
fetch_button.click(
|
| 780 |
fn=handle_model_fetch,
|
|
@@ -785,7 +792,7 @@ with gr.Blocks(title="Script Generator", css="""
|
|
| 785 |
template_dropdown.change(
|
| 786 |
fn=update_template,
|
| 787 |
inputs=[template_dropdown],
|
| 788 |
-
outputs=[
|
| 789 |
)
|
| 790 |
|
| 791 |
def handle_script_generation(*args):
|
|
@@ -828,12 +835,7 @@ with gr.Blocks(title="Script Generator", css="""
|
|
| 828 |
api_key,
|
| 829 |
model_dropdown,
|
| 830 |
api_base,
|
| 831 |
-
|
| 832 |
-
text_instructions,
|
| 833 |
-
scratch_pad,
|
| 834 |
-
prelude,
|
| 835 |
-
dialog,
|
| 836 |
-
gr.Textbox(value=""), # edited_transcript
|
| 837 |
custom_prompt, # user_feedback
|
| 838 |
num_parts_slider, # 添加滑動條參數
|
| 839 |
max_input_length_slider, # 添加最大輸入文本長度參數
|
|
|
|
| 16 |
from ebooklib import epub
|
| 17 |
|
| 18 |
# 導入自定義模組
|
| 19 |
+
from prompts import get_prompt, get_all_template_names
|
| 20 |
from quality_control import DialogueQualityChecker, validate_dialogue_structure, suggest_improvements
|
| 21 |
from content_planner import ContentPlanner, SmartContentSplitter, create_adaptive_prompts
|
| 22 |
|
|
|
|
| 60 |
|
| 61 |
def generate_dialogue_via_requests(
|
| 62 |
pdf_text: str,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
model: str,
|
| 64 |
llm_api_key: str,
|
| 65 |
api_base: str,
|
|
|
|
| 66 |
user_feedback: str = None,
|
| 67 |
num_parts: int = 3,
|
| 68 |
max_input_length: int = 1000000,
|
|
|
|
| 76 |
"""
|
| 77 |
logger.info(f"準備生成對話,使用模型: {model}")
|
| 78 |
|
| 79 |
+
# 檢查輸入文本是否為空或過短
|
| 80 |
+
if not pdf_text or len(pdf_text.strip()) < 50:
|
| 81 |
+
error_msg = f"錯誤:輸入文本過短或為空(當前長度: {len(pdf_text)} 字符)。請確認上傳的文件包含有效內容。"
|
| 82 |
+
logger.error(error_msg)
|
| 83 |
+
if progress_callback:
|
| 84 |
+
progress_callback(error_msg)
|
| 85 |
+
return error_msg
|
| 86 |
+
|
| 87 |
# 限制輸入文本長度
|
| 88 |
original_length = len(pdf_text)
|
| 89 |
if len(pdf_text) > max_input_length:
|
|
|
|
| 92 |
|
| 93 |
logger.info(f"輸入文本長度: {len(pdf_text)} 字符")
|
| 94 |
|
| 95 |
+
# 使用直接從 prompts 模組獲取的模板
|
| 96 |
+
try:
|
| 97 |
+
base_prompt = get_prompt(template_type, pdf_text)
|
| 98 |
+
except KeyError:
|
| 99 |
+
# 如果模板不存在,使用默認的 podcast 模板
|
| 100 |
+
logger.warning(f"模板 '{template_type}' 不存在,使用默認 podcast 模板")
|
| 101 |
+
base_prompt = get_prompt("podcast", pdf_text)
|
| 102 |
|
| 103 |
+
# 如果有自定義提示詞,添加到提示中
|
| 104 |
if user_feedback:
|
| 105 |
base_prompt += f"\n\n【額外要求】\n{user_feedback}"
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
headers = {
|
| 108 |
"Authorization": f"Bearer {llm_api_key}",
|
|
|
|
| 178 |
generated_content.strip().endswith(('在', '的', '了', '是', '會', '但', '因為', '所以', '這', '那'))
|
| 179 |
)
|
| 180 |
|
| 181 |
+
# 如果原始輸入文本為空或太短,不進行分批生成
|
| 182 |
+
if len(pdf_text.strip()) < 100:
|
| 183 |
+
logger.warning("輸入文本太短或為空,跳過分批生成")
|
| 184 |
+
is_truncated = False
|
| 185 |
+
|
| 186 |
if is_truncated:
|
| 187 |
logger.warning("檢測到內容可能被截斷,嘗試分批生成...")
|
| 188 |
if progress_callback:
|
|
|
|
| 251 |
|
| 252 |
# 從 prompts 模組獲取摘要模板
|
| 253 |
try:
|
| 254 |
+
prompt = get_prompt(summary_type, script_content)
|
|
|
|
| 255 |
except KeyError:
|
| 256 |
return f"錯誤:未找到摘要模板 '{summary_type}'"
|
| 257 |
|
|
|
|
| 303 |
分批生成的備用機制,只在單次生成被截斷時使用
|
| 304 |
"""
|
| 305 |
try:
|
| 306 |
+
# 檢查輸入文本是否足夠
|
| 307 |
+
if not pdf_text or len(pdf_text.strip()) < 100:
|
| 308 |
+
logger.error("輸入文本為空或太短,無法進行分批生成")
|
| 309 |
+
return None
|
| 310 |
+
|
| 311 |
logger.info(f"開始分批生成,共 {num_parts} 個部分")
|
| 312 |
|
| 313 |
# 生成內容大綱(簡化版)
|
|
|
|
| 355 |
- 按照正常格式開場:speaker-1: 歡迎收聽 David888 Podcast,我是 David...
|
| 356 |
- speaker-2 首次發言時自我介紹為 Cordelia
|
| 357 |
- 討論前面的主題
|
| 358 |
+
- **生成至少 20-30 輪對話**
|
| 359 |
- **不要結束對話**,在一個開放的討論點停止
|
| 360 |
- 必須使用繁體中文,格式為 speaker-1: 和 speaker-2:
|
| 361 |
"""
|
|
|
|
| 375 |
請:
|
| 376 |
1. **不要重複開場**,直接繼續前面的對話
|
| 377 |
2. 完成剩餘主題的討論
|
| 378 |
+
3. **生成至少 20-30 輪對話**
|
| 379 |
+
4. **自然地結束對話**,包含總結和告別
|
| 380 |
|
| 381 |
**必須使用繁體中文,格式為 speaker-1: 和 speaker-2:**
|
| 382 |
"""
|
|
|
|
| 396 |
請:
|
| 397 |
1. **不要重複開場**,直接繼續前面的對話
|
| 398 |
2. ��論相應的主題
|
| 399 |
+
3. **生成至少 20-30 輪對話**
|
| 400 |
+
4. **不要結束對話**,在一個開放的討論點停止
|
| 401 |
|
| 402 |
**必須使用繁體中文,格式為 speaker-1: 和 speaker-2:**
|
| 403 |
"""
|
| 404 |
|
| 405 |
+
# 生成當前部分,使用更高的 token 限制
|
| 406 |
part_payload = {
|
| 407 |
"model": model,
|
| 408 |
"messages": [{"role": "user", "content": part_prompt}],
|
| 409 |
"temperature": 0.7,
|
| 410 |
+
"max_tokens": 16384 # 提高每部分的 token 限制
|
| 411 |
}
|
| 412 |
|
| 413 |
for attempt in range(max_retries):
|
|
|
|
| 452 |
openai_api_key,
|
| 453 |
text_model,
|
| 454 |
api_base_value,
|
| 455 |
+
template_type,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 456 |
user_feedback,
|
| 457 |
num_parts=3,
|
| 458 |
max_input_length=1000000,
|
|
|
|
| 525 |
progress_callback(f"處理 EPUB 文件: {os.path.basename(filename)}")
|
| 526 |
|
| 527 |
book = epub.read_epub(file.name)
|
| 528 |
+
items = list(book.get_items())
|
| 529 |
+
item_count = len(items)
|
| 530 |
processed_count = 0
|
| 531 |
|
| 532 |
+
# 如果沒有找到任何項目,嘗試替代方法
|
| 533 |
+
if item_count == 0:
|
| 534 |
+
logger.warning(f"EPUB 文件沒有找到項目,嘗試替代處理方法: {filename}")
|
| 535 |
+
# 嘗試直接讀取 spine 中的文檔
|
| 536 |
+
for spine_item in book.spine:
|
| 537 |
try:
|
| 538 |
+
item_id = spine_item[0]
|
| 539 |
+
item = book.get_item_by_id(item_id)
|
| 540 |
+
if item:
|
| 541 |
+
soup = BeautifulSoup(item.get_body_content(), 'html.parser')
|
| 542 |
+
item_text = soup.get_text()
|
| 543 |
+
if item_text.strip(): # 只添加非空內容
|
| 544 |
+
combined_text += item_text + "\n\n"
|
| 545 |
+
processed_count += 1
|
| 546 |
+
except Exception as spine_error:
|
| 547 |
+
logger.debug(f"處理 spine 項目失敗: {spine_error}")
|
| 548 |
+
continue
|
| 549 |
+
else:
|
| 550 |
+
# 正常處理流程
|
| 551 |
+
for item in items:
|
| 552 |
+
if item.get_type() == ebooklib.ITEM_DOCUMENT:
|
| 553 |
+
try:
|
| 554 |
+
processed_count += 1
|
| 555 |
+
soup = BeautifulSoup(item.get_body_content(), 'html.parser')
|
| 556 |
+
item_text = soup.get_text()
|
| 557 |
+
if item_text.strip(): # 只添加非空內容
|
| 558 |
+
combined_text += item_text + "\n\n"
|
| 559 |
+
|
| 560 |
+
logger.debug(f"已處理 EPUB 項目 {processed_count}/{item_count}")
|
| 561 |
+
if processed_count % 5 == 0 and progress_callback:
|
| 562 |
+
progress_callback(f"處理 EPUB: {os.path.basename(filename)} - {processed_count}/{item_count} 項目")
|
| 563 |
+
except Exception as item_error:
|
| 564 |
+
logger.error(f"EPUB 項目處理錯誤: {item_error}")
|
| 565 |
+
continue
|
| 566 |
|
| 567 |
logger.info(f"EPUB 處理完成: {filename}, 共處理 {processed_count} 個項目")
|
| 568 |
if progress_callback:
|
|
|
|
| 572 |
logger.error(error_msg)
|
| 573 |
if progress_callback:
|
| 574 |
progress_callback(error_msg)
|
| 575 |
+
|
| 576 |
+
# 提供 EPUB 處理失敗的具體建議
|
| 577 |
+
suggestion = "建議:1) 檢查 EPUB 文件是否完整;2) 嘗試用其他工具轉換為 PDF 或 TXT 格式後重新上傳"
|
| 578 |
+
logger.info(suggestion)
|
| 579 |
+
if progress_callback:
|
| 580 |
+
progress_callback(suggestion)
|
| 581 |
else:
|
| 582 |
logger.warning(f"跳過不支持的文件格式: {filename}")
|
| 583 |
if progress_callback:
|
|
|
|
| 588 |
if progress_callback:
|
| 589 |
progress_callback(f"所有文件處理完成,合併文本長度: {text_length} 字符")
|
| 590 |
|
| 591 |
+
# 檢查是否成功提取到文本內容
|
| 592 |
+
if text_length < 50:
|
| 593 |
+
error_msg = f"錯誤:未能從上傳的文件中提取到足夠的文本內容(提取到 {text_length} 字符)。請檢查文件格式和內容是否正確。"
|
| 594 |
+
logger.error(error_msg)
|
| 595 |
+
if progress_callback:
|
| 596 |
+
progress_callback(error_msg)
|
| 597 |
+
return None, error_msg
|
| 598 |
+
|
| 599 |
# 生成對話腳本
|
| 600 |
logger.info("開始生成腳本...")
|
| 601 |
if progress_callback:
|
|
|
|
| 603 |
|
| 604 |
script = generate_dialogue_via_requests(
|
| 605 |
pdf_text=combined_text,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 606 |
model=text_model,
|
| 607 |
llm_api_key=openai_api_key,
|
| 608 |
api_base=api_base_value,
|
|
|
|
| 609 |
user_feedback=user_feedback,
|
| 610 |
num_parts=num_parts,
|
| 611 |
max_input_length=max_input_length,
|
| 612 |
max_output_tokens=max_output_tokens,
|
| 613 |
progress_callback=progress_callback,
|
| 614 |
+
template_type=template_type
|
| 615 |
)
|
| 616 |
|
| 617 |
logger.info("腳本生成完成")
|
|
|
|
| 674 |
interactive=True
|
| 675 |
)
|
| 676 |
|
| 677 |
+
# 移除舊版本的多個提示詞欄位,現在使用模板化系統
|
| 678 |
+
# 只保留一個模板預覽欄位
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 679 |
dialog = gr.Textbox(
|
| 680 |
+
label="模板預覽 | Template Preview",
|
| 681 |
lines=8,
|
| 682 |
+
value="選擇模板後將顯示提示詞內容",
|
| 683 |
+
interactive=False,
|
| 684 |
+
info="這是當前選擇模板的提示詞內容預覽"
|
| 685 |
)
|
| 686 |
|
| 687 |
custom_prompt = gr.Textbox(
|
|
|
|
| 776 |
def update_template(template):
|
| 777 |
logger.info(f"切換模板至: {template}")
|
| 778 |
try:
|
| 779 |
+
# 使用新的模板系統
|
| 780 |
+
template_content = get_prompt(template, "[內容將在此處顯示]")
|
| 781 |
+
return template_content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 782 |
except KeyError:
|
| 783 |
logger.error(f"模板 {template} 不存在")
|
| 784 |
+
return "錯誤:模板不存在"
|
| 785 |
|
| 786 |
fetch_button.click(
|
| 787 |
fn=handle_model_fetch,
|
|
|
|
| 792 |
template_dropdown.change(
|
| 793 |
fn=update_template,
|
| 794 |
inputs=[template_dropdown],
|
| 795 |
+
outputs=[dialog]
|
| 796 |
)
|
| 797 |
|
| 798 |
def handle_script_generation(*args):
|
|
|
|
| 835 |
api_key,
|
| 836 |
model_dropdown,
|
| 837 |
api_base,
|
| 838 |
+
template_dropdown, # 使用模板選擇
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 839 |
custom_prompt, # user_feedback
|
| 840 |
num_parts_slider, # 添加滑動條參數
|
| 841 |
max_input_length_slider, # 添加最大輸入文本長度參數
|
prompts.py
CHANGED
|
@@ -133,7 +133,6 @@ PROMPTS = {
|
|
| 133 |
|
| 134 |
{content}""",
|
| 135 |
|
| 136 |
-
# 新增的摘要模板
|
| 137 |
"blog-summary": """你是 David888 Podcast 中文博客的編輯,將播客內容改寫成適合搜索引擎收錄的博客文章。
|
| 138 |
|
| 139 |
【工作目標】
|
|
@@ -236,40 +235,4 @@ def validate_template(template: str) -> bool:
|
|
| 236 |
template.format(content="test")
|
| 237 |
return True
|
| 238 |
except (KeyError, ValueError):
|
| 239 |
-
return False
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
# 為了向後兼容,提供舊版本接口
|
| 243 |
-
def get_template(template_name: str) -> dict:
|
| 244 |
-
"""
|
| 245 |
-
向後兼容函數:模擬舊版本的模板格式
|
| 246 |
-
|
| 247 |
-
Args:
|
| 248 |
-
template_name: 模板名稱
|
| 249 |
-
|
| 250 |
-
Returns:
|
| 251 |
-
dict: 模擬舊版本格式的模板數據
|
| 252 |
-
"""
|
| 253 |
-
if template_name not in PROMPTS:
|
| 254 |
-
available_templates = list(PROMPTS.keys())
|
| 255 |
-
raise KeyError(f"模板 '{template_name}' 不存在。可用模板: {available_templates}")
|
| 256 |
-
|
| 257 |
-
# 為向後兼容,將新格式轉換為舊格式
|
| 258 |
-
prompt = PROMPTS[template_name]
|
| 259 |
-
return {
|
| 260 |
-
"intro": f"使用模板: {template_name}",
|
| 261 |
-
"text_instructions": "處理輸入文本",
|
| 262 |
-
"scratch_pad": "分析和規劃內容",
|
| 263 |
-
"prelude": "準備生成內容",
|
| 264 |
-
"dialog": prompt
|
| 265 |
-
}
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
# 舊版本兼容
|
| 269 |
-
def get_template_names():
|
| 270 |
-
"""向後兼容的函數名稱"""
|
| 271 |
-
return get_all_template_names()
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
# 舊版本兼容
|
| 275 |
-
INSTRUCTION_TEMPLATES = {name: get_template(name) for name in PROMPTS.keys()}
|
|
|
|
| 133 |
|
| 134 |
{content}""",
|
| 135 |
|
|
|
|
| 136 |
"blog-summary": """你是 David888 Podcast 中文博客的編輯,將播客內容改寫成適合搜索引擎收錄的博客文章。
|
| 137 |
|
| 138 |
【工作目標】
|
|
|
|
| 235 |
template.format(content="test")
|
| 236 |
return True
|
| 237 |
except (KeyError, ValueError):
|
| 238 |
+
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|