paygent-sdk 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.
- examples/__init__.py +1 -0
- examples/advanced_usage.py +101 -0
- examples/basic_usage.py +38 -0
- examples/constants_usage.py +136 -0
- paygent_sdk/__init__.py +47 -0
- paygent_sdk/client.py +464 -0
- paygent_sdk/constants.py +217 -0
- paygent_sdk/models.py +485 -0
- paygent_sdk-1.0.0.dist-info/METADATA +383 -0
- paygent_sdk-1.0.0.dist-info/RECORD +15 -0
- paygent_sdk-1.0.0.dist-info/WHEEL +5 -0
- paygent_sdk-1.0.0.dist-info/licenses/LICENSE +21 -0
- paygent_sdk-1.0.0.dist-info/top_level.txt +3 -0
- tests/__init__.py +1 -0
- tests/test_client.py +277 -0
tests/test_client.py
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for the Paygent SDK client.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import unittest
|
|
8
|
+
from unittest.mock import Mock, patch, MagicMock
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
from paygent_sdk.client import Client
|
|
13
|
+
from paygent_sdk.models import UsageData, UsageDataWithStrings, APIRequest, ModelPricing, MODEL_PRICING
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestClient(unittest.TestCase):
|
|
17
|
+
"""Test cases for the Client class."""
|
|
18
|
+
|
|
19
|
+
def setUp(self):
|
|
20
|
+
"""Set up test fixtures."""
|
|
21
|
+
self.api_key = "test-api-key"
|
|
22
|
+
self.base_url = "https://api.paygent.com"
|
|
23
|
+
self.client = Client(self.api_key, self.base_url)
|
|
24
|
+
|
|
25
|
+
def test_new_client(self):
|
|
26
|
+
"""Test creating a new client with default URL."""
|
|
27
|
+
client = Client.new_client(self.api_key)
|
|
28
|
+
self.assertIsNotNone(client)
|
|
29
|
+
self.assertEqual(client.api_key, self.api_key)
|
|
30
|
+
self.assertEqual(client.base_url, "https://api.paygent.com")
|
|
31
|
+
|
|
32
|
+
def test_new_client_with_url(self):
|
|
33
|
+
"""Test creating a new client with custom URL."""
|
|
34
|
+
custom_url = "https://custom-api.paygent.com"
|
|
35
|
+
client = Client.new_client_with_url(self.api_key, custom_url)
|
|
36
|
+
self.assertIsNotNone(client)
|
|
37
|
+
self.assertEqual(client.api_key, self.api_key)
|
|
38
|
+
self.assertEqual(client.base_url, custom_url)
|
|
39
|
+
|
|
40
|
+
def test_calculate_cost_llama(self):
|
|
41
|
+
"""Test cost calculation for Llama model."""
|
|
42
|
+
usage_data = UsageData(
|
|
43
|
+
service_provider="openai",
|
|
44
|
+
model="llama",
|
|
45
|
+
prompt_tokens=1000,
|
|
46
|
+
completion_tokens=500,
|
|
47
|
+
total_tokens=1500
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
cost = self.client._calculate_cost("llama", usage_data)
|
|
51
|
+
expected = 0.00015 # (1000/1000 + 500/1000) * 0.0001
|
|
52
|
+
self.assertAlmostEqual(cost, expected, places=6)
|
|
53
|
+
|
|
54
|
+
def test_calculate_cost_gpt4(self):
|
|
55
|
+
"""Test cost calculation for GPT-4 model."""
|
|
56
|
+
usage_data = UsageData(
|
|
57
|
+
service_provider="openai",
|
|
58
|
+
model="gpt-4",
|
|
59
|
+
prompt_tokens=1000,
|
|
60
|
+
completion_tokens=500,
|
|
61
|
+
total_tokens=1500
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
cost = self.client._calculate_cost("gpt-4", usage_data)
|
|
65
|
+
expected = 0.06 # (1000/1000) * 0.03 + (500/1000) * 0.06
|
|
66
|
+
self.assertEqual(cost, expected)
|
|
67
|
+
|
|
68
|
+
def test_calculate_cost_unknown_model(self):
|
|
69
|
+
"""Test cost calculation for unknown model (default pricing)."""
|
|
70
|
+
usage_data = UsageData(
|
|
71
|
+
service_provider="custom",
|
|
72
|
+
model="unknown-model",
|
|
73
|
+
prompt_tokens=1000,
|
|
74
|
+
completion_tokens=500,
|
|
75
|
+
total_tokens=1500
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
cost = self.client._calculate_cost("unknown-model", usage_data)
|
|
79
|
+
expected = 0.00015 # (1000/1000 + 500/1000) * 0.0001 (default pricing)
|
|
80
|
+
self.assertAlmostEqual(cost, expected, places=6)
|
|
81
|
+
|
|
82
|
+
@patch('paygent_sdk.client.requests.Session.post')
|
|
83
|
+
def test_send_usage_success(self, mock_post):
|
|
84
|
+
"""Test successful usage data sending."""
|
|
85
|
+
# Mock successful response
|
|
86
|
+
mock_response = Mock()
|
|
87
|
+
mock_response.status_code = 200
|
|
88
|
+
mock_response.text = "Success"
|
|
89
|
+
mock_post.return_value = mock_response
|
|
90
|
+
|
|
91
|
+
usage_data = UsageData(
|
|
92
|
+
service_provider="openai",
|
|
93
|
+
model="llama",
|
|
94
|
+
prompt_tokens=1000,
|
|
95
|
+
completion_tokens=500,
|
|
96
|
+
total_tokens=1500
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Should not raise any exception
|
|
100
|
+
self.client.send_usage("agent-123", "customer-456", "test-indicator", usage_data)
|
|
101
|
+
|
|
102
|
+
# Verify the request was made correctly
|
|
103
|
+
mock_post.assert_called_once()
|
|
104
|
+
call_args = mock_post.call_args
|
|
105
|
+
|
|
106
|
+
# Check URL
|
|
107
|
+
self.assertEqual(call_args[0][0], "https://api.paygent.com/api/v1/usage")
|
|
108
|
+
|
|
109
|
+
# Check headers
|
|
110
|
+
headers = call_args[1]['headers']
|
|
111
|
+
self.assertEqual(headers['Content-Type'], 'application/json')
|
|
112
|
+
self.assertEqual(headers['paygent-api-key'], 'test-api-key')
|
|
113
|
+
|
|
114
|
+
# Check request data
|
|
115
|
+
request_data = call_args[1]['json']
|
|
116
|
+
self.assertEqual(request_data['agentId'], 'agent-123')
|
|
117
|
+
self.assertEqual(request_data['customerId'], 'customer-456')
|
|
118
|
+
self.assertEqual(request_data['indicator'], 'test-indicator')
|
|
119
|
+
self.assertAlmostEqual(request_data['amount'], 0.00015, places=6) # Expected cost for llama model
|
|
120
|
+
self.assertEqual(request_data['inputToken'], 1000) # prompt_tokens
|
|
121
|
+
self.assertEqual(request_data['outputToken'], 500) # completion_tokens
|
|
122
|
+
self.assertEqual(request_data['model'], 'llama') # model
|
|
123
|
+
self.assertEqual(request_data['serviceProvider'], 'openai') # service_provider
|
|
124
|
+
|
|
125
|
+
@patch('paygent_sdk.client.requests.Session.post')
|
|
126
|
+
def test_send_usage_http_error(self, mock_post):
|
|
127
|
+
"""Test handling of HTTP errors."""
|
|
128
|
+
# Mock HTTP error response
|
|
129
|
+
mock_response = Mock()
|
|
130
|
+
mock_response.status_code = 400
|
|
131
|
+
mock_response.text = "Bad Request"
|
|
132
|
+
mock_response.raise_for_status.side_effect = requests.HTTPError("400 Bad Request")
|
|
133
|
+
mock_post.return_value = mock_response
|
|
134
|
+
|
|
135
|
+
usage_data = UsageData(
|
|
136
|
+
service_provider="openai",
|
|
137
|
+
model="llama",
|
|
138
|
+
prompt_tokens=1000,
|
|
139
|
+
completion_tokens=500,
|
|
140
|
+
total_tokens=1500
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Should raise HTTPError
|
|
144
|
+
with self.assertRaises(requests.HTTPError):
|
|
145
|
+
self.client.send_usage("agent-123", "customer-456", "test-indicator", usage_data)
|
|
146
|
+
|
|
147
|
+
@patch('paygent_sdk.client.requests.Session.post')
|
|
148
|
+
def test_send_usage_network_error(self, mock_post):
|
|
149
|
+
"""Test handling of network errors."""
|
|
150
|
+
# Mock network error
|
|
151
|
+
mock_post.side_effect = requests.ConnectionError("Network error")
|
|
152
|
+
|
|
153
|
+
usage_data = UsageData(
|
|
154
|
+
service_provider="openai",
|
|
155
|
+
model="llama",
|
|
156
|
+
prompt_tokens=1000,
|
|
157
|
+
completion_tokens=500,
|
|
158
|
+
total_tokens=1500
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Should raise ConnectionError
|
|
162
|
+
with self.assertRaises(requests.ConnectionError):
|
|
163
|
+
self.client.send_usage("agent-123", "customer-456", "test-indicator", usage_data)
|
|
164
|
+
|
|
165
|
+
def test_set_log_level(self):
|
|
166
|
+
"""Test setting log level."""
|
|
167
|
+
self.client.set_log_level(logging.DEBUG)
|
|
168
|
+
self.assertEqual(self.client.logger.level, logging.DEBUG)
|
|
169
|
+
|
|
170
|
+
def test_get_logger(self):
|
|
171
|
+
"""Test getting logger instance."""
|
|
172
|
+
logger = self.client.get_logger()
|
|
173
|
+
self.assertIsNotNone(logger)
|
|
174
|
+
self.assertEqual(logger, self.client.logger)
|
|
175
|
+
|
|
176
|
+
@patch('paygent_sdk.client.requests.Session.post')
|
|
177
|
+
def test_send_usage_with_token_string_success(self, mock_post):
|
|
178
|
+
"""Test successful usage data sending with token strings."""
|
|
179
|
+
# Mock successful response
|
|
180
|
+
mock_response = Mock()
|
|
181
|
+
mock_response.status_code = 200
|
|
182
|
+
mock_response.text = "Success"
|
|
183
|
+
mock_post.return_value = mock_response
|
|
184
|
+
|
|
185
|
+
usage_data = UsageDataWithStrings(
|
|
186
|
+
service_provider="openai",
|
|
187
|
+
model="llama",
|
|
188
|
+
prompt_string="Hello, world!",
|
|
189
|
+
output_string="Hi there!"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Should not raise any exception
|
|
193
|
+
self.client.send_usage_with_token_string("agent-123", "customer-456", "test-indicator", usage_data)
|
|
194
|
+
|
|
195
|
+
# Verify the request was made correctly
|
|
196
|
+
mock_post.assert_called_once()
|
|
197
|
+
call_args = mock_post.call_args
|
|
198
|
+
|
|
199
|
+
# Check URL
|
|
200
|
+
self.assertEqual(call_args[0][0], "https://api.paygent.com/api/v1/usage")
|
|
201
|
+
|
|
202
|
+
# Check headers
|
|
203
|
+
headers = call_args[1]['headers']
|
|
204
|
+
self.assertEqual(headers['Content-Type'], 'application/json')
|
|
205
|
+
self.assertEqual(headers['paygent-api-key'], 'test-api-key')
|
|
206
|
+
|
|
207
|
+
# Check request data
|
|
208
|
+
request_data = call_args[1]['json']
|
|
209
|
+
self.assertEqual(request_data['agentId'], 'agent-123')
|
|
210
|
+
self.assertEqual(request_data['customerId'], 'customer-456')
|
|
211
|
+
self.assertEqual(request_data['indicator'], 'test-indicator')
|
|
212
|
+
self.assertIn('amount', request_data) # Amount should be calculated
|
|
213
|
+
self.assertIn('inputToken', request_data) # inputToken should be present
|
|
214
|
+
self.assertIn('outputToken', request_data) # outputToken should be present
|
|
215
|
+
self.assertIsInstance(request_data['inputToken'], int) # inputToken should be integer
|
|
216
|
+
self.assertIsInstance(request_data['outputToken'], int) # outputToken should be integer
|
|
217
|
+
self.assertEqual(request_data['model'], 'llama') # model
|
|
218
|
+
self.assertEqual(request_data['serviceProvider'], 'openai') # service_provider
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class TestModels(unittest.TestCase):
|
|
222
|
+
"""Test cases for data models."""
|
|
223
|
+
|
|
224
|
+
def test_usage_data(self):
|
|
225
|
+
"""Test UsageData model."""
|
|
226
|
+
usage_data = UsageData(
|
|
227
|
+
service_provider="openai",
|
|
228
|
+
model="llama",
|
|
229
|
+
prompt_tokens=1000,
|
|
230
|
+
completion_tokens=500,
|
|
231
|
+
total_tokens=1500
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
self.assertEqual(usage_data.service_provider, "openai")
|
|
235
|
+
self.assertEqual(usage_data.model, "llama")
|
|
236
|
+
self.assertEqual(usage_data.prompt_tokens, 1000)
|
|
237
|
+
self.assertEqual(usage_data.completion_tokens, 500)
|
|
238
|
+
self.assertEqual(usage_data.total_tokens, 1500)
|
|
239
|
+
|
|
240
|
+
def test_api_request(self):
|
|
241
|
+
"""Test APIRequest model."""
|
|
242
|
+
api_request = APIRequest(
|
|
243
|
+
agent_id="agent-123",
|
|
244
|
+
customer_id="customer-456",
|
|
245
|
+
indicator="test-indicator",
|
|
246
|
+
amount=0.15
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
self.assertEqual(api_request.agent_id, "agent-123")
|
|
250
|
+
self.assertEqual(api_request.customer_id, "customer-456")
|
|
251
|
+
self.assertEqual(api_request.indicator, "test-indicator")
|
|
252
|
+
self.assertEqual(api_request.amount, 0.15)
|
|
253
|
+
|
|
254
|
+
def test_model_pricing(self):
|
|
255
|
+
"""Test ModelPricing model."""
|
|
256
|
+
pricing = ModelPricing(
|
|
257
|
+
prompt_tokens_cost=0.0001,
|
|
258
|
+
completion_tokens_cost=0.0001
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
self.assertEqual(pricing.prompt_tokens_cost, 0.0001)
|
|
262
|
+
self.assertEqual(pricing.completion_tokens_cost, 0.0001)
|
|
263
|
+
|
|
264
|
+
def test_model_pricing_constants(self):
|
|
265
|
+
"""Test that MODEL_PRICING contains expected models."""
|
|
266
|
+
expected_models = [
|
|
267
|
+
"llama", "gpt-3.5-turbo", "gpt-4",
|
|
268
|
+
"claude-3-sonnet", "claude-3-opus"
|
|
269
|
+
]
|
|
270
|
+
|
|
271
|
+
for model in expected_models:
|
|
272
|
+
self.assertIn(model, MODEL_PRICING)
|
|
273
|
+
self.assertIsInstance(MODEL_PRICING[model], ModelPricing)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
if __name__ == '__main__':
|
|
277
|
+
unittest.main()
|