sentor-ml 1.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.
sentor/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .client import SentorClient
2
+
3
+ __all__ = ["SentorClient"]
sentor/client.py ADDED
@@ -0,0 +1,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 analyzed 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://ml.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 analyze(self, documents: List[DocumentInput]):
35
+ """Analyze documents for sentiment and entity extraction.
36
+
37
+ Args:
38
+ documents: List of documents to analyze
39
+
40
+ Returns:
41
+ dict: Analysis results
42
+
43
+ Raises:
44
+ ValueError: If input is empty
45
+ RateLimitError: If API rate limit is exceeded
46
+ AuthenticationError: If API key is invalid
47
+ SentorAPIError: For other API errors
48
+ """
49
+ if not documents:
50
+ raise ValueError("Input is required")
51
+
52
+ url = f"{self.base_url}/ml/predict"
53
+ payload = {"docs": documents}
54
+ response = requests.post(
55
+ url, json=payload, headers=self.headers, timeout=self.timeout
56
+ )
57
+
58
+ if response.status_code == 200:
59
+ return response.json()
60
+ elif response.status_code == 429:
61
+ raise RateLimitError(response.json())
62
+ elif response.status_code == 401:
63
+ raise AuthenticationError(response.json())
64
+ else:
65
+ raise SentorAPIError(response.json())
66
+
67
+ def check_health(self):
68
+ """Check the health status of the Sentor API.
69
+
70
+ Returns:
71
+ dict: Health status information
72
+
73
+ Raises:
74
+ SentorAPIError: If health check fails
75
+ """
76
+ url = f"{self.base_url}/health"
77
+ response = requests.get(
78
+ url,
79
+ headers=self.headers,
80
+ timeout=self.timeout,
81
+ )
82
+
83
+ if response.status_code == 200:
84
+ return response.json()
85
+ else:
86
+ raise SentorAPIError(response.json())
sentor/exceptions.py ADDED
@@ -0,0 +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
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.4
2
+ Name: sentor-ml
3
+ Version: 1.0.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
+ ## Installation
36
+
37
+ ```bash
38
+ pip install sentor-ml
39
+ ```
40
+
41
+ ## Features
42
+
43
+ - 🚀 Python 3.7+ support
44
+ - ⚡ Simple and intuitive API
45
+ - 🌍 Support for multiple languages
46
+ - 📦 Batch processing capabilities
47
+ - 🛡️ Comprehensive error handling
48
+ - 🔄 Real-time sentiment analysis
49
+
50
+ ## Usage
51
+
52
+ ### Basic Usage
53
+
54
+ ```python
55
+ from sentor import SentorClient
56
+
57
+ # Initialize the client
58
+ client = SentorClient('your-api-key')
59
+
60
+ # Analyze sentiment
61
+ input_data = [
62
+ {
63
+ "doc": "Apple's new iPhone is amazing!",
64
+ "doc_id": "1",
65
+ "entities": [
66
+ "Apple",
67
+ "iPhone"
68
+ ]
69
+ },
70
+ {
71
+ "doc": "Samsung's new phone is amazing!",
72
+ "doc_id": "2",
73
+ "entities": [
74
+ "Samsung",
75
+ "phone"
76
+ ]
77
+ }
78
+ ]
79
+ result = client.analyze(input_data)
80
+ print(result)
81
+ ```
82
+
83
+ ### Sample Output
84
+
85
+ ```json
86
+ {
87
+ "results": [
88
+ {
89
+ "doc_id": "1",
90
+ "predicted_class": 2,
91
+ "predicted_label": "positive",
92
+ "probabilities": {
93
+ "negative": 0.00010637386003509164,
94
+ "neutral": 0.0002509312762413174,
95
+ "positive": 0.9996427297592163
96
+ }
97
+ },
98
+ {
99
+ "doc_id": "2",
100
+ "predicted_class": 2,
101
+ "predicted_label": "positive",
102
+ "probabilities": {
103
+ "negative": 0.00010637386003509164,
104
+ "neutral": 0.0002509312762413174,
105
+ "positive": 0.9996427297592163
106
+ }
107
+ }
108
+ ]
109
+ }
110
+ ```
111
+
112
+ ## API Reference
113
+
114
+ Please refer to the [Sentor ML API Documentation](https://ml.sentor.app) for more details.
115
+
116
+ ## Contributing
117
+
118
+ Contributions are welcome! Please feel free to submit a Pull Request.
119
+
120
+ ## License
121
+
122
+ MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,10 @@
1
+ sentor/__init__.py,sha256=K0z9SVT_Bkif2bj3Yqr7QtvQNTJll6an3DCuE0-rD0c,64
2
+ sentor/client.py,sha256=6Sps6UFLmUaEiE69yY8C537OQTtLteGLROs3N6om5Ss,2519
3
+ sentor/exceptions.py,sha256=MY5uFUJd_WBhCMc0zNxVvoasC1rME4Brqv82MwhDM7c,648
4
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ tests/test_client.py,sha256=kuhFtgwehMGqC0JKJfzAvqEl6UTvYo2nZDNa7YRF4rs,4101
6
+ sentor_ml-1.0.0.dist-info/METADATA,sha256=EhHpLSuh4y8VYjHGhPrrU4zGBU9VnbR8HhJd-ri0iRY,2993
7
+ sentor_ml-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ sentor_ml-1.0.0.dist-info/entry_points.txt,sha256=iYoIGXM8AusRG36JhqnBREngOd0iDOYN0C8PpiHtgXE,46
9
+ sentor_ml-1.0.0.dist-info/top_level.txt,sha256=nVfiI7y35XyGm5wyA94o24nDInXLiheycaaRtkR10ko,13
10
+ sentor_ml-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ sentor = sentor.client:main
@@ -0,0 +1,2 @@
1
+ sentor
2
+ tests
tests/__init__.py ADDED
File without changes
tests/test_client.py ADDED
@@ -0,0 +1,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://ml.sentor.app/api"
24
+ assert client.timeout == 30
25
+
26
+
27
+ @patch("requests.post")
28
+ def test_analyze_success(mock_post):
29
+ """Test successful document analysis 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 analyze method
44
+ client = SentorClient("test-key")
45
+ documents = [{"doc_id": "1", "doc": "Test text", "entities": []}]
46
+ result = client.analyze(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
+
53
+ @patch("requests.post")
54
+ def test_analyze_rate_limit(mock_post):
55
+ """Test rate limit error handling during document analysis."""
56
+ # Setup mock response for rate limit error
57
+ mock_response = Mock()
58
+ mock_response.status_code = 429
59
+ mock_response.json.return_value = {
60
+ "detail": "Rate limit exceeded",
61
+ "retry_after": 60,
62
+ }
63
+ mock_post.return_value = mock_response
64
+
65
+ # Test rate limit error
66
+ client = SentorClient("test-key")
67
+ documents = [{"doc_id": "1", "doc": "Test text", "entities": []}]
68
+
69
+ with pytest.raises(RateLimitError) as exc_info:
70
+ client.analyze(documents)
71
+
72
+ assert exc_info.value.retry_after == 60
73
+
74
+
75
+ @patch("requests.post")
76
+ def test_analyze_auth_error(mock_post):
77
+ """Test authentication error handling during document analysis."""
78
+ # Setup mock response for authentication error
79
+ mock_response = Mock()
80
+ mock_response.status_code = 401
81
+ mock_response.json.return_value = {"detail": "Invalid API key"}
82
+ mock_post.return_value = mock_response
83
+
84
+ # Test authentication error
85
+ client = SentorClient("test-key")
86
+ documents = [{"doc_id": "1", "doc": "Test text", "entities": []}]
87
+
88
+ with pytest.raises(AuthenticationError):
89
+ client.analyze(documents)
90
+
91
+
92
+ def test_analyze_empty_input():
93
+ """Test validation of empty input in document analysis."""
94
+ client = SentorClient("test-key")
95
+ with pytest.raises(ValueError, match="Input is required"):
96
+ client.analyze([])
97
+
98
+
99
+ @patch("requests.get")
100
+ def test_check_health_success(mock_get):
101
+ """Test successful health check response."""
102
+ # Setup mock response
103
+ mock_response = Mock()
104
+ mock_response.status_code = 200
105
+ mock_response.json.return_value = {"status": "healthy"}
106
+ mock_get.return_value = mock_response
107
+
108
+ # Test health check
109
+ client = SentorClient("test-key")
110
+ result = client.check_health()
111
+
112
+ assert result["status"] == "healthy"
113
+ mock_get.assert_called_once()
114
+
115
+
116
+ @patch("requests.get")
117
+ def test_check_health_error(mock_get):
118
+ """Test error handling during health check."""
119
+ # Setup mock response for error
120
+ mock_response = Mock()
121
+ mock_response.status_code = 500
122
+ mock_response.json.return_value = {"detail": "Internal server error"}
123
+ mock_get.return_value = mock_response
124
+
125
+ # Test health check error
126
+ client = SentorClient("test-key")
127
+ with pytest.raises(SentorAPIError):
128
+ client.check_health()
129
+
130
+
131
+ if __name__ == "__main__":
132
+ pytest.main([__file__])