karaoke-gen 0.76.20__py3-none-any.whl → 0.82.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. karaoke_gen/instrumental_review/static/index.html +179 -16
  2. karaoke_gen/karaoke_gen.py +5 -4
  3. karaoke_gen/lyrics_processor.py +25 -6
  4. {karaoke_gen-0.76.20.dist-info → karaoke_gen-0.82.0.dist-info}/METADATA +79 -3
  5. {karaoke_gen-0.76.20.dist-info → karaoke_gen-0.82.0.dist-info}/RECORD +33 -31
  6. lyrics_transcriber/core/config.py +8 -0
  7. lyrics_transcriber/core/controller.py +43 -1
  8. lyrics_transcriber/correction/agentic/observability/langfuse_integration.py +178 -5
  9. lyrics_transcriber/correction/agentic/prompts/__init__.py +23 -0
  10. lyrics_transcriber/correction/agentic/prompts/classifier.py +66 -6
  11. lyrics_transcriber/correction/agentic/prompts/langfuse_prompts.py +298 -0
  12. lyrics_transcriber/correction/agentic/providers/config.py +7 -0
  13. lyrics_transcriber/correction/agentic/providers/constants.py +1 -1
  14. lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +22 -7
  15. lyrics_transcriber/correction/agentic/providers/model_factory.py +28 -13
  16. lyrics_transcriber/correction/agentic/router.py +18 -13
  17. lyrics_transcriber/correction/corrector.py +1 -45
  18. lyrics_transcriber/frontend/.gitignore +1 -0
  19. lyrics_transcriber/frontend/e2e/agentic-corrections.spec.ts +207 -0
  20. lyrics_transcriber/frontend/e2e/fixtures/agentic-correction-data.json +226 -0
  21. lyrics_transcriber/frontend/package.json +4 -1
  22. lyrics_transcriber/frontend/playwright.config.ts +1 -1
  23. lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +34 -30
  24. lyrics_transcriber/frontend/src/components/Header.tsx +141 -34
  25. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +120 -3
  26. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +11 -1
  27. lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +122 -35
  28. lyrics_transcriber/frontend/src/components/shared/types.ts +6 -0
  29. lyrics_transcriber/output/generator.py +50 -3
  30. lyrics_transcriber/transcribers/local_whisper.py +260 -0
  31. lyrics_transcriber/correction/handlers/llm.py +0 -293
  32. lyrics_transcriber/correction/handlers/llm_providers.py +0 -60
  33. {karaoke_gen-0.76.20.dist-info → karaoke_gen-0.82.0.dist-info}/WHEEL +0 -0
  34. {karaoke_gen-0.76.20.dist-info → karaoke_gen-0.82.0.dist-info}/entry_points.txt +0 -0
  35. {karaoke_gen-0.76.20.dist-info → karaoke_gen-0.82.0.dist-info}/licenses/LICENSE +0 -0
@@ -8,12 +8,12 @@ karaoke_gen/instrumental_review/analyzer.py,sha256=Heg8TbrwM4g5IV7bavmO6EfVD4M0U
8
8
  karaoke_gen/instrumental_review/editor.py,sha256=_DGTjKMk5WhoGtLGtTvHzU522LJyQQ_DSY1r8fULuiA,11568
9
9
  karaoke_gen/instrumental_review/models.py,sha256=cUSb_JheJK0cGdKx9f59-9sRvRrhrgdTdKBzQN3lHto,5226
10
10
  karaoke_gen/instrumental_review/server.py,sha256=Ick90X77t2EeMRwtx2U08sSybadQyWH7G0tDG-4JqP4,19377
11
- karaoke_gen/instrumental_review/static/index.html,sha256=EjMFxCQJOUSrsgwIXAW3R4bN6hYxDLmL4MHzoXxI4f0,59362
11
+ karaoke_gen/instrumental_review/static/index.html,sha256=1lzo_W5B4HxNStWPiVaP4I6ctqDkXAABJkQmojvBDqc,63235
12
12
  karaoke_gen/instrumental_review/waveform.py,sha256=Q6LBPZrJAD6mzZ7TmRf3Tf4gwYhUYTHumJKytLs3hSg,12940
13
13
  karaoke_gen/karaoke_finalise/__init__.py,sha256=HqZ7TIhgt_tYZ-nb_NNCaejWAcF_aK-7wJY5TaW_keM,46
14
14
  karaoke_gen/karaoke_finalise/karaoke_finalise.py,sha256=Wn1KcdRyINT63UxKUPT9uB-bsrFVih0Im_cjXtequS0,93534
15
- karaoke_gen/karaoke_gen.py,sha256=-kmv26iqF36OXHoKAdFCXqaLPhrqk-aH958v-cPbTWM,65694
16
- karaoke_gen/lyrics_processor.py,sha256=IzwscxBtDe2l7NhmWY8PdglYeMfIXhh2AWHQMY-ro1M,22829
15
+ karaoke_gen/karaoke_gen.py,sha256=84n2SE0MixJr01_btLmm5cVdf35hJvp7W638b8TKR-Q,65734
16
+ karaoke_gen/lyrics_processor.py,sha256=9BtL2uJa4Ekrodj2w_SXSeOraVKCB2kzYuHcGHTFpo8,23979
17
17
  karaoke_gen/metadata.py,sha256=SZW6TuUpkGGU98gRdjPfrR8F4vWXjnfCSGry2XD5_A4,6689
18
18
  karaoke_gen/pipeline/__init__.py,sha256=-MZnba4qobr1qGDamG9CieLl2pWCZMEB5_Yur62RKeM,2106
19
19
  karaoke_gen/pipeline/base.py,sha256=yg4LIm7Mc9ER0zCmZcUv4huEkotSSXK_0OAFio-TSNI,6235
@@ -44,8 +44,8 @@ lyrics_transcriber/__init__.py,sha256=g9ZbJg9U1qo7XzrC25J3bTKcNzzwUJWDVdi_7-hjcM
44
44
  lyrics_transcriber/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
45
  lyrics_transcriber/cli/cli_main.py,sha256=F72ENLTj934bXjHAUbRm0toCK73qnuJhwEm9agBVKHQ,11596
46
46
  lyrics_transcriber/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
