lattifai 0.4.5__py3-none-any.whl → 1.0.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 (76) hide show
  1. lattifai/__init__.py +61 -47
  2. lattifai/alignment/__init__.py +6 -0
  3. lattifai/alignment/lattice1_aligner.py +119 -0
  4. lattifai/alignment/lattice1_worker.py +185 -0
  5. lattifai/{tokenizer → alignment}/phonemizer.py +4 -4
  6. lattifai/alignment/segmenter.py +166 -0
  7. lattifai/{tokenizer → alignment}/tokenizer.py +244 -169
  8. lattifai/audio2.py +211 -0
  9. lattifai/caption/__init__.py +20 -0
  10. lattifai/caption/caption.py +1275 -0
  11. lattifai/{io → caption}/gemini_reader.py +30 -30
  12. lattifai/{io → caption}/gemini_writer.py +17 -17
  13. lattifai/{io → caption}/supervision.py +4 -3
  14. lattifai/caption/text_parser.py +145 -0
  15. lattifai/cli/__init__.py +17 -0
  16. lattifai/cli/alignment.py +153 -0
  17. lattifai/cli/caption.py +204 -0
  18. lattifai/cli/server.py +19 -0
  19. lattifai/cli/transcribe.py +197 -0
  20. lattifai/cli/youtube.py +128 -0
  21. lattifai/client.py +460 -251
  22. lattifai/config/__init__.py +20 -0
  23. lattifai/config/alignment.py +73 -0
  24. lattifai/config/caption.py +178 -0
  25. lattifai/config/client.py +46 -0
  26. lattifai/config/diarization.py +67 -0
  27. lattifai/config/media.py +335 -0
  28. lattifai/config/transcription.py +84 -0
  29. lattifai/diarization/__init__.py +5 -0
  30. lattifai/diarization/lattifai.py +89 -0
  31. lattifai/errors.py +98 -91
  32. lattifai/logging.py +116 -0
  33. lattifai/mixin.py +552 -0
  34. lattifai/server/app.py +420 -0
  35. lattifai/transcription/__init__.py +76 -0
  36. lattifai/transcription/base.py +108 -0
  37. lattifai/transcription/gemini.py +219 -0
  38. lattifai/transcription/lattifai.py +103 -0
  39. lattifai/{workflows → transcription}/prompts/__init__.py +4 -4
  40. lattifai/types.py +30 -0
  41. lattifai/utils.py +16 -44
  42. lattifai/workflow/__init__.py +22 -0
  43. lattifai/workflow/agents.py +6 -0
  44. lattifai/{workflows → workflow}/base.py +22 -22
  45. lattifai/{workflows → workflow}/file_manager.py +239 -215
  46. lattifai/workflow/youtube.py +564 -0
  47. lattifai-1.0.0.dist-info/METADATA +736 -0
  48. lattifai-1.0.0.dist-info/RECORD +52 -0
  49. {lattifai-0.4.5.dist-info → lattifai-1.0.0.dist-info}/WHEEL +1 -1
  50. lattifai-1.0.0.dist-info/entry_points.txt +13 -0
  51. {lattifai-0.4.5.dist-info → lattifai-1.0.0.dist-info}/licenses/LICENSE +1 -1
  52. lattifai/base_client.py +0 -126
  53. lattifai/bin/__init__.py +0 -3
  54. lattifai/bin/agent.py +0 -325
  55. lattifai/bin/align.py +0 -296
  56. lattifai/bin/cli_base.py +0 -25
  57. lattifai/bin/subtitle.py +0 -210
  58. lattifai/io/__init__.py +0 -42
  59. lattifai/io/reader.py +0 -85
  60. lattifai/io/text_parser.py +0 -75
  61. lattifai/io/utils.py +0 -15
  62. lattifai/io/writer.py +0 -90
  63. lattifai/tokenizer/__init__.py +0 -3
  64. lattifai/workers/__init__.py +0 -3
  65. lattifai/workers/lattice1_alpha.py +0 -284
  66. lattifai/workflows/__init__.py +0 -34
  67. lattifai/workflows/agents.py +0 -10
  68. lattifai/workflows/gemini.py +0 -167
  69. lattifai/workflows/prompts/README.md +0 -22
  70. lattifai/workflows/prompts/gemini/README.md +0 -24
  71. lattifai/workflows/prompts/gemini/transcription_gem.txt +0 -81
  72. lattifai/workflows/youtube.py +0 -931
  73. lattifai-0.4.5.dist-info/METADATA +0 -808
  74. lattifai-0.4.5.dist-info/RECORD +0 -39
  75. lattifai-0.4.5.dist-info/entry_points.txt +0 -3
  76. {lattifai-0.4.5.dist-info → lattifai-1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,52 @@
1
+ lattifai/__init__.py,sha256=K46XVINrXgjGehO8uXByTIbUnBCdB7QwsvVNWzKbdeU,2364
2
+ lattifai/audio2.py,sha256=WPAhcaEoIMRQBf2QZe-0yyAbgyyiqUVAthJ-z54R9Wc,7761
3
+ lattifai/client.py,sha256=Wkz7Q1XvCQ9KxD0uZ_M1ix457ZbgIG1gAxxt8nMBUj4,22147
4
+ lattifai/errors.py,sha256=dFQ_7c8rwuHrq2pDPjpzA755tAV3t8daXXFbHmWblbs,11015
5
+ lattifai/logging.py,sha256=MbUEeOUFlF92pA9v532DiPPWKl03S7UHCJ6Z652cf0w,2860
6
+ lattifai/mixin.py,sha256=7D6zeI9EIhva5FRomnj2RaJkY6ghR86otbqudcijGK4,23380
7
+ lattifai/types.py,sha256=SjYBfwrCBOXlICvH04niFQJ7OzTx7oTaa_npfRkB67U,659
8
+ lattifai/utils.py,sha256=TqOPrd_Et7KxrbfI_JbBNIGZ5-oGJY8ZUyJMPDTih1I,3848
9
+ lattifai/alignment/__init__.py,sha256=ehpkKfjNIYUx7_M-RWD_8Efcrzd9bE-NSm0QgMMVLW0,178
10
+ lattifai/alignment/lattice1_aligner.py,sha256=soBRZ98jRIju-wN5eqYUmQfF56KiEUxVGw0UvtRcx4A,4464
11
+ lattifai/alignment/lattice1_worker.py,sha256=U3izvaBCnrRmr2jBnaEcHLJLGHbZlHNb1qroFxTH8oQ,7440
12
+ lattifai/alignment/phonemizer.py,sha256=fbhN2DOl39lW4nQWKzyUUTMUabg7v61lB1kj8SKK-Sw,1761
13
+ lattifai/alignment/segmenter.py,sha256=-FKtIwv9Z4fU9Fs08jhL9VyREVSYcfcwuTqb8jxCiuo,6228
14
+ lattifai/alignment/tokenizer.py,sha256=WilqU9Ecdkl_cW86IkB1mh_PFlHN-35Jsreiyse2r-8,22355
15
+ lattifai/caption/__init__.py,sha256=6MM_2j6CaqwZ81LfSy4di2EP0ykvheRjMZKAYDx2rQs,477
16
+ lattifai/caption/caption.py,sha256=NNkBJbSdfXe4CwlCvMplrd4UOxlZyxq5Cs5g-dReB1E,46974
17
+ lattifai/caption/gemini_reader.py,sha256=GqY2w78xGYCMDP5kD5WGS8jK0gntel2SK-EPpPKTrwU,15138
18
+ lattifai/caption/gemini_writer.py,sha256=sYPxYEmVQcEan5WVGgSrcraxs3QJRQRh8CJkl2yUQ1s,6515
19
+ lattifai/caption/supervision.py,sha256=DRrM8lfKU_x9aVBcLG6xnT0xIJrnc8jzHpzcSwQOg8c,905
20
+ lattifai/caption/text_parser.py,sha256=XDb8KTt031uJ1hg6dpbINglGOTX-6pBcghbg3DULM1I,4633
21
+ lattifai/cli/__init__.py,sha256=dIUmrpN-OwR4h6BqMhXp87_5ZwgO41ShPru_iZGnpQs,463
22
+ lattifai/cli/alignment.py,sha256=uKMTE95_JMikfbyCcwLbQxms-EQmZXEj7oYugiupk9I,5890
23
+ lattifai/cli/caption.py,sha256=ucgYxJ43ab71nGpZBAiVn8QA0DAVht2QMZFE5IdgxP0,6853
24
+ lattifai/cli/server.py,sha256=k3kuQsZ2JNHZD_eMDxGSn5OFHiUrC2ybHN_xQQlLEgU,493
25
+ lattifai/cli/transcribe.py,sha256=6uJfvtB1o_u1uQwxt4fje_koyfN93mGaFLlskmjqx2c,7406
26
+ lattifai/cli/youtube.py,sha256=9_erdIkhX8pCiy7BRzNstEiO9saM-VKZ1WVqvbXbmrc,5267
27
+ lattifai/config/__init__.py,sha256=Z8OudvS6fgfLNLu_2fvoXartQiYCECOnNfzDt-PfCN4,543
28
+ lattifai/config/alignment.py,sha256=z0b9tg67ftDI90j9Td3qmXFg3WZjSbgszjzeTdwrjZA,3291
29
+ lattifai/config/caption.py,sha256=nmfdsJ-18l4UmapdVgxF1ARJbA4aOr7jek1bmse2F_E,6787
30
+ lattifai/config/client.py,sha256=I1JqLQlsQNU5ouovTumr-PP_8GWC9DI_e9B5UwsDZws,1492
31
+ lattifai/config/diarization.py,sha256=cIkwCfsYqfMns3i6tKWcwBBBkdnhhmB_Eo0TuOPCw9o,2484
32
+ lattifai/config/media.py,sha256=5JOPjifXDM2WWQERySDZen4-7YfgQNcYM2NkkKp0LjQ,13610
33
+ lattifai/config/transcription.py,sha256=bzghOGgcNWzTnDYd_cqCOB7GT8OnzHDiyam7LSixqxM,2901
34
+ lattifai/diarization/__init__.py,sha256=MgBDQ1ehL2qDnZprEp8KqON7CmbG-qaP37gzBsV0jzk,119
35
+ lattifai/diarization/lattifai.py,sha256=SE2BpIZ3_deKyhXdBqe77bsDLXIUV9AQV34gfINv7_s,2657
36
+ lattifai/server/app.py,sha256=ndMBC_FuAFZGs9bvrgHUMtgDDDeYGhCkW0Lx49SEh7k,15193
37
+ lattifai/transcription/__init__.py,sha256=mEoMTbs5jAgtXQn1jTjlFY_GUr-S0WmPn8uZ6WZCkU0,2643
38
+ lattifai/transcription/base.py,sha256=59b4nQHFMyTRyyzBJTM8ZpEuUy1KjwA2o6rNfrNluKY,3911
39
+ lattifai/transcription/gemini.py,sha256=pdc4Q1rk1QLpheC7zIauUOXWViUOM1qGnwC5cRoxH-4,7992
40
+ lattifai/transcription/lattifai.py,sha256=h0nhXST0qljhyndf80IEddM7Y_N1jiS28YoaE536eME,3483
41
+ lattifai/transcription/prompts/__init__.py,sha256=G9b42COaCYv3sPPNkHsGDLOMBuVGKt4mXGYal_BYtYQ,1351
42
+ lattifai/workflow/__init__.py,sha256=GOT9jptXwpIMiNRqJ_LToEt_5Dt0k7XXbLkFzhrl31o,548
43
+ lattifai/workflow/agents.py,sha256=yEOnxnhcTvr1iOhCorNvp8B76P6nQsLRXJCu_rCYFfM,38
44
+ lattifai/workflow/base.py,sha256=8QoVIBZwJfr5mppJbtUFafHv5QR9lL-XrULjTWD0oBg,6257
45
+ lattifai/workflow/file_manager.py,sha256=d106KHLY8A9amLy5h1vR32e4od8mmJGqMD-iDyiRPLI,32917
46
+ lattifai/workflow/youtube.py,sha256=n8L1c6tl8FuYzAzKZ-B76zf5yZsvVggZEJ9mPdbEWGQ,22989
47
+ lattifai-1.0.0.dist-info/licenses/LICENSE,sha256=_IkHdwOWLAWcE1M_tIpDoRWdNSJwFdtIqI-XSkK3yPU,1066
48
+ lattifai-1.0.0.dist-info/METADATA,sha256=orTvmokTIN-W7Lcm5LBs3nof6T8DWKcY9mrU1MwOApE,23109
49
+ lattifai-1.0.0.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
50
+ lattifai-1.0.0.dist-info/entry_points.txt,sha256=Ay-Rxb214lYSuJIXO_wuNlUu1LbvOXtMJ-G-PcvGZFw,424
51
+ lattifai-1.0.0.dist-info/top_level.txt,sha256=tHSoXF26r-IGfbIP_JoYATqbmf14h5NrnNJGH4j5reI,9
52
+ lattifai-1.0.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (79.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,13 @@
1
+ [console_scripts]
2
+ lai-align = lattifai.cli.alignment:main
3
+ lai-server = lattifai.cli.server:main
4
+ lai-transcribe = lattifai.cli.transcribe:main
5
+ lai-youtube = lattifai.cli.youtube:main
6
+ laicap-convert = lattifai.cli.caption:main_convert
7
+ laicap-normalize = lattifai.cli.caption:main_normalize
8
+ laicap-shift = lattifai.cli.caption:main_shift
9
+
10
+ [lai_run.cli]
11
+ alignment = lattifai.cli
12
+ caption = lattifai.cli
13
+ transcribe = lattifai.cli
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
lattifai/base_client.py DELETED
@@ -1,126 +0,0 @@
1
- """Base client classes for LattifAI SDK."""
2
-
3
- import os
4
- from abc import ABC
5
- from typing import Any, Awaitable, Callable, Dict, Optional, Union # noqa: F401
6
-
7
- import httpx
8
-
9
- # Import from errors module for consistency
10
- from .errors import APIError, ConfigurationError, LattifAIError
11
-
12
-
13
- class BaseAPIClient(ABC):
14
- """Abstract base class for API clients."""
15
-
16
- def __init__(
17
- self,
18
- *,
19
- api_key: Optional[str] = None,
20
- base_url: Optional[str] = None,
21
- timeout: Union[float, httpx.Timeout] = 60.0,
22
- max_retries: int = 2,
23
- default_headers: Optional[Dict[str, str]] = None,
24
- ) -> None:
25
- if api_key is None:
26
- api_key = os.environ.get('LATTIFAI_API_KEY')
27
- if api_key is None:
28
- raise ConfigurationError(
29
- 'The api_key client option must be set either by passing api_key to the client '
30
- 'or by setting the LATTIFAI_API_KEY environment variable'
31
- )
32
-
33
- self._api_key = api_key
34
- self._base_url = base_url
35
- self._timeout = timeout
36
- self._max_retries = max_retries
37
-
38
- headers = {
39
- 'User-Agent': 'LattifAI/Python',
40
- 'Authorization': f'Bearer {self._api_key}',
41
- }
42
- if default_headers:
43
- headers.update(default_headers)
44
- self._default_headers = headers
45
-
46
-
47
- class SyncAPIClient(BaseAPIClient):
48
- """Synchronous API client."""
49
-
50
- def __init__(self, **kwargs) -> None:
51
- super().__init__(**kwargs)
52
- self._client = httpx.Client(
53
- base_url=self._base_url,
54
- timeout=self._timeout,
55
- headers=self._default_headers,
56
- )
57
-
58
- def __enter__(self):
59
- return self
60
-
61
- def __exit__(self, exc_type, exc_val, exc_tb):
62
- self.close()
63
-
64
- def close(self) -> None:
65
- """Close the HTTP client."""
66
- self._client.close()
67
-
68
- def _request(
69
- self,
70
- method: str,
71
- url: str,
72
- *,
73
- json: Optional[Dict[str, Any]] = None,
74
- **kwargs,
75
- ) -> httpx.Response:
76
- """Make an HTTP request."""
77
- return self._client.request(method=method, url=url, json=json, **kwargs)
78
-
79
- def post(self, api_endpoint: str, *, json: Optional[Dict[str, Any]] = None, **kwargs) -> httpx.Response:
80
- """Make a POST request to the specified API endpoint."""
81
- return self._request('POST', api_endpoint, json=json, **kwargs)
82
-
83
-
84
- class AsyncAPIClient(BaseAPIClient):
85
- """Asynchronous API client."""
86
-
87
- def __init__(self, **kwargs) -> None:
88
- super().__init__(**kwargs)
89
- self._client = httpx.AsyncClient(
90
- base_url=self._base_url,
91
- timeout=self._timeout,
92
- headers=self._default_headers,
93
- )
94
-
95
- async def __aenter__(self):
96
- return self
97
-
98
- async def __aexit__(self, exc_type, exc_val, exc_tb):
99
- await self.close()
100
-
101
- async def close(self) -> None:
102
- """Close the HTTP client."""
103
- await self._client.aclose()
104
-
105
- async def _request(
106
- self,
107
- method: str,
108
- url: str,
109
- *,
110
- json: Optional[Dict[str, Any]] = None,
111
- files: Optional[Dict[str, Any]] = None,
112
- **kwargs,
113
- ) -> httpx.Response:
114
- """Make an HTTP request."""
115
- return await self._client.request(method=method, url=url, json=json, files=files, **kwargs)
116
-
117
- async def post(
118
- self,
119
- api_endpoint: str,
120
- *,
121
- json: Optional[Dict[str, Any]] = None,
122
- files: Optional[Dict[str, Any]] = None,
123
- **kwargs,
124
- ) -> httpx.Response:
125
- """Make a POST request to the specified API endpoint."""
126
- return await self._request('POST', api_endpoint, json=json, files=files, **kwargs)
lattifai/bin/__init__.py DELETED
@@ -1,3 +0,0 @@
1
- from .agent import * # noqa
2
- from .align import * # noqa
3
- from .subtitle import * # noqa
lattifai/bin/agent.py DELETED
@@ -1,325 +0,0 @@
1
- """
2
- Agent command for YouTube workflow
3
- """
4
-
5
- import asyncio
6
- import os
7
- import sys
8
- from typing import List, Optional
9
-
10
- import click
11
- import colorful
12
- from lhotse.utils import Pathlike
13
-
14
- from lattifai.bin.cli_base import cli
15
- from lattifai.io import OUTPUT_SUBTITLE_FORMATS
16
-
17
-
18
- @cli.command()
19
- @click.option('--youtube', '--yt', is_flag=True, help='Process YouTube URL through agentic workflow.')
20
- @click.option(
21
- '-K',
22
- '-L',
23
- '--api-key',
24
- '--api_key',
25
- type=str,
26
- help='LattifAI API key for alignment (overrides LATTIFAI_API_KEY env var).',
27
- )
28
- @click.option(
29
- '-G',
30
- '--gemini-api-key',
31
- '--gemini_api_key',
32
- type=str,
33
- help='Gemini API key for transcription (overrides GEMINI_API_KEY env var).',
34
- )
35
- @click.option(
36
- '-D',
37
- '--device',
38
- type=click.Choice(['cpu', 'cuda', 'mps'], case_sensitive=False),
39
- default='cpu',
40
- help='Device to use for inference.',
41
- )
42
- @click.option(
43
- '-M',
44
- '--model-name-or-path',
45
- '--model_name_or_path',
46
- type=str,
47
- default='Lattifai/Lattice-1-Alpha',
48
- help='Model name or path for alignment.',
49
- )
50
- @click.option(
51
- '--media-format',
52
- '--media_format',
53
- type=click.Choice(
54
- ['mp3', 'wav', 'm4a', 'aac', 'opus', 'mp4', 'webm', 'mkv', 'avi', 'mov', 'flv', 'wmv', 'mpeg', 'mpg', '3gp'],
55
- case_sensitive=False,
56
- ),
57
- default='mp4',
58
- help='Media format for YouTube download (audio or video).',
59
- )
60
- @click.option(
61
- '--output-format',
62
- '--output_format',
63
- type=click.Choice(OUTPUT_SUBTITLE_FORMATS, case_sensitive=False),
64
- default='srt',
65
- help='Subtitle output format.',
66
- )
67
- @click.option(
68
- '--output-dir',
69
- '--output_dir',
70
- type=click.Path(exists=False, file_okay=False, dir_okay=True),
71
- help='Output directory for generated files (default: current directory).',
72
- )
73
- @click.option(
74
- '--max-retries',
75
- '--max_retries',
76
- type=int,
77
- default=0,
78
- help='Maximum number of retries for failed steps.',
79
- )
80
- @click.option(
81
- '-S',
82
- '--split-sentence',
83
- '--split_sentence',
84
- is_flag=True,
85
- default=False,
86
- help='Re-segment subtitles by semantics.',
87
- )
88
- @click.option(
89
- '--word-level',
90
- '--word_level',
91
- is_flag=True,
92
- default=False,
93
- help='Include word-level alignment timestamps in output (for JSON, TextGrid, and subtitle formats).',
94
- )
95
- @click.option('--verbose', '-v', is_flag=True, help='Enable verbose logging.')
96
- @click.option('--force', '-f', is_flag=True, help='Force overwrite existing files without confirmation.')
97
- @click.argument('url', type=str, required=True)
98
- def agent(
99
- youtube: bool,
100
- url: str,
101
- api_key: Optional[str] = None,
102
- gemini_api_key: Optional[str] = None,
103
- device: str = 'cpu',
104
- model_name_or_path: str = 'Lattifai/Lattice-1-Alpha',
105
- media_format: str = 'mp4',
106
- output_format: str = 'srt',
107
- output_dir: Optional[str] = None,
108
- max_retries: int = 0,
109
- split_sentence: bool = False,
110
- word_level: bool = False,
111
- verbose: bool = False,
112
- force: bool = False,
113
- ):
114
- """
115
- LattifAI Agentic Workflow Agent
116
-
117
- Process multimedia content through intelligent agent-based pipelines.
118
-
119
- Example:
120
- lattifai agent --youtube https://www.youtube.com/watch?v=example
121
- """
122
-
123
- if not youtube:
124
- click.echo(colorful.red('❌ Please specify a workflow type. Use --youtube for YouTube processing.'))
125
- return
126
-
127
- # Setup logging
128
- import logging
129
-
130
- log_level = logging.DEBUG if verbose else logging.INFO
131
- logging.basicConfig(level=log_level, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
132
-
133
- # Set default output directory
134
- if not output_dir:
135
- output_dir = os.getcwd()
136
-
137
- # Get API keys
138
- lattifai_api_key = api_key or os.getenv('LATTIFAI_API_KEY')
139
- gemini_key = gemini_api_key or os.getenv('GEMINI_API_KEY')
140
-
141
- try:
142
- # Run the YouTube workflow
143
- asyncio.run(
144
- _run_youtube_workflow(
145
- url=url,
146
- lattifai_api_key=lattifai_api_key,
147
- gemini_api_key=gemini_key,
148
- device=device,
149
- model_name_or_path=model_name_or_path,
150
- media_format=media_format,
151
- output_format=output_format,
152
- output_dir=output_dir,
153
- max_retries=max_retries,
154
- split_sentence=split_sentence,
155
- word_level=word_level,
156
- force_overwrite=force,
157
- )
158
- )
159
-
160
- except KeyboardInterrupt:
161
- click.echo(colorful.yellow('\n⚠️ Process interrupted by user'))
162
- sys.exit(1)
163
- except Exception as e:
164
- from lattifai.errors import LattifAIError
165
-
166
- # Extract error message without support info (to avoid duplication)
167
- if isinstance(e, LattifAIError):
168
- # Use the get_message() method which includes proper formatting
169
- click.echo(colorful.red('❌ Workflow failed:'))
170
- click.echo(e.get_message())
171
- # Show support info once at the end
172
- click.echo(e.get_support_info())
173
- else:
174
- click.echo(colorful.red(f'❌ Workflow failed: {str(e)}'))
175
-
176
- if verbose:
177
- import traceback
178
-
179
- traceback.print_exc()
180
- sys.exit(1)
181
-
182
-
183
- async def _run_youtube_workflow(
184
- url: str,
185
- lattifai_api_key: Optional[str],
186
- gemini_api_key: str,
187
- device: str,
188
- model_name_or_path: str,
189
- media_format: str,
190
- output_format: str,
191
- output_dir: str,
192
- max_retries: int,
193
- split_sentence: bool = False,
194
- word_level: bool = False,
195
- force_overwrite: bool = False,
196
- ):
197
- """Run the YouTube processing workflow"""
198
-
199
- click.echo(colorful.cyan('🚀 LattifAI Agentic Workflow - YouTube Processing'))
200
- click.echo(f'📺 YouTube URL: {url}')
201
- click.echo(f'🎬 Media format: {media_format}')
202
- click.echo(f'📝 Output format: {output_format}')
203
- click.echo(f'📁 Output directory: {output_dir}')
204
- click.echo(f'🔄 Max retries: {max_retries}')
205
- click.echo()
206
-
207
- # Import workflow components
208
- from lattifai.client import AsyncLattifAI
209
- from lattifai.workflows import YouTubeSubtitleAgent
210
- from lattifai.workflows.gemini import GeminiTranscriber
211
- from lattifai.workflows.youtube import YouTubeDownloader
212
-
213
- # Initialize components with their configuration (only persistent config, not runtime params)
214
- downloader = YouTubeDownloader()
215
- transcriber = GeminiTranscriber(api_key=gemini_api_key)
216
- aligner = AsyncLattifAI(model_name_or_path=model_name_or_path, device=device, api_key=lattifai_api_key)
217
-
218
- # Initialize agent with components
219
- agent = YouTubeSubtitleAgent(
220
- downloader=downloader,
221
- transcriber=transcriber,
222
- aligner=aligner,
223
- max_retries=max_retries,
224
- )
225
-
226
- # Process the URL
227
- result = await agent.process_youtube_url(
228
- url=url,
229
- output_dir=output_dir,
230
- media_format=media_format,
231
- force_overwrite=force_overwrite,
232
- output_format=output_format,
233
- split_sentence=split_sentence,
234
- word_level=word_level,
235
- )
236
-
237
- # Display results
238
- click.echo(colorful.bold_white_on_green('🎉 Workflow completed successfully!'))
239
- click.echo()
240
- click.echo(colorful.bold_white_on_green('📊 Results:'))
241
-
242
- # Show metadata
243
- metadata = result.get('metadata', {})
244
- if metadata:
245
- click.echo(f'🎬 Title: {metadata.get("title", "Unknown")}')
246
- click.echo(f'👤 Uploader: {metadata.get("uploader", "Unknown").strip()}')
247
- click.echo(f'⏱️ Duration: {metadata.get("duration", 0)} seconds')
248
- click.echo()
249
-
250
- # Show exported files
251
- exported_files = result.get('exported_files', {})
252
- if exported_files:
253
- click.echo(colorful.bold_white_on_green('📄 Generated subtitle files:'))
254
- for format_name, file_path in exported_files.items():
255
- click.echo(f' {format_name.upper()}: {file_path}')
256
- click.echo()
257
-
258
- # Show subtitle count
259
- subtitle_count = result.get('subtitle_count', 0)
260
- click.echo(f'📝 Generated {subtitle_count} subtitle segments')
261
-
262
- click.echo(colorful.bold_white_on_green('✨ All done! Your aligned subtitles are ready.'))
263
-
264
-
265
- # Add dependencies check
266
- def check_dependencies():
267
- """Check if required dependencies are installed"""
268
- missing_deps = []
269
-
270
- try:
271
- from google import genai
272
- except ImportError:
273
- missing_deps.append('google-genai')
274
-
275
- try:
276
- import yt_dlp
277
- except ImportError:
278
- missing_deps.append('yt-dlp')
279
-
280
- try:
281
- from dotenv import load_dotenv
282
- except ImportError:
283
- missing_deps.append('python-dotenv')
284
-
285
- if missing_deps:
286
- click.echo(colorful.red('❌ Missing required dependencies:'))
287
- for dep in missing_deps:
288
- click.echo(f' - {dep}')
289
- click.echo()
290
- click.echo('Install them with:')
291
- click.echo(f' pip install {" ".join(missing_deps)}')
292
- return False
293
-
294
- return True
295
-
296
-
297
- # Check dependencies when module is imported
298
- if not check_dependencies():
299
- pass # Don't exit on import, let the command handle it
300
-
301
-
302
- if __name__ == '__main__':
303
- import os
304
-
305
- from dotenv import find_dotenv, load_dotenv
306
-
307
- load_dotenv(find_dotenv(usecwd=True))
308
-
309
- asyncio.run(
310
- _run_youtube_workflow(
311
- # url='https://www.youtube.com/watch?v=7nv1snJRCEI',
312
- url='https://www.youtube.com/watch?v=DQacCB9tDaw',
313
- lattifai_api_key=os.getenv('LATTIFAI_API_KEY'),
314
- gemini_api_key=os.getenv('GEMINI_API_KEY', ''),
315
- device='mps',
316
- model_name_or_path='Lattifai/Lattice-1-Alpha',
317
- media_format='mp3',
318
- output_format='TextGrid',
319
- output_dir='~/Downloads/lattifai_youtube',
320
- max_retries=0,
321
- split_sentence=True,
322
- word_level=False,
323
- force_overwrite=False,
324
- )
325
- )