Skip to content
This repository has been archived by the owner on Feb 6, 2024. It is now read-only.

Commit

Permalink
Merge pull request #122 from aflorithmic/org-resource
Browse files Browse the repository at this point in the history
Organization resource
  • Loading branch information
zeritte authored Sep 12, 2022
2 parents 6922191 + c1d4b59 commit 8220ecb
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 133 deletions.
174 changes: 87 additions & 87 deletions CHANGELOG.md

Large diffs are not rendered by default.

58 changes: 45 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- [Authentication with environment variable](#authentication_env)
- [Super Organizations](#super-organizations)
- [Resource usage](#resource)
- [Organization](#organization)
- [Script](#script)
- [Speech](#speech)
- [Voice](#voice)
Expand Down Expand Up @@ -201,6 +202,8 @@ apiaudio.set_assume_org_id('child_org_id')
apiaudio.set_assume_org_id(None)
```

See [organization](#organization) resource for more operations you can perform about your organization.

### Resource Usage <a name = "resource"> </a>

There are two approaches to use the resources.
Expand All @@ -220,6 +223,34 @@ apiaudio.Script.create()

Same logic applies for other resources (`Speech`, `Voice`, `Sound`...)

### `Organization` resource <a name = "organization"> </a>

The Organization resource/class allows you to perform some data retrieval about your organization and your child organizations.

Organization methods are:

- `get_org_data()` - Get organizations data, including orgId, orgName etc.
- Parameters:
- None.
- Example:
```python
org_data = apiaudio.Organization.get_org_data()
```
- `list_child_orgs()` - List your child organizations.
- Parameters:
- None.
- Example:
```python
child_orgs = apiaudio.Organization.list_child_orgs()
```
- `get_secrets()` - Get your api key, webhook url and webhook secret.
- Parameters:
- None.
- Example:
```python
secrets = apiaudio.Organization.get_secrets()
```

### `Script` resource <a name = "script"> </a>

The Script resource/class allows you to create, retrieve and list scripts. Learn more about scripts [here](https://docs.api.audio/docs/script-2).
Expand Down Expand Up @@ -683,18 +714,18 @@ Birdcache methods are:

Often when working with TTS, the models can fail to accurately pronounce specific words, for example brands, names and locations are commonly mis-pronounced. As a first attempt to fix this we have introduced our lexi flag, which works in a similar way to SSML. For example, adding <!peadar> instead of Peadar (who is one of our founders) to your script will cause the model to produce an alternative pronunciation of this name. This is particularly useful in cases where words can have multiple pronunciations, for example the cities ‘reading’ and ‘nice’. In this instance placing <!reading> and <!nice> will ensure that these are pronounced correctly, given the script:

```" The city of <!nice> is a really nice place in the south of france."```

If this solution does not work for you, you can instead make use of our custom (self-serve) lexi feature.
`" The city of <!nice> is a really nice place in the south of france."`

This can be used to achieve one of two things, correcting single words, or expanding acronyms. For example, you can replace all occurrences of the word Aflorithmic with “af low rhythmic” or occurrences of the word ‘BMWwith “Bayerische Motoren Werke”. Replacement words can be supplied as plain text or an IPA phonemisation.
If this solution does not work for you, you can instead make use of our custom (self-serve) lexi feature.

This can be used to achieve one of two things, correcting single words, or expanding acronyms. For example, you can replace all occurrences of the word Aflorithmic with “af low rhythmic” or occurrences of the word ‘BMWwith “Bayerische Motoren Werke”. Replacement words can be supplied as plain text or an IPA phonemisation.

Prononciation dictionary methods are:

- `list()` Lists the publicly available dictionaries and their words

- Parameters:

- `none`

- Example:
Expand All @@ -716,23 +747,25 @@ Prononciation dictionary methods are:
types = apiaudio.Lexi.list_custom_dicts()

```

- `register_custom_word` Adds a new word to a custom dictionary.

- `lang` [required] (string) - Language family, e.g. `en` or `es`.dictionary - use `global` to register a word globally.
- `word` [required] (string) - The word that will be replaced
- `replacement` [required] (string) - The replacement token. Can be either a plain string or a IPA token.
- `contentType` [optional] (string) - The content type of the supplied replacement, can be either `basic` (default) or `ipa` for phonetic replacements.
- `specialization` [optional] (string) - by default the supplied replacement will apply regardless of the supplied voice, language code or provider. However edge cases can be supplied, these can be either a valid; provider name, language code (i.e. en-gb) or voice name.
-
- `word` [required] (string) - The word that will be replaced
- `replacement` [required] (string) - The replacement token. Can be either a plain string or a IPA token.
- `contentType` [optional] (string) - The content type of the supplied replacement, can be either `basic` (default) or `ipa` for phonetic replacements.
- `specialization` [optional] (string) - by default the supplied replacement will apply regardless of the supplied voice, language code or provider. However edge cases can be supplied, these can be either a valid; provider name, language code (i.e. en-gb) or voice name.
-
- Example:
```python
# correct the word sapiens
r = apiaudio.Lexi.register_custom_word(word="sapiens", replacement="saypeeoons", lang="en")
print(r)
```

For each language, only a single word entry is permitted. However, each word can have multiple `specializations`. When a word is first registered a `default` `specialization` is always created, which will match what is passed in. Subsequent calls with different specializations will only update the given specialization. The exact repacement that will be used is determined by the following order of preference:

``` voice name > language dialect > provider name > default```
` voice name > language dialect > provider name > default`

For example, a replacement specified for voice name `sara` will be picked over a replacement specified for provider `azure`.

Expand All @@ -747,7 +780,6 @@ Prononciation dictionary methods are:
words = apiaudio.Lexi.list_custom_words(lang="en")
```


#### Preview

The effect of applying the Pronunciation Dictionary can be seen with the `script.preview()` method. See [Script](#script) documentation for more details.
Expand All @@ -772,7 +804,7 @@ The effect of applying the Pronunciation Dictionary can be seen with the `script
```python
{"preview" : "The author of this repo has lived in two places in the UK, bude and <phoneme alphabet=\"ipa\" ph=\"###\"> bristol </phoneme>"}
```
In this example `Bristol` will be phonemised to ensure it is correctly pronouced, but as `Bude` is not in our a dictionaires it is left as is. The exact IPA tokens for words in our internal dictionaires are obsfucated.
In this example `Bristol` will be phonemised to ensure it is correctly pronouced, but as `Bude` is not in our a dictionaires it is left as is. The exact IPA tokens for words in our internal dictionaires are obsfucated.

### `Connector` resource <a name = "connector"> </a>

Expand Down
2 changes: 1 addition & 1 deletion apiaudio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# Configuration variables

sdk_version = "0.16.6"
sdk_version = "0.16.7"

api_key = None
assume_org_id = None
Expand Down
4 changes: 2 additions & 2 deletions apiaudio/api_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ def _put_request_s3(cls, data, url=None, headers=None):
# since aws s3 does not return a body on PUT requests,
# r.json() does not work here
return r

@classmethod
def _put_request(cls, json, url=None, headers=None):
headers = headers or {}
url = url or f"{apiaudio.api_base}{cls.resource_path}"
headers.update(cls._build_header())

r = requests.put(url=url, headers=headers, json=json)

cls._expanded_raise_for_status(r)
Expand Down
1 change: 1 addition & 0 deletions apiaudio/api_resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
from apiaudio.api_resources.dictionary import Lexi
from apiaudio.api_resources.orchestrator import Orchestrator
from apiaudio.api_resources.webhooks import Webhooks
from apiaudio.api_resources.organization import Organization
29 changes: 18 additions & 11 deletions apiaudio/api_resources/dictionary.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,49 @@
from apiaudio.helper_classes import UpdatableResource, ListableResource, DeletableResource
from apiaudio.helper_classes import (
UpdatableResource,
ListableResource,
DeletableResource,
)


class LexiItem(UpdatableResource, ListableResource, DeletableResource):
OBJECT_NAME = "diction/custom/item"
resource_path = "/diction/custom/item"


class CustomDict(ListableResource):
OBJECT_NAME = "diction/custom"
resource_path = "/diction/custom"


class Lexi(ListableResource):
OBJECT_NAME = "diction"
resource_path = "/diction"

custom_word_path = resource_path + "/custom/item"
list_words = resource_path + "/custom"

@classmethod
def register_custom_word(cls, word, replacement, lang, specialization="default", contentType="basic"):
def register_custom_word(
cls, word, replacement, lang, specialization="default", contentType="basic"
):
return LexiItem.update(
**{
"word" : word,
"replacement" : replacement,
"lang" : lang,
"specialization" : specialization,
"contentType" : contentType
"word": word,
"replacement": replacement,
"lang": lang,
"specialization": specialization,
"contentType": contentType,
}
)

@classmethod
def list_custom_words(cls, **args):
return LexiItem.list(**args)

@classmethod
def list_custom_dicts(cls, **args):
return CustomDict.list(**args)

@classmethod
def delete_custom_word(cls, **args):
return LexiItem.delete(**args)

20 changes: 20 additions & 0 deletions apiaudio/api_resources/organization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from apiaudio.api_request import APIRequest


class Organization(APIRequest):
OBJECT_NAME = "organization"
resource_path = "/org"
child_orgs_path = "/org/child_orgs"
secrets_path = "/secrets"

@classmethod
def get_secrets(cls):
return cls._get_request(path_param=cls.secrets_path)

@classmethod
def get_org_data(cls):
return cls._get_request()

@classmethod
def list_child_orgs(cls):
return cls._get_request(path_param=cls.child_orgs_path)
8 changes: 2 additions & 6 deletions apiaudio/api_resources/sound.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
from apiaudio.helper_classes import (
ListableResource
)
from apiaudio.helper_classes import ListableResource


class Sound(
ListableResource
):
class Sound(ListableResource):
OBJECT_NAME = "sound"
resource_path = "/sound"
list_path = "/sound/template"
Expand Down
1 change: 1 addition & 0 deletions apiaudio/helper_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class CreatableResource(APIRequest):
def create(cls, **params):
return cls._post_request(json=params)


class UpdatableResource(APIRequest):
@classmethod
def update(cls, **params):
Expand Down
18 changes: 5 additions & 13 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from time import time

apiaudio.api_key = os.environ["AFLR_API_KEY"]
#apiaudio.api_base = "https://staging-v1.api.audio"
# apiaudio.api_base = "https://staging-v1.api.audio"


def test_level_setting():
Expand Down Expand Up @@ -75,29 +75,21 @@ def test_script_versions():


def test_synctts():
audio = apiaudio.SyncTTS.create(
text="Hello, test 123!",
voice="joanna"
)
audio = apiaudio.SyncTTS.create(text="Hello, test 123!", voice="joanna")
assert isinstance(audio, bytes)
assert b"RIFF" in audio

d = apiaudio.SyncTTS.create(
text="Hello, test 123.",
voice="joanna",
url=True
)
d = apiaudio.SyncTTS.create(text="Hello, test 123.", voice="joanna", url=True)
assert isinstance(d, dict)
assert "api.audio" in d.get("url", [])

mp3_audio = apiaudio.SyncTTS.create(
text="Hello, test 123.",
voice="joanna",
format="mp3"
text="Hello, test 123.", voice="joanna", format="mp3"
)
assert isinstance(mp3_audio, bytes)
assert not b"RIFF" in mp3_audio


def test_processing_loop_speech():
t0 = time()
speech = apiaudio.Speech.create(scriptId="longProcessing", voice="Dieter")
Expand Down

0 comments on commit 8220ecb

Please sign in to comment.