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
2 changes: 1 addition & 1 deletion assemblyai/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.67.00"
__version__ = "0.68.00"
52 changes: 45 additions & 7 deletions assemblyai/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing_extensions import Self

from . import types
from .client import _build_headers, _build_limits
from .client import _MISSING_API_KEY_ERROR, _build_headers, _build_limits


class AsyncClient:
Expand All @@ -32,24 +32,30 @@ class AsyncClient:
def __init__(
self,
*,
settings: types.Settings,
settings: Optional[types.Settings] = None,
api_key: Optional[str] = None,
api_key_required: bool = True,
) -> None:
"""
Creates the asyncio AssemblyAI client.

Args:
settings: The settings to use for the client.
settings: The settings to use for the client. If `None` is given, the global
settings are used. The client holds a copy, so the given settings object
is never modified.
api_key: The API key to authenticate with. Overrides the key on `settings`.
api_key_required: If an API key is required (either as environment variable or the global settings).
Can be set to `False` if a different authentication method is used, e.g., a temporary token.
"""
from . import settings as default_settings

self._settings = settings.copy()
self._settings = (settings if settings is not None else default_settings).copy()

if api_key is not None:
self._settings.api_key = api_key

if api_key_required and not self._settings.api_key:
raise ValueError(
"Please provide an API key via the ASSEMBLYAI_API_KEY environment variable or the global settings."
)
raise ValueError(_MISSING_API_KEY_ERROR)

self._last_response: Optional[httpx.Response] = None

Expand Down Expand Up @@ -111,3 +117,35 @@ async def __aexit__(
traceback: Optional[TracebackType],
) -> None:
await self.aclose()


def _resolve_client(
client: Optional[AsyncClient],
api_key: Optional[str],
) -> AsyncClient:
"""
Returns the client a transcriber sends its requests with.

`api_key` takes precedence. A client's credentials are baked into its
connection pool when it is built, so a key given alongside a `client`
derives a new client from a copy of that client's settings rather than
changing anything on it. The given client is left untouched and unused.

A client built here is owned by the transcriber, which closes it. Only the
caller's `client`, passed without an `api_key`, stays the caller's to close.

Args:
client: An explicit `AsyncClient`, or `None`.
api_key: An API key, or `None`.
"""

if client is not None:
if api_key is not None:
# `AsyncClient.__init__` copies the settings it is given.
return AsyncClient(settings=client.settings, api_key=api_key)

return client

from . import settings as default_settings

return AsyncClient(settings=default_settings, api_key=api_key)
57 changes: 51 additions & 6 deletions assemblyai/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,43 @@ def _build_limits(settings: types.Settings) -> httpx.Limits:
)


_MISSING_API_KEY_ERROR = (
"Please provide an API key: set the ASSEMBLYAI_API_KEY environment variable, "
"set aai.settings.api_key, or pass api_key= to the transcriber or client."
)


class Client:
_default: ClassVar[Optional["Client"]] = None
_lock: ClassVar[threading.Lock] = threading.Lock()

def __init__(
self,
*,
settings: types.Settings,
settings: Optional[types.Settings] = None,
api_key: Optional[str] = None,
api_key_required: bool = True,
) -> None:
"""
Creates the AssemblyAI client.

Args:
settings: The settings to use for the client.
settings: The settings to use for the client. If `None` is given, the global
settings are used. The client holds a copy, so the given settings object
is never modified.
api_key: The API key to authenticate with. Overrides the key on `settings`.
api_key_required: If an API key is required (either as environment variable or the global settings).
Can be set to `False` if a different authentication method is used, e.g., a temporary token.
"""
from . import settings as default_settings

self._settings = (settings if settings is not None else default_settings).copy()

self._settings = settings.copy()
if api_key is not None:
self._settings.api_key = api_key

if api_key_required and not self._settings.api_key:
raise ValueError(
"Please provide an API key via the ASSEMBLYAI_API_KEY environment variable or the global settings."
)
raise ValueError(_MISSING_API_KEY_ERROR)

self._last_response: Optional[httpx.Response] = None

Expand Down Expand Up @@ -128,3 +140,36 @@ def get_default(cls, api_key_required: bool = True):
)

return cls._default


def _resolve_client(
client: Optional[Client],
api_key: Optional[str],
) -> Client:
"""
Returns the client a transcriber sends its requests with.

`api_key` takes precedence. A client's credentials are baked into its
connection pool when it is built, so a key given alongside a `client`
derives a new client from a copy of that client's settings rather than
changing anything on it. The given client is left untouched and unused.

A client returned here is the transcriber's own except when it is the
caller's `client` passed without an `api_key`.

Args:
client: An explicit `Client`, or `None`.
api_key: An API key, or `None`.
"""

if client is not None:
if api_key is not None:
# `Client.__init__` copies the settings it is given.
return Client(settings=client.settings, api_key=api_key)

return client

if api_key is not None:
return Client(api_key=api_key)

return Client.get_default()
13 changes: 9 additions & 4 deletions assemblyai/prerecorded/v2/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def __init__(
*,
client: Optional[_async_client.AsyncClient] = None,
config: Optional[types.TranscriptionConfig] = None,
api_key: Optional[str] = None,
) -> None:
"""
Initializes the `AsyncTranscriber` with the given parameters.
Expand All @@ -181,11 +182,15 @@ def __init__(
transcribers.
config: The default configuration for the `AsyncTranscriber`. If
`None`, a default `TranscriptionConfig` is used.
api_key: The API key to authenticate with. The transcriber builds
its own `AsyncClient` from it and closes that client on
`aclose()`. Given alongside `client`, it takes precedence: the
transcriber builds and owns a client made from a copy of that
client's settings with the key replaced, and the given client is
left untouched and stays the caller's to close.
"""
from ... import settings as default_settings

self._owns_client = client is None
self._client = client or _async_client.AsyncClient(settings=default_settings)
self._owns_client = client is None or api_key is not None
self._client = _async_client._resolve_client(client, api_key)
self.config = config or types.TranscriptionConfig()

@property
Expand Down
8 changes: 7 additions & 1 deletion assemblyai/prerecorded/v2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ def __init__(
client: Optional[_client.Client] = None,
config: Optional[types.TranscriptionConfig] = None,
max_workers: Optional[int] = None,
api_key: Optional[str] = None,
) -> None:
"""
Initializes the `Transcriber` with the given parameters.
Expand All @@ -190,6 +191,11 @@ def __init__(
the default configuration of a `TranscriptionConfig` will be used.
`max_workers`: The maximum number of parallel jobs when using the `_async`
methods on the `Transcriber`. By default it uses `os.cpu_count() - 1`
`api_key`: The API key to authenticate with. Builds a `Client` for this
`Transcriber`. Given alongside `client`, it takes precedence: the
`Transcriber` builds its own client from a copy of that client's
settings with the key replaced, and the given client is left
untouched.

Example:
To use the `Transcriber` with the default settings, you can simply do:
Expand All @@ -204,7 +210,7 @@ def __init__(
transcriber = aai.Transcriber(config=config)
```
"""
self._client = client or _client.Client.get_default()
self._client = _client._resolve_client(client, api_key)

self._impl = _TranscriberImpl(
client=self._client,
Expand Down
40 changes: 40 additions & 0 deletions assemblyai/streaming/v3/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,46 @@ def _build_headers(options: StreamingClientOptions) -> Dict[str, Optional[str]]:
}


def _resolve_options(
options: Optional[StreamingClientOptions],
api_key: Optional[str],
) -> StreamingClientOptions:
"""Returns the options a streaming client is configured with.

``api_key`` takes precedence: given alongside ``options``, it replaces
the key on a copy of ``options`` and every other field is carried over
as the caller set it. The caller's ``options`` object is never mutated.

Args:
``options``: an explicit ``StreamingClientOptions``, or ``None``.
Returned as-is when no ``api_key`` accompanies it.
``api_key``: an API key, or ``None``. On its own it builds options
with every other field left at its default.

Raises:
ValueError: if neither is given.
"""