- lyrics_transcriber/core/config.py,sha256=AM6RZKll8tzdZtMLgvHRQb1SxiXVPek0q4vmSWVUvEo,1368
48
- lyrics_transcriber/core/controller.py,sha256=laeUakqT-0CoIyoBWYvv7kWxX-_wefWRwg2xrz84gRg,29432
47
+ lyrics_transcriber/core/config.py,sha256=_X_d1wSYTJjSquqbODYCwPdOYpnSR9KERwvr_jkdYls,2056
48
+ lyrics_transcriber/core/controller.py,sha256=dUJvnehr9_Mv3Syj_TWZQsQVsDD1w8AdF5_1xISA2cw,31661
49
49
  lyrics_transcriber/correction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
50
  lyrics_transcriber/correction/agentic/__init__.py,sha256=p7PHiebuvRs8RDlPDs-9gLZKzXG5KfWg3fFCdDhY6pE,222
51
51
  lyrics_transcriber/correction/agentic/adapter.py,sha256=Z0JBTAA7xlSdctCHqO9nBMl78C4XmqsLKKtS6BvNZNI,2912
@@ -75,29 +75,30 @@ lyrics_transcriber/correction/agentic/models/observability_metrics.py,sha256=xGd
75
75
  lyrics_transcriber/correction/agentic/models/schemas.py,sha256=skWXqGkJnv9NvmvjktBqrH_4Ohyzg2x0ZMsVINbXKdg,2141
76
76
  lyrics_transcriber/correction/agentic/models/utils.py,sha256=tX7flxCB4aLrgZWkHuEt7Gr8kaLkMsXzTdWSec6Xsts,580
77
77
  lyrics_transcriber/correction/agentic/observability/__init__.py,sha256=RuaepVsltWdaF1aF_YmNVJTJ6_bbNDFo3Sp-ruBvyHA,85
78
- lyrics_transcriber/correction/agentic/observability/langfuse_integration.py,sha256=GvgisZyy5_tDeC7Hd8SDWbd-9aAqYnMTd52uNeQ2p-I,1221
78
+ lyrics_transcriber/correction/agentic/observability/langfuse_integration.py,sha256=5oBfoFT-QExZttD2wlIzXRhgSglNElFFwz8Et36vZos,7014
79
79
  lyrics_transcriber/correction/agentic/observability/metrics.py,sha256=Js_m6ljdI6Xgd9X9eHtboCsf9gjYsN1zOv3_XSwjgKk,1907
80
80
  lyrics_transcriber/correction/agentic/observability/performance.py,sha256=ekjzgL65gfs1SpKR_befu1wdWZU9xDlcafJm8htSvks,328
81
- lyrics_transcriber/correction/agentic/prompts/__init__.py,sha256=YPgEN82oygmT_pfIj2RpZM-WOLoFv6rBAPKeIRstXuI,48
82
- lyrics_transcriber/correction/agentic/prompts/classifier.py,sha256=pKbL4Cyj0-c_Ot9IxfKBOL7PnL0ZfUvKPLZwOMr-NDo,9730
81
+ lyrics_transcriber/correction/agentic/prompts/__init__.py,sha256=riiZ-f4jlvq4QjtyCpmv-sSzfcLy7O99pMBwV1H5Usc,605
82
+ lyrics_transcriber/correction/agentic/prompts/classifier.py,sha256=FwUSL59Y-5q9J1CDW8iyzyiajcy4-uq5MzfWu0If_Yo,11899
83
+ lyrics_transcriber/correction/agentic/prompts/langfuse_prompts.py,sha256=hjQhyY_GBuZt_oY9DacutXvA9dJCZksRY2fKmveJm_A,10898
83
84
  lyrics_transcriber/correction/agentic/providers/__init__.py,sha256=PS7C4sKDfa6S9lSo33GXIRamCLsv0Jn7u0GtXuhiRD4,95
84
85
  lyrics_transcriber/correction/agentic/providers/base.py,sha256=bExuntMLLInMmWWNzN81_ScWQJhNYbtlF3wZYhlX-qw,1059
85
86
  lyrics_transcriber/correction/agentic/providers/circuit_breaker.py,sha256=D3Jg4YHqvy4gzlxfkALa7PztyYQpJb8NwJAonMS0TSI,4694
86
- lyrics_transcriber/correction/agentic/providers/config.py,sha256=FsX1xIF1UG_Kbxp4tlEnZc68AsRTL4Q4XenP_VuRi6o,2937
87
- lyrics_transcriber/correction/agentic/providers/constants.py,sha256=aDIEsDvNQLEGlGk8klAaRxJmdldGBDFqwYLuCmlYoNM,692
87
+ lyrics_transcriber/correction/agentic/providers/config.py,sha256=w6-fkapEy3BgoFIsRfZ44XUCV4zuicFSNoSoVAe5lYE,3282
88
+ lyrics_transcriber/correction/agentic/providers/constants.py,sha256=cXLzKTyFVt9q6wQd_gWcv3EZ5Sm27AOAz6NyPapcess,695
88
89
  lyrics_transcriber/correction/agentic/providers/health.py,sha256=F8pHY5BQYvylGRDGXUHplcAJooAyiqVLRhBl4kHC1H8,710
89
- lyrics_transcriber/correction/agentic/providers/langchain_bridge.py,sha256=hderNRLrSZn49LrGBrgdCvBP5E7tPAugjaw7TFbb0JY,7957
90
- lyrics_transcriber/correction/agentic/providers/model_factory.py,sha256=ITPc7BLIhtHKzobERl0P7YsmunOquqbaJ_M5tinztx4,7667
90
+ lyrics_transcriber/correction/agentic/providers/langchain_bridge.py,sha256=H3C3BNjAixfkOJojxWXv-P-svlgj5rJEJdk0zPIjh7E,8540
91
+ lyrics_transcriber/correction/agentic/providers/model_factory.py,sha256=CeVDblf1HdphtUHVn3Cgl07YAeUuSxTjEHHFJN8Frj0,8257
91
92
  lyrics_transcriber/correction/agentic/providers/response_cache.py,sha256=Byr7fQJsgUMFlsvHeVCxTiFjjnbsg3KIlEmEEtAo-Gw,7047
92
93
  lyrics_transcriber/correction/agentic/providers/response_parser.py,sha256=a8pdUYKBS5X72gck3u1ndFYB__UN0UijAdxNhbHp8ZQ,3809
93
94
  lyrics_transcriber/correction/agentic/providers/retry_executor.py,sha256=hX21Zwy2cSECAw7k13ndEinWRqwjo4xYoSCQ2B2CUf0,3912
94
- lyrics_transcriber/correction/agentic/router.py,sha256=pk4xeS-BZLGJMdFj7Q7MjNaqYJF_glI590z9Alg15co,1229
95
+ lyrics_transcriber/correction/agentic/router.py,sha256=akP28A0lftmsnSyMOW6k7iTC1pv4LEgilXhIkcfJzlE,1437
95
96
  lyrics_transcriber/correction/agentic/workflows/__init__.py,sha256=OsBExAbIIKxJgX6FKXFOgcUjIG9AWJQV_fESZVdO8mo,77
96
97
  lyrics_transcriber/correction/agentic/workflows/consensus_workflow.py,sha256=gMuLTUxkgYaciMsI4yrZSC3wi--7V_PgaDNE-Vd6FE8,575
97
98
  lyrics_transcriber/correction/agentic/workflows/correction_graph.py,sha256=kgZKnz0h9cG1EfhW7BSSl-kSpQtJrRM_S86kAniXfE4,1815
98
99
  lyrics_transcriber/correction/agentic/workflows/feedback_workflow.py,sha256=KsKLD3AP66YYmXfUn-mVZjERYLtU1Zs4a-7CB2zDfas,596
99
100
  lyrics_transcriber/correction/anchor_sequence.py,sha256=5tl4Cjiw5UlLbEb1Oy-g3ebKCinXSwohdaCB9-rTMtI,43798
100
- lyrics_transcriber/correction/corrector.py,sha256=e8N7Yys6MCmz8PbHkkl7KuxH1m3MWlH1vwCa1r3YcqA,40223
101
+ lyrics_transcriber/correction/corrector.py,sha256=qW6GwOOLM8zxYtYMmGy9Rzk_4mJzdpGiCXW3LQFXn14,38362
101
102
  lyrics_transcriber/correction/feedback/__init__.py,sha256=i1gd0Vb4qvlzZQ3lqA3fJjt288YP7f-MBPwOzZ7Rjh4,68
102
103
  lyrics_transcriber/correction/feedback/schemas.py,sha256=OiF_WUqcqiEKIoburYM8kWAIundy82PQE7ImsdP8UCk,4416
103
104
  lyrics_transcriber/correction/feedback/store.py,sha256=T4IDzf1eRA9n-wdLLrLyAW1ELYgXwK9RikJgX_B3fN8,8788
@@ -105,8 +106,6 @@ lyrics_transcriber/correction/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JC
105
106
  lyrics_transcriber/correction/handlers/base.py,sha256=ZXYMFgbCmlD62dpqdFwFPlcePdHKEFrABffnG_Mu5mI,1687
106
107
  lyrics_transcriber/correction/handlers/extend_anchor.py,sha256=IADgdPmEMokUQhh6mP-wQWLYf6GfWTvJbBjOk08A-aw,6384
107
108
  lyrics_transcriber/correction/handlers/levenshtein.py,sha256=hMERQHVgiUDSHtamYrAjqZ3qMMok4VmQ_MYM2-nrX6w,7864
108
- lyrics_transcriber/correction/handlers/llm.py,sha256=ufqHtohdU5dUXE3DikzbloAWGVgMu1wnw6P4WHRmpdk,14580
109
- lyrics_transcriber/correction/handlers/llm_providers.py,sha256=MV-KCRseccg-DEimMS0D2bXJ2xhy59r2n8UZjICUoEY,2067
110
109
  lyrics_transcriber/correction/handlers/no_space_punct_match.py,sha256=jY2fa547Qc8B63xIhF9VyWMaq5jds6E6wBqyVq6KANw,7057
111
110
  lyrics_transcriber/correction/handlers/relaxed_word_count_match.py,sha256=x4k__6gav4-STk_TycLcg5Sw4x2vUFAj5fWmOv7Yd_w,3911
112
111
  lyrics_transcriber/correction/handlers/repeat.py,sha256=1PJADW44egYh7N9D2fN-gDIusWVglFjGHrCZuTQYNpA,4313
@@ -117,17 +116,19 @@ lyrics_transcriber/correction/handlers/word_operations.py,sha256=410xhyO9tiqezV5
117
116
  lyrics_transcriber/correction/operations.py,sha256=k5N8w_8BeR7CXiclaJ3zuu_g2KLoWSnnuD4OAmY3kJs,14010
118
117
  lyrics_transcriber/correction/phrase_analyzer.py,sha256=dtO_2LjxnPdHJM7De40mYIdHCkozwhizVVQp5XGO7x0,16962
119
118
  lyrics_transcriber/correction/text_utils.py,sha256=7QHK6-PY7Rx1G1E31sWiLBw00mHorRDo-M44KMHFaZs,833
120
- lyrics_transcriber/frontend/.gitignore,sha256=lgGIPiVpFVUNSZl9oNQLelLOWUzpF7sikLW8xmsrrqI,248
119
+ lyrics_transcriber/frontend/.gitignore,sha256=cR2ofyyWArkna_jByfaWi8gTeMhsKTSoK128PmIw218,262
121
120
  lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs,sha256=KTYy2KCV2OpHhussV5jIPDdUSr7RftMRhqPsRUmgfAY,2765465
122
121
  lyrics_transcriber/frontend/.yarnrc.yml,sha256=0hZQ1OTcPqTUNBqQeme4VFkIzrsabHNzLtc_M-wSgIM,66
123
122
  lyrics_transcriber/frontend/README.md,sha256=-D6CAfKTT7Y0V3EjlZ2fMy7fyctFQ4x2TJ9vx6xtccM,1607
124
123
  lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md,sha256=iRZbicW5satHel9gbG-uLyZ7oq3xdp87KQlJEL1ZhK8,8384
125
124
  lyrics_transcriber/frontend/__init__.py,sha256=nW8acRSWTjXoRwGqcTU4w-__X7tMAE0iXL0uihBN3CU,836
125
+ lyrics_transcriber/frontend/e2e/agentic-corrections.spec.ts,sha256=yNynyV8dUfwRJ1a0Cdr6o2SZEMFiuGAQG1ZM0Ro8q9o,7359
126
+ lyrics_transcriber/frontend/e2e/fixtures/agentic-correction-data.json,sha256=_h-nI76gPXuqWErpTBrZaTgcc8LNLi6j81t3Wtt--ac,8184
126
127
  lyrics_transcriber/frontend/eslint.config.js,sha256=3ADH23ANA4NNBKFy6nCVk65e8bx1DrVd_FIaYNnhuqA,734
