paid-python 1.0.0a4__tar.gz → 1.0.2__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 (108) hide show
  1. {paid_python-1.0.0a4 → paid_python-1.0.2}/PKG-INFO +67 -73
  2. {paid_python-1.0.0a4 → paid_python-1.0.2}/README.md +68 -73
  3. {paid_python-1.0.0a4 → paid_python-1.0.2}/pyproject.toml +2 -3
  4. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/client_wrapper.py +2 -2
  5. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/logger.py +0 -3
  6. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/autoinstrumentation.py +21 -1
  7. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/context_data.py +3 -0
  8. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/context_manager.py +19 -2
  9. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/tracing.py +88 -12
  10. {paid_python-1.0.0a4 → paid_python-1.0.2}/LICENSE +0 -0
  11. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/__init__.py +0 -0
  12. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/client.py +0 -0
  13. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/contacts/__init__.py +0 -0
  14. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/contacts/client.py +0 -0
  15. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/contacts/raw_client.py +0 -0
  16. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/__init__.py +0 -0
  17. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/api_error.py +0 -0
  18. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/datetime_utils.py +0 -0
  19. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/file.py +0 -0
  20. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/force_multipart.py +0 -0
  21. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/http_client.py +0 -0
  22. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/http_response.py +0 -0
  23. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/jsonable_encoder.py +0 -0
  24. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/pydantic_utilities.py +0 -0
  25. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/query_encoder.py +0 -0
  26. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/remove_none_from_dict.py +0 -0
  27. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/request_options.py +0 -0
  28. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/core/serialization.py +0 -0
  29. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/customers/__init__.py +0 -0
  30. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/customers/client.py +0 -0
  31. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/customers/raw_client.py +0 -0
  32. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/environment.py +0 -0
  33. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/errors/__init__.py +0 -0
  34. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/errors/bad_request_error.py +0 -0
  35. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/errors/forbidden_error.py +0 -0
  36. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/errors/internal_server_error.py +0 -0
  37. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/errors/not_found_error.py +0 -0
  38. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/invoices/__init__.py +0 -0
  39. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/invoices/client.py +0 -0
  40. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/invoices/raw_client.py +0 -0
  41. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/orders/__init__.py +0 -0
  42. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/orders/client.py +0 -0
  43. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/orders/raw_client.py +0 -0
  44. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/products/__init__.py +0 -0
  45. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/products/client.py +0 -0
  46. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/products/raw_client.py +0 -0
  47. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/py.typed +0 -0
  48. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/signals/__init__.py +0 -0
  49. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/signals/client.py +0 -0
  50. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/signals/raw_client.py +0 -0
  51. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/__init__.py +0 -0
  52. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/distributed_tracing.py +0 -0
  53. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/signal.py +0 -0
  54. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/__init__.py +0 -0
  55. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/anthropic/__init__.py +0 -0
  56. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/anthropic/anthropicWrapper.py +0 -0
  57. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/bedrock/__init__.py +0 -0
  58. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/bedrock/bedrockWrapper.py +0 -0
  59. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/gemini/__init__.py +0 -0
  60. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/gemini/geminiWrapper.py +0 -0
  61. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/langchain/__init__.py +0 -0
  62. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/langchain/paidLangChainCallback.py +0 -0
  63. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/mistral/__init__.py +0 -0
  64. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/mistral/mistralWrapper.py +0 -0
  65. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/openai/__init__.py +0 -0
  66. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/openai/openAiWrapper.py +0 -0
  67. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/openai_agents/__init__.py +0 -0
  68. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/openai_agents/openaiAgentsHook.py +0 -0
  69. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/tracing/wrappers/utils.py +0 -0
  70. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/__init__.py +0 -0
  71. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/attribution.py +0 -0
  72. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/bulk_signals_response.py +0 -0
  73. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/contact.py +0 -0
  74. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/contact_billing_address.py +0 -0
  75. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/contact_list_response.py +0 -0
  76. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/customer.py +0 -0
  77. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/customer_attribution.py +0 -0
  78. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/customer_billing_address.py +0 -0
  79. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/customer_by_external_id.py +0 -0
  80. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/customer_by_id.py +0 -0
  81. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/customer_creation_state.py +0 -0
  82. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/customer_list_response.py +0 -0
  83. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/empty_response.py +0 -0
  84. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/error_response.py +0 -0
  85. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/invoice.py +0 -0
  86. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/invoice_line.py +0 -0
  87. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/invoice_line_payment_status.py +0 -0
  88. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/invoice_lines_response.py +0 -0
  89. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/invoice_list_response.py +0 -0
  90. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/invoice_payment_status.py +0 -0
  91. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/invoice_source.py +0 -0
  92. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/invoice_status.py +0 -0
  93. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/invoice_tax_status.py +0 -0
  94. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/order.py +0 -0
  95. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/order_creation_state.py +0 -0
  96. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/order_line.py +0 -0
  97. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/order_lines_response.py +0 -0
  98. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/order_list_response.py +0 -0
  99. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/pagination.py +0 -0
  100. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/product.py +0 -0
  101. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/product_by_external_id.py +0 -0
  102. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/product_by_id.py +0 -0
  103. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/product_list_response.py +0 -0
  104. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/signal.py +0 -0
  105. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/update_contact_request.py +0 -0
  106. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/update_customer_request.py +0 -0
  107. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/types/update_product_request.py +0 -0
  108. {paid_python-1.0.0a4 → paid_python-1.0.2}/src/paid/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: paid-python
3
- Version: 1.0.0a4
3
+ Version: 1.0.2
4
4
  Summary:
5
5
  Requires-Python: >=3.9,<3.14
6
6
  Classifier: Intended Audience :: Developers
@@ -20,6 +20,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
20
  Classifier: Typing :: Typed
21
21
  Requires-Dist: httpx (>=0.21.2)
22
22
  Requires-Dist: mutagen (>=1.47.0)
23
+ Requires-Dist: openinference-instrumentation-anthropic (>=0.1.20)
23
24
  Requires-Dist: openinference-instrumentation-bedrock (>=0.1.0)
24
25
  Requires-Dist: openinference-instrumentation-google-genai (>=0.1.8)
25
26
  Requires-Dist: openinference-instrumentation-instructor (>=0.1.0)
@@ -28,11 +29,9 @@ Requires-Dist: openinference-instrumentation-openai (>=0.1.40)
28
29
  Requires-Dist: openinference-instrumentation-openai-agents (>=1.0.0)
29
30
  Requires-Dist: opentelemetry-api (>=1.23.0)
30
31
  Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.23.0)
31
- Requires-Dist: opentelemetry-instrumentation-anthropic (>=0.47.0)
32
32
  Requires-Dist: opentelemetry-sdk (>=1.23.0)
33
33
  Requires-Dist: pydantic (>=1.9.2)
34
34
  Requires-Dist: pydantic-core (>=2.18.2,<3.0.0)
35
- Requires-Dist: python-dotenv (>=0.15.0)
36
35
  Requires-Dist: typing_extensions (>=4.0.0)
37
36
  Description-Content-Type: text/markdown
38
37
 
@@ -281,83 +280,78 @@ image_generate()
281
280
 
282
281
  You can attach custom metadata to your traces by passing a `metadata` dictionary to the `paid_tracing()` decorator or context manager. This metadata will be stored with the trace and can be used to filter and query traces later.
283
282
 
284
- <Tabs>
285
- <Tab title="Python - Decorator">
286
- ```python
287
- from paid.tracing import paid_tracing, signal, initialize_tracing
288
- from paid.tracing.wrappers import PaidOpenAI
289
- from openai import OpenAI
283
+ <details>
284
+ <summary><strong>Python - Decorator</strong></summary>
290
285
 
291
- initialize_tracing()
286
+ ```python
287
+ from paid.tracing import paid_tracing, signal, initialize_tracing
288
+ from paid.tracing.wrappers import PaidOpenAI
289
+ from openai import OpenAI
292
290
 
293
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
291
+ initialize_tracing()
294
292
 
295
- @paid_tracing(
296
- "customer_123",
297
- external_product_id="product_123",
298
- metadata={
299
- "campaign_id": "campaign_456",
300
- "environment": "production",
301
- "user_tier": "enterprise"
302
- }
293
+ openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
294
+
295
+ @paid_tracing(
296
+ "customer_123",
297
+ external_product_id="product_123",
298
+ metadata={
299
+ "campaign_id": "campaign_456",
300
+ "environment": "production",
301
+ "user_tier": "enterprise"
302
+ }
303
+ )
304
+ def process_event(event):
305
+ """Process event with custom metadata"""
306
+ response = openai_client.chat.completions.create(
307
+ model="gpt-4",
308
+ messages=[{"role": "user", "content": event.content}]
309
+ )
310
+
311
+ signal("event_processed", enable_cost_tracing=True)
312
+ return response
313
+
314
+ process_event(incoming_event)
315
+ ```
316
+
317
+ </details>
318
+
319
+ <details>
320
+ <summary><strong>Python - Context Manager</strong></summary>
321
+
322
+ ```python
323
+ from paid.tracing import paid_tracing, signal, initialize_tracing
324
+ from paid.tracing.wrappers import PaidOpenAI
325
+ from openai import OpenAI
326
+
327
+ initialize_tracing()
328
+
329
+ openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
330
+
331
+ def process_event(event):
332
+ """Process event with custom metadata"""
333
+ response = openai_client.chat.completions.create(
334
+ model="gpt-4",
335
+ messages=[{"role": "user", "content": event.content}]
303
336
  )
304
- def process_event(event):
305
- """Process event with custom metadata"""
306
- response = openai_client.chat.completions.create(
307
- model="gpt-4",
308
- messages=[{"role": "user", "content": event.content}]
309
- )
310
337
 
311
- signal("event_processed", enable_cost_tracing=True)
312
- return response
338
+ signal("event_processed", enable_cost_tracing=True)
339
+ return response
313
340
 
341
+ # Pass metadata to context manager
342
+ with paid_tracing(
343
+ "customer_123",
344
+ external_product_id="product_123",
345
+ metadata={
346
+ "campaign_id": "campaign_456",
347
+ "environment": "production",
348
+ "user_tier": "enterprise"
349
+ }
350
+ ):
314
351
  process_event(incoming_event)
315
- ```
316
-
317
- </Tab>
318
-
319
- <Tab title="Python - Context Manager">
320
- ```python
321
- from paid.tracing import paid_tracing, signal, initialize_tracing
322
- from paid.tracing.wrappers import PaidOpenAI
323
- from openai import OpenAI
324
-
325
- initialize_tracing()
326
-
327
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
328
-
329
- def process_event(event):
330
- """Process event with custom metadata"""
331
- response = openai_client.chat.completions.create(
332
- model="gpt-4",
333
- messages=[{"role": "user", "content": event.content}]
334
- )
335
-
336
- signal("event_processed", enable_cost_tracing=True)
337
- return response
338
-
339
- # Pass metadata to context manager
340
- with paid_tracing(
341
- "customer_123",
342
- external_product_id="product_123",
343
- metadata={
344
- "campaign_id": "campaign_456",
345
- "environment": "production",
346
- "user_tier": "enterprise"
347
- }
348
- ):
349
- process_event(incoming_event)
350
- ```
351
-
352
- </Tab>
353
-
354
- <Tab title="Node.js">
355
- ```typescript
356
- // Metadata support is not yet available in the Node.js SDK.
357
- // Please use Python for passing custom metadata to traces.
358
- ```
359
- </Tab>
360
- </Tabs>
352
+ ```
353
+
354
+ </details>
361
355
 
362
356
  #### Querying Traces by Metadata
363
357
 
@@ -243,83 +243,78 @@ image_generate()
243
243
 
244
244
  You can attach custom metadata to your traces by passing a `metadata` dictionary to the `paid_tracing()` decorator or context manager. This metadata will be stored with the trace and can be used to filter and query traces later.
245
245
 
246
- <Tabs>
247
- <Tab title="Python - Decorator">
248
- ```python
249
- from paid.tracing import paid_tracing, signal, initialize_tracing
250
- from paid.tracing.wrappers import PaidOpenAI
251
- from openai import OpenAI
252
-
253
- initialize_tracing()
254
-
255
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
256
-
257
- @paid_tracing(
258
- "customer_123",
259
- external_product_id="product_123",
260
- metadata={
261
- "campaign_id": "campaign_456",
262
- "environment": "production",
263
- "user_tier": "enterprise"
264
- }
246
+ <details>
247
+ <summary><strong>Python - Decorator</strong></summary>
248
+
249
+ ```python
250
+ from paid.tracing import paid_tracing, signal, initialize_tracing
251
+ from paid.tracing.wrappers import PaidOpenAI
252
+ from openai import OpenAI
253
+
254
+ initialize_tracing()
255
+
256
+ openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
257
+
258
+ @paid_tracing(
259
+ "customer_123",
260
+ external_product_id="product_123",
261
+ metadata={
262
+ "campaign_id": "campaign_456",
263
+ "environment": "production",
264
+ "user_tier": "enterprise"
265
+ }
266
+ )
267
+ def process_event(event):
268
+ """Process event with custom metadata"""
269
+ response = openai_client.chat.completions.create(
270
+ model="gpt-4",
271
+ messages=[{"role": "user", "content": event.content}]
265
272
  )
266
- def process_event(event):
267
- """Process event with custom metadata"""
268
- response = openai_client.chat.completions.create(
269
- model="gpt-4",
270
- messages=[{"role": "user", "content": event.content}]
271
- )
272
273
 
273
- signal("event_processed", enable_cost_tracing=True)
274
- return response
274
+ signal("event_processed", enable_cost_tracing=True)
275
+ return response
276
+
277
+ process_event(incoming_event)
278
+ ```
279
+
280
+ </details>
281
+
282
+ <details>
283
+ <summary><strong>Python - Context Manager</strong></summary>
275
284
 
285
+ ```python
286
+ from paid.tracing import paid_tracing, signal, initialize_tracing
287
+ from paid.tracing.wrappers import PaidOpenAI
288
+ from openai import OpenAI
289
+
290
+ initialize_tracing()
291
+
292
+ openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
293
+
294
+ def process_event(event):
295
+ """Process event with custom metadata"""
296
+ response = openai_client.chat.completions.create(
297
+ model="gpt-4",
298
+ messages=[{"role": "user", "content": event.content}]
299
+ )
300
+
301
+ signal("event_processed", enable_cost_tracing=True)
302
+ return response
303
+
304
+ # Pass metadata to context manager
305
+ with paid_tracing(
306
+ "customer_123",
307
+ external_product_id="product_123",
308
+ metadata={
309
+ "campaign_id": "campaign_456",
310
+ "environment": "production",
311
+ "user_tier": "enterprise"
312
+ }
313
+ ):
276
314
  process_event(incoming_event)
277
- ```
278
-
279
- </Tab>
280
-
281
- <Tab title="Python - Context Manager">
282
- ```python
283
- from paid.tracing import paid_tracing, signal, initialize_tracing
284
- from paid.tracing.wrappers import PaidOpenAI
285
- from openai import OpenAI
286
-
287
- initialize_tracing()
288
-
289
- openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
290
-
291
- def process_event(event):
292
- """Process event with custom metadata"""
293
- response = openai_client.chat.completions.create(
294
- model="gpt-4",
295
- messages=[{"role": "user", "content": event.content}]
296
- )
297
-
298
- signal("event_processed", enable_cost_tracing=True)
299
- return response
300
-
301
- # Pass metadata to context manager
302
- with paid_tracing(
303
- "customer_123",
304
- external_product_id="product_123",
305
- metadata={
306
- "campaign_id": "campaign_456",
307
- "environment": "production",
308
- "user_tier": "enterprise"
309
- }
310
- ):
311
- process_event(incoming_event)
312
- ```
313
-
314
- </Tab>
315
-
316
- <Tab title="Node.js">
317
- ```typescript
318
- // Metadata support is not yet available in the Node.js SDK.
319
- // Please use Python for passing custom metadata to traces.
320
- ```
321
- </Tab>
322
- </Tabs>
315
+ ```
316
+
317
+ </details>
323
318
 
324
319
  #### Querying Traces by Metadata
325
320
 
@@ -3,7 +3,7 @@ name = "paid-python"
3
3
 
4
4
  [tool.poetry]
5
5
  name = "paid-python"
6
- version = "1.0.0-alpha4"
6
+ version = "1.0.2"
7
7
  description = ""
8
8
  readme = "README.md"
9
9
  authors = []
@@ -37,6 +37,7 @@ Repository = 'https://github.com/paid-ai/paid-python'
37
37
  python = ">=3.9,<3.14"
38
38
  httpx = ">=0.21.2"
39
39
  mutagen = ">=1.47.0"
40
+ openinference-instrumentation-anthropic = ">=0.1.20"
40
41
  openinference-instrumentation-bedrock = ">=0.1.0"
41
42
  openinference-instrumentation-google-genai = ">=0.1.8"
42
43
  openinference-instrumentation-instructor = ">=0.1.0"
@@ -45,11 +46,9 @@ openinference-instrumentation-openai = ">=0.1.40"
45
46
  openinference-instrumentation-openai-agents = ">=1.0.0"
46
47
  opentelemetry-api = ">=1.23.0"
47
48
  opentelemetry-exporter-otlp-proto-http = ">=1.23.0"
48
- opentelemetry-instrumentation-anthropic = ">=0.47.0"
49
49
  opentelemetry-sdk = ">=1.23.0"
50
50
  pydantic = ">= 1.9.2"
51
51
  pydantic-core = "^2.18.2"
52
- python-dotenv = ">=0.15.0"
53
52
  typing_extensions = ">= 4.0.0"
54
53
 
55
54
  [tool.poetry.group.dev.dependencies]
@@ -20,10 +20,10 @@ class BaseClientWrapper:
20
20
 
21
21
  def get_headers(self) -> typing.Dict[str, str]:
22
22
  headers: typing.Dict[str, str] = {
23
- "User-Agent": "paid-python/1.0.0-alpha4",
23
+ "User-Agent": "paid-python/1.0.2",
24
24
  "X-Fern-Language": "Python",
25
25
  "X-Fern-SDK-Name": "paid-python",
26
- "X-Fern-SDK-Version": "1.0.0-alpha4",
26
+ "X-Fern-SDK-Version": "1.0.2",
27
27
  }
28
28
  headers["Authorization"] = f"Bearer {self._get_token()}"
29
29
  return headers
@@ -5,10 +5,7 @@ This file exports `logger` object for unified logging across the Paid SDK.
5
5
  import logging
6
6
  import os
7
7
 
8
- import dotenv
9
-
10
8
  # Configure logging
11
- _ = dotenv.load_dotenv()
12
9
  logger = logging.getLogger(__name__)
13
10
 
14
11
  # Set default log level to ERROR, allow override via PAID_LOG_LEVEL environment variable
@@ -15,7 +15,7 @@ from paid.logger import logger
15
15
 
16
16
  # Safe imports for instrumentation libraries
17
17
  try:
18
- from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor
18
+ from openinference.instrumentation.anthropic import AnthropicInstrumentor
19
19
 
20
20
  ANTHROPIC_AVAILABLE = True
21
21
  except Exception:
@@ -118,9 +118,15 @@ def paid_autoinstrument(libraries: Optional[List[str]] = None) -> None:
118
118
  logger.info("Tracing not initialized, initializing automatically")
119
119
  initialize_tracing()
120
120
 
121
+ logger.debug(
122
+ "[paid:autoinstrument] tracer_provider type=%s, requested libraries=%s",
123
+ type(tracing.paid_tracer_provider).__name__, libraries,
124
+ )
125
+
121
126
  # Default to all supported libraries if none specified
122
127
  if libraries is None:
123
128
  libraries = ["anthropic", "openai", "openai-agents", "bedrock", "langchain", "google-genai", "instructor"]
129
+ logger.debug("[paid:autoinstrument] No libraries specified, defaulting to all: %s", libraries)
124
130
 
125
131
  for library in libraries:
126
132
  if library in _initialized_instrumentors:
@@ -157,6 +163,8 @@ def _instrument_anthropic() -> None:
157
163
  logger.warning("Anthropic library not available, skipping instrumentation")
158
164
  return
159
165
 
166
+ logger.debug("[paid:autoinstrument] Instrumenting anthropic with AnthropicInstrumentor, provider=%s",
167
+ type(tracing.paid_tracer_provider).__name__)
160
168
  # Instrument Anthropic with Paid's tracer provider
161
169
  AnthropicInstrumentor().instrument(tracer_provider=tracing.paid_tracer_provider)
162
170
 
@@ -172,6 +180,8 @@ def _instrument_openai() -> None:
172
180
  logger.warning("OpenAI library not available, skipping instrumentation")
173
181
  return
174
182
 
183
+ logger.debug("[paid:autoinstrument] Instrumenting openai with OpenAIInstrumentor, provider=%s",
184
+ type(tracing.paid_tracer_provider).__name__)
175
185
  # Instrument OpenAI with Paid's tracer provider
176
186
  OpenAIInstrumentor().instrument(tracer_provider=tracing.paid_tracer_provider)
177
187
 
@@ -187,6 +197,8 @@ def _instrument_openai_agents() -> None:
187
197
  logger.warning("OpenAI Agents library not available, skipping instrumentation")
188
198
  return
189
199
 
200
+ logger.debug("[paid:autoinstrument] Instrumenting openai-agents with OpenAIAgentsInstrumentor, provider=%s",
201
+ type(tracing.paid_tracer_provider).__name__)
190
202
  # Instrument OpenAI Agents with Paid's tracer provider
191
203
  OpenAIAgentsInstrumentor().instrument(tracer_provider=tracing.paid_tracer_provider)
192
204
 
@@ -202,6 +214,8 @@ def _instrument_bedrock() -> None:
202
214
  logger.warning("Bedrock instrumentation library not available, skipping instrumentation")
203
215
  return
204
216
 
217
+ logger.debug("[paid:autoinstrument] Instrumenting bedrock with BedrockInstrumentor, provider=%s",
218
+ type(tracing.paid_tracer_provider).__name__)
205
219
  # Instrument Bedrock with Paid's tracer provider
206
220
  BedrockInstrumentor().instrument(tracer_provider=tracing.paid_tracer_provider)
207
221
 
@@ -217,6 +231,8 @@ def _instrument_langchain() -> None:
217
231
  logger.warning("LangChain instrumentation library not available, skipping instrumentation")
218
232
  return
219
233
 
234
+ logger.debug("[paid:autoinstrument] Instrumenting langchain with LangChainInstrumentor, provider=%s",
235
+ type(tracing.paid_tracer_provider).__name__)
220
236
  # Instrument LangChain with Paid's tracer provider
221
237
  LangChainInstrumentor().instrument(tracer_provider=tracing.paid_tracer_provider)
222
238
 
@@ -232,6 +248,8 @@ def _instrument_google_genai() -> None:
232
248
  logger.warning("Google GenAI instrumentation library not available, skipping instrumentation")
233
249
  return
234
250
 
251
+ logger.debug("[paid:autoinstrument] Instrumenting google-genai with GoogleGenAIInstrumentor, provider=%s",
252
+ type(tracing.paid_tracer_provider).__name__)
235
253
  GoogleGenAIInstrumentor().instrument(tracer_provider=tracing.paid_tracer_provider)
236
254
  _initialized_instrumentors.append("google-genai")
237
255
  logger.info("Google GenAI auto-instrumentation enabled")
@@ -245,6 +263,8 @@ def _instrument_instructor() -> None:
245
263
  logger.warning("Instructor library not available, skipping instrumentation")
246
264
  return
247
265
 
266
+ logger.debug("[paid:autoinstrument] Instrumenting instructor with InstructorInstrumentor, provider=%s",
267
+ type(tracing.paid_tracer_provider).__name__)
248
268
  InstructorInstrumentor().instrument(tracer_provider=tracing.paid_tracer_provider)
249
269
  _initialized_instrumentors.append("instructor")
250
270
  logger.info("Instructor auto-instrumentation enabled")
@@ -52,6 +52,7 @@ class ContextData:
52
52
  reset_token = cls._context[key].set(value)
53
53
  reset_tokens = cls._get_or_create_reset_tokens()
54
54
  reset_tokens[key] = reset_token
55
+ logger.debug("[paid:ctx] set_context_key: %s (type=%s)", key, type(value).__name__)
55
56
 
56
57
  @classmethod
57
58
  def unset_context_key(cls, key: str) -> None:
@@ -59,6 +60,7 @@ class ContextData:
59
60
  if key not in cls._context:
60
61
  logger.warning(f"Invalid context key: {key}")
61
62
  return
63
+ logger.debug("[paid:ctx] unset_context_key: %s", key)
62
64
  reset_tokens = cls._reset_tokens.get()
63
65
  if reset_tokens:
64
66
  _ = cls._context[key].set(None)
@@ -69,6 +71,7 @@ class ContextData:
69
71
  def reset_context(cls) -> None:
70
72
  reset_tokens = cls._reset_tokens.get()
71
73
  if reset_tokens:
74
+ logger.debug("[paid:ctx] reset_context: resetting keys=%s", list(reset_tokens.keys()))
72
75
  for key, reset_token in reset_tokens.items():
73
76
  cls._context[key].reset(reset_token)
74
77
  reset_tokens.clear()
@@ -80,7 +80,14 @@ class paid_tracing:
80
80
  self.span: Optional[Span] = None
81
81
  self.span_ctx: Optional[Any] = None # Context manager for the span
82
82
 
83
+ logger.debug(
84
+ "[paid:ctx] paid_tracing.__init__: customer_id=%s, product_id=%s, tracing_token=%s, store_prompt=%s",
85
+ external_customer_id, external_product_id,
86
+ "present" if tracing_token else "none", store_prompt,
87
+ )
88
+
83
89
  if not get_token():
90
+ logger.debug("[paid:ctx] paid_tracing.__init__: token not set, auto-initializing tracing")
84
91
  initialize_tracing(None, self.collector_endpoint)
85
92
 
86
93
  def _setup_context(self) -> Optional[Context]:
@@ -106,6 +113,12 @@ class paid_tracing:
106
113
  trace_flags=TraceFlags(TraceFlags.SAMPLED),
107
114
  )
108
115
  ctx = trace.set_span_in_context(NonRecordingSpan(span_context))
116
+ token_source = "explicit" if self.tracing_token else "context-inherited"
117
+ logger.debug("[paid:distributed] _setup_context: trace_id=%s (source=%s)",
118
+ format(override_trace_id, '032x') if isinstance(override_trace_id, int) else str(override_trace_id),
119
+ token_source)
120
+ else:
121
+ logger.debug("[paid:distributed] _setup_context: no override trace_id, using auto-generated")
109
122
 
110
123
  return ctx
111
124
 
@@ -130,9 +143,11 @@ class paid_tracing:
130
143
  def _enter_ctx(self):
131
144
  ctx = self._setup_context()
132
145
  tracer = get_paid_tracer()
133
- logger.info(f"Creating span for external_customer_id: {self.external_customer_id}")
146
+ logger.debug("[paid:ctx] _enter_ctx: creating parent_span for customer_id=%s", self.external_customer_id)
134
147
  self.span_ctx = tracer.start_as_current_span("parent_span", context=ctx)
135
148
  self.span = self.span_ctx.__enter__()
149
+ logger.debug("[paid:ctx] _enter_ctx: span created, trace_id=%s",
150
+ format(self.span.get_span_context().trace_id, '032x') if isinstance(self.span.get_span_context().trace_id, int) else str(self.span.get_span_context().trace_id))
136
151
  return self
