Rishit Pant commited on
Commit
bc9ec9b
·
1 Parent(s): b9d54f5

Improved ingestion, retrieve and generate (#4)

Browse files

* Eval prompts and scripts

* Update dependencies

* Answer generation using LLLM

* Evaluation metrics using DeepEval

* Fix: mismatched heading tags

* Add noise regex and context

* Fixed noise patterns

* Improve system prompt

.deepeval/.deepeval_telemetry.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ DEEPEVAL_ID=5f67d0f6-8daa-40e0-8e0c-a6e888223837
2
+ DEEPEVAL_STATUS=old
data/grading-document.md CHANGED
@@ -1019,7 +1019,7 @@ BS-DS_ May 2026 Grading document (Student) Updated automatically every 5 minutes
1019
 
1020
  (W11/W12 contents will be included for the final exam. Hence, please practice and submit W11/W12 assignment).
1021
 
1022
- # 6. English 2
1023
 
1024
  **Quiz 1:** July 19 2026 **Quiz 2:** August 16 2026 **End term:** September 13 2026
1025
 
@@ -1041,9 +1041,9 @@ F = score in final exam
1041
 
1042
  (W11/W12 contents will be included for the final exam. Hence, please practice and submit W11/W12 assignment).
1043
 
1044
- # 7. Intro to python programming
1045
 
1046
- ## Academic policies
1047
 
1048
  1. In each programming assignment, be it any course or any OPPE, taking help from LLMs (e.g. ChatGPT, Gemini) partially or completely is considered plagiarism.
1049
 
@@ -1291,7 +1291,7 @@ Updated automatically every 5 minutes
1291
 
1292
  Suggested pathway to register and study
1293
 
1294
- ## Diploma level courses:
1295
 
1296
  1. **Most aggressive pathway** - completing in 4 terms - ONLY IF YOU ARE DOING THIS AS FULL TIME AND NOTHING ELSE AND CAN SPEND <u>70 HRs PER WEEK</u> MINIMUM
1297
 
@@ -1872,16 +1872,9 @@ July 3rd week (Tentative): We will release the slots for OPPE 1 & Dates for OPPE
1872
 
1873
  https://docs.google.com/document/d/e/2PACX-1vT5PBOz4OH663W0IJPVGVjG_nfmYZGfFI7W1j-6wTLcex13O_7BZmf6a96Q6liO0W-mLZB5hOGZ… 13/40
1874
 
1875
- 6/9/26, 7:10 PM BS-DS_ May 2026 Grading document (Student)
1876
-
1877
- Google Docs info icon Published using Google Docs Report abuse Learn more
1878
 
1879
  to attend the exam and if you are eligible, exam will be scheduled as per the slots allocated. Please choose courses for the May 2026 term keeping all these points in mind.
1880
 
1881
- BS-DS_ May 2026 Grading document (Student) Updated automatically every 5 minutes
1882
-
1883
- # Diploma level courses
1884
-
1885
  ## 1. Machine Learning foundations (DS Diploma)
1886
 
1887
  **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
@@ -1947,17 +1940,6 @@ SoP for the SCT Exam is as follows: <u>Click Here for OPPE SCT SoP Document</u>
1947
 
1948
  https://docs.google.com/document/d/e/2PACX-1vT5PBOz4OH663W0IJPVGVjG_nfmYZGfFI7W1j-6wTLcex13O_7BZmf6a96Q6liO0W-mLZB5hOGZ… 14/40
1949
 
1950
- 6/9/26, 7:10 PM
1951
-
1952
- BS-DS_ May 2026 Grading document (Student)
1953
-
1954
- Google Docs icon Published using Google Docs
1955
-
1956
- Report abuse Learn more
1957
-
1958
- BS-DS_ May 2026 Grading document (Student)
1959
-
1960
- Updated automatically every 5 minutes
1961
 
1962
  OPPE1 will not be scheduled for students who fail to complete the OPPE SCT exam.
1963
  <u>Repeat_OPPE Criteria</u> (w.e.f from Jan 2026 term) [updated on Jan 05, 2026]
@@ -2096,7 +2078,9 @@ Google Docs logo Published using Google Docs Report abuse Learn more
2096
  # BS-DS_ May 2026 Grading document (Student)
2097
  Updated automatically every 5 minutes
2098
 
2099
- **September 13 2026**
 
 
2100
 
2101
  **Above to be attended in person at designated centres.**
2102
 
@@ -2117,7 +2101,7 @@ Overall score for eligible students:
2117
 
2118
  (Though the W11/W12 assignment score is not included in GAA, W11/W12 contents will be included for the final exam. Hence, please practice and submit W11/W12 assignment).
2119
 
2120
- # 5. Business Analytics (Diploma in DS)
2121
 
2122
  **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
2123
 
@@ -2182,7 +2166,7 @@ A = Sum of the Best 2 out of (Assignment 1, Assignment 2, Assignment 3)
2182
  </tbody>
2183
  </table>
2184
 
2185
- # 6. Tools in Data Science (Diploma in DS) [Co-
2186
 
2187
  https://docs.google.com/document/d/e/2PACX-1vT5PBOz4OH663W0IJPVGVjG_nfmYZGfFI7W1j-6wTLcex13O_7BZmf6a96Q6liO0W-mLZB5hOGZ... 16/40
2188
 
@@ -2195,7 +2179,7 @@ Report abuse Learn more
2195
  # BS-DS_ May 2026 Grading document (Student)
2196
  Updated automatically every 5 minutes
2197
 
2198
- ## Academic policies
2199
 
2200
  1. The Tools in Data Science portal has all the course content: Graded Assignments, Projects and ROE links. The Seek Portal is not used for submissions.
2201
 
@@ -2248,7 +2232,7 @@ Before registering for TDS, please attempt the **Entrance Exam** at **https://ex
2248
 
2249
  <mark>**Final course score T = 0.2 GAA + 0.2 ROE + 0.2 P1 + 0.2 P2 + 0.2 F**</mark>
2250
 
2251
- # 7. Programming Data structures and algorithms using Python (PDSA) - Diploma in Programming
2252
 
2253
  **Weekly assignments:** Mix of autograded assignment and Programming assignments
2254
 
@@ -2301,9 +2285,9 @@ OP = Score in Online proctored remote exam
2301
  F = score in final exam
2302
  **T = 0.05GAA + 0.2OP + 0.45F + max (0.2max(Qz1, Qz2), (0.10Qz1+0.20Qz2 ))**
2303
 
2304
- # 8. Database management system (DBMS) -
2305
 
2306
- ## Diploma in Programming
2307
 
2308
  **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
2309
 
@@ -2479,7 +2463,7 @@ BS-DS_ May 2026 Grading document (Student) Updated automatically every 5 minutes
2479
  </tbody>
2480
  </table>
2481
 
2482
- # 9. Application development - 1 (Diploma in programming)
2483
 
2484
  **Quiz 1**: July 19 2026
2485
  **Quiz 2**: August 16 2026
@@ -2533,7 +2517,7 @@ Google Docs logo Published using Google Docs Report abuse Learn more
2533
 
2534
  **Final course score T = 0.05 GLA + max (0.6F + 0.25max(Qz1, Qz2), 0.4F + 0.25Qz1 + 0.3Qz2)**
2535
 
2536
- # 10. Programming concepts using Java (Diploma in programming)
2537
 
2538
  **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
2539
 
@@ -2744,7 +2728,7 @@ BPT 4 - August 14, 2026
2744
 
2745
  Will be conducted in the course VM - Each BPT has 4 Questions
2746
 
2747
- ## Repeat_OPPE Criteria
2748
  **(w.e.f from Jan 2026 term) [updated on Jan 05, 2026]**
2749
  **Students who were OPPE eligible and received an I_OP in the Jan 2026 term, and have registered as Repeat OPPE for the May 2026 term, will continue to be considered eligible for OPPE in May 2026. However, students who were marked OPPE Ineligible with I_OP or I_Both and have registered for the respective course as Repeat OPPE or Repeat OPPE & End Term must regain OPPE eligibility in the May 2026 term by submitting the required assignments and OPPE SCT. If OPPE eligibility is not attained, the OPPE will not be scheduled and a U grade will be awarded for the May 2026 term.**
2750
 
@@ -2752,13 +2736,13 @@ Will be conducted in the course VM - Each BPT has 4 Questions
2752
  **SCT for OPPE and exam day rules:**
2753
  https://docs.google.com/document/d/e/2PACX-1vS4Hhh4MsKD2WL8_D26Vw2WJKw0CBtPihZyKrnEM_kefRXm_O75GqTcJA6IR0X_xCiVL5gUi5y6_bjw/pub
2754
 
2755
- ## Eligibility to attend the end term exam:
2756
  Average of the best 5 out of the first 7 weekly assessments (objective and programming) scores >= 40/100 AND OPPE should be eligible
2757
 
2758
- ## Eligibility to get the course grade:
2759
  Attending the end semester exam AND programming exam (OPPE) score >= 40/100
2760
 
2761
- ## There will be ONE OPPE based on weeks 1-9.
2762
  Students have to mandatorily attend the OPPE on the first date.
2763
 
2764
  * If you fail in this, you get a chance to reappear the next weekend. So you get 2 chances to attempt the exam.
@@ -2952,7 +2936,7 @@ Overall score for eligible students:
2952
 
2953
  ## Project Courses:
2954
 
2955
- Project courses are now for 2 credits each - BDM, MLP, App Dev 1, App Dev 2, DI-GenAi project.
2956
 
2957
  The Project courses are not part of CCC. The CCC is only for theory courses.
2958
 
@@ -2992,7 +2976,7 @@ Submission deadline :
2992
  </tbody>
2993
  </table>
2994
 
2995
- ## FOR, MAD1, MAD2 PROJECTS:
2996
 
2997
  <table>
2998
  <thead>
@@ -3034,7 +3018,7 @@ Updated automatically every 5 minutes
3034
 
3035
  https://docs.google.com/document/d/e/2PACX-1vT5PBOz4OH663W0IJPVGVjG_nfmYZGfFI7W1j-6wTLcex13O_7BZmf6a96Q6liO0W-mLZB5hOGZ-A9dMIXVrfe069kqUkplPMRAiZBBIMJcp9XTCR/pub
3036
 
3037
- ## MAD I Project
3038
 
3039
  **Project Document:**
3040
  https://docs.google.com/document/d/e/2PACX-1vR9pOLPvPzsxqhtrmtLi8GAV2j2JxpisWkIHCjEb8WuYrgm4ZPq8S_Sor-MygixO4hvGcPvO6Ei_W/pub
@@ -3042,7 +3026,7 @@ https://docs.google.com/document/d/e/2PACX-1vR9pOLPvPzsxqhtrmtLi8GAV2j2JxpisWkIH
3042
  **Project statement - Trekking Management Application:**
3043
  https://docs.google.com/document/d/e/2PACX-1vQvqzWz2tFt96B8VApnHqWqIP3LtPDbnXwApYr8VOffLCm_Zh2JuTa51z7d1CNJbrZKC0oWPredYcV/pub
3044
 
3045
- ## MAD II Project
3046
 
3047
  **Project document:**
3048
  https://docs.google.com/document/d/e/2PACX-1vTKQBIsllAp4VUq_rST3_rbpMSoADyfb3agZ1E-jrGQxMeN2IBNOXu3GS0mchzRWaxWa204q52tbFYA/pub
@@ -3055,11 +3039,11 @@ https://docs.google.com/document/d/e/2PACX-1vTcayCKq8OPAVeTZHXNwCXxvJfiQRCmxiigX
3055
 
3056
  **BDM Project: BDM PROJECT SUBMISSION TIMELINE**
3057
 
3058
- ## Deep learning and Generative AI Project
3059
 
3060
  https://docs.google.com/document/d/e/2PACX-1vTTorjQe5LH-TP_c0x-iDjdm-zX30uvJ3jv_QKtDvGsVoQydTkCWsfKwTUtCQv5syWsnSxmN4W3KFzE/pub_
3061
 
3062
- ## Rules regarding project fees:
3063
 
3064
  The fee paid for each of the 4 projects is valid for 2 terms. Please read this carefully so that you register properly.
3065
 
@@ -3118,7 +3102,7 @@ who will be entering Degree level in the Sep 2026 term. **Without completing the
3118
 
3119
  **Students are requested to complete the projects at least two terms before their diploma completion term i.e DO NOT REGISTER OR HAVE PROJECTS ALONE LEFT IN YOUR LAST TERM OF DIPLOMA.**
3120
 
3121
- ## Very important:
3122
 
3123
  1. <u>Viva policy for MLP, DL&GEN AI</u>, <u>Viva Policy for Appdev</u>
3124
 
@@ -3187,7 +3171,7 @@ It is important you learn more on programming and data science outside of what t
3187
 
3188
  4. Please do not share your assignments with others before the deadlines. If similarities are found between submissions, all will be penalised irrespective of who did it first and who shared it with whom.
3189
 
3190
- ## 1. Software Testing
3191
 
3192
  **Quiz 1**: July 19 2026 **Quiz 2**: August 16 2026 **End term**: September 13 2026
3193
 
@@ -3204,7 +3188,7 @@ Qz2 = score in Quiz II (0, if not attempted)
3204
  F - score in End Term exam
3205
  **T = 0.1GAA + 0.4F + 0.25Qz1 + 0.25Qz2**
3206
 
3207
- ## 2. Software Engineering
3208
 
3209
  **Quiz 1**: No Quiz **Quiz 2**: August 16 2026 **End term**: September 13 2026
3210
 
@@ -3269,7 +3253,7 @@ BS-DS_ May 2026 Grading document (Student)
3269
 
3270
  Updated automatically every 5 minutes
3271
 
3272
- ## 3. Deep Learning
3273
 
3274
  **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
3275
 
@@ -3296,7 +3280,7 @@ F - score in End Term exam
3296
 
3297
  **T = 0.05GAA + 0.25Qz1 + 0.25Qz2 + 0.45F**
3298
 
3299
- ## 4. AI: Search Methods for Problem Solving
3300
 
3301
  **Academic policies**
3302
 
@@ -3339,7 +3323,7 @@ Programming Assignment 1 will be released between Quiz 1 and Quiz 2.
3339
 
3340
  The dates will be announced in the forum. This assignment will be evaluated offline.
3341
 
3342
- ## 5. Strategies for Professional Growth
3343
 
3344
  **Academic policies**
3345
 
@@ -3407,7 +3391,7 @@ Project:
3407
  More details about the Group Project will be given in the course.
3408
  Bonus marks for additional activities may be awarded at the discretion of faculty or instructor, provided the student passes the course.
3409
 
3410
- ## 6. Programming in C
3411
 
3412
  **Quiz 1: July 19 2026** **Quiz 2: No Quiz 2** **End term: September 13 2026**
3413
  **Above to be attended in person at designated centres**
@@ -3516,6 +3500,9 @@ Google Docs icon Published using Google Docs
3516
 
3517
  Updated automatically every 5 minutes
3518
 
 
 
 
3519
  <span style="color: red">Above to be attended in person at designated centres.</span>
3520
 
3521
  Quiz1 - based on content taught by Prof Mitesh (Based on weeks 1-4)
@@ -3721,7 +3708,7 @@ Qz2 = score in Quiz II (0, if not attempted), Syllabus: Week 5-8
3721
  F = score in final exam, Syllabus: Week 1-12
3722
  **T = 0.075 GAA + 0.025 GRPA + 0.25Qz1 + 0.25Qz2 + 0.4F**
3723
 
3724
- ## 13.Market Research
3725
 
3726
  **Quiz 1:** July 19 2026 **Quiz 2:** August 16 2026 **End term:** September 13 2026
3727
 
@@ -3752,7 +3739,7 @@ Overall score for eligible students:
3752
  **Case release date:** YTD
3753
  **Case presentation:** Depending on numbers, if needed can extend one more day for presentation.
3754
 
3755
- ## 14.Managerial Economics
3756
 
3757
  **Quiz 1:** July 19 2026 **Quiz 2:** August 16 2026 **End term:** September 13 2026
3758
 
@@ -3773,7 +3760,7 @@ F = score in final exam
3773
 
3774
  **T = 0.1GAA + 0.4F + 0.25Qz1 + 0.25Qz2**
3775
 
3776
- ## 15. MLOps (Machine Learning Operations)
3777
 
3778
  **Quiz 1:** NA **Quiz 2:** NA **End term:** September 13 2026
3779
 
@@ -3981,7 +3968,7 @@ Qz2 = score in Quiz II (0, if not attempted)
3981
  F - score in End Term exam
3982
  T = 0.2 GAA + 0.3F + 0.25Qz1 + 0.25Qz2
3983
 
3984
- ## 20. Computer Systems Design
3985
 
3986
  **Quiz 1**: July 19 2026 **Quiz 2**: August 16 2026 **End term**: September 13 2026
3987
 
@@ -3999,7 +3986,7 @@ F = score in final exam
3999
 
4000
  T = 0.1GAA + 0.4F + 0.2Qz1 + 0.25Qz2 + 0.05 circuit verse assignment
4001
 
4002
- ## 21. Game Theory and Strategy
4003
 
4004
  **Quiz 1**: July 19 2026 **Quiz 2**: August 16 2026 **End term**: September 13 2026
4005
 
@@ -4021,7 +4008,7 @@ F = score in final exam
4021
 
4022
  T = 0.1GAA + 0.4F + 0.25Qz1 + 0.25Qz2
4023
 
4024
- ## 22. Algorithms for Data Science (ADS)
4025
 
4026
  **Quiz 1**: NA **Quiz 2**: August 16 2026 **End term**: September 13 2026
4027
 
 
1019
 
1020
  (W11/W12 contents will be included for the final exam. Hence, please practice and submit W11/W12 assignment).
1021
 
1022
+ ## 6. English 2
1023
 
1024
  **Quiz 1:** July 19 2026 **Quiz 2:** August 16 2026 **End term:** September 13 2026
1025
 
 
1041
 
1042
  (W11/W12 contents will be included for the final exam. Hence, please practice and submit W11/W12 assignment).
1043
 
1044
+ ## 7. Intro to python programming
1045
 
1046
+ ### Academic policies
1047
 
1048
  1. In each programming assignment, be it any course or any OPPE, taking help from LLMs (e.g. ChatGPT, Gemini) partially or completely is considered plagiarism.
1049
 
 
1291
 
1292
  Suggested pathway to register and study
1293
 
1294
+ # Diploma level courses:
1295
 
1296
  1. **Most aggressive pathway** - completing in 4 terms - ONLY IF YOU ARE DOING THIS AS FULL TIME AND NOTHING ELSE AND CAN SPEND <u>70 HRs PER WEEK</u> MINIMUM
1297
 
 
1872
 
1873
  https://docs.google.com/document/d/e/2PACX-1vT5PBOz4OH663W0IJPVGVjG_nfmYZGfFI7W1j-6wTLcex13O_7BZmf6a96Q6liO0W-mLZB5hOGZ… 13/40
1874
 
 
 
 
1875
 
1876
  to attend the exam and if you are eligible, exam will be scheduled as per the slots allocated. Please choose courses for the May 2026 term keeping all these points in mind.
1877
 
 
 
 
 
1878
  ## 1. Machine Learning foundations (DS Diploma)
1879
 
1880
  **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
 
1940
 
1941
  https://docs.google.com/document/d/e/2PACX-1vT5PBOz4OH663W0IJPVGVjG_nfmYZGfFI7W1j-6wTLcex13O_7BZmf6a96Q6liO0W-mLZB5hOGZ… 14/40
1942
 
 
 
 
 
 
 
 
 
 
 
 
1943
 
1944
  OPPE1 will not be scheduled for students who fail to complete the OPPE SCT exam.
1945
  <u>Repeat_OPPE Criteria</u> (w.e.f from Jan 2026 term) [updated on Jan 05, 2026]
 
2078
  # BS-DS_ May 2026 Grading document (Student)
2079
  Updated automatically every 5 minutes
2080
 
2081
+ ## 4. Business Data Management (DS Diploma)
2082
+
2083
+ **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
2084
 
2085
  **Above to be attended in person at designated centres.**
2086
 
 
2101
 
2102
  (Though the W11/W12 assignment score is not included in GAA, W11/W12 contents will be included for the final exam. Hence, please practice and submit W11/W12 assignment).
2103
 
2104
+ ## 5. Business Analytics (Diploma in DS)
2105
 
2106
  **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
2107
 
 
2166
  </tbody>
2167
  </table>
2168
 
2169
+ ## 6. Tools in Data Science (Diploma in DS)
2170
 
2171
  https://docs.google.com/document/d/e/2PACX-1vT5PBOz4OH663W0IJPVGVjG_nfmYZGfFI7W1j-6wTLcex13O_7BZmf6a96Q6liO0W-mLZB5hOGZ... 16/40
2172
 
 
2179
  # BS-DS_ May 2026 Grading document (Student)
2180
  Updated automatically every 5 minutes
2181
 
2182
+ ### Academic policies
2183
 
2184
  1. The Tools in Data Science portal has all the course content: Graded Assignments, Projects and ROE links. The Seek Portal is not used for submissions.
2185
 
 
2232
 
2233
  <mark>**Final course score T = 0.2 GAA + 0.2 ROE + 0.2 P1 + 0.2 P2 + 0.2 F**</mark>
2234
 
2235
+ ## 7. Programming Data structures and algorithms using Python (PDSA) - Diploma in Programming
2236
 
2237
  **Weekly assignments:** Mix of autograded assignment and Programming assignments
2238
 
 
2285
  F = score in final exam
2286
  **T = 0.05GAA + 0.2OP + 0.45F + max (0.2max(Qz1, Qz2), (0.10Qz1+0.20Qz2 ))**
2287
 
2288
+ ## 8. Database management system (DBMS) -
2289
 
2290
+ ### Diploma in Programming
2291
 
2292
  **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
2293
 
 
2463
  </tbody>
2464
  </table>
2465
 
2466
+ ## 9. Application development - 1 (Diploma in programming)
2467
 
2468
  **Quiz 1**: July 19 2026
2469
  **Quiz 2**: August 16 2026
 
2517
 
2518
  **Final course score T = 0.05 GLA + max (0.6F + 0.25max(Qz1, Qz2), 0.4F + 0.25Qz1 + 0.3Qz2)**
2519
 
2520
+ ## 10. Programming concepts using Java (Diploma in programming)
2521
 
2522
  **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
2523
 
 
2728
 
2729
  Will be conducted in the course VM - Each BPT has 4 Questions
2730
 
2731
+ ### Repeat_OPPE Criteria
2732
  **(w.e.f from Jan 2026 term) [updated on Jan 05, 2026]**
2733
  **Students who were OPPE eligible and received an I_OP in the Jan 2026 term, and have registered as Repeat OPPE for the May 2026 term, will continue to be considered eligible for OPPE in May 2026. However, students who were marked OPPE Ineligible with I_OP or I_Both and have registered for the respective course as Repeat OPPE or Repeat OPPE & End Term must regain OPPE eligibility in the May 2026 term by submitting the required assignments and OPPE SCT. If OPPE eligibility is not attained, the OPPE will not be scheduled and a U grade will be awarded for the May 2026 term.**
2734
 
 
2736
  **SCT for OPPE and exam day rules:**
2737
  https://docs.google.com/document/d/e/2PACX-1vS4Hhh4MsKD2WL8_D26Vw2WJKw0CBtPihZyKrnEM_kefRXm_O75GqTcJA6IR0X_xCiVL5gUi5y6_bjw/pub
2738
 
2739
+ ### Eligibility to attend the end term exam:
2740
  Average of the best 5 out of the first 7 weekly assessments (objective and programming) scores >= 40/100 AND OPPE should be eligible
2741
 
2742
+ ### Eligibility to get the course grade:
2743
  Attending the end semester exam AND programming exam (OPPE) score >= 40/100
2744
 
2745
+ ### There will be ONE OPPE based on weeks 1-9.
2746
  Students have to mandatorily attend the OPPE on the first date.
2747
 
2748
  * If you fail in this, you get a chance to reappear the next weekend. So you get 2 chances to attempt the exam.
 
2936
 
2937
  ## Project Courses:
2938
 
2939
+ Project courses are now for 2 credits each - BDM, MLP, App Dev 1, App Dev 2, DL-GenAI project.
2940
 
2941
  The Project courses are not part of CCC. The CCC is only for theory courses.
2942
 
 
2976
  </tbody>
2977
  </table>
2978
 
2979
+ ### MAD1, MAD2 PROJECTS:
2980
 
2981
  <table>
2982
  <thead>
 
3018
 
3019
  https://docs.google.com/document/d/e/2PACX-1vT5PBOz4OH663W0IJPVGVjG_nfmYZGfFI7W1j-6wTLcex13O_7BZmf6a96Q6liO0W-mLZB5hOGZ-A9dMIXVrfe069kqUkplPMRAiZBBIMJcp9XTCR/pub
3020
 
3021
+ ### MAD I Project
3022
 
3023
  **Project Document:**
3024
  https://docs.google.com/document/d/e/2PACX-1vR9pOLPvPzsxqhtrmtLi8GAV2j2JxpisWkIHCjEb8WuYrgm4ZPq8S_Sor-MygixO4hvGcPvO6Ei_W/pub
 
3026
  **Project statement - Trekking Management Application:**
3027
  https://docs.google.com/document/d/e/2PACX-1vQvqzWz2tFt96B8VApnHqWqIP3LtPDbnXwApYr8VOffLCm_Zh2JuTa51z7d1CNJbrZKC0oWPredYcV/pub
3028
 
3029
+ ### MAD II Project
3030
 
3031
  **Project document:**
3032
  https://docs.google.com/document/d/e/2PACX-1vTKQBIsllAp4VUq_rST3_rbpMSoADyfb3agZ1E-jrGQxMeN2IBNOXu3GS0mchzRWaxWa204q52tbFYA/pub
 
3039
 
3040
  **BDM Project: BDM PROJECT SUBMISSION TIMELINE**
3041
 
3042
+ ### Deep learning and Generative AI Project
3043
 
3044
  https://docs.google.com/document/d/e/2PACX-1vTTorjQe5LH-TP_c0x-iDjdm-zX30uvJ3jv_QKtDvGsVoQydTkCWsfKwTUtCQv5syWsnSxmN4W3KFzE/pub_
3045
 
3046
+ ### Rules regarding project fees:
3047
 
3048
  The fee paid for each of the 4 projects is valid for 2 terms. Please read this carefully so that you register properly.
3049
 
 
3102
 
3103
  **Students are requested to complete the projects at least two terms before their diploma completion term i.e DO NOT REGISTER OR HAVE PROJECTS ALONE LEFT IN YOUR LAST TERM OF DIPLOMA.**
3104
 
3105
+ ### Very important:
3106
 
3107
  1. <u>Viva policy for MLP, DL&GEN AI</u>, <u>Viva Policy for Appdev</u>
3108
 
 
3171
 
3172
  4. Please do not share your assignments with others before the deadlines. If similarities are found between submissions, all will be penalised irrespective of who did it first and who shared it with whom.
3173
 
3174
+ # 1. Software Testing
3175
 
3176
  **Quiz 1**: July 19 2026 **Quiz 2**: August 16 2026 **End term**: September 13 2026
3177
 
 
3188
  F - score in End Term exam
3189
  **T = 0.1GAA + 0.4F + 0.25Qz1 + 0.25Qz2**
3190
 
3191
+ # 2. Software Engineering
3192
 
3193
  **Quiz 1**: No Quiz **Quiz 2**: August 16 2026 **End term**: September 13 2026
3194
 
 
3253
 
3254
  Updated automatically every 5 minutes
3255
 
3256
+ # 3. Deep Learning
3257
 
3258
  **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
3259
 
 
3280
 
3281
  **T = 0.05GAA + 0.25Qz1 + 0.25Qz2 + 0.45F**
3282
 
3283
+ # 4. AI: Search Methods for Problem Solving
3284
 
3285
  **Academic policies**
3286
 
 
3323
 
3324
  The dates will be announced in the forum. This assignment will be evaluated offline.
3325
 
3326
+ # 5. Strategies for Professional Growth
3327
 
3328
  **Academic policies**
3329
 
 
3391
  More details about the Group Project will be given in the course.
3392
  Bonus marks for additional activities may be awarded at the discretion of faculty or instructor, provided the student passes the course.
3393
 
3394
+ # 6. Programming in C
3395
 
3396
  **Quiz 1: July 19 2026** **Quiz 2: No Quiz 2** **End term: September 13 2026**
3397
  **Above to be attended in person at designated centres**
 
3500
 
3501
  Updated automatically every 5 minutes
3502
 
3503
+ # 9. Deep Learning Practice
3504
+
3505
+ **Quiz 1: July 19 2026** **Quiz 2: August 16 2026** **End term: September 13 2026**
3506
  <span style="color: red">Above to be attended in person at designated centres.</span>
3507
 
3508
  Quiz1 - based on content taught by Prof Mitesh (Based on weeks 1-4)
 
3708
  F = score in final exam, Syllabus: Week 1-12
3709
  **T = 0.075 GAA + 0.025 GRPA + 0.25Qz1 + 0.25Qz2 + 0.4F**
3710
 
3711
+ # 13.Market Research
3712
 
3713
  **Quiz 1:** July 19 2026 **Quiz 2:** August 16 2026 **End term:** September 13 2026
3714
 
 
3739
  **Case release date:** YTD
3740
  **Case presentation:** Depending on numbers, if needed can extend one more day for presentation.
3741
 
3742
+ # 14.Managerial Economics
3743
 
3744
  **Quiz 1:** July 19 2026 **Quiz 2:** August 16 2026 **End term:** September 13 2026
3745
 
 
3760
 
3761
  **T = 0.1GAA + 0.4F + 0.25Qz1 + 0.25Qz2**
3762
 
3763
+ # 15. MLOps (Machine Learning Operations)
3764
 
3765
  **Quiz 1:** NA **Quiz 2:** NA **End term:** September 13 2026
3766
 
 
3968
  F - score in End Term exam
3969
  T = 0.2 GAA + 0.3F + 0.25Qz1 + 0.25Qz2
3970
 
3971
+ # 20. Computer Systems Design
3972
 
3973
  **Quiz 1**: July 19 2026 **Quiz 2**: August 16 2026 **End term**: September 13 2026
3974
 
 
3986
 
3987
  T = 0.1GAA + 0.4F + 0.2Qz1 + 0.25Qz2 + 0.05 circuit verse assignment
3988
 
3989
+ # 21. Game Theory and Strategy
3990
 
3991
  **Quiz 1**: July 19 2026 **Quiz 2**: August 16 2026 **End term**: September 13 2026
3992
 
 
4008
 
4009
  T = 0.1GAA + 0.4F + 0.25Qz1 + 0.25Qz2
4010
 
4011
+ # 22. Algorithms for Data Science (ADS)
4012
 
4013
  **Quiz 1**: NA **Quiz 2**: August 16 2026 **End term**: September 13 2026
4014
 
eval/eval_prompts.json CHANGED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "fee_001",
4
+ "question": "What is the foundation level fee for students joining from Jan 2026 onwards?",
5
+ "expected_keywords": ["48,000", "48000"],
6
+ "source": "student-handbook.md",
7
+ "category": "fees"
8
+ },
9
+ {
10
+ "id": "fee_002",
11
+ "question": "What is the total fee for Foundation + Two Diplomas under the revised fee structure?",
12
+ "expected_keywords": ["210,000", "210000"],
13
+ "source": "student-handbook.md",
14
+ "category": "fees"
15
+ },
16
+ {
17
+ "id": "fee_003",
18
+ "question": "What is the total fee for the BS Degree under the revised fee structure?",
19
+ "expected_keywords": ["386,000", "450,000"],
20
+ "source": "student-handbook.md",
21
+ "category": "fees"
22
+ },
23
+ {
24
+ "id": "fee_004",
25
+ "question": "What is the foundation level fee for students who joined the program before Sep 2025?",
26
+ "expected_keywords": ["32,000", "32000"],
27
+ "source": "student-handbook.md",
28
+ "category": "fees"
29
+ },
30
+ {
31
+ "id": "fee_005",
32
+ "question": "What fee waiver does an SC/ST student with family income above 5 LPA get?",
33
+ "expected_keywords": ["50%"],
34
+ "source": "student-handbook.md",
35
+ "category": "fees"
36
+ }
37
+ ]
eval/evaluate.py CHANGED
@@ -0,0 +1,423 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ evaluate.py — Offline evaluation using DeepEval
3
+ for the IITM BS Degree RAG System
4
+
5
+ Judge LLM : Groq (llama-3.1-8b-instant) via DeepEvalBaseLLM wrapper
6
+ Metrics :
7
+ - FaithfulnessMetric — are answer claims grounded in retrieved chunks?
8
+ - AnswerRelevancyMetric — is the answer relevant to the question?
9
+ - ContextualPrecisionMetric — are the top-ranked chunks the most useful ones?
10
+
11
+ Plus a fast keyword-hit check (no LLM needed) as a CI gate.
12
+
13
+ Usage:
14
+ python eval/evaluate.py # full DeepEval run
15
+ python eval/evaluate.py --category fees # one category only
16
+ python eval/evaluate.py --threshold 0.75 # custom pass threshold
17
+ python eval/evaluate.py --no-deepeval # keyword-only, fast CI mode
18
+ python eval/evaluate.py --save-report # write results to eval/report.json
19
+ """
20
+
21
+ import os
22
+ import sys
23
+ import json
24
+ import argparse
25
+ import time
26
+ import re
27
+ from datetime import datetime
28
+ from pathlib import Path
29
+
30
+ from dotenv import load_dotenv
31
+ from groq import Groq
32
+ from groq import RateLimitError
33
+ from pydantic import BaseModel
34
+
35
+ from deepeval.models import DeepEvalBaseLLM
36
+ from deepeval.metrics import (
37
+ AnswerRelevancyMetric,
38
+ FaithfulnessMetric,
39
+ ContextualPrecisionMetric,
40
+ )
41
+ from deepeval.test_case import LLMTestCase
42
+
43
+ # ── Path setup ─────────────────────────────────────────────────────────────────
44
+ ROOT = Path(__file__).parent.parent
45
+ sys.path.insert(0, str(ROOT / "src"))
46
+ from retrieve import Retriever # noqa: E402
47
+
48
+ load_dotenv()
49
+
50
+ API_KEY = os.getenv("GROQ_API_KEY")
51
+ if not API_KEY:
52
+ print("ERROR: GROQ_API_KEY not found in environment.")
53
+ sys.exit(1)
54
+
55
+ EVAL_DATA_PATH = Path(__file__).parent / "eval_prompts.json"
56
+ REPORT_PATH = Path(__file__).parent / "report.json"
57
+ DEFAULT_THRESHOLD = 0.5
58
+ JUDGE_MODEL = "llama-3.1-8b-instant"
59
+ GEN_MODEL = "llama-3.1-8b-instant"
60
+
61
+ # Retry / throttle settings
62
+ MAX_RETRIES = 6
63
+ BACKOFF_BASE = 2 # seconds — used only if retry delay isn't parseable
64
+ BETWEEN_CALLS = 3 # polite gap after every successful Groq call
65
+
66
+
67
+ # ── Retry helper ───────────────────────────────────────────────────────────────
68
+
69
+ def _parse_retry_delay(error: RateLimitError) -> float | None:
70
+ """
71
+ Extract the suggested wait time from a Groq 429 error message.
72
+ Groq says things like:
73
+ 'Please try again in 760ms'
74
+ 'Please try again in 1.2s'
75
+ We parse that and add a small buffer.
76
+ """
77
+ msg = str(error)
78
+
79
+ # milliseconds: "760ms"
80
+ ms_match = re.search(r'try again in (\d+(?:\.\d+)?)ms', msg, re.I)
81
+ if ms_match:
82
+ return float(ms_match.group(1)) / 1000.0 + 0.5
83
+
84
+ # seconds: "1.2s"
85
+ s_match = re.search(r'try again in (\d+(?:\.\d+)?)s', msg, re.I)
86
+ if s_match:
87
+ return float(s_match.group(1)) + 0.5
88
+
89
+ return None
90
+
91
+
92
+ def groq_call_with_retry(fn, *args, **kwargs):
93
+ """
94
+ Call any Groq SDK function with automatic retry on 429 rate limits.
95
+ Reads the suggested delay from the error response when available,
96
+ otherwise falls back to exponential backoff.
97
+
98
+ Usage:
99
+ response = groq_call_with_retry(
100
+ client.chat.completions.create,
101
+ model=..., messages=..., ...
102
+ )
103
+ """
104
+ for attempt in range(1, MAX_RETRIES + 1):
105
+ try:
106
+ result = fn(*args, **kwargs)
107
+ time.sleep(BETWEEN_CALLS)
108
+ return result
109
+ except RateLimitError as e:
110
+ if attempt == MAX_RETRIES:
111
+ raise
112
+
113
+ suggested = _parse_retry_delay(e)
114
+ wait = suggested if suggested else (BACKOFF_BASE ** attempt)
115
+ print(f"\n ⏳ 429 rate-limited — waiting {wait:.2f}s (attempt {attempt}/{MAX_RETRIES})...")
116
+ time.sleep(wait)
117
+
118
+
119
+ # ── Groq wrapper for DeepEval ──────────────────────────────────────────────────
120
+
121
+ class GroqJudge(DeepEvalBaseLLM):
122
+ """
123
+ Wraps the Groq SDK so DeepEval can use it as its judge LLM.
124
+
125
+ DeepEval calls generate() with either:
126
+ - just a prompt string → return a plain string
127
+ - a prompt + Pydantic schema → return a parsed schema instance
128
+
129
+ The schema path is used for metric scoring (DeepEval asks the judge
130
+ to fill in a structured verdict JSON). We force json_object mode so
131
+ Groq always returns valid JSON for those calls.
132
+ """
133
+ def __init__(self, api_key: str, model_name: str = JUDGE_MODEL):
134
+ self.api_key = api_key
135
+ self.model_name = model_name
136
+ self._client = Groq(api_key=api_key)
137
+
138
+ def load_model(self):
139
+ return self._client
140
+
141
+ def generate(self, prompt: str, schema: BaseModel = None):
142
+ client = self.load_model()
143
+ kwargs = dict(
144
+ model=self.model_name,
145
+ messages=[{"role": "user", "content": prompt}],
146
+ temperature=0.0,
147
+ max_tokens=1024,
148
+ )
149
+ if schema is not None:
150
+ kwargs["response_format"] = {"type": "json_object"}
151
+
152
+ response = groq_call_with_retry(client.chat.completions.create, **kwargs)
153
+ raw = response.choices[0].message.content.strip()
154
+
155
+ if schema is not None:
156
+ for candidate in [raw] + raw.split("```"):
157
+ candidate = candidate.lstrip("json").strip()
158
+ try:
159
+ return schema(**json.loads(candidate))
160
+ except Exception:
161
+ continue
162
+ raise ValueError(f"GroqJudge: could not parse schema: {raw[:200]}")
163
+
164
+ return raw
165
+
166
+ async def a_generate(self, prompt: str, schema: BaseModel = None):
167
+ return self.generate(prompt, schema)
168
+
169
+ def get_model_name(self) -> str:
170
+ return f"Groq/{self.model_name}"
171
+
172
+
173
+ # ── Answer generator ───────────────────────────────────────────────────────────
174
+
175
+ def generate_answer(question: str, chunks: list[dict], client: Groq) -> str:
176
+ """Generate a grounded answer from retrieved chunks using Groq."""
177
+ context_parts = [
178
+ f"[{c['source']} — Section: {c['page']}]\n{c['text']}"
179
+ for c in chunks
180
+ ]
181
+ prompt = (
182
+ "You are an official academic advisor AI for the IITM BS Degree Programme.\n"
183
+ "Answer the student question ONLY from the provided context. Be concise and factual.\n"
184
+ 'If the answer is not in the context, say "I don\'t have that information."\n\n'
185
+ "CONTEXT:\n"
186
+ + "\n---\n".join(context_parts)
187
+ + f"\n\nSTUDENT QUESTION: {question}\n"
188
+ )
189
+ response = groq_call_with_retry(
190
+ client.chat.completions.create,
191
+ model=GEN_MODEL,
192
+ messages=[
193
+ {"role": "system", "content": "Answer only from the provided context."},
194
+ {"role": "user", "content": prompt},
195
+ ],
196
+ temperature=0.0,
197
+ max_tokens=200,
198
+ )
199
+ return response.choices[0].message.content.strip()
200
+
201
+
202
+ # ── Keyword hit ────────────────────────────────────────────────────────────────
203
+
204
+ def keyword_hit(answer: str, expected_keywords: list[str]) -> bool:
205
+ """Check if any expected keyword appears in the answer (case-insensitive)."""
206
+ a = answer.lower()
207
+ return any(kw.lower() in a for kw in expected_keywords)
208
+
209
+
210
+ # ── Main evaluation ────────────────────────────────────────────────────────────
211
+
212
+ def run_evaluation(
213
+ category: str = None,
214
+ use_deepeval: bool = True,
215
+ threshold: float = DEFAULT_THRESHOLD,
216
+ save_report: bool = False,
217
+ ):
218
+ print("\n" + "=" * 65)
219
+ print(" IITM BS RAG — DEEPEVAL EVALUATION")
220
+ print("=" * 65)
221
+
222
+ if not EVAL_DATA_PATH.exists():
223
+ print(f"ERROR: eval_prompts.json not found at {EVAL_DATA_PATH}")
224
+ sys.exit(1)
225
+
226
+ with open(EVAL_DATA_PATH) as f:
227
+ eval_data = json.load(f)
228
+
229
+ if category:
230
+ eval_data = [q for q in eval_data if q.get("category") == category]
231
+ print(f" Category filter : '{category}' → {len(eval_data)} questions")
232
+ else:
233
+ print(f" Total questions : {len(eval_data)}")
234
+
235
+ print(f" Judge model : {JUDGE_MODEL}")
236
+ print(f" DeepEval : {'enabled' if use_deepeval else 'disabled (keyword-only)'}")
237
+ print(f" Threshold : {threshold}\n")
238
+
239
+ print("Initializing retriever...")
240
+ retriever = Retriever()
241
+ groq_client = Groq(api_key=API_KEY)
242
+
243
+ # ── Phase 1: retrieve + generate ──────────────────────────────────────────
244
+ print("\nPhase 1 — Retrieve & Generate\n" + "-" * 40)
245
+
246
+ test_cases : list[LLMTestCase] = []
247
+ kw_hits : list[bool] = []
248
+ item_map : list[dict] = []
249
+ chunk_scores: list[list[float]] = []
250
+
251
+ for i, item in enumerate(eval_data, 1):
252
+ question = item["question"]
253
+ print(f" [{i:02d}/{len(eval_data)}] {question[:70]}")
254
+
255
+ chunks = retriever.retrieve(question, top_n=2)
256
+ answer = generate_answer(question, chunks, groq_client)
257
+ contexts = [c["text"][:1000] for c in chunks]
258
+ scores = [round(c["rerank_score"], 3) for c in chunks]
259
+
260
+ kw = keyword_hit(answer, item["expected_keywords"])
261
+ kw_hits.append(kw)
262
+ chunk_scores.append(scores)
263
+
264
+ print(f" rerank scores : {scores}")
265
+ print(f" keyword : {'✅' if kw else '❌'} {answer[:80]}{'…' if len(answer) > 80 else ''}\n")
266
+
267
+ test_cases.append(LLMTestCase(
268
+ input=question,
269
+ actual_output=answer,
270
+ retrieval_context=contexts,
271
+ expected_output=" | ".join(item["expected_keywords"]),
272
+ ))
273
+ item_map.append(item)
274
+
275
+ kw_rate = sum(kw_hits) / len(kw_hits)
276
+
277
+ # ── Phase 2: DeepEval scoring ──────────────────────────────────────────────
278
+ results_by_metric : dict[str, list[float]] = {}
279
+ per_question_scores : list[dict] = []
280
+
281
+ if use_deepeval:
282
+ print("\nPhase 2 — DeepEval Metrics\n" + "-" * 40)
283
+ print(f" Judge model : {JUDGE_MODEL}")
284
+ print(f" Gap between calls : {BETWEEN_CALLS}s | Max retries on 429 : {MAX_RETRIES}\n")
285
+
286
+ judge = GroqJudge(api_key=API_KEY)
287
+
288
+ metrics = [
289
+ FaithfulnessMetric(
290
+ threshold=threshold, model=judge,
291
+ include_reason=True, async_mode=False,
292
+ ),
293
+ AnswerRelevancyMetric(
294
+ threshold=threshold, model=judge,
295
+ include_reason=True, async_mode=False,
296
+ ),
297
+ ContextualPrecisionMetric(
298
+ threshold=threshold, model=judge,
299
+ include_reason=True, async_mode=False,
300
+ ),
301
+ ]
302
+
303
+ for i, tc in enumerate(test_cases, 1):
304
+ print(f" [{i:02d}/{len(test_cases)}] {tc.input[:65]}")
305
+ q_scores = {"question": tc.input, "keyword_hit": kw_hits[i - 1]}
306
+
307
+ for m in metrics:
308
+ mname = type(m).__name__
309
+ try:
310
+ m.measure(tc)
311
+ score = m.score if m.score is not None else 0.0
312
+ reason = (m.reason or "—")[:110]
313
+ icon = "✅" if score >= threshold else "❌"
314
+ print(f" {mname:<32} {icon} {score:.3f} {reason}")
315
+ except RateLimitError as e:
316
+ score = 0.0
317
+ print(f" {mname:<32} ⚠️ rate limit exhausted after {MAX_RETRIES} retries: {e}")
318
+ except Exception as e:
319
+ score = 0.0
320
+ print(f" {mname:<32} ⚠️ error: {e}")
321
+
322
+ results_by_metric.setdefault(mname, []).append(score)
323
+ q_scores[mname] = round(score, 4)
324
+
325
+ per_question_scores.append(q_scores)
326
+ print()
327
+
328
+ # ── Phase 3: Aggregate summary ─────────────────────────────────────────────
329
+ print("=" * 65)
330
+ print(" AGGREGATE RESULTS")
331
+ print("=" * 65)
332
+ print(f" Questions evaluated : {len(eval_data)}")
333
+ print(f" Keyword Hit Rate : {kw_rate:.1%} {'✅' if kw_rate >= threshold else '❌'}")
334
+
335
+ def _avg(lst: list[float]) -> float:
336
+ valid = [s for s in lst if s is not None]
337
+ return sum(valid) / len(valid) if valid else 0.0
338
+
339
+ avg_faith = avg_rel = avg_prec = None
340
+
341
+ if use_deepeval and results_by_metric:
342
+ avg_faith = _avg(results_by_metric.get("FaithfulnessMetric", []))
343
+ avg_rel = _avg(results_by_metric.get("AnswerRelevancyMetric", []))
344
+ avg_prec = _avg(results_by_metric.get("ContextualPrecisionMetric",[]))
345
+
346
+ print(f" Faithfulness (avg) : {avg_faith:.3f} {'✅' if avg_faith >= threshold else '❌'}")
347
+ print(f" Answer Relevancy (avg) : {avg_rel:.3f} {'✅' if avg_rel >= threshold else '❌'}")
348
+ print(f" Contextual Precision (avg) : {avg_prec:.3f} {'✅' if avg_prec >= threshold else '❌'}")
349
+
350
+ print("\n Per-question breakdown:")
351
+ header = f" {'ID':<28} {'kw':>3} {'Faith':>6} {'Rel':>6} {'Prec':>6}"
352
+ print(header)
353
+ print(" " + "-" * (len(header) - 2))
354
+ for item, kw, pq in zip(item_map, kw_hits, per_question_scores):
355
+ f = pq.get("FaithfulnessMetric", 0)
356
+ r = pq.get("AnswerRelevancyMetric", 0)
357
+ p = pq.get("ContextualPrecisionMetric", 0)
358
+ print(
359
+ f" {item['id']:<28} {'✅' if kw else '❌':>3} "
360
+ f" {f:>6.3f} {r:>6.3f} {p:>6.3f}"
361
+ )
362
+
363
+ # ── CI gate ────────────────────────────────────────────────────────────────
364
+ if avg_faith is not None:
365
+ gate_metric = min(kw_rate, avg_faith)
366
+ gate_label = f"min(keyword={kw_rate:.1%}, faithfulness={avg_faith:.3f})"
367
+ else:
368
+ gate_metric = kw_rate
369
+ gate_label = f"keyword hit rate = {kw_rate:.1%}"
370
+
371
+ print(f"\n Gate : {gate_label}")
372
+ print(f" Score : {gate_metric:.3f} (threshold: {threshold:.2f})")
373
+
374
+ # ── Optional JSON report ───────────────────────────────────────────────────
375
+ if save_report:
376
+ report = {
377
+ "timestamp": datetime.now().isoformat(),
378
+ "judge_model": JUDGE_MODEL,
379
+ "threshold": threshold,
380
+ "category": category,
381
+ "num_questions": len(eval_data),
382
+ "keyword_hit_rate": round(kw_rate, 4),
383
+ "averages": {
384
+ "faithfulness": round(avg_faith, 4) if avg_faith is not None else None,
385
+ "answer_relevancy": round(avg_rel, 4) if avg_rel is not None else None,
386
+ "contextual_precision": round(avg_prec, 4) if avg_prec is not None else None,
387
+ },
388
+ "gate_score": round(gate_metric, 4),
389
+ "passed": gate_metric >= threshold,
390
+ "per_question": per_question_scores,
391
+ }
392
+ REPORT_PATH.write_text(json.dumps(report, indent=2))
393
+ print(f"\n 📄 Report saved → {REPORT_PATH}")
394
+
395
+ # ── Exit with CI-friendly code ─────────────────────────────────────────────
396
+ if gate_metric >= threshold:
397
+ print(f"\n ✅ PASSED — RAG quality is above threshold ({threshold:.0%})\n")
398
+ sys.exit(0)
399
+ else:
400
+ print(f"\n ❌ FAILED — Quality dropped below threshold ({threshold:.0%})")
401
+ print(" Check ❌ rows above. Re-run ingest.py if documents changed.\n")
402
+ sys.exit(1)
403
+
404
+
405
+ # ── CLI ────────────────────────────────────────────────────────────────────────
406
+ if __name__ == "__main__":
407
+ parser = argparse.ArgumentParser(description="Evaluate IITM BS RAG with DeepEval")
408
+ parser.add_argument("--category", type=str, default=None,
409
+ help="Filter eval_prompts.json by category field")
410
+ parser.add_argument("--threshold", type=float, default=DEFAULT_THRESHOLD,
411
+ help="Pass/fail threshold for all metrics (default: 0.5)")
412
+ parser.add_argument("--no-deepeval", action="store_true",
413
+ help="Skip DeepEval metrics; run keyword check only")
414
+ parser.add_argument("--save-report", action="store_true",
415
+ help="Write results to eval/report.json")
416
+ args = parser.parse_args()
417
+
418
+ run_evaluation(
419
+ category=args.category,
420
+ use_deepeval=not args.no_deepeval,
421
+ threshold=args.threshold,
422
+ save_report=args.save_report,
423
+ )
pyproject.toml CHANGED
@@ -12,7 +12,6 @@ dependencies = [
12
  "faiss-cpu>=1.14.2",
13
  "google-genai>=2.8.0",
14
  "gradio>=6.17.3",
15
- "groq>=1.4.0",
16
  "ipykernel>=7.3.0",
17
  "langchain-chroma>=1.1.0",
18
  "langchain>=1.3.7",
@@ -31,4 +30,5 @@ dependencies = [
31
  "sentence-transformers>=5.5.1",
32
  "langchain-huggingface>=1.2.2",
33
  "langchain-classic>=1.0.8",
 
34
  ]
 
12
  "faiss-cpu>=1.14.2",
13
  "google-genai>=2.8.0",
14
  "gradio>=6.17.3",
 
15
  "ipykernel>=7.3.0",
16
  "langchain-chroma>=1.1.0",
17
  "langchain>=1.3.7",
 
30
  "sentence-transformers>=5.5.1",
31
  "langchain-huggingface>=1.2.2",
32
  "langchain-classic>=1.0.8",
33
+ "langchain-groq>=1.1.3",
34
  ]
requirements.txt CHANGED
@@ -7,7 +7,6 @@ sentence-transformers
7
  faiss-cpu
8
  chromadb
9
  rank-bm25
10
- groq
11
  pyyaml
12
  datasets
13
  ragas
@@ -23,4 +22,5 @@ python-dotenv
23
  deepeval
24
  langchain_chroma
25
  langchain_huggingface
26
- langchain-classic
 
 
7
  faiss-cpu
8
  chromadb
9
  rank-bm25
 
10
  pyyaml
11
  datasets
12
  ragas
 
22
  deepeval
23
  langchain_chroma
24
  langchain_huggingface
25
+ langchain-classic
26
+ langchain_groq
src/generate.py CHANGED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ from dotenv import load_dotenv
4
+
5
+ from langchain_groq import ChatGroq
6
+ from langchain_core.prompts import ChatPromptTemplate
7
+ from langchain_core.output_parsers import StrOutputParser
8
+ from langchain_core.runnables import RunnablePassthrough, RunnableLambda
9
+ from retrieve import Retriever
10
+
11
+ load_dotenv()
12
+
13
+ if not os.getenv("GROQ_API_KEY"):
14
+ print("ERROR: GROQ_API_KEY environment variable not found.")
15
+ sys.exit(1)
16
+
17
+ SYSTEM_PROMPT = """
18
+ You are the official academic advisor AI for the IITM BS Degree Programme. Answer student questions accurately, professionally, and concisely.
19
+
20
+ Follow these strict rules:
21
+ 1. **Strict Grounding:** Answer ONLY using the provided Context Documents. If the answer is not explicitly stated, state that you do not have the information. Do not guess or hallucinate.
22
+ 2. **Data Extraction:** Carefully align rows and columns when extracting tabular data.
23
+ 3. **Citations:** Always cite the source document name when stating factual rules or fees (e.g., "According to student-handbook.md...").
24
+ 4. **Definitions First:** Define any acronyms, fee names, or policy terms before giving specific details. Never omit context that changes how a number or fact should be interpreted.
25
+ 5. **Redundancy Handling:** If the context contains numerically-related figures across different sections that might represent the same fee, explicitly surface this potential overlap rather than stating them as definitively separate.
26
+ 6. **Course Focus:** Detail only the single most relevant course. If courses with similar names exist, mention them in one brief line at the end as alternatives; do not provide their grading details unless explicitly requested.
27
+ """
28
+
29
+ HUMAN_PROMPT = """CONTEXT DOCUMENTS:
30
+ {context}
31
+
32
+ STUDENT QUESTION:
33
+ {question}"""
34
+
35
+ prompt = ChatPromptTemplate.from_messages([
36
+ ("system", SYSTEM_PROMPT),
37
+ ("human", HUMAN_PROMPT),
38
+ ])
39
+
40
+
41
+ def format_context(chunks: list[dict]) -> str:
42
+ """Convert retrieved chunk dicts into a formatted context string."""
43
+ parts = []
44
+ for i, c in enumerate(chunks, 1):
45
+ tag = f"[{c['source']} — {c['page']}]"
46
+ parts.append(f"--- Chunk {i} {tag} ---\n{c['text']}")
47
+ return "\n\n".join(parts)
48
+
49
+
50
+ class Generator:
51
+ def __init__(self):
52
+ print("Initializing retrieval system...")
53
+ self.retriever = Retriever()
54
+
55
+ llm = ChatGroq(
56
+ model="llama-3.3-70b-versatile", #llama-3.3-70b-versatile
57
+ temperature=0.1,
58
+ streaming=True,
59
+ )
60
+
61
+ # LCEL chain:
62
+ # 1. Retrieve chunks for the question
63
+ # 2. Format them into a context string
64
+ # 3. Pass context + question into the prompt
65
+ # 4. Send to LLM and parse output
66
+ self.chain = (
67
+ {
68
+ "context": RunnableLambda(lambda q: format_context(self.retriever.retrieve(q))),
69
+ "question": RunnablePassthrough(),
70
+ }
71
+ | prompt
72
+ | llm
73
+ | StrOutputParser()
74
+ )
75
+
76
+ # Keep a reference so answer() can show sources
77
+ self._last_chunks: list[dict] = []
78
+
79
+ def answer(self, query: str, top_n: int = 4) -> str:
80
+ """Run the full RAG chain and stream the answer to stdout."""
81
+ print(f"\n🔍 Retrieving context for: '{query}'...")
82
+ self._last_chunks = self.retriever.retrieve(query, top_n=top_n)
83
+
84
+ if not self._last_chunks:
85
+ msg = "I couldn't find any official documentation related to that question."
86
+ print(msg)
87
+ return msg
88
+
89
+ print("🧠 Generating answer...\n")
90
+
91
+ full_response = ""
92
+ for token in self.chain.stream(query):
93
+ print(token, end="", flush=True)
94
+ full_response += token
95
+
96
+ print("\n\n" + "-" * 60)
97
+ print("SOURCES USED:")
98
+ seen = set()
99
+ for c in self._last_chunks:
100
+ label = f"- {c['source']} (Section: {c['page'][:60]})"
101
+ if label not in seen:
102
+ print(label)
103
+ seen.add(label)
104
+
105
+ return full_response
106
+
107
+ if __name__ == "__main__":
108
+ agent = Generator()
109
+
110
+ print("\n🎓 IITM BS Degree RAG Assistant Online!")
111
+ print("Type 'exit' or 'quit' to close.\n")
112
+
113
+ while True:
114
+ try:
115
+ user_input = input("\nStudent Question: ").strip()
116
+ if user_input.lower() in ("exit", "quit"):
117
+ print("Shutting down...")
118
+ break
119
+ if not user_input:
120
+ continue
121
+ agent.answer(user_input)
122
+ except KeyboardInterrupt:
123
+ print("\nShutting down...")
124
+ break
src/ingest.py CHANGED
@@ -10,8 +10,8 @@ DB_DIR = "db"
10
  COLLECTION_NAME = "handbook_docs"
11
  EMBED_MODEL = "BAAI/bge-small-en-v1.5"
12
 
13
- CHUNK_SIZE = 1500
14
- CHUNK_OVERLAP = 150
15
 
16
  HEADERS_TO_SPLIT = [
17
  ("#", "h1"),
@@ -76,12 +76,17 @@ def clean_markdown(raw_text: str) -> str:
76
  # Google Docs URLs
77
  r'https://docs\.google\.com/\S+',
78
 
79
- # Bare page-number artifacts: " 38/66 " or "10/66" on their own
80
- r'(?<!\d)\d{1,3}/\d{2,3}(?!\d)(?=\s|$)',
 
 
 
 
 
81
  ]
82
  text = raw_text
83
  for pattern in noise_patterns:
84
- text = re.sub(pattern, '', text, flags=re.IGNORECASE)
85
 
86
  text = html_tables_to_markdown(text)
87
  # Collapse runs of whitespace left behind by removals
@@ -108,6 +113,26 @@ def load_and_split(md_path: Path) -> list[Document]:
108
  )
109
  final_docs = char_splitter.split_documents(header_docs)
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  for doc in final_docs:
112
  doc.metadata["source"] = md_path.name
113
 
 
10
  COLLECTION_NAME = "handbook_docs"
11
  EMBED_MODEL = "BAAI/bge-small-en-v1.5"
12
 
13
+ CHUNK_SIZE = 2000
14
+ CHUNK_OVERLAP = 200
15
 
16
  HEADERS_TO_SPLIT = [
17
  ("#", "h1"),
 
76
  # Google Docs URLs
77
  r'https://docs\.google\.com/\S+',
78
 
79
+ # Bare page-number artifacts: " 38/66 " or "10/66" on their own line
80
+ # (excludes /100 since that's always a score threshold, e.g. "40/100")
81
+ r'(?<![\d>=])\b\d{1,3}/(?!100\b)\d{2,3}\b(?!\d)\s*(?=\n|$)',
82
+
83
+ r'^#{1,3}\s*BS-DS_\s*May\s*2026\s*Grading\s*document\s*\(Student\)\s*$',
84
+ r'^BS-DS_\s*May\s*2026\s*Grading\s*document\s*\(Student\)\s*$',
85
+ r'^Updated\s+automatically\s+every\s+\d+\s+minutes\s*$',
86
  ]
87
  text = raw_text
88
  for pattern in noise_patterns:
89
+ text = re.sub(pattern, '', text, flags=re.IGNORECASE | re.MULTILINE)
90
 
91
  text = html_tables_to_markdown(text)
92
  # Collapse runs of whitespace left behind by removals
 
113
  )
114
  final_docs = char_splitter.split_documents(header_docs)
115
 
116
+ # Prepend the section's header path to every chunk's content.
117
+ # Without this, a chunk containing e.g. a course's T formula but not
118
+ # the course name (because the header line landed in an earlier chunk)
119
+ # won't match queries that mention the course name — for either the
120
+ # vector embedding or BM25.
121
+ for doc in final_docs:
122
+ header_parts = [
123
+ doc.metadata.get("h1", ""),
124
+ doc.metadata.get("h2", ""),
125
+ doc.metadata.get("h3", ""),
126
+ ]
127
+ header_path = " > ".join(p for p in header_parts if p)
128
+ if header_path:
129
+ doc.page_content = (
130
+ f"[Course: {header_path}]\n"
131
+ f"{doc.page_content}\n"
132
+ f"[/Course: {header_path}]"
133
+ )
134
+
135
+
136
  for doc in final_docs:
137
  doc.metadata["source"] = md_path.name
138
 
src/retrieve.py CHANGED
@@ -28,7 +28,8 @@ _NOISE_PATTERNS = [
28
  re.compile(r'Report\s+abuse\s+Learn\s+more[^\n]*', re.I),
29
  re.compile(r'Updated\s+automatically\s+every\s+\d+\s+minutes[^\n]*', re.I),
30
  re.compile(r'https://docs\.google\.com/\S+'),
31
- re.compile(r'(?<!\d)\d{1,3}/\d{2,3}(?!\d)(?=\s|$)'),
 
32
  ]
33
 
34
  def _scrub_noise(text: str) -> str:
 
28
  re.compile(r'Report\s+abuse\s+Learn\s+more[^\n]*', re.I),
29
  re.compile(r'Updated\s+automatically\s+every\s+\d+\s+minutes[^\n]*', re.I),
30
  re.compile(r'https://docs\.google\.com/\S+'),
31
+ re.compile(r'(?<![\d>=])\b\d{1,3}/(?!100\b)\d{2,3}\b(?!\d)\s*(?=\n|$)'),
32
+ re.compile(r'^#{1,3}\s*BS-DS_\s*May\s*2026\s*Grading\s*document\s*\(Student\)\s*$', re.I | re.M),
33
  ]
34
 
35
  def _scrub_noise(text: str) -> str:
uv.lock CHANGED
@@ -1748,7 +1748,7 @@ wheels = [
1748
 
1749
  [[package]]
1750
  name = "groq"
1751
- version = "1.4.0"
1752
  source = { registry = "https://pypi.org/simple" }
1753
  dependencies = [
1754
  { name = "anyio" },
@@ -1758,9 +1758,9 @@ dependencies = [
1758
  { name = "sniffio" },
1759
  { name = "typing-extensions" },
1760
  ]
1761
- sdist = { url = "https://files.pythonhosted.org/packages/26/84/d99c4894d32ed52bf2763127804343d9323dce22beb61d42aebc7d9c5f4d/groq-1.4.0.tar.gz", hash = "sha256:09b1ed51408c6969a11ef1a4dfe44d42ec975b5f1510e5de3f3dab56e22dffc6", size = 158123 }
1762
  wheels = [
1763
- { url = "https://files.pythonhosted.org/packages/3a/5b/28cfd8937be95c0814fd9458710a8a257fb8424a39e291b7bbd494476108/groq-1.4.0-py3-none-any.whl", hash = "sha256:99a3bcd57c71538f69cf11c75cdae91598983d2681b9a14008636a018c4b6d17", size = 143699 },
1764
  ]
1765
 
1766
  [[package]]
@@ -2467,6 +2467,19 @@ wheels = [
2467
  { url = "https://files.pythonhosted.org/packages/04/a2/af563ff45208d22abc28d0a0e44a7fb8aceaadac201afa6a11c77bfb6338/langchain_google_vertexai-3.2.4-py3-none-any.whl", hash = "sha256:65b5615e596fdabc2e149f0160fded88bebef2bbc1ea70095ff81714f7570183", size = 118884 },
2468
  ]
2469
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2470
  [[package]]
2471
  name = "langchain-huggingface"
2472
  version = "1.2.2"
@@ -4975,7 +4988,6 @@ dependencies = [
4975
  { name = "faiss-cpu" },
4976
  { name = "google-genai" },
4977
  { name = "gradio" },
4978
- { name = "groq" },
4979
  { name = "ipykernel" },
4980
  { name = "langchain" },
4981
  { name = "langchain-chroma" },
@@ -4984,6 +4996,7 @@ dependencies = [
4984
  { name = "langchain-core" },
4985
  { name = "langchain-google-genai" },
4986
  { name = "langchain-google-vertexai" },
 
4987
  { name = "langchain-huggingface" },
4988
  { name = "langchain-openai" },
4989
  { name = "pdfplumber" },
@@ -5005,7 +5018,6 @@ requires-dist = [
5005
  { name = "faiss-cpu", specifier = ">=1.14.2" },
5006
  { name = "google-genai", specifier = ">=2.8.0" },
5007
  { name = "gradio", specifier = ">=6.17.3" },
5008
- { name = "groq", specifier = ">=1.4.0" },
5009
  { name = "ipykernel", specifier = ">=7.3.0" },
5010
  { name = "langchain", specifier = ">=1.3.7" },
5011
  { name = "langchain-chroma", specifier = ">=1.1.0" },
@@ -5014,6 +5026,7 @@ requires-dist = [
5014
  { name = "langchain-core", specifier = ">=1.4.5" },
5015
  { name = "langchain-google-genai", specifier = ">=4.2.5" },
5016
  { name = "langchain-google-vertexai", specifier = ">=3.2.4" },
 
5017
  { name = "langchain-huggingface", specifier = ">=1.2.2" },
5018
  { name = "langchain-openai", specifier = ">=1.3.0" },
5019
  { name = "pdfplumber", specifier = ">=0.11.9" },
 
1748
 
1749
  [[package]]
1750
  name = "groq"
1751
+ version = "0.37.1"
1752
  source = { registry = "https://pypi.org/simple" }
1753
  dependencies = [
1754
  { name = "anyio" },
 
1758
  { name = "sniffio" },
1759
  { name = "typing-extensions" },
1760
  ]
1761
+ sdist = { url = "https://files.pythonhosted.org/packages/e9/78/18948a9056e1509c87e10ab8316a90ecce87035fbd53342dffdf97f4de00/groq-0.37.1.tar.gz", hash = "sha256:7353d6dfb60834fd7aacbb86af106e2dc2aeaff6d0edd65fb2fd0f16bd39314c", size = 145289 }
1762
  wheels = [
1763
+ { url = "https://files.pythonhosted.org/packages/5f/d6/645a081750e43f858b7d09dce5d8e1e76cf11e7e4bdba81252e04f78963d/groq-0.37.1-py3-none-any.whl", hash = "sha256:b49f8c8898c55eaec9f71f1342f3fcacc9560d67a08ce5f35fbfb84e8dacd3da", size = 137494 },
1764
  ]
1765
 
1766
  [[package]]
 
2467
  { url = "https://files.pythonhosted.org/packages/04/a2/af563ff45208d22abc28d0a0e44a7fb8aceaadac201afa6a11c77bfb6338/langchain_google_vertexai-3.2.4-py3-none-any.whl", hash = "sha256:65b5615e596fdabc2e149f0160fded88bebef2bbc1ea70095ff81714f7570183", size = 118884 },
2468
  ]
2469
 
2470
+ [[package]]
2471
+ name = "langchain-groq"
2472
+ version = "1.1.3"
2473
+ source = { registry = "https://pypi.org/simple" }
2474
+ dependencies = [
2475
+ { name = "groq" },
2476
+ { name = "langchain-core" },
2477
+ ]
2478
+ sdist = { url = "https://files.pythonhosted.org/packages/6e/4d/64b5e57e9e43f009fcb1591f567f0095bcf38f0b705a2d63cec4c04e0d56/langchain_groq-1.1.3.tar.gz", hash = "sha256:890c099a55526bceafc3e696d123cb9d36464c6664a3ead34ae6e09e0d50caeb", size = 192228 }
2479
+ wheels = [
2480
+ { url = "https://files.pythonhosted.org/packages/a7/5d/2f862bf5623d5c8ca6ed8a8917a7b1410e6d3595ee0bac12aff508556f76/langchain_groq-1.1.3-py3-none-any.whl", hash = "sha256:a69bb8212b7a699f407c033bf41ca526db8de68f438d51a41740a72bf6dc09bf", size = 20779 },
2481
+ ]
2482
+
2483
  [[package]]
2484
  name = "langchain-huggingface"
2485
  version = "1.2.2"
 
4988
  { name = "faiss-cpu" },
4989
  { name = "google-genai" },
4990
  { name = "gradio" },
 
4991
  { name = "ipykernel" },
4992
  { name = "langchain" },
4993
  { name = "langchain-chroma" },
 
4996
  { name = "langchain-core" },
4997
  { name = "langchain-google-genai" },
4998
  { name = "langchain-google-vertexai" },
4999
+ { name = "langchain-groq" },
5000
  { name = "langchain-huggingface" },
5001
  { name = "langchain-openai" },
5002
  { name = "pdfplumber" },
 
5018
  { name = "faiss-cpu", specifier = ">=1.14.2" },
5019
  { name = "google-genai", specifier = ">=2.8.0" },
5020
  { name = "gradio", specifier = ">=6.17.3" },
 
5021
  { name = "ipykernel", specifier = ">=7.3.0" },
5022
  { name = "langchain", specifier = ">=1.3.7" },
5023
  { name = "langchain-chroma", specifier = ">=1.1.0" },
 
5026
  { name = "langchain-core", specifier = ">=1.4.5" },
5027
  { name = "langchain-google-genai", specifier = ">=4.2.5" },
5028
  { name = "langchain-google-vertexai", specifier = ">=3.2.4" },
5029
+ { name = "langchain-groq", specifier = ">=1.1.3" },
5030
  { name = "langchain-huggingface", specifier = ">=1.2.2" },
5031
  { name = "langchain-openai", specifier = ">=1.3.0" },
5032
  { name = "pdfplumber", specifier = ">=0.11.9" },