cwattsnogueira commited on
Commit
3df5c00
·
verified ·
1 Parent(s): 680b000

app tinytutor

Browse files
Files changed (1) hide show
  1. app.py +146 -0
app.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import asyncio
3
+ import google.generativeai as genai
4
+
5
+ from google.adk.agents import Agent
6
+ from google.adk.models.google_llm import Gemini
7
+ from google.adk.runners import InMemoryRunner
8
+ from google.adk.tools import google_search
9
+ from google.genai import types
10
+
11
+ from google.cloud import texttospeech
12
+ from pydub import AudioSegment
13
+ import gradio as gr
14
+
15
+ # --- Configure API Keys ---
16
+ GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
17
+ if not GOOGLE_API_KEY:
18
+ raise RuntimeError("❌ Missing GOOGLE_API_KEY environment variable.")
19
+ genai.configure(api_key=GOOGLE_API_KEY)
20
+
21
+ SERVICE_ACCOUNT_JSON = os.getenv("GCP_VI_SERVICE_ACCOUNT_JSON")
22
+ if not SERVICE_ACCOUNT_JSON:
23
+ raise RuntimeError("❌ Missing GCP_VI_SERVICE_ACCOUNT_JSON environment variable.")
24
+
25
+ with open("tinytutor-tss-agent.json", "w") as f:
26
+ f.write(SERVICE_ACCOUNT_JSON)
27
+
28
+ os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "tinytutor-tss-agent.json"
29
+ tts_client = texttospeech.TextToSpeechClient()
30
+
31
+ # --- Retry Options ---
32
+ retry_config = types.HttpRetryOptions(
33
+ attempts=5,
34
+ exp_base=7,
35
+ initial_delay=1,
36
+ http_status_codes=[429, 500, 503, 504]
37
+ )
38
+
39
+ # --- Pedagogy Agent ---
40
+ pedagogy_agent = Agent(
41
+ name="PedagogyAgent",
42
+ model=Gemini(model="gemini-2.5-flash-lite", retry_options=retry_config),
43
+ description="Explains topics in simple ELI5 style.",
44
+ instruction="Explain the topic like I'm 5. Use google_search if needed.",
45
+ tools=[google_search],
46
+ )
47
+ runner = InMemoryRunner(agent=pedagogy_agent)
48
+
49
+ async def run_pedagogy_async(topic: str) -> str:
50
+ response = await runner.run_debug(topic)
51
+ return response[0].content.parts[0].text
52
+
53
+ # --- ScriptWriter Agent ---
54
+ SCRIPTWRITER_SYSTEM_PROMPT = """
55
+ You are a Teacher.
56
+ ... (same as your original prompt) ...
57
+ """
58
+
59
+ def run_scriptwriter(explanation: str) -> str:
60
+ model = genai.GenerativeModel(
61
+ model_name="gemini-2.5-flash",
62
+ system_instruction=SCRIPTWRITER_SYSTEM_PROMPT
63
+ )
64
+ response = model.generate_content(
65
+ f"Write a children's story based on this:\n{explanation}",
66
+ generation_config=genai.GenerationConfig(
67
+ temperature=0.9,
68
+ max_output_tokens=4096
69
+ )
70
+ )
71
+ try:
72
+ return response.text
73
+ except Exception:
74
+ try:
75
+ return response.candidates[0].content.parts[0].text
76
+ except Exception:
77
+ return "⚠️ ScriptWriter failed."
78
+
79
+ # --- Audio Generator ---
80
+ def chunk_text(text, max_chars=4500):
81
+ text = text.strip()
82
+ if len(text) <= max_chars:
83
+ return [text]
84
+ chunks = []
85
+ while len(text) > max_chars:
86
+ cut = text.rfind(". ", 0, max_chars)
87
+ if cut == -1:
88
+ cut = max_chars
89
+ chunks.append(text[:cut+1])
90
+ text = text[cut+1:].strip()
91
+ chunks.append(text)
92
+ return chunks
93
+
94
+ def tts_segment(text):
95
+ synthesis_input = texttospeech.SynthesisInput(text=text)
96
+ voice = texttospeech.VoiceSelectionParams(
97
+ language_code="en-US",
98
+ name="en-US-Journey-F"
99
+ )
100
+ audio_cfg = texttospeech.AudioConfig(
101
+ audio_encoding=texttospeech.AudioEncoding.MP3,
102
+ speaking_rate=0.94,
103
+ pitch=0.0,
104
+ volume_gain_db=0.0
105
+ )
106
+ response = tts_client.synthesize_speech(
107
+ input=synthesis_input,
108
+ voice=voice,
109
+ audio_config=audio_cfg
110
+ )
111
+ return response.audio_content
112
+
113
+ def audio_writer(script_text: str, out="story.mp3"):
114
+ chunks = chunk_text(script_text)
115
+ audio = AudioSegment.silent(200)
116
+ for i, chunk in enumerate(chunks, 1):
117
+ path = f"seg_{i}.mp3"
118
+ with open(path, "wb") as f:
119
+ f.write(tts_segment(chunk))
120
+ audio += AudioSegment.from_mp3(path)
121
+ audio += AudioSegment.silent(150)
122
+ audio.export(out, format="mp3")
123
+ return out
124
+
125
+ # --- Full Pipeline ---
126
+ async def full_pipeline(topic: str):
127
+ eli5 = await run_pedagogy_async(topic)
128
+ script = run_scriptwriter(eli5)
129
+ audio_path = audio_writer(script, "story.mp3")
130
+ return eli5, script, audio_path
131
+
132
+ # --- Gradio App ---
133
+ app = gr.Interface(
134
+ fn=full_pipeline,
135
+ inputs=gr.Textbox(label="Your Topic"),
136
+ outputs=[
137
+ gr.Textbox(label="ELI5 Explanation", lines=8),
138
+ gr.Textbox(label="Generated Story Script", lines=20),
139
+ gr.Audio(label="Generated Audio")
140
+ ],
141
+ title="🎧 TinyTutor — Full Pipeline",
142
+ css=".gradio-container { min-height: 1200px !important; }"
143
+ )
144
+
145
+ if __name__ == "__main__":
146
+ app.launch()