Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -320,7 +320,7 @@ def generate_markdown_slides(slides, title, speaker="Prof. AI Feynman", date="Ap
|
|
320 |
"""
|
321 |
markdown_slides.append(slide_md.strip())
|
322 |
|
323 |
-
logger.info(f"Generated Markdown slides for: {title}")
|
324 |
return markdown_slides
|
325 |
except Exception as e:
|
326 |
logger.error(f"Failed to generate Markdown slides: {str(e)}")
|
@@ -368,7 +368,7 @@ Example output for 2 slides:
|
|
368 |
model_client=model_client,
|
369 |
handoffs=["feynman_agent"],
|
370 |
system_message=f"""
|
371 |
-
You are a Script Agent. Access the JSON array of {total_slides} slides from the conversation history. Generate a narration script (1-2 sentences) for each of the {total_slides} slides, summarizing its content in a clear, academically inclined tone as a professor would deliver it. Avoid using non-verbal fillers such as "um," "you know," or "like." Output ONLY a JSON array wrapped in ```json ... ``` with exactly {total_slides} strings, one script per slide, in the same order. Ensure the JSON is valid and complete. After outputting, use the
|
372 |
Example for 3 slides:
|
373 |
```json
|
374 |
[
|
@@ -502,7 +502,7 @@ Example: 'Received {total_slides} slides and {total_slides} scripts. Lecture is
|
|
502 |
extracted_json = extract_json_from_message(message)
|
503 |
if extracted_json:
|
504 |
slides = extracted_json
|
505 |
-
logger.info("Slide Agent generated %d slides", len(slides))
|
506 |
if len(slides) != total_slides:
|
507 |
if slide_retry_count < max_retries:
|
508 |
slide_retry_count += 1
|
@@ -547,7 +547,7 @@ Example: 'Received {total_slides} slides and {total_slides} scripts. Lecture is
|
|
547 |
extracted_json = extract_json_from_message(message)
|
548 |
if extracted_json:
|
549 |
scripts = extracted_json
|
550 |
-
logger.info("Script Agent generated scripts for %d slides", len(scripts))
|
551 |
for i, script in enumerate(scripts):
|
552 |
script_file = os.path.join(OUTPUT_DIR, f"slide_{i+1}_script.txt")
|
553 |
try:
|
@@ -661,7 +661,7 @@ Example: 'Received {total_slides} slides and {total_slides} scripts. Lecture is
|
|
661 |
return
|
662 |
|
663 |
audio_files = []
|
664 |
-
|
665 |
validated_speaker_wav = await validate_and_convert_speaker_audio(speaker_audio)
|
666 |
if not validated_speaker_wav:
|
667 |
logger.error("Invalid speaker audio after conversion, skipping TTS")
|
@@ -691,9 +691,9 @@ Example: 'Received {total_slides} slides and {total_slides} scripts. Lecture is
|
|
691 |
if not cleaned_script:
|
692 |
logger.error("Skipping audio for slide %d due to empty or invalid script", i + 1)
|
693 |
audio_files.append(None)
|
694 |
-
|
695 |
progress = 90 + ((i + 1) / len(scripts)) * 10
|
696 |
-
label = f"
|
697 |
yield (
|
698 |
html_with_progress(label, progress),
|
699 |
[]
|
@@ -715,11 +715,12 @@ Example: 'Received {total_slides} slides and {total_slides} scripts. Lecture is
|
|
715 |
if not success:
|
716 |
raise RuntimeError("TTS generation failed")
|
717 |
|
718 |
-
logger.info("
|
719 |
audio_files.append(audio_file)
|
720 |
-
|
|
|
721 |
progress = 90 + ((i + 1) / len(scripts)) * 10
|
722 |
-
label = f"
|
723 |
yield (
|
724 |
html_with_progress(label, progress),
|
725 |
[]
|
@@ -731,9 +732,9 @@ Example: 'Received {total_slides} slides and {total_slides} scripts. Lecture is
|
|
731 |
if attempt == max_audio_retries:
|
732 |
logger.error("Max retries reached for slide %d, skipping", i + 1)
|
733 |
audio_files.append(None)
|
734 |
-
|
735 |
progress = 90 + ((i + 1) / len(scripts)) * 10
|
736 |
-
label = f"
|
737 |
yield (
|
738 |
html_with_progress(label, progress),
|
739 |
[]
|
@@ -748,18 +749,16 @@ Example: 'Received {total_slides} slides and {total_slides} scripts. Lecture is
|
|
748 |
|
749 |
# Generate audio timeline with playable audio elements
|
750 |
audio_timeline = ""
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
# Use the file path directly; Gradio will serve it via gr.File
|
755 |
-
audio_urls.append(audio_file)
|
756 |
-
audio_timeline += f'<audio id="audio-{i+1}" controls src="" style="display: inline-block; margin: 0 10px; width: 200px;"></audio>'
|
757 |
else:
|
758 |
audio_timeline += f'<span id="audio-{i+1}" style="display: inline-block; margin: 0 10px;">slide_{i+1}.mp3 (not generated)</span>'
|
759 |
|
760 |
slides_info = json.dumps({"slides": markdown_slides, "audioFiles": audio_urls})
|
761 |
|
762 |
html_output = f"""
|
|
|
763 |
<div id="lecture-container" style="height: 700px; border: 1px solid #ddd; border-radius: 8px; display: flex; flex-direction: column; justify-content: space-between;">
|
764 |
<div id="slide-content" style="flex: 1; overflow: auto; padding: 20px; text-align: center; background-color: #fff; color: #333;">
|
765 |
<!-- Slides will be rendered here -->
|
@@ -782,20 +781,20 @@ Example: 'Received {total_slides} slides and {total_slides} scripts. Lecture is
|
|
782 |
const totalSlides = lectureData.slides.length;
|
783 |
let audioElements = [];
|
784 |
|
785 |
-
// Populate audio elements
|
786 |
for (let i = 0; i < totalSlides; i++) {{
|
787 |
const audio = document.getElementById(`audio-${{i+1}}`);
|
788 |
-
if (audio && lectureData.audioFiles[i]) {{
|
789 |
-
audio.src = lectureData.audioFiles[i];
|
790 |
-
}}
|
791 |
audioElements.push(audio);
|
792 |
}}
|
793 |
|
794 |
function renderSlide() {{
|
795 |
const slideContent = document.getElementById('slide-content');
|
796 |
if (lectureData.slides[currentSlide]) {{
|
797 |
-
|
798 |
-
|
|
|
|
|
|
|
799 |
}} else {{
|
800 |
slideContent.innerHTML = '<h2>No slide content available</h2>';
|
801 |
console.log("No slide content for index:", currentSlide);
|
@@ -926,4 +925,4 @@ with gr.Blocks(title="Agent Feynman") as demo:
|
|
926 |
)
|
927 |
|
928 |
if __name__ == "__main__":
|
929 |
-
demo.launch()
|
|
|
320 |
"""
|
321 |
markdown_slides.append(slide_md.strip())
|
322 |
|
323 |
+
logger.info(f"Generated Markdown slides for: {title}: {markdown_slides}")
|
324 |
return markdown_slides
|
325 |
except Exception as e:
|
326 |
logger.error(f"Failed to generate Markdown slides: {str(e)}")
|
|
|
368 |
model_client=model_client,
|
369 |
handoffs=["feynman_agent"],
|
370 |
system_message=f"""
|
371 |
+
You are a Script Agent. Access the JSON array of {total_slides} slides from the conversation history. Generate a narration script (1-2 sentences) for each of the {total_slides} slides, summarizing its content in a clear, academically inclined tone as a professor would deliver it. Avoid using non-verbal fillers such as "um," "you know," or "like." Output ONLY a JSON array wrapped in ```json ... ``` with exactly {total_slides} strings, one script per slide, in the same order. Ensure the JSON is valid and complete. After outputting, use the handoff_to_feynman_agent tool. If scripts cannot be generated, retry once.
|
372 |
Example for 3 slides:
|
373 |
```json
|
374 |
[
|
|
|
502 |
extracted_json = extract_json_from_message(message)
|
503 |
if extracted_json:
|
504 |
slides = extracted_json
|
505 |
+
logger.info("Slide Agent generated %d slides: %s", len(slides), slides)
|
506 |
if len(slides) != total_slides:
|
507 |
if slide_retry_count < max_retries:
|
508 |
slide_retry_count += 1
|
|
|
547 |
extracted_json = extract_json_from_message(message)
|
548 |
if extracted_json:
|
549 |
scripts = extracted_json
|
550 |
+
logger.info("Script Agent generated scripts for %d slides: %s", len(scripts), scripts)
|
551 |
for i, script in enumerate(scripts):
|
552 |
script_file = os.path.join(OUTPUT_DIR, f"slide_{i+1}_script.txt")
|
553 |
try:
|
|
|
661 |
return
|
662 |
|
663 |
audio_files = []
|
664 |
+
audio_urls = []
|
665 |
validated_speaker_wav = await validate_and_convert_speaker_audio(speaker_audio)
|
666 |
if not validated_speaker_wav:
|
667 |
logger.error("Invalid speaker audio after conversion, skipping TTS")
|
|
|
691 |
if not cleaned_script:
|
692 |
logger.error("Skipping audio for slide %d due to empty or invalid script", i + 1)
|
693 |
audio_files.append(None)
|
694 |
+
audio_urls.append(None)
|
695 |
progress = 90 + ((i + 1) / len(scripts)) * 10
|
696 |
+
label = f"Generated audio for slide {i + 1}/{len(scripts)}..."
|
697 |
yield (
|
698 |
html_with_progress(label, progress),
|
699 |
[]
|
|
|
715 |
if not success:
|
716 |
raise RuntimeError("TTS generation failed")
|
717 |
|
718 |
+
logger.info("Generated audio for slide %d: %s", i + 1, audio_file)
|
719 |
audio_files.append(audio_file)
|
720 |
+
# Use Gradio's file serving URL
|
721 |
+
audio_urls.append(f"/gradio_api/file={audio_file}")
|
722 |
progress = 90 + ((i + 1) / len(scripts)) * 10
|
723 |
+
label = f"Generated audio for slide {i + 1}/{len(scripts)}..."
|
724 |
yield (
|
725 |
html_with_progress(label, progress),
|
726 |
[]
|
|
|
732 |
if attempt == max_audio_retries:
|
733 |
logger.error("Max retries reached for slide %d, skipping", i + 1)
|
734 |
audio_files.append(None)
|
735 |
+
audio_urls.append(None)
|
736 |
progress = 90 + ((i + 1) / len(scripts)) * 10
|
737 |
+
label = f"Generated audio for slide {i + 1}/{len(scripts)}..."
|
738 |
yield (
|
739 |
html_with_progress(label, progress),
|
740 |
[]
|
|
|
749 |
|
750 |
# Generate audio timeline with playable audio elements
|
751 |
audio_timeline = ""
|
752 |
+
for i, audio_url in enumerate(audio_urls):
|
753 |
+
if audio_url:
|
754 |
+
audio_timeline += f'<audio id="audio-{i+1}" controls src="{audio_url}" style="display: inline-block; margin: 0 10px; width: 200px;"></audio>'
|
|
|
|
|
|
|
755 |
else:
|
756 |
audio_timeline += f'<span id="audio-{i+1}" style="display: inline-block; margin: 0 10px;">slide_{i+1}.mp3 (not generated)</span>'
|
757 |
|
758 |
slides_info = json.dumps({"slides": markdown_slides, "audioFiles": audio_urls})
|
759 |
|
760 |
html_output = f"""
|
761 |
+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/marked.min.js"></script>
|
762 |
<div id="lecture-container" style="height: 700px; border: 1px solid #ddd; border-radius: 8px; display: flex; flex-direction: column; justify-content: space-between;">
|
763 |
<div id="slide-content" style="flex: 1; overflow: auto; padding: 20px; text-align: center; background-color: #fff; color: #333;">
|
764 |
<!-- Slides will be rendered here -->
|
|
|
781 |
const totalSlides = lectureData.slides.length;
|
782 |
let audioElements = [];
|
783 |
|
784 |
+
// Populate audio elements
|
785 |
for (let i = 0; i < totalSlides; i++) {{
|
786 |
const audio = document.getElementById(`audio-${{i+1}}`);
|
|
|
|
|
|
|
787 |
audioElements.push(audio);
|
788 |
}}
|
789 |
|
790 |
function renderSlide() {{
|
791 |
const slideContent = document.getElementById('slide-content');
|
792 |
if (lectureData.slides[currentSlide]) {{
|
793 |
+
const markdownText = lectureData.slides[currentSlide];
|
794 |
+
const htmlContent = marked.parse(markdownText);
|
795 |
+
slideContent.innerHTML = htmlContent;
|
796 |
+
console.log("Rendering slide:", markdownText);
|
797 |
+
console.log("Rendered HTML:", htmlContent);
|
798 |
}} else {{
|
799 |
slideContent.innerHTML = '<h2>No slide content available</h2>';
|
800 |
console.log("No slide content for index:", currentSlide);
|
|
|
925 |
)
|
926 |
|
927 |
if __name__ == "__main__":
|
928 |
+
demo.launch(allowed_paths=[OUTPUT_DIR], max_file_size="5mb")
|