chenemii commited on
Commit
2f3c69c
·
1 Parent(s): 7e84f86

Update LLM analysis format and fix navigation issues

Browse files

- Updated LLM analyzer to use clean format without emojis or em dashes
- Changed to numbered format (1-3) for Strengths, Areas for Improvement, and Practice Tips
- Updated practice tips to only use drills from golf articles CSV
- Removed putting/short game drills, kept only swing technique and setup drills
- Fixed navigation issue between improvements page and chatbot page
- Added session state safeguards to prevent unnecessary video re-uploads
- Enhanced chatbot access with graceful fallbacks for missing analysis data

app/golf_swing_articles_complete.csv ADDED
The diff for this file is too large to render. See raw diff
 
app/models/llm_analyzer.py CHANGED
@@ -531,36 +531,40 @@ Use the benchmarks above to guide your evaluation. Follow this exact format:
531
  **PERFORMANCE_CLASSIFICATION:** [XX%]
532
  (XX = number from 10% to 100%)
533
 
534
- **STRENGTHS:**
535
- List exactly 3 strengths. Each should:
536
- - Be qualitative (no numbers)
 
 
537
  - Compare to professional benchmarks
538
- - Highlight what's working well and when (e.g. during backswing, at impact)
539
- - Use a positive, supportive tone
540
 
541
  Example:
542
- Your shoulder rotation during the backswing shows strong upper body mobility, similar to professional swings.
 
 
 
543
 
544
- **WEAKNESSES:**
545
- List exactly 3 areas for improvement. Each should:
546
- - Use numbers when necessary, and only use 1 number per weakness (for example, the difference between your metric and the professional standard)
547
- - Describe the impact on power, accuracy, or consistency
548
- - Use phrases like "less than optimal" or "more than ideal"
549
- - Don't suggest fixes here—save those for the next section
550
 
551
  Example:
552
- Your hip rotation is less than optimal, which may reduce your power through the downswing.
 
553
 
554
- **PRIORITY_IMPROVEMENTS:**
555
- List exactly 3 improvement areas. Each should:
556
- - Include the topic name
557
- - Explain what to improve and when in the swing
558
- - Reference benchmarks when relevant, without being too technical
559
- - Use coaching-style language (e.g. "try increasing...")
560
- - Emphasize benefits
561
 
562
  Example:
563
- Hip Mobility: Try increasing your hip rotation during the downswing to unlock more lower body power.
564
 
565
  **SCORING GUIDELINES (Use to help decide % score)**
566
 
@@ -585,12 +589,26 @@ Hip Mobility: Try increasing your hip rotation during the downswing to unlock mo
585
  - **10–39%**: Novice
586
 
587
  **STYLE & FORMATTING RULES:**
588
- - Use these headers: PERFORMANCE_CLASSIFICATION, STRENGTHS, WEAKNESSES, PRIORITY_IMPROVEMENTS
589
- - Avoid statistics in strengths/weaknesses (okay in improvements if helpful)
 
 
590
  - Tie all points to professional standards
591
  - Use a positive, coaching tone throughout
592
  - Avoid saying "perfect" — say "strong" or "meets standards"
593
  - Focus on biomechanics, not timing (e.g. tempo, frame count)
 
 
 
 
 
 
 
 
 
 
 
 