137
152
 
138
153
  def _exit_ctx(self, exc_type, exc_val, exc_tb):
@@ -141,9 +156,10 @@ class paid_tracing:
141
156
  if self.span and self.span_ctx:
142
157
  if exc_type is not None:
143
158
  self.span.set_status(Status(StatusCode.ERROR, str(exc_val)))
159
+ logger.debug("[paid:ctx] _exit_ctx: span ended with ERROR: %s", exc_val)
144
160
  else:
145
161
  self.span.set_status(Status(StatusCode.OK))
146
- logger.info("Context block executed successfully")
162
+ logger.debug("[paid:ctx] _exit_ctx: span ended with OK")
147
163
 
148
164
  self.span_ctx.__exit__(exc_type, exc_val, exc_tb)
149
165
  self.span_ctx = None
@@ -157,6 +173,7 @@ class paid_tracing:
157
173
  # Decorator functionality
158
174
  def __call__(self, func: Callable[..., Any]) -> Callable[..., Any]:
159
175
  """Use as a decorator."""
176
+ logger.debug("[paid:ctx] __call__: wrapping fn=%s (async=%s)", getattr(func, '__name__', repr(func)), asyncio.iscoroutinefunction(func))
160
177
  if asyncio.iscoroutinefunction(func):
161
178
 
162
179
  @functools.wraps(func)
@@ -6,21 +6,20 @@ import signal
6
6
  from dataclasses import dataclass
7
7
  from typing import Any, Awaitable, Callable, Dict, Optional, Tuple, TypeVar, Union
8
8
 
9
- import dotenv
10
9
  from . import distributed_tracing
11
10
  from .context_data import ContextData
12
11
  from opentelemetry import trace
13
12
  from opentelemetry.context import Context
14
13
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
15
14
  from opentelemetry.sdk.resources import Resource
16
- from opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor, TracerProvider
15
+ from opentelemetry.sdk.trace import ReadableSpan, Span, SpanLimits, SpanProcessor, TracerProvider
17
16
  from opentelemetry.sdk.trace.export import SimpleSpanProcessor
17
+ from opentelemetry.sdk.trace.sampling import ALWAYS_ON
18
18
  from opentelemetry.trace import NonRecordingSpan, NoOpTracerProvider, SpanContext, Status, StatusCode, TraceFlags
19
19
  from opentelemetry.util.types import Attributes
20
20
 
21
21
  from paid.logger import logger
22
22
 
23
- _ = dotenv.load_dotenv()
24
23
  DEFAULT_COLLECTOR_ENDPOINT = (
25
24
  os.environ.get("PAID_OTEL_COLLECTOR_ENDPOINT") or "https://collector.agentpaid.io:4318/v1/traces"
26
25
  )
@@ -208,7 +207,7 @@ class PaidSpanProcessor(SpanProcessor):
208
207
  # Langchain instrumentation creates spans, that are created by other instrumentations (ex. OpenAI, Anthropic).
209
208
  # Not all spans need filtering (ex. ChatGoogleGenerativeAI), so first test actual telemetry before adding filters.
210
209
  # TODO: maybe consider a dropping sampler for such spans instead of raising an exception?
211
- logger.debug(f"Dropping Langchain span: {span.name}")
210
+ logger.debug("[paid:span] Dropping duplicate LangChain span: %s", span.name)
212
211
  raise Exception(f"Dropping Langchain span: {span.name}")
213
212
 
214
213
  # Prefix the span name
@@ -224,10 +223,29 @@ class PaidSpanProcessor(SpanProcessor):
224
223
  if agent_id:
225
224
  span.set_attribute("external_agent_id", agent_id)
226
225
 
226
+ logger.debug(
227
+ "[paid:span] on_start: name=%s, customer_id=%s, agent_id=%s",
228
+ span.name, customer_id, agent_id,
229
+ )
230
+
227
231
  metadata = ContextData.get_context_key("user_metadata")
228
232
  if metadata:
229
233
  metadata_attributes: dict[str, Any] = {}
230
234
 
235
+ # OTEL attributes only accept: bool, str, bytes, int, float
236
+ _OTEL_SAFE_TYPES = (bool, str, bytes, int, float)
237
+
238
+ def _sanitize_value(v: Any) -> Any:
239
+ """Convert non-OTEL-safe values to str."""
240
+ if isinstance(v, _OTEL_SAFE_TYPES):
241
+ return v
242
+ if isinstance(v, (list, tuple)):
243
+ return [
244
+ el if isinstance(el, _OTEL_SAFE_TYPES) else str(el)
245
+ for el in v
246
+ ]
247
+ return str(v)
248
+
231
249
  def flatten_dict(d: dict[str, Any], parent_key: str = "") -> None:
232
250
  """Recursively flatten nested dictionaries into dot-notation keys."""
233
251
  for k, v in d.items():
@@ -235,13 +253,14 @@ class PaidSpanProcessor(SpanProcessor):
235
253
  if isinstance(v, dict):
236
254
  flatten_dict(v, new_key)
237
255
  else:
238
- metadata_attributes[new_key] = v
256
+ metadata_attributes[new_key] = _sanitize_value(v)
239
257
 
240
258
  flatten_dict(metadata)
241
259
 
242
260
  # Add all flattened metadata attributes to the span
243
261
  for key, value in metadata_attributes.items():
244
262
  span.set_attribute(f"metadata.{key}", value)
263
+ logger.debug("[paid:span] on_start: attached metadata keys=%s", list(metadata_attributes.keys()))
245
264
 
246
265
  def on_end(self, span: ReadableSpan) -> None:
247
266
  if span.name and not span.name.startswith(self.SPAN_NAME_PREFIX):
@@ -251,6 +270,7 @@ class PaidSpanProcessor(SpanProcessor):
251
270
  """Filter out prompt and response contents unless explicitly asked to store"""
252
271
  store_prompt = ContextData.get_context_key("store_prompt")
253
272
  if store_prompt:
273
+ logger.debug("[paid:span] on_end: name=%s, store_prompt=True, keeping all attributes", span.name)
254
274
  return
255
275
 
256
276
  original_attributes = span.attributes
@@ -262,6 +282,12 @@ class PaidSpanProcessor(SpanProcessor):
262
282
  for k, v in original_attributes.items()
263
283
  if not any(substr in k for substr in self.PROMPT_ATTRIBUTES_SUBSTRINGS)
264
284
  }
285
+ filtered_count = len(original_attributes) - len(filtered_attrs)
286
+ if filtered_count > 0:
287
+ logger.debug(
288
+ "[paid:span] on_end: name=%s, filtered %d prompt attribute(s)",
289
+ span.name, filtered_count,
290
+ )
265
291
  # This works because the exporter reads attributes during serialization
266
292
  object.__setattr__(span, "_attributes", filtered_attrs)
267
293
 
@@ -303,6 +329,7 @@ class PydanticSpanProcessor(SpanProcessor):
303
329
  settings = _PydanticSettingsRegistry.get(scope_name)
304
330
  if settings is None or settings.track_usage:
305
331
  # No filtering needed - keep usage/cost data
332
+ logger.debug("[paid:span] PydanticSpanProcessor on_end: scope=%s, track_usage=True, keeping usage data", scope_name)
306
333
  return
307
334
 
308
335
  original_attributes = span.attributes
@@ -314,6 +341,11 @@ class PydanticSpanProcessor(SpanProcessor):
314
341
  for k, v in original_attributes.items()
315
342
  if not any(substr in k for substr in self.USAGE_ATTRIBUTES_SUBSTRINGS)
316
343
  }
344
+ filtered_count = len(original_attributes) - len(filtered_attrs)
345
+ logger.debug(
346
+ "[paid:span] PydanticSpanProcessor on_end: scope=%s, filtered %d usage/cost attribute(s)",
347
+ scope_name, filtered_count,
348
+ )
317
349
  # This works because the exporter reads attributes during serialization
318
350
  object.__setattr__(span, "_attributes", filtered_attrs)
319
351
 
@@ -355,6 +387,7 @@ def setup_graceful_termination(paid_tracer_provider: TracerProvider):
355
387
  # signal handlers
356
388
  for sig in (signal.SIGINT, signal.SIGTERM):
357
389
  signal.signal(sig, create_chained_signal_handler(sig))
390
+ logger.debug("[paid:init] Registered atexit and signal handlers (SIGINT, SIGTERM)")
358
391
  except Exception as e:
359
392
  logger.warning(
360
393
  f"Could not set up termination handlers: {e}"
@@ -385,6 +418,9 @@ def initialize_tracing(
385
418
  if not collector_endpoint:
386
419
  collector_endpoint = DEFAULT_COLLECTOR_ENDPOINT
387
420
 
421
+ logger.debug("[paid:init] initialize_tracing called, endpoint=%s, api_key=%s",
422
+ collector_endpoint, "provided" if api_key else "not provided (will check env)")
423
+
388
424
  try:
389
425
  # Check if tracing is disabled via environment variable
390
426
  paid_enabled = os.environ.get("PAID_ENABLED", "true").lower()
@@ -403,27 +439,51 @@ def initialize_tracing(
403
439
  logger.error("API key must be provided via PAID_API_KEY environment variable")
404
440
  # don't throw - tracing should not break the app
405
441
  return
442
+ logger.debug("[paid:init] API key resolved from PAID_API_KEY environment variable")
443
+ else:
444
+ logger.debug("[paid:init] API key provided via parameter")
406
445
 
407
446
  set_token(api_key)
408
447
 
409
448
  resource = Resource(attributes={"api.key": api_key})
410
449
  # Create isolated tracer provider for Paid - don't use or modify global provider
411
- paid_tracer_provider = TracerProvider(resource=resource)
450
+ # Pass explicit sampler and span_limits to avoid inheriting from OTEL env vars
451
+ # (OTEL_TRACES_SAMPLER=always_off or OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=1
452
+ # set by the client app would silently break this)
453
+ paid_tracer_provider = TracerProvider(
454
+ resource=resource,
455
+ sampler=ALWAYS_ON,
456
+ span_limits=SpanLimits(
457
+ max_span_attributes=128,
458
+ max_event_attributes=128,
459
+ max_link_attributes=128,
460
+ max_events=128,
461
+ max_links=128,
462
+ ),
463
+ )
464
+ # Override OTEL_SDK_DISABLED - the client may set it to disable their own OTEL
465
+ paid_tracer_provider._disabled = False
466
+ logger.debug("[paid:init] TracerProvider created (sampler=ALWAYS_ON, OTEL_SDK_DISABLED overridden)")
412
467
 
413
468
  # Add span processor to prefix span names and add customer/agent ID attributes
414
469
  paid_tracer_provider.add_span_processor(PaidSpanProcessor())
470
+ logger.debug("[paid:init] Added PaidSpanProcessor")
415
471
 
416
472
  # Add Pydantic span processor - it self-filters by scope using the settings registry
417
473
  paid_tracer_provider.add_span_processor(PydanticSpanProcessor())
474
+ logger.debug("[paid:init] Added PydanticSpanProcessor")
418
475
 
419
476
  # Legacy support: if processor_settings.pydantic is provided, register it for the default scope
420
477
  if processor_settings and processor_settings.pydantic:
421
478
  _PydanticSettingsRegistry.register(PYDANTIC_SCOPE_NAME, processor_settings.pydantic)
479
+ logger.debug("[paid:init] Registered legacy PydanticProcessorSettings for scope=%s", PYDANTIC_SCOPE_NAME)
422
480
 
423
- # Set up OTLP exporter
481
+ # Set up OTLP exporter with explicit settings to avoid inheriting from
482
+ # client OTEL env vars (e.g. OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TIMEOUT)
424
483
  otlp_exporter = OTLPSpanExporter(
425
484
  endpoint=collector_endpoint,
426
- headers={}, # No additional headers needed for OTLP
485
+ headers={"_paid": "1"}, # Non-empty to prevent env var OTEL_EXPORTER_OTLP_HEADERS leak (empty dict is falsy)
486
+ timeout=10, # Explicit timeout to prevent env var OTEL_EXPORTER_OTLP_TIMEOUT override
427
487
  )
428
488
 
429
489
  # Use SimpleSpanProcessor for immediate span export.
@@ -431,6 +491,7 @@ def initialize_tracing(
431
491
  # Airflow terminates processes before the batch is sent, losing traces.
432
492
  span_processor = SimpleSpanProcessor(otlp_exporter)
433
493
  paid_tracer_provider.add_span_processor(span_processor)
494
+ logger.debug("[paid:init] Added SimpleSpanProcessor with OTLPSpanExporter -> %s", collector_endpoint)
434
495
 
435
496
  setup_graceful_termination(paid_tracer_provider) # doesn't throw
436
497
 
@@ -454,6 +515,7 @@ def get_paid_tracer() -> trace.Tracer:
454
515
  Tracing is automatically initialized when using @paid_tracing decorator or context manager.
455
516
  """
456
517
  global paid_tracer_provider
518
+ logger.debug("[paid:init] get_paid_tracer: provider_type=%s", type(paid_tracer_provider).__name__)
457
519
  return paid_tracer_provider.get_tracer("paid.python")
458
520
 
459
521
 
@@ -553,15 +615,22 @@ def trace_sync_(
553
615
  trace_flags=TraceFlags(TraceFlags.SAMPLED),
554
616
  )
555
617
  ctx = trace.set_span_in_context(NonRecordingSpan(span_context))
618
+ logger.debug("[paid:distributed] trace_sync_ using override trace_id=%s",
619
+ format(override_trace_id, '032x') if isinstance(override_trace_id, int) else str(override_trace_id))
620
+ else:
621
+ logger.debug("[paid:distributed] trace_sync_ no override trace_id, using auto-generated")
556
622
 
557
623
  try:
558
624
  tracer = get_paid_tracer()
559
- logger.info(f"Creating span for external_customer_id: {external_customer_id}")
625
+ logger.debug("[paid:span] trace_sync_ creating parent_span for customer_id=%s, agent_id=%s, fn=%s",
626
+ external_customer_id, external_agent_id, getattr(fn, '__name__', repr(fn)))
560
627
  with tracer.start_as_current_span("parent_span", context=ctx) as span:
628
+ logger.debug("[paid:span] trace_sync_ span created, trace_id=%s",
629
+ format(span.get_span_context().trace_id, '032x') if isinstance(span.get_span_context().trace_id, int) else str(span.get_span_context().trace_id))
561
630
  try:
562
631
  result = fn(*args, **kwargs)
563
632
  span.set_status(Status(StatusCode.OK))
564
- logger.info(f"Function {fn.__name__} executed successfully")
633
+ logger.debug("[paid:span] trace_sync_ fn=%s completed successfully", getattr(fn, '__name__', repr(fn)))
565
634
  return result
566
635
  except Exception as error:
567
636
  span.set_status(Status(StatusCode.ERROR, str(error)))
@@ -624,18 +693,25 @@ async def trace_async_(
624
693
  trace_flags=TraceFlags(TraceFlags.SAMPLED),
625
694
  )
626
695
  ctx = trace.set_span_in_context(NonRecordingSpan(span_context))
696
+ logger.debug("[paid:distributed] trace_async_ using override trace_id=%s",
697
+ format(override_trace_id, '032x') if isinstance(override_trace_id, int) else str(override_trace_id))
698
+ else:
699
+ logger.debug("[paid:distributed] trace_async_ no override trace_id, using auto-generated")
627
700
 
628
701
  try:
629
702
  tracer = get_paid_tracer()
630
- logger.info(f"Creating span for external_customer_id: {external_customer_id}")
703
+ logger.debug("[paid:span] trace_async_ creating parent_span for customer_id=%s, agent_id=%s, fn=%s",
704
+ external_customer_id, external_agent_id, getattr(fn, '__name__', repr(fn)))
631
705
  with tracer.start_as_current_span("parent_span", context=ctx) as span:
706
+ logger.debug("[paid:span] trace_async_ span created, trace_id=%s",
707
+ format(span.get_span_context().trace_id, '032x') if isinstance(span.get_span_context().trace_id, int) else str(span.get_span_context().trace_id))
632
708
  try:
633
709
  if asyncio.iscoroutinefunction(fn):
634
710
  result = await fn(*args, **kwargs)
635
711
  else:
636
712
  result = fn(*args, **kwargs)
637
713
  span.set_status(Status(StatusCode.OK))
638
- logger.info(f"Async function {fn.__name__} executed successfully")
714
+ logger.debug("[paid:span] trace_async_ fn=%s completed successfully", getattr(fn, '__name__', repr(fn)))
639
715
  return result
640
716
  except Exception as error:
641
717
  span.set_status(Status(StatusCode.ERROR, str(error)))
File without changes