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.
- paygent_sdk/__init__.py +66 -1
- paygent_sdk/client.py +48 -43
- paygent_sdk/constants.py +121 -0
- paygent_sdk/models.py +56 -0
- paygent_sdk/voice_client.py +283 -0
- paygent_sdk/wrappers/__init__.py +44 -0
- paygent_sdk/wrappers/anthropic_wrapper.py +132 -0
- paygent_sdk/wrappers/gemini_wrapper.py +334 -0
- paygent_sdk/wrappers/langchain_wrapper.py +257 -0
- paygent_sdk/wrappers/mistral_wrapper.py +128 -0
- paygent_sdk/wrappers/openai_wrapper.py +316 -0
- {paygent_sdk-1.0.0.dist-info → paygent_sdk-3.0.0.dist-info}/METADATA +1 -1
- paygent_sdk-3.0.0.dist-info/RECORD +22 -0
- {paygent_sdk-1.0.0.dist-info → paygent_sdk-3.0.0.dist-info}/WHEEL +1 -1
- paygent_sdk-1.0.0.dist-info/RECORD +0 -15
- {paygent_sdk-1.0.0.dist-info → paygent_sdk-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {paygent_sdk-1.0.0.dist-info → paygent_sdk-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -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
|