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 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
@@ -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
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)