Spaces:
Sleeping
Sleeping
Commit History
-
Revert "Rebuild the interface around the IPA loop"
This reverts commit 76e5208aa0e2802ebd31c3afbdc3c7545048983c.c9c0ede -
Rebuild the interface around the IPA loop
The page was nested bordered boxes with three competing buttons and the player parked below the fold. Now it is one card, one primary action, and the edit-speak-hear loop is contiguous: language and voice sit in a toolbar at the top, then the IPA box, then Speak, then the player directly under it. Text-to-phonemes moves below a rule as the supporting step, and quality/speed go into an Advanced accordion so six controls are no longer shouting at once. New in the IPA box: a one-click palette of the 21 symbols people actually hand-correct (stress, length, the awkward vowels), inserted at the caret, and Cmd/Ctrl+Enter to play. Both resolve their target element whether elem_id lands on the wrapper or the node. Restyled throughout: CSS variables, one accent, calmer type scale, mono only where it means something, a real focus ring, and mobile stacking. css and theme go back on launch(): Gradio 6 moved them off Blocks, which now accepts them only to warn and drop them. Verified locally agai…76e5208 -
Make IPA the main surface and drop voice cloning
The IPA box moves to the top of the page and is what Generate speaks; the text box below it is now a way to fill that box (Text -> IPA, or just leaving the field), with speak-the-text-directly kept as a secondary button. Two edits protect what the user typed: blur on an empty text box no longer wipes hand-written phonemes, and a language switch warns in the note instead of clearing the box. Voice cloning is gone with it — the upload panel, style_from_wav, _export_voice_payload, the reference-WAV cache, clone_voice and the now-orphaned _hash_file. The engine's reference-latent support stays (Style.z_ref, style_from_z_ref, the z_ref duration predictor): saved voices can ship in that form and export_new_voice.py still writes them offline. Space renamed to BlueTTSV2 - IPA text-to-speech. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7FCaWh8KrCzm8RmdJcGbG750477b -
Find the Renikud weights by any of their names
notmax123/RenikudPlus renamed model.onnx to renikud_cons5_point8{,_int8} .onnx, so startup died on a 404 before Gradio ever came up. Try the known names in order, then anything .onnx the repo actually lists, preferring the int8 export — that is the ~310 MB graph this Space has always run, and the cheaper one on CPU hardware. BLUE_RENIKUD_FILE overrides. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7FCaWh8KrCzm8RmdJcGbGcb1b349 -
Let pip pick the torch version instead of pinning it
The 2.9.1 pin was below bluecodec's torchaudio>=2.11 floor, so the CPU build could not resolve at all. The pins were never the mechanism that kept the CUDA wheels out — the extra index is: it serves the same releases as +cpu local versions, and PEP 440 sorts 2.13.0+cpu above 2.13.0, so pip prefers them unprompted. Verified with a --dry-run resolve against manylinux_2_28/cp312: torch 2.13.0+cpu, torchaudio 2.11.0+cpu, torchcodec 0.16.0+cpu, no nvidia-* packages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7FCaWh8KrCzm8RmdJcGbG255b6f5 -
Point renikud-plus at main; its PR branch was merged and deleted
The pin on git+…@onnxruntime-optional had been failing every build since that PR merged: pip clones the repo, cannot check the ref out, and the install dies before it reaches anything else. main is 0.4.0 with the change in it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7FCaWh8KrCzm8RmdJcGbGec8ab00 -
Match suggested_hardware to the cpu-basic the Space actually runs on
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D7FCaWh8KrCzm8RmdJcGbG0fe80ed -
Add an IPA panel, open the language list to espeak, build on CPU
Three changes: * IPA / phonemes panel. "Get IPA from text" runs the full front end and shows the phonemes with the internal <lang> tags stripped; "Generate from IPA" feeds the box back with phonemize=False, so nothing sits between the typed phonemes and the encoder. The flag is plumbed through _synthesize_with_fallback and both engine wrappers, and each chunk is re-wrapped in its language tag on the way in. The note under the box lists any symbol missing from vocab.json (those encode to the pad id and are heard as nothing). * Any espeak-ng language. The dropdown is built from the espeak build actually installed rather than a hardcoded six, so ~130 voices and their regional variants are selectable. Hebrew and Russian are blocked from that path (ESPEAK_BLOCKED_BASES) and keep RenikudPlus/Phonikud and RUAccent+RUPhon. Two things had made the six a hard limit: UnicodeProcessor._preprocess raised on any other code, and Cyrillic text was routed to the Russian fron…eebd92d
-
Name the voice files after the voices
voices/libri_male_6209.json is now voices/daniel.json. The model repo keeps the LibriTTS-R reader ids — renaming there would break every client pinned to the published bundle — so the catalog carries both: `remote` is what is fetched, `file` is what it is called on disk. The old stems are retired so they are deleted rather than discovered a second time as "Libri Male 6209", and aliased so a caller that pinned one still resolves to the voice it named. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsUd0a3534 -
Bump serverless image tag to v7
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsU0a3acd3 -
Withdraw the Maya voice
Dropping the catalog entry only stops new installs from fetching it: every deployment that already ran keeps voices/female.json on disk, and discover_voices names whatever it finds, so Maya would have come straight back as "Female". Retired stems are therefore deleted at startup and skipped if the delete cannot happen (read-only layer, mounted volume). The default voice becomes Noa, and "Female" from a v2 client now errors with the current names instead of resolving to a withdrawn speaker. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsU2547fc3 -
Ignore sparse nikud, and split partial nikud between front ends
Phonikud spells a Hebrew word out of its points, so a word handed to it without them comes back as consonant soup: שלוש -> ʃˈlʃ. The Hebrew branch routed the whole segment through it whenever *any* mark was present, so one shuruk dot in השלמוּת was enough to mangle a paragraph that Renikud would have read correctly. A word now counts as vocalized only if it carries a vowel point and more than one mark overall — short words excepted, since לֹא is fully specified by its one vowel. Everything else is stripped and goes to Renikud. When both kinds are present the text is split into runs so a properly pointed word keeps its reading while its neighbours keep Renikud. Also ship Russian in the serverless image. RUAccent/RUPhon go in before the serverless set so onnxruntime-gpu still wins the wheel collision, torch comes from the CPU index (the front end runs on CPU anyway), and the ~1 GB of weights is warmed at build time rather than on a cold worker's first job. Co-Authored-By: Claude Opus …7daf341
-
Treat Russian hyphenated compounds as one word, not two
Removing the dash was not enough — Санкт-Петербург was still reaching the model as two words, so it still put a boundary where a speaker has none. A hyphenated Russian compound is a single prosodic word, so the parts are now concatenated: sankt- pʲɪtʲɪrbˈurk -> sanktpʲɪtʲɪrbˈurk The parts are still phonemized separately, because that is the only way RUPhon gets their stress right, and stitched back afterwards by token count (falling back to the previous behaviour if the counts do not line up). Which stress survives is decided by RUAccent's '+', not by position: вс+ё-таки keeps it on the first part, по-р+усски on the second. When several parts are marked (С+анкт-Петерб+ург) the last wins, which is where the primary stress sits in that shape. If RUPhon left the marked part unstressed — it does that for some monosyllables, кт+о -> kto — the mark is ignored rather than obeyed, which would otherwise strip кто-то's only stress. Word overrides now run before the rejoin, while the …4b76f53 -
Make the GPU the default on ZeroGPU
With the CUDA provider configured properly the earlier conclusion inverts. Warm in-window synthesis is 0.10s against ~0.4s on CPU, and end to end: ru 3.11s -> 0.38s median (8x) he 0.63s -> 0.48s en 0.46s -> 0.47s Russian was the complaint and Russian is where it pays. The 9.3s I measured before was ORT brute-forcing convolution algorithms with the default EXHAUSTIVE search, not the per-request session rebuild I blamed it on; the rebuild is real but only costs ~0.85s. So invert the flag: GPU by default, BLUE_ZEROGPU_ONNX=0 to opt out. The remaining cost is variance rather than throughput — when ZeroGPU recycles the worker that request rebuilds and takes ~3-4s, so a workload of rare one-off requests may still prefer CPU. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsU375e0ce -
Give ORT's CUDA provider sane options, and time inside the GPU window
I blamed per-request session rebuilds for the GPU path being slow, but my own logs contradict that: the session built in 0.9s while the request took 9.3s. Something else was eating ~8s and I never measured it. One obvious candidate was never configured: ORT defaults cudnn_conv_algo_search to EXHAUSTIVE, which benchmarks every convolution algorithm on first inference. That is seconds of warmup a short request cannot earn back, and on ZeroGPU it is paid again on each fresh worker. Set HEURISTIC (overridable via BLUE_CUDNN_CONV_ALGO), pin arena_extend_strategy to kSameAsRequested, and drop the CPU arena when the weights live on the GPU. Also log engine-acquisition and synthesis time from inside @spaces.GPU, so whatever remains can be attributed to ZeroGPU scheduling instead of guessed at. BLUE_ZEROGPU_ONNX=1 is set on the Space so this path is actually exercised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6…090cf73 -
Let each PT checkpoint come from its own repo and path
Hash-scanning every notmax123 repo against the bundle manifest found the v2.5 autoencoder already published — Lightblue-Finetune-Weights at dim_1253_blue_v1/ae_latest.pt, sha256 f562072564699dca, byte-exact. The other three (ckpt_step_748000.pt, duration_predictor_final.pt, stats_yiddish.pt) are on neither HF nor this machine; the local pt_models copies are v2, off by a few KB from the v2.5 sizes. Requiring one flat repo would mean re-uploading 700 MB that is already hosted, so each role can now name its own source: BLUE_PT_AE=notmax123/Lightblue-Finetune-Weights:dim_1253_blue_v1/ae_latest.pt Files land in pt_weights/ under their basename, so subdirectories in the source repo do not matter. Anything not overridden still comes from BLUE_PT_REPO by filename, and a missing file now names the env var that would fix it instead of just listing what it could not find. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138…d64c855 -
Accept v2.5 PT checkpoints and verify them against the bundle by hash
The route to a fast GPU on ZeroGPU is PyTorch — that is what ZeroGPU packs across its per-request fork, where an ORT CUDA session gets rebuilt every call. What blocked it was that BLUE_PT_REPO still meant the v2 safetensors, and the only guard against mismatched weights was an env-var promise. The bundle already carries the proof: manifest.json records the path, size and sha256 of every source checkpoint the ONNX was exported from. So: - manifest.json joins the bundle download. - _ensure_pt_weights accepts the manifest's own filenames (ckpt_step_748000.pt, ae_latest.pt, duration_predictor_final.pt, stats_yiddish.pt) alongside the v2 names, picking per role from the repo's actual file list. - After download, every file is sha256-checked against the manifest; a match writes pt_weights/.bundle_match, and _pt_matches_onnx_bundle now reads that evidence instead of configuration. BLUE_PT_MATCHES_ONNX_REPO survives as a manual override for re-exported tensors whose bytes legitimat…179d786
-
Fix Russian hyphens and clause dashes
Санкт-Петербург was read as two words with a pause between them. RUPhon splits on whitespace, so a hyphenated compound reaches it as a single token and it mishandles the '+' inside: Санкт-Петерб+ург came back as "sankt- pʲɪtʲɪrbˈurk". The hyphen survives as vocab token 9 — a dash, which the model renders as the pause — and the stress drifts off the right element. Feeding the parts as separate words fixes it, because that is the form RUPhon already handles: "Санкт Петербург" phonemizes clean and now the hyphenated spelling produces exactly the same IPA. RUAccent still runs first, on the original punctuation, since кто-то and по-русски are dictionary entries whose stress would be lost by splitting them earlier. ё is marked after the split, so each part is checked on its own — which also repairs всё-таки, previously "fsʲe- tˈakʲɪ" with the ё silently gone, now "fsʲˈɵ tˈakʲɪ". A dash standing between clauses is a different thing and RUPhon was reading it as a letter: "Москва — столица" p…da3fac6 -
Default ZeroGPU back to the CPU session, now that the GPU path is measurable
The GPU works now — the pill reports GPU and the logs show CUDA sessions building. It is also 18x slower, so it should not be the default. ZeroGPU hands each call a fresh forked worker, and an ORT CUDA session cannot be built in the parent and inherited the way ZeroGPU packs torch weights: the parent has no device. So the graph is rebuilt on every single request. The logs show five builds for five requests, ~0.9 s each, with the rest going to entering the window with a process this heavy. Measured on this Space, same 4.7 s of English: GPU window : 9.34 s (25.4 s on the first, including the probe) CPU session: 0.52 s Russian is the same shape: 10.6 s against 1.6 s. CPU also spends no GPU quota and cannot raise AcceleratorError, which is what started this thread. Keep the machinery — it is correct and the Docker image genuinely benefits, where one long-lived process builds the session once. Just do not route ZeroGPU through it by default. BLUE_ZEROGPU_ONNX=1 opts back in. …2597cab -
Detect ZeroGPU by truthiness, not the literal "1"
With the two-phase install in place the CUDA provider is finally present — the "no CUDA provider" warning is gone from the logs. But the backend still reports cpu, and none of _onnx_cuda_engine's messages appear at all, which means it was never called rather than called and failed. The gate is _ZERO_GPU, and it compared SPACES_ZERO_GPU to the exact string "1". Spaces has also set that variable to "true", in which case every GPU path in this file quietly turns itself off — including the probe, so it reports cpu without ever looking. Match on truthiness, and log the raw value alongside the ORT version and provider list at startup so the next question of this shape is answerable from the logs instead of another deploy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsUa0f0787 -
Split the CPU-onnxruntime dependencies into pre-requirements.txt
Pinning renikud-plus to the PR branch was necessary but not sufficient: ruphon and ruaccent each declare `onnxruntime` too. Those are the Russian front end, which means the Russian support added in this series is itself part of what has been holding the Space on CPU. Three packages pulling the CPU wheel cannot be ordered around inside one requirements file, because pip installs topologically rather than in file order. Two pip passes can: HF Spaces installs pre-requirements.txt first, so putting the CPU-build dependencies there leaves onnxruntime-gpu as the last thing to write onnxruntime/. Confirmed on linux/amd64 with the real package set: after phase 1 (ruaccent, ruphon, renikud): CUDA absent after phase 2 (onnxruntime-gpu): ['Tensorrt', 'CUDA', 'CPU'] transformers moves along with them — ruphon's tokenizer imports symbols that transformers 5 removed, so the pin has to apply in the pass that installs it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthro…f2d1b4a -
Install renikud-plus from the PR branch so ONNX gets the GPU
The Space has been running on CPU on zero-a10g hardware this whole time: not because the GPU is absent, but because the installed onnxruntime wheel is the CPU-only build with no CUDA provider compiled in, so there is nothing to bind the A10G to. The cause is renikud-plus 0.3.0's hard `onnxruntime` dependency clobbering `onnxruntime-gpu`. The fix is already written and tested in PR #3, but it needs a merge and a PyPI publish to reach us. A git reference does not: renikud-plus @ git+.../RenikudPlus.git@onnxruntime-optional Verified on linux/amd64 — that install leaves onnxruntime-gpu as the only ONNX Runtime distribution, get_available_providers() reports CUDA, and renikud imports unchanged. With a CUDA provider present, _onnx_cuda_engine() can finally build its session inside @spaces.GPU and the probe should report cuda instead of skipping the window. This helps every language; Russian is simply where it showed up worst. Revert to `renikud-plus>=0.4.0` once the PR is merged and…92bfbd1 -
Report g2p time in the stats bar
Hebrew and Russian latency has been guesswork from the outside: the bar showed only a total, so there was no way to tell G2P from synthesis without a deploy cycle per hypothesis. Locally both Russian stages measure ~3 ms warm while the Space shows seconds of language-specific cost, and that gap is not something to keep probing blind. Accumulate the wall time spent in g2p.phonemize across a synthesize() call and surface it, mirroring the g2p_s the RunPod handler already returns. Shown only on the CPU path, where synthesis ran in this process — a ZeroGPU child's counters never reach the parent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsU1fc3a2c -
Memoize RUPhon's per-word inference
With the thread cap in, Hebrew dropped from ~3.0 s to ~0.5 s on the Space but Russian stayed at ~2.5-3.1 s. Splitting the front end shows why: RUAccent is 3 ms, RUPhon is 54 ms, because RUPhon runs one ONNX call per word and pays the fixed per-call overhead on every token. That mapping is deterministic for a given '-accented word, so it caches exactly. Russian function words repeat constantly, both inside a passage and across requests: a three-sentence pass warms to 165 hits against 24 misses and 40 ms. The cache key is the accented word, not the surface form, so homographs stay distinguished — з+амок still gives zˈamək and зам+ок still gives zɐmˈok, which is the whole reason this front end exists rather than espeak. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsU5da9d5c -
Stop the Hebrew and Russian front ends oversubscribing the CPU
Hebrew and Russian were 5-9x slower than the espeak languages on the Space: ~3.0 s and ~3.8-5.5 s of compute for ~3 s of audio, against 0.25 s for 1.2 s in English. Nearly all of it was G2P, and nearly all of that was thread contention rather than work. Renikud builds its own ORT session, and we passed no options, so it sized the pool from os.cpu_count() — the host's cores, not the container's quota. Its own docstring warns about exactly this. Measured on linux/amd64 with 10 visible cores and a 2-CPU quota: default (host core count): 2506 ms intra_op_num_threads=2: 577 ms RUPhon has the same shape but exposes no way in — it constructs its session with providers and no sess_options — so supply a default while it builds. RUAccent is torch-backed and picks its threads the same way, so cap those too. The thread policy now lives in one place (ort_thread_count) instead of being duplicated in BlueTTS, and still honours ORT_NUM_THREADS, which the Docker image already sets t…098c009
-
Close the gap between synthesized chunks
The duration predictor rounds up and the vocoder fills the remainder with room tone, so each chunk arrives with silence baked onto its edges — 240 ms of tail and 24 ms of lead measured on a two-chunk clip. Concatenating stacked that onto the 150 ms join, giving a ~415 ms hole mid-paragraph. Trim each chunk's own edge silence before joining, keeping 25 ms of margin so a consonant onset or release never gets clipped. The inserted gap becomes the actual gap instead of a floor on it: measured joins drop 412 -> 197 ms and 300 -> 179 ms, while the model's own sentence pauses (784 ms, 729 ms) and everything inside a chunk are untouched. Single-chunk output just loses its trailing room tone. Also stop pausing where there is no sentence to pause between. chunk_text splits on length when a sentence outruns the limit, and a full 150 ms there sounds like a stumble mid-clause; those joins now get 30 ms. Measured 43 ms end to end on a run-on paragraph, versus 201 ms at a real sentence boundary. C…2693420
-
Correct the onnxruntime notes: ordering does not fix the collision
The previous commit claimed listing onnxruntime last would keep the GPU wheel. It does not — pip installs in topological order, and the Space build log shows onnxruntime-gpu at position 65 and the CPU wheel at 66 regardless. The Space is still on CPU. Point both notes at the real fix instead, which is upstream in renikud-plus: https://github.com/maxmelichov/RenikudPlus/pull/3 Once 0.4.0 ships, bumping the pin to renikud-plus>=0.4.0 restores CUDA on its own, because nothing pulls the CPU wheel any more. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsUf4495b5 -
Install onnxruntime last so the GPU wheel is not clobbered
The Space logs said "onnxruntime has no CUDA provider", which is why the probe kept reporting cpu on ZeroGPU hardware. renikud-plus depends on `onnxruntime` (the CPU build) and we ask for `onnxruntime-gpu`; both wheels install into the same onnxruntime/ directory, so whichever pip writes last wins. With the GPU wheel first in the file, renikud-plus's CPU wheel overwrote it and CUDAExecutionProvider disappeared. Confirmed in a clean linux/amd64 container: gpu installed last -> [Tensorrt, CUDA, CPU] cpu installed last -> [Azure, CPU] Move onnxruntime to the end of every requirements file. The Docker image happened to land the right way round already, so this is a no-op there — which is exactly the problem, so the build now asserts the provider survived rather than printing it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsU9f6a881 -
Make ORT find CUDA on ZeroGPU, and let the audio player rewind
The probe reported cpu on the live Space, so the GPU was never being used. Cause: onnxruntime-gpu dlopens libcudnn/libcublas by SONAME, and in a Space those live only under site-packages/nvidia/*/lib. The Docker image exports LD_LIBRARY_PATH from its entrypoint; a Space has no equivalent, so the provider failed to initialise and ORT quietly ran the graph on CPU. Preload those libs RTLD_GLOBAL (cudnn last, it needs cublas resident) before building the CUDA session. on_cuda now reads back from session.get_providers() rather than the requested list, because a provider that fails to initialise is not an error — it just leaves every node on CPU. Without that the probe would have cheerfully reported cuda while running on CPU. Also: the audio element kept its playhead across generations. Once a clip had finished, the stale position sat past the end of the next (usually shorter) one, so pressing play jumped straight to the end instead of replaying. Rewind on loadedmetadata/emptied/ended, and…864febc -
Only enter the ZeroGPU window when it actually yields a GPU
AcceleratorError was being surfaced as a hard synthesis failure for work that was not using the GPU anyway. Two things changed under this Space: the PyTorch path is now gated on a checkpoint match, and ONNX Runtime's CUDA provider needs cuDNN it cannot reliably load on Spaces. When both miss, _tts_engine() hands back the same CPU session the parent already has — so every request was spending ZeroGPU quota and inheriting its failure modes to run CPU code. Probe once, inside @spaces.GPU, and report the device back across the fork (a child's globals never reach the parent). If the answer is cpu, synthesize directly and skip the window entirely. When there is a real GPU the behaviour is unchanged — GPU first, retry once on a node fault — except that two faults in a row now serve the request on CPU instead of erroring. That reverses 3fb43cf, which was written when the GPU path meant PyTorch CUDA and a drop to CPU meant a large slowdown; it is now the same ONNX engine either way. The stats…869b554 -
Prewarm the Russian front end instead of stalling the first request
The previous change announced the RUAccent/RUPhon load on every Russian generation, not just the cold one: warm_g2p is a no-op once loaded, but the notice was printed before checking. So it claimed "first time only" every time and parked at 10%, which is just the progress fraction. Check the real state (g2p_is_cold) before saying anything, and fetch the ~1 GB in a background thread at startup so the first Russian request normally finds it warm. Off-thread so the Space does not sit in APP_STARTING; BLUE_PREWARM_RU=0 opts out. A lock keeps the prewarm thread and a concurrent request from both building it — the GPU child only reads the loaded globals, so forking while it is held cannot deadlock the child. When it genuinely is cold, the banner now says what is happening and how big it is rather than claiming to be a one-off that recurs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsUdb5b79b -
Load the Russian front end outside the ZeroGPU window
synthesize() phonemizes per chunk, so G2P runs inside @spaces.GPU. That is harmless for the front ends already loaded at import, but RUAccent + RUPhon fetch ~1 GB on first use — inside a ~20 s allocation. The first request after switching to Russian could exhaust its budget, and every request after charged CPU phonemization to the GPU. Warm it explicitly before the GPU call instead, with a progress note since the first one takes a few seconds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138DB6F6YxWtYWy7WNaLAsU1011d7e -
Move to BlueTTS 2.5: new ONNX bundle, named voices, Russian, RenikudPlus
The 2.5 export is not drop-in compatible with v2, so the pipeline changed with it: - The vocoder now exports bare ([1,24,6T] in), so denormalizing with stats.npz and folding the latents moved out of the graph and into the caller. - Duration prediction split in two. Saved voices go through duration_predictor_style.onnx; the z_ref-conditioned duration_predictor.onnx serves cloned voices. - vector_estimator lost its cfg_scale input, so guidance runs off uncond.npz — which the old download list never fetched, meaning cfg_scale had been silently inert. - The bundle marker carries a stamp, so a stale v2 marker forces a re-pull instead of mixing generations. Voices are named rather than numbered: Maya, Noa, Lily, Daniel, Adam, with gender and pitch shown in the dropdown. The voice's gender is passed to RenikudPlus as speaker/target_speaker so Hebrew forms like היא רצה resolve correctly. Russian goes through RUAccent then RUPhon, vendored at g2p/russian_g2p.py — the front end …faea714
-
Keep synthesis on ZeroGPU only — retry instead of CPU fallback
User wants GPU path; on ECC/node faults retry once rather than silently dropping to ONNX CPU. Co-authored-by: Cursor <cursoragent@cursor.com>3fb43cf -
Fall back to ONNX CPU when ZeroGPU hits CUDA ECC faults
Space logs showed uncorrectable ECC on the allocated GPU during worker init; catch AcceleratorError-class infra failures and keep synth usable. Co-authored-by: Cursor <cursoragent@cursor.com>591b5d1 -
Fix stale audio by disabling Gradio streaming replace mode
Streaming Audio kept the previous clip across Generate; yield full concatenated waveforms with streaming off so each run replaces cleanly. Co-authored-by: Cursor <cursoragent@cursor.com>3a79ff4 -
Reset Gradio streaming audio at start of each generation
Co-authored-by: Cursor <cursoragent@cursor.com>738485b -
Fix ZeroGPU synth: do not re-import app inside PT worker
Co-authored-by: Cursor <cursoragent@cursor.com>f6287f3 -
Use PyTorch Hub weights for real ZeroGPU CUDA synthesis
ONNX CUDA EP is unavailable on ZeroGPU; load notmax123/blue-v2 safetensors and run TE/VF/DP/decoder on CUDA inside @spaces.GPU. Co-authored-by: Cursor <cursoragent@cursor.com>f9de6b5 -
Run BlueTTS on Hugging Face ZeroGPU
Decorate synth/clone with @spaces.GPU, lazy CUDA ORT in workers, and prefer onnxruntime-gpu on Linux Spaces. Co-authored-by: Cursor <cursoragent@cursor.com>ff0646b
-
Stream synthesis chunks to Gradio Audio as they finish
Co-authored-by: Cursor <cursoragent@cursor.com>5473bb7
-
Improve Hebrew text preprocessing for the Space
Preserve vocalized Hebrew through Phonikud and make mixed text, codes, numbers, and punctuation synthesize more naturally in the deployed app. Co-authored-by: Cursor <cursoragent@cursor.com>fe7706f
-
DP ref encoder uses compressed latent channels from config; README v2 onnx path; pytest smoke tests
Made-with: Cursor8876cf1
-
Hebrew dates: spell month with ordinal names (לראשון … לשנים עשר)
Made-with: Cursor920d18e -
Hebrew: format expanded dates as 'D ל-M Y'
Made-with: Cursor4e7bf3c -
Expand DD/MM/YYYY-style dates before generic number expansion
Made-with: Cursor9eebcc5
-
Hebrew: expand 'ל' before Latin tokens (e.g. CPU-GPU) to 'אל'
Made-with: Cursor0ce399f -
Lower BLUE_SYNTH_MAX_CHUNK_LEN to 200
Made-with: Cursor9010a30 -
Add phonemes_for_display and strip language tags for user-facing text
Made-with: Cursorff5e826 -
Strip Hebrew gershayim/quote marks in abbreviations before synthesis
Made-with: Cursor07b30a1