Jaward commited on
Commit
8b5a6cc
·
verified ·
1 Parent(s): f37a4ab

added support for lecture styles or preferences

Browse files
Files changed (1) hide show
  1. app.py +33 -17
app.py CHANGED
@@ -94,7 +94,7 @@ def search_web(query: str, serpapi_key: str) -> str:
94
  logger.error("Unexpected error during search: %s", str(e))
95
  return None
96
 
97
- # Custom function to render Markdown to HTML
98
  def render_md_to_html(md_content: str) -> str:
99
  try:
100
  html_content = markdown.markdown(md_content, extensions=['extra', 'fenced_code', 'tables'])
@@ -103,7 +103,7 @@ def render_md_to_html(md_content: str) -> str:
103
  logger.error("Failed to render Markdown to HTML: %s", str(e))
104
  return "<div>Error rendering content</div>"
105
 
106
- # Define create_slides tool for generating HTML slides
107
  def create_slides(slides: list[dict], title: str, output_dir: str = OUTPUT_DIR) -> list[str]:
108
  try:
109
  html_files = []
@@ -145,7 +145,7 @@ def create_slides(slides: list[dict], title: str, output_dir: str = OUTPUT_DIR)
145
  logger.error("Failed to create HTML slides: %s", str(e))
146
  return []
147
 
148
- # Define helper function for progress HTML
149
  def html_with_progress(label, progress):
150
  return f"""
151
  <div style="display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100%; min-height: 700px; padding: 20px; text-align: center; border: 1px solid #ddd; border-radius: 8px;">
@@ -201,7 +201,7 @@ def clean_script_text(script):
201
  logger.info("Cleaned script: %s", script)
202
  return script
203
 
204
- # Helper function to validate and convert speaker audio
205
  async def validate_and_convert_speaker_audio(speaker_audio):
206
  if not speaker_audio or not os.path.exists(speaker_audio):
207
  logger.warning("Speaker audio file does not exist: %s. Using default voice.", speaker_audio)
@@ -388,7 +388,7 @@ def get_gradio_file_url(local_path):
388
  return f"/gradio_api/file={relative_path}"
389
 
390
  # Async generate lecture materials and audio
391
- async def on_generate(api_service, api_key, serpapi_key, title, lecture_content_description, lecture_type, speaker_audio, num_slides):
392
  model_client = get_model_client(api_service, api_key)
393
 
394
  if os.path.exists(OUTPUT_DIR):
@@ -427,7 +427,12 @@ You are a Slide Agent. Using the research from the conversation history and the
427
 
428
  - The Introduction slide (first slide) should have the title "{title}" and content containing only the lecture title, speaker name (Prof. AI Feynman), and date {date}, centered, in plain text.
429
  - The Closing slide (last slide) should have the title "Closing" and content containing only "The End\nThank you", centered, in plain text.
430
- - The remaining {content_slides} slides should be content slides based on the lecture description and audience type, with meaningful titles and content in valid Markdown format.
 
 
 
 
 
431
 
432
  Output ONLY a JSON array wrapped in ```json ... ``` in a TextMessage, where each slide is an object with 'title' and 'content' keys. After generating the JSON, use the create_slides tool to produce HTML slides, then use the handoff_to_script_agent tool to pass the task to the Script Agent. Do not include any explanatory text or other messages.
433
 
@@ -448,11 +453,15 @@ Example output for 1 content slide (total 3 slides):
448
  model_client=model_client,
449
  handoffs=["feynman_agent"],
450
  system_message=f"""
451
- You are a Script Agent model after Richard Feynman. Access the JSON array of {total_slides} slides from the conversation history, which includes an Introduction slide, {content_slides} content slides, and a Closing slide. Generate a narration script (1-2 sentences) for each of the {total_slides} slides, summarizing its content in a clear, academically inclined tone, with humour as professor feynman would deliver it. Ensure the lecture is engaging and covers the fundamental requirements of the topic. Overall keep lecture engaging yet highly informative, covering the fundamental requirements of the topic. 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.
 
 
 
 
 
 
452
 
