paid-python 1.0.6__tar.gz → 1.2.0__tar.gz

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.
Files changed (126) hide show
  1. {paid_python-1.0.6 → paid_python-1.2.0}/PKG-INFO +65 -78
  2. {paid_python-1.0.6 → paid_python-1.2.0}/README.md +63 -76
  3. {paid_python-1.0.6 → paid_python-1.2.0}/pyproject.toml +2 -2
  4. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/__init__.py +15 -0
  5. paid_python-1.2.0/src/paid/contacts/__init__.py +34 -0
  6. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/contacts/client.py +38 -16
  7. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/contacts/raw_client.py +36 -10
  8. paid_python-1.2.0/src/paid/contacts/types/__init__.py +34 -0
  9. paid_python-1.2.0/src/paid/contacts/types/create_contact_request_roles_item.py +5 -0
  10. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/client_wrapper.py +2 -2
  11. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/customers/client.py +8 -0
  12. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/customers/raw_client.py +8 -0
  13. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/orders/client.py +21 -8
  14. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/orders/raw_client.py +26 -8
  15. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/__init__.py +2 -1
  16. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/context_data.py +2 -0
  17. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/context_manager.py +24 -37
  18. paid_python-1.2.0/src/paid/tracing/signal.py +156 -0
  19. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/tracing.py +101 -69
  20. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/__init__.py +12 -0
  21. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/contact.py +2 -0
  22. paid_python-1.2.0/src/paid/types/contact_roles_item.py +5 -0
  23. paid_python-1.2.0/src/paid/types/create_order_line_attribute_request.py +21 -0
  24. paid_python-1.2.0/src/paid/types/create_order_line_request.py +32 -0
  25. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/customer.py +3 -0
  26. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/signal.py +6 -0
  27. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/update_contact_request.py +2 -0
  28. paid_python-1.2.0/src/paid/types/update_contact_request_roles_item.py +5 -0
  29. paid_python-1.2.0/src/paid/version.py +3 -0
  30. paid_python-1.0.6/src/paid/signals/__init__.py +0 -4
  31. paid_python-1.0.6/src/paid/tracing/signal.py +0 -84
  32. paid_python-1.0.6/src/paid/version.py +0 -6
  33. {paid_python-1.0.6 → paid_python-1.2.0}/LICENSE +0 -0
  34. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/client.py +0 -0
  35. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/__init__.py +0 -0
  36. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/api_error.py +0 -0
  37. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/datetime_utils.py +0 -0
  38. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/file.py +0 -0
  39. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/force_multipart.py +0 -0
  40. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/http_client.py +0 -0
  41. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/http_response.py +0 -0
  42. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/http_sse/__init__.py +0 -0
  43. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/http_sse/_api.py +0 -0
  44. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/http_sse/_decoders.py +0 -0
  45. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/http_sse/_exceptions.py +0 -0
  46. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/http_sse/_models.py +0 -0
  47. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/jsonable_encoder.py +0 -0
  48. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/pydantic_utilities.py +0 -0
  49. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/query_encoder.py +0 -0
  50. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/remove_none_from_dict.py +0 -0
  51. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/request_options.py +0 -0
  52. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/core/serialization.py +0 -0
  53. {paid_python-1.0.6/src/paid/contacts → paid_python-1.2.0/src/paid/customers}/__init__.py +0 -0
  54. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/environment.py +0 -0
  55. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/errors/__init__.py +0 -0
  56. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/errors/bad_request_error.py +0 -0
  57. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/errors/forbidden_error.py +0 -0
  58. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/errors/internal_server_error.py +0 -0
  59. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/errors/not_found_error.py +0 -0
  60. {paid_python-1.0.6/src/paid/customers → paid_python-1.2.0/src/paid/invoices}/__init__.py +0 -0
  61. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/invoices/client.py +0 -0
  62. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/invoices/raw_client.py +0 -0
  63. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/logger.py +0 -0
  64. {paid_python-1.0.6/src/paid/invoices → paid_python-1.2.0/src/paid/orders}/__init__.py +0 -0
  65. {paid_python-1.0.6/src/paid/orders → paid_python-1.2.0/src/paid/products}/__init__.py +0 -0
  66. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/products/client.py +0 -0
  67. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/products/raw_client.py +0 -0
  68. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/py.typed +0 -0
  69. {paid_python-1.0.6/src/paid/products → paid_python-1.2.0/src/paid/signals}/__init__.py +0 -0
  70. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/signals/client.py +0 -0
  71. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/signals/raw_client.py +0 -0
  72. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/anthropic_patches/__init__.py +0 -0
  73. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/anthropic_patches/patches.py +0 -0
  74. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/autoinstrumentation.py +0 -0
  75. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/distributed_tracing.py +0 -0
  76. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/gemini_patches/__init__.py +0 -0
  77. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/gemini_patches/patches.py +0 -0
  78. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/__init__.py +0 -0
  79. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/anthropic/__init__.py +0 -0
  80. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/anthropic/anthropicWrapper.py +0 -0
  81. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/bedrock/__init__.py +0 -0
  82. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/bedrock/bedrockWrapper.py +0 -0
  83. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/gemini/__init__.py +0 -0
  84. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/gemini/geminiWrapper.py +0 -0
  85. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/langchain/__init__.py +0 -0
  86. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/langchain/paidLangChainCallback.py +0 -0
  87. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/mistral/__init__.py +0 -0
  88. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/mistral/mistralWrapper.py +0 -0
  89. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/openai/__init__.py +0 -0
  90. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/openai/openAiWrapper.py +0 -0
  91. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/openai_agents/__init__.py +0 -0
  92. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/openai_agents/openaiAgentsHook.py +0 -0
  93. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/tracing/wrappers/utils.py +0 -0
  94. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/attribution.py +0 -0
  95. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/bulk_signals_response.py +0 -0
  96. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/contact_billing_address.py +0 -0
  97. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/contact_list_response.py +0 -0
  98. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/customer_attribution.py +0 -0
  99. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/customer_billing_address.py +0 -0
  100. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/customer_by_external_id.py +0 -0
  101. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/customer_by_id.py +0 -0
  102. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/customer_creation_state.py +0 -0
  103. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/customer_list_response.py +0 -0
  104. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/empty_response.py +0 -0
  105. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/error_response.py +0 -0
  106. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/invoice.py +0 -0
  107. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/invoice_line.py +0 -0
  108. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/invoice_line_payment_status.py +0 -0
  109. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/invoice_lines_response.py +0 -0
  110. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/invoice_list_response.py +0 -0
  111. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/invoice_payment_status.py +0 -0
  112. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/invoice_source.py +0 -0
  113. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/invoice_status.py +0 -0
  114. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/invoice_tax_status.py +0 -0
  115. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/order.py +0 -0
  116. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/order_creation_state.py +0 -0
  117. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/order_line.py +0 -0
  118. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/order_lines_response.py +0 -0
  119. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/order_list_response.py +0 -0
  120. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/pagination.py +0 -0
  121. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/product.py +0 -0
  122. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/product_by_external_id.py +0 -0
  123. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/product_by_id.py +0 -0
  124. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/product_list_response.py +0 -0
  125. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/update_customer_request.py +0 -0
  126. {paid_python-1.0.6 → paid_python-1.2.0}/src/paid/types/update_product_request.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: paid-python
3
- Version: 1.0.6
3
+ Version: 1.2.0
4
4
  Summary:
5
5
  Requires-Python: >=3.10,<3.14
6
6
  Classifier: Intended Audience :: Developers
@@ -19,7 +19,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Classifier: Typing :: Typed
20
20
  Requires-Dist: httpx (>=0.21.2)
21
21
  Requires-Dist: mutagen (>=1.47.0)
22
- Requires-Dist: openinference-instrumentation-anthropic (>=0.1.20)
22
+ Requires-Dist: openinference-instrumentation-anthropic (>=0.1.20,<1.0.0)
23
23
  Requires-Dist: openinference-instrumentation-bedrock (>=0.1.0)
24
24
  Requires-Dist: openinference-instrumentation-google-genai (>=0.1.8)
25
25
  Requires-Dist: openinference-instrumentation-instructor (>=0.1.0)
@@ -144,15 +144,6 @@ Your Paid API key for authentication. This is used as a fallback when you don't
144
144
  export PAID_API_KEY="your_api_key_here"
145
145
  ```
146
146
 
147
- You can then initialize the client without passing the token:
148
-
149
- ```python
150
- from paid import Paid
151
-
152
- # API key is read from PAID_API_KEY environment variable
153
- client = Paid()
154
- ```
155
-
156
147
  ### `PAID_ENABLED`
157
148
 
158
149
  Controls whether Paid tracing is enabled. Set to `false` (case-insensitive) to disable all tracing functionality.
@@ -189,14 +180,19 @@ Defaults to `https://collector.agentpaid.io:4318/v1/traces`.
189
180
 
190
181
  The easiest way to add cost tracking is using the `@paid_tracing` decorator or context manager:
191
182
 
183
+ > **Important:** Always call `initialize_tracing()` and `paid_autoinstrument()` once at startup before using `paid_tracing`. `initialize_tracing` also accepts optional arguments like OTEL collector endpoint and api key if you want to reroute your tracing somewhere else.
184
+
192
185
  #### As a Decorator
193
186
 
194
187
  ```python
195
- from paid.tracing import paid_tracing
188
+ from paid.tracing import paid_tracing, initialize_tracing, paid_autoinstrument
189
+
190
+ initialize_tracing()
191
+ paid_autoinstrument() # instruments all supported libraries by default
196
192
 
197
193
  @paid_tracing("<external_customer_id>", external_product_id="<optional_external_product_id>")
198
194
  def some_agent_workflow(): # your function
199
- # Your logic - use any AI providers with Paid wrappers or send signals with signal().
195
+ # Your logic - use any AI providers and send signals with signal().
200
196
  # This function is typically an event processor that should lead to AI calls or events emitted as Paid signals
201
197
  ```
202
198
 
@@ -205,7 +201,10 @@ def some_agent_workflow(): # your function
205
201
  You can also use `paid_tracing` as a context manager with `with` statements:
206
202
 
207
203
  ```python
208
- from paid.tracing import paid_tracing
204
+ from paid.tracing import paid_tracing, initialize_tracing, paid_autoinstrument
205
+
206
+ initialize_tracing()
207
+ paid_autoinstrument() # instruments all supported libraries by default
209
208
 
210
209
  # Synchronous
211
210
  with paid_tracing("customer_123", external_product_id="product_456"):
@@ -218,52 +217,26 @@ async with paid_tracing("customer_123", external_product_id="product_456"):
218
217
 
219
218
  Both approaches:
220
219
 
221
- - Initialize tracing using your API key you provided to the Paid client, falls back to `PAID_API_KEY` environment variable.
222
220
  - Handle both sync and async functions/code blocks
223
221
  - Gracefully fall back to normal execution if tracing fails
224
222
  - Support the same parameters: `external_customer_id`, `external_product_id`, `tracing_token`, `store_prompt`, `metadata`
225
223
 
226
- * Note - if it happens that you're calling `paid_tracing` from non-main thread, then it's advised to initialize from main thread:
227
-
228
- ```python
229
- from paid.tracing import initialize_tracing
230
- initialize_tracing()
231
- ```
224
+ ### Using AI Provider SDKs
232
225
 
233
- - `initialize_tracing` also accepts optional arguments like OTEL collector endpoint and api key if you want to reroute your tracing somewhere else :)
234
-
235
- ### Using the Paid wrappers
236
-
237
- You can track usage costs by using Paid wrappers around your AI provider's SDK.
238
- As of now, the following SDKs' APIs are wrapped:
239
-
240
- ```
241
- openai
242
- openai-agents (as a hook)
243
- anthropic
244
- langchain (as a hook)
245
- bedrock (boto3)
246
- mistral
247
- gemini (google-genai)
248
- ```
249
-
250
- Example usage:
226
+ After calling `initialize_tracing()` and `paid_autoinstrument()`, use your AI provider SDKs directly no wrapper classes needed:
251
227
 
252
228
  ```python
253
229
  from openai import OpenAI
254
- from paid.tracing import paid_tracing, initialize_tracing
255
- from paid.tracing.wrappers.openai import PaidOpenAI
230
+ from paid.tracing import paid_tracing, initialize_tracing, paid_autoinstrument
256
231
 
257
232
  initialize_tracing()
233
+ paid_autoinstrument(libraries=["openai"])
258
234
 
259
- openAIClient = PaidOpenAI(OpenAI(
260
- # This is the default and can be omitted
261
- api_key="<OPENAI_API_KEY>",
262
- ))
235
+ openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
263
236
 
264
237
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
265
238
  def image_generate():
266
- response = openAIClient.images.generate(
239
+ response = openai_client.images.generate(
267
240
  model="dall-e-3",
268
241
  prompt="A sunset over mountains",
269
242
  size="1024x1024",
@@ -284,13 +257,13 @@ You can attach custom metadata to your traces by passing a `metadata` dictionary
284
257
  <summary><strong>Python - Decorator</strong></summary>
285
258
 
286
259
  ```python
287
- from paid.tracing import paid_tracing, signal, initialize_tracing
288
- from paid.tracing.wrappers import PaidOpenAI
260
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
289
261
  from openai import OpenAI
290
262
 
291
263
  initialize_tracing()
264
+ paid_autoinstrument(libraries=["openai"])
292
265
 
293
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
266
+ openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
294
267
 
295
268
  @paid_tracing(
296
269
  "customer_123",
@@ -320,13 +293,13 @@ process_event(incoming_event)
320
293
  <summary><strong>Python - Context Manager</strong></summary>
321
294
 
322
295
  ```python
323
- from paid.tracing import paid_tracing, signal, initialize_tracing
324
- from paid.tracing.wrappers import PaidOpenAI
296
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
325
297
  from openai import OpenAI
326
298
 
327
299
  initialize_tracing()
300
+ paid_autoinstrument(libraries=["openai"])
328
301
 
329
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
302
+ openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
330
303
 
331
304
  def process_event(event):
332
305
  """Process event with custom metadata"""
@@ -377,14 +350,13 @@ For maximum convenience, you can use OpenTelemetry auto-instrumentation to autom
377
350
 
378
351
  ```python
379
352
  from paid import Paid
380
- from paid.tracing import paid_autoinstrument, initialize_tracing
353
+ from paid.tracing import paid_autoinstrument, initialize_tracing, paid_tracing
381
354
  from openai import OpenAI
382
355
 
383
356
  # Initialize Paid SDK
384
357
  client = Paid(token="PAID_API_KEY")
385
358
  initialize_tracing()
386
-
387
- paid_autoinstrument() # instruments all available: anthropic, gemini, openai, openai-agents, bedrock, langchain, instructor
359
+ paid_autoinstrument(libraries=["openai"])
388
360
 
389
361
  # Now all OpenAI calls will be automatically traced
390
362
  openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
@@ -441,7 +413,10 @@ Use the `signal()` function which must be called within an active `@paid_tracing
441
413
  Here's an example of how to use it:
442
414
 
443
415
  ```python
444
- from paid.tracing import paid_tracing, signal
416
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
417
+
418
+ initialize_tracing()
419
+ paid_autoinstrument()
445
420
 
446
421
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
447
422
  def do_work():
@@ -457,7 +432,10 @@ do_work()
457
432
  Same approach with context manager:
458
433
 
459
434
  ```python
460
- from paid.tracing import paid_tracing, signal
435
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
436
+
437
+ initialize_tracing()
438
+ paid_autoinstrument()
461
439
 
462
440
  def do_work():
463
441
  # ...do some work...
@@ -480,19 +458,22 @@ as the wrappers and hooks that recorded those costs.
480
458
  This will look something like this:
481
459
 
482
460
  ```python
483
- from paid.tracing import paid_tracing, signal
461
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
462
+
463
+ initialize_tracing()
464
+ paid_autoinstrument()
484
465
 
485
466
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
486
467
  def do_work():
487
468
  # ... your workflow logic
488
- # ... your AI calls made through Paid wrappers or hooks
469
+ # ... your AI calls (auto-instrumented)
489
470
  signal(
490
471
  event_name="<your_signal_name>",
491
472
  data={ }, # optional data (ex. manual cost tracking data)
492
473
  enable_cost_tracing=True, # set this flag to associate it with costs
493
474
  )
494
475
  # ... your workflow logic
495
- # ... your AI calls made through Paid wrappers or hooks (can be sent after the signal too)
476
+ # ... your AI calls (auto-instrumented, can be sent after the signal too)
496
477
 
497
478
  do_work()
498
479
  ```
@@ -511,13 +492,13 @@ For such cases, you can pass a tracing token directly to `@paid_tracing()` or co
511
492
  The simplest way to implement distributed tracing is to pass the token directly to the decorator or context manager:
512
493
 
513
494
  ```python
514
- from paid.tracing import paid_tracing, signal, generate_tracing_token, initialize_tracing
515
- from paid.tracing.wrappers.openai import PaidOpenAI
495
+ from paid.tracing import paid_tracing, signal, generate_tracing_token, initialize_tracing, paid_autoinstrument
516
496
  from openai import OpenAI
517
497
 
518
498
  initialize_tracing()
499
+ paid_autoinstrument(libraries=["openai"])
519
500
 
520
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
501
+ openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
521
502
 
522
503
  # Process 1: Generate token and do initial work
523
504
  token = generate_tracing_token()
@@ -558,14 +539,13 @@ process_part_2()
558
539
  Using context manager instead of decorator:
559
540
 
560
541
  ```python
561
- from paid.tracing import paid_tracing, signal, generate_tracing_token, initialize_tracing
562
- from paid.tracing.wrappers.openai import PaidOpenAI
542
+ from paid.tracing import paid_tracing, signal, generate_tracing_token, initialize_tracing, paid_autoinstrument
563
543
  from openai import OpenAI
564
544
 
565
545
  initialize_tracing()
546
+ paid_autoinstrument(libraries=["openai"])
566
547
 
567
- # Initialize
568
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
548
+ openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
569
549
 
570
550
  # Process 1: Generate token and do initial work
571
551
  token = generate_tracing_token()
@@ -610,7 +590,8 @@ signal = Signal(
610
590
  "amount": 0.002,
611
591
  "currency": "USD"
612
592
  },
613
- "gen_ai.response.model": "<ai_model_name>",
593
+ "gen_ai.response.model": "<ai_model_name>", # optional, but will be displayed on UI
594
+ "start_time": "2024-01-01T11:45:00.000Z", # optional, affects where trace is on the timeline
614
595
  }
615
596
  }
616
597
  )
@@ -621,7 +602,10 @@ client.signals.create_signals(signals=[signal])
621
602
  Alternatively the same `costData` payload can be passed to OTLP signaling mechanism:
622
603
 
623
604
  ```python
624
- from paid.tracing import paid_tracing, signal
605
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
606
+
607
+ initialize_tracing()
608
+ paid_autoinstrument()
625
609
 
626
610
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
627
611
  def do_work():
@@ -635,7 +619,8 @@ def do_work():
635
619
  "amount": 0.002,
636
620
  "currency": "USD"
637
621
  },
638
- "gen_ai.response.model": "<ai_model_name>",
622
+ "gen_ai.response.model": "<ai_model_name>", # optional, but will be displayed on UI
623
+ "start_time": "2024-01-01T11:45:00.000Z", # optional, affects where trace is on the timeline
639
624
  }
640
625
  }
641
626
  )
@@ -675,7 +660,10 @@ client.signals.create_signals(signals=[signal])
675
660
  Same but via OTEL signaling:
676
661
 
677
662
  ```python
678
- from paid.tracing import paid_tracing, signal
663
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
664
+
665
+ initialize_tracing()
666
+ paid_autoinstrument()
679
667
 
680
668
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
681
669
  def do_work():
@@ -721,13 +709,12 @@ The `@paid_tracing` decorator automatically handles both sync and async function
721
709
 
722
710
  ```python
723
711
  from openai import AsyncOpenAI
724
- from paid.tracing import paid_tracing, initialize_tracing
725
- from paid.tracing.wrappers.openai import PaidAsyncOpenAI
712
+ from paid.tracing import paid_tracing, initialize_tracing, paid_autoinstrument
726
713
 
727
714
  initialize_tracing()
715
+ paid_autoinstrument(libraries=["openai"])
728
716
 
729
- # Wrap the async OpenAI client
730
- openai_client = PaidAsyncOpenAI(AsyncOpenAI(api_key="<OPENAI_API_KEY>"))
717
+ openai_client = AsyncOpenAI(api_key="<OPENAI_API_KEY>")
731
718
 
732
719
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
733
720
  async def generate_image():
@@ -749,13 +736,13 @@ await generate_image()
749
736
  The `signal()` function works seamlessly in async contexts:
750
737
 
751
738
  ```python
752
- from paid.tracing import paid_tracing, signal, initialize_tracing
753
- from paid.tracing.wrappers.openai import PaidAsyncOpenAI
739
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
754
740
  from openai import AsyncOpenAI
755
741
 
756
742
  initialize_tracing()
743
+ paid_autoinstrument(libraries=["openai"])
757
744
 
758
- openai_client = PaidAsyncOpenAI(AsyncOpenAI(api_key="<OPENAI_API_KEY>"))
745
+ openai_client = AsyncOpenAI(api_key="<OPENAI_API_KEY>")
759
746
 
760
747
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
761
748
  async def do_work():
@@ -107,15 +107,6 @@ Your Paid API key for authentication. This is used as a fallback when you don't
107
107
  export PAID_API_KEY="your_api_key_here"
108
108
  ```
109
109
 
110
- You can then initialize the client without passing the token:
111
-
112
- ```python
113
- from paid import Paid
114
-
115
- # API key is read from PAID_API_KEY environment variable
116
- client = Paid()
117
- ```
118
-
119
110
  ### `PAID_ENABLED`
120
111
 
121
112
  Controls whether Paid tracing is enabled. Set to `false` (case-insensitive) to disable all tracing functionality.
@@ -152,14 +143,19 @@ Defaults to `https://collector.agentpaid.io:4318/v1/traces`.
152
143
 
153
144
  The easiest way to add cost tracking is using the `@paid_tracing` decorator or context manager:
154
145
 
146
+ > **Important:** Always call `initialize_tracing()` and `paid_autoinstrument()` once at startup before using `paid_tracing`. `initialize_tracing` also accepts optional arguments like OTEL collector endpoint and api key if you want to reroute your tracing somewhere else.
147
+
155
148
  #### As a Decorator
156
149
 
157
150
  ```python
158
- from paid.tracing import paid_tracing
151
+ from paid.tracing import paid_tracing, initialize_tracing, paid_autoinstrument
152
+
153
+ initialize_tracing()
154
+ paid_autoinstrument() # instruments all supported libraries by default
159
155
 
160
156
  @paid_tracing("<external_customer_id>", external_product_id="<optional_external_product_id>")
161
157
  def some_agent_workflow(): # your function
162
- # Your logic - use any AI providers with Paid wrappers or send signals with signal().
158
+ # Your logic - use any AI providers and send signals with signal().
163
159
  # This function is typically an event processor that should lead to AI calls or events emitted as Paid signals
164
160
  ```
165
161
 
@@ -168,7 +164,10 @@ def some_agent_workflow(): # your function
168
164
  You can also use `paid_tracing` as a context manager with `with` statements:
169
165
 
170
166
  ```python
171
- from paid.tracing import paid_tracing
167
+ from paid.tracing import paid_tracing, initialize_tracing, paid_autoinstrument
168
+
169
+ initialize_tracing()
170
+ paid_autoinstrument() # instruments all supported libraries by default
172
171
 
173
172
  # Synchronous
174
173
  with paid_tracing("customer_123", external_product_id="product_456"):
@@ -181,52 +180,26 @@ async with paid_tracing("customer_123", external_product_id="product_456"):
181
180
 
182
181
  Both approaches:
183
182
 
184
- - Initialize tracing using your API key you provided to the Paid client, falls back to `PAID_API_KEY` environment variable.
185
183
  - Handle both sync and async functions/code blocks
186
184
  - Gracefully fall back to normal execution if tracing fails
187
185
  - Support the same parameters: `external_customer_id`, `external_product_id`, `tracing_token`, `store_prompt`, `metadata`
188
186
 
189
- * Note - if it happens that you're calling `paid_tracing` from non-main thread, then it's advised to initialize from main thread:
190
-
191
- ```python
192
- from paid.tracing import initialize_tracing
193
- initialize_tracing()
194
- ```
187
+ ### Using AI Provider SDKs
195
188
 
196
- - `initialize_tracing` also accepts optional arguments like OTEL collector endpoint and api key if you want to reroute your tracing somewhere else :)
197
-
198
- ### Using the Paid wrappers
199
-
200
- You can track usage costs by using Paid wrappers around your AI provider's SDK.
201
- As of now, the following SDKs' APIs are wrapped:
202
-
203
- ```
204
- openai
205
- openai-agents (as a hook)
206
- anthropic
207
- langchain (as a hook)
208
- bedrock (boto3)
209
- mistral
210
- gemini (google-genai)
211
- ```
212
-
213
- Example usage:
189
+ After calling `initialize_tracing()` and `paid_autoinstrument()`, use your AI provider SDKs directly no wrapper classes needed:
214
190
 
215
191
  ```python
216
192
  from openai import OpenAI
217
- from paid.tracing import paid_tracing, initialize_tracing
218
- from paid.tracing.wrappers.openai import PaidOpenAI
193
+ from paid.tracing import paid_tracing, initialize_tracing, paid_autoinstrument
219
194
 
220
195
  initialize_tracing()
196
+ paid_autoinstrument(libraries=["openai"])
221
197
 
222
- openAIClient = PaidOpenAI(OpenAI(
223
- # This is the default and can be omitted
224
- api_key="<OPENAI_API_KEY>",
225
- ))
198
+ openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
226
199
 
227
200
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
228
201
  def image_generate():
229
- response = openAIClient.images.generate(
202
+ response = openai_client.images.generate(
230
203
  model="dall-e-3",
231
204
  prompt="A sunset over mountains",
232
205
  size="1024x1024",
@@ -247,13 +220,13 @@ You can attach custom metadata to your traces by passing a `metadata` dictionary
247
220
  <summary><strong>Python - Decorator</strong></summary>
248
221
 
249
222
  ```python
250
- from paid.tracing import paid_tracing, signal, initialize_tracing
251
- from paid.tracing.wrappers import PaidOpenAI
223
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
252
224
  from openai import OpenAI
253
225
 
254
226
  initialize_tracing()
227
+ paid_autoinstrument(libraries=["openai"])
255
228
 
256
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
229
+ openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
257
230
 
258
231
  @paid_tracing(
259
232
  "customer_123",
@@ -283,13 +256,13 @@ process_event(incoming_event)
283
256
  <summary><strong>Python - Context Manager</strong></summary>
284
257
 
285
258
  ```python
286
- from paid.tracing import paid_tracing, signal, initialize_tracing
287
- from paid.tracing.wrappers import PaidOpenAI
259
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
288
260
  from openai import OpenAI
289
261
 
290
262
  initialize_tracing()
263
+ paid_autoinstrument(libraries=["openai"])
291
264
 
292
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
265
+ openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
293
266
 
294
267
  def process_event(event):
295
268
  """Process event with custom metadata"""
@@ -340,14 +313,13 @@ For maximum convenience, you can use OpenTelemetry auto-instrumentation to autom
340
313
 
341
314
  ```python
342
315
  from paid import Paid
343
- from paid.tracing import paid_autoinstrument, initialize_tracing
316
+ from paid.tracing import paid_autoinstrument, initialize_tracing, paid_tracing
344
317
  from openai import OpenAI
345
318
 
346
319
  # Initialize Paid SDK
347
320
  client = Paid(token="PAID_API_KEY")
348
321
  initialize_tracing()
349
-
350
- paid_autoinstrument() # instruments all available: anthropic, gemini, openai, openai-agents, bedrock, langchain, instructor
322
+ paid_autoinstrument(libraries=["openai"])
351
323
 
352
324
  # Now all OpenAI calls will be automatically traced
353
325
  openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
@@ -404,7 +376,10 @@ Use the `signal()` function which must be called within an active `@paid_tracing
404
376
  Here's an example of how to use it:
405
377
 
406
378
  ```python
407
- from paid.tracing import paid_tracing, signal
379
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
380
+
381
+ initialize_tracing()
382
+ paid_autoinstrument()
408
383
 
409
384
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
410
385
  def do_work():
@@ -420,7 +395,10 @@ do_work()
420
395
  Same approach with context manager:
421
396
 
422
397
  ```python
423
- from paid.tracing import paid_tracing, signal
398
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
399
+
400
+ initialize_tracing()
401
+ paid_autoinstrument()
424
402
 
425
403
  def do_work():
426
404
  # ...do some work...
@@ -443,19 +421,22 @@ as the wrappers and hooks that recorded those costs.
443
421
  This will look something like this:
444
422
 
445
423
  ```python
446
- from paid.tracing import paid_tracing, signal
424
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
425
+
426
+ initialize_tracing()
427
+ paid_autoinstrument()
447
428
 
448
429
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
449
430
  def do_work():
450
431
  # ... your workflow logic
451
- # ... your AI calls made through Paid wrappers or hooks
432
+ # ... your AI calls (auto-instrumented)
452
433
  signal(
453
434
  event_name="<your_signal_name>",
454
435
  data={ }, # optional data (ex. manual cost tracking data)
455
436
  enable_cost_tracing=True, # set this flag to associate it with costs
456
437
  )
457
438
  # ... your workflow logic
458
- # ... your AI calls made through Paid wrappers or hooks (can be sent after the signal too)
439
+ # ... your AI calls (auto-instrumented, can be sent after the signal too)
459
440
 
460
441
  do_work()
461
442
  ```
@@ -474,13 +455,13 @@ For such cases, you can pass a tracing token directly to `@paid_tracing()` or co
474
455
  The simplest way to implement distributed tracing is to pass the token directly to the decorator or context manager:
475
456
 
476
457
  ```python
477
- from paid.tracing import paid_tracing, signal, generate_tracing_token, initialize_tracing
478
- from paid.tracing.wrappers.openai import PaidOpenAI
458
+ from paid.tracing import paid_tracing, signal, generate_tracing_token, initialize_tracing, paid_autoinstrument
479
459
  from openai import OpenAI
480
460
 
481
461
  initialize_tracing()
462
+ paid_autoinstrument(libraries=["openai"])
482
463
 
483
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
464
+ openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
484
465
 
485
466
  # Process 1: Generate token and do initial work
486
467
  token = generate_tracing_token()
@@ -521,14 +502,13 @@ process_part_2()
521
502
  Using context manager instead of decorator:
522
503
 
523
504
  ```python
524
- from paid.tracing import paid_tracing, signal, generate_tracing_token, initialize_tracing
525
- from paid.tracing.wrappers.openai import PaidOpenAI
505
+ from paid.tracing import paid_tracing, signal, generate_tracing_token, initialize_tracing, paid_autoinstrument
526
506
  from openai import OpenAI
527
507
 
528
508
  initialize_tracing()
509
+ paid_autoinstrument(libraries=["openai"])
529
510
 
530
- # Initialize
531
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
511
+ openai_client = OpenAI(api_key="<OPENAI_API_KEY>")
532
512
 
533
513
  # Process 1: Generate token and do initial work
534
514
  token = generate_tracing_token()
@@ -573,7 +553,8 @@ signal = Signal(
573
553
  "amount": 0.002,
574
554
  "currency": "USD"
575
555
  },
576
- "gen_ai.response.model": "<ai_model_name>",
556
+ "gen_ai.response.model": "<ai_model_name>", # optional, but will be displayed on UI
557
+ "start_time": "2024-01-01T11:45:00.000Z", # optional, affects where trace is on the timeline
577
558
  }
578
559
  }
579
560
  )
@@ -584,7 +565,10 @@ client.signals.create_signals(signals=[signal])
584
565
  Alternatively the same `costData` payload can be passed to OTLP signaling mechanism:
585
566
 
586
567
  ```python
587
- from paid.tracing import paid_tracing, signal
568
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
569
+
570
+ initialize_tracing()
571
+ paid_autoinstrument()
588
572
 
589
573
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
590
574
  def do_work():
@@ -598,7 +582,8 @@ def do_work():
598
582
  "amount": 0.002,
599
583
  "currency": "USD"
600
584
  },
601
- "gen_ai.response.model": "<ai_model_name>",
585
+ "gen_ai.response.model": "<ai_model_name>", # optional, but will be displayed on UI
586
+ "start_time": "2024-01-01T11:45:00.000Z", # optional, affects where trace is on the timeline
602
587
  }
603
588
  }
604
589
  )
@@ -638,7 +623,10 @@ client.signals.create_signals(signals=[signal])
638
623
  Same but via OTEL signaling:
639
624
 
640
625
  ```python
641
- from paid.tracing import paid_tracing, signal
626
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
627
+
628
+ initialize_tracing()
629
+ paid_autoinstrument()
642
630
 
643
631
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
644
632
  def do_work():
@@ -684,13 +672,12 @@ The `@paid_tracing` decorator automatically handles both sync and async function
684
672
 
685
673
  ```python
686
674
  from openai import AsyncOpenAI
687
- from paid.tracing import paid_tracing, initialize_tracing
688
- from paid.tracing.wrappers.openai import PaidAsyncOpenAI
675
+ from paid.tracing import paid_tracing, initialize_tracing, paid_autoinstrument
689
676
 
690
677
  initialize_tracing()
678
+ paid_autoinstrument(libraries=["openai"])
691
679
 
692
- # Wrap the async OpenAI client
693
- openai_client = PaidAsyncOpenAI(AsyncOpenAI(api_key="<OPENAI_API_KEY>"))
680
+ openai_client = AsyncOpenAI(api_key="<OPENAI_API_KEY>")
694
681
 
695
682
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
696
683
  async def generate_image():
@@ -712,13 +699,13 @@ await generate_image()
712
699
  The `signal()` function works seamlessly in async contexts:
713
700
 
714
701
  ```python
715
- from paid.tracing import paid_tracing, signal, initialize_tracing
716
- from paid.tracing.wrappers.openai import PaidAsyncOpenAI
702
+ from paid.tracing import paid_tracing, signal, initialize_tracing, paid_autoinstrument
717
703
  from openai import AsyncOpenAI
718
704
 
719
705
  initialize_tracing()
706
+ paid_autoinstrument(libraries=["openai"])
720
707
 
721
- openai_client = PaidAsyncOpenAI(AsyncOpenAI(api_key="<OPENAI_API_KEY>"))
708
+ openai_client = AsyncOpenAI(api_key="<OPENAI_API_KEY>")
722
709
 
723
710
  @paid_tracing("your_external_customer_id", external_product_id="your_external_product_id")
724
711
  async def do_work():
@@ -4,7 +4,7 @@ dynamic = ["version"]
4
4
 
5
5
  [tool.poetry]
6
6
  name = "paid-python"
7
- version = "1.0.6"
7
+ version = "1.2.0"
8
8
  description = ""
9
9
  readme = "README.md"
10
10
  authors = []
@@ -37,7 +37,7 @@ Repository = 'https://github.com/paid-ai/paid-python'
37
37
  python = ">=3.10,<3.14"
38
38
  httpx = ">=0.21.2"
39
39
  mutagen = ">=1.47.0"
40
- openinference-instrumentation-anthropic = ">=0.1.20"
40
+ openinference-instrumentation-anthropic = ">=0.1.20,<1.0.0"
41
41
  openinference-instrumentation-bedrock = ">=0.1.0"
42
42
  openinference-instrumentation-google-genai = ">=0.1.8"
43
43
  openinference-instrumentation-instructor = ">=0.1.0"