paygent-sdk 1.0.0__py3-none-any.whl → 3.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.
@@ -0,0 +1,283 @@
1
+ """
2
+ Voice client implementation for STT and TTS usage tracking.
3
+ """
4
+
5
+ import logging
6
+ from typing import Dict
7
+ from urllib.parse import urljoin
8
+
9
+ import requests
10
+
11
+ from .constants import (
12
+ DeepgramSTTModels,
13
+ MicrosoftAzureSpeechSTTModels,
14
+ GoogleCloudSpeechSTTModels,
15
+ AssemblyAISTTModels,
16
+ ElevenLabsSTTModels,
17
+ SonioxSTTModels,
18
+ AmazonPollyTTSModels,
19
+ MicrosoftAzureSpeechTTSModels,
20
+ GoogleCloudTextToSpeechTTSModels,
21
+ DeepgramTTSModels,
22
+ ElevenLabsTTSModels,
23
+ )
24
+ from .models import SttModelPricing, TtsModelPricing
25
+
26
+
27
+ # STT model pricing (cost per hour in USD)
28
+ STT_MODEL_PRICING: Dict[str, SttModelPricing] = {
29
+ # Deepgram Models
30
+ DeepgramSTTModels.FLUX: SttModelPricing(cost_per_hour=0.462), # $0.462 per hour
31
+ DeepgramSTTModels.NOVA_3_MONOLINGUAL: SttModelPricing(cost_per_hour=0.462), # $0.462 per hour
32
+ DeepgramSTTModels.NOVA_3_MULTILINGUAL: SttModelPricing(cost_per_hour=0.552), # $0.552 per hour
33
+ DeepgramSTTModels.NOVA_1: SttModelPricing(cost_per_hour=0.348), # $0.348 per hour
34
+ DeepgramSTTModels.NOVA_2: SttModelPricing(cost_per_hour=0.348), # $0.348 per hour
35
+ DeepgramSTTModels.ENHANCED: SttModelPricing(cost_per_hour=0.99), # $0.99 per hour
36
+ DeepgramSTTModels.BASE: SttModelPricing(cost_per_hour=0.87), # $0.87 per hour
37
+ DeepgramSTTModels.REDACTION: SttModelPricing(cost_per_hour=0.12), # $0.12 per hour (add-on)
38
+ DeepgramSTTModels.KEYTERM_PROMPTING: SttModelPricing(cost_per_hour=0.072), # $0.072 per hour (add-on)
39
+ DeepgramSTTModels.SPEAKER_DIARIZATION: SttModelPricing(cost_per_hour=0.12), # $0.12 per hour (add-on)
40
+ # Growth tier models
41
+ DeepgramSTTModels.GROWTH_NOVA_3_MONOLINGUAL: SttModelPricing(cost_per_hour=0.39), # $0.39 per hour
42
+ DeepgramSTTModels.GROWTH_NOVA_3_MULTILINGUAL: SttModelPricing(cost_per_hour=0.468), # $0.468 per hour
43
+ DeepgramSTTModels.GROWTH_NOVA_1: SttModelPricing(cost_per_hour=0.282), # $0.282 per hour
44
+ DeepgramSTTModels.GROWTH_NOVA_2: SttModelPricing(cost_per_hour=0.282), # $0.282 per hour
45
+
46
+ # Microsoft Azure Speech Service Models
47
+ MicrosoftAzureSpeechSTTModels.STANDARD: SttModelPricing(cost_per_hour=1.0), # $1.0 per hour
48
+ MicrosoftAzureSpeechSTTModels.CUSTOM: SttModelPricing(cost_per_hour=1.2), # $1.2 per hour
49
+
50
+ # Google Cloud Speech-to-Text Models
51
+ GoogleCloudSpeechSTTModels.STANDARD: SttModelPricing(cost_per_hour=0.96), # $0.96 per hour
52
+
53
+ # AssemblyAI Models
54
+ AssemblyAISTTModels.UNIVERSAL_STREAMING: SttModelPricing(cost_per_hour=0.15), # $0.15 per hour
55
+ AssemblyAISTTModels.UNIVERSAL_STREAMING_MULTILANG: SttModelPricing(cost_per_hour=0.15), # $0.15 per hour
56
+ AssemblyAISTTModels.KEYTERMS_PROMPTING: SttModelPricing(cost_per_hour=0.04), # $0.04 per hour
57
+
58
+ # Eleven Labs STT Models
59
+ ElevenLabsSTTModels.BUSINESS_SCRIBE_V1_V2: SttModelPricing(cost_per_hour=0.22), # $0.22 per hour
60
+
61
+ # Soniox STT Models
62
+ SonioxSTTModels.REAL_TIME: SttModelPricing(cost_per_hour=0.12), # $0.12 per hour
63
+ }
64
+
65
+
66
+ # TTS model pricing (cost per 1 million characters in USD)
67
+ TTS_MODEL_PRICING: Dict[str, TtsModelPricing] = {
68
+ # Amazon Polly Models
69
+ AmazonPollyTTSModels.STANDARD: TtsModelPricing(cost_per_million_characters=0.4), # $0.4 per 1 million characters
70
+ AmazonPollyTTSModels.NEURAL: TtsModelPricing(cost_per_million_characters=16.0), # $16 per 1 million characters
71
+ AmazonPollyTTSModels.LONG_FORM: TtsModelPricing(cost_per_million_characters=100.0), # $100 per 1 million characters
72
+ AmazonPollyTTSModels.GENERATIVE: TtsModelPricing(cost_per_million_characters=30.0), # $30 per 1 million characters
73
+
74
+ # Microsoft Azure Speech Service TTS Models
75
+ MicrosoftAzureSpeechTTSModels.STANDARD_NEURAL: TtsModelPricing(cost_per_million_characters=15.0), # $15 per 1 million characters
76
+ MicrosoftAzureSpeechTTSModels.CUSTOM_SYNTHESIS: TtsModelPricing(cost_per_million_characters=24.0), # $24 per 1 million characters
77
+ MicrosoftAzureSpeechTTSModels.CUSTOM_SYNTHESIS_NEURAL_HD: TtsModelPricing(cost_per_million_characters=48.0), # $48 per 1 million characters
78
+
79
+ # Google Cloud Text-to-Speech TTS Models
80
+ GoogleCloudTextToSpeechTTSModels.CHIRP_3_HD: TtsModelPricing(cost_per_million_characters=30.0), # $30 per 1 million characters
81
+ GoogleCloudTextToSpeechTTSModels.INSTANT_CUSTOM: TtsModelPricing(cost_per_million_characters=60.0), # $60 per 1 million characters
82
+ GoogleCloudTextToSpeechTTSModels.WAVENET: TtsModelPricing(cost_per_million_characters=4.0), # $4 per 1 million characters
83
+ GoogleCloudTextToSpeechTTSModels.STUDIO: TtsModelPricing(cost_per_million_characters=160.0), # $160 per 1 million characters
84
+ GoogleCloudTextToSpeechTTSModels.STANDARD: TtsModelPricing(cost_per_million_characters=4.0), # $4 per 1 million characters
85
+ GoogleCloudTextToSpeechTTSModels.NEURAL2: TtsModelPricing(cost_per_million_characters=16.0), # $16 per 1 million characters
86
+ GoogleCloudTextToSpeechTTSModels.POLYGLOT_PREVIEW: TtsModelPricing(cost_per_million_characters=16.0), # $16 per 1 million characters
87
+
88
+ # Deepgram TTS Models
89
+ DeepgramTTSModels.AURA_2: TtsModelPricing(cost_per_million_characters=30.0), # $30 per 1 million characters
90
+ DeepgramTTSModels.AURA_1: TtsModelPricing(cost_per_million_characters=15.0), # $15 per 1 million characters
91
+ # Growth tier models
92
+ DeepgramTTSModels.GROWTH_AURA_2: TtsModelPricing(cost_per_million_characters=27.0), # $27 per 1 million characters
93
+ DeepgramTTSModels.GROWTH_AURA_1: TtsModelPricing(cost_per_million_characters=13.5), # $13.5 per 1 million characters
94
+
95
+ # Eleven Labs TTS Models
96
+ ElevenLabsTTSModels.BUSINESS_MULTILINGUAL_V2_V3: TtsModelPricing(cost_per_million_characters=120.0), # $120 per 1 million characters
97
+ }
98
+
99
+
100
+ def _calculate_stt_cost(client_instance, model: str, audio_duration_seconds: int) -> float:
101
+ """
102
+ Calculate the cost based on STT model and audio duration.
103
+
104
+ Args:
105
+ client_instance: The Client instance
106
+ model: The STT model name
107
+ audio_duration_seconds: Audio duration in seconds
108
+
109
+ Returns:
110
+ Calculated cost in USD
111
+ """
112
+ pricing = STT_MODEL_PRICING.get(model)
113
+
114
+ if not pricing:
115
+ client_instance.logger.warning(f"Unknown STT model '{model}', using default pricing")
116
+ # Use default pricing for unknown models (per hour)
117
+ pricing = SttModelPricing(cost_per_hour=0.5) # $0.50 per hour default
118
+
119
+ # Calculate cost: (duration in seconds / 3600) * cost per hour
120
+ # Convert seconds to hours and multiply by cost per hour
121
+ duration_hours = audio_duration_seconds / 3600.0
122
+ total_cost = duration_hours * pricing.cost_per_hour
123
+
124
+ client_instance.logger.debug(
125
+ f"STT cost calculation for model '{model}': "
126
+ f"duration={audio_duration_seconds} seconds ({duration_hours:.6f} hours), "
127
+ f"cost_per_hour={pricing.cost_per_hour:.6f}, total={total_cost:.6f}"
128
+ )
129
+
130
+ return total_cost
131
+
132
+
133
+ def _calculate_tts_cost(client_instance, model: str, character_count: int) -> float:
134
+ """
135
+ Calculate the cost based on TTS model and character count.
136
+
137
+ Args:
138
+ client_instance: The Client instance
139
+ model: The TTS model name
140
+ character_count: Number of characters
141
+
142
+ Returns:
143
+ Calculated cost in USD
144
+ """
145
+ pricing = TTS_MODEL_PRICING.get(model)
146
+
147
+ if not pricing:
148
+ client_instance.logger.warning(f"Unknown TTS model '{model}', using default pricing")
149
+ # Use default pricing for unknown models (per 1 million characters)
150
+ pricing = TtsModelPricing(cost_per_million_characters=10.0) # $10 per 1 million characters default
151
+
152
+ # Calculate cost: (character count / 1,000,000) * cost per 1 million characters
153
+ # Convert character count to millions and multiply by cost per million
154
+ characters_in_millions = character_count / 1000000.0
155
+ total_cost = characters_in_millions * pricing.cost_per_million_characters
156
+
157
+ client_instance.logger.debug(
158
+ f"TTS cost calculation for model '{model}': "
159
+ f"characters={character_count} ({characters_in_millions:.6f} millions), "
160
+ f"cost_per_million={pricing.cost_per_million_characters:.6f}, total={total_cost:.6f}"
161
+ )
162
+
163
+ return total_cost
164
+
165
+
166
+ def send_stt_usage(client_instance, agent_id: str, customer_id: str, stt_usage_data) -> None:
167
+ """
168
+ Send STT usage data to the Paygent API.
169
+
170
+ Args:
171
+ client_instance: The Client instance
172
+ agent_id: Unique identifier for the agent
173
+ customer_id: Unique identifier for the customer
174
+ stt_usage_data: SttUsageData containing model and audio duration information
175
+
176
+ Raises:
177
+ requests.RequestException: If the request fails
178
+ """
179
+ client_instance.logger.info(
180
+ f"Starting send_stt_usage for agentID={agent_id}, customerID={customer_id}, "
181
+ f"model={stt_usage_data.model}, duration={stt_usage_data.audio_duration} seconds"
182
+ )
183
+
184
+ try:
185
+ # Calculate cost
186
+ cost = _calculate_stt_cost(client_instance, stt_usage_data.model, stt_usage_data.audio_duration)
187
+ client_instance.logger.info(f"Calculated STT cost: {cost:.6f} for model {stt_usage_data.model}")
188
+
189
+ # Prepare API request
190
+ api_request = {
191
+ "agentId": agent_id,
192
+ "customerId": customer_id,
193
+ "indicator": "stt-usage", # Default indicator for STT usage
194
+ "amount": cost,
195
+ "audioDuration": stt_usage_data.audio_duration,
196
+ "model": stt_usage_data.model,
197
+ "serviceProvider": stt_usage_data.service_provider,
198
+ }
199
+
200
+ # Make HTTP request
201
+ url = urljoin(client_instance.base_url, "/api/v1/usage")
202
+ headers = {
203
+ "Content-Type": "application/json",
204
+ "paygent-api-key": client_instance.api_key,
205
+ }
206
+
207
+ response = client_instance.session.post(url, json=api_request, headers=headers)
208
+ response.raise_for_status()
209
+
210
+ client_instance.logger.info(
211
+ f"Successfully sent STT usage data for agentID={agent_id}, "
212
+ f"customerID={customer_id}, cost={cost:.6f}"
213
+ )
214
+ except requests.RequestException as e:
215
+ client_instance.logger.error(f"Failed to send STT usage data: {str(e)}")
216
+ raise
217
+
218
+
219
+ def send_tts_usage(client_instance, agent_id: str, customer_id: str, tts_usage_data) -> None:
220
+ """
221
+ Send TTS usage data to the Paygent API.
222
+
223
+ Args:
224
+ client_instance: The Client instance
225
+ agent_id: Unique identifier for the agent
226
+ customer_id: Unique identifier for the customer
227
+ tts_usage_data: TtsUsageData containing model and character count information
228
+
229
+ Raises:
230
+ requests.RequestException: If the request fails
231
+ """
232
+ client_instance.logger.info(
233
+ f"Starting send_tts_usage for agentID={agent_id}, customerID={customer_id}, "
234
+ f"model={tts_usage_data.model}, characters={tts_usage_data.character_count}"
235
+ )
236
+
237
+ try:
238
+ # Calculate cost
239
+ cost = _calculate_tts_cost(client_instance, tts_usage_data.model, tts_usage_data.character_count)
240
+ client_instance.logger.info(f"Calculated TTS cost: {cost:.6f} for model {tts_usage_data.model}")
241
+
242
+ # Prepare API request
243
+ api_request = {
244
+ "agentId": agent_id,
245
+ "customerId": customer_id,
246
+ "indicator": "tts-usage", # Default indicator for TTS usage
247
+ "amount": cost,
248
+ "characterCount": tts_usage_data.character_count,
249
+ "model": tts_usage_data.model,
250
+ "serviceProvider": tts_usage_data.service_provider,
251
+ }
252
+
253
+ # Make HTTP request
254
+ url = urljoin(client_instance.base_url, "/api/v1/usage")
255
+ headers = {
256
+ "Content-Type": "application/json",
257
+ "paygent-api-key": client_instance.api_key,
258
+ }
259
+
260
+ response = client_instance.session.post(url, json=api_request, headers=headers)
261
+ response.raise_for_status()
262
+
263
+ client_instance.logger.info(
264
+ f"Successfully sent TTS usage data for agentID={agent_id}, "
265
+ f"customerID={customer_id}, cost={cost:.6f}"
266
+ )
267
+ except requests.RequestException as e:
268
+ client_instance.logger.error(f"Failed to send TTS usage data: {str(e)}")
269
+ raise
270
+
271
+
272
+ # Add methods to Client class
273
+ def _add_voice_methods_to_client():
274
+ """Dynamically add voice methods to the Client class."""
275
+ from .client import Client
276
+
277
+ Client.send_stt_usage = send_stt_usage
278
+ Client.send_tts_usage = send_tts_usage
279
+
280
+
281
+ # Call this function to attach methods when module is imported
282
+ _add_voice_methods_to_client()
283
+
@@ -0,0 +1,44 @@
1
+ """
2
+ Wrappers for automatic usage tracking with AI provider SDKs.
3
+
4
+ This module provides wrapper classes that intercept API calls to various AI providers
5
+ and automatically send usage data to Paygent for tracking and billing.
6
+
7
+ Note: All wrappers are lazily imported to avoid requiring installation of peer dependencies
8
+ that you don't use. For example, if you only use Gemini, you don't need to install openai,
9
+ anthropic, or mistral packages.
10
+ """
11
+
12
+ __all__ = [
13
+ "PaygentOpenAI",
14
+ "PaygentAnthropic",
15
+ "PaygentMistral",
16
+ "PaygentGemini",
17
+ "PaygentLangChainCallback",
18
+ ]
19
+
20
+
21
+ def __getattr__(name):
22
+ """
23
+ Lazy import wrappers to avoid requiring peer dependencies that aren't being used.
24
+
25
+ This allows users to only install the AI provider packages they actually use,
26
+ rather than requiring all of them as dependencies.
27
+ """
28
+ if name == "PaygentOpenAI":
29
+ from .openai_wrapper import PaygentOpenAI
30
+ return PaygentOpenAI
31
+ elif name == "PaygentAnthropic":
32
+ from .anthropic_wrapper import PaygentAnthropic
33
+ return PaygentAnthropic
34
+ elif name == "PaygentMistral":
35
+ from .mistral_wrapper import PaygentMistral
36
+ return PaygentMistral
37
+ elif name == "PaygentGemini":
38
+ from .gemini_wrapper import PaygentGemini
39
+ return PaygentGemini
40
+ elif name == "PaygentLangChainCallback":
41
+ from .langchain_wrapper import PaygentLangChainCallback
42
+ return PaygentLangChainCallback
43
+
44
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
@@ -0,0 +1,132 @@
1
+ """
2
+ Anthropic wrapper for automatic usage tracking with Paygent.
3
+ This wrapper intercepts Anthropic API calls and automatically sends usage data to Paygent.
4
+ """
5
+
6
+ import json
7
+ from typing import Any
8
+
9
+ try:
10
+ from anthropic import Anthropic
11
+ except ImportError:
12
+ raise ImportError(
13
+ "anthropic package is a peer-dependency. To use the Paygent wrapper around anthropic "
14
+ "you're assumed to already have anthropic package installed."
15
+ )
16
+
17
+ from ..client import Client
18
+ from ..models import UsageData, UsageDataWithStrings
19
+
20
+
21
+ class PaygentAnthropic:
22
+ """Main wrapper class for Anthropic that provides automatic usage tracking."""
23
+
24
+ def __init__(self, anthropic_client: Anthropic, paygent_client: Client):
25
+ """
26
+ Create a new PaygentAnthropic wrapper.
27
+
28
+ Args:
29
+ anthropic_client: The Anthropic client instance
30
+ paygent_client: The Paygent client instance for usage tracking
31
+ """
32
+ self.anthropic = anthropic_client
33
+ self.paygent_client = paygent_client
34
+
35
+ @property
36
+ def messages(self) -> 'MessagesWrapper':
37
+ """Access to messages API with automatic usage tracking."""
38
+ return MessagesWrapper(self.anthropic, self.paygent_client)
39
+
40
+
41
+ class MessagesWrapper:
42
+ """Wrapper for Anthropic messages API."""
43
+
44
+ def __init__(self, anthropic_client: Anthropic, paygent_client: Client):
45
+ self.anthropic = anthropic_client
46
+ self.paygent_client = paygent_client
47
+
48
+ def create(
49
+ self,
50
+ *,
51
+ model: str,
52
+ messages: list,
53
+ max_tokens: int,
54
+ indicator: str,
55
+ external_agent_id: str,
56
+ external_customer_id: str,
57
+ **kwargs
58
+ ) -> Any:
59
+ """
60
+ Create a message with automatic usage tracking.
61
+ Note: Streaming is not supported with automatic tracking.
62
+
63
+ Args:
64
+ model: The model to use
65
+ messages: The messages to send
66
+ max_tokens: Maximum tokens to generate
67
+ indicator: Indicator for the usage event
68
+ external_agent_id: External agent identifier
69
+ external_customer_id: External customer identifier
70
+ **kwargs: Additional Anthropic parameters
71
+
72
+ Returns:
73
+ The message response from Anthropic
74
+ """
75
+ # Ensure streaming is disabled for automatic tracking
76
+ kwargs['stream'] = False
77
+
78
+ # Make the Anthropic API call (non-streaming)
79
+ response = self.anthropic.messages.create(
80
+ model=model,
81
+ messages=messages,
82
+ max_tokens=max_tokens,
83
+ **kwargs
84
+ )
85
+
86
+ # Extract usage data from response with robust fallback mechanism
87
+ has_valid_usage = (
88
+ hasattr(response, 'usage') and
89
+ response.usage and
90
+ response.usage.input_tokens > 0 and
91
+ response.usage.output_tokens > 0
92
+ )
93
+
94
+ if has_valid_usage:
95
+ # Primary path: Use usage data from API response
96
+ usage_data = UsageData(
97
+ service_provider=model,
98
+ model=model,
99
+ prompt_tokens=response.usage.input_tokens,
100
+ completion_tokens=response.usage.output_tokens,
101
+ total_tokens=response.usage.input_tokens + response.usage.output_tokens
102
+ )
103
+
104
+ self.paygent_client.send_usage(
105
+ external_agent_id,
106
+ external_customer_id,
107
+ indicator,
108
+ usage_data
109
+ )
110
+ else:
111
+ # Fallback path: Calculate tokens from actual strings
112
+ prompt_string = json.dumps(messages)
113
+ output_string = ''
114
+ if hasattr(response, 'content') and response.content:
115
+ if len(response.content) > 0 and hasattr(response.content[0], 'text'):
116
+ output_string = response.content[0].text or ''
117
+
118
+ usage_data_with_strings = UsageDataWithStrings(
119
+ service_provider=model,
120
+ model=model,
121
+ prompt_string=prompt_string,
122
+ output_string=output_string
123
+ )
124
+
125
+ self.paygent_client.send_usage_with_token_string(
126
+ external_agent_id,
127
+ external_customer_id,
128
+ indicator,
129
+ usage_data_with_strings
130
+ )
131
+
132
+ return response