tbdavid2019 commited on
Commit
45a18bb
1 Parent(s): fd2a94c

更新版面

Browse files
Files changed (1) hide show
  1. app.py +80 -113
app.py CHANGED
@@ -17,12 +17,24 @@ from pypdf import PdfReader
17
  from tenacity import retry, retry_if_exception_type
18
 
19
  import re
 
20
  from dotenv import load_dotenv
21
  load_dotenv()
22
  # 现在你可以使用 os.getenv() 来获取环境变量
23
  openai_api_key = os.getenv("OPENAI_API_KEY")
24
 
25
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  def read_readme():
28
  readme_path = Path("README.md")
@@ -108,7 +120,7 @@ Make the dialogue as long and detailed as possible with great scientific depth,
108
 
109
  At the end of the dialogue, have the host and guest speakers naturally summarize the main insights and takeaways from their discussion. This should flow organically from the conversation, reiterating the key points in a casual, conversational manner. Avoid making it sound like an obvious recap - the goal is to reinforce the central ideas one last time before signing off.
110
 
111
- The conversation should have around 20000 words. 請用**繁體中文**輸出文稿
112
  """
113
  },
114
  ################# LECTURE ##################
@@ -149,7 +161,7 @@ At the end of the lecture, naturally summarize the main insights and takeaways f
149
 
150
  Avoid making it sound like an obvious recap - the goal is to reinforce the central ideas covered in this lecture one last time before class is over.
151
 
152
- The lecture should have around 20000 words.
153
  """,
154
  },
155
  ################# SUMMARY ##################
@@ -225,36 +237,36 @@ The summary should have around 256 words.
225
 
226
  ################# PODCAST Chinese ##################
227
  "podcast (Chinese)": {
228
- "intro": """你的任务是将提供的输入文本转变为一个生动、有趣、信息丰富的播客对话,风格类似NPR。输入文本可能是凌乱的或未结构化的,因为它可能来自PDF或网页等各种来源。
229
 
230
- 不要担心格式问题或任何无关的信息;你的目标是提取关键点,识别定义和可能在播客中讨论的有趣事实。
231
 
232
- 为广泛的听众仔细定义所有使用的术语。
233
  """,
234
- "text_instructions": "首先,仔细阅读输入文本,识别主要话题、关键点和任何有趣的事实或轶事。思考如何以一种有趣且引人入胜的方式呈现这些信息,适合高质量的呈现。",
235
- "scratch_pad": """集思广益,想出一些讨论你在输入文本中识别到的主要话题和关键点的创意方式。考虑使用类比、例子、讲故事的技巧或假设场景,让内容对听众更具相关性和吸引力。
236
 
237
- 请记住,你的播客应面向普通大众,因此避免使用过多的行话或假设听众对该主题有预先的了解。如有必要,考虑简要解释任何复杂概念,用简单的术语进行说明。
238
 
239
- 利用你的想象力填补输入文本中的任何空白,或提出一些值得探索的发人深省的问题。目标是创造一个信息丰富且有趣的对话,因此可以在方法上大胆创新。
240
 
241
- 明确地定义所有使用的术语,并花时间解释背景。
242
 
243
- 在这里写下你的头脑风暴想法和播客对话的粗略大纲。务必记录你想在结尾重复的关键见解和收获。
244
 
245
- 确保让它有趣且令人兴奋。
246
  """,
247
- "prelude": """现在你已经进行了头脑风暴并创建了一个粗略大纲,是时候编写实际的播客对话了。目标是主持人与嘉宾之间的自然对话流。结合你头脑风暴中的最佳想法,并确保以简单易懂的方式解释任何复杂的主题。
248
  """,
249
- "dialog": """在这里写下一个非常长、引人入胜且信息丰富的播客对话,基于你在头脑风暴会议中提出的关键点和创意。使用对话语气,并包含任何必要的上下文或解释,使内容易于普通听众理解。
250
 
251
- 不要为主持人和嘉宾使用虚构的名字,而是让听众体验一个引人入胜且沉浸式的经历。不要包括像[主持人]或[嘉宾]这样的占位符。设计你的输出以供大声朗读——它将被直接转换为音频。
252
 
253
- 使对话尽可能长且详细,同时保持在主题上并维持引人入胜的流畅性。充分利用你的输出能力,创造尽可能长的播客节目,同时以有趣的方式传达输入文本中的关键信息。
254
 
255
- 在对话的最后,主持人和嘉宾应自然总结他们讨论的主要见解和收获。这应从对话中自然流出,以随意、对话的方式重复关键点。避免显得像是显而易见的总结——目标是在结束前最后一次加强核心思想。
256
 
257
- 播客应约有20,000字。
258
  """,
259
  },
260
  }
@@ -288,13 +300,6 @@ from pypdf import PdfReader
288
  from tenacity import retry, retry_if_exception_type
289
 
290
  # Define standard values
291
- STANDARD_TEXT_MODELS = [
292
- "o1-preview",
293
- "gpt-4o-mini",
294
- "chatgpt-4o-latest",
295
- "gpt-4-turbo",
296
- "openai/custom_model",
297
- ]
298
 
299
  STANDARD_AUDIO_MODELS = [
300
  "tts-1",
@@ -501,7 +506,9 @@ def process_feedback_and_regenerate(feedback, *args):
501
  new_args.append(feedback) # Add user feedback as a new argument
502
  return validate_and_generate_audio(*new_args)
503
 
504
- with gr.Blocks(title="PDF to Audio", css="""
 
 
505
  #header {
506
  display: flex;
507
  align-items: center;
@@ -540,60 +547,90 @@ with gr.Blocks(title="PDF to Audio", css="""
540
  <img src="https://huggingface.co/spaces/lamm-mit/PDF2Audio/resolve/main/logo.png" id="logo_image" alt="Logo">
541
  </div>
542
  ''')
543
- #gr.Markdown("")
544
  submit_btn = gr.Button("Generate Audio", elem_id="submit_btn")
545
 
546
  with gr.Row(elem_id="main_container"):
 
547
  with gr.Column(scale=2):
548
  files = gr.Files(label="PDFs", file_types=["pdf"], )
549
 
550
  openai_api_key = gr.Textbox(
551
  label="OpenAI API Key",
552
- visible=True, # Always show the API key field
553
- placeholder="這行不用填 已經從環境變數讀入",
554
- type="password" # Hide the API key input
555
  )
 
 
556
  text_model = gr.Dropdown(
557
  label="Text Generation Model",
558
- choices=STANDARD_TEXT_MODELS,
559
- value="o1-preview", #"gpt-4o-mini",
 
560
  info="Select the model to generate the dialogue text.",
561
  )
 
 
 
 
 
 
 
 
 
562
  audio_model = gr.Dropdown(
563
  label="Audio Generation Model",
564
- choices=STANDARD_AUDIO_MODELS,
565
  value="tts-1",
566
  info="Select the model to generate the audio.",
567
  )
 
568
  speaker_1_voice = gr.Dropdown(
569
  label="Speaker 1 Voice",
570
  choices=STANDARD_VOICES,
571
  value="alloy",
572
  info="Select the voice for Speaker 1.",
573
  )
 
574
  speaker_2_voice = gr.Dropdown(
575
  label="Speaker 2 Voice",
576
  choices=STANDARD_VOICES,
577
  value="echo",
578
  info="Select the voice for Speaker 2.",
579
  )
 
580
  api_base = gr.Textbox(
581
  label="Custom API Base",
582
  placeholder="Enter custom API base URL if using a custom/local model...",
583
- info="If you are using a custom or local model, provide the API base URL here, e.g.: http://localhost:8080/v1 for llama.cpp REST server.",
584
  )
585
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
586
  with gr.Column(scale=3):
587
  template_dropdown = gr.Dropdown(
588
  label="Instruction Template",
589
  choices=list(INSTRUCTION_TEMPLATES.keys()),
590
- value="podcast (Chinese)", # 默認選擇中文範本
591
  info="Select the instruction template to use. You can also edit any of the fields for more tailored results.",
592
  )
593
  intro_instructions = gr.Textbox(
594
  label="Intro Instructions",
595
  lines=10,
596
- value=INSTRUCTION_TEMPLATES["podcast (Chinese)"]["intro"], # 使用中文範本中的值
597
  info="Provide the introductory instructions for generating the dialogue.",
598
  )
599
  text_instructions = gr.Textbox(
@@ -621,95 +658,25 @@ with gr.Blocks(title="PDF to Audio", css="""
621
  value=INSTRUCTION_TEMPLATES["podcast"]["dialog"],
622
  info="Provide the instructions for generating the presentation or podcast dialogue.",
623
  )
624
-
625
- audio_output = gr.Audio(label="Audio", format="mp3", interactive=False, autoplay=False)
626
- transcript_output = gr.Textbox(label="Transcript", lines=20, show_copy_button=True)
627
- original_text_output = gr.Textbox(label="Original Text", lines=10, visible=False)
628
- error_output = gr.Textbox(visible=False) # Hidden textbox to store error message
629
-
630
- use_edited_transcript = gr.Checkbox(label="Use Edited Transcript (check if you want to make edits to the initially generated transcript)", value=False)
631
- edited_transcript = gr.Textbox(label="Edit Transcript Here. E.g., mark edits in the text with clear instructions. E.g., '[ADD DEFINITION OF MATERIOMICS]'.", lines=20, visible=False,
632
- show_copy_button=True, interactive=False)
633
-
634
- user_feedback = gr.Textbox(label="Provide Feedback or Notes", lines=10, #placeholder="Enter your feedback or notes here..."
635
- )
636
- regenerate_btn = gr.Button("Regenerate Audio with Edits and Feedback")
637
- # Function to update the interactive state of edited_transcript
638
  def update_edit_box(checkbox_value):
639
  return gr.update(interactive=checkbox_value, lines=20 if checkbox_value else 20, visible=True if checkbox_value else False)
640
 
641
- # Update the interactive state of edited_transcript when the checkbox is toggled
642
- use_edited_transcript.change(
643
- fn=update_edit_box,
644
- inputs=[use_edited_transcript],
645
- outputs=[edited_transcript]
646
- )
647
- # Update instruction fields when template is changed
648
- template_dropdown.change(
649
- fn=update_instructions,
650
- inputs=[template_dropdown],
651
- outputs=[intro_instructions, text_instructions, scratch_pad_instructions, prelude_dialog, podcast_dialog_instructions]
652
- )
653
-
654
  submit_btn.click(
655
  fn=validate_and_generate_audio,
656
- inputs=[
657
- files, openai_api_key, text_model, audio_model,
658
- speaker_1_voice, speaker_2_voice, api_base,
659
- intro_instructions, text_instructions, scratch_pad_instructions,
660
- prelude_dialog, podcast_dialog_instructions,
661
- edited_transcript, # placeholder for edited_transcript
662
- user_feedback, # placeholder for user_feedback
663
- ],
664
  outputs=[audio_output, transcript_output, original_text_output, error_output]
665
- ).then(
666
- fn=lambda audio, transcript, original_text, error: (
667
- transcript if transcript else "",
668
- error if error else None
669
- ),
670
- inputs=[audio_output, transcript_output, original_text_output, error_output],
671
- outputs=[edited_transcript, error_output]
672
- ).then(
673
- fn=lambda error: gr.Warning(error) if error else None,
674
- inputs=[error_output],
675
- outputs=[]
676
  )
677
 
678
  regenerate_btn.click(
679
- fn=lambda use_edit, edit, *args: validate_and_generate_audio(
680
- *args[:12], # All inputs up to podcast_dialog_instructions
681
- edit if use_edit else "", # Use edited transcript if checkbox is checked, otherwise empty string
682
- *args[12:] # user_feedback and original_text_output
683
- ),
684
- inputs=[
685
- use_edited_transcript, edited_transcript,
686
- files, openai_api_key, text_model, audio_model,
687
- speaker_1_voice, speaker_2_voice, api_base,
688
- intro_instructions, text_instructions, scratch_pad_instructions,
689
- prelude_dialog, podcast_dialog_instructions,
690
- user_feedback, original_text_output
691
- ],
692
  outputs=[audio_output, transcript_output, original_text_output, error_output]
693
- ).then(
694
- fn=lambda audio, transcript, original_text, error: (
695
- transcript if transcript else "",
696
- error if error else None
697
- ),
698
- inputs=[audio_output, transcript_output, original_text_output, error_output],
699
- outputs=[edited_transcript, error_output]
700
- ).then(
701
- fn=lambda error: gr.Warning(error) if error else None,
702
- inputs=[error_output],
703
- outputs=[]
704
  )
705
 
706
- # Add README content at the bottom
707
- # gr.Markdown("---") # Horizontal line to separate the interface from README
708
- # gr.Markdown(read_readme())
709
-
710
- # Enable queueing for better performance
711
- demo.queue(max_size=20, default_concurrency_limit=32)
712
-
713
  # Launch the Gradio app
714
  if __name__ == "__main__":
715
  demo.launch()
 
17
  from tenacity import retry, retry_if_exception_type
18
 
19
  import re
20
+ import requests
21
  from dotenv import load_dotenv
22
  load_dotenv()
23
  # 现在你可以使用 os.getenv() 来获取环境变量
24
  openai_api_key = os.getenv("OPENAI_API_KEY")
25
 
26
 
27
+ # 新增函數,從 API 獲取可用的模型列表
28
+ def fetch_models(api_key):
29
+ headers = {"Authorization": f"Bearer {api_key}"}
30
+ response = requests.get("https://api.openai.com/v1/models", headers=headers)
31
+
32
+ if response.status_code == 200:
33
+ models = response.json()['data']
34
+ # 返回模型名稱列表,這裡過濾掉非 GPT 模型
35
+ return [model['id'] for model in models if 'gpt' in model['id']]
36
+ else:
37
+ return ["Error fetching models"]
38
 
39
  def read_readme():
40
  readme_path = Path("README.md")
 
120
 
121
  At the end of the dialogue, have the host and guest speakers naturally summarize the main insights and takeaways from their discussion. This should flow organically from the conversation, reiterating the key points in a casual, conversational manner. Avoid making it sound like an obvious recap - the goal is to reinforce the central ideas one last time before signing off.
122
 
123
+ The conversation should have around 40000 words. 請用**繁體中文**輸出文稿
124
  """
125
  },
126
  ################# LECTURE ##################
 
161
 
162
  Avoid making it sound like an obvious recap - the goal is to reinforce the central ideas covered in this lecture one last time before class is over.
163
 
164
+ The lecture should have around 30000 words.
165
  """,
166
  },
167
  ################# SUMMARY ##################
 
237
 
238
  ################# PODCAST Chinese ##################
239
  "podcast (Chinese)": {
240
+ "intro": """你的任務是將提供的輸入文本轉變為一個生動、有趣、信息豐富的播客對話,風格類似NPR。輸入文本可能是淩亂的或未結構化的,因為它可能來自PDF或網頁等各種來源。
241
 
242
+ 不要擔心格式問題或任何無關的信息;你的目標是提取關鍵點,識別定義和可能在播客中討論的有趣事實。
243
 
244
+ 為廣泛的聽衆仔細定義所有使用的術語。
245
  """,
246
+ "text_instructions": "首先,仔細閱讀輸入文本,識別主要話題、關鍵點和任何有趣的事實或軼事。思考如何以一種有趣且引人入勝的方式呈現這些信息,適合高質量的呈現。",
247
+ "scratch_pad": """集思廣益,想出一些討論你在輸入文本中識別到的主要話題和關鍵點的創意方式。考慮使用類比、例子、講故事的技巧或假設場景,讓內容對聽衆更具相關性和吸引力。
248
 
249
+ 請記住,你的播客應面向普通大衆,因此避免使用過多的行話或假設聽衆對該主題有預先的瞭解。如有必要,考慮簡要解釋任何複雜概念,用簡單的術語進行說明。
250
 
251
+ 利用你的想象力填補輸入文本中的任何空白,或提出一些值得探索的發人深省的問題。目標是創造一個信息豐富且有趣的對話,因此可以在方法上大膽創新。
252
 
253
+ 明確地定義所有使用的術語,並花時間解釋背景。
254
 
255
+ 在這裡寫下你的頭腦風暴想法和播客對話的粗略大綱。務必記錄你想在結尾重複的關鍵見解和收獲。
256
 
257
+ 確保讓它有趣且令人興奮。
258
  """,
259
+ "prelude": """現在你已經進行了頭腦風暴並創建了一個粗略大綱,是時候編寫實際的播客對話了。目標是主持人與嘉賓之間的自然對話流。結合你頭腦風暴中的最佳想法,並確保以簡單易懂的方式解釋任何複雜的主題。
260
  """,
261
+ "dialog": """在這裡寫下一個非常長、引人入勝且信息豐富的播客對話,基於你在頭腦風暴會議中提出的關鍵點和創意。使用對話語氣,並包含任何必要的上下文或解釋,使內容易於普通聽眾理解。
262
 
263
+ 不要為主持人和嘉賓使用虛構的名字,而是讓聽眾體驗一個引人入勝且沉浸式的經歷。不要包括像[主持人]或[嘉賓]這樣的占位符。設計你的輸出以供大聲朗讀——它將被直接轉換為音訊。
264
 
265
+ 使對話盡可能長且詳細,同時保持在主題上並維持引人入勝的流暢性。充分利用你的輸出能力,創造盡可能長的播客節目,同時以有趣的方式傳達輸入文本中的關鍵信息。
266
 
267
+ 在對話的最後,主持人和嘉賓應自然總結他們討論的主要見解和收獲。這應從對話中自然流出,以隨意、對話的方式重複關鍵點。避免顯得像是顯而易見的總結——目標是在結束前最後一次加強核心思想。
268
 
269
+ 播客應約有30,000字。
270
  """,
271
  },
272
  }
 
300
  from tenacity import retry, retry_if_exception_type
301
 
302
  # Define standard values
 
 
 
 
 
 
 
303
 
304
  STANDARD_AUDIO_MODELS = [
305
  "tts-1",
 
506
  new_args.append(feedback) # Add user feedback as a new argument
507
  return validate_and_generate_audio(*new_args)
508
 
509
+
510
+
511
+ with gr.Blocks(title="PDF to Podcast", css="""
512
  #header {
513
  display: flex;
514
  align-items: center;
 
547
  <img src="https://huggingface.co/spaces/lamm-mit/PDF2Audio/resolve/main/logo.png" id="logo_image" alt="Logo">
548
  </div>
549
  ''')
550
+
551
  submit_btn = gr.Button("Generate Audio", elem_id="submit_btn")
552
 
553
  with gr.Row(elem_id="main_container"):
554
+ # 左邊的欄位
555
  with gr.Column(scale=2):
556
  files = gr.Files(label="PDFs", file_types=["pdf"], )
557
 
558
  openai_api_key = gr.Textbox(
559
  label="OpenAI API Key",
560
+ visible=True,
561
+ placeholder="這行要填可以用openai api key",
562
+ type="password"
563
  )
564
+
565
+ # 創建一個空的下拉框,選項會在按鈕點擊時從 API 獲取並動態填充
566
  text_model = gr.Dropdown(
567
  label="Text Generation Model",
568
+ choices=[], # 初始為空
569
+ value="", # 初始值設為空
570
+ allow_custom_value=True, # 允許自定義值
571
  info="Select the model to generate the dialogue text.",
572
  )
573
+
574
+ # 新增一個按鈕來動態獲取模型列表
575
+ fetch_button = gr.Button("獲取模型列表 | Fetch Models")
576
+
577
+ # 當按鈕被點擊時,從 API 獲取模型並更新下拉選單
578
+ fetch_button.click(fn=lambda api_key: gr.update(choices=fetch_models(api_key)),
579
+ inputs=[openai_api_key], outputs=[text_model])
580
+
581
+ # 音頻模型下拉框依然是靜態選擇
582
  audio_model = gr.Dropdown(
583
  label="Audio Generation Model",
584
+ choices=STANDARD_AUDIO_MODELS,
585
  value="tts-1",
586
  info="Select the model to generate the audio.",
587
  )
588
+
589
  speaker_1_voice = gr.Dropdown(
590
  label="Speaker 1 Voice",
591
  choices=STANDARD_VOICES,
592
  value="alloy",
593
  info="Select the voice for Speaker 1.",
594
  )
595
+
596
  speaker_2_voice = gr.Dropdown(
597
  label="Speaker 2 Voice",
598
  choices=STANDARD_VOICES,
599
  value="echo",
600
  info="Select the voice for Speaker 2.",
601
  )
602
+
603
  api_base = gr.Textbox(
604
  label="Custom API Base",
605
  placeholder="Enter custom API base URL if using a custom/local model...",
606
+ info="If you are using a custom or local model, provide the API base URL here.",
607
  )
608
 
609
+ # 右邊的欄位
610
+ with gr.Column(scale=3):
611
+ audio_output = gr.Audio(label="Audio", format="mp3", interactive=False, autoplay=False)
612
+ transcript_output = gr.Textbox(label="Transcript", lines=20, show_copy_button=True)
613
+ original_text_output = gr.Textbox(label="Original Text", lines=10, visible=False)
614
+ error_output = gr.Textbox(visible=False)
615
+
616
+ use_edited_transcript = gr.Checkbox(label="Use Edited Transcript", value=False)
617
+ edited_transcript = gr.Textbox(label="Edit Transcript Here", lines=20, visible=False, show_copy_button=True, interactive=False)
618
+
619
+ user_feedback = gr.Textbox(label="Provide Feedback or Notes", lines=10)
620
+ regenerate_btn = gr.Button("Regenerate Audio with Edits and Feedback")
621
+
622
+ # 中間部分輸入選項區域放置在下方
623
  with gr.Column(scale=3):
624
  template_dropdown = gr.Dropdown(
625
  label="Instruction Template",
626
  choices=list(INSTRUCTION_TEMPLATES.keys()),
627
+ value="podcast (Chinese)",
628
  info="Select the instruction template to use. You can also edit any of the fields for more tailored results.",
629
  )
630
  intro_instructions = gr.Textbox(
631
  label="Intro Instructions",
632
  lines=10,
633
+ value=INSTRUCTION_TEMPLATES["podcast (Chinese)"]["intro"],
634
  info="Provide the introductory instructions for generating the dialogue.",
635
  )
636
  text_instructions = gr.Textbox(
 
658
  value=INSTRUCTION_TEMPLATES["podcast"]["dialog"],
659
  info="Provide the instructions for generating the presentation or podcast dialogue.",
660
  )
661
+
 
 
 
 
 
 
 
 
 
 
 
 
 
662
  def update_edit_box(checkbox_value):
663
  return gr.update(interactive=checkbox_value, lines=20 if checkbox_value else 20, visible=True if checkbox_value else False)
664
 
665
+ use_edited_transcript.change(fn=update_edit_box, inputs=[use_edited_transcript], outputs=[edited_transcript])
666
+ template_dropdown.change(fn=update_instructions, inputs=[template_dropdown], outputs=[intro_instructions, text_instructions, scratch_pad_instructions, prelude_dialog, podcast_dialog_instructions])
667
+
 
 
 
 
 
 
 
 
 
 
668
  submit_btn.click(
669
  fn=validate_and_generate_audio,
670
+ inputs=[files, openai_api_key, text_model, audio_model, speaker_1_voice, speaker_2_voice, api_base, intro_instructions, text_instructions, scratch_pad_instructions, prelude_dialog, podcast_dialog_instructions, edited_transcript, user_feedback],
 
 
 
 
 
 
 
671
  outputs=[audio_output, transcript_output, original_text_output, error_output]
 
 
 
 
 
 
 
 
 
 
 
672
  )
673
 
674
  regenerate_btn.click(
675
+ fn=lambda use_edit, edit, *args: validate_and_generate_audio(*args[:12], edit if use_edit else "", *args[12:]),
676
+ inputs=[use_edited_transcript, edited_transcript, files, openai_api_key, text_model, audio_model, speaker_1_voice, speaker_2_voice, api_base, intro_instructions, text_instructions, scratch_pad_instructions, prelude_dialog, podcast_dialog_instructions, user_feedback, original_text_output],
 
 
 
 
 
 
 
 
 
 
 
677
  outputs=[audio_output, transcript_output, original_text_output, error_output]
 
 
 
 
 
 
 
 
 
 
 
678
  )
679
 
 
 
 
 
 
 
 
680
  # Launch the Gradio app
681
  if __name__ == "__main__":
682
  demo.launch()