Skip to content

Commit a32f558

Browse files
authored
Merge pull request #4026 from pipecat-ai/mb/fix-deepgram-base-url
Fix DeepgramSTTService base_url forcing HTTPS/WSS schemes
2 parents 4e99cb3 + 2f7c441 commit a32f558

3 files changed

Lines changed: 92 additions & 2 deletions

File tree

changelog/4026.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fixed `DeepgramSTTService` ignoring the `base_url` scheme when using `ws://` or `http://`. Previously these were silently overwritten with `wss://` / `https://`, breaking air-gapped or private deployments that don't use TLS. All scheme choices (`wss://`, `https://`, `ws://`, `http://`, or bare hostname) are now respected.

src/pipecat/services/deepgram/stt.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,45 @@ def _sync_extra_to_fields(self) -> None:
247247
del self.extra[key]
248248

249249

250+
def _derive_deepgram_urls(base_url: str) -> tuple[str, str]:
251+
"""Derive paired WebSocket and HTTP URLs from a single base URL.
252+
253+
The Deepgram SDK client requires both a WebSocket URL (for streaming)
254+
and an HTTP URL (for REST calls). This helper lets developers provide
255+
a single ``base_url`` and consistently derives both, preserving the
256+
security level they chose. Useful for air-gapped or private deployments
257+
where insecure schemes (ws:// / http://) are acceptable.
258+
259+
Accepted inputs:
260+
- ``wss://`` or ``https://`` — secure (paired as wss + https)
261+
- ``ws://`` or ``http://`` — insecure (paired as ws + http)
262+
- Bare hostname (no scheme) — defaults to secure
263+
- Unrecognized scheme — logs a warning, defaults to secure
264+
265+
Args:
266+
base_url: Host with optional scheme, port, and path.
267+
268+
Returns:
269+
A (ws_url, http_url) tuple with consistent schemes.
270+
"""
271+
known_schemes = ("wss://", "https://", "ws://", "http://")
272+
if "://" in base_url:
273+
scheme, host = base_url.split("://", 1)
274+
scheme += "://"
275+
if scheme not in known_schemes:
276+
logger.warning(
277+
f"Unrecognized scheme in base_url '{base_url}', defaulting to wss:// / https://"
278+
)
279+
else:
280+
scheme = ""
281+
host = base_url
282+
283+
insecure = scheme in ("ws://", "http://")
284+
ws_url = f"{'ws' if insecure else 'wss'}://{host}"
285+
http_url = f"{'http' if insecure else 'https'}://{host}"
286+
return ws_url, http_url
287+
288+
250289
class DeepgramSTTService(STTService):
251290
"""Deepgram speech-to-text service.
252291
@@ -445,8 +484,7 @@ def __init__(
445484
try:
446485
from deepgram import DeepgramClientEnvironment
447486

448-
ws_url = base_url if base_url.startswith("wss://") else f"wss://{base_url}"
449-
http_url = base_url if base_url.startswith("https://") else f"https://{base_url}"
487+
ws_url, http_url = _derive_deepgram_urls(base_url)
450488
environment = DeepgramClientEnvironment(
451489
base=http_url,
452490
production=ws_url,

tests/test_deepgram_stt.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#
2+
# Copyright (c) 2024-2026, Daily
3+
#
4+
# SPDX-License-Identifier: BSD 2-Clause License
5+
#
6+
7+
import io
8+
9+
import pytest
10+
from loguru import logger
11+
12+
from pipecat.services.deepgram.stt import _derive_deepgram_urls
13+
14+
15+
@pytest.mark.parametrize(
16+
"base_url, expected_ws, expected_http",
17+
[
18+
# Secure schemes
19+
("wss://mydeepgram.com", "wss://mydeepgram.com", "https://mydeepgram.com"),
20+
("https://mydeepgram.com", "wss://mydeepgram.com", "https://mydeepgram.com"),
21+
# Insecure schemes (air-gapped deployments)
22+
("ws://mydeepgram.com", "ws://mydeepgram.com", "http://mydeepgram.com"),
23+
("http://mydeepgram.com", "ws://mydeepgram.com", "http://mydeepgram.com"),
24+
# Bare hostname defaults to secure
25+
("mydeepgram.com", "wss://mydeepgram.com", "https://mydeepgram.com"),
26+
# With port
27+
("ws://localhost:8080", "ws://localhost:8080", "http://localhost:8080"),
28+
("wss://localhost:443", "wss://localhost:443", "https://localhost:443"),
29+
("localhost:8080", "wss://localhost:8080", "https://localhost:8080"),
30+
# With path
31+
("wss://host/v1/listen", "wss://host/v1/listen", "https://host/v1/listen"),
32+
("http://host/v1/listen", "ws://host/v1/listen", "http://host/v1/listen"),
33+
],
34+
)
35+
def test_derive_deepgram_urls(base_url, expected_ws, expected_http):
36+
ws_url, http_url = _derive_deepgram_urls(base_url)
37+
assert ws_url == expected_ws
38+
assert http_url == expected_http
39+
40+
41+
def test_derive_deepgram_urls_unknown_scheme_warns():
42+
sink = io.StringIO()
43+
handler_id = logger.add(sink, format="{message}")
44+
try:
45+
ws_url, http_url = _derive_deepgram_urls("ftp://mydeepgram.com")
46+
# Falls back to secure
47+
assert ws_url == "wss://mydeepgram.com"
48+
assert http_url == "https://mydeepgram.com"
49+
assert "Unrecognized scheme" in sink.getvalue()
50+
finally:
51+
logger.remove(handler_id)

0 commit comments

Comments
 (0)