sumit-sdk 1.2.4__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.
- sumit_sdk/__init__.py +0 -0
- sumit_sdk/api.py +41 -0
- sumit_sdk/api_helper.py +109 -0
- sumit_sdk/base_task.py +89 -0
- sumit_sdk/exceptions.py +0 -0
- sumit_sdk/realtime_stt.py +181 -0
- sumit_sdk/storage.py +278 -0
- sumit_sdk/stream_stt.py +214 -0
- sumit_sdk/sumit_reckit.py +84 -0
- sumit_sdk/summary_api.py +45 -0
- sumit_sdk/transcript.py +104 -0
- sumit_sdk/translate_api.py +49 -0
- sumit_sdk/translate_subtitles.py +52 -0
- sumit_sdk/users.py +26 -0
- sumit_sdk/utils/__init__.py +0 -0
- sumit_sdk/utils/alsa_recorder.py +74 -0
- sumit_sdk/utils/audio_helper.py +98 -0
- sumit_sdk/utils/downsample_helper.py +29 -0
- sumit_sdk/utils/socketio_client.py +45 -0
- sumit_sdk/utils/streamlink_helper.py +130 -0
- sumit_sdk/utils.py +0 -0
- sumit_sdk-1.2.4.dist-info/METADATA +67 -0
- sumit_sdk-1.2.4.dist-info/RECORD +26 -0
- sumit_sdk-1.2.4.dist-info/WHEEL +5 -0
- sumit_sdk-1.2.4.dist-info/licenses/LICENSE +13 -0
- sumit_sdk-1.2.4.dist-info/top_level.txt +1 -0
sumit_sdk/__init__.py
ADDED
|
File without changes
|
sumit_sdk/api.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from sumit_sdk.api_helper import APIHelper
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
class APIClient(APIHelper):
|
|
6
|
+
|
|
7
|
+
def _get_data(self, endpoint: str, json_data: dict={}):
|
|
8
|
+
"""
|
|
9
|
+
Makes a GET request to the given API endpoint.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
- endpoint (str): The API endpoint to make the request to.
|
|
13
|
+
- json_data (dict, optional): The JSON data to send in the request. Defaults to None.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
- dict: The response from the API.
|
|
17
|
+
"""
|
|
18
|
+
headers = {"Authorization": f"Bearer {self.token}"}
|
|
19
|
+
response = requests.get(f"{self.api_url}/{endpoint}", json=json_data, headers=headers)
|
|
20
|
+
return response.json()
|
|
21
|
+
|
|
22
|
+
def _post_data(self, endpoint: str, json_data: dict={}):
|
|
23
|
+
"""
|
|
24
|
+
Makes a POST request to the given API endpoint.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
- endpoint (str): The API endpoint to make the request to.
|
|
28
|
+
- json_data (dict, optional): The JSON data to send in the request. Defaults to None.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
- dict: The response from the API.
|
|
32
|
+
"""
|
|
33
|
+
headers = {"Authorization": f"Bearer {self.token}"}
|
|
34
|
+
response = requests.post(f"{self.api_url}/{endpoint}", json=json_data, headers=headers)
|
|
35
|
+
return response.json()
|
|
36
|
+
|
|
37
|
+
class BaseWrapper:
|
|
38
|
+
def __init__(self, api_instance) -> None:
|
|
39
|
+
if not isinstance(api_instance, APIClient):
|
|
40
|
+
raise TypeError("Expected api_instance to be an instance of APIClient")
|
|
41
|
+
self.api = api_instance
|
sumit_sdk/api_helper.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import requests
|
|
3
|
+
import os
|
|
4
|
+
from retry import retry
|
|
5
|
+
|
|
6
|
+
API_URL = {
|
|
7
|
+
"prod": "https://api.sumit-labs.com",
|
|
8
|
+
"tenants": "https://api.{tenant}.sumit-labs.com",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class APIEPS:
|
|
12
|
+
login = 'login'
|
|
13
|
+
realtime_update = 'realtime/set_status'
|
|
14
|
+
|
|
15
|
+
class APIHelper:
|
|
16
|
+
def __init__(self, cred_path: str, env="prod", onprem=False, verify_ssl=None) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Initializes the APIHelper.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
- cred_path (str): The credential json path
|
|
22
|
+
- env (str): api environment. 'prod' for cloud env. default is 'prod'
|
|
23
|
+
- verify_ssl (bool | None): if False - ignore ssl verification and self-sigend urls.
|
|
24
|
+
"""
|
|
25
|
+
self._env = env
|
|
26
|
+
self.verify_ssl = verify_ssl
|
|
27
|
+
if env not in API_URL:
|
|
28
|
+
if onprem and env.startswith('http'):
|
|
29
|
+
self.api_url = env
|
|
30
|
+
else:
|
|
31
|
+
self.api_url = API_URL["tenants"].format(tenant=env)
|
|
32
|
+
else:
|
|
33
|
+
self.api_url = API_URL[env]
|
|
34
|
+
self.token = None
|
|
35
|
+
self.invalid_token_code = 401
|
|
36
|
+
if not os.path.exists(cred_path):
|
|
37
|
+
raise Exception(f"credential file doesn't exists: {cred_path}")
|
|
38
|
+
try:
|
|
39
|
+
print("load credentials")
|
|
40
|
+
self.login_sa = self.load_cred(cred_path)
|
|
41
|
+
print(self.login_sa)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print("failed to load credentials", e)
|
|
44
|
+
return
|
|
45
|
+
self.login()
|
|
46
|
+
|
|
47
|
+
@retry(tries=3, delay=10)
|
|
48
|
+
def load_cred(self, path):
|
|
49
|
+
with open(path, 'r') as fd:
|
|
50
|
+
return json.load(fd)
|
|
51
|
+
|
|
52
|
+
@retry(tries=3, delay=10)
|
|
53
|
+
def try_login(self):
|
|
54
|
+
t = requests.post(f"{self.api_url}/{APIEPS.login}", json=self.login_sa, verify=self.verify_ssl)
|
|
55
|
+
self.token = t.json()['token']
|
|
56
|
+
print(self.token)
|
|
57
|
+
|
|
58
|
+
def login(self):
|
|
59
|
+
"""
|
|
60
|
+
Logs in using the provided credentials, and sets the token.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
print("try login")
|
|
65
|
+
self.try_login()
|
|
66
|
+
except Exception as e:
|
|
67
|
+
print(e)
|
|
68
|
+
|
|
69
|
+
def safe_call(self, endpoint: str, data: dict, re_login=True, raise_on_failure=False):
|
|
70
|
+
"""
|
|
71
|
+
Makes a safe POST request to the given API endpoint. if not logged in, or token is expired - automatically reconnect
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
- endpoint (str): The API endpoint to make the request to.
|
|
75
|
+
- data (dict): The JSON data to send in the request.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
- dict: The response from the API.
|
|
79
|
+
"""
|
|
80
|
+
if not self.token:
|
|
81
|
+
self.login()
|
|
82
|
+
try:
|
|
83
|
+
ret = requests.post(f"{self.api_url}/{endpoint}", json=data, headers={"Authorization": f"Bearer {self.token}"}, verify=self.verify_ssl)
|
|
84
|
+
if ret.status_code == self.invalid_token_code:
|
|
85
|
+
self.token = None
|
|
86
|
+
self.login()
|
|
87
|
+
if re_login:
|
|
88
|
+
return self.safe_call(endpoint, data, re_login=False)
|
|
89
|
+
return ret
|
|
90
|
+
except:
|
|
91
|
+
if raise_on_failure:
|
|
92
|
+
raise
|
|
93
|
+
|
|
94
|
+
def safe_call_args(self, endpoint: str, re_login=True, **kwargs):
|
|
95
|
+
"""
|
|
96
|
+
same as safe_call, with flexible arguments for request
|
|
97
|
+
"""
|
|
98
|
+
if not self.token:
|
|
99
|
+
self.login()
|
|
100
|
+
try:
|
|
101
|
+
ret = requests.post(f"{self.api_url}/{endpoint}", headers={"Authorization": f"Bearer {self.token}"}, verify=self.verify_ssl, **kwargs)
|
|
102
|
+
if ret.status_code == self.invalid_token_code:
|
|
103
|
+
self.token = None
|
|
104
|
+
self.login()
|
|
105
|
+
if re_login:
|
|
106
|
+
return self.safe_call(endpoint, re_login=False, **kwargs)
|
|
107
|
+
return ret
|
|
108
|
+
except:
|
|
109
|
+
pass
|
sumit_sdk/base_task.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from sumit_sdk.api import BaseWrapper
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
class TaskOperations:
|
|
6
|
+
TRANSCRIPT = 'transcript'
|
|
7
|
+
GET_STATUS = 'get_status'
|
|
8
|
+
TRANSLATE_API = 'translate'
|
|
9
|
+
TRANSLATE_SUBS = 'translate_subtitles'
|
|
10
|
+
SUMMARY_API = 'summarize'
|
|
11
|
+
|
|
12
|
+
class BaseTask(BaseWrapper):
|
|
13
|
+
"""
|
|
14
|
+
Abstract class to implement API calls for tasks.
|
|
15
|
+
|
|
16
|
+
Methods:
|
|
17
|
+
- execute(): run new task, with custom payload.
|
|
18
|
+
- build_request(**kwargs): create payload for the request.
|
|
19
|
+
- get_active_sessions(): Returns the currently active sessions.
|
|
20
|
+
"""
|
|
21
|
+
_API_VERSION = 'v3'
|
|
22
|
+
|
|
23
|
+
def __init__(self, api_instance) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Initializes the BaseTask.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
- api_instance (APIClient): An instance of the APIClient class.
|
|
29
|
+
"""
|
|
30
|
+
super().__init__(api_instance)
|
|
31
|
+
self._operation = None
|
|
32
|
+
self._wait_interval = 5
|
|
33
|
+
self._set_op()
|
|
34
|
+
|
|
35
|
+
def _set_op(self):
|
|
36
|
+
raise Exception("_set_op not implemented")
|
|
37
|
+
|
|
38
|
+
def execute(self, payload):
|
|
39
|
+
endpoint = f"{self._API_VERSION}/{self._operation}"
|
|
40
|
+
try:
|
|
41
|
+
ret = self.api.safe_call(endpoint, payload)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
return {
|
|
44
|
+
'status_code': -1,
|
|
45
|
+
'content': str(e)
|
|
46
|
+
}
|
|
47
|
+
try:
|
|
48
|
+
ret = ret.json()
|
|
49
|
+
return ret
|
|
50
|
+
except:
|
|
51
|
+
return {
|
|
52
|
+
'status_code': ret.status_code,
|
|
53
|
+
'content': ret.content
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def build_request(self, **kwargs):
|
|
57
|
+
raise Exception("build_request not implemented")
|
|
58
|
+
|
|
59
|
+
def run(self, **kwargs):
|
|
60
|
+
return self.execute(self.build_request(**kwargs))
|
|
61
|
+
|
|
62
|
+
def get_task_status(self, task_id: str):
|
|
63
|
+
"""
|
|
64
|
+
get the status of async task, by task_id.
|
|
65
|
+
task_id returned from the response: response['response']['job_id']
|
|
66
|
+
"""
|
|
67
|
+
ret = self.api.safe_call(TaskOperations.GET_STATUS, {
|
|
68
|
+
'id': task_id
|
|
69
|
+
})
|
|
70
|
+
ret = ret.json()
|
|
71
|
+
return ret
|
|
72
|
+
|
|
73
|
+
def wait_for_task(self, task_id: str):
|
|
74
|
+
"""
|
|
75
|
+
blocking function to wait until task is done.
|
|
76
|
+
alternatively, you can use `callback` to get notified when task is done
|
|
77
|
+
"""
|
|
78
|
+
finish = False
|
|
79
|
+
ret = None
|
|
80
|
+
while not finish:
|
|
81
|
+
try:
|
|
82
|
+
ret = self.get_task_status(task_id)
|
|
83
|
+
finish = ret['response']['progress'] == 100
|
|
84
|
+
if finish:
|
|
85
|
+
break
|
|
86
|
+
except:
|
|
87
|
+
pass
|
|
88
|
+
time.sleep(self._wait_interval)
|
|
89
|
+
return ret
|
sumit_sdk/exceptions.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
from sumit_sdk.api import BaseWrapper
|
|
2
|
+
from sumit_sdk.utils.socketio_client import SocketClient
|
|
3
|
+
import time
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
class Profiles:
|
|
7
|
+
default = 'default' # Est. latency: 3 seconds
|
|
8
|
+
low_latency = 'low_latency' # Est. latency: 2.5 seconds
|
|
9
|
+
lower_latency = 'lower_latency' # Est. latency: 1.5 seconds
|
|
10
|
+
accurate = 'accurate' # Est. latency: 5 seconds
|
|
11
|
+
very_accurate = 'very_accurate' # Est. latency: 7 seconds
|
|
12
|
+
|
|
13
|
+
class VadProfile:
|
|
14
|
+
default = 'default'
|
|
15
|
+
high = 'high'
|
|
16
|
+
low = 'low'
|
|
17
|
+
very_low = 'very_low'
|
|
18
|
+
off = 'off'
|
|
19
|
+
|
|
20
|
+
class BufferMode:
|
|
21
|
+
default = 'accumulate'
|
|
22
|
+
switch = 'switch'
|
|
23
|
+
|
|
24
|
+
class RealtimeSTT(BaseWrapper):
|
|
25
|
+
"""
|
|
26
|
+
Manages realtime sessions.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
- sessions (dict): A dictionary to store session IDs and their corresponding data, including URLs for web socket communication.
|
|
30
|
+
|
|
31
|
+
Methods:
|
|
32
|
+
- start_session(): Starts a new session and stores its details.
|
|
33
|
+
- stop_session(session_id): Stops an existing session.
|
|
34
|
+
- get_active_sessions(): Returns the currently active sessions.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
_START_EP = "realtime/start"
|
|
38
|
+
_STOP_EP = "realtime/stop"
|
|
39
|
+
_STATUS_EP = "realtime/get_status"
|
|
40
|
+
|
|
41
|
+
def __init__(self, api_instance) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Initializes the RealtimeSTT.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
- api_instance (APIClient): An instance of the APIClient class.
|
|
47
|
+
"""
|
|
48
|
+
super().__init__(api_instance)
|
|
49
|
+
self.sessions = {}
|
|
50
|
+
self.current_session = None
|
|
51
|
+
self.transcript_callback = None
|
|
52
|
+
|
|
53
|
+
def start_session(self, transcript_callback, language:str=None, profile:str=None, vad_profile:str=None, buffer_mode=None, _add_params=None) -> dict:
|
|
54
|
+
"""
|
|
55
|
+
Starts a new session and stores its details.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
- transcript_callback: callback function to notify when transcription received.
|
|
59
|
+
- language (str): language code. default is he-IL (Hebrew)
|
|
60
|
+
- profile (str): one of `realtime_stt.Profiles` - control the latency and accuracy of the transcription.
|
|
61
|
+
usually the higher the latency, higher the accuracy
|
|
62
|
+
- vad_profile (str): one of `realtime_stt.VadProfiles` - control the VAD thresholds to avoid non-speech transcription.
|
|
63
|
+
- buffer_mode (str): one of `realtime_stt.BufferMode` - control the buffering method for the speech context.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
- dict: containing the session ID and its corresponding URL.
|
|
67
|
+
"""
|
|
68
|
+
req = {}
|
|
69
|
+
if language:
|
|
70
|
+
req['lang'] = language
|
|
71
|
+
if profile:
|
|
72
|
+
req['profile'] = profile
|
|
73
|
+
if vad_profile:
|
|
74
|
+
req['vad_profile'] = vad_profile
|
|
75
|
+
if buffer_mode:
|
|
76
|
+
req['buffer_mode'] = buffer_mode
|
|
77
|
+
if _add_params:
|
|
78
|
+
req.update(_add_params)
|
|
79
|
+
print(req)
|
|
80
|
+
data = self.api.safe_call(RealtimeSTT._START_EP, req).json()
|
|
81
|
+
session_id = data.get("session_id")
|
|
82
|
+
self.sessions[session_id] = data
|
|
83
|
+
self.current_session = session_id
|
|
84
|
+
self.transcript_callback = transcript_callback
|
|
85
|
+
return data
|
|
86
|
+
|
|
87
|
+
def _inject_session(self, session_id, url, transcript_callback) -> dict:
|
|
88
|
+
"""
|
|
89
|
+
Starts a new session and stores its details.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
- dict: containing the session ID and its corresponding URL.
|
|
93
|
+
"""
|
|
94
|
+
# data = self.api.safe_call(RealtimeSTT._START_EP, {}).json()
|
|
95
|
+
self.sessions[session_id] = {"id": session_id, "endpoint": url}
|
|
96
|
+
self.current_session = session_id
|
|
97
|
+
self.transcript_callback = transcript_callback
|
|
98
|
+
return self.sessions[session_id]
|
|
99
|
+
|
|
100
|
+
def stop_session(self, session_id: str=None):
|
|
101
|
+
"""
|
|
102
|
+
Stops an existing session.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
- session_id (str): The ID of the session to stop. if None - get the last created session
|
|
106
|
+
"""
|
|
107
|
+
if not session_id:
|
|
108
|
+
session_id = self.current_session
|
|
109
|
+
ret = self.api.safe_call(RealtimeSTT._STOP_EP, {"id": session_id})
|
|
110
|
+
if ret:
|
|
111
|
+
ret = ret.json()
|
|
112
|
+
else:
|
|
113
|
+
raise Exception("failed to stop session")
|
|
114
|
+
if session_id in self.sessions:
|
|
115
|
+
self.sessions.pop(session_id)
|
|
116
|
+
if ret.get('success') and session_id == self.current_session:
|
|
117
|
+
self.current_session = None
|
|
118
|
+
return ret
|
|
119
|
+
|
|
120
|
+
def get_active_sessions(self) -> dict:
|
|
121
|
+
"""
|
|
122
|
+
Returns the currently active sessions ids.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
- dict: A dictionary containing the session IDs and their corresponding URLs.
|
|
126
|
+
"""
|
|
127
|
+
return self.sessions
|
|
128
|
+
|
|
129
|
+
def _wait_ready(self, session_id: str):
|
|
130
|
+
ready = False
|
|
131
|
+
wait_time = 30
|
|
132
|
+
while not ready:
|
|
133
|
+
ret = self.api.safe_call(RealtimeSTT._STATUS_EP, {"id": session_id})
|
|
134
|
+
if ret:
|
|
135
|
+
ret = ret.json()
|
|
136
|
+
else:
|
|
137
|
+
continue
|
|
138
|
+
ready = ret.get('status', {}).get('transcript')
|
|
139
|
+
if not ready:
|
|
140
|
+
# limit the number of calls to sumit-api to avoid `quota exceed` block
|
|
141
|
+
# in future release it's will replaces with events
|
|
142
|
+
time.sleep(wait_time)
|
|
143
|
+
if wait_time >= 10:
|
|
144
|
+
wait_time -= 5
|
|
145
|
+
|
|
146
|
+
def connect(self, session_id: str=None) -> SocketClient:
|
|
147
|
+
"""
|
|
148
|
+
connect to streaming end point for realtime transcription.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
- session_id (str): The ID of the session to stop. if None - get the last created session
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
SocketClient instance for async streaming to the realtime server
|
|
155
|
+
"""
|
|
156
|
+
if not session_id:
|
|
157
|
+
session_id = self.current_session
|
|
158
|
+
print("monitor instance state...")
|
|
159
|
+
self._wait_ready(session_id)
|
|
160
|
+
url = self.sessions[session_id]["endpoint"]
|
|
161
|
+
print(f"ready, init socket client: {url}")
|
|
162
|
+
sock = SocketClient("https://" + url)
|
|
163
|
+
sock.register_callback('txt', self._parse_json)
|
|
164
|
+
sock.connect()
|
|
165
|
+
return sock
|
|
166
|
+
|
|
167
|
+
def send(self, sock: SocketClient, data):
|
|
168
|
+
"""
|
|
169
|
+
send audio chunk to transcript
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
- sock (SocketClient): socket instance to use. received from `connect` method
|
|
173
|
+
- data: base64 bytes to send
|
|
174
|
+
|
|
175
|
+
"""
|
|
176
|
+
sock.send_message('data', data)
|
|
177
|
+
|
|
178
|
+
def _parse_json(self, msg):
|
|
179
|
+
m = json.loads(msg)
|
|
180
|
+
if self.transcript_callback:
|
|
181
|
+
self.transcript_callback(m)
|