most-client 1.0.8__tar.gz

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.
@@ -0,0 +1,3 @@
1
+ recursive-include most *
2
+ include README.md
3
+ include requirements.txt
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.1
2
+ Name: most-client
3
+ Version: 1.0.8
4
+ Summary: Most AI API for https://the-most.ai
5
+ Home-page: https://github.com/the-most-ai/most-client
6
+ Author: George Kasparyants
7
+ Author-email: george@the-most.ai
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.6
14
+ Requires-Python: >=3.6
15
+ Requires-Dist: requests
16
+ Requires-Dist: wheel
17
+ Requires-Dist: adaptix
18
+ Requires-Dist: json5
19
+ Requires-Dist: dataclasses_json
20
+ Requires-Dist: black
21
+ Requires-Dist: coverage
22
+ Requires-Dist: flake8
23
+ Requires-Dist: mypy
24
+ Requires-Dist: pylint
25
+ Requires-Dist: pytest
26
+ Requires-Dist: tox
27
+ Requires-Dist: twine
28
+ Requires-Dist: httpx
@@ -0,0 +1,30 @@
1
+ # MOST AI Python Client
2
+
3
+
4
+ ```python
5
+ from most import MostClient
6
+
7
+ most = MostClient(model_id="most-***")
8
+
9
+ audio_path = "example.mp3"
10
+ results = most(audio_path)
11
+ print(results)
12
+
13
+ {
14
+ "id": "AudioID",
15
+ "url": "Audio CDN Url",
16
+ "text": "Transcribed text from audio",
17
+ "results": [
18
+ {
19
+ "name": "Приветствие",
20
+ "subcolumns": [
21
+ {
22
+ "name": "Назвал свое имя",
23
+ "description": "Менеджер назвал свое имя - Алина",
24
+ "score": 2,
25
+ }
26
+ ]
27
+ }
28
+ ]
29
+ }
30
+ ```
@@ -0,0 +1,2 @@
1
+ from .api import MostClient
2
+ from .async_api import AsyncMostClient
@@ -0,0 +1,197 @@
1
+ from typing import List
2
+ import json5
3
+ import requests
4
+ from adaptix import Retort
5
+ from most.types import Audio, Result, Script, JobStatus
6
+ from pathlib import Path
7
+
8
+
9
+ class MostClient(object):
10
+ retort = Retort()
11
+
12
+ def __init__(self,
13
+ client_id=None,
14
+ client_secret=None,
15
+ model_id=None):
16
+ super(MostClient, self).__init__()
17
+ self.client_id = client_id
18
+ self.client_secret = client_secret
19
+
20
+ if self.client_id is None:
21
+ credentials = self.load_credentials()
22
+ self.client_id = credentials.get('client_id')
23
+ self.client_secret = credentials.get('client_secret')
24
+
25
+ if self.client_id is None:
26
+ print("Visit: https://app.the-most.ai/integrations and get clientId, clientSecret")
27
+ self.client_id = input("Please enter your client ID: ")
28
+ self.client_secret = input("Please enter your client secret: ")
29
+ self.save_credentials()
30
+ else:
31
+ self.save_credentials()
32
+
33
+ self.session = requests.Session()
34
+ self.access_token = None
35
+ self.model_id = model_id
36
+
37
+ self.refresh_access_token()
38
+
39
+ @property
40
+ def cache_path(self):
41
+ path = Path.home() / ".most"
42
+ path.mkdir(parents=True, exist_ok=True)
43
+ return path
44
+
45
+ def load_credentials(self):
46
+ path = self.cache_path / "credentials.json"
47
+ if not path.exists():
48
+ return {}
49
+ else:
50
+ return json5.loads(path.read_text())
51
+
52
+ def save_credentials(self):
53
+ path = self.cache_path / "credentials.json"
54
+ path.write_text(json5.dumps({
55
+ "client_id": self.client_id,
56
+ "client_secret": self.client_secret,
57
+ }))
58
+
59
+ def clone(self):
60
+ client = MostClient(client_id=self.client_id,
61
+ client_secret=self.client_secret,
62
+ model_id=self.model_id)
63
+ client.access_token = self.access_token
64
+ client.session = self.session
65
+ return client
66
+
67
+ def with_model(self, model_id):
68
+ client = self.clone()
69
+ client.model_id = model_id
70
+ return client
71
+
72
+ def refresh_access_token(self):
73
+ resp = self.session.post("https://api.the-most.ai/api/external/access_token",
74
+ json={"client_id": self.client_id,
75
+ "client_secret": self.client_secret},
76
+ timeout=None)
77
+ access_token = resp.json()
78
+ self.access_token = access_token
79
+
80
+ def get(self, url, **kwargs):
81
+ if self.access_token is None:
82
+ self.refresh_access_token()
83
+ headers = kwargs.pop("headers", {})
84
+ headers.update({"Authorization": "Bearer %s" % self.access_token})
85
+ resp = self.session.get(url,
86
+ headers=headers,
87
+ timeout=None,
88
+ **kwargs)
89
+ if resp.status_code == 401:
90
+ self.refresh_access_token()
91
+ return self.get(url,
92
+ headers=headers,
93
+ **kwargs)
94
+ if resp.status_code >= 400:
95
+ raise RuntimeError(resp.json()['message'] if resp.headers.get("Content-Type") == "application/json" else "Something went wrong.")
96
+ return resp
97
+
98
+ def post(self, url,
99
+ data=None,
100
+ json=None,
101
+ **kwargs):
102
+ if self.access_token is None:
103
+ self.refresh_access_token()
104
+ headers = kwargs.pop("headers", {})
105
+ headers.update({"Authorization": "Bearer %s" % self.access_token})
106
+ resp = self.session.post(url,
107
+ data=data,
108
+ json=json,
109
+ headers=headers,
110
+ timeout=None,
111
+ **kwargs)
112
+ if resp.status_code == 401:
113
+ self.refresh_access_token()
114
+ return self.post(url,
115
+ data=data,
116
+ json=json,
117
+ headers=headers,
118
+ **kwargs)
119
+ if resp.status_code >= 400:
120
+ raise RuntimeError(resp.json()['message'] if resp.headers.get("Content-Type") == "application/json" else "Something went wrong.")
121
+ return resp
122
+
123
+ def upload_audio(self, audio_path) -> Audio:
124
+ with open(audio_path, 'rb') as f:
125
+ resp = self.post(f"https://api.the-most.ai/api/external/{self.client_id}/upload",
126
+ files={"audio_file": f})
127
+ return self.retort.load(resp.json(), Audio)
128
+
129
+ def upload_audio_url(self, audio_url) -> Audio:
130
+ resp = self.post(f"https://api.the-most.ai/api/external/{self.client_id}/upload_url",
131
+ json={"audio_url": audio_url})
132
+ return self.retort.load(resp.json(), Audio)
133
+
134
+ def list_audios(self,
135
+ offset: int = 0,
136
+ limit: int = 10) -> List[Audio]:
137
+ resp = self.get(f"https://api.the-most.ai/api/external/{self.client_id}/list?offset={offset}&limit={limit}")
138
+ audio_list = resp.json()
139
+ return self.retort.load(audio_list, List[Audio])
140
+
141
+ def get_model_script(self) -> Script:
142
+ if self.model_id is None:
143
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
144
+ resp = self.get(f"https://api.the-most.ai/api/external/{self.client_id}/model/{self.model_id}/script")
145
+ return self.retort.load(resp.json(), Script)
146
+
147
+ def list_models(self):
148
+ resp = self.get("https://api.the-most.ai/api/external/list_models")
149
+ return [self.with_model(model['model'])
150
+ for model in resp.json()]
151
+
152
+ def apply(self, audio_id) -> Result:
153
+ if self.model_id is None:
154
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
155
+ resp = self.post(f"https://api.the-most.ai/api/external/{self.client_id}/audio/{audio_id}/model/{self.model_id}/apply")
156
+ return self.retort.load(resp.json(), Result)
157
+
158
+ def apply_later(self, audio_id) -> Result:
159
+ if self.model_id is None:
160
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
161
+ resp = self.post(f"https://api.the-most.ai/api/external/{self.client_id}/audio/{audio_id}/model/{self.model_id}/apply_async")
162
+ return self.retort.load(resp.json(), Result)
163
+
164
+ def get_job_status(self, audio_id) -> JobStatus:
165
+ if self.model_id is None:
166
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
167
+ resp = self.post(f"https://api.the-most.ai/api/external/{self.client_id}/audio/{audio_id}/model/{self.model_id}/apply_status")
168
+ return self.retort.load(resp.json(), JobStatus)
169
+
170
+ def fetch_results(self, audio_id) -> Result:
171
+ if self.model_id is None:
172
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
173
+
174
+ resp = self.get(f"https://api.the-most.ai/api/external/{self.client_id}/audio/{audio_id}/model/{self.model_id}/results")
175
+ return self.retort.load(resp.json(), Result)
176
+
177
+ def fetch_text(self, audio_id: str) -> Result:
178
+ if self.model_id is None:
179
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
180
+
181
+ resp = self.get(f"https://api.the-most.ai/api/external/{self.client_id}/audio/{audio_id}/model/{self.model_id}/text")
182
+ return self.retort.load(resp.json(), Result)
183
+
184
+ def export(self, audio_ids: List[str]) -> str:
185
+ if self.model_id is None:
186
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
187
+
188
+ resp = self.get(f"https://api.the-most.ai/api/external/{self.client_id}/model/{self.model_id}/export",
189
+ params={'audio_ids': ','.join(audio_ids)})
190
+ return resp.url
191
+
192
+ def __call__(self, audio_path: Path):
193
+ audio = self.upload_audio(audio_path)
194
+ return self.apply(audio.id)
195
+
196
+ def __repr__(self):
197
+ return "<MostClient(model_id='%s')>" % (self.model_id, )
@@ -0,0 +1,204 @@
1
+ from typing import List
2
+ import json5
3
+ from adaptix import Retort
4
+ from most.types import Audio, Result, Script, JobStatus
5
+ from pathlib import Path
6
+ import httpx
7
+
8
+
9
+ class AsyncMostClient(object):
10
+ retort = Retort()
11
+
12
+ def __init__(self,
13
+ client_id=None,
14
+ client_secret=None,
15
+ model_id=None):
16
+ super(AsyncMostClient, self).__init__()
17
+ self.client_id = client_id
18
+ self.client_secret = client_secret
19
+
20
+ if self.client_id is None:
21
+ credentials = self.load_credentials()
22
+ self.client_id = credentials.get('client_id')
23
+ self.client_secret = credentials.get('client_secret')
24
+
25
+ if self.client_id is None:
26
+ print("Visit: https://app.the-most.ai/integrations and get clientId, clientSecret")
27
+ self.client_id = input("Please enter your client ID: ")
28
+ self.client_secret = input("Please enter your client secret: ")
29
+ self.save_credentials()
30
+ else:
31
+ self.save_credentials()
32
+
33
+ self.session = httpx.AsyncClient()
34
+ self.access_token = None
35
+ self.model_id = model_id
36
+
37
+ async def __aenter__(self):
38
+ await self.session.__aenter__()
39
+ await self.refresh_access_token()
40
+ return self
41
+
42
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
43
+ await self.session.__aexit__(exc_type, exc_val, exc_tb)
44
+
45
+ @property
46
+ def cache_path(self):
47
+ path = Path.home() / ".most"
48
+ path.mkdir(parents=True, exist_ok=True)
49
+ return path
50
+
51
+ def load_credentials(self):
52
+ path = self.cache_path / "credentials.json"
53
+ if not path.exists():
54
+ return {}
55
+ else:
56
+ return json5.loads(path.read_text())
57
+
58
+ def save_credentials(self):
59
+ path = self.cache_path / "credentials.json"
60
+ path.write_text(json5.dumps({
61
+ "client_id": self.client_id,
62
+ "client_secret": self.client_secret,
63
+ }))
64
+
65
+ def clone(self):
66
+ client = AsyncMostClient(client_id=self.client_id,
67
+ client_secret=self.client_secret,
68
+ model_id=self.model_id)
69
+ client.access_token = self.access_token
70
+ client.session = self.session
71
+ return client
72
+
73
+ def with_model(self, model_id):
74
+ client = self.clone()
75
+ client.model_id = model_id
76
+ return client
77
+
78
+ async def refresh_access_token(self):
79
+ resp = await self.session.post("https://api.the-most.ai/api/external/access_token",
80
+ json={"client_id": self.client_id,
81
+ "client_secret": self.client_secret})
82
+ access_token = resp.json()
83
+ self.access_token = access_token
84
+
85
+ async def get(self, url, **kwargs):
86
+ if self.access_token is None:
87
+ await self.refresh_access_token()
88
+ headers = kwargs.pop("headers", {})
89
+ headers.update({"Authorization": "Bearer %s" % self.access_token})
90
+ resp = await self.session.get(url,
91
+ headers=headers,
92
+ timeout=None,
93
+ **kwargs)
94
+ if resp.status_code == 401:
95
+ await self.refresh_access_token()
96
+ return await self.get(url,
97
+ headers=headers,
98
+ **kwargs)
99
+ if resp.status_code >= 400:
100
+ raise RuntimeError(resp.json()['message'] if resp.headers.get(
101
+ "Content-Type") == "application/json" else "Something went wrong.")
102
+ return resp
103
+
104
+ async def post(self, url,
105
+ data=None,
106
+ json=None,
107
+ **kwargs):
108
+ if self.access_token is None:
109
+ await self.refresh_access_token()
110
+ headers = kwargs.pop("headers", {})
111
+ headers.update({"Authorization": "Bearer %s" % self.access_token})
112
+ resp = await self.session.post(url,
113
+ data=data,
114
+ json=json,
115
+ headers=headers,
116
+ timeout=None,
117
+ **kwargs)
118
+ if resp.status_code == 401:
119
+ await self.refresh_access_token()
120
+ return await self.post(url,
121
+ data=data,
122
+ json=json,
123
+ headers=headers,
124
+ **kwargs)
125
+ if resp.status_code >= 400:
126
+ raise RuntimeError(resp.json()['message'] if resp.headers.get(
127
+ "Content-Type") == "application/json" else "Something went wrong.")
128
+ return resp
129
+
130
+ async def upload_audio(self, audio_path) -> Audio:
131
+ with open(audio_path, mode='rb') as f:
132
+ resp = await self.post(f"https://api.the-most.ai/api/external/{self.client_id}/upload",
133
+ files={"audio_file": f})
134
+ return self.retort.load(resp.json(), Audio)
135
+
136
+ async def upload_audio_url(self, audio_url) -> Audio:
137
+ resp = await self.post(f"https://api.the-most.ai/api/external/{self.client_id}/upload_url",
138
+ json={"audio_url": audio_url})
139
+ return self.retort.load(resp.json(), Audio)
140
+
141
+ async def list_audios(self,
142
+ offset: int = 0,
143
+ limit: int = 10) -> List[Audio]:
144
+ resp = await self.get(f"https://api.the-most.ai/api/external/{self.client_id}/list?offset={offset}&limit={limit}")
145
+ audio_list = resp.json()
146
+ return self.retort.load(audio_list, List[Audio])
147
+
148
+ async def get_model_script(self) -> Script:
149
+ if self.model_id is None:
150
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
151
+ resp = await self.get(f"https://api.the-most.ai/api/external/{self.client_id}/model/{self.model_id}/script")
152
+ return self.retort.load(resp.json(), Script)
153
+
154
+ async def list_models(self):
155
+ resp = await self.get("https://api.the-most.ai/api/external/list_models")
156
+ return [self.with_model(model['model'])
157
+ for model in resp.json()]
158
+
159
+ async def apply(self, audio_id) -> Result:
160
+ if self.model_id is None:
161
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
162
+ resp = await self.post(f"https://api.the-most.ai/api/external/{self.client_id}/audio/{audio_id}/model/{self.model_id}/apply")
163
+ return self.retort.load(resp.json(), Result)
164
+
165
+ async def apply_later(self, audio_id):
166
+ if self.model_id is None:
167
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
168
+ resp = await self.post(f"https://api.the-most.ai/api/external/{self.client_id}/audio/{audio_id}/model/{self.model_id}/apply_async")
169
+ return self.retort.load(resp.json(), Result)
170
+
171
+ async def get_job_status(self, audio_id) -> JobStatus:
172
+ if self.model_id is None:
173
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
174
+ resp = await self.post(f"https://api.the-most.ai/api/external/{self.client_id}/audio/{audio_id}/model/{self.model_id}/apply_status")
175
+ return self.retort.load(resp.json(), JobStatus)
176
+
177
+ async def fetch_results(self, audio_id) -> Result:
178
+ if self.model_id is None:
179
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
180
+
181
+ resp = await self.get(f"https://api.the-most.ai/api/external/{self.client_id}/audio/{audio_id}/model/{self.model_id}/results")
182
+ return self.retort.load(resp.json(), Result)
183
+
184
+ async def fetch_text(self, audio_id) -> Result:
185
+ if self.model_id is None:
186
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
187
+
188
+ resp = await self.get(f"https://api.the-most.ai/api/external/{self.client_id}/audio/{audio_id}/model/{self.model_id}/text")
189
+ return self.retort.load(resp.json(), Result)
190
+
191
+ async def export(self, audio_ids: List[str]) -> str:
192
+ if self.model_id is None:
193
+ raise RuntimeError("Please choose a model to apply. [try list_models()]")
194
+
195
+ resp = await self.get(f"https://api.the-most.ai/api/external/{self.client_id}/model/{self.model_id}/export",
196
+ params={'audio_ids': ','.join(audio_ids)})
197
+ return resp.next_request.url
198
+
199
+ async def __call__(self, audio_path: Path):
200
+ audio = await self.upload_audio(audio_path)
201
+ return await self.apply(audio.id)
202
+
203
+ def __repr__(self):
204
+ return "<AsyncMostClient(model_id='%s')>" % (self.model_id, )
@@ -0,0 +1,62 @@
1
+ from dataclasses import dataclass
2
+ from dataclasses_json import dataclass_json, DataClassJsonMixin
3
+ from typing import Optional, List, Literal
4
+
5
+
6
+ @dataclass_json
7
+ @dataclass
8
+ class Audio(DataClassJsonMixin):
9
+ id: str
10
+ url: str
11
+
12
+
13
+ @dataclass_json
14
+ @dataclass
15
+ class SubcolumnResult(DataClassJsonMixin):
16
+ name: str
17
+ score: Optional[int]
18
+ description: str
19
+
20
+
21
+ @dataclass_json
22
+ @dataclass
23
+ class ColumnResult(DataClassJsonMixin):
24
+ name: str
25
+ subcolumns: List[SubcolumnResult]
26
+
27
+
28
+ @dataclass_json
29
+ @dataclass
30
+ class Column(DataClassJsonMixin):
31
+ name: str
32
+ subcolumns: List[str]
33
+
34
+
35
+ @dataclass_json
36
+ @dataclass
37
+ class Script(DataClassJsonMixin):
38
+ columns: List[Column]
39
+
40
+
41
+ @dataclass_json
42
+ @dataclass
43
+ class JobStatus(DataClassJsonMixin):
44
+ status: Literal["not_found", "pending", "completed", "error"]
45
+
46
+
47
+ @dataclass_json
48
+ @dataclass
49
+ class Result(DataClassJsonMixin):
50
+ id: str
51
+ text: Optional[str]
52
+ url: Optional[str]
53
+ results: Optional[List[ColumnResult]]
54
+
55
+ def get_script(self) -> Optional[Script]:
56
+ if self.results is None:
57
+ return None
58
+
59
+ return Script(columns=[Column(name=column_result.name,
60
+ subcolumns=[subcolumn_result.name
61
+ for subcolumn_result in column_result.subcolumns])
62
+ for column_result in self.results])
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.1
2
+ Name: most-client
3
+ Version: 1.0.8
4
+ Summary: Most AI API for https://the-most.ai
5
+ Home-page: https://github.com/the-most-ai/most-client
6
+ Author: George Kasparyants
7
+ Author-email: george@the-most.ai
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.6
14
+ Requires-Python: >=3.6
15
+ Requires-Dist: requests
16
+ Requires-Dist: wheel
17
+ Requires-Dist: adaptix
18
+ Requires-Dist: json5
19
+ Requires-Dist: dataclasses_json
20
+ Requires-Dist: black
21
+ Requires-Dist: coverage
22
+ Requires-Dist: flake8
23
+ Requires-Dist: mypy
24
+ Requires-Dist: pylint
25
+ Requires-Dist: pytest
26
+ Requires-Dist: tox
27
+ Requires-Dist: twine
28
+ Requires-Dist: httpx
@@ -0,0 +1,14 @@
1
+ MANIFEST.in
2
+ README.md
3
+ requirements.txt
4
+ setup.py
5
+ most/__init__.py
6
+ most/api.py
7
+ most/async_api.py
8
+ most/types.py
9
+ most_client.egg-info/PKG-INFO
10
+ most_client.egg-info/SOURCES.txt
11
+ most_client.egg-info/dependency_links.txt
12
+ most_client.egg-info/requires.txt
13
+ most_client.egg-info/top_level.txt
14
+ most_client.egg-info/zip-safe
@@ -0,0 +1,14 @@
1
+ requests
2
+ wheel
3
+ adaptix
4
+ json5
5
+ dataclasses_json
6
+ black
7
+ coverage
8
+ flake8
9
+ mypy
10
+ pylint
11
+ pytest
12
+ tox
13
+ twine
14
+ httpx
@@ -0,0 +1,14 @@
1
+ requests
2
+ wheel
3
+ adaptix
4
+ json5
5
+ dataclasses_json
6
+ black
7
+ coverage
8
+ flake8
9
+ mypy
10
+ pylint
11
+ pytest
12
+ tox
13
+ twine
14
+ httpx
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,31 @@
1
+ from setuptools import setup
2
+ from setuptools import find_packages
3
+
4
+
5
+ with open('requirements.txt', 'r') as f:
6
+ requirements = f.read()
7
+
8
+
9
+ setup(
10
+ name='most-client',
11
+ version='1.0.8',
12
+ python_requires=f'>=3.6',
13
+ description='Most AI API for https://the-most.ai',
14
+ url='https://github.com/the-most-ai/most-client',
15
+ author='George Kasparyants',
16
+ author_email='george@the-most.ai',
17
+ license='',
18
+ packages=find_packages(include=['most', 'most.*']),
19
+ install_requires=requirements,
20
+ zip_safe=True,
21
+ include_package_data=True,
22
+ exclude_package_data={'': ['notebooks']},
23
+ classifiers=[
24
+ "Intended Audience :: Developers",
25
+ "Operating System :: OS Independent",
26
+ "Topic :: Software Development :: Libraries :: Python Modules",
27
+ "Programming Language :: Python",
28
+ "Programming Language :: Python :: 3 :: Only",
29
+ "Programming Language :: Python :: 3.6"
30
+ ],
31
+ )