| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
feat: implement frontend UI components and expand research documentation for voice processing and translation workflows. | 6 个月前 | |
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. | 2 个月前 | |
feat: onboarding demos, opt-in bug reporting, error-docs deeplinks + issue triage (#133) * feat: onboarding demos, opt-in bug reporting, error-docs deeplinks + issue triage Working-tree snapshot bundling several in-flight workstreams (v0.3.0): - Onboarding/demo system: DemoPresetGrid, DictationDemo, DubbingDemo components + tests, render scripts (render_demos_omnivoice.py, build_demos.sh, build_dub_demo.sh), personalities preview URLs, alembic 0002 voice-profile demo fields. - Opt-in bug reporting: ReportBugButton (prefilled GitHub-issue URL path). - Error transparency UX: errorDocsMap deeplinks + BootstrapSplash/error wiring. - Dub workspace: DubSegmentRow/Table, WaveformTimeline, dubSlice tweaks. - Issue triage: .planning/issue-clusters/ (plan-01..05 root-cause masters, GH #128-#132). - CLAUDE.md: hard rule — everything ships on v0.3.0, no version bumps. KNOWN GAP (why this is a draft): the generated demo audio assets are NOT in this tree, and backend/assets/samples/demo_voice.wav is deleted. onboarding.py guards the missing file (skips seeding the demo profile with a warning), so no crash — but first-run Launchpad will be empty and /demo_audio/ preview URLs 404 until assets are regenerated via scripts/build_demos.sh. Do not merge before regenerating + committing the demo assets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(dub): timing strategies — kill audio compression, add Concise + Stretch Video Replaces the current audio time-compression default (atempo squeeze to fit slot) that produced chipmunk/alien output on high-density target languages like Bengali. Two new user-selectable modes; legacy behaviour kept behind an explicit "Strict slot" choice. New `DubRequest.timing_strategy` enum (default "concise"): - "concise" Translator trims text to fit at natural rate; if it still overflows, hard-trim at slot with a fade so we never overlap the next speaker. Surface overflow_s per segment so the user can shorten the text. - "stretch_video" Audio plays at natural 1.0× rate. Backend computes a per-segment new timeline; persists a video_stretch_plan on the job. Mux step (dub_export) builds an ffmpeg trim+setpts+concat filter graph that stretches each segment's video portion to match the natural-rate dub audio. Gaps/pre-roll/tail pass through at 1.0×. Sub burn under stretch_video is skipped in one pass (cues would drift). - "strict_slot" Legacy atempo squeeze. Retained for back-compat. Director rate-bias side-effect (seg_speed *= bias) now gated on strict_slot only, so "urgent"/"slow" direction tokens keep their instruct effect in the new modes without chipmunking. Per-segment fit_status emitted in the SSE done event: {status: "fits" | "overflows" | "video_stretched", overflow_s?, stretch_ratio?} DubSegmentRow's "Sync: 100%" badge (which was lying — sync_ratio was always ~1.0 because the TTS loop pre-trimmed to slot) is replaced with a truthful "Fits / Overflows +Ns / Video 1.18×" label. Frontend: - prefsSlice.timingStrategy (persisted, store v3→v4 with safe migrate). - DubTab footer Segmented control: "Concise · Stretch Video · Strict slot". - useDubWorkflow passes timing_strategy on /dub/generate; consumes fit_status. Tests: tests/test_dub_timing_strategy.py — 13 cases covering schema defaults/validation, _build_video_stretch_filter_graph (pre-roll, gap, tail, empty-plan early return, post-subtitle chain-in), and _video_stretch_plan_for guards. 30/30 existing dub tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(waveform): surface missing source as "Source media missing" instead of code-4 black box When a project's underlying media file is gone (moved or deleted between save and reload) the <video> element fires MediaError code 4 and the companion audio fetch returns HTTP 404 — both were silently warned to the console while the user stared at an unresponsive black panel and an empty waveform. - WaveformTimeline now flips loadError when the video element rejects code 3 (decode) or 4 (src not supported), and tracks `sourceMissing` separately so the error UI can name the actual problem. - The audio decode fallback chain catches HTTP 404 specifically and treats it as source-missing instead of loading silent empty peaks — an empty waveform on a deleted source is more confusing than a clear "Re-upload the video to continue" message. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(tray): "Show OmniVoice" reloads when the webview is blank When the dev Vite server restarts (or the main window is created before the backend is ready), the webview load fails and the window is left with `<body></body>` plus a "Could not connect to the server" console error. Clicking "Show OmniVoice" from the tray menu just re-showed the broken window — there was no recovery path short of quit+relaunch. Now the show handler runs a tiny eval after `show()`/`set_focus()` that calls `location.reload()` only when `document.body.childElementCount === 0`. A healthy window doesn't blink (body is non-empty); a blank one self-recovers as soon as the user clicks Show. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(#133): bug-report diagnostics field mapping + drop unused imports Address PR #133 review: - ReportBugButton: /system/info exposes `platform` + `device`, not `os`/`torch_device`/`gpu` — those reads silently dropped OS/GPU from every bug report. Map to the real fields (CodeRabbit). Also remove the dead `home` local in stripHome (CodeQL unused-variable). - DictationDemo: drop unused `Loader` import (CodeQL unused-import). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> | 4 个月前 | |
feat(profiles): consent-locked voice profiles — verified_own_voice + spoken consent flow (Wave 0.2) (#354) * feat(profiles): consent-locked voice profiles — verified_own_voice + spoken consent flow (Wave 0.2) A profile becomes 'verified own voice' when its owner records themselves reading a consent statement (spoken attestation, not a checkbox). Agentic features and gallery sharing will gate on the flag; plain local synthesis never does. - alembic 0003 (additive, PRAGMA-guarded, downgrade supported) + _BASE_SCHEMA columns: verified_own_voice, consent_text, consent_audio_path, consent_recorded_at - POST/DELETE /profiles/{id}/consent — stores the recording as provenance in VOICES_DIR ({id}_consent.*), replaces on re-record, cleans up on revoke and on profile delete; 422 on empty statement / too-short audio - VoiceProfile page: Verified badge + Voice ownership panel (record via the existing useRecording denoise flow, revoke with confirm); en.json keys only (other locales fall back per the advisory i18n parity policy) Spec: docs/competitive-analysis.md Action 22 / parity program Wave 0.2. Prerequisite for agentic v2/v3 and the persona gallery. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(profiles): harden consent paths against py/path-injection; drop lifespan in tests - _voices_path(): resolve DB-stored filenames strictly inside VOICES_DIR (bare-filename check + realpath containment); extension whitelist on the uploaded consent filename (fallback .wav) so a crafted filename can never steer the on-disk path. Applied to write, re-record cleanup, revoke, and profile-delete cleanup. New test: malicious upload filename falls back. - Test fixture no longer runs the app lifespan: startup/shutdown touched module-level asyncio primitives bound to another module's event loop, making the suite order-dependent in full-suite CI. init_db() is called directly; endpoints under test need only the schema. Fixes the CodeQL (3x py/path-injection high) and full-suite event-loop failures on PR #354. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 4 个月前 | |
feat(mcp): MCP server v1 — mount on /mcp, per-agent voice binding, stdio shim (Wave 2.2) (#368) * feat(mcp): MCP server v1 — mount on /mcp, per-agent voice binding, stdio shim (Wave 2.2) The FastMCP server (previously dead code, never mounted) is now mounted on the main FastAPI app at /mcp via Streamable HTTP, with its session manager composed into the app lifespan through an AsyncExitStack (best-effort: a missing mcp package or OMNIVOICE_MCP_DISABLE=1 never breaks startup). streamable_http_path set to '/' so the sub-mount lands at /mcp, not /mcp/mcp. Adds the 'mcp' dependency (1.27.x). Per-agent voice binding (Spec 2 headline): each MCP client sends an X-OmniVoice-Client-Id header; generate_speech resolves the voice as explicit arg > the client's binding > global default > app default. New mcp_client_bindings table (alembic 0004 + _BASE_SCHEMA, additive/idempotent), services/mcp_bindings.py (CRUD + resolve_voice + best-effort last_seen), and a loopback-gated REST router (/api/mcp/bindings) the Settings panel drives. New transcribe tool (base64 audio in, 200 MB cap). Stdio shim (backend/mcp_shim, httpx-only, ported from voicebox MIT) proxies stdio clients to the mounted endpoint and forwards OMNIVOICE_CLIENT_ID as the binding header. Settings → Sharing gains an MCP bindings panel. Docs: docs/mcp.md (both connection modes + binding REST) and docs/mcp.json updated to the shim form. Tests: bindings service + resolution precedence + migration up/down (pure, run locally); REST CRUD + mount-not-404 + disable-flag (main-importing, validated in CI). MCP build + mount + initialize handshake verified out-of-band (no torch). Spec: docs/competitive-analysis.md Spec 2 / parity program Wave 2.2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(mcp): assert /mcp mount via app.routes, not a lifespan client The two main-importing mount tests ran the app lifespan, which now starts the FastMCP session manager and binds asyncio queues to the test loop — contaminating later lifespan-running tests ('bound to a different event loop'). The mount happens at import time, so inspecting app.routes for the /mcp Mount is the correct loop-free assertion. Same fix shape as the Wave 0.2 consent tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(mcp): stop reload-main poisoning across the MCP test files Root cause of the CI failure: the bindings REST fixture set OMNIVOICE_MCP_DISABLE=1 and reloaded main but never restored it, so a later 'from main import app' in test_mcp_mount saw /mcp un-mounted ({'/audio','/voice_audio'}). Reloading main mutates the shared module for every subsequent test. - REST fixture: drop the disable flag (the mount is harmless without a lifespan), yield the client, and restore main (+ core.config/db) to the default data dir in teardown so the global module is clean again. - test_main_mounts_mcp_route: reload main with the disable flag cleared so the assertion is independent of any earlier reload. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 4 个月前 | |
feat(profiles): unified profile model — kind discriminator + stored design params (spec P3) (#376) Migration 0005_unified_profiles (0004 taken by mcp bindings): - voice_profiles.kind TEXT DEFAULT 'clone' ('clone' | 'design'), backfilled - voice_profiles.vd_states TEXT NULL — JSON of design category picks - mirrored in _BASE_SCHEMA; idempotent _has_column guards; downgrade drops POST /profiles: - ref_audio now optional; kind + vd_states form fields with validation (clone requires audio; design requires vd_states JSON object + instruct) - design profiles render a deterministic identity sample (seed 42) through the shared archetype renderer — one TTS code path POST /generate: - profile resolution branches on profile.kind (authoritative) instead of the brittle is_locked/instruct inference; legacy pre-0005 rows keep the old inference as fallback; history.mode records profile.kind Frontend: - 'Save design as profile' in the Design tab (vd_states + buildDesignInstruct) - selecting a design profile restores its sliders (vd_states) for re-editing Also unforks the alembic chain (0004_mcp + my 0004 both revised 0003 → multiple heads broke alembic upgrade head and the 0003 migration tests). Tests: tests/test_profile_unification.py — validation, design-create with mocked renderer, migration up/backfill/downgrade. 18/18 profile tests, 312/312 frontend, related backend suite green. Note: docs/specs/voice-studio-unification.md (on feat/studio-ux-overhaul) still says 0004 — renumber to 0005 when branches meet. Co-authored-by: mergetest <test@local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 4 个月前 | |
fix(voice): stop poisoning design profiles with "[object Object]" instruct (#556) Voice Studio "Save design as profile" passed buildDesignInstruct()'s RETURN OBJECT ({instruct, unsupported, duplicates}) straight to FormData.append, which string-coerced it to the literal "[object Object]". That got persisted into voice_profiles.instruct and 400'd on first preview/use with "Unsupported instruct items found in [object Object]" (#550 #545 #542 #537 #530 #525). Fix the whole class + heal already-poisoned data (backward-compatible-data rule): - CloneDesignTab.jsx:609 — pass `.instruct` (the string), not the builder object. - useProfiles.js — append via new instructToFormValue() helper, which extracts `.instruct` if an object ever slips through again (defense-in-depth). - omnivoice/_resolve_instruct — drop the "[object Object]" sentinel instead of raising, so any value that slips through (e.g. generation_history) degrades to neutral conditioning rather than a hard 400. A genuine unsupported token still raises (keeps the #114/#115 user feedback). - migration 0006 — UPDATE voice_profiles SET instruct='' WHERE it's the sentinel, idempotent + table-guarded, to heal profiles saved on the buggy build. Tests (fail-before/pass-after): frontend voiceInstruct (instructToFormValue never yields "[object Object]"); backend _resolve_instruct tolerates the sentinel but still rejects a real bad token; alembic 0006 heals a poisoned row, leaves a healthy one untouched. Frontend 9 passed + typecheck clean; backend 5 passed. Co-authored-by: mergetest <test@local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | 4 个月前 | |
fix(design): heal validator-rejecting instruct on design voices (#594/#571/#596) (#600) * fix(design): heal validator-rejecting instruct on design voices (#594/#571/#596) A designed voice could persist an `instruct` the engine validator rejects — either the literal "[object Object]" from a pre-fix build (#550) or freeform prose typed into the style field — so every Generate/Dub that used the voice failed with `Unsupported instruct items found in …` (400/500, and "Can't reach the local backend" when it tore down mid-render). Migration 0006 only *blanked* "[object Object]", which silently discarded the design — an Indonesian female voice then rendered male (#594). Fix the whole class by healing at every seam and rebuilding from the authoritative source (the design's saved `vd_states` category picks): - omnivoice/utils/voice_design.py: add sanitize_instruct / instruct_from_vd_states / heal_design_instruct — forgiving (never raise), drop poison/prose to valid tags, and rebuild tags from vd_states when the stored value is unusable. - profiles.py: sanitize + rebuild at save (POST) and sanitize at edit (PUT), so no poisoned instruct can ever be persisted again. - generation.py + dub_generate.py: heal whenever a profile drives synthesis, so legacy poisoned rows resolve to valid tags instead of 400-ing. - migration 0007: heal existing profiles in place (recovers gender/age/pitch from vd_states), self-contained (frozen vocab snapshot) so it never drags torch into startup; supersedes 0006's blanking. Backward-compatible. Tests: unit coverage for the healer, a migration test driving 0006->0007 on the real schema, a parity guard so the frozen snapshot can't drift, and two API guards. Corrected one existing test that had encoded the #594 behaviour. Resolves #571, #594, #596; removes a major driver of the "Can't reach backend" reports. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(cjk): allowlist migration 0007's frozen dialect-tag snapshot (#564) The 0007 instruct-heal migration carries a frozen copy of the design-tag whitelist (incl. Chinese dialect tags) so it stays self-contained; add it to the hardcoded-CJK allowlist like omnivoice/utils/voice_design.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: mergetest <test@local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | 4 个月前 | |
feat(tts): user pronunciation dictionary (expressive-tts slice 1) (#685) * feat(tts): user pronunciation dictionary (expressive-tts slice 1) Per-term, per-language pronunciation overrides applied to text before synthesis, so names, brands, and acronyms come out right across generate, longform, and dub. Closes part of the #1 perceived-quality gap vs ElevenLabs (pronunciation dictionaries). First slice of docs/specs/01-expressive-tts.md. - Schema: additive `pronunciation_entries` table (alembic 0008, mirrored into _BASE_SCHEMA; tested upgrade — idempotent, downgrade, converge, back-compat). - Service: extend pronunciation.py to load enabled entries (cached) and apply longest-first, word-boundary-aware, per-language (global '*' + lang match, lang overrides global), reusing the existing ReDoS-safe matcher. - Inline one-off `[[term|replacement]]` overrides that don't persist and don't collide with [voice:]/[pause]/[Name]/SSML-lite (resolved pre-chunking). - API: /pronunciation CRUD + /test dry-run + import/export (loopback-guarded). - Apply point: generation.py after language resolves, before chunking — covers native + pluggable engines. - UI: PronunciationPanel in Settings → General; all strings via i18n. - Tests: migration lifecycle, CRUD, per-language, precedence, inline override, apply-at-synth. Route snapshot regenerated (+7). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(security): bound inline-override regex (ReDoS) + annotate parameterized UPDATE CodeQL flagged py/polynomial-redos on the [[...]] inline-override regex: [^\]] also matches [, so an unterminated run of [ allowed O(n) rescans from O(n) positions. Bound the inner class to {0,256} (linear; an inline override is a short respelling). Annotate the dynamic UPDATE (B608) — its column fragments are fixed literals and every value is a bound parameter; not an injection vector. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: mergetest <test@local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | 4 个月前 | |
feat(studio): generation takes — star, replay, and restore past takes; capped history retention (#1052) Every generate already recorded a generation_history row; now that history is usable: a takes rail in the workspace history lists recent takes with star/ unstar, replay, and one-click restore as the active output. Alembic migration 0009 adds the starred column (the startup schema self-heal covers pre- migration DBs), a retention cap (setting, default 200) prunes the oldest UNstarred rows — starred takes are never pruned — and history WAVs are only deleted when no other row references them. Co-authored-by: mergetest <test@local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 3 个月前 | |
fix(worker): address legacy transport review findings | 2 个月前 | |
fix(workers): retain granted deadlines across disconnects and restart | 1 个月前 | |
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> | 1 个月前 | |
refactor: clean-room reimplementation of prosody records, 8-bit WAV loading, mix scratch naming, migration docstrings and piped-stdout progress bars | 1 天前 | |
fix(cloud-sync): never wipe settings on a tombstone; keep unknown keys; guard newer schemas - A malformed pulled item is skipped instead of killing the round. - Singleton items (settings groups, shortcuts, favorites) only forget their base on a tombstone or a full resync; local values are never wiped. - Value keys this version does not manage are carried over on every push and merge. - Collections seen with a newer schema_version are never pushed over, and are pulled again once this app supports them. - The outbox row gets a generation: an edit made while its push is in flight is not lost, each change gets a new idempotency key, batches use their own key, and idempotency_key_reused renews the key. - Collections switched on later pull what the cloud already has and drop stale removals. | 12 天前 | |
refactor: clean-room reimplementation of prosody records, 8-bit WAV loading, mix scratch naming, migration docstrings and piped-stdout progress bars | 1 天前 | |
fix(design): address review findings and document profile flows | 3 天前 | |
Merge origin/main into #3542; renumber the job_events migration to 0017 after 0016_profile_design_recipe Keeps both sides in voice_convert: the warm reference ASR from this PR and main's pinned source language. | 2 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 6 个月前 | ||
| 2 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 3 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 天前 | ||
| 12 天前 | ||
| 1 天前 | ||
| 3 天前 | ||
| 2 天前 |