if options is not None:
if api_key is not None:
# pydantic v2 renamed `copy(update=...)` to `model_copy(update=...)`.
if hasattr(options, "model_copy"):
return options.model_copy(update={"api_key": api_key})

return options.copy(update={"api_key": api_key})

return options

if api_key is not None:
return StreamingClientOptions(api_key=api_key)

raise ValueError(
"Please provide credentials: pass api_key= to the client, or pass "
"options=StreamingClientOptions(api_key=...) — or "
"options=StreamingClientOptions(token=...) for a temporary token."
)


class _BaseStreamingClient:
"""Sync/async-agnostic core for streaming clients.

Expand Down
24 changes: 23 additions & 1 deletion assemblyai/streaming/v3/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
_dump_model_json,
_emit_param_warnings,
_normalize_min_turn_silence,
_resolve_options,
_user_agent,
)
from .models import (
Expand Down Expand Up @@ -94,7 +95,28 @@ class AsyncStreamingClient(_BaseStreamingClient):
raises.
"""

def __init__(self, options: StreamingClientOptions):
def __init__(
self,
options: Optional[StreamingClientOptions] = None,
*,
api_key: Optional[str] = None,
):
"""Create an asyncio streaming client.

Args:
``options``: the full client configuration — credentials, host,
timeouts, retries.
``api_key``: the API key to authenticate with. On its own it
builds ``StreamingClientOptions`` with every other option left
at its default. Passed alongside ``options`` it takes
precedence, replacing the key while every other field is
carried over; the ``options`` object itself is left untouched.

Raises:
ValueError: if neither ``options`` nor ``api_key`` is given.
"""
options = _resolve_options(options, api_key)

super().__init__(options)

self._client = _AsyncHTTPClient(
Expand Down
24 changes: 23 additions & 1 deletion assemblyai/streaming/v3/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
_dump_model_json,
_emit_param_warnings,
_normalize_min_turn_silence,
_resolve_options,
_user_agent,
)
from .models import (
Expand All @@ -41,7 +42,28 @@


class StreamingClient(_BaseStreamingClient):
def __init__(self, options: StreamingClientOptions):
def __init__(
self,
options: Optional[StreamingClientOptions] = None,
*,
api_key: Optional[str] = None,
):
"""Create a streaming client.

Args:
``options``: the full client configuration — credentials, host,
timeouts, retries.
``api_key``: the API key to authenticate with. On its own it
builds ``StreamingClientOptions`` with every other option left
at its default. Passed alongside ``options`` it takes
precedence, replacing the key while every other field is
carried over; the ``options`` object itself is left untouched.

Raises:
ValueError: if neither ``options`` nor ``api_key`` is given.
"""
options = _resolve_options(options, api_key)

super().__init__(options)

self._client = _HTTPClient(api_host=options.api_host, api_key=options.api_key)
Expand Down
13 changes: 9 additions & 4 deletions assemblyai/sync/v1/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def __init__(
*,
client: Optional[_async_client.AsyncClient] = None,
config: Optional[types.SyncTranscriptionConfig] = None,
api_key: Optional[str] = None,
) -> None:
"""
Creates an `AsyncSyncTranscriber`.
Expand All @@ -81,11 +82,15 @@ def __init__(
transcribers.
config: Default transcription options. Per-call `config`
overrides it.
api_key: The API key to authenticate with. The transcriber builds
its own `AsyncClient` from it and closes that client on
`aclose()`. Given alongside `client`, it takes precedence: the
transcriber builds and owns a client made from a copy of that
client's settings with the key replaced, and the given client is
left untouched and stays the caller's to close.
"""
from ... import settings as default_settings

self._owns_client = client is None
self._client = client or _async_client.AsyncClient(settings=default_settings)
self._owns_client = client is None or api_key is not None
self._client = _async_client._resolve_client(client, api_key)
self.config = config or types.SyncTranscriptionConfig()

@property
Expand Down
Loading
Loading