socaity 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- socaity/__init__.py +3 -0
- socaity/api/__init__.py +18 -0
- socaity/api/audio/__init__.py +1 -0
- socaity/api/audio/bark.py +89 -0
- socaity/api/simple_api.py +13 -0
- socaity/core/__init__.py +6 -0
- socaity/core/client/__init__.py +4 -0
- socaity/core/client/client.py +53 -0
- socaity/core/client/client_factory.py +46 -0
- socaity/core/client/local_client.py +104 -0
- socaity/core/client/remote_client.py +104 -0
- socaity/core/client_api.py +66 -0
- socaity/core/endpoint.py +73 -0
- socaity/core/job.py +85 -0
- socaity/globals.py +13 -0
- socaity/registry/__init__.py +1 -0
- socaity/registry/endpoints.py +30 -0
- socaity/registry/registry.py +77 -0
- socaity/utils/__init__.py +0 -0
- socaity/utils/audio.py +15 -0
- socaity/utils/utils.py +39 -0
- socaity-0.0.0.dist-info/LICENSE +674 -0
- socaity-0.0.0.dist-info/METADATA +11 -0
- socaity-0.0.0.dist-info/RECORD +26 -0
- socaity-0.0.0.dist-info/WHEEL +5 -0
- socaity-0.0.0.dist-info/top_level.txt +1 -0
socaity/__init__.py
ADDED
socaity/api/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The references to ClientAPIs and SimpleAPIs
|
|
3
|
+
|
|
4
|
+
# CLIENT APIs
|
|
5
|
+
A ClientAPI is a class that contains the logic to pre and post_process a request to an endpoint.
|
|
6
|
+
It is a wrapper around the client and job classes.
|
|
7
|
+
Instead of using clients and jobs directly, you can use the predefined "model" ClientAPI classes to do the work.
|
|
8
|
+
|
|
9
|
+
# SIMPLE APIs
|
|
10
|
+
A SimpleAPI is a wrapper around a ClientAPI.
|
|
11
|
+
It initiates the ClientAPI with the correct endpoint and model name, and provides a simple function to run the job.
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
# Defined ClientAPIs
|
|
15
|
+
from .audio import *
|
|
16
|
+
|
|
17
|
+
# This references to all simple api functions
|
|
18
|
+
from .simple_api import *
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .bark import Bark
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from socaity.core.job import Job
|
|
4
|
+
from socaity.core.client_api import ClientAPI
|
|
5
|
+
from socaity.globals import ModelType, EndPointType
|
|
6
|
+
from socaity.utils.audio import audio_from_bytes
|
|
7
|
+
from socaity.utils.utils import get_function_parameters_as_dict
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Bark(ClientAPI):
|
|
11
|
+
"""
|
|
12
|
+
With Bark (adapted by socAIty) you can:
|
|
13
|
+
- Convert text to natural sounding speech with predefined speakers in 100+ languages.
|
|
14
|
+
- Clone voices by creating embeddings from audio files
|
|
15
|
+
- Convert voice2voice aka voice cloning
|
|
16
|
+
|
|
17
|
+
For available speakers visit:
|
|
18
|
+
- Official page of suno: https://suno-ai.notion.site/8b8e8749ed514b0cbf3f699013548683?v=bc67cff786b04b50b3ceb756fd05f68c
|
|
19
|
+
- For other speakers visit the socaity references.
|
|
20
|
+
|
|
21
|
+
Bark is developed by Suno.ai. With this client you interact with the Bark adaptation of socAIty.
|
|
22
|
+
For the repos visit: https://github.com/SocAIty/BarkVoiceCloneREST
|
|
23
|
+
"""
|
|
24
|
+
def __init__(self, endpoint_type: Union[EndPointType, str] = EndPointType.REMOTE):
|
|
25
|
+
super().__init__(
|
|
26
|
+
model_type=ModelType.TEXT2VOICE,
|
|
27
|
+
model_name="bark",
|
|
28
|
+
endpoint_type=endpoint_type
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def validate_params(
|
|
32
|
+
self,
|
|
33
|
+
text: str | list,
|
|
34
|
+
voice_name_or_embedding_path: str = "en_speaker_3",
|
|
35
|
+
*args, **kwargs) -> (bool, str):
|
|
36
|
+
"""
|
|
37
|
+
Method is executed before a job is created.
|
|
38
|
+
Returns True if the parameters are valid, otherwise False.
|
|
39
|
+
"""
|
|
40
|
+
return True, None
|
|
41
|
+
|
|
42
|
+
def _pre_process(self, text: str | list, *args, **kwargs):
|
|
43
|
+
# add text again to args
|
|
44
|
+
_kwargs = get_function_parameters_as_dict(self.run, locals(), kwargs)
|
|
45
|
+
|
|
46
|
+
# attempt to make it batched.
|
|
47
|
+
if isinstance(text, str):
|
|
48
|
+
_kwargs["text"] = [text]
|
|
49
|
+
|
|
50
|
+
return _kwargs
|
|
51
|
+
|
|
52
|
+
def _post_process(self, result, *args, **kwargs):
|
|
53
|
+
return audio_from_bytes(result, save_file_path=None)
|
|
54
|
+
|
|
55
|
+
def __call__(
|
|
56
|
+
self,
|
|
57
|
+
text: str | list,
|
|
58
|
+
voice_name_or_embedding_path: str = "en_speaker_3",
|
|
59
|
+
semantic_temp=0.7,
|
|
60
|
+
semantic_top_k=50,
|
|
61
|
+
semantic_top_p=0.95,
|
|
62
|
+
coarse_temp=0.7,
|
|
63
|
+
coarse_top_k=50,
|
|
64
|
+
coarse_top_p=0.95,
|
|
65
|
+
fine_temp=0.5,
|
|
66
|
+
**kwargs
|
|
67
|
+
):
|
|
68
|
+
# just reuse args to easy pass them to super
|
|
69
|
+
_kwargs = get_function_parameters_as_dict(self.run, locals(), kwargs)
|
|
70
|
+
|
|
71
|
+
return super().__call__(**_kwargs)
|
|
72
|
+
|
|
73
|
+
def run(
|
|
74
|
+
self,
|
|
75
|
+
text: str | list,
|
|
76
|
+
voice_name_or_embedding_path: str = "en_speaker_3",
|
|
77
|
+
semantic_temp=0.7,
|
|
78
|
+
semantic_top_k=50,
|
|
79
|
+
semantic_top_p=0.95,
|
|
80
|
+
coarse_temp=0.7,
|
|
81
|
+
coarse_top_k=50,
|
|
82
|
+
coarse_top_p=0.95,
|
|
83
|
+
fine_temp=0.5,
|
|
84
|
+
**kwargs
|
|
85
|
+
) -> Job:
|
|
86
|
+
# just reuse args to easy pass them to super
|
|
87
|
+
_kwargs = get_function_parameters_as_dict(self.run, locals(), kwargs)
|
|
88
|
+
return self.__call__(**_kwargs)
|
|
89
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from socaity.api.audio import Bark
|
|
2
|
+
from socaity.globals import EndPointType
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def text2speech(text, *args, **kwargs) -> (bytes, int):
|
|
6
|
+
bark = Bark(endpoint_type=EndPointType.REMOTE)
|
|
7
|
+
return bark.run(text, *args, **kwargs)
|
|
8
|
+
|
|
9
|
+
def face2face():
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
def voice2voice():
|
|
13
|
+
pass
|
socaity/core/__init__.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from socaity.core.endpoint import EndPoint
|
|
2
|
+
from socaity.core.job import Job
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
class Client:
|
|
6
|
+
"""
|
|
7
|
+
A Client handles the requests to an API.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, endpoint: EndPoint):
|
|
11
|
+
self.endpoint = endpoint
|
|
12
|
+
|
|
13
|
+
self._job_queue = []
|
|
14
|
+
self._results = []
|
|
15
|
+
|
|
16
|
+
def __call__(self, *args, **kwargs):
|
|
17
|
+
return self.run(*args, **kwargs)
|
|
18
|
+
|
|
19
|
+
def prepare_payload(self, job: Job) -> Job:
|
|
20
|
+
"""
|
|
21
|
+
Subclass this method to implement your own payload preparation.
|
|
22
|
+
This is for example needed in web requests to have post, get and file parameters.
|
|
23
|
+
"""
|
|
24
|
+
return job
|
|
25
|
+
|
|
26
|
+
def request(self, job: Job):
|
|
27
|
+
"""
|
|
28
|
+
The method to make the request to the API.
|
|
29
|
+
Subclass this method.
|
|
30
|
+
"""
|
|
31
|
+
raise NotImplementedError("Subclass the request method")
|
|
32
|
+
|
|
33
|
+
def run(self, job: Job, *args, **kwargs) -> Job:
|
|
34
|
+
"""
|
|
35
|
+
Run the job.
|
|
36
|
+
"""
|
|
37
|
+
# call pre_process function of client api (which is referenced in the job)
|
|
38
|
+
job.pre_process_params()
|
|
39
|
+
# obtain post, get, and files for web request
|
|
40
|
+
self.prepare_payload(job)
|
|
41
|
+
# make the request
|
|
42
|
+
print(f"Requesting {self.endpoint.endpoint_name} with job {job.job_statistics.created}")
|
|
43
|
+
endpoint_result = self.request(job) # result is also stored in the job
|
|
44
|
+
# call post_process function of client api
|
|
45
|
+
job.post_process_result(endpoint_result)
|
|
46
|
+
print(f"Job {job.job_statistics.created} finished in {job.job_statistics.get_execution_time()} seconds.")
|
|
47
|
+
return job
|
|
48
|
+
|
|
49
|
+
def run_async(self):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from socaity.core.client.local_client import LocalClient
|
|
4
|
+
from socaity.core.client.remote_client import RemoteClient
|
|
5
|
+
from socaity.globals import ModelType, EndPointType
|
|
6
|
+
from socaity.registry.registry import get_endpoint, ACTIVE_CLIENT_REGISTRY
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def create_client(
|
|
10
|
+
model_type: Union[ModelType, str, None] = None,
|
|
11
|
+
model_name: str = None,
|
|
12
|
+
endpoint_type: Union[EndPointType, str] = EndPointType.REMOTE,
|
|
13
|
+
provider: str = "socaity",
|
|
14
|
+
) -> Union[LocalClient, RemoteClient]:
|
|
15
|
+
"""
|
|
16
|
+
Create a client to interact with an API.
|
|
17
|
+
:param model_type: for example "text2speech"
|
|
18
|
+
:param model_name: for example "bark"
|
|
19
|
+
:param endpoint_type: for example "remote"
|
|
20
|
+
:param provider: for example "socaity"
|
|
21
|
+
:return: a client to interact with the API
|
|
22
|
+
"""
|
|
23
|
+
clients = {
|
|
24
|
+
EndPointType.LOCAL: LocalClient,
|
|
25
|
+
EndPointType.REMOTE: RemoteClient
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
endpoint = get_endpoint(model_type=model_type, model_name=model_name, endpoint_type=endpoint_type, provider=provider)
|
|
29
|
+
|
|
30
|
+
# check if the client is already created and return from the registry if it is the case
|
|
31
|
+
client = ACTIVE_CLIENT_REGISTRY.get_client(endpoint, default_return_value=None)
|
|
32
|
+
if client is not None:
|
|
33
|
+
return client
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# create a new client
|
|
37
|
+
if endpoint.endpoint_type in clients:
|
|
38
|
+
client = clients[endpoint.endpoint_type](endpoint)
|
|
39
|
+
else:
|
|
40
|
+
print(f"Endpoint type {endpoint_type} not supported. Defaulting to remote.")
|
|
41
|
+
client = RemoteClient(endpoint)
|
|
42
|
+
|
|
43
|
+
# add the client to the registry
|
|
44
|
+
ACTIVE_CLIENT_REGISTRY.add_active_client(client)
|
|
45
|
+
|
|
46
|
+
return client
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import os
|
|
3
|
+
from subprocess import Popen, CREATE_NEW_CONSOLE
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
from socaity.core.job import Job
|
|
7
|
+
from socaity.core.client.remote_client import RemoteClient, web_request
|
|
8
|
+
from socaity.core.endpoint import LocalEndPoint
|
|
9
|
+
|
|
10
|
+
class LocalServerStatus(Enum):
|
|
11
|
+
OK = "ok"
|
|
12
|
+
NOT_OK = "not ok"
|
|
13
|
+
TIMEOUT = "timeout"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LocalClient(RemoteClient):
|
|
18
|
+
def __init__(self, endpoint: LocalEndPoint,
|
|
19
|
+
start_server_from_bat_file: bool = True,
|
|
20
|
+
server_not_reachable_timeout: int = 60
|
|
21
|
+
):
|
|
22
|
+
super().__init__(endpoint)
|
|
23
|
+
self.endpoint = endpoint
|
|
24
|
+
self.is_running = False
|
|
25
|
+
self.__start_server_from_bat_file = start_server_from_bat_file
|
|
26
|
+
self.server_not_reachable_timeout = server_not_reachable_timeout
|
|
27
|
+
|
|
28
|
+
def _start_server(self):
|
|
29
|
+
if not self.is_running:
|
|
30
|
+
print("starting server")
|
|
31
|
+
|
|
32
|
+
# make a test request to check if the server is running
|
|
33
|
+
if self.__check_server_status_ok():
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
if not os.path.isfile(self.endpoint.start_bat_path):
|
|
37
|
+
raise ValueError(
|
|
38
|
+
f"start_server.bat not found in {self.endpoint.start_bat_path}. Is Bark installed?")
|
|
39
|
+
|
|
40
|
+
# start the server in a new console
|
|
41
|
+
print(f"starting server {self.endpoint.start_bat_path}")
|
|
42
|
+
self.process = Popen(self.endpoint.start_bat_path,
|
|
43
|
+
creationflags=CREATE_NEW_CONSOLE,
|
|
44
|
+
cwd=os.path.dirname(self.endpoint.start_bat_path)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
status = self._wait_for_ok_of_server()
|
|
48
|
+
|
|
49
|
+
return status
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _wait_for_ok_of_server(self):
|
|
53
|
+
# print("wait until bark is ready")
|
|
54
|
+
start_open = time.time()
|
|
55
|
+
|
|
56
|
+
## not managed to print the console output in pipe don't know why it doesnt work.
|
|
57
|
+
while True:
|
|
58
|
+
status = self.__check_server_status_ok()
|
|
59
|
+
if status:
|
|
60
|
+
print("server is running")
|
|
61
|
+
break
|
|
62
|
+
elif int(time.time() - start_open) >= self.server_not_reachable_timeout:
|
|
63
|
+
self.is_running = False
|
|
64
|
+
status = LocalServerStatus.TIMEOUT
|
|
65
|
+
print("server start timed out")
|
|
66
|
+
break
|
|
67
|
+
else:
|
|
68
|
+
time.sleep(1) # wait one sec
|
|
69
|
+
|
|
70
|
+
return status
|
|
71
|
+
|
|
72
|
+
def stop_server(self):
|
|
73
|
+
if self.process is not None:
|
|
74
|
+
self.process.terminate()
|
|
75
|
+
self.is_running = False
|
|
76
|
+
|
|
77
|
+
def __check_server_status_ok(self) -> LocalServerStatus:
|
|
78
|
+
"""
|
|
79
|
+
Check if the server is running and responding with "ok"
|
|
80
|
+
"""
|
|
81
|
+
url = self.endpoint.service_url + "/status"
|
|
82
|
+
status, error = web_request(url=url)
|
|
83
|
+
if "ok" in str(status).lower() and not error:
|
|
84
|
+
self.is_running = True
|
|
85
|
+
return LocalServerStatus.OK
|
|
86
|
+
else:
|
|
87
|
+
return LocalServerStatus.NOT_OK
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def request(self, job: Job):
|
|
91
|
+
"""
|
|
92
|
+
Subclass of RemoteClient.request
|
|
93
|
+
:param job: the job with the payload to send
|
|
94
|
+
"""
|
|
95
|
+
# start the server if not running
|
|
96
|
+
if not self.is_running:
|
|
97
|
+
if self.__start_server_from_bat_file:
|
|
98
|
+
self._start_server()
|
|
99
|
+
else:
|
|
100
|
+
raise ValueError("Server is not running. Please start the server first.")
|
|
101
|
+
|
|
102
|
+
# make the request
|
|
103
|
+
result = super().request(job)
|
|
104
|
+
return result
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from typing import Union,Tuple
|
|
2
|
+
import requests
|
|
3
|
+
from requests import JSONDecodeError
|
|
4
|
+
|
|
5
|
+
from socaity.core.client.client import Client
|
|
6
|
+
from socaity.core.endpoint import RemoteEndPoint
|
|
7
|
+
from socaity.core.job import Job
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def web_request(
|
|
11
|
+
url: str,
|
|
12
|
+
get_params: dict = None,
|
|
13
|
+
post_params: dict = None,
|
|
14
|
+
files: dict = None) -> Tuple[object, Union[str, None]]:
|
|
15
|
+
"""
|
|
16
|
+
Request an hosted API.
|
|
17
|
+
:param url: The url of the API
|
|
18
|
+
:param get_params: The parameters to be sent in the GET request
|
|
19
|
+
:param post_params: The parameters to be sent in the POST request
|
|
20
|
+
:param files: The files to be sent in the request.
|
|
21
|
+
:return: result, error_msg (or None if nor error occurred)
|
|
22
|
+
"""
|
|
23
|
+
# add get parameters to url
|
|
24
|
+
if get_params:
|
|
25
|
+
url += "?"
|
|
26
|
+
for k, v in get_params:
|
|
27
|
+
url += f"{k}={v}&"
|
|
28
|
+
url = url[:-1]
|
|
29
|
+
|
|
30
|
+
# send request
|
|
31
|
+
error = None
|
|
32
|
+
res = None
|
|
33
|
+
try:
|
|
34
|
+
response = requests.post(url, params=post_params, files=files)
|
|
35
|
+
if response.status_code == 500:
|
|
36
|
+
print(f"API {url} call error: {response.content}")
|
|
37
|
+
return response.content
|
|
38
|
+
if response.headers.get("content-type") in ["audio/wav", "image/png", "image/jpg", "octet-stream"]:
|
|
39
|
+
res = response.content
|
|
40
|
+
else:
|
|
41
|
+
res = response.json()
|
|
42
|
+
except JSONDecodeError as e:
|
|
43
|
+
print(f"Response of API {url} is not JSON format. Intended?")
|
|
44
|
+
error = str(e)
|
|
45
|
+
except Exception as e:
|
|
46
|
+
error = str(e)
|
|
47
|
+
print(f"API {url} call error: {str(e)}")
|
|
48
|
+
|
|
49
|
+
return res, error
|
|
50
|
+
|
|
51
|
+
class RemoteClient(Client):
|
|
52
|
+
"""
|
|
53
|
+
Used to interact with remote APIs. Implements Authentication and Authorization.
|
|
54
|
+
It creates Jobs which are then run in a thread.
|
|
55
|
+
The jobs have the required logic to pre and postprocess the requests.
|
|
56
|
+
|
|
57
|
+
1. Get the token
|
|
58
|
+
2. Make the request
|
|
59
|
+
"""
|
|
60
|
+
def __init__(self, endpoint: RemoteEndPoint):
|
|
61
|
+
super().__init__(endpoint)
|
|
62
|
+
self.endpoint = endpoint
|
|
63
|
+
|
|
64
|
+
def prepare_payload(self, job: Job) -> Job:
|
|
65
|
+
"""
|
|
66
|
+
Moves params for post, get, and files.
|
|
67
|
+
"""
|
|
68
|
+
# get the named parameters
|
|
69
|
+
payload = {
|
|
70
|
+
"post_params": {k: v for k, v in job.raw_payload.items() if k in self.endpoint.post_params},
|
|
71
|
+
"get_params": {k: v for k, v in job.raw_payload.items() if k in self.endpoint.get_params},
|
|
72
|
+
"files": {k: v for k, v in job.raw_payload.items() if k in self.endpoint.files}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# add remaining parameters to post
|
|
76
|
+
remaining_params = {
|
|
77
|
+
k: v for k, v in job.raw_payload.items()
|
|
78
|
+
if k not in payload["post_params"]
|
|
79
|
+
and k not in payload["files"]
|
|
80
|
+
and k not in payload["get_params"]
|
|
81
|
+
}
|
|
82
|
+
payload["post_params"].update(remaining_params)
|
|
83
|
+
|
|
84
|
+
job.payload = payload
|
|
85
|
+
|
|
86
|
+
return job
|
|
87
|
+
|
|
88
|
+
def request(self, job: Job) -> Union[dict, bytes, str, object, None]:
|
|
89
|
+
"""
|
|
90
|
+
:param job: the job with the payload to send
|
|
91
|
+
:return: the result of the request to the remote API
|
|
92
|
+
"""
|
|
93
|
+
url = self.endpoint.service_url
|
|
94
|
+
url = url if self.endpoint.endpoint_name is None else f"{url}/{self.endpoint.endpoint_name}"
|
|
95
|
+
|
|
96
|
+
res, error = web_request(url, job.payload["get_params"], job.payload["post_params"], job.payload["files"])
|
|
97
|
+
|
|
98
|
+
if error:
|
|
99
|
+
raise Exception(f"API {url} call error: {error}")
|
|
100
|
+
|
|
101
|
+
return res
|
|
102
|
+
|
|
103
|
+
def run(self, job: Job, *args, **kwargs) -> Job:
|
|
104
|
+
return super().run(job, *args, **kwargs)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from socaity.core.client.client_factory import create_client
|
|
4
|
+
from socaity.globals import ModelType, EndPointType
|
|
5
|
+
from socaity.core.job import Job
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ClientAPI:
|
|
9
|
+
def __init__(self,
|
|
10
|
+
model_type: ModelType,
|
|
11
|
+
model_name: str,
|
|
12
|
+
endpoint_type: Union[EndPointType, str] = EndPointType.REMOTE
|
|
13
|
+
):
|
|
14
|
+
self.model_type = model_type
|
|
15
|
+
self.model_name = model_name
|
|
16
|
+
self.endpoint_type = endpoint_type
|
|
17
|
+
self.client = create_client(model_type=model_type, model_name=model_name, endpoint_type=endpoint_type)
|
|
18
|
+
|
|
19
|
+
def validate_params(self, *args, **kwargs) -> (bool, str):
|
|
20
|
+
"""
|
|
21
|
+
Method is executed before a job is created.
|
|
22
|
+
Validate the parameters passed to the job before job is created.
|
|
23
|
+
:return: validation_success, error_message
|
|
24
|
+
Subclass this method to implement your own validation.
|
|
25
|
+
"""
|
|
26
|
+
return True, None
|
|
27
|
+
|
|
28
|
+
def _pre_process(self, *args, **kwargs):
|
|
29
|
+
"""
|
|
30
|
+
This method is called before the job is executed.
|
|
31
|
+
Subclass this method if you want to do some preprocessing of the args and kwargs.
|
|
32
|
+
"""
|
|
33
|
+
return args, kwargs
|
|
34
|
+
|
|
35
|
+
def _post_process(self, *args, **kwargs):
|
|
36
|
+
"""
|
|
37
|
+
This method is called after the job is executed.
|
|
38
|
+
Subclass this method if you want to do some postprocessing.
|
|
39
|
+
"""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def __call__(self, **kwargs) -> Union[Job, None]:
|
|
43
|
+
"""
|
|
44
|
+
Run the job with the given parameters.
|
|
45
|
+
Subclass this method if you want type hinting. But don't forget to call super().__call__(..)
|
|
46
|
+
:kwargs: The parameters for running the job. Note only named are further passed to the job. Transform args to kwargs
|
|
47
|
+
"""
|
|
48
|
+
valid, error = self.validate_params(**kwargs)
|
|
49
|
+
if not valid:
|
|
50
|
+
print(f"Validation of params failed before job execute: {error}. Job will not be created.")
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
# The parameters for running the job are in *args and **kwargs and then stored in the job itself.
|
|
54
|
+
job = Job(self._pre_process, self._post_process, **kwargs)
|
|
55
|
+
result = self.client.run(job)
|
|
56
|
+
#try:
|
|
57
|
+
# result = self.client.run(job)
|
|
58
|
+
#except Exception as e:
|
|
59
|
+
# print(f"Job execution failed: {e}.")
|
|
60
|
+
# return None
|
|
61
|
+
|
|
62
|
+
return result
|
|
63
|
+
|
|
64
|
+
def run(self, *args, **kwargs):
|
|
65
|
+
""" Please subclass the class to implement type hinting."""
|
|
66
|
+
return self(*args, **kwargs)
|
socaity/core/endpoint.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from socaity.globals import EndPointType, ModelType
|
|
2
|
+
|
|
3
|
+
class EndPoint:
|
|
4
|
+
"""
|
|
5
|
+
An endpoint contains the information to connect to a model hosted on a provider (Remote, Local or Decentralized).
|
|
6
|
+
"""
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
model_type: ModelType,
|
|
10
|
+
model_name: str,
|
|
11
|
+
endpoint_type: EndPointType,
|
|
12
|
+
endpoint_name: str = None,
|
|
13
|
+
provider: str = None,
|
|
14
|
+
post_params: list = None,
|
|
15
|
+
get_params: list = None,
|
|
16
|
+
files: list = None,
|
|
17
|
+
*args,
|
|
18
|
+
**kwargs
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
:param model_type: The type of the model (for example ModelType.TEXT2VOICE)
|
|
22
|
+
:param model_name: The name of the model (for example "bark")
|
|
23
|
+
:param endpoint_type: The type of the endpoint (for example EndPointType.REMOTE)
|
|
24
|
+
:
|
|
25
|
+
:param provider: The provider of the model (for example "socaity")
|
|
26
|
+
:param post_params: The parameters to be sent in the POST request
|
|
27
|
+
:param get_params: The parameters to be sent in the GET request
|
|
28
|
+
:param files: The files to be sent in the request.
|
|
29
|
+
All parameters in a request which are not defined in post, get, or files are default as post parameters.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
self.model_name = model_name
|
|
33
|
+
self.model_type = model_type
|
|
34
|
+
self.endpoint_type = endpoint_type
|
|
35
|
+
self.endpoint_name = endpoint_name
|
|
36
|
+
self.provider = provider
|
|
37
|
+
|
|
38
|
+
# initiating empty lists if not given to avoid errors
|
|
39
|
+
self.post_params = post_params if post_params is not None else []
|
|
40
|
+
self.get_params = get_params if get_params is not None else []
|
|
41
|
+
self.files = files if files is not None else []
|
|
42
|
+
|
|
43
|
+
def __str__(self):
|
|
44
|
+
return f"{self.model_type.value}_{self.model_name}_{self.endpoint_type.value}_{self.provider}"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class RemoteEndPoint(EndPoint):
|
|
49
|
+
def __init__(self, model_type: ModelType, model_name: str, service_url: str, endpoint_name: str, *args, **kwargs):
|
|
50
|
+
super().__init__(model_type=model_type, model_name=model_name, endpoint_type=EndPointType.REMOTE,
|
|
51
|
+
endpoint_name=endpoint_name,
|
|
52
|
+
*args, **kwargs)
|
|
53
|
+
self.service_url = service_url
|
|
54
|
+
self.endpoint_name = endpoint_name
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class LocalEndPoint(EndPoint):
|
|
58
|
+
def __init__(self, service_url: str, endpoint_name: str, start_bat_path: str = "", *args, **kwargs):
|
|
59
|
+
super().__init__(service_url=service_url, endpoint_name=endpoint_name, endpoint_type=EndPointType.LOCAL,
|
|
60
|
+
*args, **kwargs)
|
|
61
|
+
self.service_url = service_url
|
|
62
|
+
self.start_bat_path = start_bat_path
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class SocaityEndpoint(RemoteEndPoint):
|
|
66
|
+
""" Helper class with predifined URLS to define the SOCAITY endpoints. """
|
|
67
|
+
def __init__(self, model_type: ModelType, model_name: str, *args, **kwargs):
|
|
68
|
+
self.service_url = f"https://socaity.ai/api/"
|
|
69
|
+
self.endpoint_name = f"{model_type.value}/{model_name}"
|
|
70
|
+
super().__init__(model_type=model_type, model_name=model_name,
|
|
71
|
+
service_url=self.service_url, endpoint_name=self.endpoint_name,
|
|
72
|
+
provider="socaity", *args, **kwargs)
|
|
73
|
+
|
socaity/core/job.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class JobStatistics:
|
|
5
|
+
"""
|
|
6
|
+
Stores the time when the different stages of the job were executed.
|
|
7
|
+
"""
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.created = time.time()
|
|
10
|
+
self.pre_processing_started = None
|
|
11
|
+
self.pre_processing_ended = None
|
|
12
|
+
self.request_send = None
|
|
13
|
+
self.request_result_received = None
|
|
14
|
+
self.post_processing_started = None
|
|
15
|
+
self.post_processing_ended = None
|
|
16
|
+
|
|
17
|
+
def get_execution_time(self):
|
|
18
|
+
"""
|
|
19
|
+
The time it took to execute the job in seconds.
|
|
20
|
+
"""
|
|
21
|
+
return self.post_processing_ended - self.pre_processing_started
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Job:
|
|
25
|
+
"""
|
|
26
|
+
A job contains the:
|
|
27
|
+
- the payload to handle a request.
|
|
28
|
+
- references to the pre and post processing functions.
|
|
29
|
+
- the result of the request.
|
|
30
|
+
- the statistics and execution times of the job.
|
|
31
|
+
"""
|
|
32
|
+
def __init__(self,
|
|
33
|
+
pre_process_func: callable = None,
|
|
34
|
+
post_process_func: callable = None,
|
|
35
|
+
**kwargs):
|
|
36
|
+
"""
|
|
37
|
+
:param pre_process_func: A function that pre_processes the request parameters, before sending the request.
|
|
38
|
+
:param post_process_func: A function that post_processes the result of the request, before returning it.
|
|
39
|
+
:param kwargs: The named parameters of the request. (Note: args where transformed to kwargs in the clientAPI.)
|
|
40
|
+
|
|
41
|
+
If parameters are not named in post, get, or files, they are default sent as post parameters.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
self.preprocess_func = pre_process_func
|
|
45
|
+
self.post_process_func = post_process_func
|
|
46
|
+
self.job_statistics = JobStatistics()
|
|
47
|
+
self.result = None
|
|
48
|
+
|
|
49
|
+
# prepare payload
|
|
50
|
+
self.raw_payload = kwargs # will be modified in job.preprocess_params in client.run
|
|
51
|
+
self.payload = None # will be prepared for post, get, and file params in client before request is sent
|
|
52
|
+
|
|
53
|
+
def get_payload(self):
|
|
54
|
+
return self.payload
|
|
55
|
+
|
|
56
|
+
def pre_process_params(self):
|
|
57
|
+
"""
|
|
58
|
+
This function is called before the request is sent.
|
|
59
|
+
Use it to modify the request payload parameters if needed.
|
|
60
|
+
It is better to do it here than in the init function because this method will be called threaded.
|
|
61
|
+
:param kwargs:
|
|
62
|
+
:return:
|
|
63
|
+
"""
|
|
64
|
+
if self.preprocess_func is not None:
|
|
65
|
+
self.job_statistics.pre_processing_started = time.time()
|
|
66
|
+
self.raw_payload = self.preprocess_func(**self.raw_payload)
|
|
67
|
+
self.job_statistics.pre_processing_ended = time.time()
|
|
68
|
+
|
|
69
|
+
return self.raw_payload
|
|
70
|
+
|
|
71
|
+
def post_process_result(self, result, *args, **kwargs):
|
|
72
|
+
"""
|
|
73
|
+
The result of the request is the raw response from the server.
|
|
74
|
+
Use this method to process the result before returning it.
|
|
75
|
+
:param result:
|
|
76
|
+
:param kwargs:
|
|
77
|
+
:return:
|
|
78
|
+
"""
|
|
79
|
+
if self.post_process_func is not None:
|
|
80
|
+
self.job_statistics.post_processing_started = time.time()
|
|
81
|
+
self.result = self.post_process_func(result, *args, **kwargs)
|
|
82
|
+
self.job_statistics.post_processing_ended = time.time()
|
|
83
|
+
|
|
84
|
+
return self
|
|
85
|
+
|