127
128
  lyrics_transcriber/frontend/index.html,sha256=hcVQvxU1yITMrMS4vVLwn4YwvnlXsfl4XY9UNtXvWAw,1135
128
129
  lyrics_transcriber/frontend/package-lock.json,sha256=gQekpsz4CAKMJ8Fi331Q3Pv5yqhZlQ-nbGoDNnF35WE,159262
129
- lyrics_transcriber/frontend/package.json,sha256=ttlZ0EqPSz0HIkjyCt_3szk1CsFrLR-uNtyhdxsc30w,1278
130
- lyrics_transcriber/frontend/playwright.config.ts,sha256=7AtyWuAP193WVbFDqzqKMYZwE1dZ7TZ_QKzNfP8hIB0,1520
130
+ lyrics_transcriber/frontend/package.json,sha256=qujjeqPUSJizfHxK_2egicJYea8fziJO4O6u2A6N9Xw,1395
131
+ lyrics_transcriber/frontend/playwright.config.ts,sha256=l5aoc_rEbrYxIipTAVbpRER0FL5bAevYtRTT-chGUqA,1523
131
132
  lyrics_transcriber/frontend/public/android-chrome-192x192.png,sha256=lg-6aPF5mGLiuG7LyftZk_0RI41srmpA8wj-NkaaQms,17632
132
133
  lyrics_transcriber/frontend/public/android-chrome-512x512.png,sha256=x-zuKT3NYsTqAWzhKRTZeD4-0uYoUjqMPZpKTChqNJ8,123447
133
134
  lyrics_transcriber/frontend/public/apple-touch-icon.png,sha256=6y5vGra54w5oc8VP6sn2JjoQtN9hWTKn0YPhmdlmfU0,16188
@@ -143,7 +144,7 @@ lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx,sha256=ubJwQewryjU
143
144
  lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx,sha256=Yg6FG0LtrneRfAYeBu3crt_RdN-_o7FojtYhDMDKi0o,8595
144
145
  lyrics_transcriber/frontend/src/components/AppHeader.tsx,sha256=5KUVADDv9cAs2WNX9M31utQIYHKMi81unzrCW3j1fl0,2396
145
146
  lyrics_transcriber/frontend/src/components/AudioPlayer.tsx,sha256=XOCz0VtGiAIBs1qnCwrAixwfgHbTSGpjEb1jQg8wqzc,5441
146
- lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx,sha256=lcOQhSQBuTBv-adGB4uOb66qR-uX4OuN5P0CfFMxmHo,4941
147
+ lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx,sha256=Z5i0MMaFC1dbafUsZVsNEMkdoqBLjkA6yCWtjoMmqi8,5207
147
148
  lyrics_transcriber/frontend/src/components/CorrectionAnnotationModal.tsx,sha256=XtF5XNLL2ztm714tXql7rKi2BX4k_bsizpZ_ZCvpu8s,13368
148
149
  lyrics_transcriber/frontend/src/components/CorrectionDetailCard.tsx,sha256=Hp-i1iSB3pzrpPH2wIREtEHHaReimBaYi8vcSUUArlg,9512
149
150
  lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx,sha256=CoTZS9Z3pf4lfPrzpQ2hZvLqFvt-IarSGBSCxFxD-y4,6274
@@ -154,8 +155,8 @@ lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx,sha256=VQy5fp
154
155
  lyrics_transcriber/frontend/src/components/EditWordList.tsx,sha256=XN59JnNPYNI4KrSenW7cYC6zUIlK7GvlyRbj9eg-Eac,13716
155
156
  lyrics_transcriber/frontend/src/components/FileUpload.tsx,sha256=fwn2rMWtMLPTZLREMb3ps4prSf9nzxGwnjmeC6KYsJA,2383
156
157
  lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx,sha256=U7duKns4IqNXwbWFbQfdyaswnvkSRpfsU0UG__-Serc,20192
157
- lyrics_transcriber/frontend/src/components/Header.tsx,sha256=UmQzQpsdLrtzi9U5HFDtvtr6vq9qZu3cnPDnT_T3QuI,18151
158
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx,sha256=TFo2HCr6k9bENsQm-AfT797ZyFLqvKDa8g5W-pw1v24,54256
158
+ lyrics_transcriber/frontend/src/components/Header.tsx,sha256=_lIo1ZsObF1lygXYX865Rhj053KPWvPewi7e0p11xuA,23429
159
+ lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx,sha256=9UZbTAXXTcYgFWwr030z-vrd8vklYrmgDCxCrPifho8,59256
159
160
  lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx,sha256=j4rQjBQVbaPsp1ra_rvEoCqmX3JFJdfNnFvj3BvfsgQ,6069
160
161
  lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx,sha256=DuQBAdF8bYcAYKNEWVklXSAlEykhIDQKbELq_4SEPCg,27415
161
162
  lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx,sha256=BXkEeo5yMgHkeOCBcZKqxMb1rspjXH-X5_6X9Hl7z3E,2588
@@ -171,16 +172,16 @@ lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx,sha256=VQg_gBF
171
172
  lyrics_transcriber/frontend/src/components/SegmentDetailsModal.tsx,sha256=6ME02FkFwCgDAxW49yW260N4vbr80eAJ332Ex811GOo,1643
172
173
  lyrics_transcriber/frontend/src/components/TimelineEditor.tsx,sha256=gJRCxdmJo80g0h5hq5AtDHK-HbOoYhMaQYvP2WgOuRI,13201
173
174
  lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx,sha256=aivGi6ehI6cDqwtoKBb6Eif8gpPqi0t3mJT8i5Feu7Q,4803
174
- lyrics_transcriber/frontend/src/components/TranscriptionView.tsx,sha256=SK5nVjey4Eg2_JiVAnJbOtNwDEmrKYlNb529F7Q0nLY,11337
175
+ lyrics_transcriber/frontend/src/components/TranscriptionView.tsx,sha256=6FqgDS6NBbdeGMANKQyF1rjxN0CZy6tYAheOq_FTYEE,11827
175
176
  lyrics_transcriber/frontend/src/components/WordDivider.tsx,sha256=ynib_j0w0q4iOYAk7D4IyZJCq71LykX7SaD9haGlZeI,6695
