Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

## New Features

### `telnyx` plugin: LLM, STT and TTS (#620, #621, #622)

The Telnyx plugin, until now a phone transport, also exposes `telnyx.LLM`,
`telnyx.STT`, and `telnyx.TTS`, so a phone agent can run end to end on Telnyx.
`telnyx.LLM` wraps Telnyx Inference's OpenAI-compatible Chat Completions
endpoint and defaults to `meta-llama/Llama-3.3-70B-Instruct`. `telnyx.STT`
streams `linear16` over WebSocket and takes a `sample_rate`, so telephony audio
from `TelnyxMediaStream` can be transcribed at 8 kHz without an upsample; pick
the engine with `transcription_engine`. `telnyx.TTS` streams MP3 over WebSocket
and decodes to `PcmData` as it arrives. All three read `TELNYX_API_KEY` from the
environment. See `plugins/telnyx/examples/voice_agent_call.py` for an inbound
call answered by an all-Telnyx pipeline.

### `speechify` plugin: Speechify TTS

Adds a new `speechify` plugin exposing `speechify.TTS`, backed by Speechify's streaming API. It streams raw PCM audio, defaults to the `simba-3.2` model with the `geffen_32` voice, and reads `SPEECHIFY_API_KEY` from the environment. Install with `vision-agents[speechify]`.
Expand Down
79 changes: 79 additions & 0 deletions plugins/telnyx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ real-time bidirectional media streaming.
- **Audio Conversion**: PCMU, PCMA, and L16 RTP payload conversion
- **WebSocket Management**: Handle Telnyx WebSocket media events
- **Stream Bridge**: Attach a Telnyx phone participant to a Stream call
- **LLM**: Telnyx Inference via the OpenAI-compatible Chat Completions API
- **STT**: Streaming speech to text over WebSocket
- **TTS**: Streaming text to speech over WebSocket

## Installation

Expand Down Expand Up @@ -46,6 +49,75 @@ call.telnyx_stream = stream
await stream.run()
```

## LLM

Telnyx Inference is OpenAI-compatible, so the LLM is a thin wrapper over
`ChatCompletionsLLM` pointed at `https://api.telnyx.com/v2/ai`. Streaming and
tool calling work the same as any other Chat Completions provider.

```python
from vision_agents.plugins import telnyx

llm = telnyx.LLM(model="openai/gpt-4o")
```

Requires `TELNYX_API_KEY` in the environment, or an `api_key` argument.

Model ids come from the Telnyx catalogue at `GET /v2/ai/models` and are not
validated locally. The default is `meta-llama/Llama-3.3-70B-Instruct`.

## STT

```python
from vision_agents.plugins import telnyx

# 8000 matches the PCMU telephony audio that TelnyxMediaStream decodes,
# so nothing is upsampled on the way to the transcriber.
stt = telnyx.STT(sample_rate=8000)
```

Requires `TELNYX_API_KEY` in the environment, or an `api_key` argument.

Audio is resampled to `sample_rate` and sent as raw `linear16` frames. Pick the
engine with `transcription_engine`; the default is `Telnyx`. The engine
catalogue is served by Telnyx and is not validated locally.

Telnyx does not send VAD signals on this endpoint, so the plugin emits
transcripts only and leaves turn detection to the agent.

`interim_results` is honoured per engine rather than per endpoint, and defaults
to `False`. Measured against the live API with the same audio, `Speechmatics`
and `Soniox` stream partial transcripts, while `Telnyx` and `Deepgram` accept
the parameter and return finals only:

```python
stt = telnyx.STT(transcription_engine="Speechmatics", interim_results=True)
```

## TTS

```python
from vision_agents.plugins import telnyx

tts = telnyx.TTS(voice="AWS.Polly.Danielle-Neural")
```

Requires `TELNYX_API_KEY` in the environment, or an `api_key` argument.

Voice ids come from `GET /v2/text-to-speech/voices`. The default is
`Telnyx.KokoroTTS.af_heart`.

Telnyx serves each synthesis on its own WebSocket and closes the socket after
the stop frame, so the plugin reconnects per `stream_audio` call. Audio arrives
as MP3 and is decoded to `PcmData` as it streams. The output sample rate follows
the voice, so it is taken from the decoder rather than configured.

The endpoint takes an `audio_format` parameter, but it is honoured only by some
voices — `AWS.Polly.*` and `Telnyx.NaturalHD.*` serve raw PCM, while the default
`Telnyx.KokoroTTS.*` returns MP3 regardless. Since the PCM sample rate is not
reported on the wire and differs per voice, the plugin decodes MP3 for every
voice rather than carrying a voice-to-rate table that would go stale.

## Examples

See [examples/](examples/) for minimal inbound and outbound Telnyx phone
Expand All @@ -62,6 +134,11 @@ uv run plugins/telnyx/examples/outbound_call.py \
uv run plugins/telnyx/examples/inbound_call.py \
--setup-telnyx \
--phone-number +15551234567

# Inbound call answered by an all-Telnyx STT/LLM/TTS pipeline
uv run plugins/telnyx/examples/voice_agent_call.py \
--setup-telnyx \
--phone-number +15551234567
```

Telnyx phone calls require a Call Control App. The Call Control App is where
Expand Down Expand Up @@ -189,5 +266,7 @@ payload = pcm_to_pcmu(pcm)
## Dependencies

- vision-agents
- vision-agents-plugins-openai
- aiohttp
- numpy
- fastapi
19 changes: 16 additions & 3 deletions plugins/telnyx/examples/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# Telnyx Phone Examples

Minimal inbound and outbound phone examples for the Telnyx plugin. These examples
use Telnyx Call Control, Telnyx Media Streaming, Stream, and Gemini Realtime.
Minimal inbound and outbound phone examples for the Telnyx plugin.

- `outbound_call.py` and `inbound_call.py` use Telnyx Call Control, Telnyx Media
Streaming, Stream, and Gemini Realtime.
- `voice_agent_call.py` answers an inbound call with a pipeline that runs
entirely on Telnyx: `telnyx.STT`, `telnyx.LLM`, and `telnyx.TTS`. It needs no
`GOOGLE_API_KEY`.

## Requirements

Expand All @@ -10,7 +15,7 @@ Create a `.env` file at the repo root or export these variables:
```bash
STREAM_API_KEY=
STREAM_API_SECRET=
GOOGLE_API_KEY=
GOOGLE_API_KEY= # not needed by voice_agent_call.py
TELNYX_API_KEY=
TELNYX_PUBLIC_KEY=
```
Expand Down Expand Up @@ -57,6 +62,14 @@ uv run plugins/telnyx/examples/inbound_call.py \
--phone-number +15551234567
```

Inbound, all Telnyx:

```bash
uv run plugins/telnyx/examples/voice_agent_call.py \
--setup-telnyx \
--phone-number +15551234567
```

For inbound calls, `--setup-telnyx` also routes the Telnyx number to the
temporary Call Control App and restores the previous routing on normal shutdown.

Expand Down
Loading
Loading