openai-agents 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of openai-agents might be problematic. Click here for more details.
- agents/__init__.py +223 -0
- agents/_config.py +23 -0
- agents/_debug.py +17 -0
- agents/_run_impl.py +792 -0
- agents/_utils.py +61 -0
- agents/agent.py +159 -0
- agents/agent_output.py +144 -0
- agents/computer.py +107 -0
- agents/exceptions.py +63 -0
- agents/extensions/handoff_filters.py +67 -0
- agents/extensions/handoff_prompt.py +19 -0
- agents/function_schema.py +340 -0
- agents/guardrail.py +320 -0
- agents/handoffs.py +236 -0
- agents/items.py +246 -0
- agents/lifecycle.py +105 -0
- agents/logger.py +3 -0
- agents/model_settings.py +35 -0
- agents/models/__init__.py +0 -0
- agents/models/_openai_shared.py +34 -0
- agents/models/fake_id.py +5 -0
- agents/models/interface.py +107 -0
- agents/models/openai_chatcompletions.py +952 -0
- agents/models/openai_provider.py +65 -0
- agents/models/openai_responses.py +384 -0
- agents/result.py +220 -0
- agents/run.py +904 -0
- agents/run_context.py +26 -0
- agents/stream_events.py +58 -0
- agents/strict_schema.py +167 -0
- agents/tool.py +286 -0
- agents/tracing/__init__.py +97 -0
- agents/tracing/create.py +306 -0
- agents/tracing/logger.py +3 -0
- agents/tracing/processor_interface.py +69 -0
- agents/tracing/processors.py +261 -0
- agents/tracing/scope.py +45 -0
- agents/tracing/setup.py +211 -0
- agents/tracing/span_data.py +188 -0
- agents/tracing/spans.py +264 -0
- agents/tracing/traces.py +195 -0
- agents/tracing/util.py +17 -0
- agents/usage.py +22 -0
- agents/version.py +7 -0
- openai_agents-0.0.2.dist-info/METADATA +202 -0
- openai_agents-0.0.2.dist-info/RECORD +49 -0
- openai_agents-0.0.2.dist-info/licenses/LICENSE +21 -0
- openai-agents/example.py +0 -2
- openai_agents-0.0.1.dist-info/METADATA +0 -17
- openai_agents-0.0.1.dist-info/RECORD +0 -6
- openai_agents-0.0.1.dist-info/licenses/LICENSE +0 -20
- {openai-agents → agents/extensions}/__init__.py +0 -0
- {openai_agents-0.0.1.dist-info → openai_agents-0.0.2.dist-info}/WHEEL +0 -0
agents/tracing/setup.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import threading
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from . import util
|
|
8
|
+
from .logger import logger
|
|
9
|
+
from .processor_interface import TracingProcessor
|
|
10
|
+
from .scope import Scope
|
|
11
|
+
from .spans import NoOpSpan, Span, SpanImpl, TSpanData
|
|
12
|
+
from .traces import NoOpTrace, Trace, TraceImpl
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SynchronousMultiTracingProcessor(TracingProcessor):
|
|
16
|
+
"""
|
|
17
|
+
Forwards all calls to a list of TracingProcessors, in order of registration.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
# Using a tuple to avoid race conditions when iterating over processors
|
|
22
|
+
self._processors: tuple[TracingProcessor, ...] = ()
|
|
23
|
+
self._lock = threading.Lock()
|
|
24
|
+
|
|
25
|
+
def add_tracing_processor(self, tracing_processor: TracingProcessor):
|
|
26
|
+
"""
|
|
27
|
+
Add a processor to the list of processors. Each processor will receive all traces/spans.
|
|
28
|
+
"""
|
|
29
|
+
with self._lock:
|
|
30
|
+
self._processors += (tracing_processor,)
|
|
31
|
+
|
|
32
|
+
def set_processors(self, processors: list[TracingProcessor]):
|
|
33
|
+
"""
|
|
34
|
+
Set the list of processors. This will replace the current list of processors.
|
|
35
|
+
"""
|
|
36
|
+
with self._lock:
|
|
37
|
+
self._processors = tuple(processors)
|
|
38
|
+
|
|
39
|
+
def on_trace_start(self, trace: Trace) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Called when a trace is started.
|
|
42
|
+
"""
|
|
43
|
+
for processor in self._processors:
|
|
44
|
+
processor.on_trace_start(trace)
|
|
45
|
+
|
|
46
|
+
def on_trace_end(self, trace: Trace) -> None:
|
|
47
|
+
"""
|
|
48
|
+
Called when a trace is finished.
|
|
49
|
+
"""
|
|
50
|
+
for processor in self._processors:
|
|
51
|
+
processor.on_trace_end(trace)
|
|
52
|
+
|
|
53
|
+
def on_span_start(self, span: Span[Any]) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Called when a span is started.
|
|
56
|
+
"""
|
|
57
|
+
for processor in self._processors:
|
|
58
|
+
processor.on_span_start(span)
|
|
59
|
+
|
|
60
|
+
def on_span_end(self, span: Span[Any]) -> None:
|
|
61
|
+
"""
|
|
62
|
+
Called when a span is finished.
|
|
63
|
+
"""
|
|
64
|
+
for processor in self._processors:
|
|
65
|
+
processor.on_span_end(span)
|
|
66
|
+
|
|
67
|
+
def shutdown(self) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Called when the application stops.
|
|
70
|
+
"""
|
|
71
|
+
for processor in self._processors:
|
|
72
|
+
logger.debug(f"Shutting down trace processor {processor}")
|
|
73
|
+
processor.shutdown()
|
|
74
|
+
|
|
75
|
+
def force_flush(self):
|
|
76
|
+
"""
|
|
77
|
+
Force the processors to flush their buffers.
|
|
78
|
+
"""
|
|
79
|
+
for processor in self._processors:
|
|
80
|
+
processor.force_flush()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TraceProvider:
|
|
84
|
+
def __init__(self):
|
|
85
|
+
self._multi_processor = SynchronousMultiTracingProcessor()
|
|
86
|
+
self._disabled = os.environ.get("OPENAI_AGENTS_DISABLE_TRACING", "false").lower() in (
|
|
87
|
+
"true",
|
|
88
|
+
"1",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def register_processor(self, processor: TracingProcessor):
|
|
92
|
+
"""
|
|
93
|
+
Add a processor to the list of processors. Each processor will receive all traces/spans.
|
|
94
|
+
"""
|
|
95
|
+
self._multi_processor.add_tracing_processor(processor)
|
|
96
|
+
|
|
97
|
+
def set_processors(self, processors: list[TracingProcessor]):
|
|
98
|
+
"""
|
|
99
|
+
Set the list of processors. This will replace the current list of processors.
|
|
100
|
+
"""
|
|
101
|
+
self._multi_processor.set_processors(processors)
|
|
102
|
+
|
|
103
|
+
def get_current_trace(self) -> Trace | None:
|
|
104
|
+
"""
|
|
105
|
+
Returns the currently active trace, if any.
|
|
106
|
+
"""
|
|
107
|
+
return Scope.get_current_trace()
|
|
108
|
+
|
|
109
|
+
def get_current_span(self) -> Span[Any] | None:
|
|
110
|
+
"""
|
|
111
|
+
Returns the currently active span, if any.
|
|
112
|
+
"""
|
|
113
|
+
return Scope.get_current_span()
|
|
114
|
+
|
|
115
|
+
def set_disabled(self, disabled: bool) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Set whether tracing is disabled.
|
|
118
|
+
"""
|
|
119
|
+
self._disabled = disabled
|
|
120
|
+
|
|
121
|
+
def create_trace(
|
|
122
|
+
self,
|
|
123
|
+
name: str,
|
|
124
|
+
trace_id: str | None = None,
|
|
125
|
+
group_id: str | None = None,
|
|
126
|
+
metadata: dict[str, Any] | None = None,
|
|
127
|
+
disabled: bool = False,
|
|
128
|
+
) -> Trace:
|
|
129
|
+
"""
|
|
130
|
+
Create a new trace.
|
|
131
|
+
"""
|
|
132
|
+
if self._disabled or disabled:
|
|
133
|
+
logger.debug(f"Tracing is disabled. Not creating trace {name}")
|
|
134
|
+
return NoOpTrace()
|
|
135
|
+
|
|
136
|
+
trace_id = trace_id or util.gen_trace_id()
|
|
137
|
+
|
|
138
|
+
logger.debug(f"Creating trace {name} with id {trace_id}")
|
|
139
|
+
|
|
140
|
+
return TraceImpl(
|
|
141
|
+
name=name,
|
|
142
|
+
trace_id=trace_id,
|
|
143
|
+
group_id=group_id,
|
|
144
|
+
metadata=metadata,
|
|
145
|
+
processor=self._multi_processor,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
def create_span(
|
|
149
|
+
self,
|
|
150
|
+
span_data: TSpanData,
|
|
151
|
+
span_id: str | None = None,
|
|
152
|
+
parent: Trace | Span[Any] | None = None,
|
|
153
|
+
disabled: bool = False,
|
|
154
|
+
) -> Span[TSpanData]:
|
|
155
|
+
"""
|
|
156
|
+
Create a new span.
|
|
157
|
+
"""
|
|
158
|
+
if self._disabled or disabled:
|
|
159
|
+
logger.debug(f"Tracing is disabled. Not creating span {span_data}")
|
|
160
|
+
return NoOpSpan(span_data)
|
|
161
|
+
|
|
162
|
+
if not parent:
|
|
163
|
+
current_span = Scope.get_current_span()
|
|
164
|
+
current_trace = Scope.get_current_trace()
|
|
165
|
+
if current_trace is None:
|
|
166
|
+
logger.error(
|
|
167
|
+
"No active trace. Make sure to start a trace with `trace()` first"
|
|
168
|
+
"Returning NoOpSpan."
|
|
169
|
+
)
|
|
170
|
+
return NoOpSpan(span_data)
|
|
171
|
+
elif isinstance(current_trace, NoOpTrace) or isinstance(current_span, NoOpSpan):
|
|
172
|
+
logger.debug(
|
|
173
|
+
f"Parent {current_span} or {current_trace} is no-op, returning NoOpSpan"
|
|
174
|
+
)
|
|
175
|
+
return NoOpSpan(span_data)
|
|
176
|
+
|
|
177
|
+
parent_id = current_span.span_id if current_span else None
|
|
178
|
+
trace_id = current_trace.trace_id
|
|
179
|
+
|
|
180
|
+
elif isinstance(parent, Trace):
|
|
181
|
+
if isinstance(parent, NoOpTrace):
|
|
182
|
+
logger.debug(f"Parent {parent} is no-op, returning NoOpSpan")
|
|
183
|
+
return NoOpSpan(span_data)
|
|
184
|
+
trace_id = parent.trace_id
|
|
185
|
+
parent_id = None
|
|
186
|
+
elif isinstance(parent, Span):
|
|
187
|
+
if isinstance(parent, NoOpSpan):
|
|
188
|
+
logger.debug(f"Parent {parent} is no-op, returning NoOpSpan")
|
|
189
|
+
return NoOpSpan(span_data)
|
|
190
|
+
parent_id = parent.span_id
|
|
191
|
+
trace_id = parent.trace_id
|
|
192
|
+
|
|
193
|
+
logger.debug(f"Creating span {span_data} with id {span_id}")
|
|
194
|
+
|
|
195
|
+
return SpanImpl(
|
|
196
|
+
trace_id=trace_id,
|
|
197
|
+
span_id=span_id,
|
|
198
|
+
parent_id=parent_id,
|
|
199
|
+
processor=self._multi_processor,
|
|
200
|
+
span_data=span_data,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
def shutdown(self) -> None:
|
|
204
|
+
try:
|
|
205
|
+
logger.debug("Shutting down trace provider")
|
|
206
|
+
self._multi_processor.shutdown()
|
|
207
|
+
except Exception as e:
|
|
208
|
+
logger.error(f"Error shutting down trace provider: {e}")
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
GLOBAL_TRACE_PROVIDER = TraceProvider()
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
from collections.abc import Mapping, Sequence
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from openai.types.responses import Response, ResponseInputItemParam
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SpanData(abc.ABC):
|
|
12
|
+
@abc.abstractmethod
|
|
13
|
+
def export(self) -> dict[str, Any]:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
@abc.abstractmethod
|
|
18
|
+
def type(self) -> str:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AgentSpanData(SpanData):
|
|
23
|
+
__slots__ = ("name", "handoffs", "tools", "output_type")
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
name: str,
|
|
28
|
+
handoffs: list[str] | None = None,
|
|
29
|
+
tools: list[str] | None = None,
|
|
30
|
+
output_type: str | None = None,
|
|
31
|
+
):
|
|
32
|
+
self.name = name
|
|
33
|
+
self.handoffs: list[str] | None = handoffs
|
|
34
|
+
self.tools: list[str] | None = tools
|
|
35
|
+
self.output_type: str | None = output_type
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def type(self) -> str:
|
|
39
|
+
return "agent"
|
|
40
|
+
|
|
41
|
+
def export(self) -> dict[str, Any]:
|
|
42
|
+
return {
|
|
43
|
+
"type": self.type,
|
|
44
|
+
"name": self.name,
|
|
45
|
+
"handoffs": self.handoffs,
|
|
46
|
+
"tools": self.tools,
|
|
47
|
+
"output_type": self.output_type,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class FunctionSpanData(SpanData):
|
|
52
|
+
__slots__ = ("name", "input", "output")
|
|
53
|
+
|
|
54
|
+
def __init__(self, name: str, input: str | None, output: str | None):
|
|
55
|
+
self.name = name
|
|
56
|
+
self.input = input
|
|
57
|
+
self.output = output
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def type(self) -> str:
|
|
61
|
+
return "function"
|
|
62
|
+
|
|
63
|
+
def export(self) -> dict[str, Any]:
|
|
64
|
+
return {
|
|
65
|
+
"type": self.type,
|
|
66
|
+
"name": self.name,
|
|
67
|
+
"input": self.input,
|
|
68
|
+
"output": self.output,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class GenerationSpanData(SpanData):
|
|
73
|
+
__slots__ = (
|
|
74
|
+
"input",
|
|
75
|
+
"output",
|
|
76
|
+
"model",
|
|
77
|
+
"model_config",
|
|
78
|
+
"usage",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
input: Sequence[Mapping[str, Any]] | None = None,
|
|
84
|
+
output: Sequence[Mapping[str, Any]] | None = None,
|
|
85
|
+
model: str | None = None,
|
|
86
|
+
model_config: Mapping[str, Any] | None = None,
|
|
87
|
+
usage: dict[str, Any] | None = None,
|
|
88
|
+
):
|
|
89
|
+
self.input = input
|
|
90
|
+
self.output = output
|
|
91
|
+
self.model = model
|
|
92
|
+
self.model_config = model_config
|
|
93
|
+
self.usage = usage
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def type(self) -> str:
|
|
97
|
+
return "generation"
|
|
98
|
+
|
|
99
|
+
def export(self) -> dict[str, Any]:
|
|
100
|
+
return {
|
|
101
|
+
"type": self.type,
|
|
102
|
+
"input": self.input,
|
|
103
|
+
"output": self.output,
|
|
104
|
+
"model": self.model,
|
|
105
|
+
"model_config": self.model_config,
|
|
106
|
+
"usage": self.usage,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ResponseSpanData(SpanData):
|
|
111
|
+
__slots__ = ("response", "input")
|
|
112
|
+
|
|
113
|
+
def __init__(
|
|
114
|
+
self,
|
|
115
|
+
response: Response | None = None,
|
|
116
|
+
input: str | list[ResponseInputItemParam] | None = None,
|
|
117
|
+
) -> None:
|
|
118
|
+
self.response = response
|
|
119
|
+
# This is not used by the OpenAI trace processors, but is useful for other tracing
|
|
120
|
+
# processor implementations
|
|
121
|
+
self.input = input
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def type(self) -> str:
|
|
125
|
+
return "response"
|
|
126
|
+
|
|
127
|
+
def export(self) -> dict[str, Any]:
|
|
128
|
+
return {
|
|
129
|
+
"type": self.type,
|
|
130
|
+
"response_id": self.response.id if self.response else None,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class HandoffSpanData(SpanData):
|
|
135
|
+
__slots__ = ("from_agent", "to_agent")
|
|
136
|
+
|
|
137
|
+
def __init__(self, from_agent: str | None, to_agent: str | None):
|
|
138
|
+
self.from_agent = from_agent
|
|
139
|
+
self.to_agent = to_agent
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def type(self) -> str:
|
|
143
|
+
return "handoff"
|
|
144
|
+
|
|
145
|
+
def export(self) -> dict[str, Any]:
|
|
146
|
+
return {
|
|
147
|
+
"type": self.type,
|
|
148
|
+
"from_agent": self.from_agent,
|
|
149
|
+
"to_agent": self.to_agent,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class CustomSpanData(SpanData):
|
|
154
|
+
__slots__ = ("name", "data")
|
|
155
|
+
|
|
156
|
+
def __init__(self, name: str, data: dict[str, Any]):
|
|
157
|
+
self.name = name
|
|
158
|
+
self.data = data
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def type(self) -> str:
|
|
162
|
+
return "custom"
|
|
163
|
+
|
|
164
|
+
def export(self) -> dict[str, Any]:
|
|
165
|
+
return {
|
|
166
|
+
"type": self.type,
|
|
167
|
+
"name": self.name,
|
|
168
|
+
"data": self.data,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class GuardrailSpanData(SpanData):
|
|
173
|
+
__slots__ = ("name", "triggered")
|
|
174
|
+
|
|
175
|
+
def __init__(self, name: str, triggered: bool = False):
|
|
176
|
+
self.name = name
|
|
177
|
+
self.triggered = triggered
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def type(self) -> str:
|
|
181
|
+
return "guardrail"
|
|
182
|
+
|
|
183
|
+
def export(self) -> dict[str, Any]:
|
|
184
|
+
return {
|
|
185
|
+
"type": self.type,
|
|
186
|
+
"name": self.name,
|
|
187
|
+
"triggered": self.triggered,
|
|
188
|
+
}
|
agents/tracing/spans.py
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import contextvars
|
|
5
|
+
from typing import Any, Generic, TypeVar
|
|
6
|
+
|
|
7
|
+
from typing_extensions import TypedDict
|
|
8
|
+
|
|
9
|
+
from . import util
|
|
10
|
+
from .logger import logger
|
|
11
|
+
from .processor_interface import TracingProcessor
|
|
12
|
+
from .scope import Scope
|
|
13
|
+
from .span_data import SpanData
|
|
14
|
+
|
|
15
|
+
TSpanData = TypeVar("TSpanData", bound=SpanData)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SpanError(TypedDict):
|
|
19
|
+
message: str
|
|
20
|
+
data: dict[str, Any] | None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Span(abc.ABC, Generic[TSpanData]):
|
|
24
|
+
@property
|
|
25
|
+
@abc.abstractmethod
|
|
26
|
+
def trace_id(self) -> str:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
@abc.abstractmethod
|
|
31
|
+
def span_id(self) -> str:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
@abc.abstractmethod
|
|
36
|
+
def span_data(self) -> TSpanData:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
@abc.abstractmethod
|
|
40
|
+
def start(self, mark_as_current: bool = False):
|
|
41
|
+
"""
|
|
42
|
+
Start the span.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
mark_as_current: If true, the span will be marked as the current span.
|
|
46
|
+
"""
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
@abc.abstractmethod
|
|
50
|
+
def finish(self, reset_current: bool = False) -> None:
|
|
51
|
+
"""
|
|
52
|
+
Finish the span.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
reset_current: If true, the span will be reset as the current span.
|
|
56
|
+
"""
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
@abc.abstractmethod
|
|
60
|
+
def __enter__(self) -> Span[TSpanData]:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
@abc.abstractmethod
|
|
64
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
@abc.abstractmethod
|
|
69
|
+
def parent_id(self) -> str | None:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
@abc.abstractmethod
|
|
73
|
+
def set_error(self, error: SpanError) -> None:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
@abc.abstractmethod
|
|
78
|
+
def error(self) -> SpanError | None:
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
@abc.abstractmethod
|
|
82
|
+
def export(self) -> dict[str, Any] | None:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
@abc.abstractmethod
|
|
87
|
+
def started_at(self) -> str | None:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
@abc.abstractmethod
|
|
92
|
+
def ended_at(self) -> str | None:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class NoOpSpan(Span[TSpanData]):
|
|
97
|
+
__slots__ = ("_span_data", "_prev_span_token")
|
|
98
|
+
|
|
99
|
+
def __init__(self, span_data: TSpanData):
|
|
100
|
+
self._span_data = span_data
|
|
101
|
+
self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def trace_id(self) -> str:
|
|
105
|
+
return "no-op"
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def span_id(self) -> str:
|
|
109
|
+
return "no-op"
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def span_data(self) -> TSpanData:
|
|
113
|
+
return self._span_data
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def parent_id(self) -> str | None:
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
def start(self, mark_as_current: bool = False):
|
|
120
|
+
if mark_as_current:
|
|
121
|
+
self._prev_span_token = Scope.set_current_span(self)
|
|
122
|
+
|
|
123
|
+
def finish(self, reset_current: bool = False) -> None:
|
|
124
|
+
if reset_current and self._prev_span_token is not None:
|
|
125
|
+
Scope.reset_current_span(self._prev_span_token)
|
|
126
|
+
self._prev_span_token = None
|
|
127
|
+
|
|
128
|
+
def __enter__(self) -> Span[TSpanData]:
|
|
129
|
+
self.start(mark_as_current=True)
|
|
130
|
+
return self
|
|
131
|
+
|
|
132
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
133
|
+
reset_current = True
|
|
134
|
+
if exc_type is GeneratorExit:
|
|
135
|
+
logger.debug("GeneratorExit, skipping span reset")
|
|
136
|
+
reset_current = False
|
|
137
|
+
|
|
138
|
+
self.finish(reset_current=reset_current)
|
|
139
|
+
|
|
140
|
+
def set_error(self, error: SpanError) -> None:
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def error(self) -> SpanError | None:
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
def export(self) -> dict[str, Any] | None:
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def started_at(self) -> str | None:
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def ended_at(self) -> str | None:
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class SpanImpl(Span[TSpanData]):
|
|
160
|
+
__slots__ = (
|
|
161
|
+
"_trace_id",
|
|
162
|
+
"_span_id",
|
|
163
|
+
"_parent_id",
|
|
164
|
+
"_started_at",
|
|
165
|
+
"_ended_at",
|
|
166
|
+
"_error",
|
|
167
|
+
"_prev_span_token",
|
|
168
|
+
"_processor",
|
|
169
|
+
"_span_data",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def __init__(
|
|
173
|
+
self,
|
|
174
|
+
trace_id: str,
|
|
175
|
+
span_id: str | None,
|
|
176
|
+
parent_id: str | None,
|
|
177
|
+
processor: TracingProcessor,
|
|
178
|
+
span_data: TSpanData,
|
|
179
|
+
):
|
|
180
|
+
self._trace_id = trace_id
|
|
181
|
+
self._span_id = span_id or util.gen_span_id()
|
|
182
|
+
self._parent_id = parent_id
|
|
183
|
+
self._started_at: str | None = None
|
|
184
|
+
self._ended_at: str | None = None
|
|
185
|
+
self._processor = processor
|
|
186
|
+
self._error: SpanError | None = None
|
|
187
|
+
self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None
|
|
188
|
+
self._span_data = span_data
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def trace_id(self) -> str:
|
|
192
|
+
return self._trace_id
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def span_id(self) -> str:
|
|
196
|
+
return self._span_id
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def span_data(self) -> TSpanData:
|
|
200
|
+
return self._span_data
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def parent_id(self) -> str | None:
|
|
204
|
+
return self._parent_id
|
|
205
|
+
|
|
206
|
+
def start(self, mark_as_current: bool = False):
|
|
207
|
+
if self.started_at is not None:
|
|
208
|
+
logger.warning("Span already started")
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
self._started_at = util.time_iso()
|
|
212
|
+
self._processor.on_span_start(self)
|
|
213
|
+
if mark_as_current:
|
|
214
|
+
self._prev_span_token = Scope.set_current_span(self)
|
|
215
|
+
|
|
216
|
+
def finish(self, reset_current: bool = False) -> None:
|
|
217
|
+
if self.ended_at is not None:
|
|
218
|
+
logger.warning("Span already finished")
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
self._ended_at = util.time_iso()
|
|
222
|
+
self._processor.on_span_end(self)
|
|
223
|
+
if reset_current and self._prev_span_token is not None:
|
|
224
|
+
Scope.reset_current_span(self._prev_span_token)
|
|
225
|
+
self._prev_span_token = None
|
|
226
|
+
|
|
227
|
+
def __enter__(self) -> Span[TSpanData]:
|
|
228
|
+
self.start(mark_as_current=True)
|
|
229
|
+
return self
|
|
230
|
+
|
|
231
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
232
|
+
reset_current = True
|
|
233
|
+
if exc_type is GeneratorExit:
|
|
234
|
+
logger.debug("GeneratorExit, skipping span reset")
|
|
235
|
+
reset_current = False
|
|
236
|
+
|
|
237
|
+
self.finish(reset_current=reset_current)
|
|
238
|
+
|
|
239
|
+
def set_error(self, error: SpanError) -> None:
|
|
240
|
+
self._error = error
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def error(self) -> SpanError | None:
|
|
244
|
+
return self._error
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def started_at(self) -> str | None:
|
|
248
|
+
return self._started_at
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def ended_at(self) -> str | None:
|
|
252
|
+
return self._ended_at
|
|
253
|
+
|
|
254
|
+
def export(self) -> dict[str, Any] | None:
|
|
255
|
+
return {
|
|
256
|
+
"object": "trace.span",
|
|
257
|
+
"id": self.span_id,
|
|
258
|
+
"trace_id": self.trace_id,
|
|
259
|
+
"parent_id": self._parent_id,
|
|
260
|
+
"started_at": self._started_at,
|
|
261
|
+
"ended_at": self._ended_at,
|
|
262
|
+
"span_data": self.span_data.export(),
|
|
263
|
+
"error": self._error,
|
|
264
|
+
}
|