Skip to content
Open
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
16 changes: 16 additions & 0 deletions src/elevenlabs/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ class ElevenLabs(BaseElevenLabs):

- api_key: typing.Optional[str].

- headers: typing.Optional[typing.Dict[str, str]]. Additional headers to send with every request.

- timeout: typing.Optional[float]. The timeout to be used, in seconds, for requests by default the timeout is 240 seconds.

- follow_redirects: typing.Optional[bool]. Whether the default httpx client should follow redirects. Defaults to True. Ignored when a custom `httpx_client` is supplied.

- httpx_client: typing.Optional[httpx.Client]. The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration.
---
from elevenlabs.client import ElevenLabs
Expand All @@ -49,14 +53,18 @@ def __init__(
base_url: typing.Optional[str] = None,
environment: ElevenLabsEnvironment = ElevenLabsEnvironment.PRODUCTION,
api_key: typing.Optional[str] = os.getenv("ELEVENLABS_API_KEY"),
headers: typing.Optional[typing.Dict[str, str]] = None,
timeout: typing.Optional[float] = 240,
follow_redirects: typing.Optional[bool] = True,
httpx_client: typing.Optional[httpx.Client] = None
):
super().__init__(
base_url=base_url,
environment=environment,
api_key=api_key,
headers=headers,
timeout=timeout,
follow_redirects=follow_redirects,
httpx_client=httpx_client
)
self._text_to_speech = RealtimeTextToSpeechClient(client_wrapper=self._client_wrapper)
Expand All @@ -83,8 +91,12 @@ class AsyncElevenLabs(AsyncBaseElevenLabs):

- api_key: typing.Optional[str].

- headers: typing.Optional[typing.Dict[str, str]]. Additional headers to send with every request.

- timeout: typing.Optional[float]. The timeout to be used, in seconds, for requests by default the timeout is 240 seconds.

- follow_redirects: typing.Optional[bool]. Whether the default httpx client should follow redirects. Defaults to True. Ignored when a custom `httpx_client` is supplied.

- httpx_client: typing.Optional[httpx.AsyncClient]. The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration.
---
from elevenlabs.client import AsyncElevenLabs
Expand All @@ -100,14 +112,18 @@ def __init__(
base_url: typing.Optional[str] = None,
environment: ElevenLabsEnvironment = ElevenLabsEnvironment.PRODUCTION,
api_key: typing.Optional[str] = os.getenv("ELEVENLABS_API_KEY"),
headers: typing.Optional[typing.Dict[str, str]] = None,
timeout: typing.Optional[float] = 240,
follow_redirects: typing.Optional[bool] = True,
httpx_client: typing.Optional[httpx.AsyncClient] = None
):
super().__init__(
base_url=base_url,
environment=environment,
api_key=api_key,
headers=headers,
timeout=timeout,
follow_redirects=follow_redirects,
httpx_client=httpx_client
)
self._webhooks = AsyncWebhooksClient(client_wrapper=self._client_wrapper)
Expand Down
106 changes: 106 additions & 0 deletions tests/test_client_headers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""Tests that the user-facing ElevenLabs / AsyncElevenLabs classes forward
the ``headers`` and ``follow_redirects`` kwargs through to the underlying
client wrapper / httpx client.

Regression coverage for the case where ``ElevenLabs(headers={...})`` and
``ElevenLabs(follow_redirects=False)`` raised TypeError because the
subclass's hand-rolled ``__init__`` did not accept or forward kwargs that
``BaseElevenLabs`` supports.
"""

import pytest

from elevenlabs.client import AsyncElevenLabs, ElevenLabs


def test_sync_client_accepts_headers_kwarg():
"""ElevenLabs(headers=...) does not raise and stores the headers."""
custom = {"x-trace-id": "abc-123", "x-tenant": "acme"}
client = ElevenLabs(api_key="sk-test", headers=custom)
assert client._client_wrapper.get_custom_headers() == custom


def test_sync_client_headers_default_is_none():
"""Omitting headers leaves the wrapper's custom headers unset (backward-compatible)."""
client = ElevenLabs(api_key="sk-test")
assert client._client_wrapper.get_custom_headers() is None


def test_sync_client_custom_headers_appear_in_merged_headers():
"""Headers from the constructor are merged into get_headers() output."""
client = ElevenLabs(api_key="sk-test", headers={"x-trace-id": "abc-123"})
merged = client._client_wrapper.get_headers()
assert merged.get("x-trace-id") == "abc-123"


async def test_async_client_accepts_headers_kwarg():
"""AsyncElevenLabs(headers=...) does not raise and stores the headers."""
custom = {"x-trace-id": "abc-123"}
client = AsyncElevenLabs(api_key="sk-test", headers=custom)
assert client._client_wrapper.get_custom_headers() == custom


async def test_async_client_headers_default_is_none():
"""Omitting headers on AsyncElevenLabs leaves custom headers unset."""
client = AsyncElevenLabs(api_key="sk-test")
assert client._client_wrapper.get_custom_headers() is None


def test_sync_constructor_signature_advertises_headers():
"""`headers` is a documented keyword-only parameter on ElevenLabs.__init__."""
import inspect

sig = inspect.signature(ElevenLabs.__init__)
assert "headers" in sig.parameters
assert sig.parameters["headers"].kind == inspect.Parameter.KEYWORD_ONLY


def test_async_constructor_signature_advertises_headers():
"""`headers` is a documented keyword-only parameter on AsyncElevenLabs.__init__."""
import inspect

sig = inspect.signature(AsyncElevenLabs.__init__)
assert "headers" in sig.parameters
assert sig.parameters["headers"].kind == inspect.Parameter.KEYWORD_ONLY


def test_sync_client_accepts_follow_redirects_false():
"""ElevenLabs(follow_redirects=False) propagates to the default httpx client."""
client = ElevenLabs(api_key="sk-test", follow_redirects=False)
assert client._client_wrapper.httpx_client.httpx_client.follow_redirects is False


def test_sync_client_follow_redirects_default_is_true():
"""Omitting follow_redirects keeps the parent's default of True."""
client = ElevenLabs(api_key="sk-test")
assert client._client_wrapper.httpx_client.httpx_client.follow_redirects is True


def test_async_client_accepts_follow_redirects_false():
"""AsyncElevenLabs(follow_redirects=False) propagates to the default httpx client."""
client = AsyncElevenLabs(api_key="sk-test", follow_redirects=False)
assert client._client_wrapper.httpx_client.httpx_client.follow_redirects is False


def test_async_client_follow_redirects_default_is_true():
"""Omitting follow_redirects on AsyncElevenLabs keeps the parent's default of True."""
client = AsyncElevenLabs(api_key="sk-test")
assert client._client_wrapper.httpx_client.httpx_client.follow_redirects is True


def test_sync_constructor_signature_advertises_follow_redirects():
"""`follow_redirects` is a documented keyword-only parameter on ElevenLabs.__init__."""
import inspect

sig = inspect.signature(ElevenLabs.__init__)
assert "follow_redirects" in sig.parameters
assert sig.parameters["follow_redirects"].kind == inspect.Parameter.KEYWORD_ONLY


def test_async_constructor_signature_advertises_follow_redirects():
"""`follow_redirects` is a documented keyword-only parameter on AsyncElevenLabs.__init__."""
import inspect

sig = inspect.signature(AsyncElevenLabs.__init__)
assert "follow_redirects" in sig.parameters
assert sig.parameters["follow_redirects"].kind == inspect.Parameter.KEYWORD_ONLY