594
  """
595
 
596
  return prompt
@@ -637,23 +655,29 @@ def parse_and_format_analysis(raw_analysis):
637
  break
638
 
639
  # Extract strengths using the new structured format
640
- strengths_match = re.search(r'\*\*STRENGTHS:\*\*\s*(.*?)(?=\*\*WEAKNESSES:\*\*|\*\*PRIORITY_IMPROVEMENTS:\*\*|$)', raw_analysis, re.IGNORECASE | re.DOTALL)
641
  if strengths_match:
642
  strengths_text = strengths_match.group(1)
643
- # Extract bullet points
644
- strength_items = re.findall(r'•\s*([^\n]+)', strengths_text)
645
- formatted_analysis['strengths'] = [item.strip() for item in strength_items if item.strip()]
646
 
647
- # Extract weaknesses using the new structured format
648
- weaknesses_match = re.search(r'\*\*WEAKNESSES:\*\*\s*(.*?)(?=\*\*PRIORITY_IMPROVEMENTS:\*\*|$)', raw_analysis, re.IGNORECASE | re.DOTALL)
649
  if weaknesses_match:
650
  weaknesses_text = weaknesses_match.group(1)
651
- # Extract bullet points
652
- weakness_items = re.findall(r'•\s*([^\n]+)', weaknesses_text)
653
- formatted_analysis['weaknesses'] = [item.strip() for item in weakness_items if item.strip()]
 
 
 
 
 
 
654
 
655
- # Extract priority improvements using the new structured format
656
- priority_match = re.search(r'\*\*PRIORITY_IMPROVEMENTS:\*\*\s*(.*?)$', raw_analysis, re.IGNORECASE | re.DOTALL)
657
  if priority_match:
658
  priority_text = priority_match.group(1)
659
  # First try to parse numbered format: "1. Topic: Description"
@@ -680,11 +704,11 @@ def parse_and_format_analysis(raw_analysis):
680
 
681
  # Ensure exactly 3 priority improvements with distinct topics
682
  if len(formatted_analysis['priority_improvements']) < 3:
683
- # Define 3 distinct improvement areas
684
  common_improvements = [
685
- "Hip Mobility: Try increasing your hip rotation during the downswing to unlock more lower body power and improve overall swing efficiency.",
686
- "Arm Extension: Focus on achieving better arm extension at impact to improve power transfer and ball striking consistency.",
687
- "Weight Transfer: Work on shifting your weight more effectively from back foot to front foot during the swing to enhance balance and power generation."
688
  ]
689
 
690
  # Get existing topics to avoid duplicates
@@ -784,27 +808,27 @@ def parse_and_format_analysis(raw_analysis):
784
  percentage = formatted_analysis['classification']
785
  if percentage >= 80:
786
  formatted_analysis['priority_improvements'] = [
787
- {'rank': 1, 'description': 'Technical Refinement: Fine-tune specific mechanics to achieve consistency at the highest level.'},
788
- {'rank': 2, 'description': 'Performance Optimization: Focus on maximizing efficiency and power transfer.'},
789
- {'rank': 3, 'description': 'Competitive Preparation: Enhance mental game and course management skills.'}
790
  ]
791
  elif percentage >= 60:
792
  formatted_analysis['priority_improvements'] = [
793
- {'rank': 1, 'description': 'Kinematic Sequence Enhancement: Improve body rotation coordination to generate more power and consistency.'},
794
- {'rank': 2, 'description': 'Clubface Control: Enhance swing path consistency for better ball striking accuracy.'},
795
- {'rank': 3, 'description': 'Energy Transfer Efficiency: Optimize power transfer throughout the swing to maximize distance.'}
796
  ]
797
  elif percentage >= 40:
798
  formatted_analysis['priority_improvements'] = [
799
- {'rank': 1, 'description': 'Fundamental Mechanics: Establish consistent posture, grip, and setup positions.'},
800
- {'rank': 2, 'description': 'Body Rotation Development: Improve hip and shoulder turn coordination.'},
801
- {'rank': 3, 'description': 'Weight Transfer: Develop proper weight shift from back foot to front foot during swing.'}
802
  ]
803
  else: # Below 40%
804
  formatted_analysis['priority_improvements'] = [
805
- {'rank': 1, 'description': 'Basic Setup and Posture: Focus on establishing proper spine angle and athletic stance.'},
806
- {'rank': 2, 'description': 'Fundamental Swing Motion: Develop basic backswing and downswing mechanics.'},
807
- {'rank': 3, 'description': 'Balance and Stability: Improve overall balance throughout the swing motion.'}
808
  ]
809
 
810
  return formatted_analysis
@@ -823,7 +847,7 @@ def display_formatted_analysis(analysis_data):
823
  # Display classification in black bolded header
824
  st.markdown(f"""
825
  <h2 style='color: black; font-weight: bold; text-align: center; margin-bottom: 20px;'>
826
- 🎯 Performance Score: {user_percentage}%
827
  </h2>
828
  """, unsafe_allow_html=True)
829
 
@@ -904,38 +928,46 @@ def display_formatted_analysis(analysis_data):
904
  st.markdown("---")
905
 
906
  # 2. Strengths and Weaknesses Table
907
- st.subheader("⚖️ Strengths & Areas for Improvement")
 
 
 
 
 
 
 
 
 
908
 
909
- # Create two columns for the table with a visual divider
910
  col_left, col_divider, col_right = st.columns([5, 1, 5])
911
 
912
  with col_left:
913
  st.markdown("""
914
  <div style='background-color: #e8f5e8; padding: 15px; border-radius: 10px; height: 100%;'>
915
- <h4 style='color: #2d5a2d; margin-top: 0;'>✅ Strengths</h4>
916
  """, unsafe_allow_html=True)
917
- for strength in analysis_data['strengths']:
918
- st.markdown(f" {strength}")
919
  st.markdown("</div>", unsafe_allow_html=True)
920
 
921
  with col_divider:
922
  st.markdown("""
923
- <div style='width: 2px; background-color: #ddd; height: 200px; margin: 20px auto;'></div>
924
  """, unsafe_allow_html=True)
925
 
926
  with col_right:
927
  st.markdown("""
928
  <div style='background-color: #fff5e6; padding: 15px; border-radius: 10px; height: 100%;'>
929
- <h4 style='color: #cc6600; margin-top: 0;'>⚠️ Areas for Improvement</h4>
930
  """, unsafe_allow_html=True)
931
- for weakness in analysis_data['weaknesses']:
932
- st.markdown(f" {weakness}")
933
  st.markdown("</div>", unsafe_allow_html=True)
934
 
935
  st.markdown("---")
936
 
937
- # 3. Priority Improvement Areas
938
- st.subheader("🎯 Priority Improvement Areas")
939
 
940
  for priority in sorted(analysis_data['priority_improvements'], key=lambda x: x['rank']):
941
  rank = priority['rank']
 
531
  **PERFORMANCE_CLASSIFICATION:** [XX%]
532
  (XX = number from 10% to 100%)
533
 
534
+ **Strengths**
535
+
536
+ List exactly 3 strengths numbered 1-3. Each should:
537
+ - Include the topic name followed by a colon
538
+ - Be qualitative and positive
539
  - Compare to professional benchmarks
540
+ - Highlight what's working well and the benefit
541
+ - Use supportive tone
542
 
543
  Example:
544
+ 1. Arm Extension:
545
+ Excellent extension at impact, this maintains a wide swing arc, just like elite players.
546
+
547
+ **Areas for Improvement**
548
 
549
+ List exactly 3 areas for improvement numbered 1-3. Each should:
550
+ - Include the topic name and your specific metric vs pro range
551
+ - Use format: "Topic Your metric, pro range"
552
+ - Follow with explanation of impact and drill suggestion
553
+ - Focus on what needs work and why
 
554
 
555
  Example:
556
+ 1. Hip Rotation Only 12.7°, pros range from 25–90°
557
+ Try drills that increase hip mobility during backswing. More rotation = more power and distance.
558
 
559
+ **Practice Tips**
560
+
561
+ List exactly 3 practice tips numbered 1-3. Each should:
562
+ - Include the topic name followed by "Drill:"
563
+ - Provide specific, actionable drill instructions
564
+ - Be easy to understand and practice
 
565
 
566
  Example:
567
+ 1. Ground Force Timing: Practice pushing against the ground earlier in the downswing to improve power transfer.
568
 
569
  **SCORING GUIDELINES (Use to help decide % score)**
570
 
 
589
  - **10–39%**: Novice
590
 
591
  **STYLE & FORMATTING RULES:**
592
+ - Use these headers: PERFORMANCE_CLASSIFICATION, Strengths, Areas for Improvement, Practice Tips
593
+ - No emojis anywhere in the response
594
+ - No em dashes, use colons for topic separation
595
+ - Use numbered lists (1-3) for each section
596
  - Tie all points to professional standards
597
  - Use a positive, coaching tone throughout
598
  - Avoid saying "perfect" — say "strong" or "meets standards"
599
  - Focus on biomechanics, not timing (e.g. tempo, frame count)
600
+
601
+ **PRACTICE TIPS MUST USE ONLY THESE PROVEN SWING DRILLS:**
602
+ - Ground Force Timing (push against ground earlier in downswing)
603
+ - Halfway Back Drill (check club position when lead arm parallel to ground)
604
+ - Takeaway Drill (practice one-piece takeaway movement)
605
+ - Impact Drill (work on strong left side position at impact)
606
+ - Extension & Rotation Drill (perfect arm rotation through impact)
607
+ - Feet Together L-to-L Swings (continuous swings with feet together)
608
+ - Two-Tee Drill (swing through tee gate for driver path)
609
+ - Mirror Work (practice setup and positions)
610
+ - Ballistic Exercises (explosive movements for power development)
611
+ - Strength Training (for mobility and power foundation)
612
  """
613
 
614
  return prompt
 
655
  break
656
 
657
  # Extract strengths using the new structured format
658
+ strengths_match = re.search(r'\*\*Strengths\*\*\s*(.*?)(?=\*\*Areas for Improvement\*\*|\*\*Practice Tips\*\*|$)', raw_analysis, re.IGNORECASE | re.DOTALL)
659
  if strengths_match:
660
  strengths_text = strengths_match.group(1)
661
+ # Extract numbered items (1. Topic: Description)
662
+ strength_items = re.findall(r'\d+\.\s*([^:]+):\s*([^\n\d]+)', strengths_text)
663
+ formatted_analysis['strengths'] = [f"{topic.strip()}: {desc.strip()}" for topic, desc in strength_items if topic.strip() and desc.strip()]
664
 
665
+ # Extract areas for improvement using the new structured format
666
+ weaknesses_match = re.search(r'\*\*Areas for Improvement\*\*\s*(.*?)(?=\*\*Practice Tips\*\*|$)', raw_analysis, re.IGNORECASE | re.DOTALL)
667
  if weaknesses_match:
668
  weaknesses_text = weaknesses_match.group(1)
669
+ # Extract numbered items (1. Topic — metric info\nExplanation)
670
+ weakness_items = re.findall(r'\d+\.\s*([^—\n]+)—([^\n]+)\n?([^\d]+)?', weaknesses_text)
671
+ formatted_analysis['weaknesses'] = []
672
+ for topic, metric, explanation in weakness_items:
673
+ if topic.strip() and metric.strip():
674
+ full_text = f"{topic.strip()} — {metric.strip()}"
675
+ if explanation and explanation.strip():
676
+ full_text += f"\n{explanation.strip()}"
677
+ formatted_analysis['weaknesses'].append(full_text)
678
 
679
+ # Extract practice tips using the new structured format
680
+ priority_match = re.search(r'\*\*Practice Tips\*\*\s*(.*?)$', raw_analysis, re.IGNORECASE | re.DOTALL)
681
  if priority_match:
682
  priority_text = priority_match.group(1)
683
  # First try to parse numbered format: "1. Topic: Description"
 
704
 
705
  # Ensure exactly 3 priority improvements with distinct topics
706
  if len(formatted_analysis['priority_improvements']) < 3:
707
+ # Define 3 distinct practice tips using drills from golf articles CSV
708
  common_improvements = [
709
+ "Ground Force Timing: Practice pushing against the ground earlier in the downswing to improve power transfer efficiency.",
710
+ "Halfway Back Drill: Work on swing path by checking club position when lead arm is parallel to ground.",
711
+ "Feet Together L-to-L Swings: Make continuous three-quarter swings with feet together to improve balance and timing."
712
  ]
713
 
714
  # Get existing topics to avoid duplicates
 
808
  percentage = formatted_analysis['classification']
809
  if percentage >= 80:
810
  formatted_analysis['priority_improvements'] = [
811
+ {'rank': 1, 'description': 'Ground Force Timing: Practice pushing against ground earlier in downswing for elite power transfer.'},
812
+ {'rank': 2, 'description': 'Extension & Rotation Drill: Perfect arm rotation through impact for maximum efficiency.'},
813
+ {'rank': 3, 'description': 'Ballistic Exercises: Use explosive movements for advanced power development and speed training.'}
814
  ]
815
  elif percentage >= 60:
816
  formatted_analysis['priority_improvements'] = [
817
+ {'rank': 1, 'description': 'Halfway Back Drill: Check club position when lead arm parallel to ground for proper swing path.'},
818
+ {'rank': 2, 'description': 'Two-Tee Drill: Practice swinging through tee gate to improve driver swing path and reduce slices.'},
819
+ {'rank': 3, 'description': 'Impact Drill: Work on strong left side position at impact for better ball striking.'}
820
  ]
821
  elif percentage >= 40:
822
  formatted_analysis['priority_improvements'] = [
823
+ {'rank': 1, 'description': 'Takeaway Drill: Practice one-piece takeaway movement for consistent swing foundation.'},
824
+ {'rank': 2, 'description': 'Feet Together L-to-L Swings: Continuous three-quarter swings with feet together for balance and timing.'},
825
+ {'rank': 3, 'description': 'Mirror Work: Practice setup positions and swing mechanics for consistency and muscle memory.'}
826
  ]
827
  else: # Below 40%
828
  formatted_analysis['priority_improvements'] = [
829
+ {'rank': 1, 'description': 'Mirror Work: Practice basic setup position focusing on proper spine angle and athletic stance.'},
830
+ {'rank': 2, 'description': 'Takeaway Drill: Work on simple one-piece takeaway movement for swing foundation.'},
831
+ {'rank': 3, 'description': 'Strength Training: Focus on mobility and foundational strength for better swing mechanics.'}
832
  ]
833
 
834
  return formatted_analysis
 
847
  # Display classification in black bolded header
848
  st.markdown(f"""
849
  <h2 style='color: black; font-weight: bold; text-align: center; margin-bottom: 20px;'>
850
+ Performance Score: {user_percentage}%
851
  </h2>
852
  """, unsafe_allow_html=True)
853
 
 
928
  st.markdown("---")
929
 
930
  # 2. Strengths and Weaknesses Table
931
+ st.subheader("Strengths & Areas for Improvement")
932
+
933
+ # Create responsive columns - divider hidden on mobile
934
+ st.markdown("""
935
+ <style>
936
+ @media (max-width: 768px) {
937
+ .desktop-divider { display: none !important; }
938
+ }
939
+ </style>
940
+ """, unsafe_allow_html=True)
941
 
 
942
  col_left, col_divider, col_right = st.columns([5, 1, 5])
943
 
944
  with col_left:
945
  st.markdown("""
946
  <div style='background-color: #e8f5e8; padding: 15px; border-radius: 10px; height: 100%;'>
947
+ <h4 style='color: #2d5a2d; margin-top: 0;'>Strengths</h4>
948
  """, unsafe_allow_html=True)
949
+ for i, strength in enumerate(analysis_data['strengths'], 1):
950
+ st.markdown(f"{i}. {strength}")
951
  st.markdown("</div>", unsafe_allow_html=True)
952
 
953
  with col_divider:
954
  st.markdown("""
955
+ <div class='desktop-divider' style='width: 2px; background-color: #ddd; height: 200px; margin: 20px auto;'></div>
956
  """, unsafe_allow_html=True)
957
 
958
  with col_right:
959
  st.markdown("""
960
  <div style='background-color: #fff5e6; padding: 15px; border-radius: 10px; height: 100%;'>
961
+ <h4 style='color: #cc6600; margin-top: 0;'>Areas for Improvement</h4>
962
  """, unsafe_allow_html=True)
963
+ for i, weakness in enumerate(analysis_data['weaknesses'], 1):
964
+ st.markdown(f"{i}. {weakness}")
965
  st.markdown("</div>", unsafe_allow_html=True)
966
 
967
  st.markdown("---")
968
 
969
+ # 3. Practice Tips
970
+ st.subheader("Practice Tips")
971
 
972
  for priority in sorted(analysis_data['priority_improvements'], key=lambda x: x['rank']):
973
  rank = priority['rank']
app/par-ity project horizontal logo.png DELETED

Git LFS Details

  • SHA256: efc2a6689a33aeba482922af0dcc86ada447d7c129f0dd30b2fef6e39cae17d7
  • Pointer size: 132 Bytes
  • Size of remote file: 1.41 MB
app/streamlit_app.py CHANGED
@@ -769,6 +769,9 @@ def main():
769
  st.rerun()
770
 
771
  if st.button("💬 Ask Questions", key="nav_chatbot", use_container_width=True):
 
 
 
772
  st.session_state.current_step = 5
773
  st.rerun()
774
 
@@ -781,15 +784,23 @@ def main():
781
  st.rerun()
782
 
783
  # Step-based content rendering
784
- if st.session_state.current_step == 1:
 
 
 
 
 
 
 
 
785
  render_step_1()
786
- elif st.session_state.current_step == 2:
787
  render_step_2()
788
- elif st.session_state.current_step == 3:
789
  render_step_3()
790
- elif st.session_state.current_step == 4:
791
  render_step_4()
