Merge branch 'codex/review-1861' into codex/pr-queue-integration

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Palash Debnath
2026-09-07 11:10:11 +05:30
6 changed files with 53 additions and 10 deletions
+3
View File
@@ -16,6 +16,7 @@ the frozen-backend fallback mirror it for their toolchains.
- Tilde-separated number ranges are spoken clearly without running their endpoints together (#1821) — thanks @flutterkage2k!
- Voice modes use themed tabs, with Synthesize and Convert pinned below their scrolling forms (#1823)
- Voice cloning now starts with a clear upload-or-record choice, reveals recording and reference details only when needed, and keeps sampling controls under Production Overrides (#1817)
- The first-run welcome line uses an instruction accepted by OmniVoice and VoiceDesign engines (#1861) — thanks @psiberfunk!
### Changed
@@ -67,6 +68,8 @@ the frozen-backend fallback mirror it for their toolchains.
- The notification count stays inside the title bar instead of clipping above the bell (#1823)
- The workspace engine menu opens beside its button instead of at the opposite edge of the page (#1823)
- Cloning reuses the dubbing language picker with flags, search, and single selection, opening above the pinned synthesis controls (#1823)
- The first-run welcome line uses an instruction accepted by OmniVoice and VoiceDesign engines (#1861) — thanks @psiberfunk!
## [0.5.2] — 2026-09-02
+5 -10
View File
@@ -1,3 +1,4 @@
import { firstSoundRequest } from './utils/firstSound';
import React, {
useState,
useRef,
@@ -699,16 +700,10 @@ function App() {
if (!pending) return;
(async () => {
try {
const fd = new FormData();
fd.append('text', i18n.t('firstrun.first_sound_text'));
// Functional model prompt (not user-facing copy) — keeps the demo
// voice warm without depending on seeded profiles.
fd.append('instruct', 'A warm, friendly narrator voice, medium pace');
fd.append('num_step', '16');
const res = await apiFetch(`${API}/generate`, {
method: 'POST',
body: fd,
});
const res = await apiFetch(
`${API}/generate`,
firstSoundRequest(i18n.t('firstrun.first_sound_text')),
);
const blob = await res.blob();
await playBlobAudio(blob, { label: i18n.t('player.generated_audio') });
toast.success(i18n.t('firstrun.first_sound_done'), { duration: 7000 });
@@ -0,0 +1,15 @@
import { describe, it, expect } from 'vitest';
import { firstSoundRequest } from '../utils/firstSound';
describe('onboarding first-sound request', () => {
it('posts localized text and a nonempty voice-design instruction as multipart form data', () => {
const request = firstSoundRequest('Welcome to VoiceStudio');
expect(request.method).toBe('POST');
expect(request.body).toBeInstanceOf(FormData);
expect(Object.fromEntries(request.body)).toEqual({
text: 'Welcome to VoiceStudio',
instruct: 'middle-aged, low pitch',
num_step: '16',
});
});
});
+10
View File
@@ -0,0 +1,10 @@
import defaults from './firstSound.json';
// Taxonomy tokens also provide the nonempty description VoiceDesign requires.
export function firstSoundRequest(text) {
const body = new FormData();
body.append('text', text);
body.append('instruct', defaults.instruct);
body.append('num_step', String(defaults.num_step));
return { method: 'POST', body };
}
+4
View File
@@ -0,0 +1,4 @@
{
"instruct": "middle-aged, low pitch",
"num_step": 16
}
+16
View File
@@ -0,0 +1,16 @@
"""The actual onboarding defaults must pass the runtime engine validator."""
import json
from pathlib import Path
from omnivoice.models.omnivoice import _resolve_instruct
def test_first_sound_instruction_passes_runtime_taxonomy_validation():
defaults = json.loads(
(Path(__file__).parents[1] / "frontend/src/utils/firstSound.json").read_text()
)
instruct = defaults["instruct"]
# VoiceDesign requires a nonempty description; OmniVoice validates tokens.
assert instruct.strip()
assert _resolve_instruct(instruct) == instruct
assert _resolve_instruct(instruct, use_zh=True)