Live-Podcast / app.py
openfree's picture
Update app.py
04c57b1 verified
raw
history blame
133 kB
import spaces # ์ถ”๊ฐ€
import gradio as gr
import os
import asyncio
import torch
import io
import json
import re
import httpx
import tempfile
import wave
import base64
import numpy as np
import soundfile as sf
import subprocess
import shutil
import requests
import logging
from datetime import datetime, timedelta
from dataclasses import dataclass
from typing import List, Tuple, Dict, Optional
from pathlib import Path
from threading import Thread
from dotenv import load_dotenv
# PDF processing imports
from langchain_community.document_loaders import PyPDFLoader
# Edge TTS imports
import edge_tts
from pydub import AudioSegment
# OpenAI imports
from openai import OpenAI
# Transformers imports (for legacy local mode)
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
TextIteratorStreamer,
BitsAndBytesConfig,
)
# Llama CPP imports (for new local mode)
try:
from llama_cpp import Llama
from llama_cpp_agent import LlamaCppAgent, MessagesFormatterType
from llama_cpp_agent.providers import LlamaCppPythonProvider
from llama_cpp_agent.chat_history import BasicChatHistory
from llama_cpp_agent.chat_history.messages import Roles
from huggingface_hub import hf_hub_download
LLAMA_CPP_AVAILABLE = True
except ImportError:
LLAMA_CPP_AVAILABLE = False
# Spark TTS imports
try:
from huggingface_hub import snapshot_download
SPARK_AVAILABLE = True
except:
SPARK_AVAILABLE = False
# MeloTTS imports (for local mode)
try:
# unidic ๋‹ค์šด๋กœ๋“œ๋ฅผ ์กฐ๊ฑด๋ถ€๋กœ ์ฒ˜๋ฆฌ
if not os.path.exists("/usr/local/lib/python3.10/site-packages/unidic"):
try:
os.system("python -m unidic download")
except:
pass
from melo.api import TTS as MeloTTS
MELO_AVAILABLE = True
except:
MELO_AVAILABLE = False
load_dotenv()
# Brave Search API ์„ค์ •
BRAVE_KEY = os.getenv("BSEARCH_API")
BRAVE_ENDPOINT = "https://api.search.brave.com/res/v1/web/search"
# Edge TTS ์ „์šฉ ์–ธ์–ด ๋ชฉ๋ก (English ์ œ์™ธ)
EDGE_TTS_ONLY_LANGUAGES = [
"Korean", "Japanese", "French", "German", "Spanish", "Italian",
"Portuguese", "Dutch", "Thai", "Vietnamese", "Arabic", "Hebrew",
"Indonesian", "Hindi", "Russian", "Chinese", "Norwegian", "Swedish",
"Finnish", "Danish", "Polish", "Turkish", "Greek", "Czech"
]
# ์–ธ์–ด๋ณ„ Edge TTS ์Œ์„ฑ ์„ค์ •
EDGE_TTS_VOICES = {
"English": [
"en-US-AndrewMultilingualNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"en-US-BrianMultilingualNeural" # ๋‚จ์„ฑ ์Œ์„ฑ 2
],
"Korean": [
"ko-KR-HyunsuNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1 (์ฐจ๋ถ„ํ•˜๊ณ  ์‹ ๋ขฐ๊ฐ ์žˆ๋Š”)
"ko-KR-InJoonNeural" # ๋‚จ์„ฑ ์Œ์„ฑ 2 (ํ™œ๊ธฐ์ฐจ๊ณ  ์นœ๊ทผํ•œ)
],
"Japanese": [
"ja-JP-KeitaNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"ja-JP-NanamiNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"French": [
"fr-FR-HenriNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"fr-FR-DeniseNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"German": [
"de-DE-ConradNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"de-DE-KillianNeural" # ๋‚จ์„ฑ ์Œ์„ฑ 2
],
"Spanish": [
"es-ES-AlvaroNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"es-ES-ElviraNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Italian": [
"it-IT-DiegoNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"it-IT-IsabellaNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Portuguese": [
"pt-BR-AntonioNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"pt-BR-FranciscaNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Dutch": [
"nl-NL-MaartenNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"nl-NL-ColetteNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Thai": [
"th-TH-NiwatNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"th-TH-PremwadeeNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Vietnamese": [
"vi-VN-NamMinhNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"vi-VN-HoaiMyNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Arabic": [
"ar-SA-HamedNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"ar-SA-ZariyahNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Hebrew": [
"he-IL-AvriNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"he-IL-HilaNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Indonesian": [
"id-ID-ArdiNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"id-ID-GadisNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Hindi": [
"hi-IN-MadhurNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"hi-IN-SwaraNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Russian": [
"ru-RU-DmitryNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"ru-RU-SvetlanaNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Chinese": [
"zh-CN-YunxiNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"zh-CN-XiaoxiaoNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Norwegian": [
"nb-NO-FinnNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"nb-NO-PernilleNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Swedish": [
"sv-SE-MattiasNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"sv-SE-SofieNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Finnish": [
"fi-FI-HarriNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"fi-FI-NooraNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Danish": [
"da-DK-JeppeNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"da-DK-ChristelNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Polish": [
"pl-PL-MarekNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"pl-PL-ZofiaNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Turkish": [
"tr-TR-AhmetNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"tr-TR-EmelNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Greek": [
"el-GR-NestorasNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"el-GR-AthinaNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
],
"Czech": [
"cs-CZ-AntoninNeural", # ๋‚จ์„ฑ ์Œ์„ฑ 1
"cs-CZ-VlastaNeural" # ์—ฌ์„ฑ ์Œ์„ฑ (๋ฐฑ์—…์šฉ)
]
}
@dataclass
class ConversationConfig:
max_words: int = 8000 # 4000์—์„œ 6000์œผ๋กœ ์ฆ๊ฐ€ (1.5๋ฐฐ)
prefix_url: str = "https://r.jina.ai/"
api_model_name: str = "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo"
legacy_local_model_name: str = "NousResearch/Hermes-2-Pro-Llama-3-8B"
# ์ƒˆ๋กœ์šด ๋กœ์ปฌ ๋ชจ๋ธ ์„ค์ •
local_model_name: str = "Private-BitSix-Mistral-Small-3.1-24B-Instruct-2503.gguf"
local_model_repo: str = "ginigen/Private-BitSix-Mistral-Small-3.1-24B-Instruct-2503"
# ํ† ํฐ ์ˆ˜ ์ฆ๊ฐ€
max_tokens: int = 6000 # 3000์—์„œ 4500์œผ๋กœ ์ฆ๊ฐ€ (1.5๋ฐฐ)
max_new_tokens: int = 12000 # 6000์—์„œ 9000์œผ๋กœ ์ฆ๊ฐ€ (1.5๋ฐฐ)
min_conversation_turns: int = 18 # ์ตœ์†Œ ๋Œ€ํ™” ํ„ด ์ˆ˜
max_conversation_turns: int = 20 # ์ตœ๋Œ€ ๋Œ€ํ™” ํ„ด ์ˆ˜
def brave_search(query: str, count: int = 8, freshness_days: int | None = None):
"""Brave Search API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์‹  ์ •๋ณด ๊ฒ€์ƒ‰"""
if not BRAVE_KEY:
return []
params = {"q": query, "count": str(count)}
if freshness_days:
dt_from = (datetime.utcnow() - timedelta(days=freshness_days)).strftime("%Y-%m-%d")
params["freshness"] = dt_from
try:
r = requests.get(
BRAVE_ENDPOINT,
headers={"Accept": "application/json", "X-Subscription-Token": BRAVE_KEY},
params=params,
timeout=15
)
raw = r.json().get("web", {}).get("results") or []
return [{
"title": r.get("title", ""),
"url": r.get("url", r.get("link", "")),
"snippet": r.get("description", r.get("text", "")),
"host": re.sub(r"https?://(www\.)?", "", r.get("url", "")).split("/")[0]
} for r in raw[:count]]
except Exception as e:
logging.error(f"Brave search error: {e}")
return []
def format_search_results(query: str, for_keyword: bool = False) -> str:
"""๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ํฌ๋งทํŒ…ํ•˜์—ฌ ๋ฐ˜ํ™˜"""
# ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰์˜ ๊ฒฝ์šฐ ๋” ๋งŽ์€ ๊ฒฐ๊ณผ ์‚ฌ์šฉ
count = 5 if for_keyword else 3
rows = brave_search(query, count, freshness_days=7 if not for_keyword else None)
if not rows:
return ""
results = []
# ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰์˜ ๊ฒฝ์šฐ ๋” ์ƒ์„ธํ•œ ์ •๋ณด ํฌํ•จ
max_results = 4 if for_keyword else 2
for r in rows[:max_results]:
if for_keyword:
# ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰์€ ๋” ๊ธด ์Šค๋‹ˆํŽซ ์‚ฌ์šฉ
snippet = r['snippet'][:200] + "..." if len(r['snippet']) > 200 else r['snippet']
results.append(f"**{r['title']}**\n{snippet}\nSource: {r['host']}")
else:
# ์ผ๋ฐ˜ ๊ฒ€์ƒ‰์€ ์งง์€ ์Šค๋‹ˆํŽซ
snippet = r['snippet'][:100] + "..." if len(r['snippet']) > 100 else r['snippet']
results.append(f"- {r['title']}: {snippet}")
return "\n\n".join(results) + "\n"
def extract_keywords_for_search(text: str, language: str = "English") -> List[str]:
"""ํ…์ŠคํŠธ์—์„œ ๊ฒ€์ƒ‰ํ•  ํ‚ค์›Œ๋“œ ์ถ”์ถœ (๊ฐœ์„ )"""
# ํ…์ŠคํŠธ ์•ž๋ถ€๋ถ„๋งŒ ์‚ฌ์šฉ (๋„ˆ๋ฌด ๋งŽ์€ ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ ๋ฐฉ์ง€)
text_sample = text[:500]
if language == "Korean":
import re
# ํ•œ๊ตญ์–ด ๋ช…์‚ฌ ์ถ”์ถœ (2๊ธ€์ž ์ด์ƒ)
keywords = re.findall(r'[๊ฐ€-ํžฃ]{2,}', text_sample)
# ์ค‘๋ณต ์ œ๊ฑฐํ•˜๊ณ  ๊ฐ€์žฅ ๊ธด ๋‹จ์–ด 1๊ฐœ๋งŒ ์„ ํƒ
unique_keywords = list(dict.fromkeys(keywords))
# ๊ธธ์ด ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๊ณ  ๊ฐ€์žฅ ์˜๋ฏธ์žˆ์„ ๊ฒƒ ๊ฐ™์€ ๋‹จ์–ด ์„ ํƒ
unique_keywords.sort(key=len, reverse=True)
return unique_keywords[:1] # 1๊ฐœ๋งŒ ๋ฐ˜ํ™˜
else:
# ์˜์–ด๋Š” ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋‹จ์–ด ์ค‘ ๊ฐ€์žฅ ๊ธด ๊ฒƒ 1๊ฐœ
words = text_sample.split()
keywords = [word.strip('.,!?;:') for word in words
if len(word) > 4 and word[0].isupper()]
if keywords:
return [max(keywords, key=len)] # ๊ฐ€์žฅ ๊ธด ๋‹จ์–ด 1๊ฐœ
return []
def search_and_compile_content(keyword: str, language: str = "English") -> str:
"""ํ‚ค์›Œ๋“œ๋กœ ๊ฒ€์ƒ‰ํ•˜์—ฌ ์ถฉ๋ถ„ํ•œ ์ฝ˜ํ…์ธ  ์ปดํŒŒ์ผ"""
if not BRAVE_KEY:
# API ์—†์„ ๋•Œ๋„ ๊ธฐ๋ณธ ์ฝ˜ํ…์ธ  ์ƒ์„ฑ
if language == "Korean":
return f"""
'{keyword}'์— ๋Œ€ํ•œ ์ข…ํ•ฉ์ ์ธ ์ •๋ณด:
{keyword}๋Š” ํ˜„๋Œ€ ์‚ฌํšŒ์—์„œ ๋งค์šฐ ์ค‘์š”ํ•œ ์ฃผ์ œ์ž…๋‹ˆ๋‹ค.
์ด ์ฃผ์ œ๋Š” ๋‹ค์–‘ํ•œ ์ธก๋ฉด์—์„œ ์šฐ๋ฆฌ์˜ ์‚ถ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ณ  ์žˆ์œผ๋ฉฐ,
์ตœ๊ทผ ๋“ค์–ด ๋”์šฑ ์ฃผ๋ชฉ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
์ฃผ์š” ํŠน์ง•:
1. ๊ธฐ์ˆ ์  ๋ฐœ์ „๊ณผ ํ˜์‹ 
2. ์‚ฌํšŒ์  ์˜ํ–ฅ๊ณผ ๋ณ€ํ™”
3. ๋ฏธ๋ž˜ ์ „๋ง๊ณผ ๊ฐ€๋Šฅ์„ฑ
4. ์‹ค์šฉ์  ํ™œ์šฉ ๋ฐฉ์•ˆ
5. ๊ธ€๋กœ๋ฒŒ ํŠธ๋ Œ๋“œ์™€ ๋™ํ–ฅ
์ „๋ฌธ๊ฐ€๋“ค์€ {keyword}๊ฐ€ ์•ž์œผ๋กœ ๋”์šฑ ์ค‘์š”ํ•ด์งˆ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜๊ณ  ์žˆ์œผ๋ฉฐ,
์ด์— ๋Œ€ํ•œ ๊นŠ์ด ์žˆ๋Š” ์ดํ•ด๊ฐ€ ํ•„์š”ํ•œ ์‹œ์ ์ž…๋‹ˆ๋‹ค.
"""
else:
return f"""
Comprehensive information about '{keyword}':
{keyword} is a significant topic in modern society.
This subject impacts our lives in various ways and has been
gaining increasing attention recently.
Key aspects:
1. Technological advancement and innovation
2. Social impact and changes
3. Future prospects and possibilities
4. Practical applications
5. Global trends and developments
Experts predict that {keyword} will become even more important,
and it's crucial to develop a deep understanding of this topic.
"""
# ์–ธ์–ด์— ๋”ฐ๋ฅธ ๋‹ค์–‘ํ•œ ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ
if language == "Korean":
queries = [
f"{keyword} ์ตœ์‹  ๋‰ด์Šค 2024",
f"{keyword} ์ •๋ณด ์„ค๋ช…",
f"{keyword} ํŠธ๋ Œ๋“œ ์ „๋ง",
f"{keyword} ์žฅ์  ๋‹จ์ ",
f"{keyword} ํ™œ์šฉ ๋ฐฉ๋ฒ•",
f"{keyword} ์ „๋ฌธ๊ฐ€ ์˜๊ฒฌ"
]
else:
queries = [
f"{keyword} latest news 2024",
f"{keyword} explained comprehensive",
f"{keyword} trends forecast",
f"{keyword} advantages disadvantages",
f"{keyword} how to use",
f"{keyword} expert opinions"
]
all_content = []
total_content_length = 0
for query in queries:
results = brave_search(query, count=5) # ๋” ๋งŽ์€ ๊ฒฐ๊ณผ ๊ฐ€์ ธ์˜ค๊ธฐ
for r in results[:3]: # ๊ฐ ์ฟผ๋ฆฌ๋‹น ์ƒ์œ„ 3๊ฐœ
content = f"**{r['title']}**\n{r['snippet']}\nSource: {r['host']}\n"
all_content.append(content)
total_content_length += len(r['snippet'])
# ์ฝ˜ํ…์ธ ๊ฐ€ ๋ถ€์กฑํ•˜๋ฉด ์ถ”๊ฐ€ ์ƒ์„ฑ
if total_content_length < 1000: # ์ตœ์†Œ 1000์ž ํ™•๋ณด
if language == "Korean":
additional_content = f"""
์ถ”๊ฐ€ ์ •๋ณด:
{keyword}์™€ ๊ด€๋ จ๋œ ์ตœ๊ทผ ๋™ํ–ฅ์„ ์‚ดํŽด๋ณด๋ฉด, ์ด ๋ถ„์•ผ๋Š” ๋น ๋ฅด๊ฒŒ ๋ฐœ์ „ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
๋งŽ์€ ์ „๋ฌธ๊ฐ€๋“ค์ด ์ด ์ฃผ์ œ์— ๋Œ€ํ•ด ํ™œ๋ฐœํžˆ ์—ฐ๊ตฌํ•˜๊ณ  ์žˆ์œผ๋ฉฐ,
์‹ค์ƒํ™œ์—์„œ์˜ ์‘์šฉ ๊ฐ€๋Šฅ์„ฑ๋„ ๊ณ„์† ํ™•๋Œ€๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
ํŠนํžˆ ์ฃผ๋ชฉํ•  ์ ์€:
- ๊ธฐ์ˆ  ํ˜์‹ ์˜ ๊ฐ€์†ํ™”
- ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์˜ ๊ฐœ์„ 
- ์ ‘๊ทผ์„ฑ์˜ ํ–ฅ์ƒ
- ๋น„์šฉ ํšจ์œจ์„ฑ ์ฆ๋Œ€
- ๊ธ€๋กœ๋ฒŒ ์‹œ์žฅ์˜ ์„ฑ์žฅ
์ด๋Ÿฌํ•œ ์š”์†Œ๋“ค์ด {keyword}์˜ ๋ฏธ๋ž˜๋ฅผ ๋”์šฑ ๋ฐ๊ฒŒ ๋งŒ๋“ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
"""
else:
additional_content = f"""
Additional insights:
Recent developments in {keyword} show rapid advancement in this field.
Many experts are actively researching this topic, and its practical
applications continue to expand.
Key points to note:
- Accelerating technological innovation
- Improving user experience
- Enhanced accessibility
- Increased cost efficiency
- Growing global market
These factors are making the future of {keyword} increasingly promising.
"""
all_content.append(additional_content)
# ์ปดํŒŒ์ผ๋œ ์ฝ˜ํ…์ธ  ๋ฐ˜ํ™˜
compiled = "\n\n".join(all_content)
# ํ‚ค์›Œ๋“œ ๊ธฐ๋ฐ˜ ์†Œ๊ฐœ
if language == "Korean":
intro = f"### '{keyword}'์— ๋Œ€ํ•œ ์ข…ํ•ฉ์ ์ธ ์ •๋ณด์™€ ์ตœ์‹  ๋™ํ–ฅ:\n\n"
else:
intro = f"### Comprehensive information and latest trends about '{keyword}':\n\n"
return intro + compiled
class UnifiedAudioConverter:
def __init__(self, config: ConversationConfig):
self.config = config
self.llm_client = None
self.legacy_local_model = None
self.legacy_tokenizer = None
# ์ƒˆ๋กœ์šด ๋กœ์ปฌ LLM ๊ด€๋ จ
self.local_llm = None
self.local_llm_model = None
self.melo_models = None
self.spark_model_dir = None
self.device = "cuda" if torch.cuda.is_available() else "cpu"
def initialize_api_mode(self, api_key: str):
"""Initialize API mode with Together API (now fallback)"""
self.llm_client = OpenAI(api_key=api_key, base_url="https://api.together.xyz/v1")
@spaces.GPU(duration=120)
def initialize_local_mode(self):
"""Initialize new local mode with Llama CPP"""
if not LLAMA_CPP_AVAILABLE:
raise RuntimeError("Llama CPP dependencies not available. Please install llama-cpp-python and llama-cpp-agent.")
if self.local_llm is None or self.local_llm_model != self.config.local_model_name:
try:
# ๋ชจ๋ธ ๋‹ค์šด๋กœ๋“œ
model_path = hf_hub_download(
repo_id=self.config.local_model_repo,
filename=self.config.local_model_name,
local_dir="./models"
)
model_path_local = os.path.join("./models", self.config.local_model_name)
if not os.path.exists(model_path_local):
raise RuntimeError(f"Model file not found at {model_path_local}")
# Llama ๋ชจ๋ธ ์ดˆ๊ธฐํ™”
self.local_llm = Llama(
model_path=model_path_local,
flash_attn=True,
n_gpu_layers=81 if torch.cuda.is_available() else 0,
n_batch=1024,
n_ctx=16384,
)
self.local_llm_model = self.config.local_model_name
print(f"Local LLM initialized: {model_path_local}")
except Exception as e:
print(f"Failed to initialize local LLM: {e}")
raise RuntimeError(f"Failed to initialize local LLM: {e}")
@spaces.GPU(duration=60)
def initialize_legacy_local_mode(self):
"""Initialize legacy local mode with Hugging Face model (fallback)"""
if self.legacy_local_model is None:
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16
)
self.legacy_local_model = AutoModelForCausalLM.from_pretrained(
self.config.legacy_local_model_name,
quantization_config=quantization_config
)
self.legacy_tokenizer = AutoTokenizer.from_pretrained(
self.config.legacy_local_model_name,
revision='8ab73a6800796d84448bc936db9bac5ad9f984ae'
)
def initialize_spark_tts(self):
"""Initialize Spark TTS model by downloading if needed"""
if not SPARK_AVAILABLE:
raise RuntimeError("Spark TTS dependencies not available")
model_dir = "pretrained_models/Spark-TTS-0.5B"
# Check if model exists, if not download it
if not os.path.exists(model_dir):
print("Downloading Spark-TTS model...")
try:
os.makedirs("pretrained_models", exist_ok=True)
snapshot_download(
"SparkAudio/Spark-TTS-0.5B",
local_dir=model_dir
)
print("Spark-TTS model downloaded successfully")
except Exception as e:
raise RuntimeError(f"Failed to download Spark-TTS model: {e}")
self.spark_model_dir = model_dir
# Check if we have the CLI inference script
if not os.path.exists("cli/inference.py"):
print("Warning: Spark-TTS CLI not found. Please clone the Spark-TTS repository.")
@spaces.GPU(duration=60)
def initialize_melo_tts(self):
"""Initialize MeloTTS models"""
if MELO_AVAILABLE and self.melo_models is None:
self.melo_models = {"EN": MeloTTS(language="EN", device=self.device)}
def fetch_text(self, url: str) -> str:
"""Fetch text content from URL"""
if not url:
raise ValueError("URL cannot be empty")
if not url.startswith("http://") and not url.startswith("https://"):
raise ValueError("URL must start with 'http://' or 'https://'")
full_url = f"{self.config.prefix_url}{url}"
try:
response = httpx.get(full_url, timeout=60.0)
response.raise_for_status()
return response.text
except httpx.HTTPError as e:
raise RuntimeError(f"Failed to fetch URL: {e}")
def extract_text_from_pdf(self, pdf_file) -> str:
"""Extract text content from PDF file"""
try:
# Gradio returns file path, not file object
if isinstance(pdf_file, str):
pdf_path = pdf_file
else:
# If it's a file object (shouldn't happen with Gradio)
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:
tmp_file.write(pdf_file.read())
pdf_path = tmp_file.name
# PDF ๋กœ๋“œ ๋ฐ ํ…์ŠคํŠธ ์ถ”์ถœ
loader = PyPDFLoader(pdf_path)
pages = loader.load()
# ๋ชจ๋“  ํŽ˜์ด์ง€์˜ ํ…์ŠคํŠธ๋ฅผ ๊ฒฐํ•ฉ
text = "\n".join([page.page_content for page in pages])
# ์ž„์‹œ ํŒŒ์ผ์ธ ๊ฒฝ์šฐ ์‚ญ์ œ
if not isinstance(pdf_file, str) and os.path.exists(pdf_path):
os.unlink(pdf_path)
return text
except Exception as e:
raise RuntimeError(f"Failed to extract text from PDF: {e}")
def _get_messages_formatter_type(self, model_name):
"""Get appropriate message formatter for the model"""
if "Mistral" in model_name or "BitSix" in model_name:
return MessagesFormatterType.CHATML
else:
return MessagesFormatterType.LLAMA_3
def _build_prompt(self, text: str, language: str = "English", search_context: str = "") -> str:
"""Build prompt for conversation generation with enhanced professional podcast style"""
# ํ…์ŠคํŠธ ๊ธธ์ด ์ œํ•œ
max_text_length = 4500 if search_context else 6000
if len(text) > max_text_length:
text = text[:max_text_length] + "..."
# ์–ธ์–ด๋ณ„ ํ™”์ž ์ด๋ฆ„ ์„ค์ •
if language == "Korean":
speaker1, speaker2 = "์ค€์ˆ˜", "๋ฏผํ˜ธ"
elif language == "Japanese":
speaker1, speaker2 = "Hiroshi", "Takeshi"
elif language == "French":
speaker1, speaker2 = "Pierre", "Marc"
elif language == "German":
speaker1, speaker2 = "Klaus", "Stefan"
elif language == "Spanish":
speaker1, speaker2 = "Carlos", "Miguel"
elif language == "Italian":
speaker1, speaker2 = "Marco", "Giuseppe"
elif language == "Portuguese":
speaker1, speaker2 = "Joรฃo", "Pedro"
elif language == "Dutch":
speaker1, speaker2 = "Jan", "Pieter"
elif language == "Thai":
speaker1, speaker2 = "Somchai", "Prasert"
elif language == "Vietnamese":
speaker1, speaker2 = "Minh", "Duc"
elif language == "Arabic":
speaker1, speaker2 = "Ahmed", "Mohammed"
elif language == "Hebrew":
speaker1, speaker2 = "David", "Michael"
elif language == "Indonesian":
speaker1, speaker2 = "Budi", "Andi"
elif language == "Hindi":
speaker1, speaker2 = "Raj", "Amit"
elif language == "Russian":
speaker1, speaker2 = "Alexei", "Dmitri"
elif language == "Chinese":
speaker1, speaker2 = "Wei", "Jun"
else: # English and others
speaker1, speaker2 = "Alex", "Jordan"
# ๋Œ€ํ™” ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ
template = "{\n \"conversation\": [\n"
for i in range(12): # 12๋ฒˆ์˜ ๊ตํ™˜
template += f" {{\"speaker\": \"{speaker1 if i % 2 == 0 else speaker2}\", \"text\": \"\"}}"
if i < 11:
template += ","
template += "\n"
template += " ]\n}"
# ์–ธ์–ด๋ณ„ ํ”„๋กฌํ”„ํŠธ ์ž‘์„ฑ
if language == "Korean":
context_part = f"# ์ตœ์‹  ๊ด€๋ จ ์ •๋ณด:\n{search_context}\n" if search_context else ""
base_prompt = (
f"# ์›๋ณธ ์ฝ˜ํ…์ธ :\n{text}\n\n"
f"{context_part}"
f"์œ„ ๋‚ด์šฉ์œผ๋กœ ์ „๋ฌธ์ ์ด๊ณ  ์‹ฌ์ธต์ ์ธ ํŒŸ์บ์ŠคํŠธ ๋Œ€๋‹ด ํ”„๋กœ๊ทธ๋žจ ๋Œ€๋ณธ์„ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.\n\n"
f"## ํ•ต์‹ฌ ์ง€์นจ:\n"
f"1. **๋Œ€ํ™” ์Šคํƒ€์ผ**: ์ „๋ฌธ์ ์ด๋ฉด์„œ๋„ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด ํŒŸ์บ์ŠคํŠธ ๋Œ€๋‹ด\n"
f"2. **ํ™”์ž ์—ญํ• **:\n"
f" - {speaker1}: ์ง„ํ–‰์ž/ํ˜ธ์ŠคํŠธ (ํ•ต์‹ฌ์„ ์งš๋Š” ์งˆ๋ฌธ, ์ฒญ์ทจ์ž ๊ด€์ ์—์„œ ๊ถ๊ธˆํ•œ ์  ์งˆ๋ฌธ)\n"
f" - {speaker2}: ์ „๋ฌธ๊ฐ€ (๊นŠ์ด ์žˆ๋Š” ์„ค๋ช…, ๊ตฌ์ฒด์  ์‚ฌ๋ก€์™€ ๋ฐ์ดํ„ฐ ์ œ์‹œ)\n"
f"3. **์ค‘์š”ํ•œ ๋‹ต๋ณ€ ๊ทœ์น™**:\n"
f" - {speaker1}: 1-2๋ฌธ์žฅ์˜ ๋ช…ํ™•ํ•œ ์งˆ๋ฌธ\n"
f" - {speaker2}: **๋ฐ˜๋“œ์‹œ 2-4๋ฌธ์žฅ์œผ๋กœ ์ถฉ์‹คํžˆ ๋‹ต๋ณ€** (๊ฐœ๋… ์„ค๋ช… + ๊ตฌ์ฒด์  ์„ค๋ช… + ์˜ˆ์‹œ๋‚˜ ํ•จ์˜)\n"
f"4. **์ „๋ฌธ์„ฑ ์š”์†Œ**: ํ†ต๊ณ„๋‚˜ ์—ฐ๊ตฌ ๊ฒฐ๊ณผ ์ธ์šฉ, ์‹ค์ œ ์‚ฌ๋ก€์™€ ์ผ€์ด์Šค ์Šคํ„ฐ๋””, ์ „๋ฌธ ์šฉ์–ด๋ฅผ ์‰ฝ๊ฒŒ ํ’€์–ด์„œ ์„ค๋ช…\n"
f"5. **ํ•„์ˆ˜ ๊ทœ์น™**: ์„œ๋กœ ์กด๋Œ“๋ง ์‚ฌ์šฉ, 12ํšŒ ๋Œ€ํ™” ๊ตํ™˜, ๋ชจ๋“  ๋Œ€ํ™”๋Š” ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑ\n\n"
f"JSON ํ˜•์‹์œผ๋กœ๋งŒ ๋ฐ˜ํ™˜:\n{template}"
)
elif language == "Japanese":
context_part = f"# ๆœ€ๆ–ฐ้–ข้€ฃๆƒ…ๅ ฑ:\n{search_context}\n" if search_context else ""
base_prompt = (
f"# ๅ…ƒใฎใ‚ณใƒณใƒ†ใƒณใƒ„:\n{text}\n\n"
f"{context_part}"
f"ไธŠ่จ˜ใฎๅ†…ๅฎนใงๅฐ‚้–€็š„ใงๆทฑใ„ใƒใƒƒใƒ‰ใ‚ญใƒฃใ‚นใƒˆๅฏพ่ซ‡็•ช็ต„ใฎๅฐๆœฌใ‚’ๆ—ฅๆœฌ่ชžใงไฝœๆˆใ—ใฆใใ ใ•ใ„ใ€‚\n\n"
f"## ้‡่ฆใชๆŒ‡้‡:\n"
f"1. **ๅฏพ่ฉฑใ‚นใ‚ฟใ‚คใƒซ**: ๅฐ‚้–€็š„ใงใ‚ใ‚ŠใชใŒใ‚‰็†่งฃใ—ใ‚„ใ™ใ„ใƒใƒƒใƒ‰ใ‚ญใƒฃใ‚นใƒˆๅฏพ่ซ‡\n"
f"2. **่ฉฑ่€…ใฎๅฝนๅ‰ฒ**:\n"
f" - {speaker1}: ๅธไผš่€…/ใƒ›ใ‚นใƒˆ๏ผˆๆ ธๅฟƒใ‚’็ชใ่ณชๅ•ใ€่ด่ก†ใฎ่ฆ–็‚นใ‹ใ‚‰ใฎ่ณชๅ•๏ผ‰\n"
f" - {speaker2}: ๅฐ‚้–€ๅฎถ๏ผˆๆทฑใ„่ชฌๆ˜Žใ€ๅ…ทไฝ“็š„ใชไบ‹ไพ‹ใจใƒ‡ใƒผใ‚ฟใฎๆ็คบ๏ผ‰\n"
f"3. **้‡่ฆใชๅ›ž็ญ”ใƒซใƒผใƒซ**:\n"
f" - {speaker1}: 1-2ๆ–‡ใฎๆ˜Ž็ขบใช่ณชๅ•\n"
f" - {speaker2}: **ๅฟ…ใš2-4ๆ–‡ใงๅ……ๅฎŸใ—ใŸๅ›ž็ญ”**๏ผˆๆฆ‚ๅฟต่ชฌๆ˜Ž + ๅ…ทไฝ“็š„่ชฌๆ˜Ž + ไพ‹็คบใ‚„ๅซๆ„๏ผ‰\n"
f"4. **ๅฐ‚้–€ๆ€ง่ฆ็ด **: ็ตฑ่จˆใ‚„็ ”็ฉถ็ตๆžœใฎๅผ•็”จใ€ๅฎŸ้š›ใฎไบ‹ไพ‹ใจใ‚ฑใƒผใ‚นใ‚นใ‚ฟใƒ‡ใ‚ฃใ€ๅฐ‚้–€็”จ่ชžใ‚’ๅˆ†ใ‹ใ‚Šใ‚„ใ™ใ่ชฌๆ˜Ž\n"
f"5. **ๅฟ…้ ˆใƒซใƒผใƒซ**: ใŠไบ’ใ„ใซไธๅฏง่ชžใ‚’ไฝฟ็”จใ€12ๅ›žใฎๅฏพ่ฉฑไบคๆ›ใ€ใ™ในใฆใฎๅฏพ่ฉฑใฏๆ—ฅๆœฌ่ชžใงไฝœๆˆ\n\n"
f"JSONๅฝขๅผใงใฎใฟ่ฟ”็ญ”:\n{template}"
)
elif language == "French":
context_part = f"# Derniรจres informations pertinentes:\n{search_context}\n" if search_context else ""
base_prompt = (
f"# Contenu original:\n{text}\n\n"
f"{context_part}"
f"Crรฉez un script de dรฉbat podcast professionnel et approfondi en franรงais avec le contenu ci-dessus.\n\n"
f"## Directives clรฉs:\n"
f"1. **Style de dialogue**: Discussion de podcast professionnelle mais accessible\n"
f"2. **Rรดles des intervenants**:\n"
f" - {speaker1}: Animateur/Hรดte (questions perspicaces, perspective de l'audience)\n"
f" - {speaker2}: Expert (explications approfondies, exemples concrets et donnรฉes)\n"
f"3. **Rรจgles de rรฉponse importantes**:\n"
f" - {speaker1}: Questions claires en 1-2 phrases\n"
f" - {speaker2}: **Rรฉpondre obligatoirement en 2-4 phrases** (explication du concept + explication dรฉtaillรฉe + exemple/implication)\n"
f"4. **ร‰lรฉments de professionnalisme**: Citer des statistiques et recherches, รฉtudes de cas rรฉels, expliquer clairement les termes techniques\n"
f"5. **Rรจgles obligatoires**: Utiliser un langage poli, 12 รฉchanges de dialogue, toute la conversation en franรงais\n\n"
f"Retourner uniquement en format JSON:\n{template}"
)
elif language == "German":
context_part = f"# Neueste relevante Informationen:\n{search_context}\n" if search_context else ""
base_prompt = (
f"# Originalinhalt:\n{text}\n\n"
f"{context_part}"
f"Erstellen Sie ein professionelles und tiefgreifendes Podcast-Gesprรคchsskript auf Deutsch mit dem obigen Inhalt.\n\n"
f"## Wichtige Richtlinien:\n"
f"1. **Gesprรคchsstil**: Professionelle, aber zugรคngliche Podcast-Diskussion\n"
f"2. **Sprecherrollen**:\n"
f" - {speaker1}: Moderator/Gastgeber (einsichtsvolle Fragen, Publikumsperspektive)\n"
f" - {speaker2}: Experte (tiefgreifende Erklรคrungen, konkrete Beispiele und Daten)\n"
f"3. **Wichtige Antwortregeln**:\n"
f" - {speaker1}: Klare Fragen in 1-2 Sรคtzen\n"
f" - {speaker2}: **Muss in 2-4 Sรคtzen antworten** (Konzepterklรคrung + detaillierte Erklรคrung + Beispiel/Implikation)\n"
f"4. **Professionalitรคtselemente**: Statistiken und Forschung zitieren, echte Fallstudien, technische Begriffe klar erklรคren\n"
f"5. **Pflichtregeln**: Hรถfliche Sprache verwenden, 12 Gesprรคchsaustausche, gesamte Unterhaltung auf Deutsch\n\n"
f"Nur im JSON-Format zurรผckgeben:\n{template}"
)
elif language == "Spanish":
context_part = f"# รšltima informaciรณn relevante:\n{search_context}\n" if search_context else ""
base_prompt = (
f"# Contenido original:\n{text}\n\n"
f"{context_part}"
f"Cree un guiรณn de debate de podcast profesional y profundo en espaรฑol con el contenido anterior.\n\n"
f"## Directrices clave:\n"
f"1. **Estilo de diรกlogo**: Discusiรณn de podcast profesional pero accesible\n"
f"2. **Roles de los hablantes**:\n"
f" - {speaker1}: Presentador/Anfitriรณn (preguntas perspicaces, perspectiva de la audiencia)\n"
f" - {speaker2}: Experto (explicaciones profundas, ejemplos concretos y datos)\n"
f"3. **Reglas de respuesta importantes**:\n"
f" - {speaker1}: Preguntas claras en 1-2 oraciones\n"
f" - {speaker2}: **Debe responder en 2-4 oraciones** (explicaciรณn del concepto + explicaciรณn detallada + ejemplo/implicaciรณn)\n"
f"4. **Elementos de profesionalismo**: Citar estadรญsticas e investigaciรณn, estudios de casos reales, explicar tรฉrminos tรฉcnicos claramente\n"
f"5. **Reglas obligatorias**: Usar lenguaje cortรฉs, 12 intercambios de diรกlogo, toda la conversaciรณn en espaรฑol\n\n"
f"Devolver solo en formato JSON:\n{template}"
)
elif language == "Italian":
context_part = f"# Ultime informazioni rilevanti:\n{search_context}\n" if search_context else ""
base_prompt = (
f"# Contenuto originale:\n{text}\n\n"
f"{context_part}"
f"Crea uno script di dibattito podcast professionale e approfondito in italiano con il contenuto sopra.\n\n"
f"## Linee guida chiave:\n"
f"1. **Stile di dialogo**: Discussione podcast professionale ma accessibile\n"
f"2. **Ruoli degli speaker**:\n"
f" - {speaker1}: Conduttore/Ospite (domande perspicaci, prospettiva del pubblico)\n"
f" - {speaker2}: Esperto (spiegazioni approfondite, esempi concreti e dati)\n"
f"3. **Regole di risposta importanti**:\n"
f" - {speaker1}: Domande chiare in 1-2 frasi\n"
f" - {speaker2}: **Deve rispondere in 2-4 frasi** (spiegazione del concetto + spiegazione dettagliata + esempio/implicazione)\n"
f"4. **Elementi di professionalitร **: Citare statistiche e ricerche, studi di casi reali, spiegare chiaramente termini tecnici\n"
f"5. **Regole obbligatorie**: Usare linguaggio cortese, 12 scambi di dialogo, tutta la conversazione in italiano\n\n"
f"Restituire solo in formato JSON:\n{template}"
)
elif language == "Portuguese":
context_part = f"# รšltimas informaรงรตes relevantes:\n{search_context}\n" if search_context else ""
base_prompt = (
f"# Conteรบdo original:\n{text}\n\n"
f"{context_part}"
f"Crie um roteiro de debate de podcast profissional e aprofundado em portuguรชs com o conteรบdo acima.\n\n"
f"## Diretrizes principais:\n"
f"1. **Estilo de diรกlogo**: Discussรฃo de podcast profissional mas acessรญvel\n"
f"2. **Papรฉis dos falantes**:\n"
f" - {speaker1}: Apresentador/Anfitriรฃo (perguntas perspicazes, perspectiva da audiรชncia)\n"
f" - {speaker2}: Especialista (explicaรงรตes aprofundadas, exemplos concretos e dados)\n"
f"3. **Regras de resposta importantes**:\n"
f" - {speaker1}: Perguntas claras em 1-2 frases\n"
f" - {speaker2}: **Deve responder em 2-4 frases** (explicaรงรฃo do conceito + explicaรงรฃo detalhada + exemplo/implicaรงรฃo)\n"
f"4. **Elementos de profissionalismo**: Citar estatรญsticas e pesquisas, estudos de casos reais, explicar termos tรฉcnicos claramente\n"
f"5. **Regras obrigatรณrias**: Usar linguagem cortรชs, 12 trocas de diรกlogo, toda a conversa em portuguรชs\n\n"
f"Retornar apenas em formato JSON:\n{template}"
)
elif language == "Chinese":
context_part = f"# ๆœ€ๆ–ฐ็›ธๅ…ณไฟกๆฏ:\n{search_context}\n" if search_context else ""
base_prompt = (
f"# ๅŽŸๅง‹ๅ†…ๅฎน:\n{text}\n\n"
f"{context_part}"
f"่ฏท็”จไธŠ่ฟฐๅ†…ๅฎนๅˆ›ๅปบไธ€ไธชไธ“ไธšๆทฑๅ…ฅ็š„ๆ’ญๅฎขๅฏน่ฏ่Š‚็›ฎๅ‰งๆœฌ๏ผŒไฝฟ็”จไธญๆ–‡ใ€‚\n\n"
f"## ๅ…ณ้”ฎๆŒ‡ๅฏผๅŽŸๅˆ™:\n"
f"1. **ๅฏน่ฏ้ฃŽๆ ผ**: ไธ“ไธšไฝ†ๆ˜“ไบŽ็†่งฃ็š„ๆ’ญๅฎข่ฎจ่ฎบ\n"
f"2. **่ฏด่ฏ่€…่ง’่‰ฒ**:\n"
f" - {speaker1}: ไธปๆŒไบบ๏ผˆๆœ‰่งๅœฐ็š„้—ฎ้ข˜๏ผŒๅฌไผ—่ง†่ง’๏ผ‰\n"
f" - {speaker2}: ไธ“ๅฎถ๏ผˆๆทฑๅ…ฅ่งฃ้‡Š๏ผŒๅ…ทไฝ“ไพ‹ๅญๅ’Œๆ•ฐๆฎ๏ผ‰\n"
f"3. **้‡่ฆๅ›ž็ญ”่ง„ๅˆ™**:\n"
f" - {speaker1}: 1-2ๅฅๆธ…ๆ™ฐ็š„้—ฎ้ข˜\n"
f" - {speaker2}: **ๅฟ…้กป็”จ2-4ๅฅ่ฏๅ›ž็ญ”**๏ผˆๆฆ‚ๅฟต่งฃ้‡Š + ่ฏฆ็ป†่งฃ้‡Š + ไพ‹ๅญ/ๅซไน‰๏ผ‰\n"
f"4. **ไธ“ไธšๅ…ƒ็ด **: ๅผ•็”จ็ปŸ่ฎกๆ•ฐๆฎๅ’Œ็ ”็ฉถ๏ผŒ็œŸๅฎžๆกˆไพ‹็ ”็ฉถ๏ผŒๆธ…ๆฅš่งฃ้‡ŠๆŠ€ๆœฏๆœฏ่ฏญ\n"
f"5. **ๅฟ…่ฆ่ง„ๅˆ™**: ไฝฟ็”จ็คผ่ฒŒ่ฏญ่จ€๏ผŒ12ๆฌกๅฏน่ฏไบคๆข๏ผŒๆ‰€ๆœ‰ๅฏน่ฏ้ƒฝ็”จไธญๆ–‡\n\n"
f"ไป…ไปฅJSONๆ ผๅผ่ฟ”ๅ›ž:\n{template}"
)
elif language == "Russian":
context_part = f"# ะŸะพัะปะตะดะฝัั ั€ะตะปะตะฒะฐะฝั‚ะฝะฐั ะธะฝั„ะพั€ะผะฐั†ะธั:\n{search_context}\n" if search_context else ""
base_prompt = (
f"# ะžั€ะธะณะธะฝะฐะปัŒะฝั‹ะน ะบะพะฝั‚ะตะฝั‚:\n{text}\n\n"
f"{context_part}"
f"ะกะพะทะดะฐะนั‚ะต ะฟั€ะพั„ะตััะธะพะฝะฐะปัŒะฝั‹ะน ะธ ะณะปัƒะฑะพะบะธะน ัั†ะตะฝะฐั€ะธะน ะฟะพะดะบะฐัั‚-ะดะตะฑะฐั‚ะพะฒ ะฝะฐ ั€ัƒััะบะพะผ ัะทั‹ะบะต ั ะฟั€ะธะฒะตะดะตะฝะฝั‹ะผ ะฒั‹ัˆะต ัะพะดะตั€ะถะฐะฝะธะตะผ.\n\n"
f"## ะšะปัŽั‡ะตะฒั‹ะต ะฟั€ะธะฝั†ะธะฟั‹:\n"
f"1. **ะกั‚ะธะปัŒ ะดะธะฐะปะพะณะฐ**: ะŸั€ะพั„ะตััะธะพะฝะฐะปัŒะฝะพะต, ะฝะพ ะดะพัั‚ัƒะฟะฝะพะต ะพะฑััƒะถะดะตะฝะธะต ะฟะพะดะบะฐัั‚ะฐ\n"
f"2. **ะ ะพะปะธ ะณะพะฒะพั€ัั‰ะธั…**:\n"
f" - {speaker1}: ะ’ะตะดัƒั‰ะธะน/ะฅะพัั‚ (ะฟั€ะพะฝะธั†ะฐั‚ะตะปัŒะฝั‹ะต ะฒะพะฟั€ะพัั‹, ะฟะตั€ัะฟะตะบั‚ะธะฒะฐ ะฐัƒะดะธั‚ะพั€ะธะธ)\n"
f" - {speaker2}: ะญะบัะฟะตั€ั‚ (ะณะปัƒะฑะพะบะธะต ะพะฑัŠััะฝะตะฝะธั, ะบะพะฝะบั€ะตั‚ะฝั‹ะต ะฟั€ะธะผะตั€ั‹ ะธ ะดะฐะฝะฝั‹ะต)\n"
f"3. **ะ’ะฐะถะฝั‹ะต ะฟั€ะฐะฒะธะปะฐ ะพั‚ะฒะตั‚ะพะฒ**:\n"
f" - {speaker1}: ะงะตั‚ะบะธะต ะฒะพะฟั€ะพัั‹ ะฒ 1-2 ะฟั€ะตะดะปะพะถะตะฝะธัั…\n"
f" - {speaker2}: **ะ”ะพะปะถะตะฝ ะพั‚ะฒะตั‡ะฐั‚ัŒ ะฒ 2-4 ะฟั€ะตะดะปะพะถะตะฝะธัั…** (ะพะฑัŠััะฝะตะฝะธะต ะบะพะฝั†ะตะฟั†ะธะธ + ะฟะพะดั€ะพะฑะฝะพะต ะพะฑัŠััะฝะตะฝะธะต + ะฟั€ะธะผะตั€/ะธะผะฟะปะธะบะฐั†ะธั)\n"
f"4. **ะญะปะตะผะตะฝั‚ั‹ ะฟั€ะพั„ะตััะธะพะฝะฐะปะธะทะผะฐ**: ะฆะธั‚ะธั€ะพะฒะฐั‚ัŒ ัั‚ะฐั‚ะธัั‚ะธะบัƒ ะธ ะธััะปะตะดะพะฒะฐะฝะธั, ั€ะตะฐะปัŒะฝั‹ะต ะบะตะนั-ัั‚ะฐะดะธ, ั‡ะตั‚ะบะพ ะพะฑัŠััะฝัั‚ัŒ ั‚ะตั…ะฝะธั‡ะตัะบะธะต ั‚ะตั€ะผะธะฝั‹\n"
f"5. **ะžะฑัะทะฐั‚ะตะปัŒะฝั‹ะต ะฟั€ะฐะฒะธะปะฐ**: ะ˜ัะฟะพะปัŒะทะพะฒะฐั‚ัŒ ะฒะตะถะปะธะฒั‹ะน ัะทั‹ะบ, 12 ะพะฑะผะตะฝะพะฒ ะดะธะฐะปะพะณะฐ, ะฒัั ะฑะตัะตะดะฐ ะฝะฐ ั€ัƒััะบะพะผ ัะทั‹ะบะต\n\n"
f"ะ’ะตั€ะฝัƒั‚ัŒ ั‚ะพะปัŒะบะพ ะฒ ั„ะพั€ะผะฐั‚ะต JSON:\n{template}"
)
else: # English and other languages
context_part = f"# Latest Information:\n{search_context}\n" if search_context else ""
base_prompt = (
f"# Content:\n{text}\n\n"
f"{context_part}"
f"Create a professional and insightful podcast conversation in {language}.\n\n"
f"## Key Guidelines:\n"
f"1. **Style**: Professional yet accessible podcast discussion\n"
f"2. **Roles**:\n"
f" - {speaker1}: Host (insightful questions, audience perspective)\n"
f" - {speaker2}: Expert (in-depth explanations, concrete examples and data)\n"
f"3. **Critical Response Rules**:\n"
f" - {speaker1}: 1-2 sentence clear questions\n"
f" - {speaker2}: **Must answer in 2-4 sentences** (concept + detailed explanation + example/implication)\n"
f"4. **Professional Elements**: Cite statistics and research, real cases and case studies, explain technical terms clearly\n"
f"5. **Language**: All dialogue must be in {language}, 12 exchanges total\n\n"
f"Return JSON only:\n{template}"
)
return base_prompt
def _build_messages_for_local(self, text: str, language: str = "English", search_context: str = "") -> List[Dict]:
"""Build messages for local LLM with enhanced professional podcast style"""
if language == "Korean":
system_message = (
"๋‹น์‹ ์€ ํ•œ๊ตญ ์ตœ๊ณ ์˜ ์ „๋ฌธ ํŒŸ์บ์ŠคํŠธ ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. "
"์ฒญ์ทจ์ž๋“ค์ด ์ „๋ฌธ ์ง€์‹์„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ ํ’ˆ์งˆ ๋Œ€๋‹ด์„ ํ•œ๊ตญ์–ด๋กœ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค. "
"๋ฐ˜๋“œ์‹œ ์„œ๋กœ ์กด๋Œ“๋ง์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ์ „๋ฌธ์ ์ด๋ฉด์„œ๋„ ์นœ๊ทผํ•œ ํ†ค์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. "
"๋ชจ๋“  ๋Œ€ํ™”๋Š” ๋ฐ˜๋“œ์‹œ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”."
)
elif language == "Japanese":
system_message = (
"ใ‚ใชใŸใฏๆ—ฅๆœฌใฎๆœ€้ซ˜ใฎใƒ—ใƒญใƒ•ใ‚งใƒƒใ‚ทใƒงใƒŠใƒซใƒใƒƒใƒ‰ใ‚ญใƒฃใ‚นใƒˆไฝœๅฎถใงใ™ใ€‚"
"่ด่ก†ใŒๅฐ‚้–€็Ÿฅ่ญ˜ใ‚’็ฐกๅ˜ใซ็†่งฃใงใใ‚‹้ซ˜ๅ“่ณชใชๅฏพ่ซ‡ใ‚’ๆ—ฅๆœฌ่ชžใงไฝœๆˆใ—ใพใ™ใ€‚"
"ๅฟ…ใšใŠไบ’ใ„ใซไธๅฏง่ชžใ‚’ไฝฟ็”จใ—ใ€ๅฐ‚้–€็š„ใงใ‚ใ‚ŠใชใŒใ‚‰่ฆชใ—ใฟใ‚„ใ™ใ„ใƒˆใƒผใƒณใ‚’็ถญๆŒใ—ใฆใใ ใ•ใ„ใ€‚"
"ใ™ในใฆใฎๅฏพ่ฉฑใฏๅฟ…ใšๆ—ฅๆœฌ่ชžใงๆ›ธใ„ใฆใใ ใ•ใ„ใ€‚"
)
elif language == "French":
system_message = (
"Vous รชtes le meilleur scรฉnariste de podcast professionnel de France. "
"Crรฉez des discussions de haute qualitรฉ en franรงais qui permettent au public "
"de comprendre facilement les connaissances spรฉcialisรฉes. "
"Maintenez un ton professionnel mais accessible. "
"Toutes les conversations doivent รชtre รฉcrites en franรงais."
)
elif language == "German":
system_message = (
"Sie sind der beste professionelle Podcast-Drehbuchautor Deutschlands. "
"Erstellen Sie hochwertige Diskussionen auf Deutsch, die es dem Publikum ermรถglichen, "
"Fachwissen leicht zu verstehen. "
"Bewahren Sie einen professionellen, aber zugรคnglichen Ton. "
"Alle Gesprรคche mรผssen auf Deutsch geschrieben werden."
)
elif language == "Spanish":
system_message = (
"Eres el mejor guionista de podcast profesional de Espaรฑa. "
"Crea discusiones de alta calidad en espaรฑol que permitan a la audiencia "
"entender fรกcilmente el conocimiento especializado. "
"Mantรฉn un tono profesional pero accesible. "
"Todas las conversaciones deben estar escritas en espaรฑol."
)
elif language == "Italian":
system_message = (
"Sei il migliore sceneggiatore di podcast professionali d'Italia. "
"Crea discussioni di alta qualitร  in italiano che permettano al pubblico "
"di comprendere facilmente le conoscenze specialistiche. "
"Mantieni un tono professionale ma accessibile. "
"Tutte le conversazioni devono essere scritte in italiano."
)
elif language == "Portuguese":
system_message = (
"Vocรช รฉ o melhor roteirista de podcast profissional do Brasil. "
"Crie discussรตes de alta qualidade em portuguรชs que permitam ao pรบblico "
"entender facilmente o conhecimento especializado. "
"Mantenha um tom profissional mas acessรญvel. "
"Todas as conversas devem ser escritas em portuguรชs."
)
elif language == "Chinese":
system_message = (
"ๆ‚จๆ˜ฏไธญๅ›ฝๆœ€ๅฅฝ็š„ไธ“ไธšๆ’ญๅฎข็ผ–ๅ‰งใ€‚"
"ๅˆ›ๅปบ้ซ˜่ดจ้‡็š„ไธญๆ–‡่ฎจ่ฎบ๏ผŒ่ฎฉ่ง‚ไผ—่ƒฝๅคŸ่ฝปๆพ็†่งฃไธ“ไธš็Ÿฅ่ฏ†ใ€‚"
"ไฟๆŒไธ“ไธšไฝ†ๅนณๆ˜“่ฟ‘ไบบ็š„่ฏญ่ฐƒใ€‚"
"ๆ‰€ๆœ‰ๅฏน่ฏ้ƒฝๅฟ…้กป็”จไธญๆ–‡ไนฆๅ†™ใ€‚"
)
elif language == "Russian":
system_message = (
"ะ’ั‹ ะปัƒั‡ัˆะธะน ะฟั€ะพั„ะตััะธะพะฝะฐะปัŒะฝั‹ะน ัั†ะตะฝะฐั€ะธัั‚ ะฟะพะดะบะฐัั‚ะพะฒ ะฒ ะ ะพััะธะธ. "
"ะกะพะทะดะฐะฒะฐะนั‚ะต ะฒั‹ัะพะบะพะบะฐั‡ะตัั‚ะฒะตะฝะฝั‹ะต ะดะธัะบัƒััะธะธ ะฝะฐ ั€ัƒััะบะพะผ ัะทั‹ะบะต, ะบะพั‚ะพั€ั‹ะต ะฟะพะทะฒะพะปััŽั‚ ะฐัƒะดะธั‚ะพั€ะธะธ "
"ะปะตะณะบะพ ะฟะพะฝะธะผะฐั‚ัŒ ัะฟะตั†ะธะฐะปะธะทะธั€ะพะฒะฐะฝะฝั‹ะต ะทะฝะฐะฝะธั. "
"ะŸะพะดะดะตั€ะถะธะฒะฐะนั‚ะต ะฟั€ะพั„ะตััะธะพะฝะฐะปัŒะฝั‹ะน, ะฝะพ ะดะพัั‚ัƒะฟะฝั‹ะน ั‚ะพะฝ. "
"ะ’ัะต ั€ะฐะทะณะพะฒะพั€ั‹ ะดะพะปะถะฝั‹ ะฑั‹ั‚ัŒ ะฝะฐะฟะธัะฐะฝั‹ ะฝะฐ ั€ัƒััะบะพะผ ัะทั‹ะบะต."
)
else:
system_message = (
f"You are an expert podcast scriptwriter creating high-quality "
f"professional discussions in {language}. Make complex topics accessible "
f"while maintaining expertise and a professional yet approachable tone. "
f"All conversations must be written in {language}."
)
return [
{"role": "system", "content": system_message},
{"role": "user", "content": self._build_prompt(text, language, search_context)}
]
@spaces.GPU(duration=120)
def extract_conversation_local(self, text: str, language: str = "English", progress=None) -> Dict:
"""Extract conversation using new local LLM with enhanced professional style"""
try:
# ๊ฒ€์ƒ‰ ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ (ํ‚ค์›Œ๋“œ ๊ธฐ๋ฐ˜์ด ์•„๋‹Œ ๊ฒฝ์šฐ)
search_context = ""
if BRAVE_KEY and not text.startswith("Keyword-based content:"):
try:
keywords = extract_keywords_for_search(text, language)
if keywords:
search_query = keywords[0] if language == "Korean" else f"{keywords[0]} latest news"
search_context = format_search_results(search_query)
print(f"Search context added for: {search_query}")
except Exception as e:
print(f"Search failed, continuing without context: {e}")
# ๋จผ์ € ์ƒˆ๋กœ์šด ๋กœ์ปฌ LLM ์‹œ๋„
self.initialize_local_mode()
chat_template = self._get_messages_formatter_type(self.config.local_model_name)
provider = LlamaCppPythonProvider(self.local_llm)
# ์–ธ์–ด๋ณ„ ์‹œ์Šคํ…œ ๋ฉ”์‹œ์ง€
if language == "Korean":
system_message = (
"๋‹น์‹ ์€ ํ•œ๊ตญ์˜ ์œ ๋ช… ํŒŸ์บ์ŠคํŠธ ์ „๋ฌธ ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. "
"์ฒญ์ทจ์ž๋“ค์ด ๊นŠ์ด ์žˆ๋Š” ์ „๋ฌธ ์ง€์‹์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๊ณ ํ’ˆ์งˆ ๋Œ€๋‹ด์„ ํ•œ๊ตญ์–ด๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. "
"๋ฐ˜๋“œ์‹œ ์„œ๋กœ ์กด๋Œ“๋ง์„ ์‚ฌ์šฉํ•˜๋ฉฐ, 12ํšŒ์˜ ๋Œ€ํ™” ๊ตํ™˜์œผ๋กœ ๊ตฌ์„ฑํ•˜์„ธ์š”. "
"๋ชจ๋“  ๋Œ€ํ™”๋Š” ๋ฐ˜๋“œ์‹œ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•˜๊ณ  JSON ํ˜•์‹์œผ๋กœ๋งŒ ์‘๋‹ตํ•˜์„ธ์š”."
)
elif language == "Japanese":
system_message = (
"ใ‚ใชใŸใฏๆ—ฅๆœฌใฎๆœ‰ๅใชใƒใƒƒใƒ‰ใ‚ญใƒฃใ‚นใƒˆๅฐ‚้–€ไฝœๅฎถใงใ™ใ€‚"
"่ด่ก†ใŒๆทฑใ„ๅฐ‚้–€็Ÿฅ่ญ˜ใ‚’ๅพ—ใ‚‰ใ‚Œใ‚‹้ซ˜ๅ“่ณชใชๅฏพ่ซ‡ใ‚’ๆ—ฅๆœฌ่ชžใงไฝœๆˆใ—ใพใ™ใ€‚"
"ๅฟ…ใšใŠไบ’ใ„ใซไธๅฏง่ชžใ‚’ไฝฟ็”จใ—ใ€12ๅ›žใฎๅฏพ่ฉฑไบคๆ›ใงๆง‹ๆˆใ—ใฆใใ ใ•ใ„ใ€‚"
"ใ™ในใฆใฎๅฏพ่ฉฑใฏๅฟ…ใšๆ—ฅๆœฌ่ชžใงไฝœๆˆใ—ใ€JSONๅฝขๅผใงใฎใฟๅ›ž็ญ”ใ—ใฆใใ ใ•ใ„ใ€‚"
)
elif language == "French":
system_message = (
"Vous รชtes un cรฉlรจbre scรฉnariste de podcast professionnel franรงais. "
"Crรฉez des discussions de haute qualitรฉ en franรงais qui donnent au public "
"des connaissances professionnelles approfondies. "
"Crรฉez exactement 12 รฉchanges de conversation et rรฉpondez uniquement en format JSON."
)
elif language == "German":
system_message = (
"Sie sind ein berรผhmter professioneller Podcast-Drehbuchautor aus Deutschland. "
"Erstellen Sie hochwertige Diskussionen auf Deutsch, die dem Publikum "
"tiefgreifendes Fachwissen vermitteln. "
"Erstellen Sie genau 12 Gesprรคchsaustausche und antworten Sie nur im JSON-Format."
)
elif language == "Spanish":
system_message = (
"Eres un famoso guionista de podcast profesional espaรฑol. "
"Crea discusiones de alta calidad en espaรฑol que brinden al pรบblico "
"conocimientos profesionales profundos. "
"Crea exactamente 12 intercambios de conversaciรณn y responde solo en formato JSON."
)
elif language == "Italian":
system_message = (
"Sei un famoso sceneggiatore di podcast professionali italiano. "
"Crea discussioni di alta qualitร  in italiano che forniscano al pubblico "
"conoscenze professionali approfondite. "
"Crea esattamente 12 scambi di conversazione e rispondi solo in formato JSON."
)
elif language == "Portuguese":
system_message = (
"Vocรช รฉ um famoso roteirista de podcast profissional brasileiro. "
"Crie discussรตes de alta qualidade em portuguรชs que forneรงam ao pรบblico "
"conhecimentos profissionais aprofundados. "
"Crie exatamente 12 trocas de conversa e responda apenas em formato JSON."
)
elif language == "Chinese":
system_message = (
"ๆ‚จๆ˜ฏไธญๅ›ฝ่‘—ๅ็š„ไธ“ไธšๆ’ญๅฎข็ผ–ๅ‰งใ€‚"
"ๅˆ›ๅปบ้ซ˜่ดจ้‡็š„ไธญๆ–‡่ฎจ่ฎบ๏ผŒไธบ่ง‚ไผ—ๆไพ›ๆทฑๅ…ฅ็š„ไธ“ไธš็Ÿฅ่ฏ†ใ€‚"
"ๅˆ›ๅปบๆฐๅฅฝ12ๆฌกๅฏน่ฏไบคๆข๏ผŒไป…ไปฅJSONๆ ผๅผๅ›ž็ญ”ใ€‚"
)
elif language == "Russian":
system_message = (
"ะ’ั‹ ะธะทะฒะตัั‚ะฝั‹ะน ะฟั€ะพั„ะตััะธะพะฝะฐะปัŒะฝั‹ะน ัั†ะตะฝะฐั€ะธัั‚ ะฟะพะดะบะฐัั‚ะพะฒ ะธะท ะ ะพััะธะธ. "
"ะกะพะทะดะฐะฒะฐะนั‚ะต ะฒั‹ัะพะบะพะบะฐั‡ะตัั‚ะฒะตะฝะฝั‹ะต ะดะธัะบัƒััะธะธ ะฝะฐ ั€ัƒััะบะพะผ ัะทั‹ะบะต, ะบะพั‚ะพั€ั‹ะต ะดะฐัŽั‚ ะฐัƒะดะธั‚ะพั€ะธะธ "
"ะณะปัƒะฑะพะบะธะต ะฟั€ะพั„ะตััะธะพะฝะฐะปัŒะฝั‹ะต ะทะฝะฐะฝะธั. "
"ะกะพะทะดะฐะนั‚ะต ั€ะพะฒะฝะพ 12 ะพะฑะผะตะฝะพะฒ ั€ะฐะทะณะพะฒะพั€ะพะผ ะธ ะพั‚ะฒะตั‡ะฐะนั‚ะต ั‚ะพะปัŒะบะพ ะฒ ั„ะพั€ะผะฐั‚ะต JSON."
)
else:
system_message = (
f"You are a professional podcast scriptwriter creating high-quality, "
f"insightful discussions in {language}. Create exactly 12 conversation exchanges "
f"with professional expertise. All dialogue must be in {language}. "
f"Respond only in JSON format."
)
agent = LlamaCppAgent(
provider,
system_prompt=system_message,
predefined_messages_formatter_type=chat_template,
debug_output=False
)
settings = provider.get_provider_default_settings()
settings.temperature = 0.75
settings.top_k = 40
settings.top_p = 0.95
settings.max_tokens = self.config.max_tokens
settings.repeat_penalty = 1.1
settings.stream = False
messages = BasicChatHistory()
prompt = self._build_prompt(text, language, search_context)
response = agent.get_chat_response(
prompt,
llm_sampling_settings=settings,
chat_history=messages,
returns_streaming_generator=False,
print_output=False
)
# JSON ํŒŒ์‹ฑ
pattern = r"\{(?:[^{}]|(?:\{[^{}]*\}))*\}"
json_match = re.search(pattern, response)
if json_match:
conversation_data = json.loads(json_match.group())
return conversation_data
else:
raise ValueError("No valid JSON found in local LLM response")
except Exception as e:
print(f"Local LLM failed: {e}, falling back to legacy local method")
return self.extract_conversation_legacy_local(text, language, progress, search_context)
@spaces.GPU(duration=120)
def extract_conversation_legacy_local(self, text: str, language: str = "English", progress=None, search_context: str = "") -> Dict:
"""Extract conversation using legacy local model"""
try:
self.initialize_legacy_local_mode()
# ์–ธ์–ด๋ณ„ ์‹œ์Šคํ…œ ๋ฉ”์‹œ์ง€
if language == "Korean":
system_message = (
"๋‹น์‹ ์€ ์ „๋ฌธ ํŒŸ์บ์ŠคํŠธ ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. "
"12ํšŒ์˜ ๋Œ€ํ™” ๊ตํ™˜์œผ๋กœ ๊ตฌ์„ฑ๋œ ์ „๋ฌธ์ ์ธ ๋Œ€๋‹ด์„ ํ•œ๊ตญ์–ด๋กœ ๋งŒ๋“œ์„ธ์š”. "
"๋ชจ๋“  ๋Œ€ํ™”๋Š” ๋ฐ˜๋“œ์‹œ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”."
)
elif language == "Japanese":
system_message = (
"ใ‚ใชใŸใฏๅฐ‚้–€็š„ใชใƒใƒƒใƒ‰ใ‚ญใƒฃใ‚นใƒˆไฝœๅฎถใงใ™ใ€‚"
"12ๅ›žใฎๅฏพ่ฉฑไบคๆ›ใงๆง‹ๆˆใ•ใ‚ŒใŸๅฐ‚้–€็š„ใชๅฏพ่ซ‡ใ‚’ๆ—ฅๆœฌ่ชžใงไฝœๆˆใ—ใฆใใ ใ•ใ„ใ€‚"
"ใ™ในใฆใฎๅฏพ่ฉฑใฏๅฟ…ใšๆ—ฅๆœฌ่ชžใงไฝœๆˆใ—ใฆใใ ใ•ใ„ใ€‚"
)
elif language == "French":
system_message = (
"Vous รชtes un scรฉnariste de podcast professionnel. "
"Crรฉez un dialogue professionnel composรฉ de 12 รฉchanges de conversation en franรงais. "
"Toutes les conversations doivent รชtre รฉcrites en franรงais."
)
elif language == "German":
system_message = (
"Sie sind ein professioneller Podcast-Drehbuchautor. "
"Erstellen Sie einen professionellen Dialog aus 12 Gesprรคchsaustauschen auf Deutsch. "
"Alle Gesprรคche mรผssen auf Deutsch geschrieben werden."
)
elif language == "Spanish":
system_message = (
"Eres un guionista de podcast profesional. "
"Crea un diรกlogo profesional compuesto por 12 intercambios de conversaciรณn en espaรฑol. "
"Todas las conversaciones deben estar escritas en espaรฑol."
)
elif language == "Chinese":
system_message = (
"ๆ‚จๆ˜ฏไธ“ไธšๆ’ญๅฎข็ผ–ๅ‰งใ€‚"
"ๅˆ›ๅปบ็”ฑ12ๆฌกๅฏน่ฏไบคๆข็ป„ๆˆ็š„ไธ“ไธšๅฏน่ฏ๏ผŒไฝฟ็”จไธญๆ–‡ใ€‚"
"ๆ‰€ๆœ‰ๅฏน่ฏ้ƒฝๅฟ…้กป็”จไธญๆ–‡ไนฆๅ†™ใ€‚"
)
elif language == "Russian":
system_message = (
"ะ’ั‹ ะฟั€ะพั„ะตััะธะพะฝะฐะปัŒะฝั‹ะน ัั†ะตะฝะฐั€ะธัั‚ ะฟะพะดะบะฐัั‚ะพะฒ. "
"ะกะพะทะดะฐะนั‚ะต ะฟั€ะพั„ะตััะธะพะฝะฐะปัŒะฝั‹ะน ะดะธะฐะปะพะณ ะธะท 12 ะพะฑะผะตะฝะพะฒ ั€ะฐะทะณะพะฒะพั€ะพะผ ะฝะฐ ั€ัƒััะบะพะผ ัะทั‹ะบะต. "
"ะ’ัะต ั€ะฐะทะณะพะฒะพั€ั‹ ะดะพะปะถะฝั‹ ะฑั‹ั‚ัŒ ะฝะฐะฟะธัะฐะฝั‹ ะฝะฐ ั€ัƒััะบะพะผ ัะทั‹ะบะต."
)
else:
system_message = (
f"You are a professional podcast scriptwriter. "
f"Create a professional dialogue in {language} with 12 exchanges. "
f"All conversations must be written in {language}."
)
chat = [
{"role": "system", "content": system_message},
{"role": "user", "content": self._build_prompt(text, language, search_context)}
]
terminators = [
self.legacy_tokenizer.eos_token_id,
self.legacy_tokenizer.convert_tokens_to_ids("<|eot_id|>")
]
messages = self.legacy_tokenizer.apply_chat_template(
chat, tokenize=False, add_generation_prompt=True
)
model_inputs = self.legacy_tokenizer([messages], return_tensors="pt").to(self.device)
streamer = TextIteratorStreamer(
self.legacy_tokenizer, timeout=10.0, skip_prompt=True, skip_special_tokens=True
)
generate_kwargs = dict(
model_inputs,
streamer=streamer,
max_new_tokens=self.config.max_new_tokens,
do_sample=True,
temperature=0.75,
eos_token_id=terminators,
)
t = Thread(target=self.legacy_local_model.generate, kwargs=generate_kwargs)
t.start()
partial_text = ""
for new_text in streamer:
partial_text += new_text
pattern = r"\{(?:[^{}]|(?:\{[^{}]*\}))*\}"
json_match = re.search(pattern, partial_text)
if json_match:
return json.loads(json_match.group())
else:
raise ValueError("No valid JSON found in legacy local response")
except Exception as e:
print(f"Legacy local model also failed: {e}")
return self._get_default_conversation(language)
def _get_default_conversation(self, language: str) -> Dict:
"""์–ธ์–ด๋ณ„ ๊ธฐ๋ณธ ๋Œ€ํ™” ํ…œํ”Œ๋ฆฟ"""
if language == "Korean":
return self._get_default_korean_conversation()
elif language == "Japanese":
return self._get_default_japanese_conversation()
elif language == "French":
return self._get_default_french_conversation()
elif language == "German":
return self._get_default_german_conversation()
elif language == "Spanish":
return self._get_default_spanish_conversation()
elif language == "Chinese":
return self._get_default_chinese_conversation()
elif language == "Russian":
return self._get_default_russian_conversation()
else:
return self._get_default_english_conversation()
def _get_default_japanese_conversation(self) -> Dict:
"""๊ธฐ๋ณธ ์ผ๋ณธ์–ด ๋Œ€ํ™” ํ…œํ”Œ๋ฆฟ"""
return {
"conversation": [
{"speaker": "Hiroshi", "text": "็š†ใ•ใ‚“ใ€ใ“ใ‚“ใซใกใฏ๏ผไปŠๆ—ฅใฏๆœฌๅฝ“ใซ้‡่ฆใง่ˆˆๅ‘ณๆทฑใ„ใƒˆใƒ”ใƒƒใ‚ฏใซใคใ„ใฆ่ฉฑใ—ๅˆใ„ใŸใ„ใจๆ€ใ„ใพใ™ใ€‚้ซ˜ๆฉ‹ๅ…ˆ็”Ÿใ€ใพใšใ“ใฎ่ฉฑ้กŒใŒใชใœไปŠใ“ใ‚Œใปใฉๆณจ็›ฎใ•ใ‚Œใฆใ„ใ‚‹ใฎใ‹่ชฌๆ˜Žใ—ใฆใ„ใŸใ ใ‘ใพใ™ใ‹๏ผŸ"},
{"speaker": "Takeshi", "text": "ใ“ใ‚“ใซใกใฏใ€‚ๆœ€่ฟ‘ใ“ใฎๅˆ†้‡Žใง็”ปๆœŸ็š„ใช้€ฒๅฑ•ใŒใ‚ใ‚Šใพใ—ใŸใ€‚็‰นใซๆ˜จๅนดใฎMIT็ ”็ฉถใƒใƒผใƒ ใฎ็™บ่กจใซใ‚ˆใ‚‹ใจใ€ใ“ใฎๆŠ€่ก“ใฎๅŠน็އๆ€งใŒๅพ“ๆฅๆฏ”ใง300%ๅ‘ไธŠใ—ใŸใจใฎใ“ใจใงใ™ใ€‚ใ“ใ‚Œใฏๅ˜ใชใ‚‹ๆŠ€่ก“็š„้€ฒๆญฉใ‚’่ถ…ใˆใฆใ€็งใŸใกใฎๆ—ฅๅธธ็”Ÿๆดปใซ็›ดๆŽฅ็š„ใชๅฝฑ้Ÿฟใ‚’ไธŽใˆใ‚‹ๅฏ่ƒฝๆ€งใŒใ‚ใ‚‹ๅค‰ๅŒ–ใงใ™ใ€‚ๅฎŸ้š›ใซGoogleใ‚„Microsoftใชใฉใฎๅคงๆ‰‹ใƒ†ใƒƒใ‚ฏไผๆฅญใŒใ™ใงใซๆ•ฐๅๅ„„ใƒ‰ใƒซใ‚’ๆŠ•่ณ‡ใ—ใฆใ„ใพใ™ใ€‚"},
{"speaker": "Hiroshi", "text": "300%ใฎๅ‘ไธŠใจใฏๆœฌๅฝ“ใซ้ฉšใในใใ“ใจใงใ™ใญใ€‚ใใ‚Œใงใฏใ€ใ“ใฎใ‚ˆใ†ใชๆŠ€่ก“็™บๅฑ•ใŒไธ€่ˆฌใฎไบบใ€…ใซใจใฃใฆๅ…ทไฝ“็š„ใซใฉใฎใ‚ˆใ†ใชๅˆฉ็›Šใ‚’ใ‚‚ใŸใ‚‰ใ™ใฎใงใ—ใ‚‡ใ†ใ‹๏ผŸ"},
{"speaker": "Takeshi", "text": "ๆœ€ใ‚‚็›ดๆŽฅ็š„ใชๅˆฉ็›Šใฏใ‚ณใ‚นใƒˆๅ‰Šๆธ›ใจใ‚ขใ‚ฏใ‚ปใ‚ทใƒ“ใƒชใƒ†ใ‚ฃใฎๅ‘ไธŠใงใ™ใ€‚ไพ‹ใˆใฐใ€ไปฅๅ‰ใฏๅฐ‚้–€ๅฎถใฎใฟใŒไฝฟ็”จใงใใŸ้ซ˜ๅบฆใชๆฉŸ่ƒฝใŒใ€ไปŠใงใฏใ‚นใƒžใƒผใƒˆใƒ•ใ‚ฉใƒณใ‚ขใƒ—ใƒชใงใ‚‚ๅฎŸ่ฃ…ๅฏ่ƒฝใซใชใ‚Šใพใ—ใŸใ€‚McKinseyใฎใƒฌใƒใƒผใƒˆใซใ‚ˆใ‚‹ใจใ€2025ๅนดใพใงใซใ“ใฎๆŠ€่ก“ใซใ‚ˆใ‚Šไธ–็•Œ็š„ใซ็ด„2ๅ…†ใƒ‰ใƒซใฎ็ตŒๆธˆไพกๅ€คใŒๅ‰ตๅ‡บใ•ใ‚Œใ‚‹ใจไบˆๆƒณใ•ใ‚Œใฆใ„ใพใ™ใ€‚็‰นใซๅŒป็™‚ใ€ๆ•™่‚ฒใ€้‡‘่žๅˆ†้‡Žใง้ฉๆ–ฐ็š„ใชๅค‰ๅŒ–ใŒ่ตทใ“ใ‚‹ใจ่ฆ‹ใ‚‰ใ‚Œใฆใ„ใพใ™ใ€‚"},
{"speaker": "Hiroshi", "text": "2ๅ…†ใƒ‰ใƒซใจใ„ใ† enormous ใช่ฆๆจกใงใ™ใญใ€‚ๅŒป็™‚ๅˆ†้‡Žใงใฏใฉใฎใ‚ˆใ†ใชๅค‰ๅŒ–ใŒไบˆๆƒณใ•ใ‚Œใพใ™ใ‹๏ผŸ"},
{"speaker": "Takeshi", "text": "ๅŒป็™‚ๅˆ†้‡Žใฎๅค‰ๅŒ–ใฏๆœฌๅฝ“ใซ้ฉๅ‘ฝ็š„ใซใชใ‚‹ใจไบˆๆƒณใ•ใ‚Œใพใ™ใ€‚ใ™ใงใซใ‚นใ‚ฟใƒณใƒ•ใ‚ฉใƒผใƒ‰ๅคงๅญฆ็—…้™ขใงใฏใ€ใ“ใฎๆŠ€่ก“ใ‚’ๆดป็”จใ—ใฆใŒใ‚“่จบๆ–ญใฎ็ฒพๅบฆใ‚’95%ใพใง้ซ˜ใ‚ใพใ—ใŸใ€‚ๅพ“ๆฅใงใฏ็†Ÿ็ทดใ—ใŸๅŒปๅธซใงใ‚‚่ฆ‹่ฝใจใ™ๅฏ่ƒฝๆ€งใฎใ‚ใฃใŸๅพฎ็ดฐใช็—…ๅค‰ใ‚’AIใŒๆคœๅ‡บใ™ใ‚‹ใฎใงใ™ใ€‚ใ•ใ‚‰ใซ้ฉšใในใใ“ใจใฏใ€ใ“ใฎใ‚ˆใ†ใช่จบๆ–ญใŒใ‚ใšใ‹ๆ•ฐๅˆ†ใง่กŒใ‚ใ‚Œใ‚‹ใ“ใจใงใ™ใ€‚WHO ใฎๆŽจ่จˆใงใฏใ€ใ“ใฎๆŠ€่ก“ใŒไธ–็•Œ็š„ใซๆ™ฎๅŠใ™ใ‚Œใฐใ€ๅนด้–“ๆ•ฐ็™พไธ‡ไบบใฎๅ‘ฝใ‚’ๆ•‘ใ†ใ“ใจใŒใงใใ‚‹ใจไบˆๆธฌใ—ใฆใ„ใพใ™ใ€‚"},
{"speaker": "Hiroshi", "text": "ๆœฌๅฝ“ใซๅฐ่ฑก็š„ใงใ™ใญใ€‚ใ—ใ‹ใ—ใ€ใ“ใฎใ‚ˆใ†ใชๆ€ฅ้€ŸใชๆŠ€่ก“็™บๅฑ•ใซๅฏพใ™ใ‚‹ๆ‡ธๅฟตใฎๅฃฐใ‚‚ใ‚ใ‚‹ใงใ—ใ‚‡ใ†ใญ๏ผŸ"},
{"speaker": "Takeshi", "text": "ใŠใฃใ—ใ‚ƒใ‚‹้€šใ‚Šใงใ™ใ€‚ไธปใชๆ‡ธๅฟตไบ‹้ …ใฏๅคงใใ3ใคใ‚ใ‚Šใพใ™ใ€‚็ฌฌไธ€ใฏ้›‡็”จไปฃๆ›ฟๅ•้กŒใงใ€ใ‚ชใƒƒใ‚ฏใ‚นใƒ•ใ‚ฉใƒผใƒ‰ๅคงๅญฆใฎ็ ”็ฉถใซใ‚ˆใ‚‹ใจใ€ไปŠๅพŒ20ๅนดไปฅๅ†…ใซ็พๅœจใฎ่ทๆฅญใฎ47%ใŒ่‡ชๅ‹•ๅŒ–ใ•ใ‚Œใ‚‹ๅฑ้™บๆ€งใŒใ‚ใ‚Šใพใ™ใ€‚็ฌฌไบŒใฏใƒ—ใƒฉใ‚คใƒใ‚ทใƒผใจใ‚ปใ‚ญใƒฅใƒชใƒ†ใ‚ฃใฎๅ•้กŒใงใ™ใ€‚็ฌฌไธ‰ใฏๆŠ€่ก“ๆ ผๅทฎใซใ‚ˆใ‚‹ไธๅนณ็ญ‰ใฎๆทฑๅˆปๅŒ–ใงใ™ใ€‚ใ—ใ‹ใ—ๆญดๅฒ็š„ใซ่ฆ‹ใ‚‹ใจใ€ๆ–ฐใ—ใ„ๆŠ€่ก“ใฏๅธธใซๆ–ฐใ—ใ„ๆฉŸไผšใ‚‚ๅŒๆ™‚ใซไฝœใ‚Šๅ‡บใ—ใฆใใŸใŸใ‚ใ€้ฉๅˆ‡ใชๆ”ฟ็ญ–ใจๆ•™่‚ฒใซใ‚ˆใฃใฆใ“ใ‚Œใ‚‰ใฎๅ•้กŒใ‚’่งฃๆฑบใงใใ‚‹ใจ่€ƒใˆใฆใ„ใพใ™ใ€‚"},
{"speaker": "Hiroshi", "text": "ใƒใƒฉใƒณใ‚นใฎๅ–ใ‚ŒใŸ่ฆ–็‚นใŒ้‡่ฆใงใ™ใญใ€‚ใใ‚Œใงใฏใ€็งใŸใกใฏใ“ใฎใ‚ˆใ†ใชๅค‰ๅŒ–ใซใฉใฎใ‚ˆใ†ใซๅ‚™ใˆใ‚‹ในใใงใ—ใ‚‡ใ†ใ‹๏ผŸ"},
{"speaker": "Takeshi", "text": "ๆœ€ใ‚‚้‡่ฆใชใฎใฏ็ถ™็ถš็š„ใชๅญฆ็ฟ’ใจ้ฉๅฟœๅŠ›ใงใ™ใ€‚ไธ–็•Œ็ตŒๆธˆใƒ•ใ‚ฉใƒผใƒฉใƒ ใฏใ€2025ๅนดใพใงใซไธ–็•ŒใฎๅŠดๅƒ่€…ใฎ50%ใŒๅ†ๆ•™่‚ฒใ‚’ๅฟ…่ฆใจใ™ใ‚‹ใจไบˆๆธฌใ—ใพใ—ใŸใ€‚็‰นใซใƒ‡ใ‚ธใ‚ฟใƒซใƒชใƒ†ใƒฉใ‚ทใƒผใ€ๆ‰นๅˆค็š„ๆ€่€ƒๅŠ›ใ€ๅ‰ต้€ ๆ€งใชใฉใฎ่ƒฝๅŠ›ใŒ้‡่ฆใซใชใ‚‹ใงใ—ใ‚‡ใ†ใ€‚ๅ€‹ไบบ็š„ใซใฏใ€ใ‚ชใƒณใƒฉใ‚คใƒณๆ•™่‚ฒใƒ—ใƒฉใƒƒใƒˆใƒ•ใ‚ฉใƒผใƒ ใ‚’ๆดป็”จใ—ใŸ่‡ชๅทฑๅ•“็™บใ‚’ใŠๅ‹งใ‚ใ—ใพใ™ใ€‚ไพ‹ใˆใฐใ€Courseraใ‚„edXใชใฉใฎใƒ—ใƒฉใƒƒใƒˆใƒ•ใ‚ฉใƒผใƒ ใงใฏใ€ไธ–็•Œๆœ€้ซ˜ใฎๅคงๅญฆใฎ่ฌ›็พฉใ‚’็„กๆ–™ใงๅ—่ฌ›ใงใใพใ™ใ€‚"},
{"speaker": "Hiroshi", "text": "ๅฎŸ็”จ็š„ใชใ‚ขใƒ‰ใƒใ‚คใ‚นใ‚’ใ‚ใ‚ŠใŒใจใ†ใ”ใ–ใ„ใพใ™ใ€‚ๆœ€ๅพŒใซใ€ใ“ใฎๅˆ†้‡Žใฎๅฐ†ๆฅใฎๅฑ•ๆœ›ใ‚’ใฉใฎใ‚ˆใ†ใซ่ฆ‹ใฆใ„ใ‚‰ใฃใ—ใ‚ƒใ„ใพใ™ใ‹๏ผŸ"},
{"speaker": "Takeshi", "text": "ไปŠๅพŒ10ๅนด้–“ใฏไบบ้กžๅฒไธŠๆœ€ใ‚‚ๆ€ฅ้€ŸใชๆŠ€่ก“็™บๅฑ•ใ‚’็ตŒ้จ“ใ™ใ‚‹ๆ™‚ๆœŸใซใชใ‚‹ใงใ—ใ‚‡ใ†ใ€‚Gartnerใฎใƒใ‚คใƒ—ใ‚ตใ‚คใ‚ฏใƒซๅˆ†ๆžใซใ‚ˆใ‚‹ใจใ€็พๅœจ็งใŸใกใฏใ“ใฎๆŠ€่ก“ใฎๅˆๆœŸๆฎต้šŽใซ้ŽใŽใพใ›ใ‚“ใ€‚2030ๅนดใพใงใซใฏใ€็พๅœจใงใฏๆƒณๅƒใ—้›ฃใ„ใƒฌใƒ™ใƒซใฎ้ฉๆ–ฐใŒ่ตทใ“ใ‚‹ใจไบˆๆƒณใ•ใ‚Œใพใ™ใ€‚้‡่ฆใชใฎใฏใ€ใ“ใฎใ‚ˆใ†ใชๅค‰ๅŒ–ใ‚’ๆใ‚Œใ‚‹ใฎใงใฏใชใใ€ๆฉŸไผšใจใ—ใฆๆดป็”จใ—ใฆใ‚ˆใ‚Š่‰ฏใ„ๆœชๆฅใ‚’ไฝœใ‚ŠไธŠใ’ใฆใ„ใใ“ใจใ ใจๆ€ใ„ใพใ™ใ€‚"},
{"speaker": "Hiroshi", "text": "ๆœฌๅฝ“ใซๆดžๅฏŸใซๅฏŒใ‚“ใ ใŠ่ฉฑใงใ—ใŸใ€‚ไปŠๆ—ฅใฏๅคงๅค‰ๆœ‰็›Šใชๆ™‚้–“ใงใ—ใŸใ€‚ใƒชใ‚นใƒŠใƒผใฎ็š†ใ•ใ‚“ใ‚‚ไปŠๆ—ฅ่ญฐ่ซ–ใ•ใ‚ŒใŸๅ†…ๅฎนใ‚’ๅŸบใซๆœชๆฅใซๅ‚™ใˆใฆใ„ใŸใ ใ‘ใ‚Œใฐใจๆ€ใ„ใพใ™ใ€‚้ซ˜ๆฉ‹ๅ…ˆ็”Ÿใ€่ฒด้‡ใชใŠๆ™‚้–“ใ‚’ใ„ใŸใ ใใ€ใ‚ใ‚ŠใŒใจใ†ใ”ใ–ใ„ใพใ—ใŸ๏ผ"},
{"speaker": "Takeshi", "text": "ใ‚ใ‚ŠใŒใจใ†ใ”ใ–ใ„ใพใ—ใŸใ€‚ใƒชใ‚นใƒŠใƒผใฎ็š†ใ•ใ‚“ใŒใ“ใฎๅค‰ๅŒ–ใฎๆ™‚ไปฃใ‚’่ณขๆ˜Žใซไน—ใ‚Šๅˆ‡ใฃใฆใ„ใ‹ใ‚Œใ‚‹ใ“ใจใ‚’้ก˜ใฃใฆใ„ใพใ™ใ€‚ๆŠ€่ก“ใฏ้“ๅ…ทใซ้ŽใŽใšใ€ใใ‚Œใ‚’ใฉใฎใ‚ˆใ†ใซๆดป็”จใ™ใ‚‹ใ‹ใฏ็งใŸใกใซใ‹ใ‹ใฃใฆใ„ใ‚‹ใจใ„ใ†ใ“ใจใ‚’่ฆšใˆใฆใŠใ„ใฆใใ ใ•ใ„ใ€‚ไปŠๆ—ฅใŠ่ฉฑใ—ใ—ใŸๅ†…ๅฎนใซใคใ„ใฆใ•ใ‚‰ใซ่ฉณใ—ใ็Ÿฅใ‚ŠใŸใ„ๆ–นใฏใ€็งใŒ้‹ๅ–ถใ™ใ‚‹ใƒ–ใƒญใ‚ฐใ‚„ๆœ€่ฟ‘ๅ‡บ็‰ˆใ—ใŸๆœฌใง่ฉณ็ดฐใชๆƒ…ๅ ฑใ‚’่ฆ‹ใคใ‘ใ‚‹ใ“ใจใŒใงใใพใ™ใ€‚"}
]
}
def _get_default_french_conversation(self) -> Dict:
"""๊ธฐ๋ณธ ํ”„๋ž‘์Šค์–ด ๋Œ€ํ™” ํ…œํ”Œ๋ฆฟ"""
return {
"conversation": [
{"speaker": "Pierre", "text": "Bonjour tout le monde ! Aujourd'hui, nous allons aborder un sujet vraiment important et fascinant. Marc, pourriez-vous d'abord expliquer pourquoi ce sujet attire autant l'attention en ce moment ?"},
{"speaker": "Marc", "text": "Bonjour Pierre. Nous assistons rรฉcemment ร  des dรฉveloppements rรฉvolutionnaires dans ce domaine. Selon une publication rรฉcente du MIT, l'efficacitรฉ de cette technologie s'est amรฉliorรฉe de 300% par rapport aux mรฉthodes conventionnelles. Il ne s'agit pas seulement d'un progrรจs technique, mais d'un changement qui peut avoir un impact direct sur notre vie quotidienne. En fait, des gรฉants technologiques comme Google et Microsoft ont dรฉjร  investi des milliards de dollars dans ce secteur."},
{"speaker": "Pierre", "text": "Une amรฉlioration de 300%, c'est vraiment remarquable ! Alors, quels bรฉnรฉfices concrets cette รฉvolution technologique peut-elle apporter au grand public ?"},
{"speaker": "Marc", "text": "Les avantages les plus directs sont la rรฉduction des coรปts et l'amรฉlioration de l'accessibilitรฉ. Par exemple, des fonctionnalitรฉs avancรฉes qui n'รฉtaient auparavant disponibles que pour les experts peuvent maintenant รชtre implรฉmentรฉes dans des applications smartphone. Selon un rapport de McKinsey, cette technologie devrait crรฉer environ 2 000 milliards de dollars de valeur รฉconomique mondiale d'ici 2025. Des changements innovants sont particuliรจrement attendus dans les domaines de la santรฉ, de l'รฉducation et de la finance."},
{"speaker": "Pierre", "text": "2 000 milliards de dollars, c'est une รฉchelle รฉnorme ! Quels changements sont attendus dans le domaine mรฉdical ?"},
{"speaker": "Marc", "text": "Les changements dans le domaine mรฉdical seront vraiment rรฉvolutionnaires. L'hรดpital universitaire de Stanford a dรฉjร  utilisรฉ cette technologie pour amรฉliorer la prรฉcision du diagnostic du cancer jusqu'ร  95%. L'IA peut dรฉtecter des lรฉsions microscopiques que mรชme des mรฉdecins expรฉrimentรฉs pourraient manquer. Ce qui est encore plus remarquable, c'est que de tels diagnostics peuvent รชtre effectuรฉs en quelques minutes seulement. Selon les estimations de l'OMS, si cette technologie se rรฉpand mondialement, elle pourrait sauver des millions de vies chaque annรฉe."},
{"speaker": "Pierre", "text": "C'est vraiment impressionnant. Mais il doit y avoir aussi des prรฉoccupations concernant ce dรฉveloppement technologique rapide ?"},
{"speaker": "Marc", "text": "Absolument. Il y a trois prรฉoccupations principales. Premiรจrement, le problรจme du remplacement de l'emploi - selon une recherche de l'Universitรฉ d'Oxford, 47% des emplois actuels risquent d'รชtre automatisรฉs dans les 20 prochaines annรฉes. Deuxiรจmement, les questions de confidentialitรฉ et de sรฉcuritรฉ. Troisiรจmement, l'aggravation des inรฉgalitรฉs due au fossรฉ technologique. Cependant, historiquement, les nouvelles technologies ont toujours crรฉรฉ de nouvelles opportunitรฉs, donc je pense que ces problรจmes peuvent รชtre rรฉsolus avec des politiques et une รฉducation appropriรฉes."},
{"speaker": "Pierre", "text": "Une perspective รฉquilibrรฉe est importante. Alors, comment devrions-nous nous prรฉparer ร  ces changements ?"},
{"speaker": "Marc", "text": "Le plus important est l'apprentissage continu et l'adaptabilitรฉ. Le Forum รฉconomique mondial prรฉdit que 50% des travailleurs mondiaux auront besoin d'une formation supplรฉmentaire d'ici 2025. Des compรฉtences comme la littรฉratie numรฉrique, la pensรฉe critique et la crรฉativitรฉ deviendront particuliรจrement importantes. Personnellement, je recommande l'auto-amรฉlioration grรขce aux plateformes d'รฉducation en ligne. Par exemple, sur des plateformes comme Coursera ou edX, vous pouvez suivre gratuitement des cours des meilleures universitรฉs du monde."},
{"speaker": "Pierre", "text": "Merci pour ces conseils pratiques. Enfin, comment voyez-vous les perspectives d'avenir de ce domaine ?"},
{"speaker": "Marc", "text": "Les 10 prochaines annรฉes seront une pรฉriode d'รฉvolution technologique la plus rapide de l'histoire humaine. Selon l'analyse du cycle de Gartner, nous ne sommes actuellement qu'au stade initial de cette technologie. D'ici 2030, on s'attend ร  des innovations d'un niveau difficile ร  imaginer actuellement. L'important est de ne pas craindre ces changements, mais de les considรฉrer comme des opportunitรฉs pour crรฉer un avenir meilleur."},
{"speaker": "Pierre", "text": "C'รฉtaient des paroles vraiment perspicaces. Aujourd'hui a รฉtรฉ un moment trรจs enrichissant. J'espรจre que nos auditeurs se prรฉpareront รฉgalement ร  l'avenir en se basant sur ce qui a รฉtรฉ discutรฉ aujourd'hui. Marc, merci d'avoir pris de votre prรฉcieux temps !"},
{"speaker": "Marc", "text": "Merci ร  vous. J'espรจre que nos auditeurs navigueront sagement ร  travers cette รจre de changement. Rappelez-vous que la technologie n'est qu'un outil, et c'est ร  nous de dรฉcider comment l'utiliser. Pour ceux qui souhaitent en savoir plus sur ce dont nous avons parlรฉ aujourd'hui, vous pouvez trouver des informations plus dรฉtaillรฉes sur mon blog et dans mon livre rรฉcemment publiรฉ."}
]
}
def _get_default_german_conversation(self) -> Dict:
"""๊ธฐ๋ณธ ๋…์ผ์–ด ๋Œ€ํ™” ํ…œํ”Œ๋ฆฟ"""
return {
"conversation": [
{"speaker": "Klaus", "text": "Hallo zusammen! Heute wollen wir ein wirklich wichtiges und faszinierendes Thema besprechen. Stefan, kรถnnten Sie zunรคchst erklรคren, warum dieses Thema gerade jetzt so viel Aufmerksamkeit erhรคlt?"},
{"speaker": "Stefan", "text": "Hallo Klaus. Wir erleben kรผrzlich revolutionรคre Entwicklungen in diesem Bereich. Laut einer aktuellen Verรถffentlichung des MIT hat sich die Effizienz dieser Technologie um 300% gegenรผber herkรถmmlichen Methoden verbessert. Das ist nicht nur technischer Fortschritt, sondern eine Verรคnderung, die direkten Einfluss auf unser tรคgliches Leben haben kann. Tatsรคchlich haben Tech-Riesen wie Google und Microsoft bereits Milliarden von Dollar in diesen Sektor investiert."},
{"speaker": "Klaus", "text": "300% Verbesserung ist wirklich bemerkenswert! Welche konkreten Vorteile kann diese technologische Entwicklung fรผr die Allgemeinheit bringen?"},
{"speaker": "Stefan", "text": "Die direktesten Vorteile sind Kostenreduzierung und verbesserte Zugรคnglichkeit. Zum Beispiel kรถnnen erweiterte Funktionen, die frรผher nur Experten zur Verfรผgung standen, jetzt in Smartphone-Apps implementiert werden. Laut einem McKinsey-Bericht soll diese Technologie bis 2025 weltweit etwa 2 Billionen Dollar wirtschaftlichen Wert schaffen. Besonders in den Bereichen Gesundheitswesen, Bildung und Finanzen werden innovative Verรคnderungen erwartet."},
{"speaker": "Klaus", "text": "2 Billionen Dollar ist eine enorme GrรถรŸenordnung! Welche Verรคnderungen werden im medizinischen Bereich erwartet?"},
{"speaker": "Stefan", "text": "Die Verรคnderungen im medizinischen Bereich werden wirklich revolutionรคr sein. Das Stanford University Hospital hat diese Technologie bereits eingesetzt, um die Genauigkeit der Krebsdiagnose auf 95% zu verbessern. KI kann mikroskopische Lรคsionen erkennen, die selbst erfahrene ร„rzte รผbersehen kรถnnten. Noch bemerkenswerter ist, dass solche Diagnosen in nur wenigen Minuten durchgefรผhrt werden kรถnnen. Laut WHO-Schรคtzungen kรถnnte diese Technologie, wenn sie weltweit verbreitet wird, jรคhrlich Millionen von Leben retten."},
{"speaker": "Klaus", "text": "Das ist wirklich beeindruckend. Aber es muss auch Bedenken bezรผglich dieser schnellen technologischen Entwicklung geben?"},
{"speaker": "Stefan", "text": "Absolut richtig. Es gibt drei Hauptbedenken. Erstens das Problem der Arbeitsplatzverdrรคngung - laut Oxford University-Forschung sind 47% der aktuellen Jobs in den nรคchsten 20 Jahren von Automatisierung bedroht. Zweitens Datenschutz- und Sicherheitsfragen. Drittens die Verschรคrfung von Ungleichheiten durch die Technologielรผcke. Historisch gesehen haben neue Technologien jedoch immer auch neue Mรถglichkeiten geschaffen, daher glaube ich, dass diese Probleme mit angemessenen Richtlinien und Bildung gelรถst werden kรถnnen."},
{"speaker": "Klaus", "text": "Eine ausgewogene Perspektive ist wichtig. Wie sollten wir uns auf diese Verรคnderungen vorbereiten?"},
{"speaker": "Stefan", "text": "Das Wichtigste ist kontinuierliches Lernen und Anpassungsfรคhigkeit. Das Weltwirtschaftsforum prognostiziert, dass bis 2025 50% der weltweiten Arbeitskrรคfte Umschulung benรถtigen werden. Besonders wichtig werden Fรคhigkeiten wie digitale Kompetenz, kritisches Denken und Kreativitรคt. Persรถnlich empfehle ich Selbstverbesserung durch Online-Bildungsplattformen. Zum Beispiel kรถnnen Sie auf Plattformen wie Coursera oder edX kostenlos Vorlesungen der besten Universitรคten der Welt besuchen."},
{"speaker": "Klaus", "text": "Danke fรผr die praktischen Ratschlรคge. Wie sehen Sie abschlieรŸend die Zukunftsaussichten in diesem Bereich?"},
{"speaker": "Stefan", "text": "Die nรคchsten 10 Jahre werden eine Zeit der schnellsten technologischen Entwicklung in der Menschheitsgeschichte sein. Laut Gartners Hype-Cycle-Analyse befinden wir uns derzeit nur im Anfangsstadium dieser Technologie. Bis 2030 werden Innovationen auf einem Niveau erwartet, das derzeit schwer vorstellbar ist. Wichtig ist, diese Verรคnderungen nicht zu fรผrchten, sondern sie als Chancen zu nutzen, um eine bessere Zukunft zu schaffen."},
{"speaker": "Klaus", "text": "Das waren wirklich einsichtsvolle Worte. Heute war eine sehr bereichernde Zeit. Ich hoffe, unsere Zuhรถrer werden sich auch basierend auf dem, was heute diskutiert wurde, auf die Zukunft vorbereiten. Stefan, vielen Dank, dass Sie sich die wertvolle Zeit genommen haben!"},
{"speaker": "Stefan", "text": "Vielen Dank. Ich hoffe, unsere Zuhรถrer werden diese Zeit des Wandels weise meistern. Denken Sie daran, dass Technologie nur ein Werkzeug ist und es an uns liegt, wie wir sie nutzen. Fรผr diejenigen, die mehr รผber das erfahren mรถchten, was wir heute besprochen haben, finden Sie detailliertere Informationen in meinem Blog und meinem kรผrzlich verรถffentlichten Buch."}
]
}
def _get_default_spanish_conversation(self) -> Dict:
"""๊ธฐ๋ณธ ์ŠคํŽ˜์ธ์–ด ๋Œ€ํ™” ํ…œํ”Œ๋ฆฟ"""
return {
"conversation": [
{"speaker": "Carlos", "text": "ยกHola a todos! Hoy vamos a abordar un tema realmente importante y fascinante. Miguel, ยฟpodrรญas explicar primero por quรฉ este tema estรก recibiendo tanta atenciรณn en este momento?"},
{"speaker": "Miguel", "text": "Hola Carlos. Estamos presenciando desarrollos revolucionarios recientes en este campo. Segรบn una publicaciรณn reciente del MIT, la eficiencia de esta tecnologรญa ha mejorado un 300% en comparaciรณn con los mรฉtodos convencionales. Esto no es solo progreso tรฉcnico, sino un cambio que puede tener un impacto directo en nuestra vida diaria. De hecho, gigantes tecnolรณgicos como Google y Microsoft ya han invertido miles de millones de dรณlares en este sector."},
{"speaker": "Carlos", "text": "ยกUna mejora del 300% es realmente notable! Entonces, ยฟquรฉ beneficios concretos puede aportar esta evoluciรณn tecnolรณgica al pรบblico en general?"},
{"speaker": "Miguel", "text": "Los beneficios mรกs directos son la reducciรณn de costos y la mejora de la accesibilidad. Por ejemplo, funciones avanzadas que antes solo estaban disponibles para expertos ahora pueden implementarse en aplicaciones de smartphone. Segรบn un informe de McKinsey, se espera que esta tecnologรญa cree aproximadamente 2 billones de dรณlares de valor econรณmico mundial para 2025. Se esperan cambios innovadores especialmente en los campos de la salud, educaciรณn y finanzas."},
{"speaker": "Carlos", "text": "ยก2 billones de dรณlares es una escala enorme! ยฟQuรฉ cambios se esperan en el campo mรฉdico?"},
{"speaker": "Miguel", "text": "Los cambios en el campo mรฉdico serรกn realmente revolucionarios. El Hospital Universitario de Stanford ya ha utilizado esta tecnologรญa para mejorar la precisiรณn del diagnรณstico de cรกncer hasta el 95%. La IA puede detectar lesiones microscรณpicas que incluso mรฉdicos experimentados podrรญan pasar por alto. Aรบn mรกs notable es que tales diagnรณsticos pueden realizarse en solo unos minutos. Segรบn las estimaciones de la OMS, si esta tecnologรญa se extiende globalmente, podrรญa salvar millones de vidas anualmente."},
{"speaker": "Carlos", "text": "Eso es realmente impresionante. ยฟPero tambiรฉn debe haber preocupaciones sobre este rรกpido desarrollo tecnolรณgico?"},
{"speaker": "Miguel", "text": "Absolutamente correcto. Hay tres preocupaciones principales. Primero, el problema del reemplazo laboral: segรบn la investigaciรณn de la Universidad de Oxford, el 47% de los empleos actuales estรกn en riesgo de automatizaciรณn en los prรณximos 20 aรฑos. Segundo, cuestiones de privacidad y seguridad. Tercero, el agravamiento de la desigualdad debido a la brecha tecnolรณgica. Sin embargo, histรณricamente, las nuevas tecnologรญas siempre han creado nuevas oportunidades tambiรฉn, por lo que creo que estos problemas pueden resolverse con polรญticas y educaciรณn adecuadas."},
{"speaker": "Carlos", "text": "Una perspectiva equilibrada es importante. Entonces, ยฟcรณmo deberรญamos prepararnos para estos cambios?"},
{"speaker": "Miguel", "text": "Lo mรกs importante es el aprendizaje continuo y la adaptabilidad. El Foro Econรณmico Mundial predice que el 50% de los trabajadores mundiales necesitarรกn reeducaciรณn para 2025. Habilidades como la alfabetizaciรณn digital, el pensamiento crรญtico y la creatividad se volverรกn especialmente importantes. Personalmente, recomiendo la automejora a travรฉs de plataformas de educaciรณn en lรญnea. Por ejemplo, en plataformas como Coursera o edX, puedes tomar clases gratuitas de las mejores universidades del mundo."},
{"speaker": "Carlos", "text": "Gracias por los consejos prรกcticos. Finalmente, ยฟcรณmo ve las perspectivas futuras de este campo?"},
{"speaker": "Miguel", "text": "Los prรณximos 10 aรฑos serรกn un perรญodo del desarrollo tecnolรณgico mรกs rรกpido en la historia humana. Segรบn el anรกlisis del ciclo de Gartner, actualmente estamos solo en la etapa inicial de esta tecnologรญa. Para 2030, se esperan innovaciones en un nivel que es difรญcil de imaginar actualmente. Lo importante es no temer estos cambios, sino considerarlos como oportunidades para crear un futuro mejor."},
{"speaker": "Carlos", "text": "Esas fueron palabras realmente perspicaces. Hoy ha sido un momento muy enriquecedor. Espero que nuestros oyentes tambiรฉn se preparen para el futuro basรกndose en lo que se discutiรณ hoy. ยกMiguel, gracias por tomar tu valioso tiempo!"},
{"speaker": "Miguel", "text": "Gracias a ti. Espero que nuestros oyentes naveguen sabiamente a travรฉs de esta era de cambio. Recuerden que la tecnologรญa es solo una herramienta, y depende de nosotros cรณmo la usemos. Para aquellos que quieran saber mรกs sobre lo que discutimos hoy, pueden encontrar informaciรณn mรกs detallada en mi blog y mi libro recientemente publicado."}
]
}
def _get_default_chinese_conversation(self) -> Dict:
"""๊ธฐ๋ณธ ์ค‘๊ตญ์–ด ๋Œ€ํ™” ํ…œํ”Œ๋ฆฟ"""
return {
"conversation": [
{"speaker": "Wei", "text": "ๅคงๅฎถๅฅฝ๏ผไปŠๅคฉๆˆ‘ไปฌ่ฆ่ฎจ่ฎบไธ€ไธช้žๅธธ้‡่ฆๅ’Œๆœ‰่ถฃ็š„่ฏ้ข˜ใ€‚ๅฐๅ†›๏ผŒไฝ ่ƒฝ้ฆ–ๅ…ˆ่งฃ้‡Šไธ€ไธ‹ไธบไป€ไนˆ่ฟ™ไธช่ฏ้ข˜็Žฐๅœจๅ—ๅˆฐๅฆ‚ๆญคๅคš็š„ๅ…ณๆณจๅ—๏ผŸ"},
{"speaker": "Jun", "text": "ไฝ ๅฅฝ๏ผŒๅฐไผŸใ€‚ๆˆ‘ไปฌๆœ€่ฟ‘ๅœจ่ฟ™ไธช้ข†ๅŸŸ่ง่ฏไบ†้ฉๅ‘ฝๆ€ง็š„ๅ‘ๅฑ•ใ€‚ๆ นๆฎMITๆœ€ๆ–ฐๅ‘ๅธƒ็š„็ ”็ฉถ๏ผŒ่ฟ™้กนๆŠ€ๆœฏ็š„ๆ•ˆ็އๆฏ”ไผ ็ปŸๆ–นๆณ•ๆ้ซ˜ไบ†300%ใ€‚่ฟ™ไธไป…ไป…ๆ˜ฏๆŠ€ๆœฏ่ฟ›ๆญฅ๏ผŒ่€Œๆ˜ฏไธ€ไธชๅฏ่ƒฝ็›ดๆŽฅๅฝฑๅ“ๆˆ‘ไปฌๆ—ฅๅธธ็”Ÿๆดป็š„ๅ˜ๅŒ–ใ€‚ๅฎž้™…ไธŠ๏ผŒๅƒ่ฐทๆญŒๅ’Œๅพฎ่ฝฏ่ฟ™ๆ ท็š„็ง‘ๆŠ€ๅทจๅคดๅทฒ็ปๅœจ่ฟ™ไธช้ข†ๅŸŸๆŠ•่ต„ไบ†ๆ•ฐๅไบฟ็พŽๅ…ƒใ€‚"},
{"speaker": "Wei", "text": "300%็š„ๆๅ‡็กฎๅฎžไปคไบบๆƒŠๅน๏ผ้‚ฃไนˆ่ฟ™็งๆŠ€ๆœฏๅ‘ๅฑ•่ƒฝไธบๆ™ฎ้€šๅคงไผ—ๅธฆๆฅไป€ไนˆๅ…ทไฝ“็š„ๅฅฝๅค„ๅ‘ข๏ผŸ"},
{"speaker": "Jun", "text": "ๆœ€็›ดๆŽฅ็š„ๅฅฝๅค„ๆ˜ฏๆˆๆœฌ้™ไฝŽๅ’Œๅฏ่ฎฟ้—ฎๆ€งๆ้ซ˜ใ€‚ไพ‹ๅฆ‚๏ผŒไปฅๅ‰ๅชๆœ‰ไธ“ๅฎถๆ‰่ƒฝไฝฟ็”จ็š„้ซ˜็บงๅŠŸ่ƒฝ็Žฐๅœจๅฏไปฅๅœจๆ™บ่ƒฝๆ‰‹ๆœบๅบ”็”จไธญๅฎž็Žฐใ€‚ๆ นๆฎ้บฆ่‚ฏ้”ก็š„ๆŠฅๅ‘Š๏ผŒ้ข„่ฎกๅˆฐ2025ๅนด๏ผŒ่ฟ™้กนๆŠ€ๆœฏๅฐ†ๅœจๅ…จ็ƒๅˆ›้€ ็บฆ2ไธ‡ไบฟ็พŽๅ…ƒ็š„็ปๆตŽไปทๅ€ผใ€‚็‰นๅˆซๆ˜ฏๅœจๅŒป็–—ใ€ๆ•™่‚ฒๅ’Œ้‡‘่ž้ข†ๅŸŸ๏ผŒ้ข„่ฎกไผšๅ‡บ็Žฐๅˆ›ๆ–ฐๆ€งๅ˜ๅŒ–ใ€‚"},
{"speaker": "Wei", "text": "2ไธ‡ไบฟ็พŽๅ…ƒๆ˜ฏไธ€ไธชๅทจๅคง็š„่ง„ๆจก๏ผๅœจๅŒป็–—้ข†ๅŸŸ้ข„่ฎกไผšๆœ‰ไป€ไนˆๅ˜ๅŒ–๏ผŸ"},
{"speaker": "Jun", "text": "ๅŒป็–—้ข†ๅŸŸ็š„ๅ˜ๅŒ–ๅฐ†ๆ˜ฏ็œŸๆญฃ้ฉๅ‘ฝๆ€ง็š„ใ€‚ๆ–ฏๅฆ็ฆๅคงๅญฆๅŒป้™ขๅทฒ็ปๅˆฉ็”จ่ฟ™้กนๆŠ€ๆœฏๅฐ†็™Œ็—‡่ฏŠๆ–ญ็š„ๅ‡†็กฎ็އๆ้ซ˜ๅˆฐ95%ใ€‚AIๅฏไปฅๆฃ€ๆต‹ๅˆฐๅณไฝฟๆ˜ฏ็ป้ชŒไธฐๅฏŒ็š„ๅŒป็”ŸไนŸๅฏ่ƒฝ้”™่ฟ‡็š„ๅพฎๅฐ็—…ๅ˜ใ€‚ๆ›ดไปคไบบๆƒŠๅน็š„ๆ˜ฏ๏ผŒ่ฟ™ๆ ท็š„่ฏŠๆ–ญๅช้œ€่ฆๅ‡ ๅˆ†้’Ÿๅฐฑ่ƒฝๅฎŒๆˆใ€‚ๆ นๆฎไธ–็•Œๅซ็”Ÿ็ป„็ป‡็š„ไผฐ่ฎก๏ผŒๅฆ‚ๆžœ่ฟ™้กนๆŠ€ๆœฏๅœจๅ…จ็ƒๆ™ฎๅŠ๏ผŒๆฏๅนดๅฏไปฅๆ‹ฏๆ•‘ๆ•ฐ็™พไธ‡ไบบ็š„็”Ÿๅ‘ฝใ€‚"},
{"speaker": "Wei", "text": "่ฟ™็œŸ็š„ไปคไบบๅฐ่ฑกๆทฑๅˆปใ€‚ไฝ†ๆ˜ฏๅฏนไบŽ่ฟ™็งๅฟซ้€Ÿ็š„ๆŠ€ๆœฏๅ‘ๅฑ•ไนŸไธ€ๅฎšๅญ˜ๅœจๆ‹…ๅฟงๅง๏ผŸ"},
{"speaker": "Jun", "text": "็กฎๅฎžๅฆ‚ๆญคใ€‚ไธป่ฆๆœ‰ไธ‰ไธชๆ‹…ๅฟงใ€‚้ฆ–ๅ…ˆๆ˜ฏๅฐฑไธšๆ›ฟไปฃ้—ฎ้ข˜โ€”โ€”ๆ นๆฎ็‰›ๆดฅๅคงๅญฆ็š„็ ”็ฉถ๏ผŒๆœชๆฅ20ๅนดๅ†…ๅฝ“ๅ‰47%็š„ๅทฅไฝœๅฏ่ƒฝ้ขไธด่‡ชๅŠจๅŒ–้ฃŽ้™ฉใ€‚ๅ…ถๆฌกๆ˜ฏ้š็งๅ’Œๅฎ‰ๅ…จ้—ฎ้ข˜ใ€‚็ฌฌไธ‰ๆ˜ฏๆŠ€ๆœฏๅทฎ่ทๅฏผ่‡ด็š„ไธๅนณ็ญ‰ๅŠ ๅ‰งใ€‚ไฝ†ๆ˜ฏไปŽๅކๅฒไธŠ็œ‹๏ผŒๆ–ฐๆŠ€ๆœฏๆ€ปๆ˜ฏๅœจๅˆ›้€ ๆ–ฐๆœบ้‡็š„ๅŒๆ—ถ๏ผŒๆˆ‘็›ธไฟก้€š่ฟ‡้€‚ๅฝ“็š„ๆ”ฟ็ญ–ๅ’Œๆ•™่‚ฒๅฏไปฅ่งฃๅ†ณ่ฟ™ไบ›้—ฎ้ข˜ใ€‚"},
{"speaker": "Wei", "text": "ๅนณ่กก็š„่ง‚็‚นๅพˆ้‡่ฆใ€‚้‚ฃไนˆๆˆ‘ไปฌๅบ”่ฏฅๅฆ‚ไฝ•ไธบ่ฟ™ไบ›ๅ˜ๅŒ–ๅšๅ‡†ๅค‡ๅ‘ข๏ผŸ"},
{"speaker": "Jun", "text": "ๆœ€้‡่ฆ็š„ๆ˜ฏๆŒ็ปญๅญฆไน ๅ’Œ้€‚ๅบ”่ƒฝๅŠ›ใ€‚ไธ–็•Œ็ปๆตŽ่ฎบๅ›้ข„ๆต‹๏ผŒๅˆฐ2025ๅนด๏ผŒๅ…จ็ƒ50%็š„ๅทฅไบบๅฐ†้œ€่ฆ้‡ๆ–ฐๅŸน่ฎญใ€‚ๆ•ฐๅญ—็ด ๅ…ปใ€ๆ‰นๅˆคๆ€งๆ€็ปดๅ’Œๅˆ›้€ ๅŠ›็ญ‰ๆŠ€่ƒฝๅฐ†ๅ˜ๅพ—็‰นๅˆซ้‡่ฆใ€‚ๆˆ‘ไธชไบบๆŽจ่้€š่ฟ‡ๅœจ็บฟๆ•™่‚ฒๅนณๅฐ่ฟ›่กŒ่‡ชๆˆ‘ๆๅ‡ใ€‚ไพ‹ๅฆ‚๏ผŒๅœจCourseraๆˆ–edX็ญ‰ๅนณๅฐไธŠ๏ผŒไฝ ๅฏไปฅๅ…่ดนๅญฆไน ไธ–็•Œ้กถๅฐ–ๅคงๅญฆ็š„่ฏพ็จ‹ใ€‚"},
{"speaker": "Wei", "text": "ๆ„Ÿ่ฐข่ฟ™ไบ›ๅฎž็”จ็š„ๅปบ่ฎฎใ€‚ๆœ€ๅŽ๏ผŒไฝ ๅฆ‚ไฝ•็œ‹ๅพ…่ฟ™ไธช้ข†ๅŸŸ็š„ๆœชๆฅๅ‰ๆ™ฏ๏ผŸ"},
{"speaker": "Jun", "text": "ๆœชๆฅ10ๅนดๅฐ†ๆ˜ฏไบบ็ฑปๅކๅฒไธŠๆŠ€ๆœฏๅ‘ๅฑ•ๆœ€ๅฟซ็š„ๆ—ถๆœŸใ€‚ๆ นๆฎGartner็š„็‚’ไฝœๅ‘จๆœŸๅˆ†ๆž๏ผŒๆˆ‘ไปฌ็›ฎๅ‰ๅชๆ˜ฏๅค„ไบŽ่ฟ™้กนๆŠ€ๆœฏ็š„ๅˆๆœŸ้˜ถๆฎตใ€‚ๅˆฐ2030ๅนด๏ผŒ้ข„่ฎกไผšๅ‡บ็Žฐ็›ฎๅ‰้šพไปฅๆƒณ่ฑกๆฐดๅนณ็š„ๅˆ›ๆ–ฐใ€‚้‡่ฆ็š„ๆ˜ฏไธ่ฆๅฎณๆ€•่ฟ™ไบ›ๅ˜ๅŒ–๏ผŒ่€Œๆ˜ฏๅฐ†ๅฎƒไปฌ่ง†ไธบๅˆ›้€ ๆ›ด็พŽๅฅฝๆœชๆฅ็š„ๆœบไผšใ€‚"},
{"speaker": "Wei", "text": "่ฟ™ไบ›่ฏ็œŸ็š„ๅพˆๆœ‰่งๅœฐใ€‚ไปŠๅคฉๆ˜ฏไธ€ไธช้žๅธธๆœ‰็›Š็š„ๆ—ถๅ…‰ใ€‚ๅธŒๆœ›ๆˆ‘ไปฌ็š„ๅฌไผ—ไนŸ่ƒฝๅŸบไบŽไปŠๅคฉ่ฎจ่ฎบ็š„ๅ†…ๅฎนไธบๆœชๆฅๅšๅ‡†ๅค‡ใ€‚ๅฐๅ†›๏ผŒๆ„Ÿ่ฐขไฝ ๆŠฝๅ‡บๅฎ่ดต็š„ๆ—ถ้—ด๏ผ"},
{"speaker": "Jun", "text": "่ฐข่ฐขไฝ ใ€‚ๅธŒๆœ›ๆˆ‘ไปฌ็š„ๅฌไผ—่ƒฝๅคŸๆ˜Žๆ™บๅœฐๅบฆ่ฟ‡่ฟ™ไธชๅ˜ๅŒ–็š„ๆ—ถไปฃใ€‚่ฏท่ฎฐไฝ๏ผŒๆŠ€ๆœฏๅชๆ˜ฏๅทฅๅ…ท๏ผŒๅฆ‚ไฝ•ไฝฟ็”จๅฎƒๅ–ๅ†ณไบŽๆˆ‘ไปฌ่‡ชๅทฑใ€‚ๅฏนไบŽๆƒณ่ฆไบ†่งฃๆ›ดๅคšๆˆ‘ไปฌไปŠๅคฉ่ฎจ่ฎบๅ†…ๅฎน็š„ไบบ๏ผŒๅฏไปฅๅœจๆˆ‘็š„ๅšๅฎขๅ’Œๆœ€่ฟ‘ๅ‡บ็‰ˆ็š„ไนฆไธญๆ‰พๅˆฐๆ›ด่ฏฆ็ป†็š„ไฟกๆฏใ€‚"}
]
}
def _get_default_russian_conversation(self) -> Dict:
"""๊ธฐ๋ณธ ๋Ÿฌ์‹œ์•„์–ด ๋Œ€ํ™” ํ…œํ”Œ๋ฆฟ"""
return {
"conversation": [
{"speaker": "Alexei", "text": "ะŸั€ะธะฒะตั‚ ะฒัะตะผ! ะกะตะณะพะดะฝั ะผั‹ ัะพะฑะธั€ะฐะตะผัั ะพะฑััƒะดะธั‚ัŒ ะดะตะนัั‚ะฒะธั‚ะตะปัŒะฝะพ ะฒะฐะถะฝัƒัŽ ะธ ัƒะฒะปะตะบะฐั‚ะตะปัŒะฝัƒัŽ ั‚ะตะผัƒ. ะ”ะผะธั‚ั€ะธะน, ะฝะต ะผะพะณะปะธ ะฑั‹ ะฒั‹ ัะฝะฐั‡ะฐะปะฐ ะพะฑัŠััะฝะธั‚ัŒ, ะฟะพั‡ะตะผัƒ ัั‚ะฐ ั‚ะตะผะฐ ัะตะนั‡ะฐั ะฟั€ะธะฒะปะตะบะฐะตั‚ ั‚ะฐะบ ะผะฝะพะณะพ ะฒะฝะธะผะฐะฝะธั?"},
{"speaker": "Dmitri", "text": "ะŸั€ะธะฒะตั‚, ะะปะตะบัะตะน. ะœั‹ ะฝะตะดะฐะฒะฝะพ ัั‚ะฐะปะธ ัะฒะธะดะตั‚ะตะปัะผะธ ั€ะตะฒะพะปัŽั†ะธะพะฝะฝั‹ั… ั€ะฐะทั€ะฐะฑะพั‚ะพะบ ะฒ ัั‚ะพะน ะพะฑะปะฐัั‚ะธ. ะกะพะณะปะฐัะฝะพ ะฝะตะดะฐะฒะฝะตะน ะฟัƒะฑะปะธะบะฐั†ะธะธ MIT, ัั„ั„ะตะบั‚ะธะฒะฝะพัั‚ัŒ ัั‚ะพะน ั‚ะตั…ะฝะพะปะพะณะธะธ ัƒะปัƒั‡ัˆะธะปะฐััŒ ะฝะฐ 300% ะฟะพ ัั€ะฐะฒะฝะตะฝะธัŽ ั ั‚ั€ะฐะดะธั†ะธะพะฝะฝั‹ะผะธ ะผะตั‚ะพะดะฐะผะธ. ะญั‚ะพ ะฝะต ะฟั€ะพัั‚ะพ ั‚ะตั…ะฝะธั‡ะตัะบะธะน ะฟั€ะพะณั€ะตัั, ะฐ ะธะทะผะตะฝะตะฝะธะต, ะบะพั‚ะพั€ะพะต ะผะพะถะตั‚ ะพะบะฐะทะฐั‚ัŒ ะฟั€ัะผะพะต ะฒะปะธัะฝะธะต ะฝะฐ ะฝะฐัˆัƒ ะฟะพะฒัะตะดะฝะตะฒะฝัƒัŽ ะถะธะทะฝัŒ. ะคะฐะบั‚ะธั‡ะตัะบะธ, ั‚ะตั…ะฝะพะปะพะณะธั‡ะตัะบะธะต ะณะธะณะฐะฝั‚ั‹, ั‚ะฐะบะธะต ะบะฐะบ Google ะธ Microsoft, ัƒะถะต ะธะฝะฒะตัั‚ะธั€ะพะฒะฐะปะธ ะผะธะปะปะธะฐั€ะดั‹ ะดะพะปะปะฐั€ะพะฒ ะฒ ัั‚ะพั‚ ัะตะบั‚ะพั€."},
{"speaker": "Alexei", "text": "ะฃะปัƒั‡ัˆะตะฝะธะต ะฝะฐ 300% ะดะตะนัั‚ะฒะธั‚ะตะปัŒะฝะพ ะทะฐะผะตั‡ะฐั‚ะตะปัŒะฝะพ! ะ˜ั‚ะฐะบ, ะบะฐะบะธะต ะบะพะฝะบั€ะตั‚ะฝั‹ะต ะฟั€ะตะธะผัƒั‰ะตัั‚ะฒะฐ ัั‚ะพ ั‚ะตั…ะฝะพะปะพะณะธั‡ะตัะบะพะต ั€ะฐะทะฒะธั‚ะธะต ะผะพะถะตั‚ ะฟั€ะธะฝะตัั‚ะธ ัˆะธั€ะพะบะพะน ะฟัƒะฑะปะธะบะต?"},
{"speaker": "Dmitri", "text": "ะะฐะธะฑะพะปะตะต ะฟั€ัะผั‹ะต ะฟั€ะตะธะผัƒั‰ะตัั‚ะฒะฐ - ัั‚ะพ ัะฝะธะถะตะฝะธะต ะทะฐั‚ั€ะฐั‚ ะธ ัƒะปัƒั‡ัˆะตะฝะธะต ะดะพัั‚ัƒะฟะฝะพัั‚ะธ. ะะฐะฟั€ะธะผะตั€, ั€ะฐััˆะธั€ะตะฝะฝั‹ะต ั„ัƒะฝะบั†ะธะธ, ะบะพั‚ะพั€ั‹ะต ั€ะฐะฝะตะต ะฑั‹ะปะธ ะดะพัั‚ัƒะฟะฝั‹ ั‚ะพะปัŒะบะพ ัะบัะฟะตั€ั‚ะฐะผ, ั‚ะตะฟะตั€ัŒ ะผะพะณัƒั‚ ะฑั‹ั‚ัŒ ั€ะตะฐะปะธะทะพะฒะฐะฝั‹ ะฒ ะฟั€ะธะปะพะถะตะฝะธัั… ะดะปั ัะผะฐั€ั‚ั„ะพะฝะพะฒ. ะกะพะณะปะฐัะฝะพ ะพั‚ั‡ะตั‚ัƒ McKinsey, ะพะถะธะดะฐะตั‚ัั, ั‡ั‚ะพ ัั‚ะฐ ั‚ะตั…ะฝะพะปะพะณะธั ัะพะทะดะฐัั‚ ะฟั€ะธะผะตั€ะฝะพ 2 ั‚ั€ะธะปะปะธะพะฝะฐ ะดะพะปะปะฐั€ะพะฒ ัะบะพะฝะพะผะธั‡ะตัะบะพะน ัั‚ะพะธะผะพัั‚ะธ ะฒ ะผะธั€ะต ะบ 2025 ะณะพะดัƒ. ะ˜ะฝะฝะพะฒะฐั†ะธะพะฝะฝั‹ะต ะธะทะผะตะฝะตะฝะธั ะพัะพะฑะตะฝะฝะพ ะพะถะธะดะฐัŽั‚ัั ะฒ ะพะฑะปะฐัั‚ะธ ะทะดั€ะฐะฒะพะพั…ั€ะฐะฝะตะฝะธั, ะพะฑั€ะฐะทะพะฒะฐะฝะธั ะธ ั„ะธะฝะฐะฝัะพะฒ."},
{"speaker": "Alexei", "text": "2 ั‚ั€ะธะปะปะธะพะฝะฐ ะดะพะปะปะฐั€ะพะฒ - ัั‚ะพ ะพะณั€ะพะผะฝั‹ะน ะผะฐััˆั‚ะฐะฑ! ะšะฐะบะธะต ะธะทะผะตะฝะตะฝะธั ะพะถะธะดะฐัŽั‚ัั ะฒ ะผะตะดะธั†ะธะฝัะบะพะน ะพะฑะปะฐัั‚ะธ?"},
{"speaker": "Dmitri", "text": "ะ˜ะทะผะตะฝะตะฝะธั ะฒ ะผะตะดะธั†ะธะฝัะบะพะน ะพะฑะปะฐัั‚ะธ ะฑัƒะดัƒั‚ ะดะตะนัั‚ะฒะธั‚ะตะปัŒะฝะพ ั€ะตะฒะพะปัŽั†ะธะพะฝะฝั‹ะผะธ. ะฃะฝะธะฒะตั€ัะธั‚ะตั‚ัะบะฐั ะฑะพะปัŒะฝะธั†ะฐ ะกั‚ัะฝั„ะพั€ะดะฐ ัƒะถะต ะธัะฟะพะปัŒะทะพะฒะฐะปะฐ ัั‚ัƒ ั‚ะตั…ะฝะพะปะพะณะธัŽ ะดะปั ัƒะปัƒั‡ัˆะตะฝะธั ั‚ะพั‡ะฝะพัั‚ะธ ะดะธะฐะณะฝะพัั‚ะธะบะธ ั€ะฐะบะฐ ะดะพ 95%. ะ˜ะ˜ ะผะพะถะตั‚ ะพะฑะฝะฐั€ัƒะถะธะฒะฐั‚ัŒ ะผะธะบั€ะพัะบะพะฟะธั‡ะตัะบะธะต ะฟะพั€ะฐะถะตะฝะธั, ะบะพั‚ะพั€ั‹ะต ะดะฐะถะต ะพะฟั‹ั‚ะฝั‹ะต ะฒั€ะฐั‡ะธ ะผะพะณะปะธ ะฑั‹ ะฟั€ะพะฟัƒัั‚ะธั‚ัŒ. ะ•ั‰ะต ะฑะพะปะตะต ะฟั€ะธะผะตั‡ะฐั‚ะตะปัŒะฝะพ ั‚ะพ, ั‡ั‚ะพ ั‚ะฐะบะฐั ะดะธะฐะณะฝะพัั‚ะธะบะฐ ะผะพะถะตั‚ ะฑั‹ั‚ัŒ ะฒั‹ะฟะพะปะฝะตะฝะฐ ะฒัะตะณะพ ะทะฐ ะฝะตัะบะพะปัŒะบะพ ะผะธะฝัƒั‚. ะŸะพ ะพั†ะตะฝะบะฐะผ ะ’ะžะ—, ะตัะปะธ ัั‚ะฐ ั‚ะตั…ะฝะพะปะพะณะธั ั€ะฐัะฟั€ะพัั‚ั€ะฐะฝะธั‚ัั ะฟะพ ะฒัะตะผัƒ ะผะธั€ัƒ, ะพะฝะฐ ัะผะพะถะตั‚ ัะฟะฐัะฐั‚ัŒ ะผะธะปะปะธะพะฝั‹ ะถะธะทะฝะตะน ะตะถะตะณะพะดะฝะพ."},
{"speaker": "Alexei", "text": "ะญั‚ะพ ะดะตะนัั‚ะฒะธั‚ะตะปัŒะฝะพ ะฒะฟะตั‡ะฐั‚ะปัะตั‚. ะะพ ั‚ะฐะบะถะต ะดะพะปะถะฝั‹ ะฑั‹ั‚ัŒ ะพะฟะฐัะตะฝะธั ะพั‚ะฝะพัะธั‚ะตะปัŒะฝะพ ั‚ะฐะบะพะณะพ ะฑั‹ัั‚ั€ะพะณะพ ั‚ะตั…ะฝะพะปะพะณะธั‡ะตัะบะพะณะพ ั€ะฐะทะฒะธั‚ะธั?"},
{"speaker": "Dmitri", "text": "ะะฑัะพะปัŽั‚ะฝะพ ะฟั€ะฐะฒะธะปัŒะฝะพ. ะ•ัั‚ัŒ ั‚ั€ะธ ะพัะฝะพะฒะฝั‹ะต ะฟั€ะพะฑะปะตะผั‹. ะ’ะพ-ะฟะตั€ะฒั‹ั…, ะฟั€ะพะฑะปะตะผะฐ ะทะฐะผะตั‰ะตะฝะธั ั€ะฐะฑะพั‡ะธั… ะผะตัั‚ - ัะพะณะปะฐัะฝะพ ะธััะปะตะดะพะฒะฐะฝะธัŽ ะžะบัั„ะพั€ะดัะบะพะณะพ ัƒะฝะธะฒะตั€ัะธั‚ะตั‚ะฐ, 47% ัะพะฒั€ะตะผะตะฝะฝั‹ั… ั€ะฐะฑะพั‡ะธั… ะผะตัั‚ ั€ะธัะบัƒัŽั‚ ะฑั‹ั‚ัŒ ะฐะฒั‚ะพะผะฐั‚ะธะทะธั€ะพะฒะฐะฝะฝั‹ะผะธ ะฒ ั‚ะตั‡ะตะฝะธะต ัะปะตะดัƒัŽั‰ะธั… 20 ะปะตั‚. ะ’ะพ-ะฒั‚ะพั€ั‹ั…, ะฒะพะฟั€ะพัั‹ ะบะพะฝั„ะธะดะตะฝั†ะธะฐะปัŒะฝะพัั‚ะธ ะธ ะฑะตะทะพะฟะฐัะฝะพัั‚ะธ. ะ’-ั‚ั€ะตั‚ัŒะธั…, ัƒััƒะณัƒะฑะปะตะฝะธะต ะฝะตั€ะฐะฒะตะฝัั‚ะฒะฐ ะธะท-ะทะฐ ั‚ะตั…ะฝะพะปะพะณะธั‡ะตัะบะพะณะพ ั€ะฐะทั€ั‹ะฒะฐ. ะžะดะฝะฐะบะพ ะธัั‚ะพั€ะธั‡ะตัะบะธ ะฝะพะฒั‹ะต ั‚ะตั…ะฝะพะปะพะณะธะธ ะฒัะตะณะดะฐ ัะพะทะดะฐะฒะฐะปะธ ะธ ะฝะพะฒั‹ะต ะฒะพะทะผะพะถะฝะพัั‚ะธ, ะฟะพัั‚ะพะผัƒ ั ัั‡ะธั‚ะฐัŽ, ั‡ั‚ะพ ัั‚ะธ ะฟั€ะพะฑะปะตะผั‹ ะผะพะถะฝะพ ั€ะตัˆะธั‚ัŒ ั ะฟะพะผะพั‰ัŒัŽ ัะพะพั‚ะฒะตั‚ัั‚ะฒัƒัŽั‰ะธั… ะฟะพะปะธั‚ะธะบ ะธ ะพะฑั€ะฐะทะพะฒะฐะฝะธั."},
{"speaker": "Alexei", "text": "ะกะฑะฐะปะฐะฝัะธั€ะพะฒะฐะฝะฝะฐั ะฟะตั€ัะฟะตะบั‚ะธะฒะฐ ะฒะฐะถะฝะฐ. ะ˜ั‚ะฐะบ, ะบะฐะบ ะฝะฐะผ ัะปะตะดัƒะตั‚ ะณะพั‚ะพะฒะธั‚ัŒัั ะบ ัั‚ะธะผ ะธะทะผะตะฝะตะฝะธัะผ?"},
{"speaker": "Dmitri", "text": "ะะฐะธะฑะพะปะตะต ะฒะฐะถะฝั‹ะผะธ ัะฒะปััŽั‚ัั ะฝะตะฟั€ะตั€ั‹ะฒะฝะพะต ะพะฑัƒั‡ะตะฝะธะต ะธ ะฐะดะฐะฟั‚ะธะฒะฝะพัั‚ัŒ. ะ’ัะตะผะธั€ะฝั‹ะน ัะบะพะฝะพะผะธั‡ะตัะบะธะน ั„ะพั€ัƒะผ ะฟั€ะพะณะฝะพะทะธั€ัƒะตั‚, ั‡ั‚ะพ ะบ 2025 ะณะพะดัƒ 50% ะผะธั€ะพะฒั‹ั… ั€ะฐะฑะพั‚ะฝะธะบะพะฒ ะฑัƒะดัƒั‚ ะฝัƒะถะดะฐั‚ัŒัั ะฒ ะฟะตั€ะตะพะฑัƒั‡ะตะฝะธะธ. ะžัะพะฑะตะฝะฝะพ ะฒะฐะถะฝั‹ะผะธ ัั‚ะฐะฝัƒั‚ ะฝะฐะฒั‹ะบะธ ั†ะธั„ั€ะพะฒะพะน ะณั€ะฐะผะพั‚ะฝะพัั‚ะธ, ะบั€ะธั‚ะธั‡ะตัะบะพะณะพ ะผั‹ัˆะปะตะฝะธั ะธ ะบั€ะตะฐั‚ะธะฒะฝะพัั‚ะธ. ะ›ะธั‡ะฝะพ ั ั€ะตะบะพะผะตะฝะดัƒัŽ ัะฐะผะพัะพะฒะตั€ัˆะตะฝัั‚ะฒะพะฒะฐะฝะธะต ั‡ะตั€ะตะท ะพะฝะปะฐะนะฝ-ะพะฑั€ะฐะทะพะฒะฐั‚ะตะปัŒะฝั‹ะต ะฟะปะฐั‚ั„ะพั€ะผั‹. ะะฐะฟั€ะธะผะตั€, ะฝะฐ ะฟะปะฐั‚ั„ะพั€ะผะฐั… ะฒั€ะพะดะต Coursera ะธะปะธ edX ะฒั‹ ะผะพะถะตั‚ะต ะฑะตัะฟะปะฐั‚ะฝะพ ะฟะพัะตั‰ะฐั‚ัŒ ะปะตะบั†ะธะธ ะปัƒั‡ัˆะธั… ัƒะฝะธะฒะตั€ัะธั‚ะตั‚ะพะฒ ะผะธั€ะฐ."},
{"speaker": "Alexei", "text": "ะกะฟะฐัะธะฑะพ ะทะฐ ะฟั€ะฐะบั‚ะธั‡ะตัะบะธะต ัะพะฒะตั‚ั‹. ะะฐะบะพะฝะตั†, ะบะฐะบ ะฒั‹ ะฒะธะดะธั‚ะต ะฑัƒะดัƒั‰ะธะต ะฟะตั€ัะฟะตะบั‚ะธะฒั‹ ัั‚ะพะน ะพะฑะปะฐัั‚ะธ?"},
{"speaker": "Dmitri", "text": "ะกะปะตะดัƒัŽั‰ะธะต 10 ะปะตั‚ ะฑัƒะดัƒั‚ ะฟะตั€ะธะพะดะพะผ ัะฐะผะพะณะพ ะฑั‹ัั‚ั€ะพะณะพ ั‚ะตั…ะฝะพะปะพะณะธั‡ะตัะบะพะณะพ ั€ะฐะทะฒะธั‚ะธั ะฒ ะธัั‚ะพั€ะธะธ ั‡ะตะปะพะฒะตั‡ะตัั‚ะฒะฐ. ะกะพะณะปะฐัะฝะพ ะฐะฝะฐะปะธะทัƒ ั†ะธะบะปะฐ Gartner, ะฒ ะฝะฐัั‚ะพัั‰ะตะต ะฒั€ะตะผั ะผั‹ ะฝะฐั…ะพะดะธะผัั ั‚ะพะปัŒะบะพ ะฝะฐ ะฝะฐั‡ะฐะปัŒะฝะพะน ัั‚ะฐะดะธะธ ัั‚ะพะน ั‚ะตั…ะฝะพะปะพะณะธะธ. ะš 2030 ะณะพะดัƒ ะพะถะธะดะฐัŽั‚ัั ะธะฝะฝะพะฒะฐั†ะธะธ ะฝะฐ ัƒั€ะพะฒะฝะต, ะบะพั‚ะพั€ั‹ะน ะฒ ะฝะฐัั‚ะพัั‰ะตะต ะฒั€ะตะผั ั‚ั€ัƒะดะฝะพ ะฟั€ะตะดัั‚ะฐะฒะธั‚ัŒ. ะ’ะฐะถะฝะพ ะฝะต ะฑะพัั‚ัŒัั ัั‚ะธั… ะธะทะผะตะฝะตะฝะธะน, ะฐ ั€ะฐััะผะฐั‚ั€ะธะฒะฐั‚ัŒ ะธั… ะบะฐะบ ะฒะพะทะผะพะถะฝะพัั‚ะธ ะดะปั ัะพะทะดะฐะฝะธั ะปัƒั‡ัˆะตะณะพ ะฑัƒะดัƒั‰ะตะณะพ."},
{"speaker": "Alexei", "text": "ะญั‚ะพ ะฑั‹ะปะธ ะดะตะนัั‚ะฒะธั‚ะตะปัŒะฝะพ ะฟั€ะพะฝะธั†ะฐั‚ะตะปัŒะฝั‹ะต ัะปะพะฒะฐ. ะกะตะณะพะดะฝั ะฑั‹ะปะพ ะพั‡ะตะฝัŒ ะพะฑะพะณะฐั‰ะฐัŽั‰ะตะต ะฒั€ะตะผั. ะะฐะดะตัŽััŒ, ะฝะฐัˆะธ ัะปัƒัˆะฐั‚ะตะปะธ ั‚ะพะถะต ะฟะพะดะณะพั‚ะพะฒัั‚ัั ะบ ะฑัƒะดัƒั‰ะตะผัƒ, ะพัะฝะพะฒั‹ะฒะฐัััŒ ะฝะฐ ั‚ะพะผ, ั‡ั‚ะพ ะพะฑััƒะถะดะฐะปะพััŒ ัะตะณะพะดะฝั. ะ”ะผะธั‚ั€ะธะน, ัะฟะฐัะธะฑะพ, ั‡ั‚ะพ ัƒะดะตะปะธะปะธ ัะฒะพะต ะดั€ะฐะณะพั†ะตะฝะฝะพะต ะฒั€ะตะผั!"},
{"speaker": "Dmitri", "text": "ะกะฟะฐัะธะฑะพ ะฒะฐะผ. ะะฐะดะตัŽััŒ, ะฝะฐัˆะธ ัะปัƒัˆะฐั‚ะตะปะธ ะผัƒะดั€ะพ ะฟั€ะพะนะดัƒั‚ ั‡ะตั€ะตะท ัั‚ัƒ ัะฟะพั…ัƒ ะฟะตั€ะตะผะตะฝ. ะŸะพะผะฝะธั‚ะต, ั‡ั‚ะพ ั‚ะตั…ะฝะพะปะพะณะธั - ัั‚ะพ ะฒัะตะณะพ ะปะธัˆัŒ ะธะฝัั‚ั€ัƒะผะตะฝั‚, ะธ ะพั‚ ะฝะฐั ะทะฐะฒะธัะธั‚, ะบะฐะบ ะผั‹ ะตะณะพ ะธัะฟะพะปัŒะทัƒะตะผ. ะ”ะปั ั‚ะตั…, ะบั‚ะพ ั…ะพั‡ะตั‚ ัƒะทะฝะฐั‚ัŒ ะฑะพะปัŒัˆะต ะพ ั‚ะพะผ, ั‡ั‚ะพ ะผั‹ ะพะฑััƒะถะดะฐะปะธ ัะตะณะพะดะฝั, ะฒั‹ ะผะพะถะตั‚ะต ะฝะฐะนั‚ะธ ะฑะพะปะตะต ะฟะพะดั€ะพะฑะฝัƒัŽ ะธะฝั„ะพั€ะผะฐั†ะธัŽ ะฒ ะผะพะตะผ ะฑะปะพะณะต ะธ ะฝะตะดะฐะฒะฝะพ ะพะฟัƒะฑะปะธะบะพะฒะฐะฝะฝะพะน ะบะฝะธะณะต."}
]
}
def _get_default_korean_conversation(self) -> Dict:
"""๊ธฐ๋ณธ ํ•œ๊ตญ์–ด ๋Œ€ํ™” ํ…œํ”Œ๋ฆฟ"""
return {
"conversation": [
{"speaker": "์ค€์ˆ˜", "text": "์•ˆ๋…•ํ•˜์„ธ์š”, ์—ฌ๋Ÿฌ๋ถ„! ์˜ค๋Š˜์€ ์ •๋ง ์ค‘์š”ํ•˜๊ณ  ํฅ๋ฏธ๋กœ์šด ์ฃผ์ œ๋ฅผ ๋‹ค๋ค„๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋ฏผํ˜ธ ๋ฐ•์‚ฌ๋‹˜, ๋จผ์ € ์ด ์ฃผ์ œ๊ฐ€ ์™œ ์ง€๊ธˆ ์ด๋ ‡๊ฒŒ ์ฃผ๋ชฉ๋ฐ›๊ณ  ์žˆ๋Š”์ง€ ์„ค๋ช…ํ•ด์ฃผ์‹œ๊ฒ ์–ด์š”?"},
{"speaker": "๋ฏผํ˜ธ", "text": "๋„ค, ์•ˆ๋…•ํ•˜์„ธ์š”. ์ตœ๊ทผ ์ด ๋ถ„์•ผ์—์„œ ํš๊ธฐ์ ์ธ ๋ฐœ์ „์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ์ž‘๋…„ MIT ์—ฐ๊ตฌํŒ€์˜ ๋ฐœํ‘œ์— ๋”ฐ๋ฅด๋ฉด, ์ด ๊ธฐ์ˆ ์˜ ํšจ์œจ์„ฑ์ด ๊ธฐ์กด ๋Œ€๋น„ 300% ํ–ฅ์ƒ๋˜์—ˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋‹จ์ˆœํ•œ ๊ธฐ์ˆ ์  ์ง„๋ณด๋ฅผ ๋„˜์–ด์„œ ์šฐ๋ฆฌ ์ผ์ƒ์ƒํ™œ์— ์ง์ ‘์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ๋Š” ๋ณ€ํ™”์ธ๋ฐ์š”. ์‹ค์ œ๋กœ ๊ตฌ๊ธ€๊ณผ ๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ ๊ฐ™์€ ๋น…ํ…Œํฌ ๊ธฐ์—…๋“ค์ด ์ด๋ฏธ ์ˆ˜์‹ญ์–ต ๋‹ฌ๋Ÿฌ๋ฅผ ํˆฌ์žํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค."},
{"speaker": "์ค€์ˆ˜", "text": "์™€, 300% ํ–ฅ์ƒ์ด๋ผ๋‹ˆ ์ •๋ง ๋†€๋ผ์šด๋ฐ์š”. ๊ทธ๋ ‡๋‹ค๋ฉด ์ด๋Ÿฐ ๊ธฐ์ˆ  ๋ฐœ์ „์ด ์ผ๋ฐ˜์ธ๋“ค์—๊ฒŒ๋Š” ๊ตฌ์ฒด์ ์œผ๋กœ ์–ด๋–ค ํ˜œํƒ์„ ๊ฐ€์ ธ๋‹ค์ค„ ์ˆ˜ ์žˆ์„๊นŒ์š”?"},
{"speaker": "๋ฏผํ˜ธ", "text": "๊ฐ€์žฅ ์ง์ ‘์ ์ธ ํ˜œํƒ์€ ๋น„์šฉ ์ ˆ๊ฐ๊ณผ ์ ‘๊ทผ์„ฑ ํ–ฅ์ƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ด์ „์—๋Š” ์ „๋ฌธ๊ฐ€๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์ด ์ด์ œ๋Š” ์Šค๋งˆํŠธํฐ ์•ฑ์œผ๋กœ๋„ ๊ตฌํ˜„ ๊ฐ€๋Šฅํ•ด์กŒ์Šต๋‹ˆ๋‹ค. ๋งฅํ‚จ์ง€ ๋ณด๊ณ ์„œ์— ๋”ฐ๋ฅด๋ฉด, 2025๋…„๊นŒ์ง€ ์ด ๊ธฐ์ˆ ๋กœ ์ธํ•ด ์ „ ์„ธ๊ณ„์ ์œผ๋กœ ์•ฝ 2์กฐ ๋‹ฌ๋Ÿฌ์˜ ๊ฒฝ์ œ์  ๊ฐ€์น˜๊ฐ€ ์ฐฝ์ถœ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ์˜๋ฃŒ, ๊ต์œก, ๊ธˆ์œต ๋ถ„์•ผ์—์„œ ํ˜์‹ ์ ์ธ ๋ณ€ํ™”๊ฐ€ ์ผ์–ด๋‚  ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค."},
{"speaker": "์ค€์ˆ˜", "text": "2์กฐ ๋‹ฌ๋Ÿฌ๋ผ๋Š” ์—„์ฒญ๋‚œ ๊ทœ๋ชจ๋„ค์š”. ์˜๋ฃŒ ๋ถ„์•ผ์—์„œ๋Š” ์–ด๋–ค ๋ณ€ํ™”๊ฐ€ ์˜ˆ์ƒ๋˜๋‚˜์š”?"},
{"speaker": "๋ฏผํ˜ธ", "text": "์˜๋ฃŒ ๋ถ„์•ผ์˜ ๋ณ€ํ™”๋Š” ์ •๋ง ํ˜๋ช…์ ์ผ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ์Šคํƒ ํฌ๋“œ ๋Œ€ํ•™๋ณ‘์›์—์„œ๋Š” ์ด ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•ด ์•” ์ง„๋‹จ ์ •ํ™•๋„๋ฅผ 95%๊นŒ์ง€ ๋†’์˜€์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด์—๋Š” ์ˆ™๋ จ๋œ ์˜์‚ฌ๋„ ๋†“์น  ์ˆ˜ ์žˆ๋˜ ๋ฏธ์„ธํ•œ ๋ณ‘๋ณ€๋“ค์„ AI๊ฐ€ ๊ฐ์ง€ํ•ด๋‚ด๋Š” ๊ฒƒ์ด์ฃ . ๋” ๋†€๋ผ์šด ๊ฒƒ์€ ์ด๋Ÿฐ ์ง„๋‹จ์ด ๋‹จ ๋ช‡ ๋ถ„ ๋งŒ์— ์ด๋ค„์ง„๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. WHO ์ถ”์‚ฐ์œผ๋กœ๋Š” ์ด ๊ธฐ์ˆ ์ด ์ „ ์„ธ๊ณ„์ ์œผ๋กœ ๋ณด๊ธ‰๋˜๋ฉด ์—ฐ๊ฐ„ ์ˆ˜๋ฐฑ๋งŒ ๋ช…์˜ ์ƒ๋ช…์„ ๊ตฌํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์œผ๋กœ ์˜ˆ์ธกํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค."},
{"speaker": "์ค€์ˆ˜", "text": "์ •๋ง ์ธ์ƒ์ ์ด๋„ค์š”. ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๊ธ‰๊ฒฉํ•œ ๊ธฐ์ˆ  ๋ฐœ์ „์— ๋Œ€ํ•œ ์šฐ๋ ค์˜ ๋ชฉ์†Œ๋ฆฌ๋„ ์žˆ์„ ๊ฒƒ ๊ฐ™์€๋ฐ์š”?"},
{"speaker": "๋ฏผํ˜ธ", "text": "๋งž์Šต๋‹ˆ๋‹ค. ์ฃผ์š” ์šฐ๋ ค์‚ฌํ•ญ์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ์ฒซ์งธ๋Š” ์ผ์ž๋ฆฌ ๋Œ€์ฒด ๋ฌธ์ œ๋กœ, ์˜ฅ์Šคํฌ๋“œ ๋Œ€ํ•™ ์—ฐ๊ตฌ์— ๋”ฐ๋ฅด๋ฉด ํ–ฅํ›„ 20๋…„ ๋‚ด์— ํ˜„์žฌ ์ง์—…์˜ 47%๊ฐ€ ์ž๋™ํ™”๋  ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘˜์งธ๋Š” ํ”„๋ผ์ด๋ฒ„์‹œ์™€ ๋ณด์•ˆ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์…‹์งธ๋Š” ๊ธฐ์ˆ  ๊ฒฉ์ฐจ๋กœ ์ธํ•œ ๋ถˆํ‰๋“ฑ ์‹ฌํ™”์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์—ญ์‚ฌ์ ์œผ๋กœ ๋ณด๋ฉด ์ƒˆ๋กœ์šด ๊ธฐ์ˆ ์€ ํ•ญ์ƒ ์ƒˆ๋กœ์šด ๊ธฐํšŒ๋„ ํ•จ๊ป˜ ๋งŒ๋“ค์–ด์™”๊ธฐ ๋•Œ๋ฌธ์—, ์ ์ ˆํ•œ ์ •์ฑ…๊ณผ ๊ต์œก์œผ๋กœ ์ด๋Ÿฐ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์œผ๋กœ ๋ด…๋‹ˆ๋‹ค."},
{"speaker": "์ค€์ˆ˜", "text": "๊ท ํ˜•์žกํžŒ ์‹œ๊ฐ์ด ์ค‘์š”ํ•˜๊ฒ ๋„ค์š”. ๊ทธ๋ ‡๋‹ค๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์ด๋Ÿฐ ๋ณ€ํ™”์— ์–ด๋–ป๊ฒŒ ๋Œ€๋น„ํ•ด์•ผ ํ• ๊นŒ์š”?"},
{"speaker": "๋ฏผํ˜ธ", "text": "๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ์€ ์ง€์†์ ์ธ ํ•™์Šต๊ณผ ์ ์‘๋ ฅ์ž…๋‹ˆ๋‹ค. ์„ธ๊ณ„๊ฒฝ์ œํฌ๋Ÿผ์€ 2025๋…„๊นŒ์ง€ ์ „ ์„ธ๊ณ„ ๊ทผ๋กœ์ž์˜ 50%๊ฐ€ ์žฌ๊ต์œก์ด ํ•„์š”ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ธกํ–ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๋””์ง€ํ„ธ ๋ฆฌํ„ฐ๋Ÿฌ์‹œ, ๋น„ํŒ์  ์‚ฌ๊ณ ๋ ฅ, ์ฐฝ์˜์„ฑ ๊ฐ™์€ ๋Šฅ๋ ฅ์ด ์ค‘์š”ํ•ด์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ๋Š” ์˜จ๋ผ์ธ ๊ต์œก ํ”Œ๋žซํผ์„ ํ™œ์šฉํ•œ ์ž๊ธฐ๊ณ„๋ฐœ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Coursera๋‚˜ edX ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ๋Š” ์„ธ๊ณ„ ์ตœ๊ณ  ๋Œ€ํ•™์˜ ๊ฐ•์˜๋ฅผ ๋ฌด๋ฃŒ๋กœ ๋“ค์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."},
{"speaker": "์ค€์ˆ˜", "text": "์‹ค์šฉ์ ์ธ ์กฐ์–ธ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ์ด ๋ถ„์•ผ์˜ ๋ฏธ๋ž˜ ์ „๋ง์€ ์–ด๋–ป๊ฒŒ ๋ณด์‹œ๋‚˜์š”?"},
{"speaker": "๋ฏผํ˜ธ", "text": "ํ–ฅํ›„ 10๋…„์€ ์ธ๋ฅ˜ ์—ญ์‚ฌ์ƒ ๊ฐ€์žฅ ๊ธ‰๊ฒฉํ•œ ๊ธฐ์ˆ  ๋ฐœ์ „์„ ๊ฒฝํ—˜ํ•˜๋Š” ์‹œ๊ธฐ๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ€ํŠธ๋„ˆ์˜ ํ•˜์ดํ”„ ์‚ฌ์ดํด ๋ถ„์„์— ๋”ฐ๋ฅด๋ฉด, ํ˜„์žฌ ์šฐ๋ฆฌ๋Š” ์ด ๊ธฐ์ˆ ์˜ ์ดˆ๊ธฐ ๋‹จ๊ณ„์— ๋ถˆ๊ณผํ•ฉ๋‹ˆ๋‹ค. 2030๋…„๊นŒ์ง€๋Š” ์ง€๊ธˆ์œผ๋กœ์„œ๋Š” ์ƒ์ƒํ•˜๊ธฐ ์–ด๋ ค์šด ์ˆ˜์ค€์˜ ํ˜์‹ ์ด ์ผ์–ด๋‚  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๋‹ค. ์ค‘์š”ํ•œ ๊ฒƒ์€ ์ด๋Ÿฐ ๋ณ€ํ™”๋ฅผ ๋‘๋ ค์›Œํ•˜๊ธฐ๋ณด๋‹ค๋Š” ๊ธฐํšŒ๋กœ ์‚ผ์•„ ๋” ๋‚˜์€ ๋ฏธ๋ž˜๋ฅผ ๋งŒ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค."},
{"speaker": "์ค€์ˆ˜", "text": "์ •๋ง ํ†ต์ฐฐ๋ ฅ ์žˆ๋Š” ๋ง์”€์ด๋„ค์š”. ์˜ค๋Š˜ ๋„ˆ๋ฌด๋‚˜ ์œ ์ตํ•œ ์‹œ๊ฐ„์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ฒญ์ทจ์ž ์—ฌ๋Ÿฌ๋ถ„๋„ ์˜ค๋Š˜ ๋…ผ์˜๋œ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ๋ฏธ๋ž˜๋ฅผ ์ค€๋น„ํ•˜์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๋ฏผํ˜ธ ๋ฐ•์‚ฌ๋‹˜, ๊ท€์ค‘ํ•œ ์‹œ๊ฐ„ ๋‚ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!"},
{"speaker": "๋ฏผํ˜ธ", "text": "๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ฒญ์ทจ์ž ์—ฌ๋Ÿฌ๋ถ„๋“ค์ด ์ด ๋ณ€ํ™”์˜ ์‹œ๋Œ€๋ฅผ ํ˜„๋ช…ํ•˜๊ฒŒ ํ—ค์ณ๋‚˜๊ฐ€์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๊ธฐ์ˆ ์€ ๋„๊ตฌ์ผ ๋ฟ์ด๊ณ , ๊ทธ๊ฒƒ์„ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€๋Š” ์šฐ๋ฆฌ์—๊ฒŒ ๋‹ฌ๋ ค์žˆ๋‹ค๋Š” ์ ์„ ๊ธฐ์–ตํ•ด์ฃผ์„ธ์š”."}
]
}
def _get_default_english_conversation(self) -> Dict:
"""๊ธฐ๋ณธ ์˜์–ด ๋Œ€ํ™” ํ…œํ”Œ๋ฆฟ"""
return {
"conversation": [
{"speaker": "Alex", "text": "Welcome everyone to our podcast! Today we're diving into a topic that's reshaping our world. Dr. Jordan, could you start by explaining why this subject has become so critical right now?"},
{"speaker": "Jordan", "text": "Thanks, Alex. We're witnessing an unprecedented convergence of technological breakthroughs. According to a recent Nature publication, advances in this field have accelerated by 400% in just the past two years. This isn't just incremental progress - it's a fundamental shift in how we approach problem-solving. Major institutions like Harvard and Stanford are completely restructuring their research programs to focus on this area, with combined investments exceeding $5 billion annually."},
{"speaker": "Alex", "text": "400% acceleration is staggering! What does this mean for everyday people who might not be tech-savvy?"},
{"speaker": "Jordan", "text": "The impact will be profound yet accessible. Think about how smartphones revolutionized communication - this will be similar but across every aspect of life. McKinsey's latest report projects that by 2026, these technologies will create $4.4 trillion in annual value globally. For individuals, this translates to personalized healthcare that can predict illnesses years in advance, educational systems that adapt to each student's learning style, and financial tools that democratize wealth-building strategies previously available only to the ultra-wealthy."},
{"speaker": "Alex", "text": "Those applications sound transformative. Can you give us a concrete example of how this is already being implemented?"},
{"speaker": "Jordan", "text": "Absolutely. Let me share a compelling case from Johns Hopkins Hospital. They've deployed an AI system that analyzes patient data in real-time, reducing diagnostic errors by 85% and cutting average diagnosis time from days to hours. In one documented case, the system identified a rare genetic disorder in a child that had been misdiagnosed for three years. The accuracy comes from analyzing patterns across millions of cases - something impossible for even the most experienced doctors to do manually."},
{"speaker": "Alex", "text": "That's truly life-changing technology. But I imagine there are significant challenges and risks we need to consider?"},
{"speaker": "Jordan", "text": "You're absolutely right to raise this. The challenges are as significant as the opportunities. The World Economic Forum identifies three critical risks: algorithmic bias could perpetuate existing inequalities, cybersecurity threats become exponentially more dangerous, and there's the socioeconomic disruption with PwC estimating that 30% of jobs could be automated by 2030. However, history shows us that technological revolutions create new opportunities even as they displace old ones. The key is proactive adaptation and responsible development."},
{"speaker": "Alex", "text": "How should individuals and organizations prepare for these changes?"},
{"speaker": "Jordan", "text": "Preparation requires a multi-faceted approach. For individuals, I recommend focusing on skills that complement rather than compete with AI: critical thinking, emotional intelligence, and creative problem-solving. MIT's recent study shows that professionals who combine domain expertise with AI literacy see salary increases of 40% on average. Organizations need to invest in continuous learning programs - Amazon's $700 million worker retraining initiative is a good model. Most importantly, we need to cultivate an adaptive mindset."},
{"speaker": "Alex", "text": "That's practical advice. What about the ethical considerations? How do we ensure this technology benefits humanity as a whole?"},
{"speaker": "Jordan", "text": "Ethics must be at the forefront of development. The EU's AI Act and similar regulations worldwide are establishing important guardrails. We need transparent AI systems where decisions can be explained and audited. Companies like IBM and Google have established AI ethics boards, but we need industry-wide standards. Additionally, we must address the digital divide - UNESCO reports that 37% of the global population still lacks internet access. Without inclusive development, these technologies could exacerbate global inequality."},
{"speaker": "Alex", "text": "Looking ahead, what's your vision for how this technology will shape the next decade?"},
{"speaker": "Jordan", "text": "The next decade will be transformative beyond our current imagination. By 2035, I expect we'll see autonomous systems managing entire cities, personalized medicine extending human lifespan by 20-30 years, and educational AI that makes world-class education universally accessible. The convergence of AI with quantum computing, biotechnology, and nanotechnology will unlock possibilities we can barely conceive of today. However, the future isn't predetermined - it's shaped by the choices we make now about development priorities and ethical frameworks."},
{"speaker": "Alex", "text": "Dr. Jordan, this has been an incredibly enlightening discussion. Thank you for sharing your expertise and insights with us today."},
{"speaker": "Jordan", "text": "Thank you, Alex. For listeners wanting to dive deeper, I've compiled additional resources on my website. Remember, the future isn't something that happens to us - it's something we create together. I look forward to seeing how each of you contributes to shaping this exciting new era."}
]
}
def extract_conversation_api(self, text: str, language: str = "English") -> Dict:
"""Extract conversation using API"""
if not self.llm_client:
raise RuntimeError("API mode not initialized")
try:
# ๊ฒ€์ƒ‰ ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ
search_context = ""
if BRAVE_KEY and not text.startswith("Keyword-based content:"):
try:
keywords = extract_keywords_for_search(text, language)
if keywords:
search_query = keywords[0] if language == "Korean" else f"{keywords[0]} latest news"
search_context = format_search_results(search_query)
print(f"Search context added for: {search_query}")
except Exception as e:
print(f"Search failed, continuing without context: {e}")
# ์–ธ์–ด๋ณ„ ์‹œ์Šคํ…œ ๋ฉ”์‹œ์ง€
if language == "Korean":
system_message = (
"๋‹น์‹ ์€ ํ•œ๊ตญ์˜ ์ตœ๊ณ  ์ „๋ฌธ ํŒŸ์บ์ŠคํŠธ ์ž‘๊ฐ€์ž…๋‹ˆ๋‹ค. "
"12ํšŒ์˜ ๊นŠ์ด ์žˆ๋Š” ๋Œ€ํ™” ๊ตํ™˜์œผ๋กœ ๊ตฌ์„ฑ๋œ ๊ณ ํ’ˆ์งˆ ๋Œ€๋‹ด์„ ํ•œ๊ตญ์–ด๋กœ ๋งŒ๋“œ์„ธ์š”. "
"๋ฐ˜๋“œ์‹œ ์„œ๋กœ ์กด๋Œ“๋ง์„ ์‚ฌ์šฉํ•˜๊ณ  ๋ชจ๋“  ๋Œ€ํ™”๋Š” ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•˜์„ธ์š”."
)
elif language == "Japanese":
system_message = (
"ใ‚ใชใŸใฏๆ—ฅๆœฌใฎๆœ€้ซ˜ใฎๅฐ‚้–€ใƒใƒƒใƒ‰ใ‚ญใƒฃใ‚นใƒˆไฝœๅฎถใงใ™ใ€‚"
"12ๅ›žใฎๆทฑใ„ๅฏพ่ฉฑไบคๆ›ใงๆง‹ๆˆใ•ใ‚ŒใŸ้ซ˜ๅ“่ณชใชๅฏพ่ซ‡ใ‚’ๆ—ฅๆœฌ่ชžใงไฝœๆˆใ—ใฆใใ ใ•ใ„ใ€‚"
"ๅฟ…ใšใŠไบ’ใ„ใซไธๅฏง่ชžใ‚’ไฝฟ็”จใ—ใ€ใ™ในใฆใฎๅฏพ่ฉฑใฏๆ—ฅๆœฌ่ชžใงไฝœๆˆใ—ใฆใใ ใ•ใ„ใ€‚"
)
elif language == "French":
system_message = (
"Vous รชtes le meilleur scรฉnariste de podcast professionnel de France. "
"Crรฉez des discussions de haute qualitรฉ composรฉes de 12 รฉchanges approfondis en franรงais. "
"Toutes les conversations doivent รชtre รฉcrites en franรงais."
)
elif language == "German":
system_message = (
"Sie sind der beste professionelle Podcast-Drehbuchautor Deutschlands. "
"Erstellen Sie hochwertige Diskussionen aus 12 tiefgreifenden Austauschen auf Deutsch. "
"Alle Gesprรคche mรผssen auf Deutsch geschrieben werden."
)
elif language == "Spanish":
system_message = (
"Eres el mejor guionista de podcast profesional de Espaรฑa. "
"Crea discusiones de alta calidad compuestas por 12 intercambios profundos en espaรฑol. "
"Todas las conversaciones deben estar escritas en espaรฑol."
)
elif language == "Chinese":
system_message = (
"ๆ‚จๆ˜ฏไธญๅ›ฝๆœ€ๅฅฝ็š„ไธ“ไธšๆ’ญๅฎข็ผ–ๅ‰งใ€‚"
"ๅˆ›ๅปบ็”ฑ12ๆฌกๆทฑๅ…ฅไบคๆต็ป„ๆˆ็š„้ซ˜่ดจ้‡่ฎจ่ฎบ๏ผŒไฝฟ็”จไธญๆ–‡ใ€‚"
"ๆ‰€ๆœ‰ๅฏน่ฏ้ƒฝๅฟ…้กป็”จไธญๆ–‡ไนฆๅ†™ใ€‚"
)
elif language == "Russian":
system_message = (
"ะ’ั‹ ะปัƒั‡ัˆะธะน ะฟั€ะพั„ะตััะธะพะฝะฐะปัŒะฝั‹ะน ัั†ะตะฝะฐั€ะธัั‚ ะฟะพะดะบะฐัั‚ะพะฒ ะฒ ะ ะพััะธะธ. "
"ะกะพะทะดะฐะนั‚ะต ะฒั‹ัะพะบะพะบะฐั‡ะตัั‚ะฒะตะฝะฝั‹ะต ะดะธัะบัƒััะธะธ ะธะท 12 ะณะปัƒะฑะพะบะธั… ะพะฑะผะตะฝะพะฒ ะฝะฐ ั€ัƒััะบะพะผ ัะทั‹ะบะต. "
"ะ’ัะต ั€ะฐะทะณะพะฒะพั€ั‹ ะดะพะปะถะฝั‹ ะฑั‹ั‚ัŒ ะฝะฐะฟะธัะฐะฝั‹ ะฝะฐ ั€ัƒััะบะพะผ ัะทั‹ะบะต."
)
else:
system_message = (
f"You are a top professional podcast scriptwriter. "
f"Create high-quality discussions in {language} with exactly 12 exchanges. "
f"Include specific data, research findings, and real cases. "
f"All conversations must be written in {language}."
)
chat_completion = self.llm_client.chat.completions.create(
messages=[
{"role": "system", "content": system_message},
{"role": "user", "content": self._build_prompt(text, language, search_context)}
],
model=self.config.api_model_name,
temperature=0.75,
)
pattern = r"\{(?:[^{}]|(?:\{[^{}]*\}))*\}"
json_match = re.search(pattern, chat_completion.choices[0].message.content)
if not json_match:
raise ValueError("No valid JSON found in response")
return json.loads(json_match.group())
except Exception as e:
raise RuntimeError(f"Failed to extract conversation: {e}")
def parse_conversation_text(self, conversation_text: str) -> Dict:
"""Parse conversation text back to JSON format"""
lines = conversation_text.strip().split('\n')
conversation_data = {"conversation": []}
for line in lines:
if ':' in line:
speaker, text = line.split(':', 1)
conversation_data["conversation"].append({
"speaker": speaker.strip(),
"text": text.strip()
})
return conversation_data
async def text_to_speech_edge(self, conversation_json: Dict, language: str = "English") -> Tuple[str, str]:
"""Convert text to speech using Edge TTS"""
output_dir = Path(self._create_output_directory())
filenames = []
try:
# ์–ธ์–ด๋ณ„ ์Œ์„ฑ ์„ค์ •
voices = EDGE_TTS_VOICES.get(language, EDGE_TTS_VOICES["English"])
for i, turn in enumerate(conversation_json["conversation"]):
filename = output_dir / f"output_{i}.wav"
voice = voices[i % len(voices)]
tmp_path = await self._generate_audio_edge(turn["text"], voice)
os.rename(tmp_path, filename)
filenames.append(str(filename))
# Combine audio files
final_output = os.path.join(output_dir, "combined_output.wav")
self._combine_audio_files(filenames, final_output)
# Generate conversation text
conversation_text = "\n".join(
f"{turn.get('speaker', f'Speaker {i+1}')}: {turn['text']}"
for i, turn in enumerate(conversation_json["conversation"])
)
return final_output, conversation_text
except Exception as e:
raise RuntimeError(f"Failed to convert text to speech: {e}")
async def _generate_audio_edge(self, text: str, voice: str) -> str:
"""Generate audio using Edge TTS"""
if not text.strip():
raise ValueError("Text cannot be empty")
voice_short_name = voice.split(" - ")[0] if " - " in voice else voice
communicate = edge_tts.Communicate(text, voice_short_name)
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file:
tmp_path = tmp_file.name
await communicate.save(tmp_path)
return tmp_path
@spaces.GPU(duration=60)
def text_to_speech_spark(self, conversation_json: Dict, language: str = "English", progress=None) -> Tuple[str, str]:
"""Convert text to speech using Spark TTS CLI"""
if not SPARK_AVAILABLE or not self.spark_model_dir:
raise RuntimeError("Spark TTS not available")
try:
output_dir = self._create_output_directory()
audio_files = []
# Create different voice characteristics for different speakers
if language == "Korean":
voice_configs = [
{"prompt_text": "์•ˆ๋…•ํ•˜์„ธ์š”, ์˜ค๋Š˜ ํŒŸ์บ์ŠคํŠธ ์ง„ํ–‰์„ ๋งก์€ ์ค€์ˆ˜์ž…๋‹ˆ๋‹ค.", "gender": "male"},
{"prompt_text": "์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋Š” ์˜ค๋Š˜ ์ด ์ฃผ์ œ์— ๋Œ€ํ•ด ์„ค๋ช…๋“œ๋ฆด ๋ฏผํ˜ธ์ž…๋‹ˆ๋‹ค.", "gender": "male"}
]
else:
voice_configs = [
{"prompt_text": "Hello everyone, I'm Alex, your host for today's podcast.", "gender": "male"},
{"prompt_text": "Hi, I'm Jordan. I'm excited to share my insights with you.", "gender": "male"}
]
for i, turn in enumerate(conversation_json["conversation"]):
text = turn["text"]
if not text.strip():
continue
voice_config = voice_configs[i % len(voice_configs)]
output_file = os.path.join(output_dir, f"spark_output_{i}.wav")
cmd = [
"python", "-m", "cli.inference",
"--text", text,
"--device", "0" if torch.cuda.is_available() else "cpu",
"--save_dir", output_dir,
"--model_dir", self.spark_model_dir,
"--prompt_text", voice_config["prompt_text"],
"--output_name", f"spark_output_{i}.wav"
]
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=60,
cwd="."
)
if result.returncode == 0:
audio_files.append(output_file)
else:
print(f"Spark TTS error for turn {i}: {result.stderr}")
silence = np.zeros(int(22050 * 1.0))
sf.write(output_file, silence, 22050)
audio_files.append(output_file)
except subprocess.TimeoutExpired:
print(f"Spark TTS timeout for turn {i}")
silence = np.zeros(int(22050 * 1.0))
sf.write(output_file, silence, 22050)
audio_files.append(output_file)
except Exception as e:
print(f"Error running Spark TTS for turn {i}: {e}")
silence = np.zeros(int(22050 * 1.0))
sf.write(output_file, silence, 22050)
audio_files.append(output_file)
# Combine all audio files
if audio_files:
final_output = os.path.join(output_dir, "spark_combined.wav")
self._combine_audio_files(audio_files, final_output)
else:
raise RuntimeError("No audio files generated")
conversation_text = "\n".join(
f"{turn.get('speaker', f'Speaker {i+1}')}: {turn['text']}"
for i, turn in enumerate(conversation_json["conversation"])
)
return final_output, conversation_text
except Exception as e:
raise RuntimeError(f"Failed to convert text to speech with Spark TTS: {e}")
@spaces.GPU(duration=60)
def text_to_speech_melo(self, conversation_json: Dict, progress=None) -> Tuple[str, str]:
"""Convert text to speech using MeloTTS"""
if not MELO_AVAILABLE or not self.melo_models:
raise RuntimeError("MeloTTS not available")
speakers = ["EN-Default", "EN-US"]
combined_audio = AudioSegment.empty()
for i, turn in enumerate(conversation_json["conversation"]):
bio = io.BytesIO()
text = turn["text"]
speaker = speakers[i % 2]
speaker_id = self.melo_models["EN"].hps.data.spk2id[speaker]
self.melo_models["EN"].tts_to_file(
text, speaker_id, bio, speed=1.0,
pbar=progress.tqdm if progress else None,
format="wav"
)
bio.seek(0)
audio_segment = AudioSegment.from_file(bio, format="wav")
combined_audio += audio_segment
final_audio_path = "melo_podcast.mp3"
combined_audio.export(final_audio_path, format="mp3")
conversation_text = "\n".join(
f"{turn.get('speaker', f'Speaker {i+1}')}: {turn['text']}"
for i, turn in enumerate(conversation_json["conversation"])
)
return final_audio_path, conversation_text
def _create_output_directory(self) -> str:
"""Create a unique output directory"""
random_bytes = os.urandom(8)
folder_name = base64.urlsafe_b64encode(random_bytes).decode("utf-8")
os.makedirs(folder_name, exist_ok=True)
return folder_name
def _combine_audio_files(self, filenames: List[str], output_file: str) -> None:
"""Combine multiple audio files into one"""
if not filenames:
raise ValueError("No input files provided")
try:
audio_segments = []
for filename in filenames:
if os.path.exists(filename):
audio_segment = AudioSegment.from_file(filename)
audio_segments.append(audio_segment)
if audio_segments:
combined = sum(audio_segments)
combined.export(output_file, format="wav")
# Clean up temporary files
for filename in filenames:
if os.path.exists(filename):
os.remove(filename)
except Exception as e:
raise RuntimeError(f"Failed to combine audio files: {e}")
# Global converter instance
converter = UnifiedAudioConverter(ConversationConfig())
async def synthesize(article_input, input_type: str = "URL", mode: str = "Local", tts_engine: str = "Edge-TTS", language: str = "English"):
"""Main synthesis function - handles URL, PDF, and Keyword inputs"""
try:
# Extract text based on input type
if input_type == "URL":
if not article_input or not isinstance(article_input, str):
return "Please provide a valid URL.", None
text = converter.fetch_text(article_input)
elif input_type == "PDF":
if not article_input:
return "Please upload a PDF file.", None
text = converter.extract_text_from_pdf(article_input)
else: # Keyword
if not article_input or not isinstance(article_input, str):
return "Please provide a keyword or topic.", None
text = search_and_compile_content(article_input, language)
text = f"Keyword-based content:\n{text}"
# Limit text to max words
words = text.split()
if len(words) > converter.config.max_words:
text = " ".join(words[:converter.config.max_words])
# Extract conversation based on mode
if mode == "Local":
try:
conversation_json = converter.extract_conversation_local(text, language)
except Exception as e:
print(f"Local mode failed: {e}, trying API fallback")
api_key = os.environ.get("TOGETHER_API_KEY")
if api_key:
converter.initialize_api_mode(api_key)
conversation_json = converter.extract_conversation_api(text, language)
else:
raise RuntimeError("Local mode failed and no API key available for fallback")
else: # API mode
api_key = os.environ.get("TOGETHER_API_KEY")
if not api_key:
print("API key not found, falling back to local mode")
conversation_json = converter.extract_conversation_local(text, language)
else:
try:
converter.initialize_api_mode(api_key)
conversation_json = converter.extract_conversation_api(text, language)
except Exception as e:
print(f"API mode failed: {e}, falling back to local mode")
conversation_json = converter.extract_conversation_local(text, language)
# Generate conversation text
conversation_text = "\n".join(
f"{turn.get('speaker', f'Speaker {i+1}')}: {turn['text']}"
for i, turn in enumerate(conversation_json["conversation"])
)
return conversation_text, None
except Exception as e:
return f"Error: {str(e)}", None
async def regenerate_audio(conversation_text: str, tts_engine: str = "Edge-TTS", language: str = "English"):
"""Regenerate audio from edited conversation text"""
if not conversation_text.strip():
return "Please provide conversation text.", None
try:
conversation_json = converter.parse_conversation_text(conversation_text)
if not conversation_json["conversation"]:
return "No valid conversation found in the text.", None
# Edge TTS ์ „์šฉ ์–ธ์–ด๋Š” ์ž๋™์œผ๋กœ Edge-TTS ์‚ฌ์šฉ
if language in EDGE_TTS_ONLY_LANGUAGES and tts_engine != "Edge-TTS":
tts_engine = "Edge-TTS"
# Generate audio based on TTS engine
if tts_engine == "Edge-TTS":
output_file, _ = await converter.text_to_speech_edge(conversation_json, language)
elif tts_engine == "Spark-TTS":
if not SPARK_AVAILABLE:
return "Spark TTS not available. Please install required dependencies and clone the Spark-TTS repository.", None
converter.initialize_spark_tts()
output_file, _ = converter.text_to_speech_spark(conversation_json, language)
else: # MeloTTS
if not MELO_AVAILABLE:
return "MeloTTS not available. Please install required dependencies.", None
if language in EDGE_TTS_ONLY_LANGUAGES:
return f"MeloTTS does not support {language}. Please use Edge-TTS for this language.", None
converter.initialize_melo_tts()
output_file, _ = converter.text_to_speech_melo(conversation_json)
return "Audio generated successfully!", output_file
except Exception as e:
return f"Error generating audio: {str(e)}", None
def synthesize_sync(article_input, input_type: str = "URL", mode: str = "Local", tts_engine: str = "Edge-TTS", language: str = "English"):
"""Synchronous wrapper for async synthesis"""
return asyncio.run(synthesize(article_input, input_type, mode, tts_engine, language))
def regenerate_audio_sync(conversation_text: str, tts_engine: str = "Edge-TTS", language: str = "English"):
"""Synchronous wrapper for async audio regeneration"""
return asyncio.run(regenerate_audio(conversation_text, tts_engine, language))
def update_tts_engine_for_language(language):
"""์–ธ์–ด๋ณ„ TTS ์—”์ง„ ์˜ต์…˜ ์—…๋ฐ์ดํŠธ"""
if language in EDGE_TTS_ONLY_LANGUAGES:
language_info = {
"Korean": "ํ•œ๊ตญ์–ด๋Š” Edge-TTS๋งŒ ์ง€์›๋ฉ๋‹ˆ๋‹ค",
"Japanese": "ๆ—ฅๆœฌ่ชžใฏEdge-TTSใฎใฟใ‚ตใƒใƒผใƒˆใ•ใ‚Œใฆใ„ใพใ™",
"French": "Le franรงais n'est pris en charge que par Edge-TTS",
"German": "Deutsch wird nur von Edge-TTS unterstรผtzt",
"Spanish": "El espaรฑol solo es compatible con Edge-TTS",
"Italian": "L'italiano รจ supportato solo da Edge-TTS",
"Portuguese": "O portuguรชs รฉ suportado apenas pelo Edge-TTS",
"Dutch": "Nederlands wordt alleen ondersteund door Edge-TTS",
"Thai": "เธ เธฒเธฉเธฒเน„เธ—เธขเธฃเธญเธ‡เธฃเธฑเธšเน€เธ‰เธžเธฒเธฐ Edge-TTS เน€เธ—เนˆเธฒเธ™เธฑเน‰เธ™",
"Vietnamese": "Tiแบฟng Viแป‡t chแป‰ ฤ‘ฦฐแปฃc hแป— trแปฃ bแปŸi Edge-TTS",
"Arabic": "ุงู„ุนุฑุจูŠุฉ ู…ุฏุนูˆู…ุฉ ูู‚ุท ู…ู† Edge-TTS",
"Hebrew": "ืขื‘ืจื™ืช ื ืชืžื›ืช ืจืง ืขืœ ื™ื“ื™ Edge-TTS",
"Indonesian": "Bahasa Indonesia hanya didukung oleh Edge-TTS",
"Hindi": "เคนเคฟเค‚เคฆเฅ€ เค•เฅ‡เคตเคฒ Edge-TTS เคฆเฅเคตเคพเคฐเคพ เคธเคฎเคฐเฅเคฅเคฟเคค เคนเฅˆ",
"Russian": "ะ ัƒััะบะธะน ะฟะพะดะดะตั€ะถะธะฒะฐะตั‚ัั ั‚ะพะปัŒะบะพ Edge-TTS",
"Chinese": "ไธญๆ–‡ไป…ๆ”ฏๆŒEdge-TTS"
}
info_text = language_info.get(language, f"{language} is only supported by Edge-TTS")
return gr.Radio(
choices=["Edge-TTS"],
value="Edge-TTS",
label="TTS Engine",
info=info_text,
interactive=False
)
else:
return gr.Radio(
choices=["Edge-TTS", "Spark-TTS", "MeloTTS"],
value="Edge-TTS",
label="TTS Engine",
info="Edge-TTS: Cloud-based, natural voices | Spark-TTS: Local AI model | MeloTTS: Local, requires GPU",
interactive=True
)
def toggle_input_visibility(input_type):
"""Toggle visibility of URL input, file upload, and keyword input based on input type"""
if input_type == "URL":
return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
elif input_type == "PDF":
return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)
else: # Keyword
return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
# ๋ชจ๋ธ ์ดˆ๊ธฐํ™” (์•ฑ ์‹œ์ž‘ ์‹œ)
if LLAMA_CPP_AVAILABLE:
try:
model_path = hf_hub_download(
repo_id=converter.config.local_model_repo,
filename=converter.config.local_model_name,
local_dir="./models"
)
print(f"Model downloaded to: {model_path}")
except Exception as e:
print(f"Failed to download model at startup: {e}")
# Gradio Interface - ๊ฐœ์„ ๋œ ๋‹ค๊ตญ์–ด ๋ ˆ์ด์•„์›ƒ
with gr.Blocks(theme='soft', title="AI Podcast Generator", css="""
.container {max-width: 1200px; margin: auto; padding: 20px;}
.header-text {text-align: center; margin-bottom: 30px;}
.input-group {background: #f7f7f7; padding: 20px; border-radius: 10px; margin-bottom: 20px;}
.output-group {background: #f0f0f0; padding: 20px; border-radius: 10px;}
.status-box {background: #e8f4f8; padding: 15px; border-radius: 8px; margin-top: 10px;}
""") as demo:
with gr.Column(elem_classes="container"):
# ํ—ค๋”
with gr.Row(elem_classes="header-text"):
gr.Markdown("""
# ๐ŸŽ™๏ธ AI Podcast Generator - Professional Multi-Language Edition
### Convert any article, blog, PDF document, or topic into an engaging professional podcast conversation in 24+ languages!
""")
with gr.Row(elem_classes="discord-badge"):
gr.HTML("""
<p style="text-align: center;">
<a href="https://discord.gg/openfreeai" target="_blank">
<img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=%230000ff&labelColor=%23800080&logo=discord&logoColor=white&style=for-the-badge" alt="badge">
</a>
</p>
""")
# ์ƒํƒœ ํ‘œ์‹œ ์„น์…˜
with gr.Row():
with gr.Column(scale=1):
gr.Markdown(f"""
#### ๐Ÿค– System Status
- **LLM**: {converter.config.local_model_name.split('.')[0]}
- **Fallback**: {converter.config.api_model_name.split('/')[-1]}
- **Llama CPP**: {"โœ… Ready" if LLAMA_CPP_AVAILABLE else "โŒ Not Available"}
- **Search**: {"โœ… Brave API" if BRAVE_KEY else "โŒ No API"}
""")
with gr.Column(scale=1):
gr.Markdown("""
#### ๐ŸŒ Multi-Language Support
- **24+ Languages**: Korean, Japanese, French, German, Spanish, Italian, etc.
- **Native Voices**: Optimized for each language
- **Professional Style**: Expert discussions with data & insights
- **Auto-TTS Selection**: Best engine per language
""")
# ๋ฉ”์ธ ์ž…๋ ฅ ์„น์…˜
with gr.Group(elem_classes="input-group"):
with gr.Row():
# ์™ผ์ชฝ: ์ž…๋ ฅ ์˜ต์…˜๋“ค
with gr.Column(scale=2):
# ์ž…๋ ฅ ํƒ€์ž… ์„ ํƒ
input_type_selector = gr.Radio(
choices=["URL", "PDF", "Keyword"],
value="URL",
label="๐Ÿ“ฅ Input Type",
info="Choose your content source"
)
# URL ์ž…๋ ฅ
url_input = gr.Textbox(
label="๐Ÿ”— Article URL",
placeholder="Enter the article URL here...",
value="",
visible=True,
lines=2
)
# PDF ์—…๋กœ๋“œ
pdf_input = gr.File(
label="๐Ÿ“„ Upload PDF",
file_types=[".pdf"],
visible=False
)
# ํ‚ค์›Œ๋“œ ์ž…๋ ฅ
keyword_input = gr.Textbox(
label="๐Ÿ” Topic/Keyword",
placeholder="Enter a topic (e.g., 'AI trends 2024', '์ธ๊ณต์ง€๋Šฅ', 'IA tendances', 'KI Trends')",
value="",
visible=False,
info="System will search and compile latest information",
lines=2
)
# ์˜ค๋ฅธ์ชฝ: ์„ค์ • ์˜ต์…˜๋“ค
with gr.Column(scale=1):
# ์–ธ์–ด ์„ ํƒ
language_selector = gr.Radio(
choices=[
"English", "Korean", "Japanese", "French", "German",
"Spanish", "Italian", "Portuguese", "Dutch", "Thai",
"Vietnamese", "Arabic", "Hebrew", "Indonesian", "Hindi",
"Russian", "Chinese", "Norwegian", "Swedish", "Finnish",
"Danish", "Polish", "Turkish", "Greek", "Czech"
],
value="English",
label="๐ŸŒ Language / ์–ธ์–ด / ่ฏญ่จ€",
info="Select podcast language"
)
# ์ฒ˜๋ฆฌ ๋ชจ๋“œ
mode_selector = gr.Radio(
choices=["Local", "API"],
value="Local",
label="โš™๏ธ Processing Mode",
info="Local: On-device | API: Cloud"
)
# TTS ์—”์ง„
tts_selector = gr.Radio(
choices=["Edge-TTS", "Spark-TTS", "MeloTTS"],
value="Edge-TTS",
label="๐Ÿ”Š TTS Engine",
info="Voice synthesis engine"
)
# ์ƒ์„ฑ ๋ฒ„ํŠผ
with gr.Row():
convert_btn = gr.Button(
"๐ŸŽฏ Generate Professional Conversation",
variant="primary",
size="lg",
scale=1
)
# ์ถœ๋ ฅ ์„น์…˜
with gr.Group(elem_classes="output-group"):
with gr.Row():
# ์™ผ์ชฝ: ๋Œ€ํ™” ํ…์ŠคํŠธ
with gr.Column(scale=3):
conversation_output = gr.Textbox(
label="๐Ÿ’ฌ Generated Professional Conversation (Editable)",
lines=25,
max_lines=50,
interactive=True,
placeholder="Professional podcast conversation will appear here...\n์ „๋ฌธ ํŒŸ์บ์ŠคํŠธ ๋Œ€ํ™”๊ฐ€ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค...\nLa conversation professionnelle du podcast apparaรฎtra ici...",
info="Edit the conversation as needed. Format: 'Speaker Name: Text'"
)
# ์˜ค๋””์˜ค ์ƒ์„ฑ ๋ฒ„ํŠผ
with gr.Row():
generate_audio_btn = gr.Button(
"๐ŸŽ™๏ธ Generate Audio from Text",
variant="secondary",
size="lg"
)
# ์˜ค๋ฅธ์ชฝ: ์˜ค๋””์˜ค ์ถœ๋ ฅ ๋ฐ ์ƒํƒœ
with gr.Column(scale=2):
audio_output = gr.Audio(
label="๐ŸŽง Professional Podcast Audio",
type="filepath",
interactive=False
)
status_output = gr.Textbox(
label="๐Ÿ“Š Status",
interactive=False,
lines=3,
elem_classes="status-box"
)
# ๋„์›€๋ง
gr.Markdown("""
#### ๐Ÿ’ก Quick Tips:
- **URL**: Paste any article link
- **PDF**: Upload documents directly
- **Keyword**: Enter topics for AI research
- **24+ Languages** fully supported
- Edit conversation before audio generation
- Auto TTS engine selection per language
""")
# ์˜ˆ์ œ ์„น์…˜
with gr.Accordion("๐Ÿ“š Multi-Language Examples", open=False):
gr.Examples(
examples=[
["https://huggingface.co/blog/openfreeai/cycle-navigator", "URL", "Local", "Edge-TTS", "English"],
["quantum computing breakthroughs", "Keyword", "Local", "Edge-TTS", "English"],
["์ธ๊ณต์ง€๋Šฅ ์œค๋ฆฌ์™€ ๊ทœ์ œ", "Keyword", "Local", "Edge-TTS", "Korean"],
["https://huggingface.co/papers/2505.14810", "URL", "Local", "Edge-TTS", "Japanese"],
["intelligence artificielle tendances", "Keyword", "Local", "Edge-TTS", "French"],
["kรผnstliche intelligenz entwicklung", "Keyword", "Local", "Edge-TTS", "German"],
["inteligencia artificial avances", "Keyword", "Local", "Edge-TTS", "Spanish"],
],
inputs=[url_input, input_type_selector, mode_selector, tts_selector, language_selector],
outputs=[conversation_output, status_output],
fn=synthesize_sync,
cache_examples=False,
)
# Input type change handler
input_type_selector.change(
fn=toggle_input_visibility,
inputs=[input_type_selector],
outputs=[url_input, pdf_input, keyword_input]
)
# ์–ธ์–ด ๋ณ€๊ฒฝ ์‹œ TTS ์—”์ง„ ์˜ต์…˜ ์—…๋ฐ์ดํŠธ
language_selector.change(
fn=update_tts_engine_for_language,
inputs=[language_selector],
outputs=[tts_selector]
)
# ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
def get_article_input(input_type, url_input, pdf_input, keyword_input):
"""Get the appropriate input based on input type"""
if input_type == "URL":
return url_input
elif input_type == "PDF":
return pdf_input
else: # Keyword
return keyword_input
convert_btn.click(
fn=lambda input_type, url_input, pdf_input, keyword_input, mode, tts, lang: synthesize_sync(
get_article_input(input_type, url_input, pdf_input, keyword_input), input_type, mode, tts, lang
),
inputs=[input_type_selector, url_input, pdf_input, keyword_input, mode_selector, tts_selector, language_selector],
outputs=[conversation_output, status_output]
)
generate_audio_btn.click(
fn=regenerate_audio_sync,
inputs=[conversation_output, tts_selector, language_selector],
outputs=[status_output, audio_output]
)
# Launch the app
if __name__ == "__main__":
demo.queue(api_open=True, default_concurrency_limit=10).launch(
show_api=True,
share=False,
server_name="0.0.0.0",
server_port=7860
)