mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-22 08:38:13 +02:00
chore: fix dograh v2 speed option
This commit is contained in:
parent
c554256db1
commit
8006d0edcd
5 changed files with 100 additions and 30 deletions
|
|
@ -17,7 +17,10 @@ from api.enums import OrganizationConfigurationKey, PostHogEvent
|
||||||
from api.schemas.ai_model_configuration import (
|
from api.schemas.ai_model_configuration import (
|
||||||
DOGRAH_DEFAULT_LANGUAGE,
|
DOGRAH_DEFAULT_LANGUAGE,
|
||||||
DOGRAH_DEFAULT_VOICE,
|
DOGRAH_DEFAULT_VOICE,
|
||||||
|
DOGRAH_SPEED_MAX,
|
||||||
|
DOGRAH_SPEED_MIN,
|
||||||
DOGRAH_SPEED_OPTIONS,
|
DOGRAH_SPEED_OPTIONS,
|
||||||
|
DOGRAH_SPEED_STEP,
|
||||||
OrganizationAIModelConfigurationResponse,
|
OrganizationAIModelConfigurationResponse,
|
||||||
OrganizationAIModelConfigurationV2,
|
OrganizationAIModelConfigurationV2,
|
||||||
)
|
)
|
||||||
|
|
@ -265,6 +268,11 @@ async def get_model_configuration_v2_defaults(
|
||||||
"voices": [DOGRAH_DEFAULT_VOICE],
|
"voices": [DOGRAH_DEFAULT_VOICE],
|
||||||
"allow_custom_input": _dograh_allows_custom_voice(),
|
"allow_custom_input": _dograh_allows_custom_voice(),
|
||||||
"speeds": list(DOGRAH_SPEED_OPTIONS),
|
"speeds": list(DOGRAH_SPEED_OPTIONS),
|
||||||
|
"speed_range": {
|
||||||
|
"min": DOGRAH_SPEED_MIN,
|
||||||
|
"max": DOGRAH_SPEED_MAX,
|
||||||
|
"step": DOGRAH_SPEED_STEP,
|
||||||
|
},
|
||||||
"languages": DOGRAH_STT_LANGUAGES,
|
"languages": DOGRAH_STT_LANGUAGES,
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"voice": DOGRAH_DEFAULT_VOICE,
|
"voice": DOGRAH_DEFAULT_VOICE,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ from api.services.configuration.registry import (
|
||||||
TTSConfig,
|
TTSConfig,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DOGRAH_SPEED_MIN = 0.5
|
||||||
|
DOGRAH_SPEED_MAX = 2.0
|
||||||
|
DOGRAH_SPEED_STEP = 0.1
|
||||||
DOGRAH_SPEED_OPTIONS: tuple[float, ...] = (0.8, 1.0, 1.2)
|
DOGRAH_SPEED_OPTIONS: tuple[float, ...] = (0.8, 1.0, 1.2)
|
||||||
DOGRAH_DEFAULT_VOICE = "default"
|
DOGRAH_DEFAULT_VOICE = "default"
|
||||||
DOGRAH_DEFAULT_LANGUAGE = "multi"
|
DOGRAH_DEFAULT_LANGUAGE = "multi"
|
||||||
|
|
@ -49,16 +52,9 @@ class EffectiveAIModelConfiguration(BaseModel):
|
||||||
class DograhManagedAIModelConfiguration(BaseModel):
|
class DograhManagedAIModelConfiguration(BaseModel):
|
||||||
api_key: str
|
api_key: str
|
||||||
voice: str = DOGRAH_DEFAULT_VOICE
|
voice: str = DOGRAH_DEFAULT_VOICE
|
||||||
speed: float = Field(default=1.0)
|
speed: float = Field(default=1.0, ge=DOGRAH_SPEED_MIN, le=DOGRAH_SPEED_MAX)
|
||||||
language: str = DOGRAH_DEFAULT_LANGUAGE
|
language: str = DOGRAH_DEFAULT_LANGUAGE
|
||||||
|
|
||||||
@model_validator(mode="after")
|
|
||||||
def validate_speed(self):
|
|
||||||
if self.speed not in DOGRAH_SPEED_OPTIONS:
|
|
||||||
allowed = ", ".join(str(speed) for speed in DOGRAH_SPEED_OPTIONS)
|
|
||||||
raise ValueError(f"Dograh speed must be one of: {allowed}")
|
|
||||||
return self
|
|
||||||
|
|
||||||
|
|
||||||
class BYOKPipelineAIModelConfiguration(BaseModel):
|
class BYOKPipelineAIModelConfiguration(BaseModel):
|
||||||
llm: LLMConfig
|
llm: LLMConfig
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ from api.enums import OrganizationConfigurationKey
|
||||||
from api.schemas.ai_model_configuration import (
|
from api.schemas.ai_model_configuration import (
|
||||||
DOGRAH_DEFAULT_LANGUAGE,
|
DOGRAH_DEFAULT_LANGUAGE,
|
||||||
DOGRAH_DEFAULT_VOICE,
|
DOGRAH_DEFAULT_VOICE,
|
||||||
DOGRAH_SPEED_OPTIONS,
|
DOGRAH_SPEED_MAX,
|
||||||
|
DOGRAH_SPEED_MIN,
|
||||||
BYOKAIModelConfiguration,
|
BYOKAIModelConfiguration,
|
||||||
BYOKPipelineAIModelConfiguration,
|
BYOKPipelineAIModelConfiguration,
|
||||||
BYOKRealtimeAIModelConfiguration,
|
BYOKRealtimeAIModelConfiguration,
|
||||||
|
|
@ -436,7 +437,11 @@ def _convert_any_dograh_legacy_configuration(
|
||||||
dograh_key: str,
|
dograh_key: str,
|
||||||
) -> OrganizationAIModelConfigurationV2:
|
) -> OrganizationAIModelConfigurationV2:
|
||||||
speed = getattr(configuration.tts, "speed", 1.0)
|
speed = getattr(configuration.tts, "speed", 1.0)
|
||||||
if speed not in DOGRAH_SPEED_OPTIONS:
|
try:
|
||||||
|
speed = float(speed)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
speed = 1.0
|
||||||
|
if not DOGRAH_SPEED_MIN <= speed <= DOGRAH_SPEED_MAX:
|
||||||
speed = 1.0
|
speed = 1.0
|
||||||
return OrganizationAIModelConfigurationV2(
|
return OrganizationAIModelConfigurationV2(
|
||||||
mode="dograh",
|
mode="dograh",
|
||||||
|
|
|
||||||
|
|
@ -59,13 +59,27 @@ def test_dograh_v2_compiles_to_effective_managed_pipeline_with_embeddings():
|
||||||
assert effective.managed_service_version == 2
|
assert effective.managed_service_version == 2
|
||||||
|
|
||||||
|
|
||||||
def test_dograh_v2_rejects_non_predefined_speed():
|
def test_dograh_v2_accepts_numeric_speed_in_registry_range():
|
||||||
|
config = OrganizationAIModelConfigurationV2(
|
||||||
|
mode="dograh",
|
||||||
|
dograh=DograhManagedAIModelConfiguration(
|
||||||
|
api_key="mps-secret",
|
||||||
|
speed=1.5,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
effective = compile_ai_model_configuration_v2(config)
|
||||||
|
|
||||||
|
assert effective.tts.speed == 1.5
|
||||||
|
|
||||||
|
|
||||||
|
def test_dograh_v2_rejects_out_of_range_speed():
|
||||||
with pytest.raises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
OrganizationAIModelConfigurationV2(
|
OrganizationAIModelConfigurationV2(
|
||||||
mode="dograh",
|
mode="dograh",
|
||||||
dograh=DograhManagedAIModelConfiguration(
|
dograh=DograhManagedAIModelConfiguration(
|
||||||
api_key="mps-secret",
|
api_key="mps-secret",
|
||||||
speed=1.5,
|
speed=2.5,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -238,6 +252,33 @@ def test_legacy_all_dograh_pipeline_converts_to_dograh_v2():
|
||||||
assert config.dograh.api_key == "mps-secret"
|
assert config.dograh.api_key == "mps-secret"
|
||||||
|
|
||||||
|
|
||||||
|
def test_legacy_dograh_pipeline_conversion_preserves_numeric_speed():
|
||||||
|
legacy = EffectiveAIModelConfiguration(
|
||||||
|
llm=DograhLLMService(
|
||||||
|
provider="dograh",
|
||||||
|
api_key=["mps-secret"],
|
||||||
|
model="default",
|
||||||
|
),
|
||||||
|
tts=DograhTTSService(
|
||||||
|
provider="dograh",
|
||||||
|
api_key=["mps-secret"],
|
||||||
|
model="default",
|
||||||
|
voice="default",
|
||||||
|
speed=1.5,
|
||||||
|
),
|
||||||
|
stt=DograhSTTService(
|
||||||
|
provider="dograh",
|
||||||
|
api_key=["mps-secret"],
|
||||||
|
model="default",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
config = convert_legacy_ai_model_configuration_to_v2(legacy)
|
||||||
|
|
||||||
|
assert config.mode == "dograh"
|
||||||
|
assert config.dograh.speed == 1.5
|
||||||
|
|
||||||
|
|
||||||
def test_legacy_mixed_dograh_pipeline_converts_to_dograh_v2():
|
def test_legacy_mixed_dograh_pipeline_converts_to_dograh_v2():
|
||||||
legacy = EffectiveAIModelConfiguration(
|
legacy = EffectiveAIModelConfiguration(
|
||||||
llm=OpenAILLMService(
|
llm=OpenAILLMService(
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ interface DograhDefaults {
|
||||||
voices: string[];
|
voices: string[];
|
||||||
allow_custom_input?: boolean;
|
allow_custom_input?: boolean;
|
||||||
speeds: number[];
|
speeds: number[];
|
||||||
|
speed_range?: {
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
step?: number;
|
||||||
|
};
|
||||||
languages: string[];
|
languages: string[];
|
||||||
defaults: {
|
defaults: {
|
||||||
voice: string;
|
voice: string;
|
||||||
|
|
@ -66,6 +71,11 @@ function firstApiKey(value: unknown): string {
|
||||||
return typeof value === "string" ? value : "";
|
return typeof value === "string" ? value : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function numberOrDefault(value: unknown, fallback: number): number {
|
||||||
|
const parsed = typeof value === "number" ? value : Number(value);
|
||||||
|
return Number.isFinite(parsed) ? parsed : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
function asRecord(value: unknown): Record<string, unknown> | null {
|
function asRecord(value: unknown): Record<string, unknown> | null {
|
||||||
return value && typeof value === "object" && !Array.isArray(value)
|
return value && typeof value === "object" && !Array.isArray(value)
|
||||||
? value as Record<string, unknown>
|
? value as Record<string, unknown>
|
||||||
|
|
@ -170,7 +180,7 @@ function buildDograhState(
|
||||||
return {
|
return {
|
||||||
api_key: String(configuredDograh.api_key || ""),
|
api_key: String(configuredDograh.api_key || ""),
|
||||||
voice: String(configuredDograh.voice || fallback.voice),
|
voice: String(configuredDograh.voice || fallback.voice),
|
||||||
speed: Number(configuredDograh.speed || fallback.speed),
|
speed: numberOrDefault(configuredDograh.speed, fallback.speed),
|
||||||
language: String(configuredDograh.language || fallback.language),
|
language: String(configuredDograh.language || fallback.language),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +192,7 @@ function buildDograhState(
|
||||||
return {
|
return {
|
||||||
api_key: firstApiKey(llm?.api_key || tts?.api_key || stt?.api_key),
|
api_key: firstApiKey(llm?.api_key || tts?.api_key || stt?.api_key),
|
||||||
voice: String(tts?.voice || fallback.voice),
|
voice: String(tts?.voice || fallback.voice),
|
||||||
speed: Number(tts?.speed || fallback.speed),
|
speed: numberOrDefault(tts?.speed, fallback.speed),
|
||||||
language: String(stt?.language || fallback.language),
|
language: String(stt?.language || fallback.language),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -272,6 +282,7 @@ export function AIModelConfigurationV2Editor({
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
const allowCustomVoice = defaults.dograh.allow_custom_input ?? false;
|
const allowCustomVoice = defaults.dograh.allow_custom_input ?? false;
|
||||||
|
const dograhSpeedRange = defaults.dograh.speed_range ?? { min: 0.5, max: 2.0, step: 0.1 };
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const rawConfiguration = asRecord(configuration);
|
const rawConfiguration = asRecord(configuration);
|
||||||
|
|
@ -288,6 +299,15 @@ export function AIModelConfigurationV2Editor({
|
||||||
setIsSavingDograh(true);
|
setIsSavingDograh(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
|
if (
|
||||||
|
!Number.isFinite(dograh.speed)
|
||||||
|
|| dograh.speed < dograhSpeedRange.min
|
||||||
|
|| dograh.speed > dograhSpeedRange.max
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Dograh speed must be between ${dograhSpeedRange.min} and ${dograhSpeedRange.max}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
await onSave({
|
await onSave({
|
||||||
version: 2,
|
version: 2,
|
||||||
mode: "dograh",
|
mode: "dograh",
|
||||||
|
|
@ -413,22 +433,22 @@ export function AIModelConfigurationV2Editor({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Speed</Label>
|
<Label htmlFor="dograh-speed">Speed</Label>
|
||||||
<Select
|
<Input
|
||||||
value={String(dograh.speed)}
|
id="dograh-speed"
|
||||||
onValueChange={(speed) => setDograh({ ...dograh, speed: Number(speed) })}
|
type="number"
|
||||||
>
|
min={dograhSpeedRange.min}
|
||||||
<SelectTrigger className="w-full">
|
max={dograhSpeedRange.max}
|
||||||
<SelectValue placeholder="Select speed" />
|
step={dograhSpeedRange.step ?? 0.1}
|
||||||
</SelectTrigger>
|
value={dograh.speed}
|
||||||
<SelectContent>
|
onChange={(event) => {
|
||||||
{defaults.dograh.speeds.map((speed) => (
|
const speed = event.currentTarget.valueAsNumber;
|
||||||
<SelectItem key={speed} value={String(speed)}>
|
setDograh({
|
||||||
{speed}x
|
...dograh,
|
||||||
</SelectItem>
|
speed: Number.isFinite(speed) ? speed : defaults.dograh.defaults.speed,
|
||||||
))}
|
});
|
||||||
</SelectContent>
|
}}
|
||||||
</Select>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2 sm:col-span-2">
|
<div className="space-y-2 sm:col-span-2">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue