paid-python 0.2.0__tar.gz → 0.3.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 (105) hide show
  1. {paid_python-0.2.0 → paid_python-0.3.0}/PKG-INFO +2 -1
  2. {paid_python-0.2.0 → paid_python-0.3.0}/pyproject.toml +2 -1
  3. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/autoinstrumentation.py +26 -2
  4. paid_python-0.3.0/src/paid/tracing/context_data.py +60 -0
  5. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/context_manager.py +8 -35
  6. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/distributed_tracing.py +3 -3
  7. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/tracing.py +17 -41
  8. {paid_python-0.2.0 → paid_python-0.3.0}/LICENSE +0 -0
  9. {paid_python-0.2.0 → paid_python-0.3.0}/README.md +0 -0
  10. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/__init__.py +0 -0
  11. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/agents/__init__.py +0 -0
  12. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/agents/client.py +0 -0
  13. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/agents/raw_client.py +0 -0
  14. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/client.py +0 -0
  15. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/contacts/__init__.py +0 -0
  16. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/contacts/client.py +0 -0
  17. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/contacts/raw_client.py +0 -0
  18. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/__init__.py +0 -0
  19. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/api_error.py +0 -0
  20. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/client_wrapper.py +0 -0
  21. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/datetime_utils.py +0 -0
  22. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/file.py +0 -0
  23. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/force_multipart.py +0 -0
  24. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/http_client.py +0 -0
  25. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/http_response.py +0 -0
  26. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/jsonable_encoder.py +0 -0
  27. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/pydantic_utilities.py +0 -0
  28. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/query_encoder.py +0 -0
  29. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/remove_none_from_dict.py +0 -0
  30. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/request_options.py +0 -0
  31. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/core/serialization.py +0 -0
  32. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/customers/__init__.py +0 -0
  33. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/customers/client.py +0 -0
  34. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/customers/raw_client.py +0 -0
  35. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/environment.py +0 -0
  36. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/errors/__init__.py +0 -0
  37. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/errors/bad_request_error.py +0 -0
  38. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/errors/forbidden_error.py +0 -0
  39. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/errors/not_found_error.py +0 -0
  40. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/logger.py +0 -0
  41. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/orders/__init__.py +0 -0
  42. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/orders/client.py +0 -0
  43. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/orders/lines/__init__.py +0 -0
  44. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/orders/lines/client.py +0 -0
  45. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/orders/lines/raw_client.py +0 -0
  46. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/orders/raw_client.py +0 -0
  47. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/py.typed +0 -0
  48. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/__init__.py +0 -0
  49. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/signal.py +0 -0
  50. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/__init__.py +0 -0
  51. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/anthropic/__init__.py +0 -0
  52. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/anthropic/anthropicWrapper.py +0 -0
  53. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/bedrock/__init__.py +0 -0
  54. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/bedrock/bedrockWrapper.py +0 -0
  55. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/gemini/__init__.py +0 -0
  56. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/gemini/geminiWrapper.py +0 -0
  57. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/langchain/__init__.py +0 -0
  58. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/langchain/paidLangChainCallback.py +0 -0
  59. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/llamaindex/__init__.py +0 -0
  60. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/llamaindex/llamaIndexWrapper.py +0 -0
  61. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/mistral/__init__.py +0 -0
  62. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/mistral/mistralWrapper.py +0 -0
  63. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/openai/__init__.py +0 -0
  64. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/openai/openAiWrapper.py +0 -0
  65. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/openai_agents/__init__.py +0 -0
  66. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/openai_agents/openaiAgentsHook.py +0 -0
  67. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/tracing/wrappers/utils.py +0 -0
  68. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/__init__.py +0 -0
  69. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/address.py +0 -0
  70. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/agent.py +0 -0
  71. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/agent_attribute.py +0 -0
  72. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/agent_price_point.py +0 -0
  73. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/agent_price_point_tiers.py +0 -0
  74. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/agent_update.py +0 -0
  75. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/api_error.py +0 -0
  76. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/billing_frequency.py +0 -0
  77. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/charge_type.py +0 -0
  78. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/contact.py +0 -0
  79. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/cost_amount.py +0 -0
  80. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/cost_trace.py +0 -0
  81. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/cost_traces_response.py +0 -0
  82. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/creation_source.py +0 -0
  83. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/creation_state.py +0 -0
  84. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/customer.py +0 -0
  85. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/customer_update.py +0 -0
  86. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/entitlement_usage.py +0 -0
  87. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/error.py +0 -0
  88. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/order.py +0 -0
  89. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/order_line.py +0 -0
  90. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/order_line_attribute.py +0 -0
  91. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/order_line_attribute_create.py +0 -0
  92. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/order_line_attribute_pricing.py +0 -0
  93. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/order_line_create.py +0 -0
  94. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/pagination_meta.py +0 -0
  95. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/price_point.py +0 -0
  96. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/pricing.py +0 -0
  97. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/pricing_model_type.py +0 -0
  98. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/salutation.py +0 -0
  99. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/signal.py +0 -0
  100. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/tax_exempt_status.py +0 -0
  101. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/types/tier.py +0 -0
  102. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/usage/__init__.py +0 -0
  103. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/usage/client.py +0 -0
  104. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/usage/raw_client.py +0 -0
  105. {paid_python-0.2.0 → paid_python-0.3.0}/src/paid/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: paid-python
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary:
5
5
  Requires-Python: >=3.9,<3.14
6
6
  Classifier: Intended Audience :: Developers
@@ -20,6 +20,7 @@ Classifier: Typing :: Typed
20
20
  Requires-Dist: httpx (>=0.21.0)
21
21
  Requires-Dist: mutagen (>=1.47.0)
22
22
  Requires-Dist: openinference-instrumentation-bedrock (>=0.1.0)
23
+ Requires-Dist: openinference-instrumentation-google-genai (>=0.1.8)
23
24
  Requires-Dist: openinference-instrumentation-openai-agents (>=1.0.0)
24
25
  Requires-Dist: opentelemetry-api (>=1.23.0)
25
26
  Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.23.0)
@@ -3,7 +3,7 @@ name = "paid-python"
3
3
 
4
4
  [tool.poetry]
5
5
  name = "paid-python"
6
- version = "0.2.0"
6
+ version = "0.3.0"
7
7
  description = ""
8
8
  readme = "README.md"
9
9
  authors = []
@@ -49,6 +49,7 @@ opentelemetry-instrumentation-openai = ">=0.47.0"
49
49
  opentelemetry-instrumentation-langchain = ">=0.47.0"
50
50
  openinference-instrumentation-openai-agents = ">=1.0.0"
51
51
  openinference-instrumentation-bedrock = ">=0.1.0"
52
+ openinference-instrumentation-google-genai = ">=0.1.8"
52
53
 
53
54
  [tool.poetry.group.dev.dependencies]
54
55
  mypy = "==1.13.0"
@@ -56,6 +56,13 @@ try:
56
56
  except ImportError:
57
57
  LANGCHAIN_AVAILABLE = False
58
58
 
59
+ try:
60
+ from openinference.instrumentation.google_genai import GoogleGenAIInstrumentor
61
+
62
+ GOOGLE_GENAI_AVAILABLE = True
63
+ except ImportError:
64
+ GOOGLE_GENAI_AVAILABLE = False
65
+
59
66
 
60
67
  # Track which instrumentors have been initialized
61
68
  _initialized_instrumentors: List[str] = []
@@ -77,6 +84,7 @@ def paid_autoinstrument(libraries: Optional[List[str]] = None) -> None:
77
84
  - "openai-agents": OpenAI Agents SDK
78
85
  - "bedrock": AWS Bedrock
79
86
  - "langchain": LangChain library
87
+ - "google-genai": Google GenAI library
80
88
  If None, all supported libraries that are installed will be instrumented.
81
89
 
82
90
  Note:
@@ -106,7 +114,7 @@ def paid_autoinstrument(libraries: Optional[List[str]] = None) -> None:
106
114
 
107
115
  # Default to all supported libraries if none specified
108
116
  if libraries is None:
