karaoke-gen 0.105.4__py3-none-any.whl → 0.107.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 (322) hide show
  1. backend/api/routes/users.py +14 -3
  2. backend/config.py +3 -0
  3. backend/services/encoding_interface.py +4 -0
  4. backend/services/job_notification_service.py +4 -21
  5. backend/tests/test_job_notification_service.py +24 -58
  6. backend/tests/test_video_worker_orchestrator.py +189 -0
  7. backend/workers/video_worker_orchestrator.py +7 -0
  8. karaoke_gen/instrumental_review/server.py +145 -35
  9. karaoke_gen/nextjs_frontend/__init__.py +98 -0
  10. karaoke_gen/nextjs_frontend/out/404/index.html +1 -0
  11. karaoke_gen/nextjs_frontend/out/404.html +1 -0
  12. karaoke_gen/nextjs_frontend/out/__next.__PAGE__.txt +9 -0
  13. karaoke_gen/nextjs_frontend/out/__next._full.txt +22 -0
  14. karaoke_gen/nextjs_frontend/out/__next._head.txt +8 -0
  15. karaoke_gen/nextjs_frontend/out/__next._index.txt +9 -0
  16. karaoke_gen/nextjs_frontend/out/__next._tree.txt +2 -0
  17. karaoke_gen/nextjs_frontend/out/_next/static/chunks/01a7f8fe40f1ff47.js +1 -0
  18. karaoke_gen/nextjs_frontend/out/_next/static/chunks/112f346e31f991df.js +4 -0
  19. karaoke_gen/nextjs_frontend/out/_next/static/chunks/16d1a4dd9d8a873a.js +3 -0
  20. karaoke_gen/nextjs_frontend/out/_next/static/chunks/1ab85c362b8b0e86.js +9 -0
  21. karaoke_gen/nextjs_frontend/out/_next/static/chunks/247eb132b7f7b574.js +1 -0
  22. karaoke_gen/nextjs_frontend/out/_next/static/chunks/2b80d15cc95e4818.js +1 -0
  23. karaoke_gen/nextjs_frontend/out/_next/static/chunks/32c7eba5cd46c1bc.js +7 -0
  24. karaoke_gen/nextjs_frontend/out/_next/static/chunks/483f26794eae53d0.js +1 -0
  25. karaoke_gen/nextjs_frontend/out/_next/static/chunks/550c3b02e85f196a.js +1 -0
  26. karaoke_gen/nextjs_frontend/out/_next/static/chunks/55c5ade44387bef8.js +1 -0
  27. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5628d92b5893add2.css +1 -0
  28. karaoke_gen/nextjs_frontend/out/_next/static/chunks/56ebf7665e4341c8.js +7 -0
  29. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5997132b61dec430.js +1 -0
  30. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5ea55255bce3eb9e.js +5 -0
  31. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5eda89a57490b3cd.js +1 -0
  32. karaoke_gen/nextjs_frontend/out/_next/static/chunks/692f5d9e0d700c76.js +3 -0
  33. karaoke_gen/nextjs_frontend/out/_next/static/chunks/71d7a05b14f9f0f4.js +1 -0
  34. karaoke_gen/nextjs_frontend/out/_next/static/chunks/81ac355749ef3302.js +1 -0
  35. karaoke_gen/nextjs_frontend/out/_next/static/chunks/95f7e5934dbb0e5d.js +1 -0
  36. karaoke_gen/nextjs_frontend/out/_next/static/chunks/9bce8f19eaa46940.js +1 -0
  37. karaoke_gen/nextjs_frontend/out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  38. karaoke_gen/nextjs_frontend/out/_next/static/chunks/a9ed54eed3e14c92.js +2 -0
  39. karaoke_gen/nextjs_frontend/out/_next/static/chunks/b35cd41238ecfb17.js +1 -0
  40. karaoke_gen/nextjs_frontend/out/_next/static/chunks/b5bc3c3d5ebd49eb.js +1 -0
  41. karaoke_gen/nextjs_frontend/out/_next/static/chunks/b5c078c08db5ae32.js +5 -0
  42. karaoke_gen/nextjs_frontend/out/_next/static/chunks/be9c44a178104187.js +1 -0
  43. karaoke_gen/nextjs_frontend/out/_next/static/chunks/c4c840e18cb4861c.js +1 -0
  44. karaoke_gen/nextjs_frontend/out/_next/static/chunks/c645af7d6b65f73e.js +1 -0
  45. karaoke_gen/nextjs_frontend/out/_next/static/chunks/d2c5e2575df784d4.js +1 -0
  46. karaoke_gen/nextjs_frontend/out/_next/static/chunks/d30af02b96d81462.js +1 -0
  47. karaoke_gen/nextjs_frontend/out/_next/static/chunks/d9bdf64f4ec1e9b7.js +7 -0
  48. karaoke_gen/nextjs_frontend/out/_next/static/chunks/dcde6ed684dacd0e.js +5 -0
  49. karaoke_gen/nextjs_frontend/out/_next/static/chunks/e422cbe931246000.js +1 -0
  50. karaoke_gen/nextjs_frontend/out/_next/static/chunks/e483af34fc792d38.js +1 -0
  51. karaoke_gen/nextjs_frontend/out/_next/static/chunks/e57422aad6b897da.js +1 -0
  52. karaoke_gen/nextjs_frontend/out/_next/static/chunks/ef02697fb404726a.js +1 -0
  53. karaoke_gen/nextjs_frontend/out/_next/static/chunks/ff1a16fafef87110.js +1 -0
  54. karaoke_gen/nextjs_frontend/out/_next/static/chunks/turbopack-2d9ca3017a9deedf.js +3 -0
  55. karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_buildManifest.js +11 -0
  56. karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_clientMiddlewareManifest.json +1 -0
  57. karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_ssgManifest.js +1 -0
  58. karaoke_gen/nextjs_frontend/out/_not-found/__next._full.txt +18 -0
  59. karaoke_gen/nextjs_frontend/out/_not-found/__next._head.txt +8 -0
  60. karaoke_gen/nextjs_frontend/out/_not-found/__next._index.txt +9 -0
  61. karaoke_gen/nextjs_frontend/out/_not-found/__next._not-found.__PAGE__.txt +5 -0
  62. karaoke_gen/nextjs_frontend/out/_not-found/__next._not-found.txt +4 -0
  63. karaoke_gen/nextjs_frontend/out/_not-found/__next._tree.txt +2 -0
  64. karaoke_gen/nextjs_frontend/out/_not-found/index.html +1 -0
  65. karaoke_gen/nextjs_frontend/out/_not-found/index.txt +18 -0
  66. karaoke_gen/nextjs_frontend/out/admin/__next._full.txt +25 -0
  67. karaoke_gen/nextjs_frontend/out/admin/__next._head.txt +8 -0
  68. karaoke_gen/nextjs_frontend/out/admin/__next._index.txt +9 -0
  69. karaoke_gen/nextjs_frontend/out/admin/__next._tree.txt +2 -0
  70. karaoke_gen/nextjs_frontend/out/admin/__next.admin.__PAGE__.txt +9 -0
  71. karaoke_gen/nextjs_frontend/out/admin/__next.admin.txt +7 -0
  72. karaoke_gen/nextjs_frontend/out/admin/beta/__next._full.txt +25 -0
  73. karaoke_gen/nextjs_frontend/out/admin/beta/__next._head.txt +8 -0
  74. karaoke_gen/nextjs_frontend/out/admin/beta/__next._index.txt +9 -0
  75. karaoke_gen/nextjs_frontend/out/admin/beta/__next._tree.txt +2 -0
  76. karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.beta.__PAGE__.txt +9 -0
  77. karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.beta.txt +4 -0
  78. karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.txt +7 -0
  79. karaoke_gen/nextjs_frontend/out/admin/beta/index.html +1 -0
  80. karaoke_gen/nextjs_frontend/out/admin/beta/index.txt +25 -0
  81. karaoke_gen/nextjs_frontend/out/admin/index.html +1 -0
  82. karaoke_gen/nextjs_frontend/out/admin/index.txt +25 -0
  83. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._full.txt +25 -0
  84. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._head.txt +8 -0
  85. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._index.txt +9 -0
  86. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._tree.txt +2 -0
  87. karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.jobs.__PAGE__.txt +9 -0
  88. karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.jobs.txt +4 -0
  89. karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.txt +7 -0
  90. karaoke_gen/nextjs_frontend/out/admin/jobs/index.html +1 -0
  91. karaoke_gen/nextjs_frontend/out/admin/jobs/index.txt +25 -0
  92. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._full.txt +25 -0
  93. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._head.txt +8 -0
  94. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._index.txt +9 -0
  95. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._tree.txt +2 -0
  96. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.rate-limits.__PAGE__.txt +9 -0
  97. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.rate-limits.txt +4 -0
  98. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.txt +7 -0
  99. karaoke_gen/nextjs_frontend/out/admin/rate-limits/index.html +1 -0
  100. karaoke_gen/nextjs_frontend/out/admin/rate-limits/index.txt +25 -0
  101. karaoke_gen/nextjs_frontend/out/admin/searches/__next._full.txt +25 -0
  102. karaoke_gen/nextjs_frontend/out/admin/searches/__next._head.txt +8 -0
  103. karaoke_gen/nextjs_frontend/out/admin/searches/__next._index.txt +9 -0
  104. karaoke_gen/nextjs_frontend/out/admin/searches/__next._tree.txt +2 -0
  105. karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.searches.__PAGE__.txt +9 -0
  106. karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.searches.txt +4 -0
  107. karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.txt +7 -0
  108. karaoke_gen/nextjs_frontend/out/admin/searches/index.html +1 -0
  109. karaoke_gen/nextjs_frontend/out/admin/searches/index.txt +25 -0
  110. karaoke_gen/nextjs_frontend/out/admin/users/__next._full.txt +25 -0
  111. karaoke_gen/nextjs_frontend/out/admin/users/__next._head.txt +8 -0
  112. karaoke_gen/nextjs_frontend/out/admin/users/__next._index.txt +9 -0
  113. karaoke_gen/nextjs_frontend/out/admin/users/__next._tree.txt +2 -0
  114. karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.txt +7 -0
  115. karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.users.__PAGE__.txt +9 -0
  116. karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.users.txt +4 -0
  117. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._full.txt +25 -0
  118. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._head.txt +8 -0
  119. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._index.txt +9 -0
  120. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._tree.txt +2 -0
  121. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.txt +7 -0
  122. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.detail.__PAGE__.txt +9 -0
  123. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.detail.txt +4 -0
  124. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.txt +4 -0
  125. karaoke_gen/nextjs_frontend/out/admin/users/detail/index.html +1 -0
  126. karaoke_gen/nextjs_frontend/out/admin/users/detail/index.txt +25 -0
  127. karaoke_gen/nextjs_frontend/out/admin/users/index.html +1 -0
  128. karaoke_gen/nextjs_frontend/out/admin/users/index.txt +25 -0
  129. karaoke_gen/nextjs_frontend/out/app/__next._full.txt +22 -0
  130. karaoke_gen/nextjs_frontend/out/app/__next._head.txt +8 -0
  131. karaoke_gen/nextjs_frontend/out/app/__next._index.txt +9 -0
  132. karaoke_gen/nextjs_frontend/out/app/__next._tree.txt +2 -0
  133. karaoke_gen/nextjs_frontend/out/app/__next.app.__PAGE__.txt +9 -0
  134. karaoke_gen/nextjs_frontend/out/app/__next.app.txt +4 -0
  135. karaoke_gen/nextjs_frontend/out/app/index.html +1 -0
  136. karaoke_gen/nextjs_frontend/out/app/index.txt +22 -0
  137. karaoke_gen/nextjs_frontend/out/app/jobs/__next._full.txt +19 -0
  138. karaoke_gen/nextjs_frontend/out/app/jobs/__next._head.txt +8 -0
  139. karaoke_gen/nextjs_frontend/out/app/jobs/__next._index.txt +9 -0
  140. karaoke_gen/nextjs_frontend/out/app/jobs/__next._tree.txt +2 -0
  141. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
  142. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.$oc$slug.txt +4 -0
  143. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.txt +4 -0
  144. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.txt +4 -0
  145. karaoke_gen/nextjs_frontend/out/app/jobs/index.html +1 -0
  146. karaoke_gen/nextjs_frontend/out/app/jobs/index.txt +19 -0
  147. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._full.txt +19 -0
  148. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._head.txt +8 -0
  149. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._index.txt +9 -0
  150. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._tree.txt +2 -0
  151. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
  152. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.$oc$slug.txt +4 -0
  153. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.txt +4 -0
  154. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.txt +4 -0
  155. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/index.html +1 -0
  156. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/index.txt +19 -0
  157. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._full.txt +19 -0
  158. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._head.txt +8 -0
  159. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._index.txt +9 -0
  160. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._tree.txt +2 -0
  161. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
  162. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.$oc$slug.txt +4 -0
  163. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.txt +4 -0
  164. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.txt +4 -0
  165. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/index.html +1 -0
  166. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/index.txt +19 -0
  167. karaoke_gen/nextjs_frontend/out/auth/verify/__next._full.txt +22 -0
  168. karaoke_gen/nextjs_frontend/out/auth/verify/__next._head.txt +8 -0
  169. karaoke_gen/nextjs_frontend/out/auth/verify/__next._index.txt +9 -0
  170. karaoke_gen/nextjs_frontend/out/auth/verify/__next._tree.txt +2 -0
  171. karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.txt +4 -0
  172. karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.verify.__PAGE__.txt +9 -0
  173. karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.verify.txt +4 -0
  174. karaoke_gen/nextjs_frontend/out/auth/verify/index.html +1 -0
  175. karaoke_gen/nextjs_frontend/out/auth/verify/index.txt +22 -0
  176. karaoke_gen/nextjs_frontend/out/index.html +1 -0
  177. karaoke_gen/nextjs_frontend/out/index.txt +22 -0
  178. karaoke_gen/nextjs_frontend/out/manifest.webmanifest +31 -0
  179. karaoke_gen/nextjs_frontend/out/order/success/__next._full.txt +22 -0
  180. karaoke_gen/nextjs_frontend/out/order/success/__next._head.txt +8 -0
  181. karaoke_gen/nextjs_frontend/out/order/success/__next._index.txt +9 -0
  182. karaoke_gen/nextjs_frontend/out/order/success/__next._tree.txt +2 -0
  183. karaoke_gen/nextjs_frontend/out/order/success/__next.order.success.__PAGE__.txt +9 -0
  184. karaoke_gen/nextjs_frontend/out/order/success/__next.order.success.txt +4 -0
  185. karaoke_gen/nextjs_frontend/out/order/success/__next.order.txt +4 -0
  186. karaoke_gen/nextjs_frontend/out/order/success/index.html +1 -0
  187. karaoke_gen/nextjs_frontend/out/order/success/index.txt +22 -0
  188. karaoke_gen/nextjs_frontend/out/payment/success/__next._full.txt +22 -0
  189. karaoke_gen/nextjs_frontend/out/payment/success/__next._head.txt +8 -0
  190. karaoke_gen/nextjs_frontend/out/payment/success/__next._index.txt +9 -0
  191. karaoke_gen/nextjs_frontend/out/payment/success/__next._tree.txt +2 -0
  192. karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.success.__PAGE__.txt +9 -0
  193. karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.success.txt +4 -0
  194. karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.txt +4 -0
  195. karaoke_gen/nextjs_frontend/out/payment/success/index.html +1 -0
  196. karaoke_gen/nextjs_frontend/out/payment/success/index.txt +22 -0
  197. karaoke_gen/nextjs_frontend/out/screenshots/email-action_reminder.png +0 -0
  198. karaoke_gen/nextjs_frontend/out/screenshots/email-beta_welcome.png +0 -0
  199. karaoke_gen/nextjs_frontend/out/screenshots/email-job_completion.png +0 -0
  200. karaoke_gen/nextjs_frontend/out/screenshots/example-output.avif +0 -0
  201. karaoke_gen/nextjs_frontend/out/screenshots/homepage-full.png +0 -0
  202. karaoke_gen/nextjs_frontend/out/screenshots/homepage-hero.png +0 -0
  203. karaoke_gen/nextjs_frontend/out/screenshots/instrumental-review.avif +0 -0
  204. karaoke_gen/nextjs_frontend/out/screenshots/instrumental-review.png +0 -0
  205. karaoke_gen/nextjs_frontend/out/screenshots/job-dashboard.avif +0 -0
  206. karaoke_gen/nextjs_frontend/out/screenshots/lyrics-review.avif +0 -0
  207. karaoke_gen/nextjs_frontend/out/screenshots/lyrics-review.png +0 -0
  208. karaoke_gen/nextjs_frontend/out/sw.js +183 -0
  209. karaoke_gen/utils/cli_args.py +3 -3
  210. karaoke_gen/utils/gen_cli.py +4 -0
  211. karaoke_gen/utils/remote_cli.py +8 -40
  212. {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/METADATA +1 -1
  213. {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/RECORD +227 -121
  214. {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/WHEEL +1 -1
  215. lyrics_transcriber/correction/agentic/agent.py +83 -60
  216. lyrics_transcriber/correction/anchor_sequence.py +48 -3
  217. lyrics_transcriber/correction/corrector.py +92 -58
  218. lyrics_transcriber/review/server.py +165 -33
  219. lyrics_transcriber/utils/tracing.py +214 -0
  220. karaoke_gen/instrumental_review/static/index.html +0 -1721
  221. lyrics_transcriber/frontend/.gitignore +0 -24
  222. lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs +0 -935
  223. lyrics_transcriber/frontend/.yarnrc.yml +0 -3
  224. lyrics_transcriber/frontend/README.md +0 -50
  225. lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +0 -210
  226. lyrics_transcriber/frontend/__init__.py +0 -25
  227. lyrics_transcriber/frontend/e2e/agentic-corrections.spec.ts +0 -207
  228. lyrics_transcriber/frontend/e2e/fixtures/agentic-correction-data.json +0 -226
  229. lyrics_transcriber/frontend/eslint.config.js +0 -28
  230. lyrics_transcriber/frontend/index.html +0 -22
  231. lyrics_transcriber/frontend/package-lock.json +0 -4553
  232. lyrics_transcriber/frontend/package.json +0 -48
  233. lyrics_transcriber/frontend/playwright.config.ts +0 -69
  234. lyrics_transcriber/frontend/public/android-chrome-192x192.png +0 -0
  235. lyrics_transcriber/frontend/public/android-chrome-512x512.png +0 -0
  236. lyrics_transcriber/frontend/src/App.tsx +0 -243
  237. lyrics_transcriber/frontend/src/api.ts +0 -262
  238. lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +0 -111
  239. lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +0 -114
  240. lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx +0 -204
  241. lyrics_transcriber/frontend/src/components/AppHeader.tsx +0 -65
  242. lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +0 -180
  243. lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +0 -175
  244. lyrics_transcriber/frontend/src/components/CorrectionAnnotationModal.tsx +0 -359
  245. lyrics_transcriber/frontend/src/components/CorrectionDetailCard.tsx +0 -281
  246. lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +0 -162
  247. lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +0 -257
  248. lyrics_transcriber/frontend/src/components/EditActionBar.tsx +0 -94
  249. lyrics_transcriber/frontend/src/components/EditModal.tsx +0 -720
  250. lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +0 -592
  251. lyrics_transcriber/frontend/src/components/EditWordList.tsx +0 -431
  252. lyrics_transcriber/frontend/src/components/FileUpload.tsx +0 -77
  253. lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +0 -467
  254. lyrics_transcriber/frontend/src/components/Header.tsx +0 -520
  255. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +0 -1526
  256. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +0 -216
  257. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +0 -721
  258. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx +0 -80
  259. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +0 -999
  260. lyrics_transcriber/frontend/src/components/MetricsDashboard.tsx +0 -51
  261. lyrics_transcriber/frontend/src/components/ModeSelectionModal.tsx +0 -127
  262. lyrics_transcriber/frontend/src/components/ModeSelector.tsx +0 -67
  263. lyrics_transcriber/frontend/src/components/ModelSelector.tsx +0 -23
  264. lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +0 -177
  265. lyrics_transcriber/frontend/src/components/ReferenceView.tsx +0 -268
  266. lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +0 -336
  267. lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +0 -354
  268. lyrics_transcriber/frontend/src/components/SegmentDetailsModal.tsx +0 -64
  269. lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +0 -383
  270. lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +0 -131
  271. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +0 -266
  272. lyrics_transcriber/frontend/src/components/WordDivider.tsx +0 -191
  273. lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +0 -466
  274. lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +0 -56
  275. lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +0 -89
  276. lyrics_transcriber/frontend/src/components/shared/constants.ts +0 -30
  277. lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +0 -180
  278. lyrics_transcriber/frontend/src/components/shared/styles.ts +0 -13
  279. lyrics_transcriber/frontend/src/components/shared/types.js +0 -2
  280. lyrics_transcriber/frontend/src/components/shared/types.ts +0 -135
  281. lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +0 -177
  282. lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts +0 -78
  283. lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +0 -75
  284. lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +0 -360
  285. lyrics_transcriber/frontend/src/components/shared/utils/timingUtils.ts +0 -110
  286. lyrics_transcriber/frontend/src/components/shared/utils/wordUtils.ts +0 -22
  287. lyrics_transcriber/frontend/src/hooks/useManualSync.ts +0 -537
  288. lyrics_transcriber/frontend/src/main.tsx +0 -11
  289. lyrics_transcriber/frontend/src/theme.ts +0 -406
  290. lyrics_transcriber/frontend/src/types/global.d.ts +0 -9
  291. lyrics_transcriber/frontend/src/types.js +0 -2
  292. lyrics_transcriber/frontend/src/types.ts +0 -199
  293. lyrics_transcriber/frontend/src/validation.ts +0 -132
  294. lyrics_transcriber/frontend/src/vite-env.d.ts +0 -1
  295. lyrics_transcriber/frontend/tsconfig.app.json +0 -26
  296. lyrics_transcriber/frontend/tsconfig.json +0 -25
  297. lyrics_transcriber/frontend/tsconfig.node.json +0 -23
  298. lyrics_transcriber/frontend/tsconfig.tsbuildinfo +0 -1
  299. lyrics_transcriber/frontend/update_version.js +0 -11
  300. lyrics_transcriber/frontend/vite.config.d.ts +0 -2
  301. lyrics_transcriber/frontend/vite.config.js +0 -15
  302. lyrics_transcriber/frontend/vite.config.ts +0 -16
  303. lyrics_transcriber/frontend/web_assets/android-chrome-192x192.png +0 -0
  304. lyrics_transcriber/frontend/web_assets/android-chrome-512x512.png +0 -0
  305. lyrics_transcriber/frontend/web_assets/apple-touch-icon.png +0 -0
  306. lyrics_transcriber/frontend/web_assets/assets/index-BSMgOq4Z.js +0 -44465
  307. lyrics_transcriber/frontend/web_assets/assets/index-BSMgOq4Z.js.map +0 -1
  308. lyrics_transcriber/frontend/web_assets/favicon-16x16.png +0 -0
  309. lyrics_transcriber/frontend/web_assets/favicon-32x32.png +0 -0
  310. lyrics_transcriber/frontend/web_assets/favicon.ico +0 -0
  311. lyrics_transcriber/frontend/web_assets/index.html +0 -22
  312. lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.png +0 -0
  313. lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.svg +0 -5
  314. lyrics_transcriber/frontend/yarn.lock +0 -3711
  315. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/apple-touch-icon.png +0 -0
  316. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon-16x16.png +0 -0
  317. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon-32x32.png +0 -0
  318. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon.ico +0 -0
  319. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/nomad-karaoke-logo.svg +0 -0
  320. /lyrics_transcriber/frontend/public/nomad-karaoke-logo.png → /karaoke_gen/nextjs_frontend/out/nomad-logo.png +0 -0
  321. {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/entry_points.txt +0 -0
  322. {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/licenses/LICENSE +0 -0
@@ -13,6 +13,7 @@ from .models.schemas import CorrectionProposal, GapClassification, GapCategory
13
13
  from .workflows.correction_graph import build_correction_graph
14
14
  from .prompts.classifier import build_classification_prompt
15
15
  from .handlers.registry import HandlerRegistry
16
+ from lyrics_transcriber.utils.tracing import create_span, add_span_attribute
16
17
 
17
18
  logger = logging.getLogger(__name__)
18
19
 
@@ -211,72 +212,94 @@ class AgenticCorrector:
211
212
  Returns:
212
213
  List of CorrectionProposal objects
213
214
  """
214
- # Step 1: Classify the gap
215
- gap_text = ' '.join(w.get('text', '') for w in gap_words)
216
- classification = self.classify_gap(
217
- gap_id=gap_id,
218
- gap_text=gap_text,
219
- preceding_words=preceding_words,
220
- following_words=following_words,
221
- reference_contexts=reference_contexts,
222
- artist=artist,
223
- title=title
224
- )
225
-
226
- if not classification:
227
- # Classification failed, flag for human review
228
- logger.warning(f"🤖 Classification failed for gap {gap_id}, flagging for review")
229
- return [CorrectionProposal(
230
- word_ids=[w['id'] for w in gap_words],
231
- action="Flag",
232
- confidence=0.0,
233
- reason="Classification failed - unable to categorize gap",
234
- requires_human_review=True,
235
- artist=artist,
236
- title=title
237
- )]
238
-
239
- # Step 2: Route to appropriate handler based on category
240
- try:
241
- handler = HandlerRegistry.get_handler(
242
- category=classification.category,
243
- artist=artist,
244
- title=title
245
- )
246
-
247
- proposals = handler.handle(
215
+ with create_span("agentic.propose_for_gap", {
216
+ "gap_id": gap_id,
217
+ "word_count": len(gap_words),
218
+ }) as span:
219
+ # Step 1: Classify the gap
220
+ gap_text = ' '.join(w.get('text', '') for w in gap_words)
221
+ classification = self.classify_gap(
248
222
  gap_id=gap_id,
249
- gap_words=gap_words,
223
+ gap_text=gap_text,
250
224
  preceding_words=preceding_words,
251
225
  following_words=following_words,
252
226
  reference_contexts=reference_contexts,
253
- classification_reasoning=classification.reasoning
254
- )
255
-
256
- # Add classification metadata to proposals
257
- for proposal in proposals:
258
- if not proposal.gap_category:
259
- proposal.gap_category = classification.category
260
- if not proposal.artist:
261
- proposal.artist = artist
262
- if not proposal.title:
263
- proposal.title = title
264
-
265
- return proposals
266
-
267
- except Exception as e:
268
- logger.error(f"🤖 Handler failed for gap {gap_id} (category: {classification.category}): {e}")
269
- # Handler failed, flag for human review
270
- return [CorrectionProposal(
271
- word_ids=[w['id'] for w in gap_words],
272
- action="Flag",
273
- confidence=0.0,
274
- reason=f"Handler error for category {classification.category}: {str(e)}",
275
- gap_category=classification.category,
276
- requires_human_review=True,
277
227
  artist=artist,
278
228
  title=title
279
- )]
229
+ )
230
+
231
+ if not classification:
232
+ # Classification failed, flag for human review
233
+ logger.warning(f"🤖 Classification failed for gap {gap_id}, flagging for review")
234
+ if span:
235
+ span.set_attribute("classification_failed", True)
236
+ # Safely extract word IDs, filtering out any missing/None values
237
+ word_ids = [w.get('id') for w in gap_words if w.get('id') is not None]
238
+ return [CorrectionProposal(
239
+ word_ids=word_ids,
240
+ action="Flag",
241
+ confidence=0.0,
242
+ reason="Classification failed - unable to categorize gap",
243
+ requires_human_review=True,
244
+ artist=artist,
245
+ title=title
246
+ )]
247
+
248
+ if span:
249
+ span.set_attribute("gap_category", classification.category.value if hasattr(classification.category, 'value') else str(classification.category))
250
+
251
+ # Step 2: Route to appropriate handler based on category
252
+ try:
253
+ handler = HandlerRegistry.get_handler(
254
+ category=classification.category,
255
+ artist=artist,
256
+ title=title
257
+ )
258
+
259
+ proposals = handler.handle(
260
+ gap_id=gap_id,
261
+ gap_words=gap_words,
262
+ preceding_words=preceding_words,
263
+ following_words=following_words,
264
+ reference_contexts=reference_contexts,
265
+ classification_reasoning=classification.reasoning
266
+ )
267
+
268
+ # Add classification metadata to proposals
269
+ for proposal in proposals:
270
+ if not proposal.gap_category:
271
+ proposal.gap_category = classification.category
272
+ if not proposal.artist:
273
+ proposal.artist = artist
274
+ if not proposal.title:
275
+ proposal.title = title
276
+
277
+ if span:
278
+ span.set_attribute("proposal_count", len(proposals))
279
+
280
+ return proposals
281
+
282
+ except Exception as e:
283
+ # Sanitize error message - use exception class name and truncated message
284
+ error_type = type(e).__name__
285
+ error_msg_truncated = str(e)[:50] if str(e) else "Unknown error"
286
+ sanitized_error = f"{error_type}: {error_msg_truncated}"
287
+ logger.error(f"🤖 Handler failed for gap {gap_id} (category: {classification.category}): {sanitized_error}")
288
+ if span:
289
+ span.set_attribute("handler_error", sanitized_error)
290
+ # Safely extract word IDs, filtering out any missing/None values
291
+ word_ids = [w.get('id') for w in gap_words if w.get('id') is not None]
292
+ # Handler failed, flag for human review
293
+ return [CorrectionProposal(
294
+ word_ids=word_ids,
295
+ action="Flag",
296
+ confidence=0.0,
297
+ reason=f"Handler error for category {classification.category} ({error_type})",
298
+ gap_category=classification.category,
299
+ requires_human_review=True,
300
+ artist=artist,
301
+ title=title
302
+ )]
280
303
 
281
304
  def propose(self, prompt: str) -> List[CorrectionProposal]:
282
305
  """Generate correction proposals using LangGraph + LangChain.
@@ -8,6 +8,9 @@ from pathlib import Path
8
8
  import json
9
9
  import hashlib
10
10
 
11
+ # Tracing utilities (optional - gracefully handles missing dependency)
12
+ from lyrics_transcriber.utils.tracing import create_span, add_span_attribute, add_span_event
13
+
11
14
  from lyrics_transcriber.types import LyricsData, PhraseScore, PhraseType, AnchorSequence, GapSequence, ScoredAnchor, TranscriptionResult, Word
12
15
  from lyrics_transcriber.correction.phrase_analyzer import PhraseAnalyzer
13
16
  from lyrics_transcriber.correction.text_utils import clean_text
@@ -381,7 +384,24 @@ class AnchorSequenceFinder:
381
384
  ) -> List[ScoredAnchor]:
382
385
  """Find anchor sequences that appear in both transcription and references with timeout protection."""
383
386
  start_time = time.time()
384
-
387
+
388
+ # Wrap entire operation in a tracing span
389
+ with create_span("anchor_search.find_anchors", {
390
+ "transcription.text_length": len(transcribed),
391
+ "reference.source_count": len(references),
392
+ "timeout_seconds": self.timeout_seconds,
393
+ }) as span:
394
+ return self._find_anchors_impl(transcribed, references, transcription_result, start_time, span)
395
+
396
+ def _find_anchors_impl(
397
+ self,
398
+ transcribed: str,
399
+ references: Dict[str, LyricsData],
400
+ transcription_result: TranscriptionResult,
401
+ start_time: float,
402
+ span,
403
+ ) -> List[ScoredAnchor]:
404
+ """Internal implementation of find_anchors wrapped in tracing span."""
385
405
  try:
386
406
  self.logger.info(f"🔍 ANCHOR SEARCH: Starting find_anchors with timeout {self.timeout_seconds}s")
387
407
  self.logger.info(f"🔍 ANCHOR SEARCH: Transcribed text length: {len(transcribed)}")
@@ -395,6 +415,10 @@ class AnchorSequenceFinder:
395
415
  self.logger.info(f"🔍 ANCHOR SEARCH: Checking cache at {cache_path}")
396
416
  if cached_data := self._load_from_cache(cache_path):
397
417
  self.logger.info("🔍 ANCHOR SEARCH: ✅ Cache hit! Loading anchors from cache")
418
+ if span:
419
+ span.set_attribute("cache_hit", True)
420
+ span.set_attribute("cached_anchors", len(cached_data))
421
+ span.set_attribute("duration_seconds", time.time() - start_time)
398
422
  try:
399
423
  # Convert cached_data to dictionary before logging
400
424
  if cached_data:
@@ -504,10 +528,24 @@ class AnchorSequenceFinder:
504
528
  }
505
529
 
506
530
  completed = 0
531
+ last_progress_log_time = time.time()
532
+ progress_log_interval = 30 # Log every 30 seconds
533
+
507
534
  for future in as_completed(future_to_n):
508
535
  n = future_to_n[future]
509
536
  completed += 1
510
537
 
538
+ # Time-based progress logging (every 30 seconds)
539
+ current_time = time.time()
540
+ if current_time - last_progress_log_time >= progress_log_interval:
541
+ elapsed = current_time - start_time
542
+ progress_pct = (completed / len(n_gram_lengths)) * 100
543
+ self.logger.info(
544
+ f"🔍 ANCHOR SEARCH: Progress {completed}/{len(n_gram_lengths)} "
545
+ f"({progress_pct:.1f}%) - {elapsed:.1f}s elapsed"
546
+ )
547
+ last_progress_log_time = current_time
548
+
511
549
  # Check timeout periodically
512
550
  if self.timeout_seconds > 0:
513
551
  elapsed_time = time.time() - start_time
@@ -577,7 +615,14 @@ class AnchorSequenceFinder:
577
615
 
578
616
  total_time = time.time() - start_time
579
617
  self.logger.info(f"🔍 ANCHOR SEARCH: 🎉 Anchor sequence computation completed successfully in {total_time:.1f}s")
580
-
618
+
619
+ # Add span attributes for metrics
620
+ if span:
621
+ span.set_attribute("candidate_anchors", len(candidate_anchors))
622
+ span.set_attribute("filtered_anchors", len(filtered_anchors))
623
+ span.set_attribute("duration_seconds", total_time)
624
+ span.set_attribute("cache_hit", False)
625
+
581
626
  return filtered_anchors
582
627
 
583
628
  except AnchorSequenceTimeoutError:
@@ -592,7 +637,7 @@ class AnchorSequenceFinder:
592
637
  self.logger.error(f"🔍 ANCHOR SEARCH: Traceback: {traceback.format_exc()}")
593
638
  raise
594
639
  finally:
595
- # No cleanup needed for time-based timeout checks
640
+ # No cleanup needed - span is managed by context manager in find_anchors()
596
641
  pass
597
642
 
598
643
  def _score_sequence(self, words: List[str], context: str) -> PhraseScore:
@@ -27,6 +27,7 @@ from lyrics_transcriber.correction.anchor_sequence import AnchorSequenceFinder
27
27
  from lyrics_transcriber.correction.handlers.base import GapCorrectionHandler
28
28
  from lyrics_transcriber.correction.handlers.extend_anchor import ExtendAnchorHandler
29
29
  from lyrics_transcriber.utils.word_utils import WordUtils
30
+ from lyrics_transcriber.utils.tracing import create_span, add_span_attribute, add_span_event
30
31
 
31
32
 
32
33
  class LyricsCorrector:
@@ -126,66 +127,91 @@ class LyricsCorrector:
126
127
  wrap this method with an outer timeout (e.g., asyncio.wait_for) as a safety
127
128
  net for hung operations.
128
129
  """
130
+ start_time = time.time()
131
+
129
132
  # Optional agentic routing flag from environment; default off for safety
130
133
  agentic_enabled = os.getenv("USE_AGENTIC_AI", "").lower() in {"1", "true", "yes"}
131
- self.logger.info(f"🤖 AGENTIC MODE: {'ENABLED' if agentic_enabled else 'DISABLED'} (USE_AGENTIC_AI={os.getenv('USE_AGENTIC_AI', 'NOT_SET')})")
132
- if not transcription_results:
133
- self.logger.error("No transcription results available")
134
- raise ValueError("No primary transcription data available")
135
-
136
- # Store reference lyrics for use in word map
137
- self.reference_lyrics = lyrics_results
138
-
139
- # Get primary transcription
140
- primary_transcription_result = sorted(transcription_results, key=lambda x: x.priority)[0]
141
- primary_transcription = primary_transcription_result.result
142
- transcribed_text = " ".join(" ".join(w.text for w in segment.words) for segment in primary_transcription.segments)
143
-
144
- # Find anchor sequences and gaps
145
- self.logger.debug("Finding anchor sequences and gaps")
146
- anchor_sequences = self.anchor_finder.find_anchors(transcribed_text, lyrics_results, primary_transcription_result)
147
- gap_sequences = self.anchor_finder.find_gaps(transcribed_text, anchor_sequences, lyrics_results, primary_transcription_result)
148
-
149
- # Store anchor sequences for use in correction handlers
150
- self._anchor_sequences = anchor_sequences
151
-
152
- # Process corrections with metadata and optional deadline for agentic timeout
153
- corrections, corrected_segments, correction_steps, word_id_map, segment_id_map = self._process_corrections(
154
- primary_transcription.segments, gap_sequences, metadata=metadata, deadline=agentic_deadline
155
- )
156
-
157
- # Calculate correction ratio
158
- total_words = sum(len(segment.words) for segment in corrected_segments)
159
- corrections_made = len(corrections)
160
- correction_ratio = 1 - (corrections_made / total_words if total_words > 0 else 0)
161
-
162
- # Get the currently enabled handler IDs using the handler's name attribute if available
163
- enabled_handlers = [getattr(handler, "name", handler.__class__.__name__) for handler in self.handlers]
164
-
165
- result = CorrectionResult(
166
- original_segments=primary_transcription.segments,
167
- corrected_segments=corrected_segments,
168
- corrections=corrections,
169
- corrections_made=corrections_made,
170
- confidence=correction_ratio,
171
- reference_lyrics=lyrics_results,
172
- anchor_sequences=anchor_sequences,
173
- resized_segments=[],
174
- gap_sequences=gap_sequences,
175
- metadata={
176
- "anchor_sequences_count": len(anchor_sequences),
177
- "gap_sequences_count": len(gap_sequences),
178
- "total_words": total_words,
179
- "correction_ratio": correction_ratio,
180
- "available_handlers": self.all_handlers,
181
- "enabled_handlers": enabled_handlers,
182
- "agentic_routing": "agentic" if agentic_enabled else "rule-based",
183
- },
184
- correction_steps=correction_steps,
185
- word_id_map=word_id_map,
186
- segment_id_map=segment_id_map,
187
- )
188
- return result
134
+
135
+ with create_span("lyrics_corrector.run", {
136
+ "agentic_enabled": agentic_enabled,
137
+ "reference_sources": len(lyrics_results),
138
+ "transcription_count": len(transcription_results),
139
+ }) as span:
140
+ self.logger.info(f"🤖 AGENTIC MODE: {'ENABLED' if agentic_enabled else 'DISABLED'} (USE_AGENTIC_AI={os.getenv('USE_AGENTIC_AI', 'NOT_SET')})")
141
+ if not transcription_results:
142
+ self.logger.error("No transcription results available")
143
+ raise ValueError("No primary transcription data available")
144
+
145
+ # Store reference lyrics for use in word map
146
+ self.reference_lyrics = lyrics_results
147
+
148
+ # Get primary transcription
149
+ primary_transcription_result = sorted(transcription_results, key=lambda x: x.priority)[0]
150
+ primary_transcription = primary_transcription_result.result
151
+ transcribed_text = " ".join(" ".join(w.text for w in segment.words) for segment in primary_transcription.segments)
152
+
153
+ # Find anchor sequences and gaps
154
+ self.logger.debug("Finding anchor sequences and gaps")
155
+ with create_span("lyrics_corrector.find_anchors_and_gaps") as anchor_span:
156
+ anchor_sequences = self.anchor_finder.find_anchors(transcribed_text, lyrics_results, primary_transcription_result)
157
+ gap_sequences = self.anchor_finder.find_gaps(transcribed_text, anchor_sequences, lyrics_results, primary_transcription_result)
158
+ if anchor_span:
159
+ anchor_span.set_attribute("anchor_count", len(anchor_sequences))
160
+ anchor_span.set_attribute("gap_count", len(gap_sequences))
161
+
162
+ # Store anchor sequences for use in correction handlers
163
+ self._anchor_sequences = anchor_sequences
164
+
165
+ # Process corrections with metadata and optional deadline for agentic timeout
166
+ with create_span("lyrics_corrector.process_corrections", {
167
+ "gap_count": len(gap_sequences),
168
+ "agentic_enabled": agentic_enabled,
169
+ }) as process_span:
170
+ corrections, corrected_segments, correction_steps, word_id_map, segment_id_map = self._process_corrections(
171
+ primary_transcription.segments, gap_sequences, metadata=metadata, deadline=agentic_deadline
172
+ )
173
+ if process_span:
174
+ process_span.set_attribute("corrections_count", len(corrections))
175
+
176
+ # Calculate correction ratio
177
+ total_words = sum(len(segment.words) for segment in corrected_segments)
178
+ corrections_made = len(corrections)
179
+ correction_ratio = 1 - (corrections_made / total_words if total_words > 0 else 0)
180
+
181
+ # Get the currently enabled handler IDs using the handler's name attribute if available
182
+ enabled_handlers = [getattr(handler, "name", handler.__class__.__name__) for handler in self.handlers]
183
+
184
+ # Add final span attributes
185
+ if span:
186
+ span.set_attribute("total_words", total_words)
187
+ span.set_attribute("corrections_made", corrections_made)
188
+ span.set_attribute("correction_ratio", correction_ratio)
189
+ span.set_attribute("duration_seconds", time.time() - start_time)
190
+
191
+ result = CorrectionResult(
192
+ original_segments=primary_transcription.segments,
193
+ corrected_segments=corrected_segments,
194
+ corrections=corrections,
195
+ corrections_made=corrections_made,
196
+ confidence=correction_ratio,
197
+ reference_lyrics=lyrics_results,
198
+ anchor_sequences=anchor_sequences,
199
+ resized_segments=[],
200
+ gap_sequences=gap_sequences,
201
+ metadata={
202
+ "anchor_sequences_count": len(anchor_sequences),
203
+ "gap_sequences_count": len(gap_sequences),
204
+ "total_words": total_words,
205
+ "correction_ratio": correction_ratio,
206
+ "available_handlers": self.all_handlers,
207
+ "enabled_handlers": enabled_handlers,
208
+ "agentic_routing": "agentic" if agentic_enabled else "rule-based",
209
+ },
210
+ correction_steps=correction_steps,
211
+ word_id_map=word_id_map,
212
+ segment_id_map=segment_id_map,
213
+ )
214
+ return result
189
215
 
190
216
  def _preserve_formatting(self, original: str, new_word: str) -> str:
191
217
  """Preserve original word's formatting when applying correction."""
@@ -465,6 +491,9 @@ class LyricsCorrector:
465
491
  # Get parallel processing config
466
492
  _config = ProviderConfig.from_env()
467
493
  max_workers = _config.max_parallel_gaps
494
+
495
+ # Wrap agentic processing in a tracing span
496
+ add_span_event("agentic_processing_started", {"gap_count": len(gap_sequences), "max_workers": max_workers})
468
497
  self.logger.info(f"🤖 Processing {len(gap_sequences)} gaps in parallel (max_workers={max_workers})")
469
498
 
470
499
  # Pre-compute shared data structures once (not per-gap)
@@ -585,6 +614,11 @@ class LyricsCorrector:
585
614
  self.logger.info(f"🤖 Gap {result['index']}/{len(gap_sequences)} completed ({proposal_count} proposals)")
586
615
 
587
616
  self.logger.info(f"🤖 Parallel processing complete: {completed_count}/{len(gap_sequences)} gaps processed")
617
+ add_span_event("agentic_processing_completed", {
618
+ "gaps_processed": completed_count,
619
+ "gaps_total": len(gap_sequences),
620
+ "errors": len(errors),
621
+ })
588
622
 
589
623
  # If any errors occurred, fail fast
590
624
  if errors: