Voice constraints on router keys: gender, pinned voice, per-model overrides

Router keys can constrain TTS voices by gender, pin a voice to one model, and override the voice for individual models while preserving caller-selected voices and legacy behavior.

Router keys have new optional TTS policy fields: gender, pinned_voice and overrides. They will be writable through the console and control plane in an upcoming release. When one of these fields activates matching, the legacy key voice is ignored.

Voice resolution now runs separately for every candidate in a failover chain. A female or male constraint is a hard filter, and a candidate with no qualifying roster voice is skipped before it is dialed. Requests using the regional accent candidate chain keep their existing accent-locked voice behavior.

On HTTP routes, when no candidate satisfies an active voice constraint, the request returns 503 with error code no_matching_voice and names the requested gender when one was set.

GET /v1/synthesize/stream now honors the legacy key voice when its config frame has no voice. The WebSocket fallback behind POST /v1/synthesize/stream and POST /v1/audio/speech/stream now honors it when the request body has no voice; all three paths previously ignored it. A key voice belonging to a different vendor than the serving model now fails over instead of silently serving that vendor's default.

A voice sent by the caller in the request body or WebSocket config frame always wins, unchanged.

Keys without the new matching fields still use their legacy key voice. Streaming requests now apply it when the caller sends no voice, and normal candidate failover remains active when a vendor rejects it.

On GET /v1/synthesize/stream, if an active voice constraint filters out every otherwise eligible WebSocket TTS candidate, the router sends an in-band error frame with code NO_MATCHING_VOICE and closes the socket.