109
- libraries = ["anthropic", "gemini", "openai", "openai-agents", "bedrock", "langchain"]
117
+ libraries = ["anthropic", "gemini", "openai", "openai-agents", "bedrock", "langchain", "google-genai"]
110
118
 
111
119
  for library in libraries:
112
120
  if library in _initialized_instrumentors:
@@ -125,6 +133,8 @@ def paid_autoinstrument(libraries: Optional[List[str]] = None) -> None:
125
133
  _instrument_bedrock()
126
134
  elif library == "langchain":
127
135
  _instrument_langchain()
136
+ elif library == "google-genai":
137
+ _instrument_google_genai()
128
138
  else:
129
139
  logger.warning(
130
140
  f"Unknown library '{library}' - supported libraries: anthropic, gemini, openai, openai-agents, bedrock, langchain"
@@ -217,7 +227,21 @@ def _instrument_langchain() -> None:
217
227
  return
218
228
 
219
229
  # Instrument LangChain with Paid's tracer provider
220
- LangchainInstrumentor().instrument(tracer_provider=tracing.paid_tracer_provider)
230
+ LangchainInstrumentor(disable_trace_context_propagation=True).instrument(tracer_provider=tracing.paid_tracer_provider)
231
+
221
232
 
222
233
  _initialized_instrumentors.append("langchain")
223
234
  logger.info("LangChain auto-instrumentation enabled")
235
+
236
+ def _instrument_google_genai() -> None:
237
+ """
238
+ Instrument Google GenAI using openinference-instrumentation-google-genai.
239
+ """
240
+ if not GOOGLE_GENAI_AVAILABLE:
241
+ logger.warning("Google GenAI instrumentation library not available, skipping instrumentation")
242
+ return
243
+
244
+
245
+ GoogleGenAIInstrumentor().instrument(tracer_provider=tracing.paid_tracer_provider)
246
+ _initialized_instrumentors.append("google-genai")
247
+ logger.info("Google GenAI auto-instrumentation enabled")
@@ -0,0 +1,60 @@
1
+ import contextvars
2
+ from typing import Any, Optional
3
+
4
+ from paid.logger import logger
5
+
6
+
7
+ # this class is used like a namespace, it's not for instantiation
8
+ class ContextData:
9
+ _EXTERNAL_CUSTOMER_ID = contextvars.ContextVar[Optional[str]]("external_customer_id", default=None)
10
+ _EXTERNAL_AGENT_ID = contextvars.ContextVar[Optional[str]]("external_agent_id", default=None)
11
+ _TRACE_ID = contextvars.ContextVar[Optional[int]]("trace_id", default=None)
12
+ _STORE_PROMPT = contextvars.ContextVar[Optional[bool]]("store_prompt", default=False)
13
+ _USER_METADATA = contextvars.ContextVar[Optional[dict[str, Any]]]("user_metadata", default=None)
14
+
15
+ _context: dict[str, contextvars.ContextVar] = {
16
+ "external_customer_id": _EXTERNAL_CUSTOMER_ID,
17
+ "external_agent_id": _EXTERNAL_AGENT_ID,
18
+ "trace_id": _TRACE_ID,
19
+ "store_prompt": _STORE_PROMPT,
20
+ "user_metadata": _USER_METADATA,
21
+ }
22
+
23
+ # Use ContextVar for reset tokens to avoid race conditions in async/concurrent scenarios
24
+ _reset_tokens: contextvars.ContextVar[Optional[dict[str, Any]]] = contextvars.ContextVar(
25
+ "reset_tokens", default=None
26
+ )
27
+
28
+ @classmethod
29
+ def _get_or_create_reset_tokens(cls) -> dict[str, Any]:
30
+ """Get the reset tokens dict for this context, creating a new one if needed."""
31
+ reset_tokens = cls._reset_tokens.get()
32
+ if reset_tokens is None:
33
+ reset_tokens = {}
34
+ cls._reset_tokens.set(reset_tokens)
35
+ return reset_tokens
36
+
37
+ @classmethod
38
+ def get_context(cls) -> dict[str, Any]:
39
+ return {key: var.get() for key, var in cls._context.items()}
40
+
41
+ @classmethod
42
+ def get_context_key(cls, key: str) -> Any:
43
+ return cls._context[key].get() if key in cls._context else None
44
+
45
+ @classmethod
46
+ def set_context_key(cls, key: str, value: Any) -> None:
47
+ if key not in cls._context:
48
+ logger.warning(f"Invalid context key: {key}")
49
+ return
50
+ reset_token = cls._context[key].set(value)
51
+ reset_tokens = cls._get_or_create_reset_tokens()
52
+ reset_tokens[key] = reset_token
53
+
54
+ @classmethod
55
+ def reset_context(cls) -> None:
56
+ reset_tokens = cls._reset_tokens.get()
57
+ if reset_tokens:
58
+ for key, reset_token in reset_tokens.items():
59
+ cls._context[key].reset(reset_token)
60
+ reset_tokens.clear()
@@ -1,9 +1,9 @@
1
1
  import asyncio
2
- import contextvars
3
2
  import functools
4
- from typing import Any, Callable, Dict, Optional, Tuple
3
+ from typing import Any, Callable, Dict, Optional
5
4
 
6
5
  from . import distributed_tracing, tracing
6
+ from .context_data import ContextData
7
7
  from .tracing import get_paid_tracer, get_token, initialize_tracing, trace_async_, trace_sync_
8
8
  from opentelemetry import trace
9
9
  from opentelemetry.context import Context
@@ -78,14 +78,6 @@ class paid_tracing:
78
78
  self.metadata = metadata
79
79
  self.span: Optional[Span] = None
80
80
  self.span_ctx: Optional[Any] = None # Context manager for the span
81
- self.reset_tokens: Optional[
82
- Tuple[
83
- contextvars.Token[Optional[str]], # external_customer_id
84
- contextvars.Token[Optional[str]], # external_agent_id
85
- contextvars.Token[Optional[bool]], # store_prompt
86
- contextvars.Token[Optional[Dict[str, Any]]], # metadata
87
- ]
88
- ] = None
89
81
 
90
82
  if not get_token():
91
83
  initialize_tracing(None, self.collector_endpoint)
@@ -94,23 +86,15 @@ class paid_tracing:
94
86
  """Set up context variables and return OTEL context if needed."""
95
87
 
96
88
  # Set context variables
97
- reset_customer_id_ctx_token = tracing.paid_external_customer_id_var.set(self.external_customer_id)
98
- reset_agent_id_ctx_token = tracing.paid_external_agent_id_var.set(self.external_agent_id)
99
- reset_store_prompt_ctx_token = tracing.paid_store_prompt_var.set(self.store_prompt)
100
- reset_user_metadata_ctx_token = tracing.paid_user_metadata_var.set(self.metadata)
101
-
102
- # Store reset tokens for cleanup
103
- self.reset_tokens = (
104
- reset_customer_id_ctx_token,
105
- reset_agent_id_ctx_token,
106
- reset_store_prompt_ctx_token,
107
- reset_user_metadata_ctx_token,
108
- )
89
+ ContextData.set_context_key("external_customer_id", self.external_customer_id)
90
+ ContextData.set_context_key("external_agent_id", self.external_agent_id)
91
+ ContextData.set_context_key("store_prompt", self.store_prompt)
92
+ ContextData.set_context_key("user_metadata", self.metadata)
109
93
 
110
94
  # Handle distributed tracing token
111
95
  override_trace_id = self.tracing_token
112
96
  if not override_trace_id:
113
- override_trace_id = tracing.paid_trace_id_var.get()
97
+ override_trace_id = ContextData.get_context_key("trace_id")
114
98
 
115
99
  ctx: Optional[Context] = None
116
100
  if override_trace_id is not None:
@@ -126,18 +110,7 @@ class paid_tracing:
126
110
 
127
111
  def _cleanup_context(self):
128
112
  """Reset all context variables."""
129
- if self.reset_tokens:
130
- (
131
- reset_customer_id_ctx_token,
132
- reset_agent_id_ctx_token,
133
- reset_store_prompt_ctx_token,
134
- reset_user_metadata_ctx_token,
135
- ) = self.reset_tokens
136
- tracing.paid_external_customer_id_var.reset(reset_customer_id_ctx_token)
137
- tracing.paid_external_agent_id_var.reset(reset_agent_id_ctx_token)
138
- tracing.paid_store_prompt_var.reset(reset_store_prompt_ctx_token)
139
- tracing.paid_user_metadata_var.reset(reset_user_metadata_ctx_token)
140
- self.reset_tokens = None
113
+ ContextData.reset_context()
141
114
 
142
115
  # Context manager methods for sync
143
116
  def __enter__(self):
@@ -1,6 +1,6 @@
1
1
  import warnings
2
2
 
3
- from . import tracing
3
+ from .context_data import ContextData
4
4
  from opentelemetry.sdk.trace.id_generator import RandomIdGenerator
5
5
 
6
6
  otel_id_generator = RandomIdGenerator()
@@ -85,7 +85,7 @@ def set_tracing_token(token: int):
85
85
  DeprecationWarning,
86
86
  stacklevel=2,
87
87
  )
88
- _ = tracing.paid_trace_id_var.set(token)
88
+ ContextData.set_context_key("trace_id", token)
89
89
 
90
90
 
91
91
  def unset_tracing_token():
@@ -110,4 +110,4 @@ def unset_tracing_token():
110
110
  DeprecationWarning,
111
111
  stacklevel=2,
112
112
  )
113
- _ = tracing.paid_trace_id_var.set(None)
113
+ ContextData.set_context_key("trace_id", None)
@@ -1,13 +1,13 @@
1
1
  # Initializing tracing for OTLP
2
2
  import asyncio
3
3
  import atexit
4
- import contextvars
5
4
  import os
6
5
  import signal
7
6
  from typing import Any, Awaitable, Callable, Dict, Optional, Tuple, TypeVar, Union
8
7
 
9
8
  import dotenv
10
9
  from . import distributed_tracing
10
+ from .context_data import ContextData
11
11
  from opentelemetry import trace
12
12
  from opentelemetry.context import Context
13
13
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
@@ -23,24 +23,6 @@ DEFAULT_COLLECTOR_ENDPOINT = (
23
23
  os.environ.get("PAID_OTEL_COLLECTOR_ENDPOINT") or "https://collector.agentpaid.io:4318/v1/traces"
24
24
  )
25
25
 
26
- # Context variables for passing data to nested spans (e.g., in openAiWrapper)
27
- paid_external_customer_id_var: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar(
28
- "paid_external_customer_id", default=None
29
- )
30
- paid_external_agent_id_var: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar(
31
- "paid_external_agent_id", default=None
32
- )
33
- # trace id storage (generated from token)
34
- paid_trace_id_var: contextvars.ContextVar[Optional[int]] = contextvars.ContextVar("paid_trace_id", default=None)
35
- # flag to enable storing prompt contents
36
- paid_store_prompt_var: contextvars.ContextVar[Optional[bool]] = contextvars.ContextVar(
37
- "paid_store_prompt", default=False
38
- )
39
- # user metadata
40
- paid_user_metadata_var: contextvars.ContextVar[Optional[Dict[str, Any]]] = contextvars.ContextVar(
41
- "paid_user_metadata", default=None
42
- )
43
-
44
26
  T = TypeVar("T")
45
27
 
46
28
 
@@ -102,15 +84,15 @@ class PaidSpanProcessor(SpanProcessor):
102
84
  span.update_name(f"{self.SPAN_NAME_PREFIX}{span.name}")
103
85
 
104
86
  # Add customer and agent IDs from context
105
- customer_id = paid_external_customer_id_var.get()
87
+ customer_id = ContextData.get_context_key("external_customer_id")
106
88
  if customer_id:
107
89
  span.set_attribute("external_customer_id", customer_id)
108
90
 
109
- agent_id = paid_external_agent_id_var.get()
91
+ agent_id = ContextData.get_context_key("external_agent_id")
110
92
  if agent_id:
111
93
  span.set_attribute("external_agent_id", agent_id)
112
94
 
113
- metadata = paid_user_metadata_var.get()
95
+ metadata = ContextData.get_context_key("user_metadata")
114
96
  if metadata:
115
97
  metadata_attributes: dict[str, Any] = {}