453
- - For the Introduction slide, the script should be a welcoming message introducing the lecture.
454
- - For the Closing slide, the script should be a brief farewell and thank you message.
455
- - For the content slides, summarize the slide content academically.
456
 
457
  Example for 3 slides (1 content slide):
458
  ```json
@@ -470,8 +479,8 @@ Example for 3 slides (1 content slide):
470
  model_client=model_client,
471
  handoffs=[],
472
  system_message=f"""
473
- You are Agent Feynman. Review the slides and scripts from the conversation history to ensure coherence, completeness, and that exactly {total_slides} slides and {total_slides} scripts are received, including the Introduction and Closing slides. Verify that HTML slide files exist in the outputs directory. Output a confirmation message summarizing the number of slides, scripts, and HTML files status. If slides, scripts, or HTML files are missing, invalid, or do not match the expected count ({total_slides}), report the issue clearly. Use 'TERMINATE' to signal completion.
474
- Example: 'Received {total_slides} slides, {total_slides} scripts, and HTML files. Lecture is coherent. TERMINATE'
475
  """)
476
 
477
  swarm = Swarm(
@@ -480,7 +489,7 @@ Example: 'Received {total_slides} slides, {total_slides} scripts, and HTML files
480
  )
481
 
482
  progress = 0
483
- label = "Research: in progress..."
484
  yield (
485
  html_with_progress(label, progress),
486
  []
@@ -491,10 +500,11 @@ Example: 'Received {total_slides} slides, {total_slides} scripts, and HTML files
491
  Lecture Title: {title}
492
  Lecture Content Description: {lecture_content_description}
493
  Audience: {lecture_type}
 
494
  Number of Content Slides: {content_slides}
495
  Please start by researching the topic, or proceed without research if search is unavailable.
496
  """
497
- logger.info("Starting lecture generation for title: %s with %d content slides (total %d slides)", title, content_slides, total_slides)
498
 
499
  slides = None
500
  scripts = None
@@ -763,14 +773,15 @@ Example: 'Received {total_slides} slides, {total_slides} scripts, and HTML files
763
  f.write(cleaned_script or "")
764
  logger.info("Saved script to %s: %s", script_file, cleaned_script)
765
  except Exception as e:
766
- logger.error("Error saving script to %s: %s", script_file, str(e))
 
767
 
768
  if not cleaned_script:
769
  logger.error("Skipping audio for slide %d due to empty or invalid script", i + 1)
770
  audio_files.append(None)
771
  audio_urls[i] = None
772
  progress = 90 + ((i + 1) / len(scripts)) * 10
773
- label = f"Generating speech for slide {i + 1}/{len(scripts)}..."
774
  yield (
775
  html_with_progress(label, progress),
776
  file_paths
@@ -1249,6 +1260,11 @@ with gr.Blocks(
1249
  title = gr.Textbox(label="Lecture Title", placeholder="e.g. Introduction to AI")
1250
  lecture_content_description = gr.Textbox(label="Lecture Content Description", placeholder="e.g. Focus on recent advancements")
1251
  lecture_type = gr.Dropdown(["Conference", "University", "High school"], label="Audience", value="University")
 
 
 
 
 
1252
  api_service = gr.Dropdown(
1253
  choices=[
1254
  "Azure AI Foundry",
@@ -1283,7 +1299,7 @@ with gr.Blocks(
1283
 
1284
  generate_btn.click(
1285
  fn=on_generate,
1286
- inputs=[api_service, api_key, serpapi_key, title, lecture_content_description, lecture_type, speaker_audio, num_slides],
1287
  outputs=[slide_display, file_output]
1288
  )
1289
 
 
94
  logger.error("Unexpected error during search: %s", str(e))
95
  return None
96
 
97
+ # Custom renderer for slides - Markdown to HTML
98
  def render_md_to_html(md_content: str) -> str:
99
  try:
100
  html_content = markdown.markdown(md_content, extensions=['extra', 'fenced_code', 'tables'])
 
103
  logger.error("Failed to render Markdown to HTML: %s", str(e))
104
  return "<div>Error rendering content</div>"
105
 
106
+ # Slide tool for generating HTML slides used by slide_agent
107
  def create_slides(slides: list[dict], title: str, output_dir: str = OUTPUT_DIR) -> list[str]:
108
  try:
109
  html_files = []
 
145
  logger.error("Failed to create HTML slides: %s", str(e))
146
  return []
147
 
148
+ # Dynamic progress bar
149
  def html_with_progress(label, progress):
150
  return f"""
151
  <div style="display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100%; min-height: 700px; padding: 20px; text-align: center; border: 1px solid #ddd; border-radius: 8px;">
 
201
  logger.info("Cleaned script: %s", script)
202
  return script
203
 
204
+ # Helper to validate and convert speaker audio
205
  async def validate_and_convert_speaker_audio(speaker_audio):
206
  if not speaker_audio or not os.path.exists(speaker_audio):
207
  logger.warning("Speaker audio file does not exist: %s. Using default voice.", speaker_audio)
 
388
  return f"/gradio_api/file={relative_path}"
389
 
390
  # Async generate lecture materials and audio
391
+ async def on_generate(api_service, api_key, serpapi_key, title, lecture_content_description, lecture_type, lecture_style, speaker_audio, num_slides):
392
  model_client = get_model_client(api_service, api_key)
393
 
394
  if os.path.exists(OUTPUT_DIR):
 
427
 
428
  - The Introduction slide (first slide) should have the title "{title}" and content containing only the lecture title, speaker name (Prof. AI Feynman), and date {date}, centered, in plain text.
429
  - The Closing slide (last slide) should have the title "Closing" and content containing only "The End\nThank you", centered, in plain text.
430
+ - The remaining {content_slides} slides should be content slides based on the lecture description, audience type, and lecture style ({lecture_style}), with meaningful titles and content in valid Markdown format. Adapt the content to the lecture style to suit diverse learners:
431
+ - Feynman: Explains complex ideas with simplicity, clarity, and enthusiasm, emulating Richard Feynman's teaching style.
432
+ - Socratic: Poses thought-provoking questions to guide learners to insights without requiring direct interaction.
433
+ - Humorous: Infuses wit and light-hearted anecdotes to make content engaging and memorable.
434
+ - Inspirational - Motivating: Uses motivational language and visionary ideas to spark enthusiasm and curiosity.
435
+ - Reflective: Encourages introspection with a calm, contemplative tone to deepen understanding.
436
 
437
  Output ONLY a JSON array wrapped in ```json ... ``` in a TextMessage, where each slide is an object with 'title' and 'content' keys. After generating the JSON, use the create_slides tool to produce HTML slides, then use the handoff_to_script_agent tool to pass the task to the Script Agent. Do not include any explanatory text or other messages.
438
 
 
453
  model_client=model_client,
454
  handoffs=["feynman_agent"],
455
  system_message=f"""
456
+ You are a Script Agent modeled after Richard Feynman. Access the JSON array of {total_slides} slides from the conversation history, which includes an Introduction slide, {content_slides} content slides, and a Closing slide. Generate a narration script (1-2 sentences) for each of the {total_slides} slides, summarizing its content in a clear, academically inclined tone, with humor as Professor Feynman would deliver it. Ensure the lecture is engaging, covers the fundamental requirements of the topic, and aligns with the lecture style ({lecture_style}) to suit diverse learners:
457
+ - Feynman: Explains complex ideas with simplicity, clarity, and enthusiasm, emulating Richard Feynman's teaching style.
458
+ - Socratic: Poses thought-provoking questions to guide learners to insights without requiring direct interaction.
459
+ - Narrative: Use storytelling or analogies to explain concepts.
460
+ - Analytical: Focus on data, equations, or logical breakdowns.
461
+ - Humorous: Infuses wit and light-hearted anecdotes to make content engaging and memorable.
462
+ - Reflective: Encourages introspection with a calm, contemplative tone to deepen understanding.
463
 
464
+ 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.
 
 
465
 
466
  Example for 3 slides (1 content slide):
467
  ```json
 
479
  model_client=model_client,
480
  handoffs=[],
481
  system_message=f"""
482
+ You are Agent Feynman. Review the slides and scripts from the conversation history to ensure coherence, completeness, and that exactly {total_slides} slides and {total_slides} scripts are received, including the Introduction and Closing slides. Verify that HTML slide files exist in the outputs directory and align with the lecture style ({lecture_style}). Output a confirmation message summarizing the number of slides, scripts, and HTML files status. If slides, scripts, or HTML files are missing, invalid, or do not match the expected count ({total_slides}), report the issue clearly. Use 'TERMINATE' to signal completion.
483
+ Example: 'Received {total_slides} slides, {total_slides} scripts, and HTML files. Lecture is coherent and aligns with {lecture_style} style. TERMINATE'
484
  """)
485
 
486
  swarm = Swarm(
 
489
  )
490
 
491
  progress = 0
492
+ label = "Researching lecture topic..."
493
  yield (
494
  html_with_progress(label, progress),
495
  []
 
500
  Lecture Title: {title}
501
  Lecture Content Description: {lecture_content_description}
502
  Audience: {lecture_type}
503
+ Lecture Style: {lecture_style}
504
  Number of Content Slides: {content_slides}
505
  Please start by researching the topic, or proceed without research if search is unavailable.
506
  """
507
+ logger.info("Starting lecture generation for title: %s with %d content slides (total %d slides), style: %s", title, content_slides, total_slides, lecture_style)
508
 
509
  slides = None
510
  scripts = None
 
773
  f.write(cleaned_script or "")
774
  logger.info("Saved script to %s: %s", script_file, cleaned_script)
775
  except Exception as e:
776
+ logger.error("Error saving script to %s: %s",
777
+ script_file, str(e))
778
 
779
  if not cleaned_script:
780
  logger.error("Skipping audio for slide %d due to empty or invalid script", i + 1)
781
  audio_files.append(None)
782
  audio_urls[i] = None
783
  progress = 90 + ((i + 1) / len(scripts)) * 10
784
+ label = f"Generating lecture speech for slide {i + 1}/{len(scripts)}..."
785
  yield (
786
  html_with_progress(label, progress),
787
  file_paths
 
1260
  title = gr.Textbox(label="Lecture Title", placeholder="e.g. Introduction to AI")
1261
  lecture_content_description = gr.Textbox(label="Lecture Content Description", placeholder="e.g. Focus on recent advancements")
1262
  lecture_type = gr.Dropdown(["Conference", "University", "High school"], label="Audience", value="University")
1263
+ lecture_style = gr.Dropdown(
1264
+ ["Feynman - Simplifies complex ideas with enthusiasm", "Socratic - Guides insights with probing questions", "Inspirational - Sparks enthusiasm with visionary ideas", "Reflective - Promotes introspection with a calm tone", "Humorous - Uses wit and anecdotes for engaging content"],
1265
+ label="Lecture Style",
1266
+ value="Feynman - Simplifies complex ideas with enthusiasm"
1267
+ )
1268
  api_service = gr.Dropdown(
1269
  choices=[
1270
  "Azure AI Foundry",
 
1299
 
1300
  generate_btn.click(
1301
  fn=on_generate,
1302
+ inputs=[api_service, api_key, serpapi_key, title, lecture_content_description, lecture_type, lecture_style, speaker_audio, num_slides],
1303
  outputs=[slide_display, file_output]
1304
  )
1305