Skip to content

Commit

Permalink
Merge pull request #174 from dsinghvi/v3
Browse files Browse the repository at this point in the history
🌿 Introduce V3 SDK (generated by Fern)
  • Loading branch information
dsinghvi authored Feb 2, 2024
2 parents ded0b10 + a938435 commit 697316e
Show file tree
Hide file tree
Showing 43 changed files with 1,100 additions and 272 deletions.
4 changes: 3 additions & 1 deletion .fernignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ src/elevenlabs/play.py
src/elevenlabs/tts.py
src/elevenlabs/voice.py
src/elevenlabs/generate.py
src/elevenlabs/clone.py
src/elevenlabs/resources/voices/client.py

README.md
assets/

tests/test_client.py
tests/
19 changes: 19 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Contributing

While we value open-source contributions to this SDK, this library is generated programmatically. Additions made directly to this library would have to be moved over to our generation code, otherwise they would be overwritten upon the next generated release. Feel free to open a PR as a proof of concept, but know that we will not be able to merge it as-is. We suggest opening an issue first to discuss with us!

On the other hand, contributions to the README are always very welcome!

### Tests

Run the following commands from the root of the repo

```sh
ELEVEN_API_KEY=<YOUR_API_KEY> poetry run pytest -sv
```

### Run a specific test
_e.g. `test_voice_design` in the `elevenlabs/tests/api/test_voice.py` file_
```sh
ELEVEN_API_KEY=<YOUR_API_KEY> poerty run pytest elevenlabs/tests/api/test_voice.py::test_voice_design -sv
```
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,38 @@ Check out the [HTTP API documentation](https://elevenlabs.io/docs/api-reference)
pip install elevenlabs
```

## V3 Migration Guide
> The SDK was rewritten in v3 and is now programatically generated from our OpenAPI spec. As part of this release
> there are some breaking changes.
- **Client Instantiation** The SDK now exports a client class that you must instantiate to call various
endpoints in our API.

```python
from elevenlabs.client import ElevenLabs

client = ElevenLabs(api_key="...")
```
As part of this change, there is no longer a `set_api_key` and `get_api_key` method exported.

- **HTTPX** The SDK now uses httpx under the hood. This allows us to export an async client in addition to
a synchronous client. Note that you can pass in your own httpx client as well.
```python
from elevenlabs.client import AsyncElevenLabs

client = AsyncElevenLabs(api_key="...", httpx=httpx.AsyncClient(...))
```
- **Static Methods** There are no longer static methods exposed directly on objects. For example,
instead of `Models.from_api()` you can now do `client.models.get_all()`.

`User.from_api()` -> `client.users.get()`
`Models.from_api()` -> `client.models.get_all()`
`Voices.from_api()` -> `client.voices.get_all()`
`History.from_api()` -> `client.history.get_all()`

- **Helper Methods** The SDK continues to export methods for `generate`, `play`, `clone`, and
`voices` which are detailed in the README below.

## 🗣️ Usage
[![Open in Spaces](https://img.shields.io/badge/🤗-Open%20in%20Spaces-blue.svg)](https://huggingface.co/spaces/elevenlabs/tts)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/gist/flavioschneider/49468d728a816c6538fd2f56b3b50b96/elevenlabs-python.ipynb)
Expand Down
50 changes: 35 additions & 15 deletions src/elevenlabs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# This file was auto-generated by Fern from our API Definition.

from .types import (
Accent,
AddProjectResponseModel,
AddPronunciationDictionaryResponseModel,
AddVoiceResponseModel,
Age,
AudioNativeCreateProjectResponseModel,
AudioNativeGetEmbedCodeResponseModel,
ChapterResponseModel,
Expand All @@ -12,25 +14,25 @@
ChapterSnapshotsResponseModel,
ChapterStatisticsResponseModel,
Currency,
ExtendedSubscriptionResponseModel,
ExtendedSubscriptionResponseModelCurrency,
FeedbackItem,
FineTuningResponseModel,
FinetuningState,
FinetunigState,
Gender,
GetChaptersResponseModel,
GetHistoryResponseModel,
GetProjectsResponseModel,
GetPronunciationDictionaryMetadataResponseModel,
GetVoicesResponseModel,
History,
HistoryItem,
HistoryItemState,
HttpValidationError,
InvoiceResponseModel,
InvoiceResponseModelCurrency,
ItemState,
LanguageResponseModel,
ManualVerificationFileResponseModel,
ManualVerificationResponseModel,
ModelResponse,
Model,
ProjectExtendedResponseModel,
ProjectResponseModel,
ProjectSnapshotResponseModel,
Expand All @@ -39,15 +41,16 @@
PronunciationDictionaryVersionLocatorDbModel,
RecordingResponseModel,
ReviewStatus,
SampleResponseModel,
Subscription,
SubscriptionResponse,
SubscriptionStatus,
UserResponse,
User,
ValidationError,
ValidationErrorLocItem,
VerificationAttemptResponseModel,
VoiceCategory,
VoiceResponse,
VoiceSample,
VoiceSettings,
VoiceSharingResponseModel,
VoiceSharingState,
Expand All @@ -62,14 +65,30 @@
samples,
text_to_speech,
user,
voices,
voices as voices_client,
)
from .environment import ElevenLabsEnvironment
from .environment import ElevenLabsEnvironment
from .clone import clone
from .generate import generate
from .play import play, save, stream
from .voice import Voice, VoiceClone, voices

__all__ = [
"clone",
"generate",
"Model",
"play",
"save",
"stream",
"Voice",
"VoiceClone",
"voices_client",
"Accent",
"AddProjectResponseModel",
"AddPronunciationDictionaryResponseModel",
"AddVoiceResponseModel",
"Age",
"AudioNativeCreateProjectResponseModel",
"AudioNativeGetEmbedCodeResponseModel",
"ChapterResponseModel",
Expand All @@ -79,25 +98,25 @@
"ChapterStatisticsResponseModel",
"Currency",
"ElevenLabsEnvironment",
"ExtendedSubscriptionResponseModel",
"ExtendedSubscriptionResponseModelCurrency",
"FeedbackItem",
"FineTuningResponseModel",
"FinetuningState",
"FinetunigState",
"Gender",
"GetChaptersResponseModel",
"GetHistoryResponseModel",
"GetProjectsResponseModel",
"GetPronunciationDictionaryMetadataResponseModel",
"GetVoicesResponseModel",
"History",
"HistoryItem",
"HistoryItemState",
"HttpValidationError",
"InvoiceResponseModel",
"InvoiceResponseModelCurrency",
"ItemState",
"LanguageResponseModel",
"ManualVerificationFileResponseModel",
"ManualVerificationResponseModel",
"ModelResponse",
"Model",
"ProjectExtendedResponseModel",
"ProjectResponseModel",
"ProjectSnapshotResponseModel",
Expand All @@ -106,16 +125,17 @@
"PronunciationDictionaryVersionLocatorDbModel",
"RecordingResponseModel",
"ReviewStatus",
"SampleResponseModel",
"Subscription",
"SubscriptionResponse",
"SubscriptionStatus",
"UnprocessableEntityError",
"UserResponse",
"User",
"ValidationError",
"ValidationErrorLocItem",
"VerificationAttemptResponseModel",
"VoiceCategory",
"VoiceResponse",
"VoiceSample",
"VoiceSettings",
"VoiceSharingResponseModel",
"VoiceSharingState",
Expand Down
28 changes: 23 additions & 5 deletions src/elevenlabs/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import typing

import httpx
import os

from .core.api_error import ApiError
from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from .environment import ElevenLabsEnvironment
from .resources.audio_native.client import AsyncAudioNativeClient, AudioNativeClient
Expand All @@ -23,13 +25,21 @@ def __init__(
*,
base_url: typing.Optional[str] = None,
environment: ElevenLabsEnvironment = ElevenLabsEnvironment.PRODUCTION,
xi_api_key: typing.Optional[str] = None,
api_key: typing.Optional[str] = os.getenv("ELEVEN_API_KEY"),
timeout: typing.Optional[float] = 60,
httpx_client: typing.Optional[httpx.Client] = None
):
"""
Args:
base_url typing.Optional[str]. Override the base URL.
environment ElevenLabsEnvironment. Defaults to PRODUCTION.
api_key typing.Optional[str]. Defaults to ELEVEN_API_KEY environment variable.
timeout typing.Optional[float]. Defaults to 60 seconds.
httpx_client typing.Optional[httpx.AsyncClient]. Override the httpx client.
"""
self._client_wrapper = SyncClientWrapper(
base_url=_get_base_url(base_url=base_url, environment=environment),
xi_api_key=xi_api_key,
xi_api_key=api_key,
httpx_client=httpx.Client(timeout=timeout) if httpx_client is None else httpx_client,
)
self.history = HistoryClient(client_wrapper=self._client_wrapper)
Expand All @@ -49,13 +59,21 @@ def __init__(
*,
base_url: typing.Optional[str] = None,
environment: ElevenLabsEnvironment = ElevenLabsEnvironment.PRODUCTION,
xi_api_key: typing.Optional[str] = None,
api_key: typing.Optional[str] = os.getenv("ELEVEN_API_KEY"),
timeout: typing.Optional[float] = 60,
httpx_client: typing.Optional[httpx.AsyncClient] = None
):
):
"""
Args:
base_url typing.Optional[str]. Override the base URL.
environment ElevenLabsEnvironment. Defaults to PRODUCTION.
api_key typing.Optional[str]. Defaults to ELEVEN_API_KEY environment variable.
timeout typing.Optional[float]. Defaults to 60 seconds.
httpx_client typing.Optional[httpx.AsyncClient]. Override the httpx client.
"""
self._client_wrapper = AsyncClientWrapper(
base_url=_get_base_url(base_url=base_url, environment=environment),
xi_api_key=xi_api_key,
xi_api_key=api_key,
httpx_client=httpx.AsyncClient(timeout=timeout) if httpx_client is None else httpx_client,
)
self.history = AsyncHistoryClient(client_wrapper=self._client_wrapper)
Expand Down
25 changes: 25 additions & 0 deletions src/elevenlabs/clone.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import json
from typing import Dict, List, Optional

from .client import ElevenLabs
from .voice import Voice


def clone(
name: str,
api_key: Optional[str] = None,
description: str = "",
files: List[str] = [],
labels: Optional[Dict[str, str]] = dict(),
) -> Voice:
client = ElevenLabs(api_key=api_key)
add_voice_response = client.voices.add(
name=name,
description=description,
files=[open(file, 'rb') for file in files],
labels=str(json.dumps(labels or {}))
)
voice = client.voices.get(voice_id=add_voice_response.voice_id)
return Voice(
**voice.dict()
)
6 changes: 1 addition & 5 deletions src/elevenlabs/core/client_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ def __init__(self, *, xi_api_key: typing.Optional[str] = None, base_url: str):
self._base_url = base_url

def get_headers(self) -> typing.Dict[str, str]:
headers: typing.Dict[str, str] = {
"X-Fern-Language": "Python",
"X-Fern-SDK-Name": "elevenlabs",
"X-Fern-SDK-Version": "v0.3.0",
}
headers: typing.Dict[str, str] = {"X-Fern-Language": "Python"}
if self._xi_api_key is not None:
headers["xi-api-key"] = self._xi_api_key
return headers
Expand Down
19 changes: 19 additions & 0 deletions src/elevenlabs/core/file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import (
IO,
Mapping,
Optional,
Tuple,
Union,
)

FileContent = Union[IO[bytes], bytes, str]
FileType = Union[
# file (or bytes)
FileContent,
# (filename, file (or bytes))
Tuple[Optional[str], FileContent],
# (filename, file (or bytes), content_type)
Tuple[Optional[str], FileContent, Optional[str]],
# (filename, file (or bytes), content_type, headers)
Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]],
]
10 changes: 4 additions & 6 deletions src/elevenlabs/generate.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import re

from typing import Union, Iterator, Optional
from typing import Iterator, Optional, Union

from .tts import TTS
from .model import Model
from .voice import Voice
from .types import VoiceSettings
from .client import voices
from .types import VoiceSettings, Model
from .voice import Voice, voices


DEFAULT_VOICE = Voice(
Expand Down Expand Up @@ -46,7 +44,7 @@ def generate(
raise ValueError(f"Voice '{voice_str}' not found.")

if isinstance(model, str):
model = Model(model_id=model)
model = Model(model_id=model) # type: ignore

if stream:
if isinstance(text, str):
Expand Down
13 changes: 0 additions & 13 deletions src/elevenlabs/model.py

This file was deleted.

1 change: 0 additions & 1 deletion src/elevenlabs/play.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ def stream(audio_stream: Iterator[bytes]) -> bytes:
mpv_process.stdin.write(chunk) # type: ignore
mpv_process.stdin.flush() # type: ignore
audio += chunk

if mpv_process.stdin:
mpv_process.stdin.close()
mpv_process.wait()
Expand Down
Loading

0 comments on commit 697316e

Please sign in to comment.