116
98
 
@@ -131,7 +113,7 @@ class PaidSpanProcessor(SpanProcessor):
131
113
 
132
114
  def on_end(self, span: ReadableSpan) -> None:
133
115
  """Filter out prompt and response contents unless explicitly asked to store"""
134
- store_prompt = paid_store_prompt_var.get()
116
+ store_prompt = ContextData.get_context_key("store_prompt")
135
117
  if store_prompt:
136
118
  return
137
119
 
@@ -299,15 +281,15 @@ def trace_sync_(
299
281
  kwargs = kwargs or {}
300
282
 
301
283
  # Set context variables for access by nested spans
302
- reset_customer_id_ctx_token = paid_external_customer_id_var.set(external_customer_id)
303
- reset_agent_id_ctx_token = paid_external_agent_id_var.set(external_agent_id)
304
- reset_store_prompt_ctx_token = paid_store_prompt_var.set(store_prompt)
305
- reset_user_metadata_ctx_token = paid_user_metadata_var.set(metadata)
284
+ ContextData.set_context_key("external_customer_id", external_customer_id)
285
+ ContextData.set_context_key("external_agent_id", external_agent_id)
286
+ ContextData.set_context_key("store_prompt", store_prompt)
287
+ ContextData.set_context_key("user_metadata", metadata)
306
288
 
307
289
  # If user set trace context manually
308
290
  override_trace_id = tracing_token
309
291
  if not override_trace_id:
310
- override_trace_id = paid_trace_id_var.get()
292
+ override_trace_id = ContextData.get_context_key("trace_id")
311
293
  ctx: Optional[Context] = None
312
294
  if override_trace_id is not None:
313
295
  span_context = SpanContext(
@@ -331,10 +313,7 @@ def trace_sync_(
331
313
  span.set_status(Status(StatusCode.ERROR, str(error)))
332
314
  raise
333
315
  finally:
334
- paid_external_customer_id_var.reset(reset_customer_id_ctx_token)
335
- paid_external_agent_id_var.reset(reset_agent_id_ctx_token)
336
- paid_store_prompt_var.reset(reset_store_prompt_ctx_token)
337
- paid_user_metadata_var.reset(reset_user_metadata_ctx_token)
316
+ ContextData.reset_context()
338
317
 
339
318
 
340
319
  async def trace_async_(
@@ -373,15 +352,15 @@ async def trace_async_(
373
352
  kwargs = kwargs or {}
374
353
 
375
354
  # Set context variables for access by nested spans
376
- reset_customer_id_ctx_token = paid_external_customer_id_var.set(external_customer_id)
377
- reset_agent_id_ctx_token = paid_external_agent_id_var.set(external_agent_id)
378
- reset_store_prompt_ctx_token = paid_store_prompt_var.set(store_prompt)
379
- reset_user_metadata_ctx_token = paid_user_metadata_var.set(metadata)
355
+ ContextData.set_context_key("external_customer_id", external_customer_id)
356
+ ContextData.set_context_key("external_agent_id", external_agent_id)
357
+ ContextData.set_context_key("store_prompt", store_prompt)
358
+ ContextData.set_context_key("user_metadata", metadata)
380
359
 
381
360
  # If user set trace context manually
382
361
  override_trace_id = tracing_token
383
362
  if not override_trace_id:
384
- override_trace_id = paid_trace_id_var.get()
363
+ override_trace_id = ContextData.get_context_key("trace_id")
385
364
  ctx: Optional[Context] = None
386
365
  if override_trace_id is not None:
387
366
  span_context = SpanContext(
@@ -408,7 +387,4 @@ async def trace_async_(
408
387
  span.set_status(Status(StatusCode.ERROR, str(error)))
409
388
  raise
410
389
  finally:
411
- paid_external_customer_id_var.reset(reset_customer_id_ctx_token)
412
- paid_external_agent_id_var.reset(reset_agent_id_ctx_token)
413
- paid_store_prompt_var.reset(reset_store_prompt_ctx_token)
414
- paid_user_metadata_var.reset(reset_user_metadata_ctx_token)
390
+ ContextData.reset_context()
File without changes
File without changes