superclonica-cpu 0.6.25__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.
- superclonica/__init__.py +5 -0
- superclonica/__main__.py +59 -0
- superclonica/_supertonic/LICENSE +21 -0
- superclonica/_supertonic/__init__.py +3 -0
- superclonica/_supertonic/config.py +247 -0
- superclonica/_supertonic/core.py +531 -0
- superclonica/_supertonic/loader.py +462 -0
- superclonica/_supertonic/pipeline.py +415 -0
- superclonica/_supertonic/utils.py +217 -0
- superclonica/assets/confirmed_runtime_heads.onnx +0 -0
- superclonica/assets/confirmed_runtime_parent.onnx +0 -0
- superclonica/assets/confirmed_runtime_window.npz +0 -0
- superclonica/assets/confirmed_v15.npz +0 -0
- superclonica/assets/confirmed_v16.npz +0 -0
- superclonica/assets/confirmed_v21.json +46 -0
- superclonica/assets/confirmed_v21.npz +0 -0
- superclonica/assets/edge_denoiser_multivoice_v5.npz +0 -0
- superclonica/assets/fade_spectral_v8.npz +0 -0
- superclonica/assets/prefix_noise_v1.npz +0 -0
- superclonica/audio_io.py +18 -0
- superclonica/batching.py +137 -0
- superclonica/chunker.py +203 -0
- superclonica/confirmed/__init__.py +1 -0
- superclonica/confirmed/boundary.py +43 -0
- superclonica/confirmed/effects.py +83 -0
- superclonica/confirmed/ending_effect_v17.py +56 -0
- superclonica/confirmed/ending_effect_v18.py +63 -0
- superclonica/confirmed/ending_effect_v18b.py +11 -0
- superclonica/confirmed/ending_effect_v19.py +49 -0
- superclonica/confirmed/learned_repair_v15.py +124 -0
- superclonica/confirmed/onnx_zero_ending.py +118 -0
- superclonica/confirmed/terminal_declick_v16.py +65 -0
- superclonica/cpu_graph.py +305 -0
- superclonica/cuda_graph.py +439 -0
- superclonica/denoise.py +181 -0
- superclonica/devices.py +278 -0
- superclonica/duration_guard.py +37 -0
- superclonica/edge_denoiser.py +258 -0
- superclonica/ending.py +1558 -0
- superclonica/ending_effect_gate.py +140 -0
- superclonica/ending_reverb.py +92 -0
- superclonica/engine.py +140 -0
- superclonica/fade_selector.py +47 -0
- superclonica/finisher.py +96 -0
- superclonica/lanes.py +282 -0
- superclonica/loudness.py +139 -0
- superclonica/model_store.py +79 -0
- superclonica/prefix_denoiser.py +109 -0
- superclonica/resources.py +11 -0
- superclonica/slang.py +53 -0
- superclonica/spectral_fade.py +156 -0
- superclonica/synthesis.py +387 -0
- superclonica/text.py +41 -0
- superclonica_cpu-0.6.25.dist-info/METADATA +243 -0
- superclonica_cpu-0.6.25.dist-info/RECORD +62 -0
- superclonica_cpu-0.6.25.dist-info/WHEEL +5 -0
- superclonica_cpu-0.6.25.dist-info/entry_points.txt +2 -0
- superclonica_cpu-0.6.25.dist-info/licenses/LICENSE.txt +201 -0
- superclonica_cpu-0.6.25.dist-info/licenses/NOTICE.txt +29 -0
- superclonica_cpu-0.6.25.dist-info/licenses/SUPERTONIC_CODE_LICENSE.txt +21 -0
- superclonica_cpu-0.6.25.dist-info/licenses/SUPERTONIC_MODEL_LICENSE.txt +209 -0
- superclonica_cpu-0.6.25.dist-info/top_level.txt +1 -0
superclonica/__init__.py
ADDED
superclonica/__main__.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Command line: superclonica --text "Hello." --voice F3 --lang en --output hello.wav"""
|
|
2
|
+
import argparse
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main(argv=None):
|
|
8
|
+
from . import __version__
|
|
9
|
+
p = argparse.ArgumentParser(prog='superclonica', description='Superclonica text to speech (Supertonic 3)')
|
|
10
|
+
p.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
|
|
11
|
+
source = p.add_mutually_exclusive_group(required=True)
|
|
12
|
+
source.add_argument('--text')
|
|
13
|
+
source.add_argument('--text-file', type=Path)
|
|
14
|
+
p.add_argument('--output', type=Path, required=True)
|
|
15
|
+
p.add_argument('--voice', default='M1', help='M1..M5, F1..F5 or a voice-style JSON file')
|
|
16
|
+
p.add_argument('--lang', default='en')
|
|
17
|
+
p.add_argument('--seed', type=int)
|
|
18
|
+
p.add_argument('--model-dir', type=Path, help='an existing Supertonic 3 copy (default: download once)')
|
|
19
|
+
p.add_argument('--steps', type=int, help='model steps (default 8)')
|
|
20
|
+
p.add_argument('--speed', type=float, default=1.05)
|
|
21
|
+
p.add_argument('--device', choices=['auto', 'cpu', 'cuda'], default='auto')
|
|
22
|
+
p.add_argument('--device-id', type=int, default=0)
|
|
23
|
+
p.add_argument('--threads', type=int, help='CPU threads (default: one per physical core)')
|
|
24
|
+
p.add_argument('--lanes', type=int, default=1, help='parallel model copies for the pieces of a long text')
|
|
25
|
+
p.add_argument('--gpu-memory-mb', type=int, default=1024, help='CUDA arena limit per model session')
|
|
26
|
+
p.add_argument('--cuda-graph', choices=('auto', 'on', 'off'), default='auto',
|
|
27
|
+
help='auto = when there is GPU memory and a start-up check shows they are faster here')
|
|
28
|
+
p.add_argument('--effect', choices=['auto', 'off'], default='auto', help='ending effects (cleanup always runs)')
|
|
29
|
+
p.add_argument('--normalize', action='store_true', help='loudness -18 LUFS')
|
|
30
|
+
p.add_argument('--no-denoise', action='store_true', help='keep the model hiss and pause residue')
|
|
31
|
+
p.add_argument('--no-chunking', action='store_true')
|
|
32
|
+
p.add_argument('--no-numbers', action='store_true', help='do not spell digits as words')
|
|
33
|
+
p.add_argument('--no-slang', action='store_true', help='do not write out chat shorthand ("гг вп", "pls", "xD")')
|
|
34
|
+
p.add_argument('--keep-silence', action='store_true', help="keep the model's silence before and after the phrase")
|
|
35
|
+
p.add_argument('--report', type=Path, help='write a JSON report of the synthesis')
|
|
36
|
+
a = p.parse_args(argv)
|
|
37
|
+
if a.threads is not None and a.threads < 1:
|
|
38
|
+
p.error('--threads must be positive')
|
|
39
|
+
if not 1 <= a.lanes <= 8:
|
|
40
|
+
p.error('--lanes must be from 1 to 8')
|
|
41
|
+
text = a.text if a.text is not None else a.text_file.read_text('utf-8-sig')
|
|
42
|
+
from . import Superclonica
|
|
43
|
+
tts = Superclonica(model_dir=a.model_dir, total_steps=a.steps, speed=a.speed, device=a.device,
|
|
44
|
+
device_id=a.device_id, threads=a.threads, lanes=a.lanes, gpu_memory_mb=a.gpu_memory_mb,
|
|
45
|
+
cuda_graph={'auto': 'auto', 'on': True, 'off': False}[a.cuda_graph],
|
|
46
|
+
chunking=not a.no_chunking, numbers=not a.no_numbers, denoise=not a.no_denoise,
|
|
47
|
+
trim_silence=not a.keep_silence, slang=not a.no_slang)
|
|
48
|
+
wave, sr, report = tts.synthesize(text, a.voice, a.lang, seed=a.seed, effect=a.effect,
|
|
49
|
+
normalize=a.normalize, return_report=True)
|
|
50
|
+
a.output.parent.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
tts.save(a.output, wave)
|
|
52
|
+
if a.report:
|
|
53
|
+
a.report.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
a.report.write_text(json.dumps(report, ensure_ascii=False, indent=2, default=str), 'utf-8')
|
|
55
|
+
print(str(a.output))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == '__main__':
|
|
59
|
+
main()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Supertone Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Configuration and constants for Supertonic TTS package.
|
|
2
|
+
|
|
3
|
+
This module centralizes all configuration values, magic numbers, and default
|
|
4
|
+
settings used throughout the package.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
# Available models
|
|
15
|
+
AVAILABLE_MODELS = ["supertonic", "supertonic-2", "supertonic-3"]
|
|
16
|
+
DEFAULT_MODEL = "supertonic-3"
|
|
17
|
+
|
|
18
|
+
# Model configuration mapping.
|
|
19
|
+
#
|
|
20
|
+
# `revision` pins the HuggingFace Hub commit each Supertonic release was
|
|
21
|
+
# tested against. Pinning by SHA (instead of `"main"`) keeps a `pip install`
|
|
22
|
+
# of a given package version reproducible even if the HF repo later
|
|
23
|
+
# changes. To upgrade the model, bump the SHA here and ship a new package
|
|
24
|
+
# release. Power users can still override with the
|
|
25
|
+
# `SUPERTONIC_MODEL_REVISION` environment variable.
|
|
26
|
+
MODEL_CONFIGS = {
|
|
27
|
+
"supertonic": {
|
|
28
|
+
"repo": "Supertone/supertonic",
|
|
29
|
+
"cache_dir": "supertonic",
|
|
30
|
+
"multilingual": False,
|
|
31
|
+
"revision": "b6856d033f622c63ea29441795be266a1133e227",
|
|
32
|
+
},
|
|
33
|
+
"supertonic-2": {
|
|
34
|
+
"repo": "Supertone/supertonic-2",
|
|
35
|
+
"cache_dir": "supertonic2",
|
|
36
|
+
"multilingual": True,
|
|
37
|
+
"revision": "75e6727618a02f323c720cba9478152d4bc16ca4",
|
|
38
|
+
},
|
|
39
|
+
"supertonic-3": {
|
|
40
|
+
"repo": "Supertone/supertonic-3",
|
|
41
|
+
"cache_dir": "supertonic3",
|
|
42
|
+
"multilingual": True,
|
|
43
|
+
"revision": "724fb5abbf5502583fb520898d45929e62f02c0b",
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Default model settings (can be overridden by environment variables)
|
|
48
|
+
DEFAULT_MODEL_REPO = os.getenv("SUPERTONIC_MODEL_REPO", MODEL_CONFIGS[DEFAULT_MODEL]["repo"])
|
|
49
|
+
# ``SUPERTONIC_CACHE_DIR`` is intentionally not snapshotted to a module-level
|
|
50
|
+
# constant here — it is consulted at call time inside
|
|
51
|
+
# :func:`get_model_cache_dir` so the override is honored on every code path
|
|
52
|
+
# (notably ``TTS(model="…")``, the default and most common entry) and
|
|
53
|
+
# late-set env vars (test fixtures, late configuration) take effect.
|
|
54
|
+
# Optional revision override. When unset (the common case), each model is
|
|
55
|
+
# downloaded at its pinned SHA from `MODEL_CONFIGS`. When set, the override
|
|
56
|
+
# applies to every model — primarily a debugging / development knob.
|
|
57
|
+
MODEL_REVISION_ENV_OVERRIDE = os.getenv("SUPERTONIC_MODEL_REVISION")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_model_config(model_name: str) -> dict:
|
|
61
|
+
"""Get configuration for a specific model.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
model_name: Model name (one of ``AVAILABLE_MODELS``)
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Dictionary with model configuration (repo, cache_dir, multilingual)
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
ValueError: If model_name is not valid
|
|
71
|
+
"""
|
|
72
|
+
if model_name not in MODEL_CONFIGS:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"Invalid model: '{model_name}'. " f"Available models: {', '.join(AVAILABLE_MODELS)}"
|
|
75
|
+
)
|
|
76
|
+
return MODEL_CONFIGS[model_name]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_model_cache_dir(model_name: str) -> Path:
|
|
80
|
+
"""Get cache directory for a specific model.
|
|
81
|
+
|
|
82
|
+
``$SUPERTONIC_CACHE_DIR`` overrides the directory for *every* model when
|
|
83
|
+
set — matching the documented contract and the historical
|
|
84
|
+
``DEFAULT_CACHE_DIR`` spelling. Users running two models from the same
|
|
85
|
+
process and wanting them separated should leave the env var unset (each
|
|
86
|
+
model then falls back to its own ``~/.cache/<cache_dir>``).
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
model_name: Model name (one of ``AVAILABLE_MODELS``)
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Path to the model's cache directory
|
|
93
|
+
"""
|
|
94
|
+
env = os.environ.get("SUPERTONIC_CACHE_DIR")
|
|
95
|
+
if env:
|
|
96
|
+
return Path(env).expanduser()
|
|
97
|
+
config = get_model_config(model_name)
|
|
98
|
+
return Path.home() / ".cache" / config["cache_dir"]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def get_model_repo(model_name: str) -> str:
|
|
102
|
+
"""Get HuggingFace repo ID for a specific model.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
model_name: Model name (one of ``AVAILABLE_MODELS``)
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
HuggingFace repository ID
|
|
109
|
+
"""
|
|
110
|
+
config = get_model_config(model_name)
|
|
111
|
+
return config["repo"]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get_model_revision(model_name: str) -> str:
|
|
115
|
+
"""Get the HuggingFace Hub revision to download for a model.
|
|
116
|
+
|
|
117
|
+
Priority: ``SUPERTONIC_MODEL_REVISION`` env var (if set) → the
|
|
118
|
+
per-model pinned SHA in ``MODEL_CONFIGS``. Pinning by SHA keeps a
|
|
119
|
+
given package version reproducible across HF Hub updates.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
model_name: Model name (one of ``AVAILABLE_MODELS``)
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Commit SHA (or whatever ref the env override points at).
|
|
126
|
+
"""
|
|
127
|
+
if MODEL_REVISION_ENV_OVERRIDE:
|
|
128
|
+
return MODEL_REVISION_ENV_OVERRIDE
|
|
129
|
+
return get_model_config(model_name)["revision"]
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def is_multilingual_model(model_name: str) -> bool:
|
|
133
|
+
"""Check if a model supports multilingual synthesis.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
model_name: Model name (one of ``AVAILABLE_MODELS``)
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
True if model supports multiple languages
|
|
140
|
+
"""
|
|
141
|
+
config = get_model_config(model_name)
|
|
142
|
+
return config["multilingual"]
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# Model paths
|
|
146
|
+
ONNX_DIR = Path("onnx")
|
|
147
|
+
VOICE_STYLES_DIR = Path("voice_styles")
|
|
148
|
+
|
|
149
|
+
CFG_REL_PATH = ONNX_DIR / "tts.json"
|
|
150
|
+
UNICODE_INDEXER_REL_PATH = ONNX_DIR / "unicode_indexer.json"
|
|
151
|
+
DP_ONNX_REL_PATH = ONNX_DIR / "duration_predictor.onnx"
|
|
152
|
+
TEXT_ENC_ONNX_REL_PATH = ONNX_DIR / "text_encoder.onnx"
|
|
153
|
+
VECTOR_EST_ONNX_REL_PATH = ONNX_DIR / "vector_estimator.onnx"
|
|
154
|
+
VOCODER_ONNX_REL_PATH = ONNX_DIR / "vocoder.onnx"
|
|
155
|
+
|
|
156
|
+
# Language configuration (multilingual support)
|
|
157
|
+
# supertonic-3 supports 31 languages plus the special "na" fallback for unknown.
|
|
158
|
+
# supertonic-2 supports a subset (en, ko, es, pt, fr) — language validation
|
|
159
|
+
# only enforces membership in this list; mismatches between a model and a
|
|
160
|
+
# language outside its training set are not blocked here.
|
|
161
|
+
SUPPORTED_LANGUAGES = [
|
|
162
|
+
"en", # English
|
|
163
|
+
"ko", # Korean
|
|
164
|
+
"ja", # Japanese
|
|
165
|
+
"ar", # Arabic
|
|
166
|
+
"bg", # Bulgarian
|
|
167
|
+
"cs", # Czech
|
|
168
|
+
"da", # Danish
|
|
169
|
+
"de", # German
|
|
170
|
+
"el", # Greek
|
|
171
|
+
"es", # Spanish
|
|
172
|
+
"et", # Estonian
|
|
173
|
+
"fi", # Finnish
|
|
174
|
+
"fr", # French
|
|
175
|
+
"hi", # Hindi
|
|
176
|
+
"hr", # Croatian
|
|
177
|
+
"hu", # Hungarian
|
|
178
|
+
"id", # Indonesian
|
|
179
|
+
"it", # Italian
|
|
180
|
+
"lt", # Lithuanian
|
|
181
|
+
"lv", # Latvian
|
|
182
|
+
"nl", # Dutch
|
|
183
|
+
"pl", # Polish
|
|
184
|
+
"pt", # Portuguese
|
|
185
|
+
"ro", # Romanian
|
|
186
|
+
"ru", # Russian
|
|
187
|
+
"sk", # Slovak
|
|
188
|
+
"sl", # Slovenian
|
|
189
|
+
"sv", # Swedish
|
|
190
|
+
"tr", # Turkish
|
|
191
|
+
"uk", # Ukrainian
|
|
192
|
+
"vi", # Vietnamese
|
|
193
|
+
]
|
|
194
|
+
# Special fallback code for unknown / unsupported language.
|
|
195
|
+
# Wraps text with the <na>...</na> token so the model can still synthesize.
|
|
196
|
+
UNKNOWN_LANGUAGE = "na"
|
|
197
|
+
AVAILABLE_LANGUAGES = SUPPORTED_LANGUAGES + [UNKNOWN_LANGUAGE]
|
|
198
|
+
DEFAULT_LANGUAGE = "en"
|
|
199
|
+
|
|
200
|
+
# TTS parameters - defaults
|
|
201
|
+
DEFAULT_TOTAL_STEPS = 8
|
|
202
|
+
DEFAULT_SPEED = 1.05
|
|
203
|
+
DEFAULT_MAX_CHUNK_LENGTH = 300
|
|
204
|
+
DEFAULT_MAX_CHUNK_LENGTH_KO = 120 # Korean requires shorter chunks
|
|
205
|
+
DEFAULT_SILENCE_DURATION = 0.3 # seconds
|
|
206
|
+
|
|
207
|
+
# TTS parameters - constraints
|
|
208
|
+
MIN_SPEED = 0.7
|
|
209
|
+
MAX_SPEED = 2.0
|
|
210
|
+
MIN_TOTAL_STEPS = 1
|
|
211
|
+
MAX_TOTAL_STEPS = 100
|
|
212
|
+
|
|
213
|
+
# ONNX Runtime configuration
|
|
214
|
+
# TODO: Add parsing of SUPERTONIC_ONNX_PROVIDERS environment variable
|
|
215
|
+
DEFAULT_ONNX_PROVIDERS = ["CPUExecutionProvider"] # GPU support can be added by extending this list
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _parse_env_int(env_var: str, default: Optional[int] = None) -> Optional[int]:
|
|
219
|
+
"""Parse integer from environment variable with validation.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
env_var: Environment variable name
|
|
223
|
+
default: Default value if not set or invalid
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Parsed integer or default value
|
|
227
|
+
"""
|
|
228
|
+
value = os.getenv(env_var)
|
|
229
|
+
if value is None:
|
|
230
|
+
return default
|
|
231
|
+
try:
|
|
232
|
+
return int(value)
|
|
233
|
+
except ValueError:
|
|
234
|
+
logger.warning(f"Invalid value for {env_var}: '{value}'. Using default: {default}")
|
|
235
|
+
return default
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# Thread configuration - None means let ONNX Runtime decide automatically
|
|
239
|
+
DEFAULT_INTRA_OP_NUM_THREADS = _parse_env_int("SUPERTONIC_INTRA_OP_THREADS")
|
|
240
|
+
DEFAULT_INTER_OP_NUM_THREADS = _parse_env_int("SUPERTONIC_INTER_OP_THREADS")
|
|
241
|
+
|
|
242
|
+
# Text processing
|
|
243
|
+
MAX_TEXT_LENGTH = 100_000 # Maximum characters per single synthesis call
|
|
244
|
+
|
|
245
|
+
# Logging configuration
|
|
246
|
+
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
247
|
+
LOG_LEVEL = os.getenv("SUPERTONIC_LOG_LEVEL", "INFO")
|