Expand realtime v3 voice support (#52363)

## Why

Realtime v3 voice validation used the v1 voice list, limiting the available voice choices.

## What changed

- Add a dedicated `v3` voice list containing the v1 voices and 16 additional voices, and use it to validate v3 requests.
- Expose `v3` in `RealtimeVoicesList`, defaulting to an empty list when omitted during deserialization.
- Update the configuration and app-server schemas, generated TypeScript and Python types, and voice-list test expectations.

GitOrigin-RevId: 7fc8060fe01d840f28eb92c3dfe49c5878c3c258
This commit is contained in:
Jackson Moody
2026-10-09 02:04:35 +00:00
committed by copyberry
parent 0ada5d8806
commit 03b761dca9
13 changed files with 168 additions and 7 deletions
+17 -1
View File
@@ -2792,22 +2792,38 @@
"alloy",
"arbor",
"ash",
"aube",
"ballad",
"breeze",
"bubbie",
"cedar",
"coral",
"cove",
"echo",
"ember",
"haetsal",
"hanul",
"himari",
"juniper",
"leher",
"maple",
"marin",
"miko",
"neer",
"porto",
"rindo",
"rio",
"rivage",
"sage",
"selva",
"shimmer",
"sol",
"sonna",
"spruce",
"tinta",
"vale",
"verse"
"verse",
"viola"
],
"type": "string"
},
@@ -18272,22 +18272,38 @@
"alloy",
"arbor",
"ash",
"aube",
"ballad",
"breeze",
"bubbie",
"cedar",
"coral",
"cove",
"echo",
"ember",
"haetsal",
"hanul",
"himari",
"juniper",
"leher",
"maple",
"marin",
"miko",
"neer",
"porto",
"rindo",
"rio",
"rivage",
"sage",
"selva",
"shimmer",
"sol",
"sonna",
"spruce",
"tinta",
"vale",
"verse"
"verse",
"viola"
],
"type": "string"
},
@@ -18310,6 +18326,13 @@
"$ref": "#/definitions/v2/RealtimeVoice"
},
"type": "array"
},
"v3": {
"default": [],
"items": {
"$ref": "#/definitions/v2/RealtimeVoice"
},
"type": "array"
}
},
"required": [
@@ -14284,22 +14284,38 @@
"alloy",
"arbor",
"ash",
"aube",
"ballad",
"breeze",
"bubbie",
"cedar",
"coral",
"cove",
"echo",
"ember",
"haetsal",
"hanul",
"himari",
"juniper",
"leher",
"maple",
"marin",
"miko",
"neer",
"porto",
"rindo",
"rio",
"rivage",
"sage",
"selva",
"shimmer",
"sol",
"sonna",
"spruce",
"tinta",
"vale",
"verse"
"verse",
"viola"
],
"type": "string"
},
@@ -14322,6 +14338,13 @@
"$ref": "#/definitions/RealtimeVoice"
},
"type": "array"
},
"v3": {
"default": [],
"items": {
"$ref": "#/definitions/RealtimeVoice"
},
"type": "array"
}
},
"required": [
@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type RealtimeVoice = "alloy" | "arbor" | "ash" | "ballad" | "breeze" | "cedar" | "coral" | "cove" | "echo" | "ember" | "juniper" | "maple" | "marin" | "sage" | "shimmer" | "sol" | "spruce" | "vale" | "verse";
export type RealtimeVoice = "alloy" | "arbor" | "ash" | "aube" | "ballad" | "breeze" | "bubbie" | "cedar" | "coral" | "cove" | "echo" | "ember" | "haetsal" | "hanul" | "himari" | "juniper" | "leher" | "maple" | "marin" | "miko" | "neer" | "porto" | "rindo" | "rio" | "rivage" | "sage" | "selva" | "shimmer" | "sol" | "sonna" | "spruce" | "tinta" | "vale" | "verse" | "viola";
@@ -3,4 +3,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { RealtimeVoice } from "./RealtimeVoice";
export type RealtimeVoicesList = { v1: Array<RealtimeVoice>, v2: Array<RealtimeVoice>, defaultV1: RealtimeVoice, defaultV2: RealtimeVoice, };
export type RealtimeVoicesList = { v1: Array<RealtimeVoice>, v2: Array<RealtimeVoice>, v3?: Array<RealtimeVoice>, defaultV1: RealtimeVoice, defaultV2: RealtimeVoice, };
@@ -1711,6 +1711,7 @@ async fn realtime_list_voices_returns_supported_names() -> Result<()> {
RealtimeVoice::Marin,
RealtimeVoice::Cedar,
],
v3: RealtimeVoicesList::builtin().v3,
default_v1: RealtimeVoice::Cove,
default_v2: RealtimeVoice::Marin,
},
+17 -1
View File
@@ -3974,22 +3974,38 @@
"alloy",
"arbor",
"ash",
"aube",
"ballad",
"breeze",
"bubbie",
"cedar",
"coral",
"cove",
"echo",
"ember",
"haetsal",
"hanul",
"himari",
"juniper",
"leher",
"maple",
"marin",
"miko",
"neer",
"porto",
"rindo",
"rio",
"rivage",
"sage",
"selva",
"shimmer",
"sol",
"sonna",
"spruce",
"tinta",
"vale",
"verse"
"verse",
"viola"
],
"type": "string"
},
+2 -1
View File
@@ -1668,8 +1668,9 @@ fn realtime_backend_item(text: String, prefix: Option<&str>) -> String {
fn validate_realtime_voice(version: RealtimeWsVersion, voice: RealtimeVoice) -> CodexResult<()> {
let voices = RealtimeVoicesList::builtin();
let allowed = match version {
RealtimeWsVersion::V1 | RealtimeWsVersion::V3 => &voices.v1,
RealtimeWsVersion::V1 => &voices.v1,
RealtimeWsVersion::V2 => &voices.v2,
RealtimeWsVersion::V3 => &voices.v3,
};
if allowed.contains(&voice) {
return Ok(());
+1
View File
@@ -11603,6 +11603,7 @@ async fn realtime_conversation_list_voices_emits_builtin_list() {
RealtimeVoice::Marin,
RealtimeVoice::Cedar,
],
v3: RealtimeVoicesList::builtin().v3,
default_v1: RealtimeVoice::Cove,
default_v2: RealtimeVoice::Marin,
},
+63
View File
@@ -257,22 +257,38 @@ pub enum RealtimeVoice {
Alloy,
Arbor,
Ash,
Aube,
Ballad,
Breeze,
Bubbie,
Cedar,
Coral,
Cove,
Echo,
Ember,
Haetsal,
Hanul,
Himari,
Juniper,
Leher,
Maple,
Marin,
Miko,
Neer,
Porto,
Rindo,
Rio,
Rivage,
Sage,
Selva,
Shimmer,
Sol,
Sonna,
Spruce,
Tinta,
Vale,
Verse,
Viola,
}
impl RealtimeVoice {
@@ -281,22 +297,38 @@ impl RealtimeVoice {
Self::Alloy => "alloy",
Self::Arbor => "arbor",
Self::Ash => "ash",
Self::Aube => "aube",
Self::Ballad => "ballad",
Self::Breeze => "breeze",
Self::Bubbie => "bubbie",
Self::Cedar => "cedar",
Self::Coral => "coral",
Self::Cove => "cove",
Self::Echo => "echo",
Self::Ember => "ember",
Self::Haetsal => "haetsal",
Self::Hanul => "hanul",
Self::Himari => "himari",
Self::Juniper => "juniper",
Self::Leher => "leher",
Self::Maple => "maple",
Self::Marin => "marin",
Self::Miko => "miko",
Self::Neer => "neer",
Self::Porto => "porto",
Self::Rindo => "rindo",
Self::Rio => "rio",
Self::Rivage => "rivage",
Self::Sage => "sage",
Self::Selva => "selva",
Self::Shimmer => "shimmer",
Self::Sol => "sol",
Self::Sonna => "sonna",
Self::Spruce => "spruce",
Self::Tinta => "tinta",
Self::Vale => "vale",
Self::Verse => "verse",
Self::Viola => "viola",
}
}
}
@@ -307,6 +339,9 @@ impl RealtimeVoice {
pub struct RealtimeVoicesList {
pub v1: Vec<RealtimeVoice>,
pub v2: Vec<RealtimeVoice>,
#[serde(default)]
#[ts(as = "Option<_>", optional)]
pub v3: Vec<RealtimeVoice>,
pub default_v1: RealtimeVoice,
pub default_v2: RealtimeVoice,
}
@@ -337,6 +372,33 @@ impl RealtimeVoicesList {
RealtimeVoice::Marin,
RealtimeVoice::Cedar,
],
v3: vec![
RealtimeVoice::Juniper,
RealtimeVoice::Maple,
RealtimeVoice::Spruce,
RealtimeVoice::Ember,
RealtimeVoice::Vale,
RealtimeVoice::Breeze,
RealtimeVoice::Arbor,
RealtimeVoice::Sol,
RealtimeVoice::Cove,
RealtimeVoice::Aube,
RealtimeVoice::Bubbie,
RealtimeVoice::Haetsal,
RealtimeVoice::Hanul,
RealtimeVoice::Himari,
RealtimeVoice::Leher,
RealtimeVoice::Miko,
RealtimeVoice::Neer,
RealtimeVoice::Porto,
RealtimeVoice::Rindo,
RealtimeVoice::Rio,
RealtimeVoice::Rivage,
RealtimeVoice::Selva,
RealtimeVoice::Sonna,
RealtimeVoice::Tinta,
RealtimeVoice::Viola,
],
default_v1: RealtimeVoice::Cove,
default_v2: RealtimeVoice::Marin,
}
@@ -5985,6 +6047,7 @@ mod tests {
RealtimeVoice::Marin,
RealtimeVoice::Cedar,
],
v3: RealtimeVoicesList::builtin().v3,
default_v1: RealtimeVoice::Cove,
default_v2: RealtimeVoice::Marin,
}
@@ -3680,22 +3680,38 @@ class RealtimeVoice(Enum):
alloy = "alloy"
arbor = "arbor"
ash = "ash"
aube = "aube"
ballad = "ballad"
breeze = "breeze"
bubbie = "bubbie"
cedar = "cedar"
coral = "coral"
cove = "cove"
echo = "echo"
ember = "ember"
haetsal = "haetsal"
hanul = "hanul"
himari = "himari"
juniper = "juniper"
leher = "leher"
maple = "maple"
marin = "marin"
miko = "miko"
neer = "neer"
porto = "porto"
rindo = "rindo"
rio = "rio"
rivage = "rivage"
sage = "sage"
selva = "selva"
shimmer = "shimmer"
sol = "sol"
sonna = "sonna"
spruce = "spruce"
tinta = "tinta"
vale = "vale"
verse = "verse"
viola = "viola"
class RealtimeVoicesList(BaseModel):
@@ -3706,6 +3722,7 @@ class RealtimeVoicesList(BaseModel):
default_v2: Annotated[RealtimeVoice, Field(alias="defaultV2")]
v1: list[RealtimeVoice]
v2: list[RealtimeVoice]
v3: list[RealtimeVoice] | None = []
class ReasoningEffort(str, Enum):