| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
fix(setup): send the HF token when installing a gated model Installing a gated model failed the fast download path with "401 Unauthorized" even with a valid token and the licence accepted, then fell back to snapshot_download and logged a 401 that reads like the token or the licence grant is at fault when it is neither (#2163). `token_resolver.resolve()` returns a ResolvedToken record, not the bearer string. Two call sites handed that record straight to consumers typed `token: str | None`, and both fail silently rather than loudly: - `_segmented_snapshot` passes it to HfApi, get_hf_file_metadata and our own segmented_download. huggingface_hub's build_hf_headers ignores a non-str token and falls back to its own ambient discovery, so a token held only in VoiceStudio's Settings produces NO Authorization header and every gated file 401s. segmented_download instead interpolates it into `f"Bearer {token}"`, sending a malformed header that also inlines the raw secret into the request. - `_step_fetch_weights` passes it to snapshot_download, so gated engine weights 401 the same way. Every other resolve() caller already unwraps `.token`; these two were the outliers. Both now unwrap once, at the seam. The existing weights tests all stubbed resolve() to return None, so no test ever exercised a resolved token — which is why this went unnoticed. The new tests drive a real ResolvedToken through both seams and assert a `str` reaches every consumer, plus an integration test that installs the pyannote diarisation pipeline end to end: a weightless config_only repo validates, both dependency repos are fetched, and every call carries the bearer string. Two catalogue invariants keep the rest of #2163 from returning by edit: dependency repos must be revision-pinned (revision_for raises otherwise, so an unpinned one ships an always-failing install), and a config_only entry must declare config_required_files (without them the completeness check can never pass and the error lists no files at all — the shape the report hit on 0.5.2). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> | 8 天前 | |
refactor: split backend into api/core/services/schemas, harden security + fd pressure, add searchable language picker, fix segment fragmentation Backend: - Split monolithic main.py into backend/{api/routers,core,schemas,services} - core/db.py: allowlist-gated migrations, db_conn context manager (kills SQL injection on ALTER) - core/tasks.py: lock-guarded listener add/remove/push, snapshot-before-iterate - services/ffmpeg_utils.py: run_ffmpeg helper with concurrency semaphore, EAGAIN retry, guaranteed reap - services/segmentation.py: Bengali/CJK/Arabic punctuation, ultra-short tier, stitch_adjacent_shorts, bounded-loop merge; public clean_up_segments API - services/model_manager.py: robust lock.locked() handling - api/routers/dub_core.py: job_id traversal guard, thread-safe _active_procs, timeouts on ffmpeg/demucs, POST /dub/cleanup-segments endpoint - api/routers/dub_export.py: guarded SSE listener remove, ffmpeg timeouts via run_ffmpeg - api/routers/exports.py: destination_path validation, safe source resolver, subprocess list-form - api/routers/generation.py: contextlib.suppress on tempfile cleanup, db_conn usage, safe output-path helper - api/routers/system.py: try/finally tmp cleanup, subprocess timeouts - schemas/requests.py: TranslateSegment.id int->str to match hex segment IDs - main.py: threading.Lock around crash log writes Frontend: - components/SearchableSelect.jsx: popover combobox with search, keyboard nav, popular+recent pins, 200-item cap - App.jsx: wire SearchableSelect for dub language / ISO code / voice-gen language; Clean Up segments button; fix blob URL leak (object-shaped prev in setter, unmount cleanup via ref) - components/WaveformTimeline.jsx: explicit <video> detach instead of innerHTML='' to release decoder - index.css: ss-* combobox styles matching Gruvbox theme Tests: - tests/test_segmentation.py (26 cases), test_dub_transcribe.py, test_dub_export_unique.py, conftest.py Chore: - .gitignore: exclude omnivoice.zip, /research/ reference clones - Remove tracked stray root test scripts + crash_log.txt Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> | 5 个月前 | |
fix(catalogue): repair the guidance the Weights-list rename broke The rename to "the engine's Weights list in Model Catalogue" left several messages without a verb, and pointed others at the wrong place: - The offline and create-voice messages say what to do again. - pyannote has no owning engine, so diarization points at Other weights. - The Hugging Face mirror moved to Settings → Network, and voice previews moved to Settings → Storage. - Unloading and switching engines happen in the engine list, not a Weights list. - A bad saved path points at Settings → Storage or the env file. - Docstrings that read "the the" are fixed. The dub stream-drop fallback goes through i18n in all 21 locales. A Dictation pick on a row that is already downloading no longer starts a second install: the row's radio is disabled while it works, and useModelDownloads refuses a second mutation for a repo already in flight. The Supertonic-3 license test checks for the Accept wording. | 15 天前 | |
fix(longform): keep the chapter cache across data-dir changes and power loss (#2284) * fix(longform): keep the chapter cache across data-dir changes and power loss Chapter and segment cache keys embedded the reference audio's absolute path, so any change in how the data dir is reached re-keyed every cached chapter. Key by the voices-dir-relative path instead; caches written under the absolute path are still found and moved to the new key. Flush cached chapter/segment WAVs and the resume manifest before their rename (F_FULLFSYNC on macOS) so a power-off cannot leave an empty manifest or a torn chapter, reject torn chapter WAVs on lookup, and log which input changed when a chapter misses. Refs #2279 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(longform): harden #2279 cache portability, torn-WAV checks and tests - Tests resolve app modules at call time: other suites pop and re-import core.config, so the module-level import patched a stale object that the code under test no longer read (CI-only failures). - Record the voices roots a cache was rendered under and probe legacy (absolute-path) keys under each, so upgrading then relocating the data dir still finds chapter, segment and remote entries. - wav_is_complete walks the RIFF chunks so a tail torn by a power-off is a miss for chapters, segments and remote entries (was: size vs payload only, missing header bytes). - Remote chapter publication is durable; a failed legacy move uses the legacy file in place instead of re-rendering remotely. - Changelog credit, CodeQL empty-except, lexicon-leak and durable-order test coverage. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(longform): record the voices root at startup and on data-dir moves - Backend Phase B notes the current voices root in an existing longform cache, so upgrading and then moving the data dir before any render still finds chapters keyed by the old absolute path (#2279). - Electron's Settings -> Storage move writes the old voices root into the moved cache's voices_roots.json (fsync'd, best-effort, never fails the move). - voices_roots.json itself is published durably (flush, rename, dir flush); the swallowed write error is now an explained return (CodeQL). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(changelog): merge duplicate Fixed section; credit #2279 highlight Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(longform): flush the cache directory after adopting a legacy entry or publishing voices_roots.json Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 3 天前 | |
fix(security): replace persistent admin keys with scoped sessions (#1528) * fix(security): replace persistent admin keys with sessions Exchange the remote administrator key once for bounded, revocable credentials. Canonicalize backend principals, enforce cookie CSRF and exact origins, and use path-bound one-use WebSocket tickets. Migrate the bundled UI away from durable master-key storage and credential-bearing URLs. Add unit, integration, static-hygiene, and production-browser regressions plus synchronized operator documentation. * docs: link session hardening to PR 1528 * fix(security): key session indexes with process pepper Use HMAC-SHA-256 instead of an unkeyed digest for in-memory session and WebSocket-ticket indexes. This preserves constant-size lookup identifiers, makes copied records unusable without the process pepper, and resolves CodeQL's weak sensitive-data hash finding. * fix(auth): align empty bearer migration precedence Centralize the Authorization-channel presence decision with canonical principal parsing. Bearer followed only by spaces now remains an empty channel during legacy cookie migration, while unsupported or invalid explicit credentials stay authoritative and fail closed. * fix(security): harden admin session review boundaries * fix(security): derive key generations with HKDF * fix(auth): anchor the admin-session store so module reloads cannot fork it test_master_exchange_does_not_bypass_pin_on_normal_routes failed in full-suite runs: test_mcp_bindings' client fixture purges the services.* tree from sys.modules and reloads main, so api.routers.auth re-imported a fresh services.admin_sessions (new AdminSessionStore) while core.auth kept its import-time reference to the old one — the exchange issued the cookie into one store and the middleware resolved it against another, turning the expected "PIN required" into "API key required". Root cause is the class of bug, not the one test: a process-global auth store defined as a bare module-level singleton forks under importlib.reload or purge-and-reimport. Fix at the source: admin_session_store now resolves through a synthetic sys.modules anchor (_omnivoice_admin_session_store_anchor) that reloads never re-execute and package-prefix purges never match, so every copy of the module shares the one per-process store. No consumer or behavior changes. Regression test reproduces both fork vectors (in-place reload and sys.modules purge + fresh import) and asserts previously issued sessions still resolve and the store identity is preserved; it fails before this fix and passes after. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(auth): honor X-Forwarded-Proto for CSRF origin and Secure cookies behind TLS proxies Behind Tailscale Serve (docs/remote-gpu.md) or any TLS-terminating proxy, the browser talks https while the backend hop stays http, so exact-origin CSRF compared an https Origin against an http expectation and rejected every legitimate request, and the session cookie shipped without Secure. uvicorn's ProxyHeadersMiddleware only rewrites the scope for loopback peers, which misses Docker and any non-loopback proxy topology. New core.csrf.effective_scheme derives the client-facing scheme: resolved scope first (uvicorn's trusted-proxy rewrite wins), then an upgrade-only read of X-Forwarded-Proto's first value — https/wss promotes http to https, everything else is ignored, and a genuine TLS hop can never be downgraded. Used by both the destination-origin comparison and auth._secure_cookie so the WS-ticket/logout CSRF paths and the cookie Secure flag agree. Spoofing gains nothing: the host:port half of the origin tuple is untouched, browsers cannot attach the header cross-site without a preflight this API never grants, and forging it on plain http only adds Secure (the browser then drops the cookie — self-harm only). Regression tests: proxied https origin accepted (origin check, Secure flag, logout), comma-separated chains, scope-fallback path, spoofed header still rejects cross-origin, cannot downgrade real https, junk values ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(auth): consume the stored admin key only after a successful exchange A remote-backend user upgrading with their backend unreachable lost the only stored copy of OMNIVOICE_API_KEY: every migration path deleted the durable ov_api_key BEFORE the session exchange settled, stranding them until they recovered the key from the server box. Close the whole class: - client.ts bootstrap: read the legacy key, exchange first, and remove the durable copy only after the exchange succeeds; on failure the key stays so the next launch retries the migration (auth gate still rises). - authSession.ts exchangeApiKey: move removeLegacyMaster from before the fetch to the cookie/bearer success paths — the key never coexists with a live session, but a rejected or hung exchange no longer consumes it. - remoteBackendProbe.ts configuredRemoteBackend: stop wiping the key on every app mount. - RemoteBackendPanel: a connection test or an aborted save no longer wipes the pending key; only disabling the remote backend discards it. - prefKeys.js: ov_api_key moves from PREF_KEYS to PRESERVED_KEYS — factory reset preserves the pending connection credential exactly like ov_backend_url; the successful migration is what deletes it. Tighten the credential-hygiene static guard to match: it accepted sessionStorage.setItem('ov_api_key', …) — the exact class it exists to close. The guard now flags .setItem(<master key>) on any storage receiver, quote style, or injected-store alias, with a self-test pinning what it catches and what stays legal. Fail-before/pass-after regression tests: backend unreachable retains the key and the next bootstrap retries it; a successful exchange removes it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * perf(auth): make session validation occupancy-independent * test(auth): catch optional master-key storage calls * feat(docs): add PR control document for bultodepapas in VoiceStudio * docs: keep the PR tracking board in the fork; credit the changelog line The pr-control document is excellent process discipline, but it is the contributor's own operational board (their inventory, their update commands) — it lives naturally in their fork, and docs/agents/ here is context every repo agent loads. Removed with appreciation; the changelog line gains its contributor credit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: debpalash <4178343+debpalash@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 1 个月前 | |
fix(media): say plainly when a file has no audio track (#2308) * fix(media): name a video with no audio track instead of dumping ffmpeg's exit 234 Loading a video-only MP4 in Dub failed at `extract` with ffmpeg's raw stream dump ("FFmpeg exited with code 234 ... Output file does not contain any stream ... Invalid argument"). Every audio-extract site now probes for an audio stream first (ffprobe, then the ffmpeg stream list) and recognizes ffmpeg's no-stream wording as a fallback, raising NoAudioTrackError with a VoiceStudio sentence and the NO_AUDIO_TRACK failure class: dub ingest, batch dub, the ASR decoder, /transcribe, /v1/audio/transcriptions, clone references and gallery imports. HTTP surfaces return a structured 422 (OpenAI routes: 400 no_audio_track); Electron shows the localized message in all 21 locales and keeps the diagnostic behind Copy diagnostic. ffmpeg's stderr stays in the log. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test+docs: reload-safe no-audio assertions; changelog entry (#2308) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(openai): keep an engine-raised no-audio error as 400 when the probe cannot run Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 2 天前 | |
feat(calls): phone call agent — outbound Twilio calls and inbound agent mode (#2306) * feat(calls): phone call agent backend — outbound/inbound calls in your voice Adds a task-driven call agent on top of the Twilio integration: outbound calls via Twilio REST with signed status callbacks, a provider-agnostic conversation loop (energy VAD -> capture ASR -> streamed LLM turn -> streaming TTS, barge-in via clear), persisted call sessions (alembic 0012), the /calls local API with SSE events, take-over/say/hangup, and inbound agent mode. Owner safeguards: editable AI disclosure on by default, outbound voices gated to verified-own or designed profiles (403), one explicit request per call with max 1-2 concurrent, recording only when enabled and disclosed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(changelog): reference #2306 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(changelog): add Unreleased highlights Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(calls): address review — affirmative recording notice, one agent-call limit, wider number guard - Recording needs an affirmative notice; negated disclosures never record (greptile) - max_concurrent now limits agent calls in both directions (greptile) - Sensitive-number guard also catches comma and Unicode-dash separators (greptile) - Constant upsert SQL, https-only urlopen guard, no BaseException catch (code scanning) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(calls): always finalize a connected call; allow brief numbers in any phone style - Finalize agent calls in a shielded finally so a failed/cancelled stream frees its slot (coderabbit) - Guard allows numbers from the brief written with parentheses or a country code (coderabbit) - Docs: the no-LLM fallback needs a configured greeting (coderabbit) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(calls): wait on record changes instead of sleeping; assert the injected stream failure Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 2 天前 | |
fix(media): say plainly when a file has no audio track (#2308) * fix(media): name a video with no audio track instead of dumping ffmpeg's exit 234 Loading a video-only MP4 in Dub failed at `extract` with ffmpeg's raw stream dump ("FFmpeg exited with code 234 ... Output file does not contain any stream ... Invalid argument"). Every audio-extract site now probes for an audio stream first (ffprobe, then the ffmpeg stream list) and recognizes ffmpeg's no-stream wording as a fallback, raising NoAudioTrackError with a VoiceStudio sentence and the NO_AUDIO_TRACK failure class: dub ingest, batch dub, the ASR decoder, /transcribe, /v1/audio/transcriptions, clone references and gallery imports. HTTP surfaces return a structured 422 (OpenAI routes: 400 no_audio_track); Electron shows the localized message in all 21 locales and keeps the diagnostic behind Copy diagnostic. ffmpeg's stderr stays in the log. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test+docs: reload-safe no-audio assertions; changelog entry (#2308) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(openai): keep an engine-raised no-audio error as 400 when the probe cannot run Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 2 天前 | |
Merge pull request #1963 from debpalash/land/queue3 Land the reviewed PR queue, part three: zh-CN locale and null capture timings | 16 天前 | |
feat(gallery): save gallery voices as profiles, with validated audio references (#1542) * feat(gallery): save gallery voices as profiles, with validated audio references Work-in-progress lifted from the concurrent gallery session at the owner's request (its uncommitted working tree, preserved verbatim from base 92b1ee5d; safety snapshot remains at rescue/gallery-wip): - gallery voices can be saved as local profiles: audio is copied into the profile store with content-addressed filenames, existing profiles are detected and refreshed only when the source clip changed - backend/core/audio_validation.py: symlink-rejecting, root-contained resolution for persisted profile WAV references, with tests - archetype/community routers and the Voice Gallery UI updated for the save-as-profile handoff (spec: docs/specs/longform/26-gallery-use-handoff.md) - locale updates for the new gallery strings across all 21 files Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: drop a stray local screenshot script that rode in with the tree copy * fix(community): explain the tolerated Content-Length parse failure; drop an unused import CodeQL on #1542: the empty except now says why it is safe (the streamed byte counter enforces the same cap regardless), and the test file loses an unused Path import. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gallery): review findings — copy outside the write lock, no stale completions CodeRabbit on #1542, all findings addressed: - the profile-audio copy stages to a .part temp BEFORE BEGIN IMMEDIATE and publishes via atomic os.replace inside it — other backend writers no longer block for the duration of an audio copy; a mid-copy failure leaves no temp droppings and no profile row (both pinned by tests) - VoiceGallery async ops carry per-operation generation tokens: a preview or save-as-profile that resolves after unmount (or after a newer operation) can no longer play audio, redirect into a workspace, or touch state — three fail-before regression tests - VoiceGalleryActions imports the page at test runtime; the e2e locator uses a stable data-testid instead of a translated string; symlink tests skip cleanly where the OS can't create symlinks; the changelog line carries its PR ref Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: static ffmpeg fallback when the chocolatey feed is down Third feed outage to break a PR run (2026-07-20, 2026-07-28, today — three attempts, three 'installed 0/1'). Chocolatey is a distribution channel, not the dependency: after the retry loop exhausts, fetch the static gyan.dev build from its GitHub release mirror and put it on PATH — same binary, no feed in the path. URL verified live (HTTP 200). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 1 个月前 | |
feat(design): free-text 'describe your voice' field maps to design parameters (#317) (#331) Parity with the hosted omnivoice.app describe field, implemented fully locally: a deterministic, ordered synonym-table mapper (no model, no network, stdlib only) projects a natural-language description onto the existing six-category design space (Gender/Age/Pitch/Style/EnglishAccent/ ChineseDialect). Every emitted token is validated at import time against the engine taxonomy, so the mapper can never produce an instruct item the engine validator would reject; Chinese token forms are derived from the taxonomy, never hardcoded (the one functional pinyin->dialect mapping is allowlisted in test_no_hardcoded_cjk.py with justification). UI: a describe textarea in the Design tab fills the attribute picker live (hand-tuning still possible afterwards); parts of the description the taxonomy can't express are listed back to the user as 'ignored' instead of failing silently. New i18n keys in all 21 locales. Fixes #317 Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 3 个月前 | |
feat(electron): add full VoiceStudio desktop app | 11 天前 | |
fix(memory): release device cache across supported accelerators (#2317) * fix(memory): release the device cache on every accelerator the engines can use The dubbing and generation recovery paths open-coded the CUDA/MPS pair when flushing the device cache. Engines pick their device through torch.accelerator, so on an Ascend NPU or Intel XPU host those paths flushed nothing at all: the allocator kept the blocks the offload had just freed, and the next allocation failed with that memory still counted as in use. Extract the narrow primitive free_vram() already used (no gc.collect, no cuBLAS clear, covers CUDA/MPS/XPU/NPU, never raises) as release_device_cache() and call it from the five recovery paths. free_vram() keeps its gc + cuBLAS behaviour. Verified on an Ascend 910B4 (torch 2.15.0.dev + torch_npu 2.15.0.dev): reserved npu memory stayed at 134.0 MiB with the old pair and dropped to 0.0 MiB with the shared helper; the new tests fail 9/9 on the pre-change tree. * docs(changelog): credit the cache-flush fix with its PR ref (#2317) * fix(memory): follow the engines' accelerator and keep free_vram raising Two review findings on the cache-release helper: 1. On a hybrid host (CUDA probes as available, inference runs elsewhere) the elif chain flushed CUDA and never reached the active allocator. Ask the same question the engine sidecars ask -- torch.accelerator.current_accelerator( check_available=True) -- and flush that backend, falling back to the CUDA/MPS/XPU/NPU probe chain only when the build cannot answer. 2. free_vram() propagated empty_cache() failures before, and model_lifecycle unload callers report a failed flush to the user, so the helper takes raise_on_failure and free_vram() passes True. The direct recovery calls stay best-effort. Tests pin both, plus the cpu-answer and pre-2.6 fallbacks. * fix(memory): flush selected allocator and offload SSE cleanup * test(memory): remove source-text assertion * test(memory): trim obsolete test tail * fix(dub): flush MPS allocator after NLLB CPU fallback --------- Co-authored-by: Palash Debnath <4178343+debpalash@users.noreply.github.com> | 1 天前 | |
fix(export): open Windows caption paths in ffmpeg hardsub filters (#2312) * fix(export): open Windows caption paths in ffmpeg hardsub filters ffmpeg subtitles and ass filters do not open a quoted C:\ path after backslashes are doubled. Convert to forward slashes, then escape colon and quote, so burned-in line and karaoke captions render. * docs(changelog): note the Windows hardsub caption path fix (#2312) * fix(export): preserve caption paths across ffmpeg parser layers (#2312) --------- Co-authored-by: Palash Debnath <4178343+debpalash@users.noreply.github.com> | 1 天前 | |
fix(memory): release device cache across supported accelerators (#2317) * fix(memory): release the device cache on every accelerator the engines can use The dubbing and generation recovery paths open-coded the CUDA/MPS pair when flushing the device cache. Engines pick their device through torch.accelerator, so on an Ascend NPU or Intel XPU host those paths flushed nothing at all: the allocator kept the blocks the offload had just freed, and the next allocation failed with that memory still counted as in use. Extract the narrow primitive free_vram() already used (no gc.collect, no cuBLAS clear, covers CUDA/MPS/XPU/NPU, never raises) as release_device_cache() and call it from the five recovery paths. free_vram() keeps its gc + cuBLAS behaviour. Verified on an Ascend 910B4 (torch 2.15.0.dev + torch_npu 2.15.0.dev): reserved npu memory stayed at 134.0 MiB with the old pair and dropped to 0.0 MiB with the shared helper; the new tests fail 9/9 on the pre-change tree. * docs(changelog): credit the cache-flush fix with its PR ref (#2317) * fix(memory): follow the engines' accelerator and keep free_vram raising Two review findings on the cache-release helper: 1. On a hybrid host (CUDA probes as available, inference runs elsewhere) the elif chain flushed CUDA and never reached the active allocator. Ask the same question the engine sidecars ask -- torch.accelerator.current_accelerator( check_available=True) -- and flush that backend, falling back to the CUDA/MPS/XPU/NPU probe chain only when the build cannot answer. 2. free_vram() propagated empty_cache() failures before, and model_lifecycle unload callers report a failed flush to the user, so the helper takes raise_on_failure and free_vram() passes True. The direct recovery calls stay best-effort. Tests pin both, plus the cpu-answer and pre-2.6 fallbacks. * fix(memory): flush selected allocator and offload SSE cleanup * test(memory): remove source-text assertion * test(memory): trim obsolete test tail * fix(dub): flush MPS allocator after NLLB CPU fallback --------- Co-authored-by: Palash Debnath <4178343+debpalash@users.noreply.github.com> | 1 天前 | |
fix(memory): release device cache across supported accelerators (#2317) * fix(memory): release the device cache on every accelerator the engines can use The dubbing and generation recovery paths open-coded the CUDA/MPS pair when flushing the device cache. Engines pick their device through torch.accelerator, so on an Ascend NPU or Intel XPU host those paths flushed nothing at all: the allocator kept the blocks the offload had just freed, and the next allocation failed with that memory still counted as in use. Extract the narrow primitive free_vram() already used (no gc.collect, no cuBLAS clear, covers CUDA/MPS/XPU/NPU, never raises) as release_device_cache() and call it from the five recovery paths. free_vram() keeps its gc + cuBLAS behaviour. Verified on an Ascend 910B4 (torch 2.15.0.dev + torch_npu 2.15.0.dev): reserved npu memory stayed at 134.0 MiB with the old pair and dropped to 0.0 MiB with the shared helper; the new tests fail 9/9 on the pre-change tree. * docs(changelog): credit the cache-flush fix with its PR ref (#2317) * fix(memory): follow the engines' accelerator and keep free_vram raising Two review findings on the cache-release helper: 1. On a hybrid host (CUDA probes as available, inference runs elsewhere) the elif chain flushed CUDA and never reached the active allocator. Ask the same question the engine sidecars ask -- torch.accelerator.current_accelerator( check_available=True) -- and flush that backend, falling back to the CUDA/MPS/XPU/NPU probe chain only when the build cannot answer. 2. free_vram() propagated empty_cache() failures before, and model_lifecycle unload callers report a failed flush to the user, so the helper takes raise_on_failure and free_vram() passes True. The direct recovery calls stay best-effort. Tests pin both, plus the cpu-answer and pre-2.6 fallbacks. * fix(memory): flush selected allocator and offload SSE cleanup * test(memory): remove source-text assertion * test(memory): trim obsolete test tail * fix(dub): flush MPS allocator after NLLB CPU fallback --------- Co-authored-by: Palash Debnath <4178343+debpalash@users.noreply.github.com> | 1 天前 | |
fix(electron): disable unsupported output language choices | 5 天前 | |
feat: real-time WebSocket event bus + sidebar reactivity fixes (#27) ## Core Infrastructure - Add backend event bus (core/event_bus.py) — in-memory pub/sub with emit(), subscribe(), unsubscribe() - Add WebSocket endpoint /ws/events (api/routers/events.py) with 25s keepalive pings and auto-cleanup on disconnect - Add frontend hook useRealtimeEvents.js — single WS connection with exponential backoff reconnect (2s→60s) ## Backend Event Integration - projects.py: emit on create/update/delete - profiles.py: emit on create/update/lock/unlock/delete - dub_core.py: emit on clear/delete history - dub_pipeline.py: emit on save_job (every pipeline write) - exports.py: emit on export/record - generation.py: emit on generate/clear/delete - gallery.py: emit on save-as-profile/to-profile ## Frontend Improvements - Replace 45s polling interval with instant WS-based invalidation - Fix critical bug: apiModelStatus was undefined, causing loadAll() to loop forever — sidebar data never loaded on startup - Add websockets to main deps (was optional, got removed by uv sync) - Reduce model/status polling from 5s to 10s, disable background polling for logs - Add ReadinessChecklist and FloatingPill components - Default UI scale changed from S (1.0) to M (1.3) ## Dependencies - Add websockets>=16.0 to main dependencies for uvicorn WS support Closes #3 (native desktop app exists via Tauri) Closes #5 (Dockerfile already uses root bun.lock) Resolves #26 (Triton workaround documented) | 4 个月前 | |
fix(export): run the app's own ffmpeg for the video watermark Two call sites still reached for the bare name `ffmpeg` instead of services.ffmpeg_utils.find_ffmpeg(), which every other call site uses. The bare name only resolves a system install: imageio-ffmpeg -- the app's default source, and a locked dependency -- ships its binary as `ffmpeg-<platform>-v<version>`, and ensure_media_tools_on_path() publishes that directory on PATH without giving the file an `ffmpeg` name. So on a host with no separate system ffmpeg, which is most installs: - /export dropped the visible video watermark. is_visible_video_enabled() defaults to ON, the spawn raised FileNotFoundError, and the except arm quietly plain-copied the file -- the user asked for a watermarked export and got an unmarked one with no error anywhere. - video_context._extract_keyframes gated on shutil.which("ffmpeg") and logged "ffmpeg not found, skipping frame extraction", so the dubbing director's visual context was empty while the app's own ffmpeg sat on disk, resolvable. Same shape as #1256. The export also no longer spawns anything when nothing resolves: it goes straight to the plain copy instead of failing a subprocess to get there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> | 8 天前 | |
fix(media): say plainly when a file has no audio track (#2308) * fix(media): name a video with no audio track instead of dumping ffmpeg's exit 234 Loading a video-only MP4 in Dub failed at `extract` with ffmpeg's raw stream dump ("FFmpeg exited with code 234 ... Output file does not contain any stream ... Invalid argument"). Every audio-extract site now probes for an audio stream first (ffprobe, then the ffmpeg stream list) and recognizes ffmpeg's no-stream wording as a fallback, raising NoAudioTrackError with a VoiceStudio sentence and the NO_AUDIO_TRACK failure class: dub ingest, batch dub, the ASR decoder, /transcribe, /v1/audio/transcriptions, clone references and gallery imports. HTTP surfaces return a structured 422 (OpenAI routes: 400 no_audio_track); Electron shows the localized message in all 21 locales and keeps the diagnostic behind Copy diagnostic. ffmpeg's stderr stays in the log. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test+docs: reload-safe no-audio assertions; changelog entry (#2308) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(openai): keep an engine-raised no-audio error as 400 when the probe cannot run Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 2 天前 | |
fix: expose Ogg/Opus format for MCP speech files (#2324) Expose actual Ogg/Opus encoding for MCP speech files and URLs, with bounded per-render caching and remote-backend verification. Keep WAV defaults and protect audio paths. Fixes #2321. | 1 天前 | |
feat(settings): LLM Skills — per-feature enable/route control for every LLM call (#912) New Settings → System → LLM Skills area: every LLM-powered capability (Cinematic & Autofit translation, speech-rate slot fitting, glossary auto-extract, direction parsing, dictation cleanup) becomes a "skill" the user can toggle or route to a specific provider (local Ollama/LM Studio vs a remote key) instead of everything riding the one global active provider. Backend: - services/llm_skills.py — skill registry + settings_store persistence (llm_skill.<id>.enabled / .provider), resolution precedence override > active > none, resolve_skill_client() (OpenAI-compat client bound to the effective provider; None when disabled/unconfigured) and skill_backend() (OffBackend when disabled — the exact no-LLM object every caller already degrades on). - All five consumption points wired through the registry; a disabled skill degrades exactly like "no LLM configured" today (Fast translation fallback, refinement pass-through, heuristic direction parse, no-llm slot fit, 503 on glossary auto-extract). No new degradation modes; defaults (enabled + no override) keep existing setups byte-identical. - OpenAICompatBackend gains an optional bound provider (None = active, the historical behavior). - GET /api/settings/llm-skills + PUT /api/settings/llm-skills/{skill_id} (404 unknown skill/provider); route snapshot updated. Frontend: - LLMSkillsPanel (Sparkles, next to LLM Providers): one row per skill — i18n name/description, enable toggle, provider Select ("Use active provider" + configured providers, local ones tagged), ready / needs-setup badge linking to LLM Providers. All strings via t() (settings.llmskills_*). Tests: 30 backend (precedence, per-consumption-point disabled semantics, endpoint round-trips, validation) + 4 panel render/PUT tests. Docs: translation-engines.md gains an LLM Skills section. Co-authored-by: mergetest <test@local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
fix(history): localize options and tolerate oversized persisted speeds | 5 天前 | |
fix: close log safety review gaps | 1 个月前 | |
fix(security): close server-mode admin bypasses (#1525) * fix(security): require keys for remote admin actions * fix(frontend): guard unavailable scrollIntoView * docs: link changelog to PR 1525 * fix(security): align PIN-only discovery policy * fix(security): preserve strict sidecar boundary * fix(security): normalize remote API keys * fix(auth): normalize credential fallback order | 1 个月前 | |
fix(security): close server-mode admin bypasses (#1525) * fix(security): require keys for remote admin actions * fix(frontend): guard unavailable scrollIntoView * docs: link changelog to PR 1525 * fix(security): align PIN-only discovery policy * fix(security): preserve strict sidecar boundary * fix(security): normalize remote API keys * fix(auth): normalize credential fallback order | 1 个月前 | |
fix: expose Ogg/Opus format for MCP speech files (#2324) Expose actual Ogg/Opus encoding for MCP speech files and URLs, with bounded per-render caching and remote-backend verification. Keep WAV defaults and protect audio paths. Fixes #2321. | 1 天前 | |
Merge remote-tracking branch 'origin/main' into fix/ghas-log-safety # Conflicts: # CHANGELOG.md | 1 个月前 | |
feat(electron): add full VoiceStudio desktop app | 11 天前 | |
fix(media): say plainly when a file has no audio track (#2308) * fix(media): name a video with no audio track instead of dumping ffmpeg's exit 234 Loading a video-only MP4 in Dub failed at `extract` with ffmpeg's raw stream dump ("FFmpeg exited with code 234 ... Output file does not contain any stream ... Invalid argument"). Every audio-extract site now probes for an audio stream first (ffprobe, then the ffmpeg stream list) and recognizes ffmpeg's no-stream wording as a fallback, raising NoAudioTrackError with a VoiceStudio sentence and the NO_AUDIO_TRACK failure class: dub ingest, batch dub, the ASR decoder, /transcribe, /v1/audio/transcriptions, clone references and gallery imports. HTTP surfaces return a structured 422 (OpenAI routes: 400 no_audio_track); Electron shows the localized message in all 21 locales and keeps the diagnostic behind Copy diagnostic. ffmpeg's stderr stays in the log. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test+docs: reload-safe no-audio assertions; changelog entry (#2308) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(openai): keep an engine-raised no-audio error as 400 when the probe cannot run Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 2 天前 | |
feat(dub): dedicated Dub home (projects/history) + project rename (#435) The dub Projects + History rail (WorkspaceProjects/WorkspaceHistory) used to sit beside the editor at all times. Now it's a landing: shown only when no project is being edited (dubStep === 'idle'); opening/creating one switches to a full-width editor. (The global Sidebar is already hidden in dub mode, so the studio-right rail is the only surface — no Sidebar change needed.) Adds project rename: - backend: PATCH /projects/{id} updates just the name (400 on empty, 404 on missing) — lighter than PUT which rewrites the whole state blob. - api: renameProject(id, name); App.jsx renameProject handler (updates the active-project label + refreshes the list). - UI: inline rename on each project card (pencil → edit → Enter/Save / Esc). Verified: PATCH create→rename→list / 400 / 404; frontend typecheck:ci clean. Co-authored-by: mergetest <test@local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | 3 个月前 | |
fix(pronunciation): say when an IPA/CMU entry is stored but not applied Closes #1949. Settings offers three notations. Only Respelling substitutes text today; IPA and CMU rows save cleanly, are validated, get a badge and can be toggled on, then get dropped before term matching and are never read again. That much is Phase 1 behaving as designed. The defect is that it was INVISIBLE: "Test a sentence" answered "No entries match — spoken as written" for a term that does match. Not a degraded answer, a wrong one — and it sent the user off to re-type an entry that was already correct, or to convert it to Respelling, where a phoneme string is then read as graphemes. docs/specs/01-expressive-tts.md asked for exactly the opposite: such entries "passed through and flagged 'phoneme not honored on this engine' (parity-rule: visible degradation)". That flag was never implemented. This is it. The dry run reports inert entries separately, and the panel names them. The substitution path is deliberately untouched — this does NOT start feeding raw phoneme strings into the grapheme stream, which is the thing Phase 1 refuses on purpose, and a test pins that it still refuses. Not Phase 2. Lowering IPA/CMU to engine markup is a real feature per engine and stays open; what changes here is that the gap is now honest rather than silent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ypcgSsh5j2PEonSJiAU1S | 15 天前 | |
fix: keep the backend alive on pre-Ampere NVIDIA GPUs (#2135) On a Tesla T4 the backend exited during the first /generate with no traceback and no HTTP response, leaving the client with RemoteDisconnected and every later call with ConnectionRefused. Three separate defects combined, which is why none of the reporter's workarounds helped. 1. torch.compile(mode="reduce-overhead") captures CUDA graphs. T4 (sm_75) passed the existing arch gate, so capture was attempted and aborted the process from inside the native CUDA library — below the interpreter, where neither the #278 eager-fallback wrapper nor any except clause can see it. The compile mode is now resolved per GPU: Ampere (sm_80) and newer keep the cudagraph mode, older cards drop to the non-cudagraph "default" mode and keep their compiled Inductor kernels. Fails open on any probe error, so no GPU that works today loses the optimization. OMNIVOICE_FORCE_CUDAGRAPH=1 restores it. 2. should_torch_compile() never read TORCH_COMPILE_DISABLE. main.py sets it on win32, build_engine_env injected it into subprocesses, and docs/install/windows.md tells users to export it — but the in-process gate ignored it, so the reporter exported the documented variable and still got "torch.compile applied". The gate now honours TORCH_COMPILE_DISABLE / TORCHDYNAMO_DISABLE / TORCHINDUCTOR_DISABLE on every platform, and an env opt-out on the parent propagates to engine subprocesses. The settings DB path is logged alongside the toggle: the reporter had three omnivoice.db files and edited one the backend never opened. 3. Settings -> Performance -> "Disable torch.compile" was rendered disabled outside Windows in both the Tauri and Electron UIs, so the one control that would have stopped this was unreachable for the affected Linux user. The toggle is now live on every platform, and build_engine_env honours it everywhere rather than only on win32. Also arms faulthandler before torch is imported, so a fatal native signal writes the faulting thread's Python stack to backend_err.log instead of the process vanishing silently. This does not prevent a crash; it makes one diagnosable. OMNIVOICE_DISABLE_FAULTHANDLER=1 skips it. Tests fail before / pass after, verified by stashing the source and running the new tests against unfixed code. The crash test kills a real child interpreter with a real SIGSEGV and requires a named Python frame in the output. test_torch_compile_path_gate's fixture now clears the compile-disable env vars: main.py setdefaults them on win32, so on a Windows runner they leaked into os.environ and decided those tests. Not verified on real hardware — no Turing GPU available. The sm_80 floor is inferred from the crash report and from docs/hardware-notes-tesla-t4.md, which already flagged cudagraphs on T4 as attempted by default and never evaluated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> | 9 天前 | |
Merge remote-tracking branch 'origin/main' into fix/ghas-path-boundary # Conflicts: # CHANGELOG.md # backend/core/path_authorization.py # frontend/src-tauri/src/commands.rs # tests/test_network_share.py | 1 个月前 | |
feat: add local speech platform (#1671) Fixes #1646 | 29 天前 | |
feat: rename the product to VoiceStudio (previously OmniVoice-Studio) Renames what users see. The app, the installers, the window title, the docs and all 21 locales now say VoiceStudio, with "(previously OmniVoice-Studio)" noted near the title of each doc surface so people recognise it. Deliberately NOT renamed, because renaming any of them silently breaks an existing install — there is no legacy-path fallback anywhere in this codebase: - bundle identifier com.debpalash.omnivoice-studio (MSI UpgradeCode, macOS TCC grants, managed venv, WebView localStorage, the single-instance lock) - data directories OmniVoice / .omnivoice and omnivoice.db - the ~150 OMNIVOICE_* environment variables - the X-OmniVoice-* HTTP headers (a wire protocol) - the published Docker image paths - the OmniVoice ENGINE, which is a model name and not this product tests/test_identity_paths_survive_the_rename.py pins every one of those so a future well-meaning sweep cannot orphan a user's library. Linux .deb users install a new package name and should apt remove omnivoice-studio; that note is in the changelog. | 1 个月前 | |
feat(electron): add full VoiceStudio desktop app | 11 天前 | |
feat(calls): phone call agent — outbound Twilio calls and inbound agent mode (#2306) * feat(calls): phone call agent backend — outbound/inbound calls in your voice Adds a task-driven call agent on top of the Twilio integration: outbound calls via Twilio REST with signed status callbacks, a provider-agnostic conversation loop (energy VAD -> capture ASR -> streamed LLM turn -> streaming TTS, barge-in via clear), persisted call sessions (alembic 0012), the /calls local API with SSE events, take-over/say/hangup, and inbound agent mode. Owner safeguards: editable AI disclosure on by default, outbound voices gated to verified-own or designed profiles (403), one explicit request per call with max 1-2 concurrent, recording only when enabled and disclosed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(changelog): reference #2306 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(changelog): add Unreleased highlights Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(calls): address review — affirmative recording notice, one agent-call limit, wider number guard - Recording needs an affirmative notice; negated disclosures never record (greptile) - max_concurrent now limits agent calls in both directions (greptile) - Sensitive-number guard also catches comma and Unicode-dash separators (greptile) - Constant upsert SQL, https-only urlopen guard, no BaseException catch (code scanning) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(calls): always finalize a connected call; allow brief numbers in any phone style - Finalize agent calls in a shielded finally so a failed/cancelled stream frees its slot (coderabbit) - Guard allows numbers from the brief written with parentheses or a country code (coderabbit) - Docs: the no-LLM fallback needs a configured greeting (coderabbit) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(calls): wait on record changes instead of sleeping; assert the injected stream failure Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 2 天前 | |
fix(security): make path containment explicit to analysis | 1 个月前 | |
feat(integrations): Twilio phone calls speak a greeting in a saved voice (#2291) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 3 天前 | |
fix(sidecars): reconcile receive deadlines with outer job budgets | 8 天前 | |
fix(security): keep private diagnostics out of API responses (#1454) * fix(security): keep private diagnostics out of API responses * docs: reference response-safety PR * fix(security): preserve constant recovery guidance * fix(security): keep recovery and logs data-independent * fix(security): close remaining response sinks * test(security): keep SOCKS diagnostics private * fix: keep Tailscale exceptions local * fix: keep Tailscale CLI output private | 1 个月前 | |
feat(electron): add full VoiceStudio desktop app | 11 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 8 天前 | ||
| 5 个月前 | ||
| 15 天前 | ||
| 3 天前 | ||
| 1 个月前 | ||
| 2 天前 | ||
| 2 天前 | ||
| 2 天前 | ||
| 16 天前 | ||
| 1 个月前 | ||
| 3 个月前 | ||
| 11 天前 | ||
| 1 天前 | ||
| 1 天前 | ||
| 1 天前 | ||
| 1 天前 | ||
| 5 天前 | ||
| 4 个月前 | ||
| 8 天前 | ||
| 2 天前 | ||
| 1 天前 | ||
| 2 个月前 | ||
| 5 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 天前 | ||
| 1 个月前 | ||
| 11 天前 | ||
| 2 天前 | ||
| 3 个月前 | ||
| 15 天前 | ||
| 9 天前 | ||
| 1 个月前 | ||
| 29 天前 | ||
| 1 个月前 | ||
| 11 天前 | ||
| 2 天前 | ||
| 1 个月前 | ||
| 3 天前 | ||
| 8 天前 | ||
| 1 个月前 | ||
| 11 天前 |