176
- lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx,sha256=ZVZ61rwrKLdlmhlNuSNR7-sgdl7JdBxVTeSeKO6K-3w,16321
177
+ lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx,sha256=0WFhL8BlcIrjw1BTakP-UgG0j2pripyqG5LK66a1IOE,21333
177
178
  lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx,sha256=FpMn-0i1NFxdIHZN0dxHkWzIIsOEi9CJc_rQMpLyxVw,2031
178
179
  lyrics_transcriber/frontend/src/components/shared/components/Word.tsx,sha256=CXzepxI3Negx2cqdfqLNGgesNbbIcczomXed71FupNw,2676
179
180
  lyrics_transcriber/frontend/src/components/shared/constants.ts,sha256=GByG5KFLJOX0iCs80_PLXxZKQ5FBX5Qw0Mg05Xf8Faw,1142
180
181
  lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts,sha256=eEgBHiKIWOzFK8eBzBgcQRv7StKpaPchqh3k2kFlwgY,6253
181
182
  lyrics_transcriber/frontend/src/components/shared/styles.ts,sha256=J1jCSuRqpk1mOFYAqJudhxeozH-q1bi-dsOibLukBJU,411
182
183
  lyrics_transcriber/frontend/src/components/shared/types.js,sha256=1DqoH1vIn6o1ng-XyBS6JRVVkf8Hj7ub_UD4x8loMjA,77
183
- lyrics_transcriber/frontend/src/components/shared/types.ts,sha256=22gpjxcUglmykSimUu6l_6XzgIga1fX8QIM1Mr0CpiI,3973
184
+ lyrics_transcriber/frontend/src/components/shared/types.ts,sha256=mA7YELw4x0TI3PG2-6EuB1WmGnG36h_C4iwTOvmIoaY,4249
184
185
  lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts,sha256=Yh5c_kOdOjE84FtKVB4BBC8QIgkFk5tO0ZJa9oJqqqU,5870
185
186
  lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts,sha256=jpLT65Rk_toaB-8X2lRGyYZ9EoMQDI45GviUT7N9Bp0,3240
186
187
  lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts,sha256=TJ2oHDitFFVxm83eFEhdlwvhx--mIt3054YbET2RiXs,2575
@@ -266,7 +267,7 @@ lyrics_transcriber/output/fonts/Zurich_Cn_BT_Bold.ttf,sha256=WNG5LOQ-uGUF_WWT5aQ
266
267
  lyrics_transcriber/output/fonts/arial.ttf,sha256=NcDzVZ2NtWnjbDEJW4pg1EFkPZX1kTneQOI_ragZuDM,275572
267
268
  lyrics_transcriber/output/fonts/georgia.ttf,sha256=fQuyDGMrtZ6BoIhfVzvSFz9x9zIE3pBY_raM4DIicHI,142964
268
269
  lyrics_transcriber/output/fonts/verdana.ttf,sha256=lu0UlJyktzks_yNbnEHVXBJTgqu-DA08K53WaJfK4Ms,139640
269
- lyrics_transcriber/output/generator.py,sha256=jruQv0FQHlVkrLML-rcJqWCR7n5YT1QRE9yDBjqQJy0,11990
270
+ lyrics_transcriber/output/generator.py,sha256=eblMtME-OBTbf1awd9BvlTLyUer_XcIXfIo0L-l38b4,13774
270
271
  lyrics_transcriber/output/lrc_to_cdg.py,sha256=2pi5tvreD_ADAR4RF5yVwj7OJ4Pf5Zo_EJ7rt4iH3k0,2063
271
272
  lyrics_transcriber/output/lyrics_file.py,sha256=_KQyQjCOMIwQdQ0115uEAUIjQWTRmShkSfQuINPKxaw,3741
272
273
  lyrics_transcriber/output/plain_text.py,sha256=XARaWcy6MeQeQCUoz0PV_bHoBw5dba-u79bjS7XucnE,3867
@@ -280,12 +281,13 @@ lyrics_transcriber/storage/dropbox.py,sha256=Dyam1ULTkoxD1X5trkZ5dGp5XhBGCn998mo
280
281
  lyrics_transcriber/transcribers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
281
282
  lyrics_transcriber/transcribers/audioshake.py,sha256=RihuLKzhhHfX7m5cjKISwIuTQkGWapCS29D6Qk3hR4U,15869
282
283
  lyrics_transcriber/transcribers/base_transcriber.py,sha256=T3m4ZCwZ9Bpv6Jvb2hNcnllk-lmeNmADDJlSySBtP1Q,6480
284
+ lyrics_transcriber/transcribers/local_whisper.py,sha256=oT-MsKdkHMgRpuCdYL4o8vtCZ4Uhls7BzkRDf-QHMHM,9926
283
285
  lyrics_transcriber/transcribers/whisper.py,sha256=YcCB1ic9H6zL1GS0jD0emu8-qlcH0QVEjjjYB4aLlIQ,13260
284
286
  lyrics_transcriber/types.py,sha256=UJjaxhVd2o14AG4G8ToU598p0JeYdiTFjpG38jGCoYQ,27917
285
287
  lyrics_transcriber/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
286
288
  lyrics_transcriber/utils/word_utils.py,sha256=-cMGpj9UV4F6IsoDKAV2i1aiqSO8eI91HMAm_igtVMk,958
287
- karaoke_gen-0.76.20.dist-info/METADATA,sha256=LuFlyEoDv5uHvTPpoUcAQChjNC1QdHnRBQzmdZRUPIw,20667
288
- karaoke_gen-0.76.20.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
289
- karaoke_gen-0.76.20.dist-info/entry_points.txt,sha256=xIyLe7K84ZyjO8L0_AmNectz93QjGSs5AkApMtlAd4g,160
290
- karaoke_gen-0.76.20.dist-info/licenses/LICENSE,sha256=81R_4XwMZDODHD7JcZeUR8IiCU8AD7Ajl6bmwR9tYDk,1074
291
- karaoke_gen-0.76.20.dist-info/RECORD,,
289
+ karaoke_gen-0.82.0.dist-info/METADATA,sha256=k51l0dhnVIM5kn6lFbTCLi1oKbcV_2iSulloMXVkU0Q,23077
290
+ karaoke_gen-0.82.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
291
+ karaoke_gen-0.82.0.dist-info/entry_points.txt,sha256=xIyLe7K84ZyjO8L0_AmNectz93QjGSs5AkApMtlAd4g,160
292
+ karaoke_gen-0.82.0.dist-info/licenses/LICENSE,sha256=81R_4XwMZDODHD7JcZeUR8IiCU8AD7Ajl6bmwR9tYDk,1074
293
+ karaoke_gen-0.82.0.dist-info/RECORD,,
@@ -11,6 +11,14 @@ class TranscriberConfig:
11
11
  runpod_api_key: Optional[str] = None
