Spaces:
Runtime error
Runtime error
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 | |
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が見つかりません。詳細についてはリポジトリを確認してください。" | |
# 複数の指示テンプレートを定義 | |
INSTRUCTION_TEMPLATES = { | |
################# ポッドキャスト ################## | |
"ポッドキャスト": { | |
"intro": """あなたのタスクは、提供された入力テキストを使用して、NPRのスタイルで活気があり、魅力的で情報豊富なポッドキャスト対話に変換することです。入力テキストはPDFやウェブページなど様々なソースから来る可能性があるため、乱雑で非構造化されている場合があります。 | |
フォーマットの問題や無関係な情報については心配しないでください。あなたの目標は、キーポイントを抽出し、定義やポッドキャストで議論できる興味深い事実を特定することです。 | |
使用するすべての用語を、幅広いリスナー向けに慎重に定義してください。""", | |
"text_instructions": """まず、入力テキストを注意深く読み、主要なトピック、キーポイント、および興味深い事実や逸話を特定してください。この情報をどのようにすれば高品質なプレゼンテーションに適した楽しく魅力的な方法で提示できるかを考えてください。""", | |
"scratch_pad": """入力テキストで特定した主要なトピックやキーポイントを議論するための創造的な方法をブレインストーミングしてください。アナロジー、例、ストーリーテリング技法、または仮想のシナリオを使用して、リスナーにとって親しみやすく魅力的なコンテンツにすることを検討してください。 | |
あなたのポッドキャストは一般の視聴者にとってアクセスしやすいものでなければならないことを忘れないでください。したがって、専門用語を多用したり、トピックに関する事前知識を前提としたりしないでください。必要に応じて、複雑な概念を簡単な言葉で簡潔に説明する方法を考えてください。 | |
入力テキストのギャップを埋めたり、ポッドキャストで探求できる思考を刺激する質問を考え出すために、想像力を活用してください。目標は情報豊富でエンターテインメント性のある対話を作成することなので、アプローチには自由に創造性を発揮してください。 | |
使用するすべての用語を明確に定義し、背景を説明するために努力してください。 | |
ここに、ブレインストーミングしたアイデアとポッドキャスト対話の大まかなアウトラインを書いてください。最後に強調したい重要な洞察や持ち帰るべきポイントを必ず記載してください。 | |
楽しくワクワクするものにしてください。""", | |
"prelude": """アイデアをブレインストーミングし、大まかなアウトラインを作成したので、実際のポッドキャスト対話を書く時が来ました。ホストとゲストスピーカーの間で自然で会話的な流れを目指してください。ブレインストーミングセッションから最高のアイデアを取り入れ、複雑なトピックもわかりやすく説明するようにしてください。""", | |
"dialog": """ここに、ブレインストーミングセッションで考え出したキーポイントと創造的なアイデアに基づいた、非常に長く、魅力的で情報豊富なポッドキャスト対話を書いてください。会話調のトーンを使用し、一般の視聴者にとってアクセスしやすいように必要なコンテキストや説明を含めてください。 | |
ホストやゲストに架空の名前を使用しないでください。しかし、リスナーにとって魅力的で没入感のある体験にしてください。[Host]や[Guest]のような括弧で囲まれたプレースホルダーを含めないでください。出力は音読されるように設計してください。直接音声に変換されます。 | |
トピックから外れず、魅力的な流れを維持しながら、できるだけ長く詳細な対話にしてください。あなたの最大の出力容量を使用して、可能な限り長いポッドキャストエピソードを作成しながら、入力テキストからの主要な情報をエンターテインメント性のある方法で伝えることを目指してください。 | |
対話の終わりには、ホストとゲストスピーカーが自然にディスカッションの主要な洞察と持ち帰るべきポイントをまとめてください。これは会話から自然に流れ出るものであり、重要なポイントをカジュアルで会話的な方法で繰り返すべきです。明らかな要約のように聞こえないようにしてください。目標は、締めくくる前に中心的なアイデアをもう一度強調することです。 | |
ポッドキャストは約20000語であるべきです。""", | |
}, | |
################# SciAgents 材料発見の要約 ################## | |
"SciAgents 材料発見の要約": { | |
"intro": """あなたのタスクは、提供された入力テキストを使用して、新しい材料を説明する教授と学生の活気ある対話に変換することです。教授はリチャード・ファインマンのように振る舞いますが、名前は一切言及しません。 | |
入力テキストは、SciAgentsというAIツールによって開発された設計の結果です。このツールは詳細な材料設計を考案しました。 | |
フォーマットの問題や無関係な情報については心配しないでください。あなたの目標は、キーポイントを抽出し、定義やポッドキャストで議論できる興味深い事実を特定することです。 | |
使用するすべての用語を、幅広いリスナー向けに慎重に定義してください。""", | |
"text_instructions": """まず、入力テキストを注意深く読み、主要なトピック、キーポイント、および興味深い事実や逸話を特定してください。この情報をどのようにすれば高品質なプレゼンテーションに適した楽しく魅力的な方法で提示できるかを考えてください。""", | |
"scratch_pad": """入力テキストで特定した主要なトピックやキーポイントを議論するための創造的な方法をブレインストーミングしてください。特にSciAgentsによって開発された設計の特徴に注意を払ってください。アナロジー、例、ストーリーテリング技法、または仮想のシナリオを使用して、リスナーにとって親しみやすく魅力的なコンテンツにすることを検討してください。 | |
あなたの説明は一般の視聴者にとってアクセスしやすいものでなければならないことを忘れないでください。したがって、専門用語を多用したり、トピックに関する事前知識を前提としたりしないでください。必要に応じて、複雑な概念を簡単な言葉で簡潔に説明する方法を考えてください。 | |
入力テキストのギャップを埋めたり、ポッドキャストで探求できる思考を刺激する質問を考え出すために、想像力を活用してください。目標は情報豊富でエンターテインメント性のある対話を作成することなので、アプローチには自由に創造性を発揮してください。 | |
使用するすべての用語を明確に定義し、背景を説明するために努力してください。 | |
ここに、ブレインストーミングしたアイデアとポッドキャスト対話の大まかなアウトラインを書いてください。最後に強調したい重要な洞察や持ち帰るべきポイントを必ず記載してください。 | |
楽しくワクワクするものにしてください。ポッドキャストには一切言及せず、新しい材料設計の議論に焦点を当ててください。""", | |
"prelude": """アイデアをブレインストーミングし、大まかなアウトラインを作成したので、実際のポッドキャスト対話を書く時が来ました。ホストとゲストスピーカーの間で自然で会話的な流れを目指してください。ブレインストーミングセッションから最高のアイデアを取り入れ、複雑なトピックもわかりやすく説明するようにしてください。""", | |
"dialog": """ここに、ブレインストーミングセッションで考え出したキーポイントと創造的なアイデアに基づいた、非常に長く、魅力的で情報豊富な対話を書いてください。プレゼンテーションは材料設計の新規性、挙動、および関連するすべての側面に焦点を当てる必要があります。 | |
会話調のトーンを使用し、一般の視聴者にとってアクセスしやすいように必要なコンテキストや説明を含めてくださいが、詳細で論理的、技術的な内容にして、リスナーが材料とその予期せぬ特性を理解するために必要なすべての側面を含めてください。 | |
これはSciAgentsによって開発された設計を説明していることを忘れず、リスナーに明示的に伝えてください。 | |
ホストやゲストに架空の名前を使用しないでください。しかし、リスナーにとって魅力的で没入感のある体験にしてください。[Host]や[Guest]のような括弧で囲まれたプレースホルダーを含めないでください。出力は音読されるように設計してください。直接音声に変換されます。 | |
トピックから外れず、魅力的な流れを維持しながら、できるだけ長く詳細な対話にしてください。あなたの最大の出力容量を使用して、可能な限り長いエピソードを作成しながら、入力テキストからの主要な情報をエンターテインメント性のある方法で伝えることを目指してください。 | |
対話の終わりには、ホストとゲストスピーカーが自然にディスカッションの主要な洞察と持ち帰るべきポイントをまとめてください。これは会話から自然に流れ出るものであり、重要なポイントをカジュアルで会話的な方法で繰り返すべきです。明らかな要約のように聞こえないようにしてください。目標は、締めくくる前に中心的なアイデアをもう一度強調することです。 | |
対話は約20000語であるべきです。""" | |
}, | |
################# 講義 ################## | |
"講義": { | |
"intro": """あなたはリチャード・ファインマン教授です。あなたのタスクは講義のスクリプトを作成することです。名前は一切言及しません。 | |
講義でカバーされる材料は提供されたテキストに基づいています。 | |
フォーマットの問題や無関係な情報については心配しないでください。あなたの目標は、キーポイントを抽出し、定義や講義でカバーすべき興味深い事実を特定することです。 | |
使用するすべての用語を、学生の幅広い聴衆向けに慎重に定義してください。""", | |
"text_instructions": """まず、入力テキストを注意深く読み、主要なトピック、キーポイント、および興味深い事実や逸話を特定してください。この情報をどのようにすれば高品質なプレゼンテーションに適した楽しく魅力的な方法で提示できるかを考えてください。""", | |
"scratch_pad": """入力テキストで特定した主要なトピックやキーポイントを議論するための創造的な方法をブレインストーミングしてください。アナロジー、例、ストーリーテリング技法、または仮想のシナリオを使用して、リスナーにとって親しみやすく魅力的なコンテンツにすることを検討してください。 | |
あなたの講義は一般の聴衆にとってアクセスしやすいものでなければならないことを忘れないでください。したがって、専門用語を多用したり、トピックに関する事前知識を前提としたりしないでください。必要に応じて、複雑な概念を簡単な言葉で簡潔に説明する方法を考えてください。 | |
入力テキストのギャップを埋めたり、講義で探求できる思考を刺激する質問を考え出すために、想像力を活用してください。目標は情報豊富でエンターテインメント性のある講義を作成することなので、アプローチには自由に創造性を発揮してください。 | |
使用するすべての用語を明確に定義し、背景を説明するために努力してください。 | |
ここに、ブレインストーミングしたアイデアと講義の大まかなアウトラインを書いてください。最後に強調したい重要な洞察や持ち帰るべきポイントを必ず記載してください。 | |
楽しくワクワクするものにしてください。""", | |
"prelude": """アイデアをブレインストーミングし、大まかなアウトラインを作成したので、実際の講義の対話を書く時が来ました。自然で会話的な流れを目指してください。ブレインストーミングセッションから最高のアイデアを取り入れ、複雑なトピックもわかりやすく説明するようにしてください。""", | |
"dialog": """ここに、ブレインストーミングセッションで考え出したキーポイントと創造的なアイデアに基づいた、非常に長く、魅力的で情報豊富なスクリプトを書いてください。会話調のトーンを使用し、学生がアクセスしやすいように必要なコンテキストや説明を含めてください。 | |
明確な定義と用語、そして例を含めてください。 | |
[Host]や[Guest]のような括弧で囲まれたプレースホルダーを含めないでください。出力は音読されるように設計してください。直接音声に変換されます。 | |
話者はあなただけ、教授です。トピックから外れず、魅力的な流れを維持しながら、できるだけ長い講義にしてください。入力テキストからの主要な情報を伝えつつ、エンターテインメント性のある方法で伝えることを目指してください。 | |
講義の終わりには、自然に主要な洞察と持ち帰るべきポイントをまとめてください。これは会話から自然に流れ出るものであり、重要なポイントをカジュアルで会話的な方法で繰り返すべきです。 | |
明らかな要約のように聞こえないようにしてください。目標は、クラスが終わる前にこの講義でカバーした中心的なアイデアをもう一度強調することです。 | |
講義は約20000語であるべきです。""", | |
}, | |
################# 要約 ################## | |
"要約": { | |
"intro": """あなたのタスクは論文の要約を作成することです。名前は一切言及しません。 | |
フォーマットの問題や無関係な情報については心配しないでください。あなたの目標は、キーポイントを抽出し、定義や要約する必要のある興味深い事実を特定することです。 | |
使用するすべての用語を、幅広い聴衆向けに慎重に定義してください。""", | |
"text_instructions": """まず、入力テキストを注意深く読み、主要なトピック、キーポイント、および重要な事実を特定してください。この情報をどのようにすれば正確な要約で提示できるかを考えてください。""", | |
"scratch_pad": """入力テキストで特定した主要なトピックやキーポイントを提示するための創造的な方法をブレインストーミングしてください。アナロジー、例、または仮想のシナリオを使用して、リスナーにとって親しみやすく魅力的なコンテンツにすることを検討してください。 | |
あなたの要約は一般の視聴者にとってアクセスしやすいものでなければならないことを忘れないでください。したがって、専門用語を多用したり、トピックに関する事前知識を前提としたりしないでください。必要に応じて、複雑な概念を簡単な言葉で簡潔に説明する方法を考えてください。使用するすべての用語を明確に定義し、背景を説明するために努力してください。 | |
ここに、ブレインストーミングしたアイデアと要約の大まかなアウトラインを書いてください。最後に強調したい重要な洞察や持ち帰るべきポイントを必ず記載してください。 | |
魅力的でワクワクするものにしてください。""", | |
"prelude": """アイデアをブレインストーミングし、大まかなアウトラインを作成したので、実際の要約を書く時が来ました。自然で会話的な流れを目指してください。ブレインストーミングセッションから最高のアイデアを取り入れ、複雑なトピックもわかりやすく説明するようにしてください。""", | |
"dialog": """ここに、ブレインストーミングセッションで考え出したキーポイントと創造的なアイデアに基づいたスクリプトを書いてください。会話調のトーンを使用し、聴衆がアクセスしやすいように必要なコンテキストや説明を含めてください。 | |
スクリプトの冒頭で、これは要約であり、入力テキストのタイトルや見出しに言及していることを述べてください。入力テキストにタイトルがない場合は、カバーされている内容の簡潔な要約を考えてください。 | |
すべての主要な問題の明確な定義と用語、そして例を含めてください。 | |
[Host]や[Guest]のような括弧で囲まれたプレースホルダーを含めないでください。出力は音読されるように設計してください。直接音声に変換されます。 | |
話者はあなただけです。トピックから外れず、魅力的な流れを維持してください。 | |
要約の主要な洞察と持ち帰るべきポイントを自然にまとめてください。これは会話から自然に流れ出るものであり、重要なポイントをカジュアルで会話的な方法で繰り返すべきです。 | |
要約は約1024語であるべきです。""", | |
}, | |
################# 短い要約 ################## | |
"短い要約": { | |
"intro": """あなたのタスクは論文の要約を作成することです。名前は一切言及しません。 | |
フォーマットの問題や無関係な情報については心配しないでください。あなたの目標は、キーポイントを抽出し、定義や要約する必要のある興味深い事実を特定することです。 | |
使用するすべての用語を、幅広い聴衆向けに慎重に定義してください。""", | |
"text_instructions": """まず、入力テキストを注意深く読み、主要なトピック、キーポイント、および重要な事実を特定してください。この情報をどのようにすれば正確な要約で提示できるかを考えてください。""", | |
"scratch_pad": """入力テキストで特定した主要なトピックやキーポイントを提示するための創造的な方法をブレインストーミングしてください。アナロジー、例、または仮想のシナリオを使用して、リスナーにとって親しみやすく魅力的なコンテンツにすることを検討してください。 | |
あなたの要約は一般の視聴者にとってアクセスしやすいものでなければならないことを忘れないでください。したがって、専門用語を多用したり、トピックに関する事前知識を前提としたりしないでください。必要に応じて、複雑な概念を簡単な言葉で簡潔に説明する方法を考えてください。使用するすべての用語を明確に定義し、背景を説明するために努力してください。 | |
ここに、ブレインストーミングしたアイデアと要約の大まかなアウトラインを書いてください。最後に強調したい重要な洞察や持ち帰るべきポイントを必ず記載してください。 | |
魅力的でワクワクするものにしてください。""", | |
"prelude": """アイデアをブレインストーミングし、大まかなアウトラインを作成したので、実際の要約を書く時が来ました。自然で会話的な流れを目指してください。ブレインストーミングセッションから最高のアイデアを取り入れ、複雑なトピックもわかりやすく説明するようにしてください。""", | |
"dialog": """ここに、ブレインストーミングセッションで考え出したキーポイントと創造的なアイデアに基づいたスクリプトを書いてください。簡潔に保ち、会話調のトーンを使用し、聴衆がアクセスしやすいように必要なコンテキストや説明を含めてください。 | |
スクリプトの冒頭で、これは要約であり、入力テキストのタイトルや見出しに言及していることを述べてください。入力テキストにタイトルがない場合は、カバーされている内容の簡潔な要約を考えてください。 | |
すべての主要な問題の明確な定義と用語、そして例を含めてください。 | |
[Host]や[Guest]のような括弧で囲まれたプレースホルダーを含めないでください。出力は音読されるように設計してください。直接音声に変換されます。 | |
話者はあなただけです。トピックから外れず、魅力的な流れを維持してください。 | |
短い要約の主要な洞察と持ち帰るべきポイントを自然にまとめてください。これは会話から自然に流れ出るものであり、重要なポイントをカジュアルで会話的な方法で繰り返すべきです。 | |
要約は約256語であるべきです。""", | |
}, | |
} | |
# テンプレート選択に基づいて指示フィールドを更新する関数 | |
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 | |
# 標準値を定義 | |
STANDARD_TEXT_MODELS = [ | |
"o1-preview-2024-09-12", | |
"o1-preview", | |
"gpt-4o-2024-08-06", | |
"gpt-4o-mini", | |
"o1-mini-2024-09-12", | |
"o1-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 = '', | |
) -> bytes: | |
# APIキーの検証 | |
if not os.getenv("OPENAI_API_KEY") and not openai_api_key: | |
raise gr.Error("OpenAI APIキーが必要です") | |
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" # 異なるファイルのテキスト間に区切りを追加 | |
# 選択されたモデルとapi_baseに基づいてLLMを設定 | |
def generate_dialogue(text: str, intro_instructions: str, text_instructions: str, scratch_pad_instructions: str, | |
prelude_dialog: str, podcast_dialog_instructions: str, | |
) -> Dialogue: | |
""" | |
{intro_instructions} | |
以下があなたが取り組む入力テキストです: | |
<input_text> | |
{text} | |
</input_text> | |
{text_instructions} | |
<scratchpad> | |
{scratch_pad_instructions} | |
</scratchpad> | |
{prelude_dialog} | |
<podcast_dialogue> | |
{podcast_dialog_instructions} | |
</podcast_dialogue> | |
""" | |
# 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 | |
) | |
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) | |
# 一時ファイルを使用 -- GradioのオーディオコンポーネントはSafariで生のバイトデータが機能しません | |
temporary_file = NamedTemporaryFile( | |
dir=temporary_directory, | |
delete=False, | |
suffix=".mp3", | |
) | |
temporary_file.write(audio) | |
temporary_file.close() | |
# .mp3で終わる古いファイルを削除 | |
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 | |
def validate_and_generate_audio(*args): | |
files = args[0] | |
if not files: | |
return None, None, "音声を生成する前に、少なくとも1つのPDFファイルをアップロードしてください。" | |
audio_file, transcript = generate_audio(*args) | |
return audio_file, transcript, None | |
with gr.Blocks(title="PDFから音声へ", 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("# PDFを音声のポッドキャスト、講義、要約などに変換\n\nまず、1つ以上のPDFをアップロードし、オプションを選択してから「音声を生成」を押してください。\n\nさまざまなカスタムオプションを選択して、結果の生成方法を調整することもできます。", 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("音声を生成", elem_id="submit_btn") | |
with gr.Row(elem_id="main_container"): | |
with gr.Column(scale=2): | |
files = gr.Files(label="PDFファイル", file_types=["pdf"], ) | |
openai_api_key = gr.Textbox( | |
label="OpenAI APIキー", | |
visible=True, # Always show the API key field | |
placeholder="ここにOpenAI APIキーを入力してください...", | |
type="password" # Hide the API key input | |
) | |
text_model = gr.Dropdown( | |
label="テキスト生成モデル", | |
choices=STANDARD_TEXT_MODELS, | |
value="o1-preview-2024-09-12", #"gpt-4o-mini", | |
info="対話テキストを生成するモデルを選択してください。", | |
) | |
audio_model = gr.Dropdown( | |
label="音声生成モデル", | |
choices=STANDARD_AUDIO_MODELS, | |
value="tts-1", | |
info="音声を生成するモデルを選択してください。", | |
) | |
speaker_1_voice = gr.Dropdown( | |
label="話者1の声", | |
choices=STANDARD_VOICES, | |
value="alloy", | |
info="話者1の声を選択してください。", | |
) | |
speaker_2_voice = gr.Dropdown( | |
label="話者2の声", | |
choices=STANDARD_VOICES, | |
value="echo", | |
info="話者2の声を選択してください。", | |
) | |
api_base = gr.Textbox( | |
label="カスタムAPIベース", | |
placeholder="カスタム/ローカルモデルを使用する場合はカスタムAPIベースURLを入力してください...", | |
info="カスタムまたはローカルモデルを使用する場合、ここにAPIベースURLを提供してください。例: http://localhost:8080/v1(llama.cpp RESTサーバーの場合)", | |
) | |
with gr.Column(scale=3): | |
template_dropdown = gr.Dropdown( | |
label="指示テンプレート", | |
choices=list(INSTRUCTION_TEMPLATES.keys()), | |
value="ポッドキャスト", | |
info="使用する指示テンプレートを選択してください。各フィールドを編集してより詳細な結果を得ることもできます。", | |
) | |
intro_instructions = gr.Textbox( | |
label="イントロダクションの指示", | |
lines=10, | |
value=INSTRUCTION_TEMPLATES["ポッドキャスト"]["intro"], | |
info="対話を生成するためのイントロダクションの指示を入力してください。", | |
) | |
text_instructions = gr.Textbox( | |
label="標準テキスト分析の指示", | |
lines=10, | |
placeholder="テキスト分析の指示を入力してください...", | |
value=INSTRUCTION_TEMPLATES["ポッドキャスト"]["text_instructions"], | |
info="生データやテキストを分析するための指示を提供してください。", | |
) | |
scratch_pad_instructions = gr.Textbox( | |
label="下書きの指示", | |
lines=15, | |
value=INSTRUCTION_TEMPLATES["ポッドキャスト"]["scratch_pad"], | |
info="プレゼンテーション/対話コンテンツをブレインストーミングするための下書きの指示を提供してください。", | |
) | |
prelude_dialog = gr.Textbox( | |
label="前置きの対話", | |
lines=5, | |
value=INSTRUCTION_TEMPLATES["ポッドキャスト"]["prelude"], | |
info="プレゼンテーション/対話を開発する前の前置きの指示を提供してください。", | |
) | |
podcast_dialog_instructions = gr.Textbox( | |
label="ポッドキャスト対話の指示", | |
lines=20, | |
value=INSTRUCTION_TEMPLATES["ポッドキャスト"]["dialog"], | |
info="プレゼンテーションまたはポッドキャスト対話を生成するための指示を提供してください。", | |
) | |
audio_output = gr.Audio(label="音声", format="mp3") | |
transcript_output = gr.Textbox(label="トランスクリプト", lines=20, show_copy_button=True) | |
error_output = gr.Textbox(visible=False) # エラーメッセージを格納する隠しテキストボックス | |
# テンプレートが変更されたときに指示フィールドを更新 | |
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 | |
], | |
outputs=[ | |
audio_output, | |
transcript_output, | |
error_output | |
] | |
).then( | |
fn=lambda error: gr.Warning(error) if error else None, | |
inputs=[error_output], | |
outputs=[] | |
) | |
# READMEの内容を下部に追加 | |
gr.Markdown("---") # インターフェースとREADMEを区切る水平線 | |
gr.Markdown(read_readme()) | |
# パフォーマンス向上のためにキューを有効化 | |
demo.queue(max_size=20, default_concurrency_limit=32) | |
# Gradioアプリを起動 | |
if __name__ == "__main__": | |
demo.launch() | |