paid-python 1.2.0__tar.gz → 1.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.
- {paid_python-1.2.0 → paid_python-1.3.0}/PKG-INFO +1 -1
- {paid_python-1.2.0 → paid_python-1.3.0}/pyproject.toml +1 -1
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/client_wrapper.py +2 -2
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/autoinstrumentation.py +3 -0
- paid_python-1.3.0/src/paid/tracing/openai_agents_patches/__init__.py +15 -0
- paid_python-1.3.0/src/paid/tracing/openai_agents_patches/patches.py +189 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/tracing.py +39 -9
- {paid_python-1.2.0 → paid_python-1.3.0}/LICENSE +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/README.md +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/contacts/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/contacts/client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/contacts/raw_client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/contacts/types/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/contacts/types/create_contact_request_roles_item.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/api_error.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/datetime_utils.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/file.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/force_multipart.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/http_client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/http_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/http_sse/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/http_sse/_api.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/http_sse/_decoders.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/http_sse/_exceptions.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/http_sse/_models.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/jsonable_encoder.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/pydantic_utilities.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/query_encoder.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/remove_none_from_dict.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/request_options.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/core/serialization.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/customers/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/customers/client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/customers/raw_client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/environment.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/errors/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/errors/bad_request_error.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/errors/forbidden_error.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/errors/internal_server_error.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/errors/not_found_error.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/invoices/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/invoices/client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/invoices/raw_client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/logger.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/orders/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/orders/client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/orders/raw_client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/products/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/products/client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/products/raw_client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/py.typed +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/signals/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/signals/client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/signals/raw_client.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/anthropic_patches/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/anthropic_patches/patches.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/context_data.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/context_manager.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/distributed_tracing.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/gemini_patches/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/gemini_patches/patches.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/signal.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/anthropic/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/anthropic/anthropicWrapper.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/bedrock/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/bedrock/bedrockWrapper.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/gemini/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/gemini/geminiWrapper.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/langchain/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/langchain/paidLangChainCallback.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/mistral/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/mistral/mistralWrapper.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/openai/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/openai/openAiWrapper.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/openai_agents/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/openai_agents/openaiAgentsHook.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/utils.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/__init__.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/attribution.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/bulk_signals_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/contact.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/contact_billing_address.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/contact_list_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/contact_roles_item.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/create_order_line_attribute_request.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/create_order_line_request.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/customer.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/customer_attribution.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/customer_billing_address.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/customer_by_external_id.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/customer_by_id.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/customer_creation_state.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/customer_list_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/empty_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/error_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/invoice.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/invoice_line.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/invoice_line_payment_status.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/invoice_lines_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/invoice_list_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/invoice_payment_status.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/invoice_source.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/invoice_status.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/invoice_tax_status.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/order.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/order_creation_state.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/order_line.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/order_lines_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/order_list_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/pagination.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/product.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/product_by_external_id.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/product_by_id.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/product_list_response.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/signal.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/update_contact_request.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/update_contact_request_roles_item.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/update_customer_request.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/update_product_request.py +0 -0
- {paid_python-1.2.0 → paid_python-1.3.0}/src/paid/version.py +0 -0
|
@@ -24,12 +24,12 @@ class BaseClientWrapper:
|
|
|
24
24
|
import platform
|
|
25
25
|
|
|
26
26
|
headers: typing.Dict[str, str] = {
|
|
27
|
-
"User-Agent": "paid-python/1.
|
|
27
|
+
"User-Agent": "paid-python/1.3.0",
|
|
28
28
|
"X-Fern-Language": "Python",
|
|
29
29
|
"X-Fern-Runtime": f"python/{platform.python_version()}",
|
|
30
30
|
"X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}",
|
|
31
31
|
"X-Fern-SDK-Name": "paid-python",
|
|
32
|
-
"X-Fern-SDK-Version": "1.
|
|
32
|
+
"X-Fern-SDK-Version": "1.3.0",
|
|
33
33
|
**(self.get_custom_headers() or {}),
|
|
34
34
|
}
|
|
35
35
|
headers["Authorization"] = f"Bearer {self._get_token()}"
|
|
@@ -203,10 +203,13 @@ def _instrument_openai_agents() -> None:
|
|
|
203
203
|
logger.warning("OpenAI Agents library not available, skipping instrumentation")
|
|
204
204
|
return
|
|
205
205
|
|
|
206
|
+
from .openai_agents_patches import instrument_openai_agents
|
|
207
|
+
|
|
206
208
|
logger.debug("[paid:autoinstrument] Instrumenting openai-agents with OpenAIAgentsInstrumentor, provider=%s",
|
|
207
209
|
type(tracing.paid_tracer_provider).__name__)
|
|
208
210
|
# Instrument OpenAI Agents with Paid's tracer provider
|
|
209
211
|
OpenAIAgentsInstrumentor().instrument(tracer_provider=tracing.paid_tracer_provider)
|
|
212
|
+
instrument_openai_agents()
|
|
210
213
|
|
|
211
214
|
_initialized_instrumentors.append("openai-agents")
|
|
212
215
|
logger.info("OpenAI Agents auto-instrumentation enabled")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Patches for openinference-instrumentation-openai-agents.
|
|
2
|
+
|
|
3
|
+
Adds tool metadata to function/tool spans by carrying schema details from
|
|
4
|
+
response spans onto the later function execution spans.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .patches import (
|
|
8
|
+
instrument_openai_agents,
|
|
9
|
+
uninstrument_openai_agents,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"instrument_openai_agents",
|
|
14
|
+
"uninstrument_openai_agents",
|
|
15
|
+
]
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""Monkey-patches for openinference-instrumentation-openai-agents.
|
|
2
|
+
|
|
3
|
+
Upstream records full tool schemas on response spans, but function/tool spans only
|
|
4
|
+
get `tool.name`. This patch caches function tool metadata per trace when the
|
|
5
|
+
response span ends, then applies that metadata to subsequent function spans.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Any, Optional
|
|
12
|
+
|
|
13
|
+
from agents.tracing.span_data import FunctionSpanData, ResponseSpanData
|
|
14
|
+
from openai.types.responses import FunctionTool, Response
|
|
15
|
+
from openinference.instrumentation import safe_json_dumps
|
|
16
|
+
from opentelemetry.context import detach
|
|
17
|
+
from opentelemetry.trace import Status, StatusCode
|
|
18
|
+
from opentelemetry.util.types import AttributeValue
|
|
19
|
+
|
|
20
|
+
from paid.logger import logger
|
|
21
|
+
|
|
22
|
+
_originals: dict[str, Any] = {}
|
|
23
|
+
_SEMCONV_FALLBACKS = {
|
|
24
|
+
"TOOL_DESCRIPTION": "tool.description",
|
|
25
|
+
"TOOL_PARAMETERS": "tool.parameters",
|
|
26
|
+
"TOOL_JSON_SCHEMA": "tool.json_schema",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def instrument_openai_agents() -> None:
|
|
31
|
+
"""Apply all OpenAI Agents patches. Call after instrumenting the SDK."""
|
|
32
|
+
_patch_tracing_processor()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def uninstrument_openai_agents() -> None:
|
|
36
|
+
"""Restore original OpenAI Agents methods."""
|
|
37
|
+
try:
|
|
38
|
+
from openinference.instrumentation.openai_agents import _processor as processor_mod
|
|
39
|
+
except ImportError:
|
|
40
|
+
_originals.clear()
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
processor = processor_mod.OpenInferenceTracingProcessor
|
|
44
|
+
if "on_span_end" in _originals:
|
|
45
|
+
processor.on_span_end = _originals.pop("on_span_end") # type: ignore[method-assign]
|
|
46
|
+
if "on_trace_end" in _originals:
|
|
47
|
+
processor.on_trace_end = _originals.pop("on_trace_end") # type: ignore[method-assign]
|
|
48
|
+
_originals.clear()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _patch_tracing_processor() -> None:
|
|
52
|
+
try:
|
|
53
|
+
from openinference.instrumentation.openai_agents import _processor as processor_mod
|
|
54
|
+
except ImportError:
|
|
55
|
+
logger.debug("Could not import openai-agents tracing processor, skipping patch")
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
processor = processor_mod.OpenInferenceTracingProcessor
|
|
59
|
+
if "on_span_end" in _originals:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
_originals["on_span_end"] = processor.on_span_end
|
|
63
|
+
_originals["on_trace_end"] = processor.on_trace_end
|
|
64
|
+
|
|
65
|
+
def _patched_on_trace_end(self, trace): # type: ignore[misc]
|
|
66
|
+
try:
|
|
67
|
+
return _originals["on_trace_end"](self, trace)
|
|
68
|
+
finally:
|
|
69
|
+
tool_cache = getattr(self, "_paid_tool_schemas_by_trace", None)
|
|
70
|
+
if isinstance(tool_cache, dict):
|
|
71
|
+
tool_cache.pop(trace.trace_id, None)
|
|
72
|
+
|
|
73
|
+
def _patched_on_span_end(self, span): # type: ignore[misc]
|
|
74
|
+
if token := self._tokens.pop(span.span_id, None):
|
|
75
|
+
detach(token) # type: ignore[arg-type]
|
|
76
|
+
if not (otel_span := self._otel_spans.pop(span.span_id, None)):
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
processing_error: Exception | None = None
|
|
80
|
+
end_time: Optional[int] = None
|
|
81
|
+
try:
|
|
82
|
+
otel_span.update_name(processor_mod._get_span_name(span))
|
|
83
|
+
data = span.span_data
|
|
84
|
+
if isinstance(data, ResponseSpanData):
|
|
85
|
+
if hasattr(data, "response") and isinstance(response := data.response, Response):
|
|
86
|
+
_cache_tool_schemas(self, span.trace_id, response)
|
|
87
|
+
otel_span.set_attribute(processor_mod.OUTPUT_MIME_TYPE, processor_mod.JSON)
|
|
88
|
+
otel_span.set_attribute(processor_mod.OUTPUT_VALUE, response.model_dump_json())
|
|
89
|
+
for k, v in processor_mod._get_attributes_from_response(response):
|
|
90
|
+
otel_span.set_attribute(k, v)
|
|
91
|
+
if hasattr(data, "input") and (input := data.input):
|
|
92
|
+
if isinstance(input, str):
|
|
93
|
+
otel_span.set_attribute(processor_mod.INPUT_VALUE, input)
|
|
94
|
+
elif isinstance(input, list):
|
|
95
|
+
otel_span.set_attribute(processor_mod.INPUT_MIME_TYPE, processor_mod.JSON)
|
|
96
|
+
otel_span.set_attribute(processor_mod.INPUT_VALUE, safe_json_dumps(input))
|
|
97
|
+
for k, v in processor_mod._get_attributes_from_input(input):
|
|
98
|
+
otel_span.set_attribute(k, v)
|
|
99
|
+
elif isinstance(data, FunctionSpanData):
|
|
100
|
+
for k, v in processor_mod._get_attributes_from_function_span_data(data):
|
|
101
|
+
otel_span.set_attribute(k, v)
|
|
102
|
+
for k, v in _get_enriched_function_attributes(self, span.trace_id, data.name):
|
|
103
|
+
otel_span.set_attribute(k, v)
|
|
104
|
+
elif isinstance(data, processor_mod.MCPListToolsSpanData):
|
|
105
|
+
for k, v in processor_mod._get_attributes_from_mcp_list_tool_span_data(data):
|
|
106
|
+
otel_span.set_attribute(k, v)
|
|
107
|
+
elif isinstance(data, processor_mod.HandoffSpanData):
|
|
108
|
+
if data.to_agent and data.from_agent:
|
|
109
|
+
key = f"{data.to_agent}:{span.trace_id}"
|
|
110
|
+
self._reverse_handoffs_dict[key] = data.from_agent
|
|
111
|
+
while len(self._reverse_handoffs_dict) > self._MAX_HANDOFFS_IN_FLIGHT:
|
|
112
|
+
self._reverse_handoffs_dict.popitem(last=False)
|
|
113
|
+
elif isinstance(data, processor_mod.AgentSpanData):
|
|
114
|
+
otel_span.set_attribute(processor_mod.GRAPH_NODE_ID, data.name)
|
|
115
|
+
key = f"{data.name}:{span.trace_id}"
|
|
116
|
+
if parent_node := self._reverse_handoffs_dict.pop(key, None):
|
|
117
|
+
otel_span.set_attribute(processor_mod.GRAPH_NODE_PARENT_ID, parent_node)
|
|
118
|
+
elif isinstance(data, processor_mod.GenerationSpanData):
|
|
119
|
+
for k, v in processor_mod._get_attributes_from_generation_span_data(data):
|
|
120
|
+
otel_span.set_attribute(k, v)
|
|
121
|
+
elif isinstance(data, (processor_mod.CustomSpanData, processor_mod.GuardrailSpanData)):
|
|
122
|
+
for k, v in processor_mod._flatten(data.export()):
|
|
123
|
+
otel_span.set_attribute(k, v)
|
|
124
|
+
except Exception as exc:
|
|
125
|
+
processing_error = exc
|
|
126
|
+
logger.debug("Failed to enrich openai-agents span", exc_info=True)
|
|
127
|
+
try:
|
|
128
|
+
otel_span.record_exception(exc)
|
|
129
|
+
except Exception:
|
|
130
|
+
logger.debug("Failed to record openai-agents patch exception", exc_info=True)
|
|
131
|
+
finally:
|
|
132
|
+
if span.ended_at:
|
|
133
|
+
try:
|
|
134
|
+
end_time = processor_mod._as_utc_nano(datetime.fromisoformat(span.ended_at))
|
|
135
|
+
except ValueError:
|
|
136
|
+
pass
|
|
137
|
+
if processing_error is not None:
|
|
138
|
+
otel_span.set_status(Status(StatusCode.ERROR, str(processing_error)))
|
|
139
|
+
else:
|
|
140
|
+
otel_span.set_status(status=processor_mod._get_span_status(span))
|
|
141
|
+
otel_span.end(end_time)
|
|
142
|
+
|
|
143
|
+
processor.on_trace_end = _patched_on_trace_end # type: ignore[method-assign]
|
|
144
|
+
processor.on_span_end = _patched_on_span_end # type: ignore[method-assign]
|
|
145
|
+
logger.debug("Patched OpenInferenceTracingProcessor to enrich openai-agents tool spans")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _cache_tool_schemas(processor: Any, trace_id: str, response: Response) -> None:
|
|
149
|
+
per_trace = getattr(processor, "_paid_tool_schemas_by_trace", None)
|
|
150
|
+
if not isinstance(per_trace, dict):
|
|
151
|
+
per_trace = {}
|
|
152
|
+
setattr(processor, "_paid_tool_schemas_by_trace", per_trace)
|
|
153
|
+
|
|
154
|
+
tool_map = per_trace.setdefault(trace_id, {})
|
|
155
|
+
for tool in response.tools or []:
|
|
156
|
+
if not isinstance(tool, FunctionTool):
|
|
157
|
+
continue
|
|
158
|
+
attrs: dict[str, AttributeValue] = {}
|
|
159
|
+
if tool.description:
|
|
160
|
+
attrs[processor_mod_attr("TOOL_DESCRIPTION")] = tool.description
|
|
161
|
+
if tool.parameters is not None:
|
|
162
|
+
attrs[processor_mod_attr("TOOL_PARAMETERS")] = safe_json_dumps(tool.parameters)
|
|
163
|
+
attrs[processor_mod_attr("TOOL_JSON_SCHEMA")] = safe_json_dumps(
|
|
164
|
+
{
|
|
165
|
+
"type": "function",
|
|
166
|
+
"function": {
|
|
167
|
+
"name": tool.name,
|
|
168
|
+
"description": tool.description,
|
|
169
|
+
"parameters": tool.parameters,
|
|
170
|
+
"strict": tool.strict,
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
tool_map[tool.name] = attrs
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _get_enriched_function_attributes(
|
|
178
|
+
processor: Any,
|
|
179
|
+
trace_id: str,
|
|
180
|
+
tool_name: str,
|
|
181
|
+
):
|
|
182
|
+
tool_map = getattr(processor, "_paid_tool_schemas_by_trace", {}).get(trace_id, {})
|
|
183
|
+
yield from tool_map.get(tool_name, {}).items()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def processor_mod_attr(name: str) -> str:
|
|
187
|
+
from openinference.instrumentation.openai_agents import _processor as processor_mod
|
|
188
|
+
|
|
189
|
+
return getattr(processor_mod, name, _SEMCONV_FALLBACKS.get(name, name.lower()))
|
|
@@ -5,7 +5,7 @@ import os
|
|
|
5
5
|
import random
|
|
6
6
|
import signal
|
|
7
7
|
from dataclasses import dataclass
|
|
8
|
-
from typing import Any, Awaitable, Callable, Dict, Optional, Tuple, TypeVar, Union
|
|
8
|
+
from typing import Any, Awaitable, Callable, Dict, Literal, Optional, Tuple, TypeVar, Union
|
|
9
9
|
|
|
10
10
|
from . import distributed_tracing
|
|
11
11
|
from .context_data import ContextData
|
|
@@ -13,7 +13,7 @@ from opentelemetry import trace
|
|
|
13
13
|
from opentelemetry.context import Context
|
|
14
14
|
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
15
15
|
from opentelemetry.sdk.trace import ReadableSpan, Span, SpanLimits, SpanProcessor, TracerProvider
|
|
16
|
-
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
|
|
16
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SimpleSpanProcessor
|
|
17
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
|
|
@@ -43,6 +43,9 @@ class ProcessorSettings:
|
|
|
43
43
|
pydantic: Optional[PydanticProcessorSettings] = None
|
|
44
44
|
"""Settings for Pydantic AI span processing. If provided, enables Pydantic-specific filtering."""
|
|
45
45
|
|
|
46
|
+
export_mode: Literal["batch", "simple"] = "simple"
|
|
47
|
+
"""Span export strategy. Defaults to simple."""
|
|
48
|
+
|
|
46
49
|
|
|
47
50
|
# Scope name constants for library-specific tracers
|
|
48
51
|
PYDANTIC_SCOPE_NAME = "paid.pydantic"
|
|
@@ -201,6 +204,10 @@ class PaidSpanProcessor(SpanProcessor):
|
|
|
201
204
|
"logfire.json_schema",
|
|
202
205
|
}
|
|
203
206
|
|
|
207
|
+
def __init__(self, span_processor_mode: Literal["batch", "simple"] = "simple") -> None:
|
|
208
|
+
"""Initialize processor with the export mode used by the OTLP span processor."""
|
|
209
|
+
self._span_processor_mode = span_processor_mode
|
|
210
|
+
|
|
204
211
|
def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None:
|
|
205
212
|
"""Called when a span is started. Prefix the span name and add attributes."""
|
|
206
213
|
|
|
@@ -232,6 +239,7 @@ class PaidSpanProcessor(SpanProcessor):
|
|
|
232
239
|
|
|
233
240
|
# Always stamp the SDK version so the backend can identify the source
|
|
234
241
|
span.set_attribute("paid.sdk.version", f"python-{_paid_version}")
|
|
242
|
+
span.set_attribute("paid_span_processor_mode", self._span_processor_mode)
|
|
235
243
|
|
|
236
244
|
logger.debug(
|
|
237
245
|
"[paid:span] on_start: name=%s, customer_id=%s, agent_id=%s",
|
|
@@ -419,7 +427,8 @@ def initialize_tracing(
|
|
|
419
427
|
Args:
|
|
420
428
|
api_key: The API key for authentication. If not provided, will try to get from PAID_API_KEY environment variable.
|
|
421
429
|
collector_endpoint: The OTLP collector endpoint URL.
|
|
422
|
-
processor_settings: Optional configuration for span processors
|
|
430
|
+
processor_settings: Optional configuration for span processors. Pydantic settings here are deprecated;
|
|
431
|
+
use get_paid_tracer_pydantic instead.
|
|
423
432
|
|
|
424
433
|
Example:
|
|
425
434
|
# Basic initialization
|
|
@@ -479,8 +488,10 @@ def initialize_tracing(
|
|
|
479
488
|
paid_tracer_provider._disabled = False
|
|
480
489
|
logger.debug("[paid:init] TracerProvider created (sampler=ALWAYS_ON, OTEL_SDK_DISABLED overridden)")
|
|
481
490
|
|
|
491
|
+
processor_settings = processor_settings or ProcessorSettings()
|
|
492
|
+
|
|
482
493
|
# Add span processor to prefix span names and add customer/agent ID attributes
|
|
483
|
-
paid_tracer_provider.add_span_processor(PaidSpanProcessor())
|
|
494
|
+
paid_tracer_provider.add_span_processor(PaidSpanProcessor(span_processor_mode=processor_settings.export_mode))
|
|
484
495
|
logger.debug("[paid:init] Added PaidSpanProcessor")
|
|
485
496
|
|
|
486
497
|
# Add Pydantic span processor - it self-filters by scope using the settings registry
|
|
@@ -500,12 +511,31 @@ def initialize_tracing(
|
|
|
500
511
|
timeout=10, # Explicit timeout to prevent env var OTEL_EXPORTER_OTLP_TIMEOUT override
|
|
501
512
|
)
|
|
502
513
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
514
|
+
span_processor: SpanProcessor
|
|
515
|
+
|
|
516
|
+
if processor_settings.export_mode == "batch":
|
|
517
|
+
# BatchSpanProcessor exports from a worker thread so collector outages do not
|
|
518
|
+
# block user logic on span end.
|
|
519
|
+
span_processor = BatchSpanProcessor(
|
|
520
|
+
otlp_exporter,
|
|
521
|
+
schedule_delay_millis=500,
|
|
522
|
+
max_export_batch_size=64,
|
|
523
|
+
max_queue_size=2048,
|
|
524
|
+
export_timeout_millis=10_000,
|
|
525
|
+
)
|
|
526
|
+
logger.debug("[paid:init] Using BatchSpanProcessor for non-blocking span export")
|
|
527
|
+
else:
|
|
528
|
+
# SimpleSpanProcessor exports inline. Keep it as an opt-in for short-lived
|
|
529
|
+
# environments where losing spans on process exit is worse than blocking.
|
|
530
|
+
span_processor = SimpleSpanProcessor(otlp_exporter)
|
|
531
|
+
logger.debug("[paid:init] Using SimpleSpanProcessor for immediate span export")
|
|
532
|
+
|
|
507
533
|
paid_tracer_provider.add_span_processor(span_processor)
|
|
508
|
-
logger.debug(
|
|
534
|
+
logger.debug(
|
|
535
|
+
"[paid:init] Added %s with OTLPSpanExporter -> %s",
|
|
536
|
+
type(span_processor).__name__,
|
|
537
|
+
collector_endpoint,
|
|
538
|
+
)
|
|
509
539
|
|
|
510
540
|
setup_graceful_termination(paid_tracer_provider) # doesn't throw
|
|
511
541
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paid_python-1.2.0 → paid_python-1.3.0}/src/paid/contacts/types/create_contact_request_roles_item.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/anthropic/anthropicWrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/langchain/paidLangChainCallback.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paid_python-1.2.0 → paid_python-1.3.0}/src/paid/tracing/wrappers/openai_agents/openaiAgentsHook.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paid_python-1.2.0 → paid_python-1.3.0}/src/paid/types/create_order_line_attribute_request.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|