karaoke-gen 0.96.0__py3-none-any.whl → 0.99.3__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.
- backend/api/routes/admin.py +184 -91
- backend/api/routes/audio_search.py +16 -6
- backend/api/routes/file_upload.py +57 -21
- backend/api/routes/health.py +65 -0
- backend/api/routes/jobs.py +19 -0
- backend/api/routes/users.py +543 -44
- backend/main.py +25 -1
- backend/services/encoding_service.py +128 -31
- backend/services/job_manager.py +12 -1
- backend/services/langfuse_preloader.py +98 -0
- backend/services/nltk_preloader.py +122 -0
- backend/services/spacy_preloader.py +65 -0
- backend/services/stripe_service.py +96 -0
- backend/tests/emulator/conftest.py +22 -1
- backend/tests/test_job_manager.py +25 -8
- backend/tests/test_jobs_api.py +11 -1
- backend/tests/test_spacy_preloader.py +119 -0
- backend/utils/test_data.py +27 -0
- backend/workers/screens_worker.py +16 -6
- {karaoke_gen-0.96.0.dist-info → karaoke_gen-0.99.3.dist-info}/METADATA +1 -1
- {karaoke_gen-0.96.0.dist-info → karaoke_gen-0.99.3.dist-info}/RECORD +30 -25
- lyrics_transcriber/correction/agentic/agent.py +17 -6
- lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +96 -43
- lyrics_transcriber/correction/agentic/providers/model_factory.py +27 -6
- lyrics_transcriber/correction/anchor_sequence.py +151 -37
- lyrics_transcriber/correction/handlers/syllables_match.py +44 -2
- lyrics_transcriber/correction/phrase_analyzer.py +18 -0
- {karaoke_gen-0.96.0.dist-info → karaoke_gen-0.99.3.dist-info}/WHEEL +0 -0
- {karaoke_gen-0.96.0.dist-info → karaoke_gen-0.99.3.dist-info}/entry_points.txt +0 -0
- {karaoke_gen-0.96.0.dist-info → karaoke_gen-0.99.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -39,6 +39,24 @@ os.environ["ADMIN_TOKENS"] = "test-admin-token"
|
|
|
39
39
|
|
|
40
40
|
# Only import app if emulators are running
|
|
41
41
|
if emulators_running():
|
|
42
|
+
from unittest.mock import Mock
|
|
43
|
+
|
|
44
|
+
# Mock theme service BEFORE importing app to ensure all jobs get theme_id="nomad"
|
|
45
|
+
_mock_theme_service = Mock()
|
|
46
|
+
_mock_theme_service.get_default_theme_id.return_value = "nomad"
|
|
47
|
+
_mock_theme_service.get_theme.return_value = None
|
|
48
|
+
|
|
49
|
+
# Apply patch at module load time (before app imports the service)
|
|
50
|
+
_theme_patches = [
|
|
51
|
+
patch("backend.services.theme_service.get_theme_service", return_value=_mock_theme_service),
|
|
52
|
+
patch("backend.api.routes.audio_search.get_theme_service", return_value=_mock_theme_service),
|
|
53
|
+
patch("backend.api.routes.file_upload.get_theme_service", return_value=_mock_theme_service),
|
|
54
|
+
patch("backend.api.routes.users.get_theme_service", return_value=_mock_theme_service),
|
|
55
|
+
patch("backend.api.routes.jobs.get_theme_service", return_value=_mock_theme_service),
|
|
56
|
+
]
|
|
57
|
+
for p in _theme_patches:
|
|
58
|
+
p.start()
|
|
59
|
+
|
|
42
60
|
from fastapi.testclient import TestClient
|
|
43
61
|
from backend.main import app
|
|
44
62
|
else:
|
|
@@ -76,7 +94,10 @@ def mock_worker_service():
|
|
|
76
94
|
|
|
77
95
|
@pytest.fixture(scope="session")
|
|
78
96
|
def client(mock_worker_service):
|
|
79
|
-
"""Create FastAPI test client with mocked workers.
|
|
97
|
+
"""Create FastAPI test client with mocked workers.
|
|
98
|
+
|
|
99
|
+
Theme service is mocked at module load time (see above).
|
|
100
|
+
"""
|
|
80
101
|
with patch("backend.api.routes.file_upload.worker_service", mock_worker_service):
|
|
81
102
|
return TestClient(app)
|
|
82
103
|
|
|
@@ -33,13 +33,28 @@ def job_manager(mock_firestore_service):
|
|
|
33
33
|
|
|
34
34
|
class TestJobCreation:
|
|
35
35
|
"""Test job creation logic."""
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
def test_create_job_requires_theme_id(self, job_manager, mock_firestore_service):
|
|
38
|
+
"""Test that jobs without theme_id are rejected."""
|
|
39
|
+
job_create = JobCreate(
|
|
40
|
+
artist="Test Artist",
|
|
41
|
+
title="Test Song"
|
|
42
|
+
# No theme_id - should fail
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
with pytest.raises(ValueError, match="theme_id is required"):
|
|
46
|
+
job_manager.create_job(job_create)
|
|
47
|
+
|
|
48
|
+
# Verify Firestore was NOT called
|
|
49
|
+
mock_firestore_service.create_job.assert_not_called()
|
|
50
|
+
|
|
37
51
|
def test_create_job_with_url(self, job_manager, mock_firestore_service):
|
|
38
52
|
"""Test creating a job with YouTube URL."""
|
|
39
53
|
job_create = JobCreate(
|
|
40
54
|
url="https://youtube.com/watch?v=test",
|
|
41
55
|
artist="Test Artist",
|
|
42
|
-
title="Test Song"
|
|
56
|
+
title="Test Song",
|
|
57
|
+
theme_id="nomad" # Required for all jobs
|
|
43
58
|
)
|
|
44
59
|
|
|
45
60
|
# The actual create_job method creates the job and returns it
|
|
@@ -59,7 +74,8 @@ class TestJobCreation:
|
|
|
59
74
|
"""Test creating a job without URL (for file upload)."""
|
|
60
75
|
job_create = JobCreate(
|
|
61
76
|
artist="Test Artist",
|
|
62
|
-
title="Test Song"
|
|
77
|
+
title="Test Song",
|
|
78
|
+
theme_id="nomad" # Required for all jobs
|
|
63
79
|
)
|
|
64
80
|
|
|
65
81
|
job = job_manager.create_job(job_create)
|
|
@@ -70,8 +86,8 @@ class TestJobCreation:
|
|
|
70
86
|
|
|
71
87
|
def test_create_job_generates_unique_id(self, job_manager, mock_firestore_service):
|
|
72
88
|
"""Test that each job gets a unique ID."""
|
|
73
|
-
job_create = JobCreate()
|
|
74
|
-
|
|
89
|
+
job_create = JobCreate(theme_id="nomad") # Required for all jobs
|
|
90
|
+
|
|
75
91
|
# Create multiple jobs
|
|
76
92
|
ids = []
|
|
77
93
|
for i in range(5):
|
|
@@ -89,8 +105,8 @@ class TestJobCreation:
|
|
|
89
105
|
|
|
90
106
|
def test_create_job_sets_initial_status(self, job_manager, mock_firestore_service):
|
|
91
107
|
"""Test that new jobs start with PENDING status."""
|
|
92
|
-
job_create = JobCreate()
|
|
93
|
-
|
|
108
|
+
job_create = JobCreate(theme_id="nomad") # Required for all jobs
|
|
109
|
+
|
|
94
110
|
mock_firestore_service.create_job.return_value = Job(
|
|
95
111
|
job_id="test123",
|
|
96
112
|
status=JobStatus.PENDING,
|
|
@@ -105,13 +121,14 @@ class TestJobCreation:
|
|
|
105
121
|
|
|
106
122
|
def test_create_job_with_distribution_settings(self, job_manager, mock_firestore_service):
|
|
107
123
|
"""Test that distribution settings are passed from JobCreate to Job.
|
|
108
|
-
|
|
124
|
+
|
|
109
125
|
This was a bug where brand_prefix, dropbox_path, gdrive_folder_id, and
|
|
110
126
|
discord_webhook_url were NOT being passed to the Job constructor.
|
|
111
127
|
"""
|
|
112
128
|
job_create = JobCreate(
|
|
113
129
|
artist="Test Artist",
|
|
114
130
|
title="Test Song",
|
|
131
|
+
theme_id="nomad", # Required for all jobs
|
|
115
132
|
brand_prefix="NOMAD",
|
|
116
133
|
discord_webhook_url="https://discord.com/webhook/test",
|
|
117
134
|
dropbox_path="/Karaoke/Tracks-Organized",
|
backend/tests/test_jobs_api.py
CHANGED
|
@@ -50,7 +50,16 @@ def mock_worker_service():
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
@pytest.fixture
|
|
53
|
-
def
|
|
53
|
+
def mock_theme_service():
|
|
54
|
+
"""Create a mock ThemeService that returns 'nomad' as default theme."""
|
|
55
|
+
service = MagicMock()
|
|
56
|
+
service.get_default_theme_id.return_value = "nomad"
|
|
57
|
+
service.get_theme.return_value = None
|
|
58
|
+
return service
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.fixture
|
|
62
|
+
def client(mock_job_manager, mock_worker_service, mock_theme_service):
|
|
54
63
|
"""Create TestClient with mocked dependencies."""
|
|
55
64
|
mock_creds = MagicMock()
|
|
56
65
|
mock_creds.universe_domain = 'googleapis.com'
|
|
@@ -63,6 +72,7 @@ def client(mock_job_manager, mock_worker_service):
|
|
|
63
72
|
# Also patch JobManager class used in dependencies.py for auth checks
|
|
64
73
|
with patch('backend.api.routes.jobs.job_manager', mock_job_manager), \
|
|
65
74
|
patch('backend.api.routes.jobs.worker_service', mock_worker_service), \
|
|
75
|
+
patch('backend.api.routes.jobs.get_theme_service', return_value=mock_theme_service), \
|
|
66
76
|
patch('backend.services.job_manager.JobManager', mock_job_manager_factory), \
|
|
67
77
|
patch('backend.services.firestore_service.firestore'), \
|
|
68
78
|
patch('backend.services.storage_service.storage'), \
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Tests for SpaCy preloader service."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from unittest.mock import patch, MagicMock
|
|
5
|
+
|
|
6
|
+
from backend.services.spacy_preloader import (
|
|
7
|
+
preload_spacy_model,
|
|
8
|
+
get_preloaded_model,
|
|
9
|
+
is_model_preloaded,
|
|
10
|
+
clear_preloaded_models,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestSpacyPreloader:
|
|
15
|
+
"""Tests for SpaCy preloading functionality."""
|
|
16
|
+
|
|
17
|
+
def setup_method(self):
|
|
18
|
+
"""Clear preloaded models before each test."""
|
|
19
|
+
clear_preloaded_models()
|
|
20
|
+
|
|
21
|
+
def teardown_method(self):
|
|
22
|
+
"""Clear preloaded models after each test."""
|
|
23
|
+
clear_preloaded_models()
|
|
24
|
+
|
|
25
|
+
def test_preload_spacy_model_loads_and_stores(self):
|
|
26
|
+
"""GIVEN no preloaded models
|
|
27
|
+
WHEN preload_spacy_model is called
|
|
28
|
+
THEN model should be loaded and stored in singleton."""
|
|
29
|
+
mock_nlp = MagicMock()
|
|
30
|
+
|
|
31
|
+
with patch("spacy.load", return_value=mock_nlp) as mock_load:
|
|
32
|
+
preload_spacy_model("en_core_web_sm")
|
|
33
|
+
|
|
34
|
+
mock_load.assert_called_once_with("en_core_web_sm")
|
|
35
|
+
assert is_model_preloaded("en_core_web_sm")
|
|
36
|
+
assert get_preloaded_model("en_core_web_sm") is mock_nlp
|
|
37
|
+
|
|
38
|
+
def test_preload_is_idempotent(self):
|
|
39
|
+
"""GIVEN a model already preloaded
|
|
40
|
+
WHEN preload_spacy_model is called again
|
|
41
|
+
THEN model should not be reloaded."""
|
|
42
|
+
mock_nlp = MagicMock()
|
|
43
|
+
|
|
44
|
+
with patch("spacy.load", return_value=mock_nlp) as mock_load:
|
|
45
|
+
preload_spacy_model("en_core_web_sm")
|
|
46
|
+
preload_spacy_model("en_core_web_sm") # Second call
|
|
47
|
+
|
|
48
|
+
# Should only load once
|
|
49
|
+
assert mock_load.call_count == 1
|
|
50
|
+
|
|
51
|
+
def test_get_preloaded_model_returns_none_if_not_loaded(self):
|
|
52
|
+
"""GIVEN no preloaded models
|
|
53
|
+
WHEN get_preloaded_model is called
|
|
54
|
+
THEN should return None."""
|
|
55
|
+
assert get_preloaded_model("en_core_web_sm") is None
|
|
56
|
+
assert not is_model_preloaded("en_core_web_sm")
|
|
57
|
+
|
|
58
|
+
def test_preload_different_models(self):
|
|
59
|
+
"""GIVEN no preloaded models
|
|
60
|
+
WHEN preload_spacy_model is called with different model names
|
|
61
|
+
THEN each model should be loaded and stored separately."""
|
|
62
|
+
mock_nlp_sm = MagicMock(name="en_core_web_sm")
|
|
63
|
+
mock_nlp_md = MagicMock(name="en_core_web_md")
|
|
64
|
+
|
|
65
|
+
def mock_load(model_name):
|
|
66
|
+
if model_name == "en_core_web_sm":
|
|
67
|
+
return mock_nlp_sm
|
|
68
|
+
elif model_name == "en_core_web_md":
|
|
69
|
+
return mock_nlp_md
|
|
70
|
+
raise ValueError(f"Unknown model: {model_name}")
|
|
71
|
+
|
|
72
|
+
with patch("spacy.load", side_effect=mock_load):
|
|
73
|
+
preload_spacy_model("en_core_web_sm")
|
|
74
|
+
preload_spacy_model("en_core_web_md")
|
|
75
|
+
|
|
76
|
+
assert is_model_preloaded("en_core_web_sm")
|
|
77
|
+
assert is_model_preloaded("en_core_web_md")
|
|
78
|
+
assert get_preloaded_model("en_core_web_sm") is mock_nlp_sm
|
|
79
|
+
assert get_preloaded_model("en_core_web_md") is mock_nlp_md
|
|
80
|
+
|
|
81
|
+
def test_clear_preloaded_models(self):
|
|
82
|
+
"""GIVEN preloaded models
|
|
83
|
+
WHEN clear_preloaded_models is called
|
|
84
|
+
THEN all models should be removed."""
|
|
85
|
+
mock_nlp = MagicMock()
|
|
86
|
+
|
|
87
|
+
with patch("spacy.load", return_value=mock_nlp):
|
|
88
|
+
preload_spacy_model("en_core_web_sm")
|
|
89
|
+
assert is_model_preloaded("en_core_web_sm")
|
|
90
|
+
|
|
91
|
+
clear_preloaded_models()
|
|
92
|
+
assert not is_model_preloaded("en_core_web_sm")
|
|
93
|
+
assert get_preloaded_model("en_core_web_sm") is None
|
|
94
|
+
|
|
95
|
+
def test_preload_failure_raises_exception(self):
|
|
96
|
+
"""GIVEN spacy.load raises an exception
|
|
97
|
+
WHEN preload_spacy_model is called
|
|
98
|
+
THEN exception should be propagated."""
|
|
99
|
+
with patch(
|
|
100
|
+
"spacy.load",
|
|
101
|
+
side_effect=OSError("Model not found"),
|
|
102
|
+
):
|
|
103
|
+
with pytest.raises(OSError, match="Model not found"):
|
|
104
|
+
preload_spacy_model("nonexistent_model")
|
|
105
|
+
|
|
106
|
+
# Model should not be marked as preloaded
|
|
107
|
+
assert not is_model_preloaded("nonexistent_model")
|
|
108
|
+
|
|
109
|
+
def test_preload_uses_default_model_name(self):
|
|
110
|
+
"""GIVEN no model name specified
|
|
111
|
+
WHEN preload_spacy_model is called without arguments
|
|
112
|
+
THEN should use en_core_web_sm as default."""
|
|
113
|
+
mock_nlp = MagicMock()
|
|
114
|
+
|
|
115
|
+
with patch("spacy.load", return_value=mock_nlp) as mock_load:
|
|
116
|
+
preload_spacy_model()
|
|
117
|
+
|
|
118
|
+
mock_load.assert_called_once_with("en_core_web_sm")
|
|
119
|
+
assert is_model_preloaded("en_core_web_sm")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utilities for identifying test data.
|
|
3
|
+
|
|
4
|
+
Test data is generated by automated E2E tests and should be filterable
|
|
5
|
+
in admin dashboards to show only real user data by default.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Email domains used by automated testing frameworks
|
|
9
|
+
TEST_EMAIL_DOMAINS = [
|
|
10
|
+
"inbox.testmail.app", # Used by E2E happy path tests
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_test_email(email: str) -> bool:
|
|
15
|
+
"""
|
|
16
|
+
Check if an email address belongs to automated test data.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
email: Email address to check
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
True if the email matches a test email domain pattern
|
|
23
|
+
"""
|
|
24
|
+
if not email:
|
|
25
|
+
return False
|
|
26
|
+
email_lower = email.lower()
|
|
27
|
+
return any(email_lower.endswith(f"@{domain}") for domain in TEST_EMAIL_DOMAINS)
|
|
@@ -218,26 +218,36 @@ async def generate_screens(job_id: str) -> bool:
|
|
|
218
218
|
def _validate_prerequisites(job) -> bool:
|
|
219
219
|
"""
|
|
220
220
|
Validate that both audio and lyrics processing are complete.
|
|
221
|
-
|
|
221
|
+
|
|
222
222
|
Single Responsibility: Validation logic separated from main flow.
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
Args:
|
|
225
225
|
job: Job object
|
|
226
|
-
|
|
226
|
+
|
|
227
227
|
Returns:
|
|
228
228
|
True if prerequisites met, False otherwise
|
|
229
229
|
"""
|
|
230
|
+
# SAFETY NET: Enforce theme requirement at processing time
|
|
231
|
+
# This catches any jobs that somehow bypassed JobManager.create_job() validation
|
|
232
|
+
if not job.theme_id:
|
|
233
|
+
logger.error(
|
|
234
|
+
f"Job {job.job_id}: CRITICAL - No theme_id configured. "
|
|
235
|
+
"All jobs must have a theme to generate styled videos. "
|
|
236
|
+
"This job should have been rejected at creation time."
|
|
237
|
+
)
|
|
238
|
+
return False
|
|
239
|
+
|
|
230
240
|
audio_complete = job.state_data.get('audio_complete', False)
|
|
231
241
|
lyrics_complete = job.state_data.get('lyrics_complete', False)
|
|
232
|
-
|
|
242
|
+
|
|
233
243
|
if not audio_complete:
|
|
234
244
|
logger.error(f"Job {job.job_id}: Audio processing not complete")
|
|
235
245
|
return False
|
|
236
|
-
|
|
246
|
+
|
|
237
247
|
if not lyrics_complete:
|
|
238
248
|
logger.error(f"Job {job.job_id}: Lyrics processing not complete")
|
|
239
249
|
return False
|
|
240
|
-
|
|
250
|
+
|
|
241
251
|
if not job.artist or not job.title:
|
|
242
252
|
logger.error(f"Job {job.job_id}: Missing artist or title")
|
|
243
253
|
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: karaoke-gen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.99.3
|
|
4
4
|
Summary: Generate karaoke videos with synchronized lyrics. Handles the entire process from downloading audio and lyrics to creating the final video with title screens.
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -7,18 +7,18 @@ backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
7
7
|
backend/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
backend/api/dependencies.py,sha256=-61nHBhiihUDSVMQd3VuHLP7uvKrUbm1y-j9RmV6_zc,16871
|
|
9
9
|
backend/api/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
backend/api/routes/admin.py,sha256=
|
|
11
|
-
backend/api/routes/audio_search.py,sha256=
|
|
10
|
+
backend/api/routes/admin.py,sha256=r9Q7cOnDJjLnQVQ5ql3S6eUOXiVss9hEanHMz6I0Mp4,30499
|
|
11
|
+
backend/api/routes/audio_search.py,sha256=r8VPlyA40DUoFPcvNVM_p4faJsjv_Ta_qsY7PhvAzfw,39678
|
|
12
12
|
backend/api/routes/auth.py,sha256=G1U2KwS3uqBJpgvg2PIe_mZOWCJKPFhhewrmKbW3Z_s,11531
|
|
13
|
-
backend/api/routes/file_upload.py,sha256=
|
|
14
|
-
backend/api/routes/health.py,sha256=
|
|
13
|
+
backend/api/routes/file_upload.py,sha256=GQ_m1ZQxFOxxkrjPY6a-9K3jvBC-Jwo_1mRYjZ6cxic,94156
|
|
14
|
+
backend/api/routes/health.py,sha256=iZlhJpmz4vminUy_qrzkyk_1tgG1gmITz4jEWaCWJnM,14010
|
|
15
15
|
backend/api/routes/internal.py,sha256=N_Fv0hYQ9gleYNehjHaZb1OKzM78PXDbcqTmHasll2w,15276
|
|
16
|
-
backend/api/routes/jobs.py,sha256=
|
|
16
|
+
backend/api/routes/jobs.py,sha256=L0Aot-BEgOlNv-_AqEVIkO5Uv4BTP5gjmBvPzjWmlR8,60866
|
|
17
17
|
backend/api/routes/review.py,sha256=hEvLUcb07DWcBc6E4fTa91zam3Z_a-x2vIvLP_OKR9o,28256
|
|
18
18
|
backend/api/routes/themes.py,sha256=_fPZg9N2KN-yyr1YR2vAy8FIm8PqVowboQ4WEpFTYiQ,4721
|
|
19
|
-
backend/api/routes/users.py,sha256=
|
|
19
|
+
backend/api/routes/users.py,sha256=9ffodZUGzxjoHXLQdOZ-PLEqK0k-wd4zK4xp_GD2wZk,56781
|
|
20
20
|
backend/config.py,sha256=-HsMaacaKuRftYXUXZJr8GBHPdmacFbrNnFDMk7DHNI,8187
|
|
21
|
-
backend/main.py,sha256=
|
|
21
|
+
backend/main.py,sha256=xYxdM9GT0vLVPmyXGgoa0_BW1JQMGEX5m7lXm_xYnaw,5666
|
|
22
22
|
backend/middleware/__init__.py,sha256=lUSMQqQc4UxPazrE1XmyNNEvTKRk7tddAdFmxbTzz7M,157
|
|
23
23
|
backend/middleware/audit_logging.py,sha256=oGdgbfH_M_3hIMAGrS5HpRvri2jjLxGFnFuA6IiU9BU,4170
|
|
24
24
|
backend/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -41,7 +41,7 @@ backend/services/discord_service.py,sha256=LnEasOgaSRlmebDKsVT2yKCdWNDmskUToP3o2
|
|
|
41
41
|
backend/services/dropbox_service.py,sha256=R47MUKCqK2BEXXmQxMLG6zkPWw02htFoES9q9xiaYAw,10717
|
|
42
42
|
backend/services/email_service.py,sha256=fxwcL-n6ZRGLQ7SHXhxUQK1MkprtgE3GcuL5ulUR-90,44699
|
|
43
43
|
backend/services/encoding_interface.py,sha256=Y8hPskDyX9DOyAD557F3-SdHjymaBkGXcoC9E_OW7SE,15825
|
|
44
|
-
backend/services/encoding_service.py,sha256=
|
|
44
|
+
backend/services/encoding_service.py,sha256=XAX4hyL2wIHdpBCTc8yglcjVJboZxRVAjc07lUTxgFg,18025
|
|
45
45
|
backend/services/firestore_service.py,sha256=XOuXUNd91eVeyEx82uh-A-WMwcyUa2HVxqTfYnGPw-Q,18512
|
|
46
46
|
backend/services/flacfetch_client.py,sha256=zj4VToKamR8bShXZifLvjDmUVa1HHvDhZuaw_b7g4lU,19901
|
|
47
47
|
backend/services/gce_encoding/README.md,sha256=frTFrp7-ugFAvUd_K9EgE4ud8Xu4Qf1V7BNirQn6qJk,2515
|
|
@@ -50,16 +50,19 @@ backend/services/gce_encoding/main.py,sha256=2A0Bd3zYzbV66ikShB3avpqfNoDFBxr8Ja6
|
|
|
50
50
|
backend/services/gce_encoding/requirements.txt,sha256=-hW5ot-PnVoKkxrpY3yV6l68wx7AvzgnLYAWEgDGWNc,408
|
|
51
51
|
backend/services/gdrive_service.py,sha256=3JTXmPBuxpKopAbWDrh6YhqR5L9BLop1R7caXBnIk2A,13266
|
|
52
52
|
backend/services/job_logging.py,sha256=theCkAlwMbqyqAbu7vCCRpVRscaIpkam3UrCs_4Yi1g,8052
|
|
53
|
-
backend/services/job_manager.py,sha256=
|
|
53
|
+
backend/services/job_manager.py,sha256=y7TCwJal7rPJm4QRuOEviv_SE9RvDiNGHz33MSgGY3M,31701
|
|
54
54
|
backend/services/job_notification_service.py,sha256=ywGJUMUDqV23OSDsugOUufpXO6N_24bM9YVaPp8bOk4,9557
|
|
55
|
+
backend/services/langfuse_preloader.py,sha256=t-Rt_ThjZm4K8W3jlyLn2H3cEy7KNUlhrvOPmdg-gYM,3100
|
|
55
56
|
backend/services/local_encoding_service.py,sha256=Q6yK2-QL9tHM_pUBoxdyfT7ln2L5xMx82JB2nC5OfNQ,20680
|
|
56
57
|
backend/services/local_preview_encoding_service.py,sha256=WLuJuIx-kH6u35gcYnvogf4Gt7iEokL2IdfxlrkY5po,14394
|
|
57
58
|
backend/services/lyrics_cache_service.py,sha256=53EIZuDs5jIKr6QiOhIKJjE2mx1Jd1_x15_uAeLnc4A,7598
|
|
58
59
|
backend/services/metrics.py,sha256=hlEeLG4jOR1i_QbWaNQthoko9CSpe-Zn_LHJ2huEFeY,12728
|
|
60
|
+
backend/services/nltk_preloader.py,sha256=TZFghQrpDTi6COcW_rkjL15SlzdRNvPmrdy690fq0hw,3579
|
|
59
61
|
backend/services/packaging_service.py,sha256=MUuBk0jtG3vGQhuOWbDv9u1uIMwbBC4NknLpJYFcWhs,10259
|
|
60
62
|
backend/services/rclone_service.py,sha256=Q6wRAjlBiMMh9FBbbFRp4SHK1ljOQBed9zUJaJgAuhU,3646
|
|
63
|
+
backend/services/spacy_preloader.py,sha256=rpsMZB0CQWcoXjq8nyZCwJBkyjGheKaCFrfhxCOln5w,1836
|
|
61
64
|
backend/services/storage_service.py,sha256=gA7Snz1pihzYpRKZjEZx3eDwyMUBezl3ccLuBdpTwE4,8573
|
|
62
|
-
backend/services/stripe_service.py,sha256=
|
|
65
|
+
backend/services/stripe_service.py,sha256=154TbTuysOiuUFKEmIG3RFZYKYEYhLJR2Weyb4xAhxs,13243
|
|
63
66
|
backend/services/structured_logging.py,sha256=daGgZUdc1xIiOdxL9-YtWHDy51hwZqd8ELpomkh2QH4,9063
|
|
64
67
|
backend/services/template_service.py,sha256=5CdSxBGhdBtxIUz24AhvltUfSWcFqZzBIxb9MjlN4YU,11212
|
|
65
68
|
backend/services/theme_service.py,sha256=6lJqMpFjmRY09CMJcW9IJ6DVPffLmwE0DnpXDUKaJ0U,17374
|
|
@@ -71,7 +74,7 @@ backend/services/youtube_upload_service.py,sha256=fvo4WxLQaKY_I75ViBMLk6vm87ym2A
|
|
|
71
74
|
backend/tests/__init__.py,sha256=jTjELMqIzpgHG9dcXwwkszTAAC4Wf-c93C6D1k-toZg,44
|
|
72
75
|
backend/tests/conftest.py,sha256=c4_jH0FIHsiKeUfo3ECPN78MlbW_IARTWwKr9RKLOSM,7843
|
|
73
76
|
backend/tests/emulator/__init__.py,sha256=yOGPU5e0JPNYyUpy-u13jW4gEfWQs3dCJxfincptaCY,156
|
|
74
|
-
backend/tests/emulator/conftest.py,sha256=
|
|
77
|
+
backend/tests/emulator/conftest.py,sha256=VXhoJ3NZg3uiLzSAsushQtozFS7Lt6lmjk7ym-k6V8o,3844
|
|
75
78
|
backend/tests/emulator/test_e2e_cli_backend.py,sha256=BTrWDv4sRTeOlheZ-d4R37tZsvJ2MhdFLbDb9tjbF08,41018
|
|
76
79
|
backend/tests/emulator/test_emulator_integration.py,sha256=pm_JzgA2bmjcnHmGA-mZfJhUAYwGx10jv48A2deujRs,11822
|
|
77
80
|
backend/tests/emulator/test_style_loading_direct.py,sha256=e-FzlCVW_-ealM9hTGa2qgpwoVRiYKrcXDWB1FjkwXc,16842
|
|
@@ -102,10 +105,10 @@ backend/tests/test_gdrive_service.py,sha256=fh02KPaFqLEWfPaDQvSehn6r6RLq0bgt6MdX
|
|
|
102
105
|
backend/tests/test_instrumental_api.py,sha256=2eWaoEAYVY02HZgY4esn5echuWGv_sznoO6FYWIgTbQ,17210
|
|
103
106
|
backend/tests/test_internal_api.py,sha256=qMGSDCkYd5OY_Z85vQu10xjqJn1JtaQcaKSnSBpVijw,12980
|
|
104
107
|
backend/tests/test_job_creation_regression.py,sha256=C1KgyNhpJzchl10QK8trfmACs2BrEwYd-WK1YMMS-7I,22602
|
|
105
|
-
backend/tests/test_job_manager.py,sha256=
|
|
108
|
+
backend/tests/test_job_manager.py,sha256=Y5De6L0BvK4yk6D4w-pDBKBk2R7w89SfSyUz5krgphE,13187
|
|
106
109
|
backend/tests/test_job_manager_notifications.py,sha256=WlzXXxTCmHHCrD4XWu2j2fDYODlHGKThOQpZQeyhPXM,12996
|
|
107
110
|
backend/tests/test_job_notification_service.py,sha256=YbXqCWbsK8O5WICItb3VCrEY8qKqoJ3NBEZtwCs7AYM,17892
|
|
108
|
-
backend/tests/test_jobs_api.py,sha256=
|
|
111
|
+
backend/tests/test_jobs_api.py,sha256=z3pGTawTpt75a1bQ5x4ynw3pTlNihLA6vqktTc6mBAU,11092
|
|
109
112
|
backend/tests/test_local_encoding_service.py,sha256=TbBaFOB-8Re09G-qXIQCl8mS9WlQMFNQF01V64IekyI,14638
|
|
110
113
|
backend/tests/test_local_preview_encoding_service.py,sha256=VwxpcAjDdYaVauSsrJ609U575VWRZbLsXew8BqBXpJY,20228
|
|
111
114
|
backend/tests/test_main.py,sha256=BvzcadLUV0SEUO1ViKm0djgy_TEKXm8PPvgfcCCEJAY,3348
|
|
@@ -116,6 +119,7 @@ backend/tests/test_routes_jobs.py,sha256=iqOZ93ACKpizSzLEJUx5hIo0vkKe2oIT7s7iZqQ
|
|
|
116
119
|
backend/tests/test_routes_review.py,sha256=ENo6_wFUDcyXZhRy_NBqajan9dvqV_7f1rIQ7LLLS-U,14334
|
|
117
120
|
backend/tests/test_services.py,sha256=JSsZZSpa0HjNZBSIrXXzBxZfJewfdR9RC3P8tHV8M8E,24288
|
|
118
121
|
backend/tests/test_services_extended.py,sha256=cBcaEicz4WdBo5WZB77AoGZTwWAOC6W4OslpT6VmFI8,4124
|
|
122
|
+
backend/tests/test_spacy_preloader.py,sha256=uWpkrOPUOOcW1-fWClKMKVhXHGk30FXq537kQH5jpd0,4437
|
|
119
123
|
backend/tests/test_storage_service.py,sha256=gorn3aFwKSr8lihxcG7kza7fYvDsTvxJhOxzuTtPK_o,19069
|
|
120
124
|
backend/tests/test_style_upload.py,sha256=8G2dGgvyMGXsh1jk8PcGpTlSdBQAlHn13kpZcKn6rTY,10491
|
|
121
125
|
backend/tests/test_template_service.py,sha256=BKURQ1M-hXXgdBl9LqhPQKfpItWOYDRBSt1y5FXQzaM,10431
|
|
@@ -130,6 +134,7 @@ backend/tests/test_workers.py,sha256=BW-MDQx5HrgICKOcd1KkvzyBO4sOfJXeLVlMiJDfmBw
|
|
|
130
134
|
backend/tests/test_workers_extended.py,sha256=uFznO4Q2v_H7xjfje5eYXXjS72EnhwXNRcT9YshuB8M,6763
|
|
131
135
|
backend/tests/test_youtube_service.py,sha256=HPm3HUJcu9rKcyRpBfufU6tzMP1lwZ9ZSe0vOoKNypI,9134
|
|
132
136
|
backend/tests/test_youtube_upload_service.py,sha256=o58DXYWLOtp8nmaowO_UHdOErVCDl8YxHJUgI-xvyMs,21428
|
|
137
|
+
backend/utils/test_data.py,sha256=0H0GmV0UnLXe-l0NKAug7COORyQI3PQtbC_qQHllIpI,713
|
|
133
138
|
backend/validate.py,sha256=u8lXKnC1ocXKAaYxNoCE9dRrhvoYI11XrTtaWIp-HLw,5040
|
|
134
139
|
backend/version.py,sha256=Ai-O0n-W-iBIYY_c71-QZikxAsmTpcel4X3T3p8AqVI,677
|
|
135
140
|
backend/workers/README.md,sha256=JYfLyngGkzxl1mRpkppIZB3ou9GR3sRVmZAxIAdURPY,15935
|
|
@@ -137,7 +142,7 @@ backend/workers/__init__.py,sha256=Eswp1Jvb6-97r7zUe7FUXSexsCZvghR08qwx1L_4298,3
|
|
|
137
142
|
backend/workers/audio_worker.py,sha256=9RWIzuhU24fpRMj9JW8cpWODeYsZrbaXhtxddrMFKIo,29088
|
|
138
143
|
backend/workers/lyrics_worker.py,sha256=nsy90pb0BPAl_lbZ0E7d7jIclcXK7W8d9RxxEdJaHVg,35149
|
|
139
144
|
backend/workers/render_video_worker.py,sha256=L6-VCxSBY6MbXBBm0-wu_DXxcIWKfW6ynTv9K03WCjY,25163
|
|
140
|
-
backend/workers/screens_worker.py,sha256=
|
|
145
|
+
backend/workers/screens_worker.py,sha256=vCS7j4dQRQ_tsWNhz-XH5nau7kGyUSzSp-dzyZpeJEo,21402
|
|
141
146
|
backend/workers/style_helper.py,sha256=7-PM79dAUAZHTj-bnATy43ImqNdl_00E9pm94CYg1BM,6906
|
|
142
147
|
backend/workers/video_worker.py,sha256=49Q7D3bNg38iJk26q2k8y3v-oUscUtE50qaUymlFQbk,55292
|
|
143
148
|
backend/workers/video_worker_orchestrator.py,sha256=NInZjFwztqcYneuxrkDqEFWV7z5BzGlaaoJxj812KA4,27510
|
|
@@ -193,7 +198,7 @@ lyrics_transcriber/core/controller.py,sha256=zRjdxOrJEaa2depvzZvwVQiEFmf8Ew3Aek8
|
|
|
193
198
|
lyrics_transcriber/correction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
194
199
|
lyrics_transcriber/correction/agentic/__init__.py,sha256=p7PHiebuvRs8RDlPDs-9gLZKzXG5KfWg3fFCdDhY6pE,222
|
|
195
200
|
lyrics_transcriber/correction/agentic/adapter.py,sha256=Z0JBTAA7xlSdctCHqO9nBMl78C4XmqsLKKtS6BvNZNI,2912
|
|
196
|
-
lyrics_transcriber/correction/agentic/agent.py,sha256=
|
|
201
|
+
lyrics_transcriber/correction/agentic/agent.py,sha256=73E2ouAiE0RUiIvxIb_lVaiIo_DJr_YaH3CAaqdjh08,13274
|
|
197
202
|
lyrics_transcriber/correction/agentic/feedback/aggregator.py,sha256=323t8LDbE26ni83woyN7uVMSuSQhnqTgwJc-d-KuDbs,273
|
|
198
203
|
lyrics_transcriber/correction/agentic/feedback/collector.py,sha256=HT-2cAP_bx7Iv-0-tpZv534do111g0FlTUt2XaKoUtA,415
|
|
199
204
|
lyrics_transcriber/correction/agentic/feedback/retention.py,sha256=dUCUsKPCzHVQxiLLBXcdfAZ5NqiG25go0Z6GFXeK0vY,881
|
|
@@ -231,8 +236,8 @@ lyrics_transcriber/correction/agentic/providers/circuit_breaker.py,sha256=D3Jg4Y
|
|
|
231
236
|
lyrics_transcriber/correction/agentic/providers/config.py,sha256=7m4I3imOZyv-6eR8mxuK9vVdqPEDcGuVAkEgdGWu10w,4299
|
|
232
237
|
lyrics_transcriber/correction/agentic/providers/constants.py,sha256=cXLzKTyFVt9q6wQd_gWcv3EZ5Sm27AOAz6NyPapcess,695
|
|
233
238
|
lyrics_transcriber/correction/agentic/providers/health.py,sha256=F8pHY5BQYvylGRDGXUHplcAJooAyiqVLRhBl4kHC1H8,710
|
|
234
|
-
lyrics_transcriber/correction/agentic/providers/langchain_bridge.py,sha256=
|
|
235
|
-
lyrics_transcriber/correction/agentic/providers/model_factory.py,sha256=
|
|
239
|
+
lyrics_transcriber/correction/agentic/providers/langchain_bridge.py,sha256=fgWZjzWiClBZ1NA6E8pf0kp23j6p56Htry_h7vtJ3ZM,12607
|
|
240
|
+
lyrics_transcriber/correction/agentic/providers/model_factory.py,sha256=ZsPdjSwnN1hFZxPwJkH-2rtgv59f9BmNRJmsLYtK67Q,10805
|
|
236
241
|
lyrics_transcriber/correction/agentic/providers/response_cache.py,sha256=Byr7fQJsgUMFlsvHeVCxTiFjjnbsg3KIlEmEEtAo-Gw,7047
|
|
237
242
|
lyrics_transcriber/correction/agentic/providers/response_parser.py,sha256=c2KypM-yHbIXXakHV5s-qh8fl8FhssLPVo3pJbyAiG4,4301
|
|
238
243
|
lyrics_transcriber/correction/agentic/providers/retry_executor.py,sha256=hX21Zwy2cSECAw7k13ndEinWRqwjo4xYoSCQ2B2CUf0,3912
|
|
@@ -241,7 +246,7 @@ lyrics_transcriber/correction/agentic/workflows/__init__.py,sha256=OsBExAbIIKxJg
|
|
|
241
246
|
lyrics_transcriber/correction/agentic/workflows/consensus_workflow.py,sha256=gMuLTUxkgYaciMsI4yrZSC3wi--7V_PgaDNE-Vd6FE8,575
|
|
242
247
|
lyrics_transcriber/correction/agentic/workflows/correction_graph.py,sha256=kgZKnz0h9cG1EfhW7BSSl-kSpQtJrRM_S86kAniXfE4,1815
|
|
243
248
|
lyrics_transcriber/correction/agentic/workflows/feedback_workflow.py,sha256=KsKLD3AP66YYmXfUn-mVZjERYLtU1Zs4a-7CB2zDfas,596
|
|
244
|
-
lyrics_transcriber/correction/anchor_sequence.py,sha256
|
|
249
|
+
lyrics_transcriber/correction/anchor_sequence.py,sha256=-FT4NoNjwGnoxgm4m117Z3DPlJAJAlFYcTe4BeUj9pQ,48974
|
|
245
250
|
lyrics_transcriber/correction/corrector.py,sha256=zqmpwt_LMG1VdijXUIMGr42ny2qIiBqwEIDCslqi2dE,42622
|
|
246
251
|
lyrics_transcriber/correction/feedback/__init__.py,sha256=i1gd0Vb4qvlzZQ3lqA3fJjt288YP7f-MBPwOzZ7Rjh4,68
|
|
247
252
|
lyrics_transcriber/correction/feedback/schemas.py,sha256=OiF_WUqcqiEKIoburYM8kWAIundy82PQE7ImsdP8UCk,4416
|
|
@@ -254,11 +259,11 @@ lyrics_transcriber/correction/handlers/no_space_punct_match.py,sha256=jY2fa547Qc
|
|
|
254
259
|
lyrics_transcriber/correction/handlers/relaxed_word_count_match.py,sha256=x4k__6gav4-STk_TycLcg5Sw4x2vUFAj5fWmOv7Yd_w,3911
|
|
255
260
|
lyrics_transcriber/correction/handlers/repeat.py,sha256=1PJADW44egYh7N9D2fN-gDIusWVglFjGHrCZuTQYNpA,4313
|
|
256
261
|
lyrics_transcriber/correction/handlers/sound_alike.py,sha256=75IvDSfoGUG2xVbYp-xsYuQXf7Jo-0ymsTzdBSOrwwQ,11935
|
|
257
|
-
lyrics_transcriber/correction/handlers/syllables_match.py,sha256=
|
|
262
|
+
lyrics_transcriber/correction/handlers/syllables_match.py,sha256=CV_sYISqPhA2VFbaU2XuX4SbjQcpeuMH7LTw91pgH4I,13103
|
|
258
263
|
lyrics_transcriber/correction/handlers/word_count_match.py,sha256=OltTEs6eYnslxdvak97M5gXDiqXJxMHKk__Q9F_akXc,3595
|
|
259
264
|
lyrics_transcriber/correction/handlers/word_operations.py,sha256=410xhyO9tiqezV5yd5JKwKbxSGwXK9LWHJ7-zNIuOWA,7423
|
|
260
265
|
lyrics_transcriber/correction/operations.py,sha256=rmSxDdlu5H2drbVvR1A9KuaZT60vbHeZKKaB7olD4ns,14659
|
|
261
|
-
lyrics_transcriber/correction/phrase_analyzer.py,sha256=
|
|
266
|
+
lyrics_transcriber/correction/phrase_analyzer.py,sha256=bEGz3KlH8iFAudMK7L0eC-CyVSwZkIZtXr8qh_k4_u8,17576
|
|
262
267
|
lyrics_transcriber/correction/text_utils.py,sha256=7QHK6-PY7Rx1G1E31sWiLBw00mHorRDo-M44KMHFaZs,833
|
|
263
268
|
lyrics_transcriber/frontend/.gitignore,sha256=cR2ofyyWArkna_jByfaWi8gTeMhsKTSoK128PmIw218,262
|
|
264
269
|
lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs,sha256=KTYy2KCV2OpHhussV5jIPDdUSr7RftMRhqPsRUmgfAY,2765465
|
|
@@ -431,8 +436,8 @@ lyrics_transcriber/transcribers/whisper.py,sha256=YcCB1ic9H6zL1GS0jD0emu8-qlcH0Q
|
|
|
431
436
|
lyrics_transcriber/types.py,sha256=UJjaxhVd2o14AG4G8ToU598p0JeYdiTFjpG38jGCoYQ,27917
|
|
432
437
|
lyrics_transcriber/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
433
438
|
lyrics_transcriber/utils/word_utils.py,sha256=-cMGpj9UV4F6IsoDKAV2i1aiqSO8eI91HMAm_igtVMk,958
|
|
434
|
-
karaoke_gen-0.
|
|
435
|
-
karaoke_gen-0.
|
|
436
|
-
karaoke_gen-0.
|
|
437
|
-
karaoke_gen-0.
|
|
438
|
-
karaoke_gen-0.
|
|
439
|
+
karaoke_gen-0.99.3.dist-info/METADATA,sha256=unsN1sCERd0RAJ-G2KFKw1UI_1sUpM-ZG6Sl3FODVJ8,23115
|
|
440
|
+
karaoke_gen-0.99.3.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
441
|
+
karaoke_gen-0.99.3.dist-info/entry_points.txt,sha256=xIyLe7K84ZyjO8L0_AmNectz93QjGSs5AkApMtlAd4g,160
|
|
442
|
+
karaoke_gen-0.99.3.dist-info/licenses/LICENSE,sha256=81R_4XwMZDODHD7JcZeUR8IiCU8AD7Ajl6bmwR9tYDk,1074
|
|
443
|
+
karaoke_gen-0.99.3.dist-info/RECORD,,
|
|
@@ -86,28 +86,39 @@ class AgenticCorrector:
|
|
|
86
86
|
|
|
87
87
|
@classmethod
|
|
88
88
|
def from_model(
|
|
89
|
-
cls,
|
|
90
|
-
model: str,
|
|
89
|
+
cls,
|
|
90
|
+
model: str,
|
|
91
91
|
config: ProviderConfig | None = None,
|
|
92
92
|
session_id: Optional[str] = None,
|
|
93
|
-
cache_dir: Optional[str] = None
|
|
93
|
+
cache_dir: Optional[str] = None,
|
|
94
|
+
warmup: bool = True
|
|
94
95
|
) -> "AgenticCorrector":
|
|
95
96
|
"""Factory method to create corrector from model specification.
|
|
96
|
-
|
|
97
|
+
|
|
97
98
|
This is a convenience method for the common case where you want
|
|
98
99
|
to use LangChainBridge with a model spec string.
|
|
99
|
-
|
|
100
|
+
|
|
100
101
|
Args:
|
|
101
102
|
model: Model identifier in format "provider/model"
|
|
102
103
|
config: Optional provider configuration
|
|
103
104
|
session_id: Optional Langfuse session ID to group related traces
|
|
104
105
|
cache_dir: Optional cache directory (uses default if not provided)
|
|
105
|
-
|
|
106
|
+
warmup: If True, eagerly initialize the model to avoid delays when
|
|
107
|
+
multiple threads call classify_gap() simultaneously (default: True)
|
|
108
|
+
|
|
106
109
|
Returns:
|
|
107
110
|
AgenticCorrector instance with LangChainBridge provider
|
|
108
111
|
"""
|
|
109
112
|
config = config or ProviderConfig.from_env(cache_dir=cache_dir)
|
|
110
113
|
provider = LangChainBridge(model=model, config=config)
|
|
114
|
+
|
|
115
|
+
# Eagerly initialize the model to avoid lazy initialization delays
|
|
116
|
+
# when multiple threads call classify_gap() simultaneously
|
|
117
|
+
if warmup:
|
|
118
|
+
logger.info(f"🤖 Warming up model {model} before parallel processing...")
|
|
119
|
+
if not provider.warmup():
|
|
120
|
+
logger.warning(f"🤖 Model warmup failed for {model}, will retry on first use")
|
|
121
|
+
|
|
111
122
|
return cls(provider=provider, session_id=session_id)
|
|
112
123
|
|
|
113
124
|
def classify_gap(
|