792
- elif st.session_state.current_step == 5:
793
  render_step_5()
794
 
795
  st.markdown("---")
@@ -857,8 +868,6 @@ def render_step_2():
857
  """Step 2: Analyzing Video and Pose"""
858
  st.markdown('<h2 style="color: #0B3B0B; font-family: Georgia, serif;">Step 2: Analyzing Video and Pose</h2>', unsafe_allow_html=True)
859
 
860
- st.markdown("🔄 **Processing your swing video...**")
861
-
862
  video_path = st.session_state.analysis_data.get('video_path')
863
 
864
  if video_path and not st.session_state.video_analyzed:
@@ -934,7 +943,6 @@ def render_step_3():
934
  display_video(video_path, width=300)
935
 
936
  # Main action buttons - larger and more prominent
937
- st.markdown("### Choose Your Path:")
938
 
939
  col1, col2 = st.columns(2)
940
 
@@ -981,8 +989,6 @@ def render_step_4():
981
  # Generate detailed analysis with recommendations
982
  analysis = generate_swing_analysis(pose_data, swing_phases, trajectory_data)
983
 
984
- st.markdown("## 🎯 Personalized Swing Improvements")
985
-
986
  # Check available services
987
  llm_services = check_llm_services()
988
  any_service_available = llm_services['ollama']['available'] or llm_services['openai']['available']
@@ -1011,6 +1017,12 @@ def render_step_5():
1011
 
1012
  st.markdown("💬 **Ready to answer your swing questions!**")
1013
 
 
 
 
 
 
 
1014
  if RAG_AVAILABLE:
1015
  render_rag_interface()
1016
  else:
 
769
  st.rerun()
770
 
771
  if st.button("💬 Ask Questions", key="nav_chatbot", use_container_width=True):
772
+ # Ensure we preserve the analysis state when navigating
773
+ if st.session_state.get('video_analyzed', False):
774
+ st.session_state.video_analyzed = True # Explicitly preserve this
775
  st.session_state.current_step = 5
776
  st.rerun()
777
 
 
784
  st.rerun()
785
 
786
  # Step-based content rendering
787
+ current_step = st.session_state.current_step
788
+
789
+ # Safeguard: If user tries to access step 4 or 5 without analysis,
790
+ # but they have analysis data, let them proceed
791
+ if current_step >= 4 and not st.session_state.get('video_analyzed', False):
792
+ if 'analysis_data' in st.session_state and st.session_state.analysis_data.get('video_path'):
793
+ st.session_state.video_analyzed = True # Restore the flag if we have data
794
+
795
+ if current_step == 1:
796
  render_step_1()
797
+ elif current_step == 2:
798
  render_step_2()
799
+ elif current_step == 3:
800
  render_step_3()
801
+ elif current_step == 4:
802
  render_step_4()
803
+ elif current_step == 5:
804
  render_step_5()
805
 
806
  st.markdown("---")
 
868
  """Step 2: Analyzing Video and Pose"""
869
  st.markdown('<h2 style="color: #0B3B0B; font-family: Georgia, serif;">Step 2: Analyzing Video and Pose</h2>', unsafe_allow_html=True)
870
 
 
 
871
  video_path = st.session_state.analysis_data.get('video_path')
872
 
873
  if video_path and not st.session_state.video_analyzed:
 
943
  display_video(video_path, width=300)
944
 
945
  # Main action buttons - larger and more prominent
 
946
 
947
  col1, col2 = st.columns(2)
948
 
 
989
  # Generate detailed analysis with recommendations
990
  analysis = generate_swing_analysis(pose_data, swing_phases, trajectory_data)
991
 
 
 
992
  # Check available services
993
  llm_services = check_llm_services()
994
  any_service_available = llm_services['ollama']['available'] or llm_services['openai']['available']
 
1017
 
1018
  st.markdown("💬 **Ready to answer your swing questions!**")
1019
 
1020
+ # Check if we have any analysis data, but don't block access if we don't
1021
+ has_analysis = st.session_state.get('video_analyzed', False) and 'analysis_data' in st.session_state
1022
+
1023
+ if not has_analysis:
1024
+ st.info("💡 **Tip**: For personalized answers about your swing, complete the video analysis first. You can still ask general golf questions!")
1025
+
1026
  if RAG_AVAILABLE:
1027
  render_rag_interface()
1028
  else: