Agent Reach Transcribe Command: Groq Whisper to OpenAI Whisper Fallback Logic Explained

The transcribe command in Agent Reach attempts audio transcription through Groq's Whisper API first and silently falls back to OpenAI's Whisper API only if Groq fails, exhausting every configured provider in order before raising an aggregated TranscribeError.

The transcribe command in the open-source Agent Reach project provides a robust audio-to-text pipeline that prefers Groq's free-tier Whisper endpoint and degrades gracefully to OpenAI's Whisper when needed. Implemented primarily in agent_reach/transcribe.py, the transcription logic handles provider selection, audio preprocessing, and automatic fallback orchestration without requiring manual user intervention. This article breaks down the exact fallback chain between Groq Whisper and OpenAI Whisper as implemented in the Panniantong/Agent-Reach repository.

Provider Configuration and Fallback Order

The Provider Definition Table

In agent_reach/transcribe.py, the PROVIDERS dictionary (lines 30-41) maps logical names to concrete API details. For Groq, it points to https://api.groq.com/openai/v1/audio/transcriptions and specifies the model whisper-large-v3. For OpenAI, it points to https://api.openai.com/v1/audio/transcriptions with model whisper-1. Each entry also declares the configuration key that holds the respective API token, ensuring secrets are injected at runtime rather than hard-coded.

Provider Selection and _provider_order

The helper _provider_order (lines 98-104) resolves which providers to attempt. When the caller passes provider="auto", the function returns ["groq", "openai"]. If a specific name is supplied, the list contains only that provider. This deterministic order is what creates the Groq-first, OpenAI-second fallback behavior.

The Fallback Orchestration Loop

The heart of the resilience logic is _transcribe_with_fallback in agent_reach/transcribe.py (lines 49-62). For each audio chunk, it iterates over the resolved provider order and performs the following steps:

  • Checks whether the provider's API key is present in the Config object via _provider_key; if the key is missing, the provider is skipped.
  • Calls transcribe_chunk to send the audio to the provider's endpoint.
  • If transcribe_chunk succeeds, the loop immediately returns the transcript.
  • If any exception—including a TranscribeError—is raised, the loop captures the failure and continues to the next provider.

If the loop exhausts every provider without success, it raises an aggregated error containing all captured failures.

End-to-End Transcription Request Flow

The main transcribe function orchestrates the pipeline from download to final text. The exact sequence implemented in agent_reach/transcribe.py is:

  1. Validation. Before touching the audio, transcribe verifies that at least one provider in the resolved order has a configured API key (lines 20-25).

  2. Source handling. The input can be a remote URL, which is downloaded with download_audio (powered by yt-dlp), or a local file path.

  3. Compression. compress_audio re-encodes the source to a Whisper-compatible mono, 16 kHz, 32 kbps format (lines 101-108).

  4. Chunking. If the compressed file exceeds the approximate 24 MiB limit, chunk_audio splits it into segments of up to 10 minutes each (lines 126-133).

  5. Per-chunk fallback. For every segment, transcribe calls _transcribe_with_fallback, which applies the provider loop described above (lines 49-62).

  6. HTTP request. Inside the loop, transcribe_chunk builds a multipart POST request: the provider-specific <endpoint>, an Authorization: Bearer <api_key> header, the audio file, and form data including model and response_format=text (lines 81-89).

  7. Error detection. On HTTP 200, the raw text response is returned. Any non-2xx status causes transcribe_chunk to raise a TranscribeError (lines 94-96), signaling the outer loop to fall back to the next provider.

  8. Aggregation. Successful transcripts are stripped and joined with newlines inside transcribe (lines 42-48) before the final string is returned.

Groq to OpenAI Fallback in Practice

When transcribe is invoked with provider="auto" (the CLI default), the following runtime behavior occurs:

  • First attempt: The request is sent to Groq's endpoint (https://api.groq.com/openai/v1/audio/transcriptions) using model whisper-large-v3. If Groq returns HTTP 200, the transcript is accepted and OpenAI is never contacted.
  • Silent fallback: If Groq fails—whether due to an HTTP 429 rate limit, a network timeout, a missing API key, or any other error—the loop catches the exception and proceeds to OpenAI's endpoint (https://api.openai.com/v1/audio/transcriptions) with model whisper-1.
  • Final failure: If OpenAI also fails, the aggregated TranscribeError is raised to the caller.

This behavior is codified in the unit-test suite in tests/test_transcribe.py. The TestFallback class (lines 99-132) validates three critical scenarios:

  • test_groq_succeeds_no_openai_call proves that a successful Groq response prevents any OpenAI traffic.
  • test_groq_429_falls_back_to_openai verifies that an HTTP 429 from Groq triggers the OpenAI path.
  • test_skip_unconfigured_provider confirms that providers missing API keys are skipped entirely.

Code Examples

Direct Script Usage

The public transcribe function accepts a source path or URL and a Config object that loads keys from the user's configuration file.

from agent_reach import transcribe
from agent_reach.config import Config

# Load keys previously stored with `agent-reach configure`.

cfg = Config()

text = transcribe(
    "https://youtu.be/example",  # can also be a local .m4a path

    provider="auto",             # default: Groq to OpenAI fallback

    config=cfg,
)
print(text)

This call orchestrates the full pipeline, relying on _provider_order to set the fallback chain and _transcribe_with_fallback to switch providers automatically.

Manual Provider Loop

For advanced use, you can invoke the fallback helper directly on a pre-generated audio chunk.

from agent_reach import transcribe as tr
from pathlib import Path
from agent_reach.config import Config

cfg = Config()
chunk = Path("tmp/chunk_001.m4a")

order = ["groq", "openai"]
try:
    result = tr._transcribe_with_fallback(chunk, order, cfg)
    print("Transcribed via:", order[0] if result else "none")
except tr.TranscribeError as exc:
    print("All providers failed:", exc)

This example demonstrates the inner _transcribe_with_fallback loop that iterates over order, invoking transcribe_chunk for each provider until one succeeds.

CLI Entry Point

The packaged CLI exposes the same logic without writing code:

agent-reach transcribe "https://youtu.be/example" --provider auto

Arguments are forwarded directly to the transcribe function, inheriting the identical Groq-to-OpenAI fallback semantics.

Key Files and Functions

Understanding the transcription fallback logic requires familiarity with these source files:

  • agent_reach/transcribe.py — Core implementation containing PROVIDERS, _provider_order, _transcribe_with_fallback, transcribe_chunk, and the main transcribe orchestrator.
  • tests/test_transcribe.py — Test suite (including TestFallback) that validates provider routing, fallback on HTTP 429, and skipping unconfigured providers.
  • agent_reach/config.py — Stores groq_api_key and openai_api_key and exposes the configuration object consumed by the transcriber.
  • agent_reach/channels/youtube.py — Example channel implementation that delegates its transcribe method to the core transcribe function.

Summary

  • Agent Reach's transcribe command in agent_reach/transcribe.py implements a hardcoded provider order that places Groq first and OpenAI second when provider="auto" is selected.
  • The _transcribe_with_fallback helper silently catches any failure from Groq—including rate limits, network errors, or missing keys—and retries the same chunk against OpenAI.
  • Each provider is defined in the PROVIDERS table with its own endpoint, model name, and configuration key, keeping secrets isolated from the orchestration logic.
  • Audio is automatically compressed, chunked if it exceeds the 24 MiB limit, and processed independently, with per-chunk fallback ensuring maximum resilience.
  • The behavior is codified in tests/test_transcribe.py, which explicitly asserts that OpenAI is never called when Groq succeeds and that fallback occurs on Groq HTTP 429 errors.

Frequently Asked Questions

What triggers the fallback from Groq to OpenAI Whisper?

Any failure during the Groq request triggers the fallback. According to _transcribe_with_fallback in agent_reach/transcribe.py, if transcribe_chunk raises an exception—such as an HTTP 429 rate limit, a network timeout, or a TranscribeError from a non-2xx response—the loop immediately proceeds to the next configured provider, which is OpenAI by default.

Can I force the transcribe command to use only one provider?

Yes. Passing provider="groq" or provider="openai" to the transcribe function bypasses the automatic ordering. The _provider_order helper returns a single-element list containing only the requested provider, so no fallback occurs if that provider fails.

How does Agent Reach handle audio files that exceed the Whisper size limit?

Before transcription, compress_audio re-encodes the source to a Whisper-compatible format. If the result still exceeds the approximate 24 MiB limit, chunk_audio splits the file into segments of up to 10 minutes. The main transcribe function then processes each segment independently through _transcribe_with_fallback, so a large file never prevents transcription.

What happens if both Groq and OpenAI providers fail?

If every provider in the resolved order fails, _transcribe_with_fallback exhausts its loop and raises an aggregated TranscribeError. The caller receives a single exception that encapsulates all underlying failures, indicating that no configured provider could complete the transcription.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →