Spaces:
Running
Running
Create utils.py
Browse files
utils.py
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from groq import Groq
|
2 |
+
from pydantic import BaseModel, ValidationError
|
3 |
+
from typing import List, Literal
|
4 |
+
import os
|
5 |
+
import tiktoken
|
6 |
+
import json
|
7 |
+
import re
|
8 |
+
import tempfile
|
9 |
+
from gtts import gTTS
|
10 |
+
from bs4 import BeautifulSoup
|
11 |
+
import requests
|
12 |
+
|
13 |
+
groq_client = Groq(api_key=os.environ["GROQ_API_KEY"])
|
14 |
+
tokenizer = tiktoken.get_encoding("cl100k_base")
|
15 |
+
|
16 |
+
class DialogueItem(BaseModel):
|
17 |
+
speaker: Literal["Sarah", "Maria"]
|
18 |
+
text: str
|
19 |
+
|
20 |
+
class Dialogue(BaseModel):
|
21 |
+
dialogue: List[DialogueItem]
|
22 |
+
|
23 |
+
def truncate_text(text, max_tokens=2048):
|
24 |
+
tokens = tokenizer.encode(text)
|
25 |
+
if len(tokens) > max_tokens:
|
26 |
+
return tokenizer.decode(tokens[:max_tokens])
|
27 |
+
return text
|
28 |
+
|
29 |
+
def extract_text_from_url(url):
|
30 |
+
try:
|
31 |
+
response = requests.get(url)
|
32 |
+
response.raise_for_status()
|
33 |
+
soup = BeautifulSoup(response.text, 'html.parser')
|
34 |
+
|
35 |
+
for script in soup(["script", "style"]):
|
36 |
+
script.decompose()
|
37 |
+
|
38 |
+
text = soup.get_text()
|
39 |
+
lines = (line.strip() for line in text.splitlines())
|
40 |
+
chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
|
41 |
+
text = '\n'.join(chunk for chunk in chunks if chunk)
|
42 |
+
|
43 |
+
return text
|
44 |
+
except Exception as e:
|
45 |
+
raise ValueError(f"Error extracting text from URL: {str(e)}")
|
46 |
+
|
47 |
+
def generate_script(system_prompt: str, input_text: str, tone: str, target_length: str):
|
48 |
+
input_text = truncate_text(input_text)
|
49 |
+
word_limit = 300 if target_length == "Short (1-2 min)" else 750
|
50 |
+
|
51 |
+
prompt = f"""
|
52 |
+
{system_prompt}
|
53 |
+
TONE: {tone}
|
54 |
+
TARGET LENGTH: {target_length} (approximately {word_limit} words)
|
55 |
+
INPUT TEXT: {input_text}
|
56 |
+
Generate a complete, well-structured podcast script that:
|
57 |
+
1. Starts with a proper introduction
|
58 |
+
2. Covers the main points from the input text
|
59 |
+
3. Has a natural flow of conversation between Sarah (American accent) and Maria (British accent)
|
60 |
+
4. Concludes with a summary and sign-off
|
61 |
+
5. Fits within the {word_limit} word limit for the target length of {target_length}
|
62 |
+
6. Strongly emphasizes the {tone} tone throughout the conversation
|
63 |
+
For a humorous tone, include jokes, puns, and playful banter.
|
64 |
+
For a casual tone, use colloquial language and make it sound like a conversation between college students.
|
65 |
+
For a formal tone, maintain a professional podcast style with well-structured arguments and formal language.
|
66 |
+
Ensure the script is not abruptly cut off and forms a complete conversation.
|
67 |
+
"""
|
68 |
+
|
69 |
+
response = groq_client.chat.completions.create(
|
70 |
+
messages=[
|
71 |
+
{"role": "system", "content": prompt},
|
72 |
+
],
|
73 |
+
model="llama-3.1-70b-versatile",
|
74 |
+
max_tokens=2048,
|
75 |
+
temperature=0.7
|
76 |
+
)
|
77 |
+
|
78 |
+
content = response.choices[0].message.content
|
79 |
+
content = re.sub(r'```json\s*|\s*```', '', content)
|
80 |
+
|
81 |
+
try:
|
82 |
+
json_data = json.loads(content)
|
83 |
+
dialogue = Dialogue.model_validate(json_data)
|
84 |
+
except json.JSONDecodeError as json_error:
|
85 |
+
match = re.search(r'\{.*\}', content, re.DOTALL)
|
86 |
+
if match:
|
87 |
+
try:
|
88 |
+
json_data = json.loads(match.group())
|
89 |
+
dialogue = Dialogue.model_validate(json_data)
|
90 |
+
except (json.JSONDecodeError, ValidationError) as e:
|
91 |
+
raise ValueError(f"Failed to parse dialogue JSON: {e}\nContent: {content}")
|
92 |
+
else:
|
93 |
+
raise ValueError(f"Failed to find valid JSON in the response: {content}")
|
94 |
+
except ValidationError as e:
|
95 |
+
raise ValueError(f"Failed to validate dialogue structure: {e}\nContent: {content}")
|
96 |
+
|
97 |
+
return dialogue
|
98 |
+
|
99 |
+
def generate_audio(text: str, speaker: str) -> str:
|
100 |
+
tld = 'com' if speaker == "Sarah" else 'co.uk'
|
101 |
+
tts = gTTS(text=text, lang='en', tld=tld)
|
102 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_audio:
|
103 |
+
tts.save(temp_audio.name)
|
104 |
+
return temp_audio.name
|