> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/labs-voice-agent/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/labs-voice-agent/_mcp/server.

# nemo_voice_agent.pipecat.services.nvidia.tts

Retry wrapper around pipecat's `NvidiaTTSService`.

Upstream treats any `SynthesizeOnline` exception as terminal for the stream
(`pipecat/services/nvidia/tts.py`: *"Once SynthesizeOnline raises, no further
reliable audio is expected"*). For a locally-hosted Riva server that is the
right call, but against NVCF the first request to a cold or rescheduled worker
routinely fails with::

StatusCode.DEADLINE\_EXCEEDED: failed to establish link to worker

That is a routing/capacity condition, not a synthesis failure — the next
attempt normally succeeds within a second. Upstream's terminal handling turns
it into a silently dropped bot turn: the pipeline gets an `ErrorFrame` and
the user simply hears nothing.

The STT side needs no equivalent: pipecat's `NvidiaSTTService` already
reconnects on any `grpc.RpcError` via `_handle_stream_drop` ->
`_do_reconnect`, and does so turn-aware. Only TTS lacks a retry path.

## Module Contents

### Classes

| Name                                                                                                   | Description                                                                   |
| ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- |
| [`ResilientNvidiaTTSService`](#nemo_voice_agent-pipecat-services-nvidia-tts-ResilientNvidiaTTSService) | `NvidiaTTSService` that retries a synthesis stream that never produced audio. |

### Functions

| Name                                                                           | Description                                                 |
| ------------------------------------------------------------------------------ | ----------------------------------------------------------- |
| [`_is_transient`](#nemo_voice_agent-pipecat-services-nvidia-tts-_is_transient) | True when `error` is a gRPC failure that is worth retrying. |

### Data

[`TRANSIENT_STATUS_CODES`](#nemo_voice_agent-pipecat-services-nvidia-tts-TRANSIENT_STATUS_CODES)

### API

```python
class nemo_voice_agent.pipecat.services.nvidia.tts.ResilientNvidiaTTSService(
    max_retries: int = 2,
    retry_backoff_secs: float = 0.25,
    kwargs = {}
)
```

**Bases:** `NvidiaTTSService`

`NvidiaTTSService` that retries a synthesis stream that never produced audio.

**Parameters:**

**`max_retries`**

Additional attempts after the first failure. `0` restores
upstream behaviour exactly.

---

**`retry_backoff_secs`**

Base delay between attempts; doubled each retry.

---

**`**kwargs`** — default: \{}

Forwarded to `NvidiaTTSService`.

---

**`_max_retries`**

---

**`_retry_backoff_secs`**

---

```python
nemo_voice_agent.pipecat.services.nvidia.tts.ResilientNvidiaTTSService._synthesis_handler(
    state
) -> None
```

Run the `SynthesizeOnline` stream, retrying establishment failures.

Overrides upstream's single-shot handler. Same contract: forward each
response onto `state.response_queue`, push the exception on failure,
and always terminate with a `None` sentinel so `_process_responses`
can finish.

Retry is deliberately restricted to attempts that produced **no audio**.
Once any frame has reached the audio context, re-running the request
would splice a duplicate prefix into the middle of an utterance, which
is worse than the dropped turn we are trying to avoid. Since
"failed to establish link to worker" fails before the first response,
the narrow rule still covers the case that motivated this class.

```python
nemo_voice_agent.pipecat.services.nvidia.tts._is_transient(
    error: BaseException
) -> bool
```

True when `error` is a gRPC failure that is worth retrying.

```python
nemo_voice_agent.pipecat.services.nvidia.tts.TRANSIENT_STATUS_CODES: Set[StatusCode] = {grpc.StatusCode.DEADLINE_EXCEEDED, grpc.StatusCode.UNAVAILABLE, grpc.StatusCode...
```