Spaces:
Running
Running
podcast-review audio
Browse files- app.py +23 -2
- final_literature_review.pdf +2 -2
- prompts/manuscript_style_example.prompt +134 -0
- prompts/papers_synthesis.prompt +3 -1
- prompts/review_podcast_manus_v2.prompt +43 -0
- prompts/review_podcast_outline.prompt +61 -0
- utils/__init__.py +4 -2
- utils/__pycache__/__init__.cpython-311.pyc +0 -0
- utils/__pycache__/review_flow.cpython-311.pyc +0 -0
- utils/__pycache__/tts_utils.cpython-311.pyc +0 -0
- utils/review_flow.py +91 -2
- utils/tts_utils.py +46 -1
app.py
CHANGED
@@ -92,7 +92,7 @@ from utils.llm_utils import (
|
|
92 |
wait_for_files_active
|
93 |
)
|
94 |
from utils.tts_utils import generate_tts_audio
|
95 |
-
from utils.review_flow import process_multiple_pdfs, generate_final_review_pdf
|
96 |
|
97 |
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
|
98 |
logger = logging.getLogger(__name__)
|
@@ -398,4 +398,25 @@ elif mode == "Write a Literature Review":
|
|
398 |
data=final_pdf_bytes,
|
399 |
file_name="final_literature_review.pdf",
|
400 |
mime="application/pdf"
|
401 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
wait_for_files_active
|
93 |
)
|
94 |
from utils.tts_utils import generate_tts_audio
|
95 |
+
from utils.review_flow import process_multiple_pdfs, generate_final_review_pdf, generate_multi_speaker_podcast
|
96 |
|
97 |
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
|
98 |
logger = logging.getLogger(__name__)
|
|
|
398 |
data=final_pdf_bytes,
|
399 |
file_name="final_literature_review.pdf",
|
400 |
mime="application/pdf"
|
401 |
+
)
|
402 |
+
# Save final text and a base filename for podcast generation.
|
403 |
+
st.session_state["final_text"] = final_review_text
|
404 |
+
st.session_state["pdf_basename"] = "final_literature_review"
|
405 |
+
|
406 |
+
if st.session_state.get("final_text"):
|
407 |
+
if st.button("Generate Multi-Speaker Podcast 🎤"):
|
408 |
+
progress_bar = st.progress(0)
|
409 |
+
with st.spinner("Generating multi-speaker podcast..."):
|
410 |
+
try:
|
411 |
+
podcast_audio = asyncio.run(
|
412 |
+
generate_multi_speaker_podcast(st.session_state["final_text"], progress_bar=progress_bar)
|
413 |
+
)
|
414 |
+
st.audio(podcast_audio, format="audio/mp3")
|
415 |
+
st.download_button(
|
416 |
+
"Download Podcast Audio 📥",
|
417 |
+
podcast_audio,
|
418 |
+
file_name=f"{st.session_state.get('pdf_basename', 'literature_review')}_podcast.mp3",
|
419 |
+
mime="audio/mp3"
|
420 |
+
)
|
421 |
+
except Exception as e:
|
422 |
+
st.error("Podcast generation failed: " + str(e))
|
final_literature_review.pdf
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:7d10e19d189310b940f8735f1725bac1451b7259bdd442aa2b7385bde9026f96
|
3 |
+
size 298472
|
prompts/manuscript_style_example.prompt
ADDED
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"opening": [
|
3 |
+
{
|
4 |
+
"speaker": "host",
|
5 |
+
"text": "Today, we're diving into the rapidly evolving world of Large Language Models, or LLMs, and their increasing role in qualitative data analysis. The sheer volume of textual data researchers are dealing with today is staggering, and it's really pushing the boundaries of traditional, labor-intensive analysis methods. This is where LLMs are starting to show real promise, offering potential ways to enhance efficiency. But a central question remains: how effectively can these models actually perform the nuanced tasks required in qualitative research, and what are the trade-offs?"
|
6 |
+
},
|
7 |
+
{
|
8 |
+
"speaker": "expert",
|
9 |
+
"text": "Absolutely. Qualitative data analysis, as practiced through methods like thematic analysis, as Braun and Clarke established back in 2006, is inherently time-consuming. It involves deep engagement with the data, identifying patterns, and constructing meaning. Researchers are now grappling with how to scale these processes, and LLMs, with their impressive natural language processing capabilities, seem like a potential solution. The question isn't just *if* LLMs can help, but *how* they can best be integrated into the research workflow."
|
10 |
+
}
|
11 |
+
],
|
12 |
+
"foundation": [
|
13 |
+
{
|
14 |
+
"speaker": "host",
|
15 |
+
"text": "So, let's get a bit more concrete. What are some of the specific tasks within qualitative data analysis that researchers are actually applying these LLMs to? And, what were those expectations at the beginning? What are the hopes for using them in this field?"
|
16 |
+
},
|
17 |
+
{
|
18 |
+
"speaker": "expert",
|
19 |
+
"text": "We're seeing LLMs applied across a range of tasks, from deductive coding, where you're applying a pre-defined coding framework to the data, to thematic analysis, which is more about identifying emergent themes. Some researchers are also using them for annotation, for instance, classifying the sentiment or political leaning of social media posts, as Törnberg did in his 2023 study on Twitter data. Initially, there's this hope that LLMs could almost automate large parts of the qualitative analysis process – imagine drastically reducing the time spent coding vast amounts of text. And many hoped that there could be a nearly perfect coding result by the models."
|
20 |
+
},
|
21 |
+
{
|
22 |
+
"speaker": "expert",
|
23 |
+
"text": "But, of course, qualitative analysis isn't just about speed; it's about depth and nuanced interpretation. So, alongside the excitement, there's also a healthy dose of skepticism, and a real need to rigorously evaluate the performance of these models."
|
24 |
+
}
|
25 |
+
|
26 |
+
],
|
27 |
+
"main_discussion_methodological_approaches": [
|
28 |
+
{
|
29 |
+
"speaker": "host",
|
30 |
+
"text": "Let's move the discussion to the ways researchers implement LLMs. Let's dive into the practical side. What are the methodologies being tested? How are researchers setting this up?"
|
31 |
+
},
|
32 |
+
{
|
33 |
+
"speaker": "expert",
|
34 |
+
"text": "There's a real spectrum of approaches. Some, like Törnberg's work with political Twitter annotation, are using a 'zero-shot' approach. Essentially, this means they're leveraging the LLM's pre-existing knowledge without any task-specific training. It's testing the inherent capabilities of the model. Then you have approaches like the 'LLM-in-the-loop' framework that Dai and colleagues developed in 2023. This is a much more structured, iterative process where the LLM and human coders work together, refining the analysis in stages. It's a collaborative model, aiming to leverage the strengths of both human and artificial intelligence."
|
35 |
+
},
|
36 |
+
{
|
37 |
+
"speaker": "expert",
|
38 |
+
"text": "Another significant methodology is LACA, or LLM-Assisted Content Analysis, presented by Chew and their team, also in 2023. This is a detailed, step-by-step process for integrating LLMs into deductive coding. It even involves the LLM participating in the co-development of the codebook, which is pretty fascinating. Then they run rigorous tests to check its reliability against human coders."
|
39 |
+
},
|
40 |
+
{
|
41 |
+
"speaker": "host",
|
42 |
+
"text": "This 'LLM-in-the-loop' approach you mention sounds particularly interesting. How does that work in practice, and how does it contrast with, say, the zero-shot approach or LACA?"
|
43 |
+
},
|
44 |
+
{
|
45 |
+
"speaker": "expert",
|
46 |
+
"text": "In the 'LLM-in-the-loop' model, it's not about handing over the entire analysis to the LLM. Instead, the LLM might generate an initial set of codes or themes, and then the human researcher steps in to review, refine, and validate those codes. It's a back-and-forth process. The human provides context and expertise that the LLM might lack, ensuring the analysis remains grounded in the nuances of the data. This differs significantly from the zero-shot approach, where you're really relying on the LLM's raw ability. LACA, while also collaborative, is more focused on a pre-defined, deductive coding process, whereas 'LLM-in-the-loop' can be more flexible and adaptable to different stages of analysis, including the more inductive, theme-development stages."
|
47 |
+
}
|
48 |
+
],
|
49 |
+
"main_discussion_performance_and_metrics": [
|
50 |
+
{
|
51 |
+
"speaker":"host",
|
52 |
+
"text": "Okay, now let's change the perspective a little bit: evaluation! How do we measure the success in these applications of LLMs? What are the main metrics that researchers are using?"
|
53 |
+
},
|
54 |
+
{
|
55 |
+
"speaker": "expert",
|
56 |
+
"text": "A dominant metric across the board is Inter-rater reliability, or IRR. This essentially measures the level of agreement between different coders – in this case, between the LLM and human coders, or even between multiple human coders to establish a benchmark. There are different ways to calculate IRR. Kirsten and colleagues, in their 2024 paper, along with Dai's team, use Cohen's Kappa, which is a common measure. Törnberg uses Krippendorf's Alpha. And Chew and colleagues, in their LACA work, advocate for Gwet's AC1, arguing it's more robust in certain situations, particularly when you have rare codes."
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"speaker": "expert",
|
60 |
+
"text": "Besides IRR, researchers are also looking at things like accuracy – how often does the LLM get the coding 'right' compared to a gold standard? – and bias, which is a huge concern. Törnberg's study, for example, examined whether ChatGPT-4 exhibited any political bias in its annotations. And then there's the issue of 'hallucinations,' where the LLM essentially invents information or makes up codes that aren't grounded in the data."
|
61 |
+
},
|
62 |
+
{
|
63 |
+
"speaker": "host",
|
64 |
+
"text": "This concept of 'hallucination' is quite specific to LLMs, isn't it? Could you elaborate on what that means in this context?"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"speaker": "expert",
|
68 |
+
"text": "Right. In the context of LLMs, 'hallucination' refers to the model generating text that is factually incorrect, nonsensical, or, in the case of qualitative coding, not supported by the actual data being analyzed. It's as if the model is 'making things up.' It's a significant concern because, in qualitative research, we're striving for interpretations that are deeply rooted in the data. A hallucinating LLM could lead to misleading or completely inaccurate findings."
|
69 |
+
},
|
70 |
+
{
|
71 |
+
"speaker": "expert",
|
72 |
+
"text": "And, to circle back a bit to Inter-rater Reliability, what we aim to achieve with such metrics is a measure of consistency. In traditional qualitative research, you'd have multiple human coders analyzing the same data to ensure the findings aren't just the result of one person's subjective interpretation. With LLMs, IRR helps us understand how well the model's coding aligns with human judgment, and whether it's consistent enough to be reliable."
|
73 |
+
|
74 |
+
}
|
75 |
+
],
|
76 |
+
"main_discussion_strengths_and_limitations": [
|
77 |
+
{
|
78 |
+
"speaker": "host",
|
79 |
+
"text": "Let's move to a discussion of strengths and limitations. What are we learning about where LLMs excel and where they fall short in qualitative data analysis, based on the current research?"
|
80 |
+
},
|
81 |
+
{
|
82 |
+
"speaker": "expert",
|
83 |
+
"text": "One consistent finding is that LLMs show real promise for efficiency. Chew and colleagues, and Dai and their team, both demonstrate significant time savings when using LLMs for coding. We're talking about reducing coding time from minutes per document to seconds. In terms of performance, Kirsten's 2024 research shows that GPT-4, in particular, can achieve very high agreement with human coders on simpler, what they call 'semantic' coding tasks. It's almost on par with human inter-coder agreement in some cases."
|
84 |
+
},
|
85 |
+
{
|
86 |
+
"speaker": "expert",
|
87 |
+
"text": "However – and this is a crucial point – task complexity matters a lot. Kirsten and colleagues found that agreement, for both humans and LLMs, decreases as you move from these simpler, semantic coding tasks to more complex, 'latent' coding. Latent coding requires deeper interpretation, drawing inferences, and understanding underlying meanings. This is where LLMs currently struggle more. It seems they're better at identifying surface-level patterns than at grasping the deeper, more nuanced interpretations that are often central to qualitative research. Chew also finds that GPT3.5 struggles with formatting of codes, and does better with semantical tasks."
|
88 |
+
},
|
89 |
+
{
|
90 |
+
"speaker": "host",
|
91 |
+
"text": "You've made this crucial distinction between 'semantic' and 'latent' tasks. Could you provide a concrete example to illustrate why latent coding presents such a challenge for these models?"
|
92 |
+
},
|
93 |
+
{
|
94 |
+
"speaker": "expert",
|
95 |
+
"text": "Sure. Imagine you're analyzing interview transcripts about people's experiences with a new technology. A semantic coding task might be to identify every time someone mentions a specific feature of the technology – that's relatively straightforward. A latent coding task, however, might be to identify underlying themes of, say, 'technological anxiety' or 'empowerment.' These themes aren't always explicitly stated; they require the coder to interpret the overall tone, the context, and the subtle meanings in the language used. That's much harder for an LLM to do reliably, at least at this stage. There are some edge cases. But it's very challenging to program it."
|
96 |
+
},
|
97 |
+
{
|
98 |
+
"speaker":"expert",
|
99 |
+
"text": "It's also important to note that there are differences between LLM models. The Kirsten study consistently found GPT-4 outperforming GPT-3.5, suggesting that model choice is a significant factor. And there are also inherent limitations like those hallucinations we talked about. And while techniques like few-shot learning – giving the LLM a few examples of how to code – can help mitigate these issues, they don't eliminate them entirely."
|
100 |
+
}
|
101 |
+
],
|
102 |
+
"implications": [
|
103 |
+
{
|
104 |
+
"speaker": "host",
|
105 |
+
"text": "So, let's broaden the scope. What are the practical implications of all this for qualitative researchers? And looking ahead, what are the critical challenges and future directions for this field?"
|
106 |
+
},
|
107 |
+
{
|
108 |
+
"speaker": "expert",
|
109 |
+
"text": "On the practical side, LLMs offer a pathway to significantly speed up the initial stages of qualitative analysis, especially with large datasets. They can help with tasks like identifying key terms, generating initial codes, or summarizing large volumes of text. But, and I want to emphasize this, it's crucial to remember that these are tools to *assist* human researchers, not to replace them. Kirsten and their colleagues rightly caution against a one-size-fits-all approach. They recommend carefully evaluating the specific task and choosing the right LLM and approach accordingly."
|
110 |
+
},
|
111 |
+
{
|
112 |
+
"speaker": "expert",
|
113 |
+
"text": "The ethical considerations are paramount. We need to be very mindful of potential biases in these models, and ensure that we're not introducing or amplifying those biases in our research. Transparency is also key. Researchers need to be very clear about how they're using LLMs, what prompts they're using, and what the limitations of their approach are. The 'black box' nature of some of these models is a real concern, and we need to find ways to make their reasoning more transparent and understandable. Going forward, I think a major focus will be on developing better methods for human-AI collaboration in qualitative research. We need interfaces and workflows that allow researchers to seamlessly interact with LLMs, to review and refine their outputs, and to bring their own expertise to bear on the analysis. And, of course, there's ongoing work on improving the models themselves, particularly their ability to handle those more complex, interpretive tasks."
|
114 |
+
},
|
115 |
+
{
|
116 |
+
"speaker": "host",
|
117 |
+
"text": "What specific types of tools or interfaces do you envision that could best support effective human-AI collaboration in qualitative data analysis in the future?"
|
118 |
+
},
|
119 |
+
{
|
120 |
+
"speaker": "expert",
|
121 |
+
"text": "I imagine interfaces that allow for a more fluid dialogue between the researcher and the LLM. For example, imagine being able to highlight a passage of text and ask the LLM, 'Why did you code this in this way?' or 'What other codes might be relevant here?' and receive a clear, understandable explanation. Or perhaps a system that allows you to easily compare and contrast different coding schemes generated by different LLMs, or by the LLM at different stages of the analysis. The key is to move beyond the model as a 'black box' and to create tools that empower the researcher to critically engage with the LLM's outputs and to integrate them thoughtfully into their own analysis."
|
122 |
+
}
|
123 |
+
],
|
124 |
+
"wrap": [
|
125 |
+
{
|
126 |
+
"speaker": "host",
|
127 |
+
"text": "To sum things up, it's clear that Large Language Models hold considerable promise for transforming qualitative data analysis. The potential for increased efficiency, particularly with large datasets, is undeniable. But the research also highlights the crucial importance of human oversight and the need for a nuanced, task-specific approach. LLMs are powerful tools, but they are not a replacement for careful, critical thinking and interpretive expertise."
|
128 |
+
},
|
129 |
+
{
|
130 |
+
"speaker": "expert",
|
131 |
+
"text": "Precisely. We're in a period of rapid development and exploration. The studies we've discussed today, from Törnberg's work on annotation to Chew's LACA methodology, Dai's 'LLM-in-the-loop' framework, and Kirsten's investigation of task complexity, all point to a future where LLMs play an increasingly significant role in qualitative research. But it's a future that demands caution, ethical awareness, and a continued commitment to rigorous methodological standards. The focus on human-AI collaboration, rather than full automation, is key. And the path forward requires further research into bias mitigation, model improvement, and perhaps most importantly development of user interfaces and methodologies which enable a seemless integration."
|
132 |
+
}
|
133 |
+
]
|
134 |
+
}
|
prompts/papers_synthesis.prompt
CHANGED
@@ -8,7 +8,7 @@ Using the paper summaries, comparative table, and detailed outline provided abov
|
|
8 |
- Sections as outlined in the analysis above
|
9 |
- Comparative overview (featuring the provided table)
|
10 |
- Conclusions and implications
|
11 |
-
- References (
|
12 |
|
13 |
2. FORMATTING REQUIREMENTS:
|
14 |
- Use markdown formatting
|
@@ -35,6 +35,8 @@ Using the paper summaries, comparative table, and detailed outline provided abov
|
|
35 |
- Length: 2500 words (excluding table and references)
|
36 |
- Academic language appropriate to the discipline
|
37 |
- APA style citations
|
|
|
|
|
38 |
- Complete reference list
|
39 |
- Refer to papers with proper academic citations, not filenames
|
40 |
- Adapt style and emphasis to disciplinary norms
|
|
|
8 |
- Sections as outlined in the analysis above
|
9 |
- Comparative overview (featuring the provided table)
|
10 |
- Conclusions and implications
|
11 |
+
- References (APA style)
|
12 |
|
13 |
2. FORMATTING REQUIREMENTS:
|
14 |
- Use markdown formatting
|
|
|
35 |
- Length: 2500 words (excluding table and references)
|
36 |
- Academic language appropriate to the discipline
|
37 |
- APA style citations
|
38 |
+
- Citations in text: (Author, Year) or Author (Year) for more than 2 authors use et al.
|
39 |
+
- For core papers with multiple authors it may be useful to create abbreviations for the author names e.g. BP (Brown & Pinker, 2010) or TRZ (Taylor, Reddy, & Ziegler, 2015)
|
40 |
- Complete reference list
|
41 |
- Refer to papers with proper academic citations, not filenames
|
42 |
- Adapt style and emphasis to disciplinary norms
|
prompts/review_podcast_manus_v2.prompt
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Generate a JSON-formatted podcast script based on a provided outline and research paper that results in a natural academic discussion. The script should follow these guidelines:
|
2 |
+
|
3 |
+
FORMAT:
|
4 |
+
{
|
5 |
+
"segment_name": [
|
6 |
+
{
|
7 |
+
"speaker": "host/expert",
|
8 |
+
"text": "content"
|
9 |
+
}
|
10 |
+
]
|
11 |
+
}
|
12 |
+
|
13 |
+
TONE & STYLE:
|
14 |
+
- Begin directly with the topic; no introductions.
|
15 |
+
- Use sophisticated yet conversational language, reflecting a tone of intellectual curiosity similar to Ezra Klein's style.
|
16 |
+
- Keep the discussion academically substantive but accessible.
|
17 |
+
- Avoid overly formal or cliché expressions; refrain from using phrases like "Exactly" or "It's not only about...it's about".
|
18 |
+
|
19 |
+
SPEAKING PATTERNS:
|
20 |
+
- The host should:
|
21 |
+
* Start with context and emphasize the importance of the topic.
|
22 |
+
* Reference studies naturally (e.g., "What I found interesting in Smith and colleagues' study...").
|
23 |
+
* Ask specific questions about the research findings.
|
24 |
+
* Draw connections between different studies organically.
|
25 |
+
- The expert should:
|
26 |
+
* Provide clear explanations of research methods and findings.
|
27 |
+
* Reference authors naturally (e.g., "The research by Jones and colleagues...").
|
28 |
+
* Introduce and elaborate on key points, sometimes taking control of the discussion.
|
29 |
+
* Build on the host’s questions by connecting broader research insights.
|
30 |
+
|
31 |
+
STRUCTURE:
|
32 |
+
- Organize the script into distinct segments (e.g., opening, foundation, individual paper discussions, comparative discussion, implications, and wrap-up).
|
33 |
+
- Ensure smooth transitions between segments, with either the host or expert explicitly introducing the next section.
|
34 |
+
- Gradually build complex ideas and make natural comparisons between studies.
|
35 |
+
- End with a discussion of broader implications and future research directions.
|
36 |
+
|
37 |
+
GENERAL INSTRUCTIONS:
|
38 |
+
- Base the discussion on the provided outline and research paper, ensuring that references to studies are integrated naturally.
|
39 |
+
- Do not include any special characters (e.g., asterisks, parentheses) that might affect text-to-speech readability.
|
40 |
+
- Keep the conversation engaging and focused on the substance of the research.
|
41 |
+
- Ensure that the resulting JSON is correctly formatted and adheres to the defined structure.
|
42 |
+
|
43 |
+
Generate a script that would represent approximately a 30-minute discussion.
|
prompts/review_podcast_outline.prompt
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Create a super detailed still focused outline for a 30-minute research podcast episode that maintains academic rigor while being engaging and accessible:
|
2 |
+
Base it on the provided PDF literature review.
|
3 |
+
|
4 |
+
PRE-SHOW PREPARATION:
|
5 |
+
- Extract 2-3 core theoretical/practical contributions
|
6 |
+
- List technical terms requiring clarification
|
7 |
+
- Note potential complex concepts that need unpacking
|
8 |
+
- Identify natural points of connection between topics
|
9 |
+
|
10 |
+
STRUCTURAL OUTLINE:
|
11 |
+
|
12 |
+
1. OPENING (1-2 min)
|
13 |
+
- "Today we're talking about..." [frame topic in broader context]
|
14 |
+
- Why this matters now
|
15 |
+
- Quick orientation to core problem/challenge
|
16 |
+
|
17 |
+
2. FOUNDATION (4-5 min)
|
18 |
+
- Accessible entry point to complex topic
|
19 |
+
- Current state of knowledge/research
|
20 |
+
- [2 focused questions that bridge common understanding with technical depth]
|
21 |
+
|
22 |
+
3. MAIN DISCUSSION (17-19 min)
|
23 |
+
Structure as 3-4 segments, each containing:
|
24 |
+
- Precise lead-in question
|
25 |
+
- Space for expert elaboration (2-4 min)
|
26 |
+
- 1 strategic follow-up question
|
27 |
+
- Optional "Earlier you mentioned..." callbacks
|
28 |
+
|
29 |
+
Key elements to include:
|
30 |
+
- Natural progression from broader to specific insights
|
31 |
+
- Points where host can request clarification
|
32 |
+
- Moments to synthesize or connect ideas
|
33 |
+
- Strategic devil's advocate questions
|
34 |
+
- Brief "let's unpack that" interventions for technical terms
|
35 |
+
|
36 |
+
4. IMPLICATIONS (3-4 min)
|
37 |
+
- Practical applications
|
38 |
+
- Critical challenges ahead
|
39 |
+
- Future directions
|
40 |
+
[1-2 challenge questions from host]
|
41 |
+
|
42 |
+
5. WRAP (1-2 min)
|
43 |
+
- Key insights crystallized
|
44 |
+
- Broader significance
|
45 |
+
- Forward-looking statement
|
46 |
+
|
47 |
+
INTERACTION GUIDELINES:
|
48 |
+
- Host interventions should be precise and purposeful
|
49 |
+
- Allow expert to fully develop ideas before follow-up
|
50 |
+
- Use "You mentioned..." to return to important points
|
51 |
+
- Frame challenges as curious inquiry rather than debate
|
52 |
+
|
53 |
+
PACING NOTES:
|
54 |
+
- Mark natural transition points
|
55 |
+
- Note segments requiring extended expert explanation
|
56 |
+
- Identify moments for brief host guidance
|
57 |
+
- Plan smooth segment transitions
|
58 |
+
- Plan where host or expert go on a tangent. They are allowed to do so!
|
59 |
+
|
60 |
+
[Apply to specific content while maintaining balance between accessibility and depth]
|
61 |
+
Just return the outline - no notes, info, remarks
|
utils/__init__.py
CHANGED
@@ -3,7 +3,8 @@ from .llm_utils import (
|
|
3 |
async_generate_text,
|
4 |
generate_title_reference_and_classification,
|
5 |
upload_to_gemini,
|
6 |
-
wait_for_files_active
|
|
|
7 |
)
|
8 |
|
9 |
from .file_utils import (
|
@@ -19,4 +20,5 @@ from .review_flow import (
|
|
19 |
generate_final_review_pdf,
|
20 |
create_comparative_table_prompt)
|
21 |
|
22 |
-
from .tts_utils import generate_tts_audio
|
|
|
|
3 |
async_generate_text,
|
4 |
generate_title_reference_and_classification,
|
5 |
upload_to_gemini,
|
6 |
+
wait_for_files_active,
|
7 |
+
clean_json_response
|
8 |
)
|
9 |
|
10 |
from .file_utils import (
|
|
|
20 |
generate_final_review_pdf,
|
21 |
create_comparative_table_prompt)
|
22 |
|
23 |
+
from .tts_utils import (generate_tts_audio,
|
24 |
+
generate_podcast_audio)
|
utils/__pycache__/__init__.cpython-311.pyc
CHANGED
Binary files a/utils/__pycache__/__init__.cpython-311.pyc and b/utils/__pycache__/__init__.cpython-311.pyc differ
|
|
utils/__pycache__/review_flow.cpython-311.pyc
CHANGED
Binary files a/utils/__pycache__/review_flow.cpython-311.pyc and b/utils/__pycache__/review_flow.cpython-311.pyc differ
|
|
utils/__pycache__/tts_utils.cpython-311.pyc
CHANGED
Binary files a/utils/__pycache__/tts_utils.cpython-311.pyc and b/utils/__pycache__/tts_utils.cpython-311.pyc differ
|
|
utils/review_flow.py
CHANGED
@@ -1,11 +1,14 @@
|
|
|
|
1 |
import os
|
|
|
2 |
import time
|
3 |
import asyncio
|
4 |
import logging
|
5 |
import streamlit as st
|
6 |
from markdown_pdf import MarkdownPdf, Section
|
7 |
from utils.file_utils import load_prompt, save_intermediate_output
|
8 |
-
from utils.llm_utils import get_generation_model, async_generate_text, upload_to_gemini, wait_for_files_active
|
|
|
9 |
|
10 |
logger = logging.getLogger(__name__)
|
11 |
|
@@ -185,4 +188,90 @@ async def generate_final_review_pdf(structured_outputs):
|
|
185 |
logger.error(f"Error generating PDF: {e}")
|
186 |
progress_bar.progress(100)
|
187 |
|
188 |
-
return final_checked_review
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# review_flow.py
|
2 |
import os
|
3 |
+
import json
|
4 |
import time
|
5 |
import asyncio
|
6 |
import logging
|
7 |
import streamlit as st
|
8 |
from markdown_pdf import MarkdownPdf, Section
|
9 |
from utils.file_utils import load_prompt, save_intermediate_output
|
10 |
+
from utils.llm_utils import get_generation_model, async_generate_text, upload_to_gemini, wait_for_files_active, clean_json_response
|
11 |
+
from utils.tts_utils import generate_podcast_audio
|
12 |
|
13 |
logger = logging.getLogger(__name__)
|
14 |
|
|
|
188 |
logger.error(f"Error generating PDF: {e}")
|
189 |
progress_bar.progress(100)
|
190 |
|
191 |
+
return final_checked_review
|
192 |
+
|
193 |
+
|
194 |
+
|
195 |
+
async def generate_multi_speaker_podcast(final_markdown: str, progress_bar=None):
|
196 |
+
"""
|
197 |
+
Generate a multi-speaker podcast audio from the final review markdown.
|
198 |
+
Uses an outline prompt, a manuscript prompt, and a style example.
|
199 |
+
The final_markdown (generated review text) is directly sent to the LLM.
|
200 |
+
Optionally updates a progress bar.
|
201 |
+
"""
|
202 |
+
# Step 0: Load prompt files.
|
203 |
+
if progress_bar:
|
204 |
+
progress_bar.progress(5)
|
205 |
+
outline_prompt = load_prompt("./dev/dev_prompts/review_podcast_outline.prompt")
|
206 |
+
manuscript_prompt = load_prompt("./dev/dev_prompts/review_podcast_manus_v2.prompt")
|
207 |
+
style_example = load_prompt("./dev/dev_prompts/manuscript_style_example.prompt")
|
208 |
+
|
209 |
+
# Step 1: Outline Generation.
|
210 |
+
# Now include the final markdown text in the outline prompt.
|
211 |
+
if progress_bar:
|
212 |
+
progress_bar.progress(15)
|
213 |
+
thinking_model_name, thinking_config = get_generation_model("thinking")
|
214 |
+
thinking_config.system_instruction = (
|
215 |
+
"You are an extremely smart researcher and communicator who crafts extremely detailed outlines for science podcasts."
|
216 |
+
)
|
217 |
+
combined_outline_prompt = f"Review Text:\n{final_markdown} \n\n {outline_prompt}"
|
218 |
+
outline = await async_generate_text(
|
219 |
+
prompt=combined_outline_prompt,
|
220 |
+
pdf_file=None, # No PDF needed here.
|
221 |
+
model_name=thinking_model_name,
|
222 |
+
generation_config=thinking_config
|
223 |
+
)
|
224 |
+
|
225 |
+
# Step 2: Manuscript Generation.
|
226 |
+
if progress_bar:
|
227 |
+
progress_bar.progress(40)
|
228 |
+
flash_model_name, flash_config = get_generation_model("flash")
|
229 |
+
flash_config.response_mime_type = "application/json"
|
230 |
+
flash_config.system_instruction = (
|
231 |
+
"You are an extremely smart manuscript writer for science communication. You produce detailed and engaging content that can be used for TTS - ABSOLUTELY NO! special characters like asterisks or parentheses."
|
232 |
+
"Below is an example of the desired writing style:\n\n" + style_example
|
233 |
+
)
|
234 |
+
combined_manuscript_prompt = (
|
235 |
+
f"Review Text:\n{final_markdown}"
|
236 |
+
f"Outline:\n{outline}\n\n"
|
237 |
+
f"{manuscript_prompt}\n\n"
|
238 |
+
"ABSOLUTELY NO! special characters like asterisks or parentheses."
|
239 |
+
|
240 |
+
)
|
241 |
+
manuscript_json_str = await async_generate_text(
|
242 |
+
prompt=combined_manuscript_prompt,
|
243 |
+
pdf_file=None,
|
244 |
+
model_name=flash_model_name,
|
245 |
+
generation_config=flash_config
|
246 |
+
)
|
247 |
+
|
248 |
+
manuscript_json_str = clean_json_response(manuscript_json_str)
|
249 |
+
try:
|
250 |
+
manuscript_data = json.loads(manuscript_json_str)
|
251 |
+
except Exception as e:
|
252 |
+
raise Exception("Failed to parse manuscript JSON: " + str(e))
|
253 |
+
|
254 |
+
# Build segments for TTS.
|
255 |
+
segments = []
|
256 |
+
if isinstance(manuscript_data, dict):
|
257 |
+
for section in manuscript_data.values():
|
258 |
+
if isinstance(section, list):
|
259 |
+
segments.extend(section)
|
260 |
+
elif isinstance(section, dict) and "text" in section:
|
261 |
+
segments.append(section)
|
262 |
+
elif isinstance(section, str):
|
263 |
+
segments.append({"speaker": "host", "text": section})
|
264 |
+
elif isinstance(manuscript_data, list):
|
265 |
+
segments = manuscript_data
|
266 |
+
|
267 |
+
if not segments:
|
268 |
+
raise Exception("No valid segments found in manuscript JSON for TTS.")
|
269 |
+
|
270 |
+
# Step 3: Generate Podcast Audio.
|
271 |
+
if progress_bar:
|
272 |
+
progress_bar.progress(60)
|
273 |
+
podcast_audio = generate_podcast_audio(segments)
|
274 |
+
|
275 |
+
if progress_bar:
|
276 |
+
progress_bar.progress(100)
|
277 |
+
return podcast_audio
|
utils/tts_utils.py
CHANGED
@@ -36,4 +36,49 @@ def generate_tts_audio(text, voice="af_heart", speed=1.0):
|
|
36 |
elif status_json.get("status") in ["FAILED", "ERROR"]:
|
37 |
logger.error("TTS generation failed.")
|
38 |
st.error("TTS generation failed. Please try again later.")
|
39 |
-
raise Exception("TTS generation failed.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
elif status_json.get("status") in ["FAILED", "ERROR"]:
|
37 |
logger.error("TTS generation failed.")
|
38 |
st.error("TTS generation failed. Please try again later.")
|
39 |
+
raise Exception("TTS generation failed.")
|
40 |
+
|
41 |
+
def generate_podcast_audio(segments, host_voice="am_michael", expert_voice="af_bella", silence_duration_ms=300, speed=1.0):
|
42 |
+
RUNPOD_API_TOKEN = os.getenv("RUNPOD_GPU")
|
43 |
+
headers = {
|
44 |
+
'Content-Type': 'application/json',
|
45 |
+
'Authorization': f'Bearer {RUNPOD_API_TOKEN}'
|
46 |
+
}
|
47 |
+
data_payload = {
|
48 |
+
"input": {
|
49 |
+
"mode": "podcast",
|
50 |
+
"segments": segments,
|
51 |
+
"host_voice": host_voice,
|
52 |
+
"expert_voice": expert_voice,
|
53 |
+
"silence_duration_ms": silence_duration_ms,
|
54 |
+
"speed": speed
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
run_url = "https://api.runpod.ai/v2/ozz8w092oprwqx/run"
|
59 |
+
print("Podcast TTS generation started, please wait...")
|
60 |
+
response = requests.post(run_url, headers=headers, json=data_payload)
|
61 |
+
if response.status_code != 200:
|
62 |
+
raise Exception(f"RunPod API call failed with status {response.status_code}: {response.text}")
|
63 |
+
|
64 |
+
run_id = response.json().get("id")
|
65 |
+
status_url = f"https://api.runpod.ai/v2/ozz8w092oprwqx/status/{run_id}"
|
66 |
+
|
67 |
+
while True:
|
68 |
+
time.sleep(5)
|
69 |
+
status_response = requests.post(status_url, headers=headers, json=data_payload)
|
70 |
+
status_json = status_response.json()
|
71 |
+
logger.debug("Podcast TTS status: %s", status_json.get("status"))
|
72 |
+
|
73 |
+
if status_json.get("status") == "COMPLETED":
|
74 |
+
download_url = status_json.get("output", {}).get("download_url")
|
75 |
+
if download_url:
|
76 |
+
mp3_response = requests.get(download_url)
|
77 |
+
if mp3_response.status_code == 200:
|
78 |
+
print("Podcast TTS generation completed!")
|
79 |
+
return mp3_response.content
|
80 |
+
else:
|
81 |
+
raise Exception(f"Failed to download audio: {mp3_response.status_code}")
|
82 |
+
elif status_json.get("status") in ["FAILED", "ERROR"]:
|
83 |
+
logger.error("Podcast TTS generation failed.")
|
84 |
+
raise Exception("Podcast TTS generation failed.")
|