PDF2podcast / app.py
tbdavid2019's picture
移除readme.md
3614d2f
raw
history blame
38.9 kB
import concurrent.futures as cf
import glob
import io
import os
import time
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import List, Literal
import gradio as gr
from loguru import logger
from openai import OpenAI
from promptic import llm
from pydantic import BaseModel, ValidationError
from pypdf import PdfReader
from tenacity import retry, retry_if_exception_type
import re
from dotenv import load_dotenv
load_dotenv()
# 现在你可以使用 os.getenv() 来获取环境变量
openai_api_key = os.getenv("OPENAI_API_KEY")
def read_readme():
readme_path = Path("README.md")
if readme_path.exists():
with open(readme_path, "r") as file:
content = file.read()
# Use regex to remove metadata enclosed in -- ... --
content = re.sub(r'--.*?--', '', content, flags=re.DOTALL)
return content
else:
return "README.md not found. Please check the repository for more information."
# Define multiple sets of instruction templates
INSTRUCTION_TEMPLATES = {
################# PODCAST ##################
"podcast": {
"intro": """Your task is to take the input text provided and turn it into an lively, engaging, informative podcast dialogue, in the style of NPR. The input text may be messy or unstructured, as it could come from a variety of sources like PDFs or web pages.
Don't worry about the formatting issues or any irrelevant information; your goal is to extract the key points, identify definitions, and interesting facts that could be discussed in a podcast.
Define all terms used carefully for a broad audience of listeners.
""",
"text_instructions": "First, carefully read through the input text and identify the main topics, key points, and any interesting facts or anecdotes. Think about how you could present this information in a fun, engaging way that would be suitable for a high quality presentation.",
"scratch_pad": """Brainstorm creative ways to discuss the main topics and key points you identified in the input text. Consider using analogies, examples, storytelling techniques, or hypothetical scenarios to make the content more relatable and engaging for listeners.
Keep in mind that your podcast should be accessible to a general audience, so avoid using too much jargon or assuming prior knowledge of the topic. If necessary, think of ways to briefly explain any complex concepts in simple terms.
Use your imagination to fill in any gaps in the input text or to come up with thought-provoking questions that could be explored in the podcast. The goal is to create an informative and entertaining dialogue, so feel free to be creative in your approach.
Define all terms used clearly and spend effort to explain the background.
Write your brainstorming ideas and a rough outline for the podcast dialogue here. Be sure to note the key insights and takeaways you want to reiterate at the end.
Make sure to make it fun and exciting.
""",
"prelude": """Now that you have brainstormed ideas and created a rough outline, it's time to write the actual podcast dialogue. Aim for a natural, conversational flow between the host and any guest speakers. Incorporate the best ideas from your brainstorming session and make sure to explain any complex topics in an easy-to-understand way.
""",
"dialog": """Write a very long, engaging, informative podcast dialogue here, based on the key points and creative ideas you came up with during the brainstorming session. Use a conversational tone and include any necessary context or explanations to make the content accessible to a general audience.
Never use made-up names for the hosts and guests, but make it an engaging and immersive experience for listeners. Do not include any bracketed placeholders like [Host] or [Guest]. Design your output to be read aloud -- it will be directly converted into audio.
Make the dialogue as long and detailed as possible, while still staying on topic and maintaining an engaging flow. Aim to use your full output capacity to create the longest podcast episode you can, while still communicating the key information from the input text in an entertaining way.
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.
The podcast should have around 20000 words. 輸出文字為繁體中文,請注意。
""",
},
################# MATERIAL DISCOVERY SUMMARY ##################
"SciAgents material discovery summary": {
"intro": """Your task is to take the input text provided and turn it into a lively, engaging conversation between a professor and a student in a panel discussion that describes a new material. The professor acts like Richard Feynman, but you never mention the name.
The input text is the result of a design developed by SciAgents, an AI tool for scientific discovery that has come up with a detailed materials design.
Don't worry about the formatting issues or any irrelevant information; your goal is to extract the key points, identify definitions, and interesting facts that could be discussed in a podcast.
Define all terms used carefully for a broad audience of listeners.
""",
"text_instructions": "First, carefully read through the input text and identify the main topics, key points, and any interesting facts or anecdotes. Think about how you could present this information in a fun, engaging way that would be suitable for a high quality presentation.",
"scratch_pad": """Brainstorm creative ways to discuss the main topics and key points you identified in the material design summary, especially paying attention to design features developed by SciAgents. Consider using analogies, examples, storytelling techniques, or hypothetical scenarios to make the content more relatable and engaging for listeners.
Keep in mind that your description should be accessible to a general audience, so avoid using too much jargon or assuming prior knowledge of the topic. If necessary, think of ways to briefly explain any complex concepts in simple terms.
Use your imagination to fill in any gaps in the input text or to come up with thought-provoking questions that could be explored in the podcast. The goal is to create an informative and entertaining dialogue, so feel free to be creative in your approach.
Define all terms used clearly and spend effort to explain the background.
Write your brainstorming ideas and a rough outline for the podcast dialogue here. Be sure to note the key insights and takeaways you want to reiterate at the end.
Make sure to make it fun and exciting. You never refer to the podcast, you just discuss the discovery and you focus on the new material design only.
""",
"prelude": """Now that you have brainstormed ideas and created a rough outline, it's time to write the actual podcast dialogue. Aim for a natural, conversational flow between the host and any guest speakers. Incorporate the best ideas from your brainstorming session and make sure to explain any complex topics in an easy-to-understand way.
""",
"dialog": """Write a very long, engaging, informative dialogue here, based on the key points and creative ideas you came up with during the brainstorming session. The presentation must focus on the novel aspects of the material design, behavior, and all related aspects.
Use a conversational tone and include any necessary context or explanations to make the content accessible to a general audience, but make it detailed, logical, and technical so that it has all necessary aspects for listeners to understand the material and its unexpected properties.
Remember, this describes a design developed by SciAgents, and this must be explicitly stated for the listeners.
Never use made-up names for the hosts and guests, but make it an engaging and immersive experience for listeners. Do not include any bracketed placeholders like [Host] or [Guest]. Design your output to be read aloud -- it will be directly converted into audio.
Make the dialogue as long and detailed as possible with great scientific depth, while still staying on topic and maintaining an engaging flow. Aim to use your full output capacity to create the longest podcast episode you can, while still communicating the key information from the input text in an entertaining way.
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.
The conversation should have around 20000 words. 請用**繁體中文**輸出文稿
"""
},
################# LECTURE ##################
"lecture": {
"intro": """You are Professor Richard Feynman. Your task is to develop a script for a lecture. You never mention your name.
The material covered in the lecture is based on the provided text.
Don't worry about the formatting issues or any irrelevant information; your goal is to extract the key points, identify definitions, and interesting facts that need to be covered in the lecture.
Define all terms used carefully for a broad audience of students.
""",
"text_instructions": "First, carefully read through the input text and identify the main topics, key points, and any interesting facts or anecdotes. Think about how you could present this information in a fun, engaging way that would be suitable for a high quality presentation.",
"scratch_pad": """
Brainstorm creative ways to discuss the main topics and key points you identified in the input text. Consider using analogies, examples, storytelling techniques, or hypothetical scenarios to make the content more relatable and engaging for listeners.
Keep in mind that your lecture should be accessible to a general audience, so avoid using too much jargon or assuming prior knowledge of the topic. If necessary, think of ways to briefly explain any complex concepts in simple terms.
Use your imagination to fill in any gaps in the input text or to come up with thought-provoking questions that could be explored in the podcast. The goal is to create an informative and entertaining dialogue, so feel free to be creative in your approach.
Define all terms used clearly and spend effort to explain the background.
Write your brainstorming ideas and a rough outline for the lecture here. Be sure to note the key insights and takeaways you want to reiterate at the end.
Make sure to make it fun and exciting.
""",
"prelude": """Now that you have brainstormed ideas and created a rough outline, it's time to write the actual podcast dialogue. Aim for a natural, conversational flow between the host and any guest speakers. Incorporate the best ideas from your brainstorming session and make sure to explain any complex topics in an easy-to-understand way.
""",
"dialog": """Write a very long, engaging, informative script here, based on the key points and creative ideas you came up with during the brainstorming session. Use a conversational tone and include any necessary context or explanations to make the content accessible to the students.
Include clear definitions and terms, and examples.
Do not include any bracketed placeholders like [Host] or [Guest]. Design your output to be read aloud -- it will be directly converted into audio.
There is only one speaker, you, the professor. Stay on topic and maintaining an engaging flow. Aim to use your full output capacity to create the longest lecture you can, while still communicating the key information from the input text in an engaging way.
At the end of the lecture, naturally summarize the main insights and takeaways from the lecture. 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 covered in this lecture one last time before class is over.
The lecture should have around 20000 words.
""",
},
################# SUMMARY ##################
"summary": {
"intro": """Your task is to develop a summary of a paper. You never mention your name.
Don't worry about the formatting issues or any irrelevant information; your goal is to extract the key points, identify definitions, and interesting facts that need to be summarized.
Define all terms used carefully for a broad audience.
""",
"text_instructions": "First, carefully read through the input text and identify the main topics, key points, and key facts. Think about how you could present this information in an accurate summary.",
"scratch_pad": """Brainstorm creative ways to present the main topics and key points you identified in the input text. Consider using analogies, examples, or hypothetical scenarios to make the content more relatable and engaging for listeners.
Keep in mind that your summary should be accessible to a general audience, so avoid using too much jargon or assuming prior knowledge of the topic. If necessary, think of ways to briefly explain any complex concepts in simple terms. Define all terms used clearly and spend effort to explain the background.
Write your brainstorming ideas and a rough outline for the summary here. Be sure to note the key insights and takeaways you want to reiterate at the end.
Make sure to make it engaging and exciting.
""",
"prelude": """Now that you have brainstormed ideas and created a rough outline, it is time to write the actual summary. Aim for a natural, conversational flow between the host and any guest speakers. Incorporate the best ideas from your brainstorming session and make sure to explain any complex topics in an easy-to-understand way.
""",
"dialog": """Write a a script here, based on the key points and creative ideas you came up with during the brainstorming session. Use a conversational tone and include any necessary context or explanations to make the content accessible to the the audience.
Start your script by stating that this is a summary, referencing the title or headings in the input text. If the input text has no title, come up with a succinct summary of what is covered to open.
Include clear definitions and terms, and examples, of all key issues.
Do not include any bracketed placeholders like [Host] or [Guest]. Design your output to be read aloud -- it will be directly converted into audio.
There is only one speaker, you. Stay on topic and maintaining an engaging flow.
Naturally summarize the main insights and takeaways from the summary. This should flow organically from the conversation, reiterating the key points in a casual, conversational manner.
The summary should have around 1024 words.
""",
},
################# SHORT SUMMARY ##################
"short summary": {
"intro": """Your task is to develop a summary of a paper. You never mention your name.
Don't worry about the formatting issues or any irrelevant information; your goal is to extract the key points, identify definitions, and interesting facts that need to be summarized.
Define all terms used carefully for a broad audience.
""",
"text_instructions": "First, carefully read through the input text and identify the main topics, key points, and key facts. Think about how you could present this information in an accurate summary.",
"scratch_pad": """Brainstorm creative ways to present the main topics and key points you identified in the input text. Consider using analogies, examples, or hypothetical scenarios to make the content more relatable and engaging for listeners.
Keep in mind that your summary should be accessible to a general audience, so avoid using too much jargon or assuming prior knowledge of the topic. If necessary, think of ways to briefly explain any complex concepts in simple terms. Define all terms used clearly and spend effort to explain the background.
Write your brainstorming ideas and a rough outline for the summary here. Be sure to note the key insights and takeaways you want to reiterate at the end.
Make sure to make it engaging and exciting.
""",
"prelude": """Now that you have brainstormed ideas and created a rough outline, it is time to write the actual summary. Aim for a natural, conversational flow between the host and any guest speakers. Incorporate the best ideas from your brainstorming session and make sure to explain any complex topics in an easy-to-understand way.
""",
"dialog": """Write a a script here, based on the key points and creative ideas you came up with during the brainstorming session. Keep it concise, and use a conversational tone and include any necessary context or explanations to make the content accessible to the the audience.
Start your script by stating that this is a summary, referencing the title or headings in the input text. If the input text has no title, come up with a succinct summary of what is covered to open.
Include clear definitions and terms, and examples, of all key issues.
Do not include any bracketed placeholders like [Host] or [Guest]. Design your output to be read aloud -- it will be directly converted into audio.
There is only one speaker, you. Stay on topic and maintaining an engaging flow.
Naturally summarize the main insights and takeaways from the short summary. This should flow organically from the conversation, reiterating the key points in a casual, conversational manner.
The summary should have around 256 words.
""",
},
################# PODCAST Chinese ##################
"podcast (Chinese)": {
"intro": """你的任务是将提供的输入文本转变为一个生动、有趣、信息丰富的播客对话,风格类似NPR。输入文本可能是凌乱的或未结构化的,因为它可能来自PDF或网页等各种来源。
不要担心格式问题或任何无关的信息;你的目标是提取关键点,识别定义和可能在播客中讨论的有趣事实。
为广泛的听众仔细定义所有使用的术语。
""",
"text_instructions": "首先,仔细阅读输入文本,识别主要话题、关键点和任何有趣的事实或轶事。思考如何以一种有趣且引人入胜的方式呈现这些信息,适合高质量的呈现。",
"scratch_pad": """集思广益,想出一些讨论你在输入文本中识别到的主要话题和关键点的创意方式。考虑使用类比、例子、讲故事的技巧或假设场景,让内容对听众更具相关性和吸引力。
请记住,你的播客应面向普通大众,因此避免使用过多的行话或假设听众对该主题有预先的了解。如有必要,考虑简要解释任何复杂概念,用简单的术语进行说明。
利用你的想象力填补输入文本中的任何空白,或提出一些值得探索的发人深省的问题。目标是创造一个信息丰富且有趣的对话,因此可以在方法上大胆创新。
明确地定义所有使用的术语,并花时间解释背景。
在这里写下你的头脑风暴想法和播客对话的粗略大纲。务必记录你想在结尾重复的关键见解和收获。
确保让它有趣且令人兴奋。
""",
"prelude": """现在你已经进行了头脑风暴并创建了一个粗略大纲,是时候编写实际的播客对话了。目标是主持人与嘉宾之间的自然对话流。结合你头脑风暴中的最佳想法,并确保以简单易懂的方式解释任何复杂的主题。
""",
"dialog": """在这里写下一个非常长、引人入胜且信息丰富的播客对话,基于你在头脑风暴会议中提出的关键点和创意。使用对话语气,并包含任何必要的上下文或解释,使内容易于普通听众理解。
不要为主持人和嘉宾使用虚构的名字,而是让听众体验一个引人入胜且沉浸式的经历。不要包括像[主持人]或[嘉宾]这样的占位符。设计你的输出以供大声朗读——它将被直接转换为音频。
使对话尽可能长且详细,同时保持在主题上并维持引人入胜的流畅性。充分利用你的输出能力,创造尽可能长的播客节目,同时以有趣的方式传达输入文本中的关键信息。
在对话的最后,主持人和嘉宾应自然总结他们讨论的主要见解和收获。这应从对话中自然流出,以随意、对话的方式重复关键点。避免显得像是显而易见的总结——目标是在结束前最后一次加强核心思想。
播客应约有20,000字。
""",
},
}
# Function to update instruction fields based on template selection
def update_instructions(template):
return (
INSTRUCTION_TEMPLATES[template]["intro"],
INSTRUCTION_TEMPLATES[template]["text_instructions"],
INSTRUCTION_TEMPLATES[template]["scratch_pad"],
INSTRUCTION_TEMPLATES[template]["prelude"],
INSTRUCTION_TEMPLATES[template]["dialog"]
)
import concurrent.futures as cf
import glob
import io
import os
import time
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import List, Literal
import gradio as gr
from loguru import logger
from openai import OpenAI
from promptic import llm
from pydantic import BaseModel, ValidationError
from pypdf import PdfReader
from tenacity import retry, retry_if_exception_type
# Define standard values
STANDARD_TEXT_MODELS = [
"o1-preview",
"gpt-4o-mini",
"chatgpt-4o-latest",
"gpt-4-turbo",
"openai/custom_model",
]
STANDARD_AUDIO_MODELS = [
"tts-1",
"tts-1-hd",
]
STANDARD_VOICES = [
"alloy",
"echo",
"fable",
"onyx",
"nova",
"shimmer",
]
class DialogueItem(BaseModel):
text: str
speaker: Literal["speaker-1", "speaker-2"]
class Dialogue(BaseModel):
scratchpad: str
dialogue: List[DialogueItem]
def get_mp3(text: str, voice: str, audio_model: str, api_key: str = None) -> bytes:
client = OpenAI(
api_key=api_key or os.getenv("OPENAI_API_KEY"),
)
with client.audio.speech.with_streaming_response.create(
model=audio_model,
voice=voice,
input=text,
) as response:
with io.BytesIO() as file:
for chunk in response.iter_bytes():
file.write(chunk)
return file.getvalue()
from functools import wraps
def conditional_llm(model, api_base=None, api_key=None):
"""
Conditionally apply the @llm decorator based on the api_base parameter.
If api_base is provided, it applies the @llm decorator with api_base.
Otherwise, it applies the @llm decorator without api_base.
"""
def decorator(func):
if api_base:
return llm(model=model, api_base=api_base)(func)
else:
return llm(model=model, api_key=api_key)(func)
return decorator
def generate_audio(
files: list,
openai_api_key: str = None,
text_model: str = "o1-preview-2024-09-12",
audio_model: str = "tts-1",
speaker_1_voice: str = "alloy",
speaker_2_voice: str = "echo",
api_base: str = None,
intro_instructions: str = '',
text_instructions: str = '',
scratch_pad_instructions: str = '',
prelude_dialog: str = '',
podcast_dialog_instructions: str = '',
edited_transcript: str = None,
user_feedback: str = None,
original_text: str = None,
debug = False,
) -> tuple:
# 讀取環境變數中 OpenAI API Key
if not openai_api_key:
openai_api_key = os.getenv("OPENAI_API_KEY")
if not openai_api_key:
raise gr.Error("OpenAI API key is required")
combined_text = original_text or ""
# If there's no original text, extract it from the uploaded files
if not combined_text:
for file in files:
with Path(file).open("rb") as f:
reader = PdfReader(f)
text = "\n\n".join([page.extract_text() for page in reader.pages if page.extract_text()])
combined_text += text + "\n\n"
# Configure the LLM based on selected model and api_base
@retry(retry=retry_if_exception_type(ValidationError))
@conditional_llm(model=text_model, api_base=api_base, api_key=openai_api_key)
def generate_dialogue(text: str, intro_instructions: str, text_instructions: str, scratch_pad_instructions: str,
prelude_dialog: str, podcast_dialog_instructions: str,
edited_transcript: str = None, user_feedback: str = None, ) -> Dialogue:
"""
{intro_instructions}
Here is the original input text:
<input_text>
{text}
</input_text>
{text_instructions}
<scratchpad>
{scratch_pad_instructions}
</scratchpad>
{prelude_dialog}
<podcast_dialogue>
{podcast_dialog_instructions}
</podcast_dialogue>
{edited_transcript}{user_feedback}
"""
instruction_improve='Based on the original text, please generate an improved version of the dialogue by incorporating the edits, comments and feedback.'
edited_transcript_processed="\nPreviously generated edited transcript, with specific edits and comments that I want you to carefully address:\n"+"<edited_transcript>\n"+edited_transcript+"</edited_transcript>" if edited_transcript !="" else ""
user_feedback_processed="\nOverall user feedback:\n\n"+user_feedback if user_feedback !="" else ""
if edited_transcript_processed.strip()!='' or user_feedback_processed.strip()!='':
user_feedback_processed="<requested_improvements>"+user_feedback_processed+"\n\n"+instruction_improve+"</requested_improvements>"
if debug:
logger.info (edited_transcript_processed)
logger.info (user_feedback_processed)
# Generate the dialogue using the LLM
llm_output = generate_dialogue(
combined_text,
intro_instructions=intro_instructions,
text_instructions=text_instructions,
scratch_pad_instructions=scratch_pad_instructions,
prelude_dialog=prelude_dialog,
podcast_dialog_instructions=podcast_dialog_instructions,
edited_transcript=edited_transcript_processed,
user_feedback=user_feedback_processed
)
# Generate audio from the transcript
audio = b""
transcript = ""
characters = 0
with cf.ThreadPoolExecutor() as executor:
futures = []
for line in llm_output.dialogue:
transcript_line = f"{line.speaker}: {line.text}"
voice = speaker_1_voice if line.speaker == "speaker-1" else speaker_2_voice
future = executor.submit(get_mp3, line.text, voice, audio_model, openai_api_key)
futures.append((future, transcript_line))
characters += len(line.text)
for future, transcript_line in futures:
audio_chunk = future.result()
audio += audio_chunk
transcript += transcript_line + "\n\n"
logger.info(f"Generated {characters} characters of audio")
temporary_directory = "./gradio_cached_examples/tmp/"
os.makedirs(temporary_directory, exist_ok=True)
# Use a temporary file -- Gradio's audio component doesn't work with raw bytes in Safari
temporary_file = NamedTemporaryFile(
dir=temporary_directory,
delete=False,
suffix=".mp3",
)
temporary_file.write(audio)
temporary_file.close()
# Delete any files in the temp directory that end with .mp3 and are over a day old
for file in glob.glob(f"{temporary_directory}*.mp3"):
if os.path.isfile(file) and time.time() - os.path.getmtime(file) > 24 * 60 * 60:
os.remove(file)
return temporary_file.name, transcript, combined_text
def validate_and_generate_audio(*args):
files = args[0]
if not files:
return None, None, None, "Please upload at least one PDF file before generating audio."
try:
audio_file, transcript, original_text = generate_audio(*args)
return audio_file, transcript, original_text, None # Return None as the error when successful
except Exception as e:
# If an error occurs during generation, return None for the outputs and the error message
return None, None, None, str(e)
def edit_and_regenerate(edited_transcript, user_feedback, *args):
# Replace the original transcript and feedback in the args with the new ones
#new_args = list(args)
#new_args[-2] = edited_transcript # Update edited transcript
#new_args[-1] = user_feedback # Update user feedback
return validate_and_generate_audio(*new_args)
# New function to handle user feedback and regeneration
def process_feedback_and_regenerate(feedback, *args):
# Add user feedback to the args
new_args = list(args)
new_args.append(feedback) # Add user feedback as a new argument
return validate_and_generate_audio(*new_args)
with gr.Blocks(title="PDF to Audio", css="""
#header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
background-color: transparent;
border-bottom: 1px solid #ddd;
}
#title {
font-size: 24px;
margin: 0;
}
#logo_container {
width: 200px;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
}
#logo_image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
#main_container {
margin-top: 20px;
}
""") as demo:
with gr.Row(elem_id="header"):
with gr.Column(scale=4):
gr.Markdown("# Convert PDFs into an audio podcast, lecture, summary and others\n\nFirst, upload one or more PDFs, select options, then push Generate Audio.\n\nYou can also select a variety of custom option and direct the way the result is generated.", elem_id="title")
with gr.Column(scale=1):
gr.HTML('''
<div id="logo_container">
<img src="https://huggingface.co/spaces/lamm-mit/PDF2Audio/resolve/main/logo.png" id="logo_image" alt="Logo">
</div>
''')
#gr.Markdown("")
submit_btn = gr.Button("Generate Audio", elem_id="submit_btn")
with gr.Row(elem_id="main_container"):
with gr.Column(scale=2):
files = gr.Files(label="PDFs", file_types=["pdf"], )
openai_api_key = gr.Textbox(
label="OpenAI API Key",
visible=True, # Always show the API key field
placeholder="Enter your OpenAI API Key here...",
type="password" # Hide the API key input
)
text_model = gr.Dropdown(
label="Text Generation Model",
choices=STANDARD_TEXT_MODELS,
value="o1-preview-2024-09-12", #"gpt-4o-mini",
info="Select the model to generate the dialogue text.",
)
audio_model = gr.Dropdown(
label="Audio Generation Model",
choices=STANDARD_AUDIO_MODELS,
value="tts-1",
info="Select the model to generate the audio.",
)
speaker_1_voice = gr.Dropdown(
label="Speaker 1 Voice",
choices=STANDARD_VOICES,
value="alloy",
info="Select the voice for Speaker 1.",
)
speaker_2_voice = gr.Dropdown(
label="Speaker 2 Voice",
choices=STANDARD_VOICES,
value="echo",
info="Select the voice for Speaker 2.",
)
api_base = gr.Textbox(
label="Custom API Base",
placeholder="Enter custom API base URL if using a custom/local model...",
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.",
)
with gr.Column(scale=3):
template_dropdown = gr.Dropdown(
label="Instruction Template",
choices=list(INSTRUCTION_TEMPLATES.keys()),
value="podcast (Chinese)", # 默認選擇中文範本
info="Select the instruction template to use. You can also edit any of the fields for more tailored results.",
)
intro_instructions = gr.Textbox(
label="Intro Instructions",
lines=10,
value=INSTRUCTION_TEMPLATES["podcast (Chinese)"]["intro"], # 使用中文範本中的值
info="Provide the introductory instructions for generating the dialogue.",
)
text_instructions = gr.Textbox(
label="Standard Text Analysis Instructions",
lines=10,
placeholder="Enter text analysis instructions...",
value=INSTRUCTION_TEMPLATES["podcast"]["text_instructions"],
info="Provide the instructions for analyzing the raw data and text.",
)
scratch_pad_instructions = gr.Textbox(
label="Scratch Pad Instructions",
lines=15,
value=INSTRUCTION_TEMPLATES["podcast"]["scratch_pad"],
info="Provide the scratch pad instructions for brainstorming presentation/dialogue content.",
)
prelude_dialog = gr.Textbox(
label="Prelude Dialog",
lines=5,
value=INSTRUCTION_TEMPLATES["podcast"]["prelude"],
info="Provide the prelude instructions before the presentation/dialogue is developed.",
)
podcast_dialog_instructions = gr.Textbox(
label="Podcast Dialog Instructions",
lines=20,
value=INSTRUCTION_TEMPLATES["podcast"]["dialog"],
info="Provide the instructions for generating the presentation or podcast dialogue.",
)
audio_output = gr.Audio(label="Audio", format="mp3", interactive=False, autoplay=False)
transcript_output = gr.Textbox(label="Transcript", lines=20, show_copy_button=True)
original_text_output = gr.Textbox(label="Original Text", lines=10, visible=False)
error_output = gr.Textbox(visible=False) # Hidden textbox to store error message
use_edited_transcript = gr.Checkbox(label="Use Edited Transcript (check if you want to make edits to the initially generated transcript)", value=False)
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,
show_copy_button=True, interactive=False)
user_feedback = gr.Textbox(label="Provide Feedback or Notes", lines=10, #placeholder="Enter your feedback or notes here..."
)
regenerate_btn = gr.Button("Regenerate Audio with Edits and Feedback")
# Function to update the interactive state of edited_transcript
def update_edit_box(checkbox_value):
return gr.update(interactive=checkbox_value, lines=20 if checkbox_value else 20, visible=True if checkbox_value else False)
# Update the interactive state of edited_transcript when the checkbox is toggled
use_edited_transcript.change(
fn=update_edit_box,
inputs=[use_edited_transcript],
outputs=[edited_transcript]
)
# Update instruction fields when template is changed
template_dropdown.change(
fn=update_instructions,
inputs=[template_dropdown],
outputs=[intro_instructions, text_instructions, scratch_pad_instructions, prelude_dialog, podcast_dialog_instructions]
)
submit_btn.click(
fn=validate_and_generate_audio,
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, # placeholder for edited_transcript
user_feedback, # placeholder for user_feedback
],
outputs=[audio_output, transcript_output, original_text_output, error_output]
).then(
fn=lambda audio, transcript, original_text, error: (
transcript if transcript else "",
error if error else None
),
inputs=[audio_output, transcript_output, original_text_output, error_output],
outputs=[edited_transcript, error_output]
).then(
fn=lambda error: gr.Warning(error) if error else None,
inputs=[error_output],
outputs=[]
)
regenerate_btn.click(
fn=lambda use_edit, edit, *args: validate_and_generate_audio(
*args[:12], # All inputs up to podcast_dialog_instructions
edit if use_edit else "", # Use edited transcript if checkbox is checked, otherwise empty string
*args[12:] # user_feedback and original_text_output
),
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
],
outputs=[audio_output, transcript_output, original_text_output, error_output]
).then(
fn=lambda audio, transcript, original_text, error: (
transcript if transcript else "",
error if error else None
),
inputs=[audio_output, transcript_output, original_text_output, error_output],
outputs=[edited_transcript, error_output]
).then(
fn=lambda error: gr.Warning(error) if error else None,
inputs=[error_output],
outputs=[]
)
# Add README content at the bottom
# gr.Markdown("---") # Horizontal line to separate the interface from README
# gr.Markdown(read_readme())
# Enable queueing for better performance
demo.queue(max_size=20, default_concurrency_limit=32)
# Launch the Gradio app
if __name__ == "__main__":
demo.launch()