sentor-ml 1.1.0__py3-none-any.whl → 1.3.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.
- sentor/__init__.py +3 -3
- sentor/client.py +96 -86
- sentor/exceptions.py +21 -21
- {sentor_ml-1.1.0.dist-info → sentor_ml-1.3.0.dist-info}/METADATA +232 -209
- sentor_ml-1.3.0.dist-info/RECORD +10 -0
- {sentor_ml-1.1.0.dist-info → sentor_ml-1.3.0.dist-info}/WHEEL +1 -1
- tests/test_client.py +192 -132
- sentor_ml-1.1.0.dist-info/RECORD +0 -10
- {sentor_ml-1.1.0.dist-info → sentor_ml-1.3.0.dist-info}/entry_points.txt +0 -0
- {sentor_ml-1.1.0.dist-info → sentor_ml-1.3.0.dist-info}/top_level.txt +0 -0
sentor/__init__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
from .client import SentorClient
|
|
2
|
-
|
|
3
|
-
__all__ = ["SentorClient"]
|
|
1
|
+
from .client import SentorClient
|
|
2
|
+
|
|
3
|
+
__all__ = ["SentorClient"]
|
sentor/client.py
CHANGED
|
@@ -1,86 +1,96 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
from .exceptions import SentorAPIError, RateLimitError, AuthenticationError
|
|
3
|
-
from typing import TypedDict, List
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class DocumentInput(TypedDict):
|
|
7
|
-
"""Represents a document to be
|
|
8
|
-
|
|
9
|
-
doc_id: str
|
|
10
|
-
doc: str
|
|
11
|
-
entities: List[str]
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class SentorClient:
|
|
15
|
-
"""Client for interacting with the Sentor ML API."""
|
|
16
|
-
|
|
17
|
-
def __init__(
|
|
18
|
-
self,
|
|
19
|
-
api_key: str,
|
|
20
|
-
base_url: str = "https://sentor.app/api",
|
|
21
|
-
timeout: int = 30,
|
|
22
|
-
):
|
|
23
|
-
"""
|
|
24
|
-
Initialize the Sentor client with API credentials and configuration.
|
|
25
|
-
"""
|
|
26
|
-
self.api_key = api_key
|
|
27
|
-
self.base_url = base_url
|
|
28
|
-
self.timeout = timeout
|
|
29
|
-
self.headers = {
|
|
30
|
-
"x-api-key": self.api_key,
|
|
31
|
-
"Content-Type": "application/json",
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
def
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
documents: List of documents to
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
1
|
+
import requests
|
|
2
|
+
from .exceptions import SentorAPIError, RateLimitError, AuthenticationError
|
|
3
|
+
from typing import TypedDict, List
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DocumentInput(TypedDict):
|
|
7
|
+
"""Represents a document to be predicted with its metadata."""
|
|
8
|
+
|
|
9
|
+
doc_id: str
|
|
10
|
+
doc: str
|
|
11
|
+
entities: List[str]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SentorClient:
|
|
15
|
+
"""Client for interacting with the Sentor ML API."""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
api_key: str,
|
|
20
|
+
base_url: str = "https://sentor.app/api",
|
|
21
|
+
timeout: int = 30,
|
|
22
|
+
):
|
|
23
|
+
"""
|
|
24
|
+
Initialize the Sentor client with API credentials and configuration.
|
|
25
|
+
"""
|
|
26
|
+
self.api_key = api_key
|
|
27
|
+
self.base_url = base_url
|
|
28
|
+
self.timeout = timeout
|
|
29
|
+
self.headers = {
|
|
30
|
+
"x-api-key": self.api_key,
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def predict(self, documents: List[DocumentInput], language: str = "en"):
|
|
35
|
+
"""Predict sentiment and entity extraction for documents.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
documents: List of documents to predict
|
|
39
|
+
language: Language code for prediction (default: "en").
|
|
40
|
+
Supported languages: "en", "nl"
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
dict: Prediction results
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
ValueError: If input is empty or invalid language is provided
|
|
47
|
+
RateLimitError: If API rate limit is exceeded
|
|
48
|
+
AuthenticationError: If API key is invalid
|
|
49
|
+
SentorAPIError: For other API errors
|
|
50
|
+
"""
|
|
51
|
+
if not documents:
|
|
52
|
+
raise ValueError("Input is required")
|
|
53
|
+
|
|
54
|
+
if language not in ["en", "nl"]:
|
|
55
|
+
raise ValueError("Language must be 'en' or 'nl'")
|
|
56
|
+
|
|
57
|
+
url = f"{self.base_url}/predicts"
|
|
58
|
+
params = {"language": language}
|
|
59
|
+
payload = {"docs": documents}
|
|
60
|
+
response = requests.post(
|
|
61
|
+
url,
|
|
62
|
+
json=payload,
|
|
63
|
+
headers=self.headers,
|
|
64
|
+
timeout=self.timeout,
|
|
65
|
+
params=params,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if response.status_code == 200 or response.status_code == 201:
|
|
69
|
+
return response.json()
|
|
70
|
+
elif response.status_code == 429:
|
|
71
|
+
raise RateLimitError(response.json())
|
|
72
|
+
elif response.status_code == 401:
|
|
73
|
+
raise AuthenticationError(response.json())
|
|
74
|
+
else:
|
|
75
|
+
raise SentorAPIError(response.json())
|
|
76
|
+
|
|
77
|
+
def check_health(self):
|
|
78
|
+
"""Check the health status of the Sentor API.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
dict: Health status information
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
SentorAPIError: If health check fails
|
|
85
|
+
"""
|
|
86
|
+
url = f"{self.base_url}/predicts/health"
|
|
87
|
+
response = requests.get(
|
|
88
|
+
url,
|
|
89
|
+
headers=self.headers,
|
|
90
|
+
timeout=self.timeout,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if response.status_code == 200:
|
|
94
|
+
return response.json()
|
|
95
|
+
else:
|
|
96
|
+
raise SentorAPIError(response.json())
|
sentor/exceptions.py
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
class SentorAPIError(Exception):
|
|
2
|
-
"""Base exception for all Sentor API errors."""
|
|
3
|
-
|
|
4
|
-
def __init__(self, response):
|
|
5
|
-
self.message = response.get("detail", "An error occurred")
|
|
6
|
-
self.code = response.get("status_code", "unknown")
|
|
7
|
-
super().__init__(self.message)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class RateLimitError(SentorAPIError):
|
|
11
|
-
"""Exception raised when API rate limit is exceeded."""
|
|
12
|
-
|
|
13
|
-
def __init__(self, response):
|
|
14
|
-
super().__init__(response)
|
|
15
|
-
self.retry_after = response.get("retry_after", 60)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class AuthenticationError(SentorAPIError):
|
|
19
|
-
"""Exception raised when API authentication fails."""
|
|
20
|
-
|
|
21
|
-
pass
|
|
1
|
+
class SentorAPIError(Exception):
|
|
2
|
+
"""Base exception for all Sentor API errors."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, response):
|
|
5
|
+
self.message = response.get("detail", "An error occurred")
|
|
6
|
+
self.code = response.get("status_code", "unknown")
|
|
7
|
+
super().__init__(self.message)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RateLimitError(SentorAPIError):
|
|
11
|
+
"""Exception raised when API rate limit is exceeded."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, response):
|
|
14
|
+
super().__init__(response)
|
|
15
|
+
self.retry_after = response.get("retry_after", 60)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AuthenticationError(SentorAPIError):
|
|
19
|
+
"""Exception raised when API authentication fails."""
|
|
20
|
+
|
|
21
|
+
pass
|
|
@@ -1,209 +1,232 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: sentor-ml
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary: A Python SDK for interacting with the Sentor ML API for sentiment analysis
|
|
5
|
-
Home-page: https://github.com/NIKX-Tech/sentor-ml-python-sdk
|
|
6
|
-
Author: NIKX Technologies
|
|
7
|
-
Author-email: sentor@nikx.one
|
|
8
|
-
Classifier: Development Status :: 4 - Beta
|
|
9
|
-
Classifier: Intended Audience :: Developers
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Operating System :: OS Independent
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
-
Requires-Python: >=3.7
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
Requires-Dist: requests>=2.28.0
|
|
21
|
-
Dynamic: author
|
|
22
|
-
Dynamic: author-email
|
|
23
|
-
Dynamic: classifier
|
|
24
|
-
Dynamic: description
|
|
25
|
-
Dynamic: description-content-type
|
|
26
|
-
Dynamic: home-page
|
|
27
|
-
Dynamic: requires-dist
|
|
28
|
-
Dynamic: requires-python
|
|
29
|
-
Dynamic: summary
|
|
30
|
-
|
|
31
|
-
# Sentor Python SDK
|
|
32
|
-
|
|
33
|
-
A Python SDK for interacting with the Sentor ML API for sentiment analysis. This SDK provides a simple and intuitive interface for sentiment analysis operations.
|
|
34
|
-
|
|
35
|
-
## Features
|
|
36
|
-
|
|
37
|
-
- 🚀 Python 3.7+ support
|
|
38
|
-
- ⚡ Simple and intuitive API
|
|
39
|
-
- 🌍
|
|
40
|
-
- 📦 Batch processing capabilities
|
|
41
|
-
- 🛡️ Comprehensive error handling
|
|
42
|
-
- 🔄 Real-time sentiment analysis
|
|
43
|
-
|
|
44
|
-
## Installation
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
pip install sentor-ml
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Work like a PRO
|
|
51
|
-
|
|
52
|
-
1. Go to [Sentor ML API](https://sentor.app/api)
|
|
53
|
-
2. Subscribe to the Starter plan
|
|
54
|
-
3. Get your API key
|
|
55
|
-
|
|
56
|
-
## Usage
|
|
57
|
-
|
|
58
|
-
### Basic Usage
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
from sentor import SentorClient
|
|
62
|
-
|
|
63
|
-
# Initialize the client
|
|
64
|
-
client = SentorClient('your-api-key')
|
|
65
|
-
|
|
66
|
-
#
|
|
67
|
-
input_data = [
|
|
68
|
-
{
|
|
69
|
-
"doc": "In the competitive landscape of consumer electronics, Apple and Samsung continue to lead the market with innovative products and strong brand loyalty. While Apple focuses on a tightly integrated ecosystem with devices like the iPhone, iPad, and Mac, Samsung excels in offering a wide range of options across various price points, especially in its Galaxy smartphone lineup. Both companies push the boundaries of technology, from cutting-edge chipsets to advanced camera systems, often setting industry trends that others follow.",
|
|
70
|
-
"doc_id": "0",
|
|
71
|
-
"entities": [
|
|
72
|
-
"Apple",
|
|
73
|
-
"Samsung",
|
|
74
|
-
"camera"
|
|
75
|
-
]
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
"doc": "Apple's new iPhone is amazing!",
|
|
79
|
-
"doc_id": "1",
|
|
80
|
-
"entities": [
|
|
81
|
-
"Apple",
|
|
82
|
-
"iPhone"
|
|
83
|
-
]
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
"doc": "Samsung's new phone is amazing!",
|
|
87
|
-
"doc_id": "2",
|
|
88
|
-
"entities": [
|
|
89
|
-
"Samsung",
|
|
90
|
-
"phone"
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
]
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
|
|
142
|
-
"
|
|
143
|
-
"
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
{
|
|
159
|
-
"sentence_index":
|
|
160
|
-
"sentence_text": "
|
|
161
|
-
"predicted_class": 2,
|
|
162
|
-
"predicted_label": "positive",
|
|
163
|
-
"probabilities": {
|
|
164
|
-
"negative": 0.
|
|
165
|
-
"neutral": 0.
|
|
166
|
-
"positive": 0.
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
]
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
"doc_id": "
|
|
173
|
-
"predicted_class": 2,
|
|
174
|
-
"predicted_label": "positive",
|
|
175
|
-
"probabilities": {
|
|
176
|
-
"negative": 0.00010637375817168504,
|
|
177
|
-
"neutral": 0.0002509312762413174,
|
|
178
|
-
"positive": 0.9996427297592163
|
|
179
|
-
},
|
|
180
|
-
"details": [
|
|
181
|
-
{
|
|
182
|
-
"sentence_index": 0,
|
|
183
|
-
"sentence_text": "
|
|
184
|
-
"predicted_class": 2,
|
|
185
|
-
"predicted_label": "positive",
|
|
186
|
-
"probabilities": {
|
|
187
|
-
"negative": 0.00010637375817168504,
|
|
188
|
-
"neutral": 0.0002509312762413174,
|
|
189
|
-
"positive": 0.9996427297592163
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
]
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sentor-ml
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: A Python SDK for interacting with the Sentor ML API for sentiment analysis
|
|
5
|
+
Home-page: https://github.com/NIKX-Tech/sentor-ml-python-sdk
|
|
6
|
+
Author: NIKX Technologies
|
|
7
|
+
Author-email: sentor@nikx.one
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Requires-Python: >=3.7
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: requests>=2.28.0
|
|
21
|
+
Dynamic: author
|
|
22
|
+
Dynamic: author-email
|
|
23
|
+
Dynamic: classifier
|
|
24
|
+
Dynamic: description
|
|
25
|
+
Dynamic: description-content-type
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: requires-dist
|
|
28
|
+
Dynamic: requires-python
|
|
29
|
+
Dynamic: summary
|
|
30
|
+
|
|
31
|
+
# Sentor Python SDK
|
|
32
|
+
|
|
33
|
+
A Python SDK for interacting with the Sentor ML API for sentiment analysis. This SDK provides a simple and intuitive interface for sentiment analysis operations.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- 🚀 Python 3.7+ support
|
|
38
|
+
- ⚡ Simple and intuitive API
|
|
39
|
+
- 🌍 Multi-language support (English and Dutch)
|
|
40
|
+
- 📦 Batch processing capabilities
|
|
41
|
+
- 🛡️ Comprehensive error handling
|
|
42
|
+
- 🔄 Real-time sentiment analysis
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install sentor-ml
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Work like a PRO
|
|
51
|
+
|
|
52
|
+
1. Go to [Sentor ML API](https://sentor.app/api)
|
|
53
|
+
2. Subscribe to the Starter plan
|
|
54
|
+
3. Get your API key
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
### Basic Usage
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from sentor import SentorClient
|
|
62
|
+
|
|
63
|
+
# Initialize the client
|
|
64
|
+
client = SentorClient('your-api-key')
|
|
65
|
+
|
|
66
|
+
# Predict sentiment
|
|
67
|
+
input_data = [
|
|
68
|
+
{
|
|
69
|
+
"doc": "In the competitive landscape of consumer electronics, Apple and Samsung continue to lead the market with innovative products and strong brand loyalty. While Apple focuses on a tightly integrated ecosystem with devices like the iPhone, iPad, and Mac, Samsung excels in offering a wide range of options across various price points, especially in its Galaxy smartphone lineup. Both companies push the boundaries of technology, from cutting-edge chipsets to advanced camera systems, often setting industry trends that others follow.",
|
|
70
|
+
"doc_id": "0",
|
|
71
|
+
"entities": [
|
|
72
|
+
"Apple",
|
|
73
|
+
"Samsung",
|
|
74
|
+
"camera"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"doc": "Apple's new iPhone is amazing!",
|
|
79
|
+
"doc_id": "1",
|
|
80
|
+
"entities": [
|
|
81
|
+
"Apple",
|
|
82
|
+
"iPhone"
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"doc": "Samsung's new phone is amazing!",
|
|
87
|
+
"doc_id": "2",
|
|
88
|
+
"entities": [
|
|
89
|
+
"Samsung",
|
|
90
|
+
"phone"
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
# Predict with default language (English)
|
|
95
|
+
result = client.predict(input_data)
|
|
96
|
+
print(result)
|
|
97
|
+
|
|
98
|
+
# Predict with Dutch language
|
|
99
|
+
result_nl = client.predict(input_data, language="nl")
|
|
100
|
+
print(result_nl)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Language Support
|
|
104
|
+
|
|
105
|
+
The SDK supports multi-language sentiment analysis with the following options:
|
|
106
|
+
|
|
107
|
+
- `"en"` (default): English language prediction
|
|
108
|
+
- `"nl"`: Dutch language prediction
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
# Default English prediction
|
|
112
|
+
result_en = client.predict(documents)
|
|
113
|
+
|
|
114
|
+
# Explicitly specify English
|
|
115
|
+
result_en = client.predict(documents, language="en")
|
|
116
|
+
|
|
117
|
+
# Dutch language prediction
|
|
118
|
+
result_nl = client.predict(documents, language="nl")
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Sample Output
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"results": [
|
|
126
|
+
{
|
|
127
|
+
"doc_id": "0",
|
|
128
|
+
"predicted_class": 2,
|
|
129
|
+
"predicted_label": "positive",
|
|
130
|
+
"probabilities": {
|
|
131
|
+
"negative": 0.00007679959526285529,
|
|
132
|
+
"neutral": 0.0002924697764683515,
|
|
133
|
+
"positive": 0.9996306896209717
|
|
134
|
+
},
|
|
135
|
+
"details": [
|
|
136
|
+
{
|
|
137
|
+
"sentence_index": 0,
|
|
138
|
+
"sentence_text": "In the competitive landscape of consumer electronics, Apple and Samsung continue to lead the market with innovative products and strong brand loyalty.",
|
|
139
|
+
"predicted_class": 2,
|
|
140
|
+
"predicted_label": "positive",
|
|
141
|
+
"probabilities": {
|
|
142
|
+
"negative": 0.00009389198385179043,
|
|
143
|
+
"neutral": 0.00032428017584607005,
|
|
144
|
+
"positive": 0.9995818734169006
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"sentence_index": 1,
|
|
149
|
+
"sentence_text": "While Apple focuses on a tightly integrated ecosystem with devices like the iPhone, iPad, and Mac, Samsung excels in offering a wide range of options across various price points, especially in its Galaxy smartphone lineup.",
|
|
150
|
+
"predicted_class": 2,
|
|
151
|
+
"predicted_label": "positive",
|
|
152
|
+
"probabilities": {
|
|
153
|
+
"negative": 0.00005746580063714646,
|
|
154
|
+
"neutral": 0.00012963586777914315,
|
|
155
|
+
"positive": 0.99981290102005
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"sentence_index": 2,
|
|
160
|
+
"sentence_text": "Both companies push the boundaries of technology, from cutting-edge chipsets to advanced camera systems, often setting industry trends that others follow.",
|
|
161
|
+
"predicted_class": 2,
|
|
162
|
+
"predicted_label": "positive",
|
|
163
|
+
"probabilities": {
|
|
164
|
+
"negative": 0.00006366783054545522,
|
|
165
|
+
"neutral": 0.00044553453335538507,
|
|
166
|
+
"positive": 0.9994907379150391
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"doc_id": "1",
|
|
173
|
+
"predicted_class": 2,
|
|
174
|
+
"predicted_label": "positive",
|
|
175
|
+
"probabilities": {
|
|
176
|
+
"negative": 0.00010637375817168504,
|
|
177
|
+
"neutral": 0.0002509312762413174,
|
|
178
|
+
"positive": 0.9996427297592163
|
|
179
|
+
},
|
|
180
|
+
"details": [
|
|
181
|
+
{
|
|
182
|
+
"sentence_index": 0,
|
|
183
|
+
"sentence_text": "Apple's new iPhone is amazing!",
|
|
184
|
+
"predicted_class": 2,
|
|
185
|
+
"predicted_label": "positive",
|
|
186
|
+
"probabilities": {
|
|
187
|
+
"negative": 0.00010637375817168504,
|
|
188
|
+
"neutral": 0.0002509312762413174,
|
|
189
|
+
"positive": 0.9996427297592163
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"doc_id": "2",
|
|
196
|
+
"predicted_class": 2,
|
|
197
|
+
"predicted_label": "positive",
|
|
198
|
+
"probabilities": {
|
|
199
|
+
"negative": 0.00010637375817168504,
|
|
200
|
+
"neutral": 0.0002509312762413174,
|
|
201
|
+
"positive": 0.9996427297592163
|
|
202
|
+
},
|
|
203
|
+
"details": [
|
|
204
|
+
{
|
|
205
|
+
"sentence_index": 0,
|
|
206
|
+
"sentence_text": "Samsung's new phone is amazing!",
|
|
207
|
+
"predicted_class": 2,
|
|
208
|
+
"predicted_label": "positive",
|
|
209
|
+
"probabilities": {
|
|
210
|
+
"negative": 0.00010637375817168504,
|
|
211
|
+
"neutral": 0.0002509312762413174,
|
|
212
|
+
"positive": 0.9996427297592163
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## API Reference
|
|
222
|
+
|
|
223
|
+
Please refer to the [Sentor ML API Documentation](https://sentor.app/docs) for more details.
|
|
224
|
+
You can also try the API in the [Sentor ML API Swagger Playground](https://sentor.app/docs).
|
|
225
|
+
|
|
226
|
+
## Contributing
|
|
227
|
+
|
|
228
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
sentor/__init__.py,sha256=Eg5MxBR7AgFAISpY3b4qNvRphTEv1y6Qyy8Qvz3L1cI,61
|
|
2
|
+
sentor/client.py,sha256=WFHBXkzlqjuLwLzRUbAhIkUKZ7k_ouWjQjdB3yjTwe8,2849
|
|
3
|
+
sentor/exceptions.py,sha256=xxL1JZxYLZCzoCrSzLnFDxtOreMMBKrwxXhlMl4UI_Q,627
|
|
4
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
tests/test_client.py,sha256=sYEe7Vf0dumhSxsLkxVGAdx9gEgO7EurphIGwpcuVkM,6070
|
|
6
|
+
sentor_ml-1.3.0.dist-info/METADATA,sha256=E2uY8w2SfHohHyPELJhxbKYjysfzuG5Y_JgPZkZcL6M,6906
|
|
7
|
+
sentor_ml-1.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
8
|
+
sentor_ml-1.3.0.dist-info/entry_points.txt,sha256=iYoIGXM8AusRG36JhqnBREngOd0iDOYN0C8PpiHtgXE,46
|
|
9
|
+
sentor_ml-1.3.0.dist-info/top_level.txt,sha256=nVfiI7y35XyGm5wyA94o24nDInXLiheycaaRtkR10ko,13
|
|
10
|
+
sentor_ml-1.3.0.dist-info/RECORD,,
|
tests/test_client.py
CHANGED
|
@@ -1,132 +1,192 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from sentor import SentorClient
|
|
3
|
-
from sentor.exceptions import (
|
|
4
|
-
SentorAPIError,
|
|
5
|
-
RateLimitError,
|
|
6
|
-
AuthenticationError,
|
|
7
|
-
)
|
|
8
|
-
from unittest.mock import Mock, patch
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@pytest.fixture
|
|
12
|
-
def mock_client():
|
|
13
|
-
"""Create a mock SentorClient instance for testing."""
|
|
14
|
-
client = SentorClient("test-key")
|
|
15
|
-
return client
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def test_client_initialization():
|
|
19
|
-
"""Test proper initialization of SentorClient with default parameters."""
|
|
20
|
-
client = SentorClient(api_key="test-key")
|
|
21
|
-
assert isinstance(client, SentorClient)
|
|
22
|
-
assert client.api_key == "test-key"
|
|
23
|
-
assert client.base_url == "https://sentor.app/api"
|
|
24
|
-
assert client.timeout == 30
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@patch("requests.post")
|
|
28
|
-
def
|
|
29
|
-
"""Test successful document
|
|
30
|
-
# Setup mock response
|
|
31
|
-
mock_response = Mock()
|
|
32
|
-
mock_response.status_code = 200
|
|
33
|
-
mock_response.json.return_value = {
|
|
34
|
-
"results": [
|
|
35
|
-
{
|
|
36
|
-
"predicted_label": "positive",
|
|
37
|
-
"probabilities": {"positive": 0.95},
|
|
38
|
-
}
|
|
39
|
-
]
|
|
40
|
-
}
|
|
41
|
-
mock_post.return_value = mock_response
|
|
42
|
-
|
|
43
|
-
# Test the
|
|
44
|
-
client = SentorClient("test-key")
|
|
45
|
-
documents = [{"doc_id": "1", "doc": "Test text", "entities": []}]
|
|
46
|
-
result = client.
|
|
47
|
-
|
|
48
|
-
assert result["results"][0]["predicted_label"] == "positive"
|
|
49
|
-
assert result["results"][0]["probabilities"]["positive"] == 0.95
|
|
50
|
-
mock_post.assert_called_once()
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
1
|
+
import pytest
|
|
2
|
+
from sentor import SentorClient
|
|
3
|
+
from sentor.exceptions import (
|
|
4
|
+
SentorAPIError,
|
|
5
|
+
RateLimitError,
|
|
6
|
+
AuthenticationError,
|
|
7
|
+
)
|
|
8
|
+
from unittest.mock import Mock, patch
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def mock_client():
|
|
13
|
+
"""Create a mock SentorClient instance for testing."""
|
|
14
|
+
client = SentorClient("test-key")
|
|
15
|
+
return client
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_client_initialization():
|
|
19
|
+
"""Test proper initialization of SentorClient with default parameters."""
|
|
20
|
+
client = SentorClient(api_key="test-key")
|
|
21
|
+
assert isinstance(client, SentorClient)
|
|
22
|
+
assert client.api_key == "test-key"
|
|
23
|
+
assert client.base_url == "https://sentor.app/api"
|
|
24
|
+
assert client.timeout == 30
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@patch("requests.post")
|
|
28
|
+
def test_predict_success(mock_post):
|
|
29
|
+
"""Test successful document prediction with positive sentiment."""
|
|
30
|
+
# Setup mock response
|
|
31
|
+
mock_response = Mock()
|
|
32
|
+
mock_response.status_code = 200
|
|
33
|
+
mock_response.json.return_value = {
|
|
34
|
+
"results": [
|
|
35
|
+
{
|
|
36
|
+
"predicted_label": "positive",
|
|
37
|
+
"probabilities": {"positive": 0.95},
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
mock_post.return_value = mock_response
|
|
42
|
+
|
|
43
|
+
# Test the predict method
|
|
44
|
+
client = SentorClient("test-key")
|
|
45
|
+
documents = [{"doc_id": "1", "doc": "Test text", "entities": []}]
|
|
46
|
+
result = client.predict(documents)
|
|
47
|
+
|
|
48
|
+
assert result["results"][0]["predicted_label"] == "positive"
|
|
49
|
+
assert result["results"][0]["probabilities"]["positive"] == 0.95
|
|
50
|
+
mock_post.assert_called_once()
|
|
51
|
+
|
|
52
|
+
# Verify the language parameter was passed correctly
|
|
53
|
+
call_args = mock_post.call_args
|
|
54
|
+
assert call_args[1]["params"] == {"language": "en"}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@patch("requests.post")
|
|
58
|
+
def test_predict_rate_limit(mock_post):
|
|
59
|
+
"""Test rate limit error handling during document prediction."""
|
|
60
|
+
# Setup mock response for rate limit error
|
|
61
|
+
mock_response = Mock()
|
|
62
|
+
mock_response.status_code = 429
|
|
63
|
+
mock_response.json.return_value = {
|
|
64
|
+
"detail": "Rate limit exceeded",
|
|
65
|
+
"retry_after": 60,
|
|
66
|
+
}
|
|
67
|
+
mock_post.return_value = mock_response
|
|
68
|
+
|
|
69
|
+
# Test rate limit error
|
|
70
|
+
client = SentorClient("test-key")
|
|
71
|
+
documents = [{"doc_id": "1", "doc": "Test text", "entities": []}]
|
|
72
|
+
|
|
73
|
+
with pytest.raises(RateLimitError) as exc_info:
|
|
74
|
+
client.predict(documents)
|
|
75
|
+
|
|
76
|
+
assert exc_info.value.retry_after == 60
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@patch("requests.post")
|
|
80
|
+
def test_predict_auth_error(mock_post):
|
|
81
|
+
"""Test authentication error handling during document prediction."""
|
|
82
|
+
# Setup mock response for authentication error
|
|
83
|
+
mock_response = Mock()
|
|
84
|
+
mock_response.status_code = 401
|
|
85
|
+
mock_response.json.return_value = {"detail": "Invalid API key"}
|
|
86
|
+
mock_post.return_value = mock_response
|
|
87
|
+
|
|
88
|
+
# Test authentication error
|
|
89
|
+
client = SentorClient("test-key")
|
|
90
|
+
documents = [{"doc_id": "1", "doc": "Test text", "entities": []}]
|
|
91
|
+
|
|
92
|
+
with pytest.raises(AuthenticationError):
|
|
93
|
+
client.predict(documents)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_predict_empty_input():
|
|
97
|
+
"""Test validation of empty input in document prediction."""
|
|
98
|
+
client = SentorClient("test-key")
|
|
99
|
+
with pytest.raises(ValueError, match="Input is required"):
|
|
100
|
+
client.predict([])
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@patch("requests.post")
|
|
104
|
+
def test_predict_with_dutch_language(mock_post):
|
|
105
|
+
"""Test document prediction with Dutch language parameter."""
|
|
106
|
+
# Setup mock response
|
|
107
|
+
mock_response = Mock()
|
|
108
|
+
mock_response.status_code = 200
|
|
109
|
+
mock_response.json.return_value = {
|
|
110
|
+
"results": [
|
|
111
|
+
{
|
|
112
|
+
"predicted_label": "positive",
|
|
113
|
+
"probabilities": {"positive": 0.95},
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
mock_post.return_value = mock_response
|
|
118
|
+
|
|
119
|
+
# Test the predict method with Dutch language
|
|
120
|
+
client = SentorClient("test-key")
|
|
121
|
+
documents = [{"doc_id": "1", "doc": "Test text", "entities": []}]
|
|
122
|
+
result = client.predict(documents, language="nl")
|
|
123
|
+
|
|
124
|
+
assert result["results"][0]["predicted_label"] == "positive"
|
|
125
|
+
|
|
126
|
+
# Verify the language parameter was passed correctly
|
|
127
|
+
call_args = mock_post.call_args
|
|
128
|
+
assert call_args[1]["params"] == {"language": "nl"}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_predict_invalid_language():
|
|
132
|
+
"""Test validation of invalid language parameter."""
|
|
133
|
+
client = SentorClient("test-key")
|
|
134
|
+
documents = [{"doc_id": "1", "doc": "Test text", "entities": []}]
|
|
135
|
+
|
|
136
|
+
with pytest.raises(ValueError, match="Language must be 'en' or 'nl'"):
|
|
137
|
+
client.predict(documents, language="invalid")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_predict_explicit_english_language():
|
|
141
|
+
"""Test document prediction with explicit English language parameter."""
|
|
142
|
+
client = SentorClient("test-key")
|
|
143
|
+
documents = [{"doc_id": "1", "doc": "Test text", "entities": []}]
|
|
144
|
+
|
|
145
|
+
# This should not raise an error
|
|
146
|
+
with patch("requests.post") as mock_post:
|
|
147
|
+
mock_response = Mock()
|
|
148
|
+
mock_response.status_code = 200
|
|
149
|
+
mock_response.json.return_value = {"results": []}
|
|
150
|
+
mock_post.return_value = mock_response
|
|
151
|
+
|
|
152
|
+
client.predict(documents, language="en")
|
|
153
|
+
|
|
154
|
+
# Verify the language parameter was passed correctly
|
|
155
|
+
call_args = mock_post.call_args
|
|
156
|
+
assert call_args[1]["params"] == {"language": "en"}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@patch("requests.get")
|
|
160
|
+
def test_check_health_success(mock_get):
|
|
161
|
+
"""Test successful health check response."""
|
|
162
|
+
# Setup mock response
|
|
163
|
+
mock_response = Mock()
|
|
164
|
+
mock_response.status_code = 200
|
|
165
|
+
mock_response.json.return_value = {"status": "healthy"}
|
|
166
|
+
mock_get.return_value = mock_response
|
|
167
|
+
|
|
168
|
+
# Test health check
|
|
169
|
+
client = SentorClient("test-key")
|
|
170
|
+
result = client.check_health()
|
|
171
|
+
|
|
172
|
+
assert result["status"] == "healthy"
|
|
173
|
+
mock_get.assert_called_once()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@patch("requests.get")
|
|
177
|
+
def test_check_health_error(mock_get):
|
|
178
|
+
"""Test error handling during health check."""
|
|
179
|
+
# Setup mock response for error
|
|
180
|
+
mock_response = Mock()
|
|
181
|
+
mock_response.status_code = 500
|
|
182
|
+
mock_response.json.return_value = {"detail": "Internal server error"}
|
|
183
|
+
mock_get.return_value = mock_response
|
|
184
|
+
|
|
185
|
+
# Test health check error
|
|
186
|
+
client = SentorClient("test-key")
|
|
187
|
+
with pytest.raises(SentorAPIError):
|
|
188
|
+
client.check_health()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
if __name__ == "__main__":
|
|
192
|
+
pytest.main([__file__])
|
sentor_ml-1.1.0.dist-info/RECORD
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
sentor/__init__.py,sha256=K0z9SVT_Bkif2bj3Yqr7QtvQNTJll6an3DCuE0-rD0c,64
|
|
2
|
-
sentor/client.py,sha256=-60oSDcdWCDXDhj4p_snvbQBRG16Fy_i1-U8CePFWZw,2523
|
|
3
|
-
sentor/exceptions.py,sha256=MY5uFUJd_WBhCMc0zNxVvoasC1rME4Brqv82MwhDM7c,648
|
|
4
|
-
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
tests/test_client.py,sha256=QMCyMt1n5e_-guT3yf5yVk_vKssHjkaGBuKd18ZixKg,4098
|
|
6
|
-
sentor_ml-1.1.0.dist-info/METADATA,sha256=v5U57qWTrIbf5-TcaUowylE1HeyiSJhkGW0Z0pSU1H0,6525
|
|
7
|
-
sentor_ml-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
sentor_ml-1.1.0.dist-info/entry_points.txt,sha256=iYoIGXM8AusRG36JhqnBREngOd0iDOYN0C8PpiHtgXE,46
|
|
9
|
-
sentor_ml-1.1.0.dist-info/top_level.txt,sha256=nVfiI7y35XyGm5wyA94o24nDInXLiheycaaRtkR10ko,13
|
|
10
|
-
sentor_ml-1.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|