# How the Transcribe Command in Agent Reach Uses Whisper via Groq with OpenAI Fallback

> Agent Reach's transcribe command uses Groq Whisper API first, then OpenAI fallback. Handles audio download, compression, and chunking automatically for efficient transcription.

- Repository: [Pnant/Agent-Reach](https://github.com/Panniantong/Agent-Reach)
- Tags: internals
- Published: 2026-06-14

---

**The transcribe command in Agent Reach automatically routes audio through Groq's free Whisper API first, then transparently falls back to OpenAI's Whisper endpoint on any HTTP error, while handling audio download, compression, and chunking automatically.**

The `agent_reach.transcribe` module serves as the core orchestrator for converting audio URLs or local files into text. As implemented in the Panniantong/Agent-Reach repository, this command leverages a layered provider architecture that prioritizes Groq's Whisper implementation before falling back to OpenAI, ensuring robust transcription even when rate limits or network issues occur.

## Provider Selection and Configuration

The transcribe command supports both automatic provider selection and explicit provider targeting through the `provider` parameter.

### Auto Mode vs Explicit Selection

In [`agent_reach/transcribe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/transcribe.py), the helper function `_provider_order` determines the execution sequence based on user input. When `provider="auto"` is specified (the default), the function returns `["groq", "openai"]`, ensuring Groq is attempted first. If a specific provider is requested, it returns a single-item list containing only that provider.

```python

# From transcribe.py lines 99-104

def _provider_order(provider: str) -> List[str]:
    if provider == "auto":
        return ["groq", "openai"]
    return [provider]

```

### Configuration Validation

Before initiating any network requests, the code validates that at least one requested provider has an API key configured in `~/.agent-reach/config.yaml`. The configuration must contain either `groq_api_key` or `openai_api_key`. If neither is present, the function raises a `NoProviderConfigured` error immediately.

```python

# From transcribe.py lines 22-26

if not any(cfg.get(f"{p}_api_key") for p in providers):
    raise NoProviderConfigured(
        f"No API key configured for providers: {providers}"
    )

```

## Audio Preparation Pipeline

The transcribe command handles the entire preprocessing workflow automatically, from downloading remote media to splitting oversized files.

### Downloading with yt-dlp

When the `source` argument is a URL rather than a local file path, the `download_audio` function (lines 77-95) invokes **yt-dlp** to fetch the media stream. The function extracts only the audio component to minimize bandwidth and processing time.

### Compression and Formatting

Raw audio is re-encoded to a mono 16 kHz m4a file to meet Whisper's requirements. This compression step ensures the file stays under the **25 MiB upload limit** imposed by the Whisper API. The processing occurs in [`agent_reach/transcribe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/transcribe.py) between lines 101-121.

### Chunking for Size Limits

If compression still yields a file exceeding 25 MiB, the `chunk_audio` function (lines 126-154) splits the audio into segments of ≤10 minutes each. Each chunk represents a valid, independent payload for the Whisper API, allowing the system to process long-form content without truncation.

## The Fallback Mechanism

The core resilience logic resides in `_transcribe_with_fallback`, which implements circuit-breaker-style retry logic across providers.

### Provider Order Iteration

For each audio chunk, the function iterates over the provider list (typically `["groq", "openai"]`). It skips any provider lacking an API key in the configuration, though the prior validation ensures at least one provider is available.

The `transcribe_chunk` helper executes the actual HTTP POST request:
- **Groq**: Posts to `https://api.groq.com/openai/v1/audio/transcriptions` using model `whisper-large-v3`
- **OpenAI**: Posts to `https://api.openai.com/v1/audio/transcriptions` using model `whisper-1`

### Error Handling and Retry Logic

If Groq returns a non-2xx status code (such as a 429 rate limit), `transcribe_chunk` raises a `TranscribeError`. The fallback loop catches this exception, records the error, and proceeds to the next provider. The first successful response returns its transcript immediately; if all providers fail, the last error is re-raised to the caller.

```python

# From transcribe.py lines 58-61 (simplified)

for provider in providers:
    try:
        return transcribe_chunk(chunk, provider, cfg)
    except TranscribeError as e:
        errors.append(e)
        continue

```

## CLI Usage

The `agent_reach.cli` module exposes this functionality through the `transcribe` sub-command (lines 1113-1122). Users can process local files or URLs directly from the terminal.

```bash

# Print transcript to stdout

agent-reach transcribe my_audio.mp3

# Save to a file

agent-reach transcribe my_audio.mp3 -o transcript.txt

# Force OpenAI only (bypass Groq)

agent-reach transcribe speech.wav --provider openai

```

### Python API Examples

```python
from agent_reach.transcribe import transcribe

# Auto-select provider (Groq first, OpenAI fallback)

text = transcribe("https://youtu.be/example")

# Force specific provider

text = transcribe("speech.wav", provider="openai")

```

### Fallback in Action

The test suite demonstrates the fallback behavior by simulating a Groq rate limit (429) followed by OpenAI success:

```python

# Test scenario from test_transcribe.py

monkeypatch.setattr(
    tr.requests, "post",
    lambda url, **kw: FakeResponse(429, "rate limited")
    if url == tr.PROVIDERS["groq"]["endpoint"]
    else FakeResponse(200, "openai result")
)
text = tr._transcribe_with_fallback(chunk, ["groq", "openai"], cfg)
assert text == "openai result"  # OpenAI used after Groq failed

```

## Summary

- The **transcribe command** uses a provider-agnostic architecture defined in [`agent_reach/transcribe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/transcribe.py) that defaults to Groq but automatically falls back to OpenAI.
- **Audio preprocessing** includes yt-dlp downloading, mono 16kHz m4a compression, and automatic chunking to respect the 25 MiB API limit.
- **Error handling** catches `TranscribeError` exceptions from failed providers and iterates through the provider list until success or exhaustion.
- **Configuration** requires API keys in `~/.agent-reach/config.yaml` for either Groq (`groq_api_key`) or OpenAI (`openai_api_key`).
- **Chunk assembly** concatenates successful segment transcripts with newline characters to produce the final output.

## Frequently Asked Questions

### What happens if both Groq and OpenAI fail?

If all configured providers return errors (rate limits, authentication failures, or network issues), the `_transcribe_with_fallback` function re-raises the last `TranscribeError` encountered. The CLI will display this error and exit with a non-zero status code.

### Why does the transcribe command prefer Groq over OpenAI?

The default `provider="auto"` mode returns `["groq", "openai"]` as the execution order because Groq offers a free tier for Whisper transcription with competitive latency. OpenAI serves as the paid fallback for reliability when Groq rate limits are hit.

### How does the command handle large audio files like podcasts or lectures?

Files exceeding the 25 MiB limit after compression are automatically split into ≤10-minute chunks by the `chunk_audio` function. Each chunk is transcribed independently, and the results are concatenated with newline separators before being returned to the user.

### Can I use the transcribe command without configuring both API keys?

Yes. You only need to configure one provider's API key in `~/.agent-reach/config.yaml`. If you specify `provider="groq"` or `provider="openai"` explicitly, only that key is required. The validation logic checks that at least one requested provider has credentials, not all of them.