12
12
  whisper_runpod_id: Optional[str] = None
13
13
 
14
+ # Local Whisper configuration - reads from environment variables with sensible defaults
15
+ # Environment variables: WHISPER_MODEL_SIZE, WHISPER_DEVICE, WHISPER_CACHE_DIR, WHISPER_LANGUAGE
16
+ enable_local_whisper: bool = True # Enabled by default as fallback
17
+ local_whisper_model_size: str = field(default_factory=lambda: os.getenv("WHISPER_MODEL_SIZE", "medium"))
18
+ local_whisper_device: Optional[str] = field(default_factory=lambda: os.getenv("WHISPER_DEVICE"))
19
+ local_whisper_cache_dir: Optional[str] = field(default_factory=lambda: os.getenv("WHISPER_CACHE_DIR"))
20
+ local_whisper_language: Optional[str] = field(default_factory=lambda: os.getenv("WHISPER_LANGUAGE"))
21
+
14
22
 
15
23
  @dataclass
16
24
  class LyricsConfig:
@@ -7,6 +7,7 @@ from lyrics_transcriber.types import LyricsData, TranscriptionResult, Correction
7
7
  from lyrics_transcriber.transcribers.base_transcriber import BaseTranscriber
8
8
  from lyrics_transcriber.transcribers.audioshake import AudioShakeTranscriber, AudioShakeConfig
9
9
  from lyrics_transcriber.transcribers.whisper import WhisperTranscriber, WhisperConfig
10
+ from lyrics_transcriber.transcribers.local_whisper import LocalWhisperTranscriber, LocalWhisperConfig
10
11
  from lyrics_transcriber.lyrics.base_lyrics_provider import BaseLyricsProvider, LyricsProviderConfig
11
12
  from lyrics_transcriber.lyrics.genius import GeniusProvider
12
13
  from lyrics_transcriber.lyrics.spotify import SpotifyProvider
@@ -206,6 +207,34 @@ class LyricsTranscriber:
206
207
  else:
207
208
  self.logger.debug("Skipping Whisper transcriber - missing runpod_api_key or whisper_runpod_id")
208
209
 
210
+ # Local Whisper - lowest priority, fallback when cloud services unavailable
211
+ if self.transcriber_config.enable_local_whisper:
212
+ # Check if whisper-timestamped is available
213
+ try:
214
+ import whisper_timestamped # noqa: F401
215
+
216
+ self.logger.debug("Initializing LocalWhisper transcriber")
217
+ transcribers["local_whisper"] = {
218
+ "instance": LocalWhisperTranscriber(
219
+ cache_dir=self.output_config.cache_dir,
220
+ config=LocalWhisperConfig(
221
+ model_size=self.transcriber_config.local_whisper_model_size,
222
+ device=self.transcriber_config.local_whisper_device,
223
+ cache_dir=self.transcriber_config.local_whisper_cache_dir,
224
+ language=self.transcriber_config.local_whisper_language,
225
+ ),
226
+ logger=self.logger,
227
+ ),
228
+ "priority": 3, # Local Whisper has lowest priority (fallback)
229
+ }
230
+ except ImportError:
231
+ self.logger.debug(
232
+ "Skipping LocalWhisper transcriber - whisper-timestamped not installed. "
233
+ "Install with: pip install karaoke-gen[local-whisper]"
234
+ )
235
+ else:
236
+ self.logger.debug("Skipping LocalWhisper transcriber - disabled via enable_local_whisper=False")
237
+
209
238
  return transcribers
210
239
 
211
240
  def _initialize_lyrics_providers(self) -> Dict[str, BaseLyricsProvider]:
@@ -442,7 +471,7 @@ class LyricsTranscriber:
442
471
  # Whisper/RunPod status
443
472
  has_runpod_key = bool(self.transcriber_config.runpod_api_key)
444
473
  has_whisper_id = bool(self.transcriber_config.whisper_runpod_id)
445
-
474
+
446
475
  if has_runpod_key and has_whisper_id:
447
476
  self.logger.debug(" - Whisper (RunPod): CONFIGURED (API key and endpoint ID provided)")
448
477
  elif has_runpod_key:
@@ -452,6 +481,19 @@ class LyricsTranscriber:
452
481
  else:
453
482
  self.logger.debug(" - Whisper (RunPod): NOT CONFIGURED (missing RUNPOD_API_KEY and WHISPER_RUNPOD_ID)")
454
483
 
484
+ # Local Whisper status
485
+ if self.transcriber_config.enable_local_whisper:
486
+ try:
487
+ import whisper_timestamped # noqa: F401
488
+ self.logger.debug(
489
+ f" - LocalWhisper: AVAILABLE (model={self.transcriber_config.local_whisper_model_size}, "
490
+ f"device={self.transcriber_config.local_whisper_device or 'auto'})"
491
+ )
492
+ except ImportError:
493
+ self.logger.debug(" - LocalWhisper: ENABLED but whisper-timestamped not installed")
494
+ else:
495
+ self.logger.debug(" - LocalWhisper: DISABLED (enable_local_whisper=False)")
496
+
455
497
  def correct_lyrics(self) -> None:
456
498
  """Run lyrics correction using transcription and internet lyrics."""
457
499
  self.logger.info("Starting lyrics correction process")
@@ -1,28 +1,115 @@
1
- from typing import Optional, Dict, Any
1
+ """LangFuse integration for agentic correction observability and prompt management.
2
+
3
+ This module provides:
4
+ - Client initialization with fail-fast behavior when configured
5
+ - Metrics recording for observability
6
+ - Prompt fetching for dynamic prompt management
7
+ - Dataset fetching for few-shot examples
8
+ """
9
+
10
+ from typing import Optional, Dict, Any, List
2
11
  import os
3
- import threading
12
+ import logging
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # Module-level client singleton
17
+ _langfuse_client: Optional[Any] = None
18
+ _client_initialized: bool = False
19
+
20
+
21
+ class LangFuseConfigError(Exception):
22
+ """Raised when LangFuse is configured but initialization fails."""
23
+ pass
24
+
4
25
 
26
+ def is_langfuse_configured() -> bool:
27
+ """Check if LangFuse credentials are configured in environment."""
28
+ public_key = os.getenv("LANGFUSE_PUBLIC_KEY")
29
+ secret_key = os.getenv("LANGFUSE_SECRET_KEY")
30
+ return bool(public_key and secret_key)
5
31
 
6
- def setup_langfuse(client_name: str = "agentic-corrector") -> Optional[object]:
32
+
33
+ def setup_langfuse() -> Optional[object]:
7
34
  """Initialize Langfuse client if keys are present; return client or None.
8
35
 
9
36
  This avoids hard dependency at import time; caller can check for None and
10
37
  no-op if observability is not configured.
38
+
39
+ Note: This function does NOT fail fast - use get_langfuse_client() for
40
+ fail-fast behavior when LangFuse is required.
11
41
  """
12
42
  secret = os.getenv("LANGFUSE_SECRET_KEY")
13
43
  public = os.getenv("LANGFUSE_PUBLIC_KEY")
14
- host = os.getenv("LANGFUSE_HOST", "https://cloud.langfuse.com")
44
+ host = os.getenv("LANGFUSE_HOST", "https://us.cloud.langfuse.com")
15
45
  if not (secret and public):
16
46
  return None
17
47
  try:
18
48
  from langfuse import Langfuse # type: ignore
19
49
 
20
- client = Langfuse(secret_key=secret, public_key=public, host=host, sdk_integration=client_name)
50
+ client = Langfuse(secret_key=secret, public_key=public, host=host)
21
51
  return client
22
52
  except Exception:
23
53
  return None
24
54
 
25
55
 
56
+ def get_langfuse_client() -> Optional[Any]:
57
+ """Get or create the LangFuse client singleton.
58
+
59
+ Unlike setup_langfuse(), this function implements fail-fast behavior:
60
+ if LangFuse keys are configured but initialization fails, it raises
61
+ an exception rather than returning None.
62
+
63
+ Returns:
64
+ Langfuse client instance, or None if not configured
65
+
66
+ Raises:
67
+ LangFuseConfigError: If keys are set but initialization fails
68
+ """
69
+ global _langfuse_client, _client_initialized
70
+
71
+ if _client_initialized:
72
+ return _langfuse_client
73
+
74
+ secret = os.getenv("LANGFUSE_SECRET_KEY")
75
+ public = os.getenv("LANGFUSE_PUBLIC_KEY")
76
+ host = os.getenv("LANGFUSE_HOST", "https://us.cloud.langfuse.com")
77
+
78
+ if not (secret and public):
79
+ logger.debug("LangFuse keys not configured, client disabled")
80
+ _client_initialized = True
81
+ return None
82
+
83
+ try:
84
+ from langfuse import Langfuse
85
+
86
+ _langfuse_client = Langfuse(
87
+ secret_key=secret,
88
+ public_key=public,
89
+ host=host,
90
+ )
91
+ _client_initialized = True
92
+ logger.info(f"LangFuse client initialized (host: {host})")
93
+ return _langfuse_client
94
+
95
+ except Exception as e:
96
+ # Fail fast - if keys are set, we expect LangFuse to work
97
+ raise LangFuseConfigError(
98
+ f"LangFuse keys are set but initialization failed: {e}\n"
99
+ f"Check:\n"
100
+ f" - LANGFUSE_PUBLIC_KEY: {public[:10] if public else 'not set'}...\n"
101
+ f" - LANGFUSE_SECRET_KEY: {'set' if secret else 'not set'}\n"
102
+ f" - LANGFUSE_HOST: {host}"
103
+ ) from e
104
+
105
+
106
+ def reset_langfuse_client() -> None:
107
+ """Reset the global LangFuse client (for testing)."""
108
+ global _langfuse_client, _client_initialized
109
+ _langfuse_client = None
110
+ _client_initialized = False
111
+
112
+
26
113
  def record_metrics(client: Optional[object], name: str, metrics: Dict[str, Any]) -> None:
27
114
  """Record custom metrics to Langfuse if initialized."""
28
115
  if client is None:
@@ -33,3 +120,89 @@ def record_metrics(client: Optional[object], name: str, metrics: Dict[str, Any])
33
120
  except Exception:
34
121
  # Swallow observability errors to never impact core flow
35
122
  pass
123
+
124
+
125
+ def fetch_prompt(name: str, client: Optional[Any] = None, label: Optional[str] = "production") -> Any:
126
+ """Fetch a prompt template from LangFuse.
127
+
128
+ Args:
129
+ name: The prompt name in LangFuse
130
+ client: Optional pre-initialized client. If None, uses get_langfuse_client()
131
+ label: Prompt label to fetch (default: "production"). If the labeled version
132
+ is not found, falls back to version 1.
133
+
134
+ Returns:
135
+ LangFuse prompt object
136
+
137
+ Raises:
138
+ LangFuseConfigError: If LangFuse is not configured
139
+ RuntimeError: If prompt fetch fails
140
+ """
141
+ if client is None:
142
+ client = get_langfuse_client()
143
+
144
+ if client is None:
145
+ raise LangFuseConfigError(
146
+ f"Cannot fetch prompt '{name}': LangFuse is not configured. "
147
+ f"Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY."
148
+ )
149
+
150
+ try:
151
+ # Try to fetch with the specified label (default: production)
152
+ prompt = client.get_prompt(name, label=label)
153
+ logger.debug(f"Fetched prompt '{name}' (label={label}) from LangFuse")
154
+ return prompt
155
+ except Exception as label_error:
156
+ # If labeled version not found, try fetching version 1 as fallback
157
+ # This handles newly created prompts that haven't been promoted yet
158
+ try:
159
+ prompt = client.get_prompt(name, version=1)
160
+ logger.warning(
161
+ f"Prompt '{name}' label '{label}' not found, using version 1. "
162
+ f"Consider promoting this prompt in LangFuse UI."
163
+ )
164
+ return prompt
165
+ except Exception as version_error:
166
+ raise RuntimeError(
167
+ f"Failed to fetch prompt '{name}' from LangFuse: "
168
+ f"Label '{label}' error: {label_error}, "
169
+ f"Version 1 fallback error: {version_error}"
170
+ ) from version_error
171
+
172
+
173
+ def fetch_dataset(name: str, client: Optional[Any] = None) -> List[Dict[str, Any]]:
174
+ """Fetch a dataset from LangFuse and return its items.
175
+
176
+ Args:
177
+ name: The dataset name in LangFuse
178
+ client: Optional pre-initialized client. If None, uses get_langfuse_client()
179
+
180
+ Returns:
181
+ List of dataset item inputs (the actual example data)
182
+
183
+ Raises:
184
+ LangFuseConfigError: If LangFuse is not configured
185
+ RuntimeError: If dataset fetch fails
186
+ """
187
+ if client is None:
188
+ client = get_langfuse_client()
189
+
190
+ if client is None:
191
+ raise LangFuseConfigError(
192
+ f"Cannot fetch dataset '{name}': LangFuse is not configured. "
193
+ f"Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY."
194
+ )
195
+
196
+ try:
197
+ dataset = client.get_dataset(name)
198
+ items = []
199
+ for item in dataset.items:
200
+ if hasattr(item, 'input') and item.input:
201
+ items.append(item.input)
202
+
203
+ logger.debug(f"Fetched {len(items)} items from dataset '{name}'")
204
+ return items
205
+ except Exception as e:
206
+ raise RuntimeError(
207
+ f"Failed to fetch dataset '{name}' from LangFuse: {e}"
208
+ ) from e
@@ -1,2 +1,25 @@
1
1
  """Prompt templates for agentic correction."""
2
2
 
3
+ from .classifier import (
4
+ build_classification_prompt,
5
+ build_classification_prompt_hardcoded,
6
+ get_hardcoded_examples,
7
+ )
8
+ from .langfuse_prompts import (
9
+ LangFusePromptService,
10
+ LangFusePromptError,
11
+ LangFuseDatasetError,
12
+ get_prompt_service,
13
+ reset_prompt_service,
14
+ )
15
+
16
+ __all__ = [
17
+ "build_classification_prompt",
18
+ "build_classification_prompt_hardcoded",
19
+ "get_hardcoded_examples",
20
+ "LangFusePromptService",
21
+ "LangFusePromptError",
22
+ "LangFuseDatasetError",
23
+ "get_prompt_service",
24
+ "reset_prompt_service",
25
+ ]
@@ -1,23 +1,35 @@
1
- """Gap classification prompt builder for agentic correction."""
1
+ """Gap classification prompt builder for agentic correction.
2
+
3
+ This module provides two modes of operation:
4
+ 1. LangFuse mode: Prompts and examples fetched from LangFuse for dynamic iteration
5
+ 2. Hardcoded mode: Fallback for local development when LangFuse is not configured
6
+
7
+ The main entry point is `build_classification_prompt()` which automatically
8
+ selects the appropriate mode based on LangFuse configuration.
9
+ """
2
10
 
3
11
  from typing import Dict, List, Optional
4
12
  import yaml
5
13
  import os
14
+ import logging
6
15
  from pathlib import Path
7
16
 
17
+ logger = logging.getLogger(__name__)
18
+
8
19
 
9
20
  def load_few_shot_examples() -> Dict[str, List[Dict]]:
10
21
  """Load few-shot examples from examples.yaml if it exists."""
11
22
  examples_path = Path(__file__).parent / "examples.yaml"
12
-
23
+
13
24
  if not examples_path.exists():
14
25
  return get_hardcoded_examples()
15
-
26
+
16
27
  try:
17
28
  with open(examples_path, 'r') as f:
18
29
  data = yaml.safe_load(f)
19
30
  return data.get('examples_by_category', {})
20
- except Exception:
31
+ except Exception as e:
32
+ logger.warning(f"Failed to load examples.yaml, using hardcoded examples: {e}")
21
33
  return get_hardcoded_examples()
22
34
 
23
35
 
@@ -122,7 +134,12 @@ def build_classification_prompt(
122
134
  gap_id: Optional[str] = None
123
135
  ) -> str:
124
136
  """Build a prompt for classifying a gap in the transcription.
125
-
137
+
138
+ This function automatically selects between LangFuse and hardcoded prompts:
139
+ - If LangFuse is configured (LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY set),
140
+ fetches the prompt template and examples from LangFuse.
141
+ - Otherwise, uses hardcoded prompts for local development.
142
+
126
143
  Args:
127
144
  gap_text: The text of the gap that needs classification
128
145
  preceding_words: Text immediately before the gap
@@ -131,7 +148,50 @@ def build_classification_prompt(
131
148
  artist: Song artist name for context
132
149
  title: Song title for context
133
150
  gap_id: Identifier for the gap
134
-
151
+
152
+ Returns:
153
+ Formatted prompt string for the LLM
154
+
155
+ Raises:
156
+ LangFusePromptError: If LangFuse is configured but prompt fetch fails
157
+ """
158
+ from .langfuse_prompts import get_prompt_service
159
+
160
+ service = get_prompt_service()
161
+ return service.get_classification_prompt(
162
+ gap_text=gap_text,
163
+ preceding_words=preceding_words,
164
+ following_words=following_words,
165
+ reference_contexts=reference_contexts,
166
+ artist=artist,
167
+ title=title,
168
+ gap_id=gap_id
169
+ )
170
+
171
+
172
+ def build_classification_prompt_hardcoded(
173
+ gap_text: str,
174
+ preceding_words: str,
175
+ following_words: str,
176
+ reference_contexts: Dict[str, str],
177
+ artist: Optional[str] = None,
178
+ title: Optional[str] = None,
179
+ gap_id: Optional[str] = None
180
+ ) -> str:
181
+ """Build a prompt for classifying a gap using hardcoded templates.
182
+
183
+ This is the fallback implementation used when LangFuse is not configured.
184
+ It is also used as the source of truth for migrating prompts to LangFuse.
185
+
186
+ Args:
187
+ gap_text: The text of the gap that needs classification
188
+ preceding_words: Text immediately before the gap
189
+ following_words: Text immediately after the gap
190
+ reference_contexts: Dictionary of reference lyrics from each source
191
+ artist: Song artist name for context
192
+ title: Song title for context
193
+ gap_id: Identifier for the gap
194
+
135
195
  Returns:
136
196
  Formatted prompt string for the LLM
137
197
  """