scale-gp-beta 0.1.0a16__py3-none-any.whl → 0.1.0a18__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.
- scale_gp_beta/_version.py +1 -1
- scale_gp_beta/lib/tracing/__init__.py +4 -0
- scale_gp_beta/lib/tracing/exceptions.py +5 -0
- scale_gp_beta/lib/tracing/scope.py +104 -0
- scale_gp_beta/lib/tracing/span.py +217 -0
- scale_gp_beta/lib/tracing/trace.py +118 -0
- scale_gp_beta/lib/tracing/trace_exporter.py +88 -0
- scale_gp_beta/lib/tracing/trace_queue_manager.py +181 -0
- scale_gp_beta/lib/tracing/tracing.py +180 -0
- scale_gp_beta/lib/tracing/types.py +40 -0
- scale_gp_beta/lib/tracing/util.py +68 -0
- scale_gp_beta/resources/spans.py +331 -1
- scale_gp_beta/types/__init__.py +4 -0
- scale_gp_beta/types/span_search_params.py +77 -0
- scale_gp_beta/types/span_search_response.py +27 -0
- scale_gp_beta/types/span_upsert_batch_params.py +78 -0
- scale_gp_beta/types/span_upsert_batch_response.py +15 -0
- {scale_gp_beta-0.1.0a16.dist-info → scale_gp_beta-0.1.0a18.dist-info}/METADATA +1 -1
- {scale_gp_beta-0.1.0a16.dist-info → scale_gp_beta-0.1.0a18.dist-info}/RECORD +21 -7
- {scale_gp_beta-0.1.0a16.dist-info → scale_gp_beta-0.1.0a18.dist-info}/WHEEL +0 -0
- {scale_gp_beta-0.1.0a16.dist-info → scale_gp_beta-0.1.0a18.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import queue
|
|
3
|
+
import atexit
|
|
4
|
+
import logging
|
|
5
|
+
import threading
|
|
6
|
+
from typing import TYPE_CHECKING, Optional
|
|
7
|
+
|
|
8
|
+
from scale_gp_beta import SGPClient, SGPClientError
|
|
9
|
+
|
|
10
|
+
from .util import configure, is_disabled
|
|
11
|
+
from .trace_exporter import TraceExporter
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from .span import Span
|
|
15
|
+
from .trace import Trace
|
|
16
|
+
|
|
17
|
+
# configurable by env vars?
|
|
18
|
+
DEFAULT_MAX_QUEUE_SIZE = 4_000
|
|
19
|
+
DEFAULT_TRIGGER_QUEUE_SIZE = 200
|
|
20
|
+
DEFAULT_TRIGGER_CADENCE = 4.0
|
|
21
|
+
DEFAULT_MAX_BATCH_SIZE = 50
|
|
22
|
+
DEFAULT_RETRIES = 4
|
|
23
|
+
|
|
24
|
+
WORKER_SLEEP_SECONDS = 0.1
|
|
25
|
+
|
|
26
|
+
log: logging.Logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TraceQueueManager:
|
|
30
|
+
"""Manage trace and spans queue
|
|
31
|
+
Store spans in-memory until the threshold has been reached then flush to server.
|
|
32
|
+
|
|
33
|
+
Optionally provide a client, if unprovided we will attempt to create a default client.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
client: Optional[SGPClient] = None,
|
|
39
|
+
max_queue_size: int = DEFAULT_MAX_QUEUE_SIZE,
|
|
40
|
+
max_batch_size: int = DEFAULT_MAX_BATCH_SIZE,
|
|
41
|
+
trigger_queue_size: int = DEFAULT_TRIGGER_QUEUE_SIZE,
|
|
42
|
+
trigger_cadence: float = DEFAULT_TRIGGER_CADENCE,
|
|
43
|
+
retries: int = DEFAULT_RETRIES,
|
|
44
|
+
):
|
|
45
|
+
self._client = client
|
|
46
|
+
self._attempted_local_client_creation = False
|
|
47
|
+
self._trigger_queue_size = trigger_queue_size
|
|
48
|
+
self._trigger_cadence = trigger_cadence
|
|
49
|
+
|
|
50
|
+
self._reset_trigger_time()
|
|
51
|
+
|
|
52
|
+
self._exporter = TraceExporter(max_batch_size, retries)
|
|
53
|
+
|
|
54
|
+
self._shutdown_event = threading.Event()
|
|
55
|
+
self._queue: queue.Queue[Span] = queue.Queue(maxsize=max_queue_size)
|
|
56
|
+
|
|
57
|
+
if not is_disabled():
|
|
58
|
+
self._worker = threading.Thread(daemon=True, target=self._run)
|
|
59
|
+
self._worker.start()
|
|
60
|
+
|
|
61
|
+
# ensure the thread joins on exit
|
|
62
|
+
atexit.register(self.shutdown)
|
|
63
|
+
|
|
64
|
+
def register_client(self, client: SGPClient) -> None:
|
|
65
|
+
log.info("Registering client")
|
|
66
|
+
self._client = client
|
|
67
|
+
|
|
68
|
+
def shutdown(self, timeout: Optional[float] = None) -> None:
|
|
69
|
+
if is_disabled():
|
|
70
|
+
log.debug("No worker to shutdown")
|
|
71
|
+
return
|
|
72
|
+
log.info(f"Shutting down trace queue manager, joining worker thread with timeout {timeout}")
|
|
73
|
+
self._shutdown_event.set()
|
|
74
|
+
self._worker.join(timeout=timeout)
|
|
75
|
+
log.info("Shutdown complete")
|
|
76
|
+
|
|
77
|
+
def report_span_start(self, span: "Span") -> None:
|
|
78
|
+
# TODO: support making this optional. Current backend requires us to send span starts
|
|
79
|
+
try:
|
|
80
|
+
self._queue.put_nowait(span)
|
|
81
|
+
except queue.Full:
|
|
82
|
+
log.warning(f"Queue full, ignoring span {span.span_id}")
|
|
83
|
+
|
|
84
|
+
def report_span_end(self, span: "Span") -> None:
|
|
85
|
+
try:
|
|
86
|
+
self._queue.put_nowait(span)
|
|
87
|
+
except queue.Full:
|
|
88
|
+
log.warning(f"Queue full, ignoring span {span.span_id}")
|
|
89
|
+
|
|
90
|
+
def report_trace_start(self, trace: "Trace") -> None:
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
def report_trace_end(self, trace: "Trace") -> None:
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
def flush_queue(self) -> None:
|
|
97
|
+
self._export()
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def client(self) -> Optional[SGPClient]:
|
|
101
|
+
"""
|
|
102
|
+
Use client provided client on init if available, otherwise attempt once to create a default one.
|
|
103
|
+
:return: SGPClient
|
|
104
|
+
"""
|
|
105
|
+
if self._client is not None:
|
|
106
|
+
return self._client
|
|
107
|
+
if self._attempted_local_client_creation:
|
|
108
|
+
# Already tried and failed to create a client
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
log.info("Tracing queue manager not initialized, attempting to create a default one")
|
|
112
|
+
try:
|
|
113
|
+
self.register_client(SGPClient())
|
|
114
|
+
except SGPClientError:
|
|
115
|
+
log.warning(
|
|
116
|
+
f"Failed to create SGPClient for tracing queue manager {self}, ignoring traces. Please initialize with a working client."
|
|
117
|
+
)
|
|
118
|
+
finally:
|
|
119
|
+
self._attempted_local_client_creation = True
|
|
120
|
+
return self._client
|
|
121
|
+
|
|
122
|
+
def _run(self) -> None:
|
|
123
|
+
# daemon worker loop
|
|
124
|
+
while not self._shutdown_event.is_set():
|
|
125
|
+
current_time = time.time()
|
|
126
|
+
queue_size = self._queue.qsize()
|
|
127
|
+
|
|
128
|
+
if queue_size >= self._trigger_queue_size or current_time >= self._next_trigger_time:
|
|
129
|
+
self._export()
|
|
130
|
+
self._reset_trigger_time()
|
|
131
|
+
continue
|
|
132
|
+
|
|
133
|
+
time.sleep(WORKER_SLEEP_SECONDS)
|
|
134
|
+
|
|
135
|
+
# flush all on shutdown
|
|
136
|
+
self._export()
|
|
137
|
+
|
|
138
|
+
def _export(self) -> None:
|
|
139
|
+
if self.client:
|
|
140
|
+
self._exporter.export(self.client, self._queue)
|
|
141
|
+
|
|
142
|
+
def _reset_trigger_time(self) -> None:
|
|
143
|
+
self._next_trigger_time = time.time() + self._trigger_cadence
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
_global_tracing_queue_manager: Optional[TraceQueueManager] = None
|
|
147
|
+
_queue_manager_lock = threading.Lock()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def init(client: Optional[SGPClient] = None, disabled: Optional[bool] = None) -> None:
|
|
151
|
+
"""Initialize the tracing backend
|
|
152
|
+
|
|
153
|
+
Good practice to always include this method call with a valid client at your program entrypoint.
|
|
154
|
+
|
|
155
|
+
Tracing will attempt to generate a default client if unprovided.
|
|
156
|
+
|
|
157
|
+
:param client: SGPClient
|
|
158
|
+
:param disabled: Set to True to disable tracing. Overrides environment variable ``DISABLE_SCALE_TRACING``
|
|
159
|
+
"""
|
|
160
|
+
if disabled is not None:
|
|
161
|
+
configure(disabled=disabled)
|
|
162
|
+
|
|
163
|
+
global _global_tracing_queue_manager
|
|
164
|
+
if _global_tracing_queue_manager is not None:
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
with _queue_manager_lock:
|
|
168
|
+
if _global_tracing_queue_manager is None:
|
|
169
|
+
_global_tracing_queue_manager = TraceQueueManager(client)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def tracing_queue_manager() -> TraceQueueManager:
|
|
173
|
+
global _global_tracing_queue_manager
|
|
174
|
+
if _global_tracing_queue_manager is None:
|
|
175
|
+
init(None)
|
|
176
|
+
|
|
177
|
+
if _global_tracing_queue_manager is None:
|
|
178
|
+
# should never happen... useful for linting
|
|
179
|
+
raise RuntimeError("Tracing queue manager failed to initialize.")
|
|
180
|
+
|
|
181
|
+
return _global_tracing_queue_manager
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, Union, Optional
|
|
3
|
+
|
|
4
|
+
from .span import Span, BaseSpan, NoOpSpan
|
|
5
|
+
from .util import is_disabled
|
|
6
|
+
from .scope import Scope
|
|
7
|
+
from .trace import Trace, BaseTrace, NoOpTrace
|
|
8
|
+
from .trace_queue_manager import tracing_queue_manager
|
|
9
|
+
|
|
10
|
+
log: logging.Logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def current_span() -> Optional[BaseSpan]:
|
|
14
|
+
"""Retrieves the currently active span from the execution context.
|
|
15
|
+
|
|
16
|
+
This function relies on `contextvars` to manage the active span in
|
|
17
|
+
a context-local manner, making it safe for concurrent execution
|
|
18
|
+
environments (e.g., threads, asyncio tasks).
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Optional[BaseSpan]: The current BaseSpan instance if one is active,
|
|
22
|
+
otherwise None. This could be a 'Span' or 'NoOpSpan'.
|
|
23
|
+
"""
|
|
24
|
+
return Scope.get_current_span()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def current_trace() -> Optional[BaseTrace]:
|
|
28
|
+
"""Retrieves the currently active trace from the execution context.
|
|
29
|
+
|
|
30
|
+
Similarly, to `current_span()`, this uses `contextvars` for context-local
|
|
31
|
+
trace management.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Optional[BaseTrace]: The current BaseTrace instance if one is active,
|
|
35
|
+
otherwise None. This could be a 'Trace' or 'NoOpTrace'.
|
|
36
|
+
"""
|
|
37
|
+
return Scope.get_current_trace()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def flush_queue() -> None:
|
|
41
|
+
"""
|
|
42
|
+
Blocking flush of all requests in the queue.
|
|
43
|
+
|
|
44
|
+
Useful for distributed applications to ensure spans have been committed before continuing.
|
|
45
|
+
:return:
|
|
46
|
+
"""
|
|
47
|
+
queue_manager = tracing_queue_manager()
|
|
48
|
+
queue_manager.flush_queue()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def create_trace(
|
|
52
|
+
name: str,
|
|
53
|
+
trace_id: Optional[str] = None,
|
|
54
|
+
root_span_id: Optional[str] = None,
|
|
55
|
+
metadata: Optional[Dict[str, Optional[str]]] = None,
|
|
56
|
+
) -> BaseTrace:
|
|
57
|
+
"""Creates a new trace and root span instance.
|
|
58
|
+
|
|
59
|
+
A trace represents a single, logical operation or workflow. It groups multiple
|
|
60
|
+
spans together. If tracing is disabled (via the 'DISABLE_SCALE_TRACING'
|
|
61
|
+
environment variable), a `NoOpTrace` instance is returned which performs no
|
|
62
|
+
actual tracing operations.
|
|
63
|
+
|
|
64
|
+
When a trace is started (e.g., by using it as a context manager or calling its
|
|
65
|
+
`start()` method), it becomes the `current_trace()` in the active scope.
|
|
66
|
+
Similarly, the root span instance becomes the `current_span()` in the active
|
|
67
|
+
scope.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
name: The name of the trace.
|
|
71
|
+
trace_id (Optional[str]): An optional, user-defined ID for the trace.
|
|
72
|
+
If None, a unique trace ID will be generated.
|
|
73
|
+
root_span_id (Optional[str]): An optional, user-defined ID for the root span.
|
|
74
|
+
metadata (Optional[Dict[str, Optional[str]]]): An optional, user-defined metadata.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
BaseTrace: A `Trace` instance if tracing is enabled, or a `NoOpTrace`
|
|
78
|
+
instance if tracing is disabled.
|
|
79
|
+
"""
|
|
80
|
+
if is_disabled():
|
|
81
|
+
log.debug(f"Tracing is disabled. Not creating a new trace.")
|
|
82
|
+
return NoOpTrace(name=name, trace_id=trace_id, root_span_id=root_span_id, metadata=metadata)
|
|
83
|
+
|
|
84
|
+
active_trace = current_trace()
|
|
85
|
+
if active_trace is not None:
|
|
86
|
+
log.warning(f"Trace with id {active_trace.trace_id} is already active. Creating a new trace anyways.")
|
|
87
|
+
|
|
88
|
+
trace = Trace(name=name, trace_id=trace_id, queue_manager=tracing_queue_manager(), metadata=metadata)
|
|
89
|
+
log.debug(f"Created new trace: {trace.trace_id}")
|
|
90
|
+
|
|
91
|
+
return trace
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def create_span(
|
|
95
|
+
name: str,
|
|
96
|
+
span_id: Optional[str] = None,
|
|
97
|
+
input: Optional[Dict[str, Any]] = None,
|
|
98
|
+
output: Optional[Dict[str, Any]] = None,
|
|
99
|
+
metadata: Optional[Dict[str, Optional[str]]] = None,
|
|
100
|
+
trace: Optional[Union[BaseTrace, str]] = None,
|
|
101
|
+
parent_span: Optional[BaseSpan] = None,
|
|
102
|
+
) -> BaseSpan:
|
|
103
|
+
"""Creates a new span instance.
|
|
104
|
+
|
|
105
|
+
A span represents a single unit of work or operation within a trace. Spans
|
|
106
|
+
can be nested to form a hierarchy.
|
|
107
|
+
|
|
108
|
+
If tracing is disabled (via 'DISABLE_SCALE_TRACING' environment variable),
|
|
109
|
+
a `NoOpSpan` is returned. Additionally, if no explicit `parent` (Trace or Span)
|
|
110
|
+
is provided and there is no `current_trace()` active in the scope, a `NoOpSpan`
|
|
111
|
+
will also be returned to prevent orphaned spans.
|
|
112
|
+
|
|
113
|
+
When a span is started (e.g., via context manager or `start()`), it becomes
|
|
114
|
+
the `current_span()` in the active scope.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
name (str): A descriptive name for the span (e.g., "database_query",
|
|
118
|
+
"http_request").
|
|
119
|
+
span_id (Optional[str]): An optional, user-defined ID for the span.
|
|
120
|
+
input (Optional[dict[str, Any]], optional): A dictionary containing
|
|
121
|
+
input data or parameters relevant to this span's operation. Defaults to None.
|
|
122
|
+
output (Optional[dict[str, Any]], optional): A dictionary containing
|
|
123
|
+
output data or results from this span's operation. Defaults to None.
|
|
124
|
+
metadata (Optional[dict[str, Union[str, int, float, bool, None]]], optional):
|
|
125
|
+
A dictionary for arbitrary key-value pairs providing additional
|
|
126
|
+
context or annotations for the span. Values should be simple types.
|
|
127
|
+
Defaults to None.
|
|
128
|
+
trace (Optional[Union[BaseTrace, str]], optional): A `Trace` instance
|
|
129
|
+
or a trace ID. Used for explicit control. Default to trace fetched
|
|
130
|
+
from the active scope.
|
|
131
|
+
parent_span (Optional[BaseSpan], optional): A `Span` instance. Like
|
|
132
|
+
trace, used for explicit control. Defaults to span fetched from the
|
|
133
|
+
active scope.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
BaseSpan: A `Span` instance if tracing is enabled and a valid trace context
|
|
137
|
+
exists, or a `NoOpSpan` otherwise.
|
|
138
|
+
"""
|
|
139
|
+
queue_manager = tracing_queue_manager()
|
|
140
|
+
parent_span_id: Optional[str] = None
|
|
141
|
+
|
|
142
|
+
if parent_span is not None:
|
|
143
|
+
trace_id = parent_span.trace_id
|
|
144
|
+
parent_span_id = parent_span.span_id
|
|
145
|
+
elif trace is not None:
|
|
146
|
+
trace_id = trace if isinstance(trace, str) else trace.trace_id
|
|
147
|
+
|
|
148
|
+
parent_span = Scope.get_current_span()
|
|
149
|
+
parent_span_id = parent_span.span_id if parent_span else None
|
|
150
|
+
else:
|
|
151
|
+
trace = Scope.get_current_trace()
|
|
152
|
+
parent_span = Scope.get_current_span()
|
|
153
|
+
|
|
154
|
+
parent_span_id = parent_span.span_id if parent_span else None
|
|
155
|
+
|
|
156
|
+
if trace is None:
|
|
157
|
+
# need to think about default behavior here... do we create a trace, some other options?
|
|
158
|
+
# I am leaning towards setting it as an option as sometimes people might want to be succinct or when we
|
|
159
|
+
# build decorators we might want this functionality
|
|
160
|
+
log.debug(f"attempting to create a span with no trace")
|
|
161
|
+
return NoOpSpan(name=name, span_id=span_id, parent_span_id=parent_span_id)
|
|
162
|
+
|
|
163
|
+
trace_id = trace.trace_id
|
|
164
|
+
|
|
165
|
+
if is_disabled():
|
|
166
|
+
return NoOpSpan(name=name, span_id=span_id, parent_span_id=parent_span_id, trace_id=trace_id)
|
|
167
|
+
|
|
168
|
+
span = Span(
|
|
169
|
+
name=name,
|
|
170
|
+
span_id=span_id,
|
|
171
|
+
parent_span_id=parent_span_id,
|
|
172
|
+
trace_id=trace_id,
|
|
173
|
+
input=input or {},
|
|
174
|
+
output=output or {},
|
|
175
|
+
metadata=metadata or {},
|
|
176
|
+
queue_manager=queue_manager,
|
|
177
|
+
)
|
|
178
|
+
log.debug(f"Created new span: {span.span_id}")
|
|
179
|
+
|
|
180
|
+
return span
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This is necessary, unfortunately. Stainless does not provide these as enums, only as type annotations.
|
|
3
|
+
|
|
4
|
+
For strict linting, we need to reference these enums.
|
|
5
|
+
|
|
6
|
+
NOTE: These will have to be manually updated to support updated span_types and status.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing_extensions import Literal
|
|
10
|
+
|
|
11
|
+
SpanStatusLiterals = Literal["SUCCESS", "ERROR"]
|
|
12
|
+
|
|
13
|
+
SpanTypeLiterals = Literal[
|
|
14
|
+
"TEXT_INPUT",
|
|
15
|
+
"TEXT_OUTPUT",
|
|
16
|
+
"COMPLETION_INPUT",
|
|
17
|
+
"COMPLETION",
|
|
18
|
+
"KB_RETRIEVAL",
|
|
19
|
+
"KB_INPUT",
|
|
20
|
+
"RERANKING",
|
|
21
|
+
"EXTERNAL_ENDPOINT",
|
|
22
|
+
"PROMPT_ENGINEERING",
|
|
23
|
+
"DOCUMENT_INPUT",
|
|
24
|
+
"MAP_REDUCE",
|
|
25
|
+
"DOCUMENT_SEARCH",
|
|
26
|
+
"DOCUMENT_PROMPT",
|
|
27
|
+
"CUSTOM",
|
|
28
|
+
"INPUT_GUARDRAIL",
|
|
29
|
+
"OUTPUT_GUARDRAIL",
|
|
30
|
+
"CODE_EXECUTION",
|
|
31
|
+
"DATA_MANIPULATION",
|
|
32
|
+
"EVALUATION",
|
|
33
|
+
"FILE_RETRIEVAL",
|
|
34
|
+
"KB_ADD_CHUNK",
|
|
35
|
+
"KB_MANAGEMENT",
|
|
36
|
+
"TRACER",
|
|
37
|
+
"AGENT_TRACER",
|
|
38
|
+
"AGENT_WORKFLOW",
|
|
39
|
+
"STANDALONE",
|
|
40
|
+
]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import uuid
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from functools import cache
|
|
6
|
+
|
|
7
|
+
_tracing_disabled: Optional[bool] = None
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def generate_trace_id() -> str:
|
|
11
|
+
"""
|
|
12
|
+
Generate a unique trace ID.
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
str: A unique trace ID.
|
|
16
|
+
"""
|
|
17
|
+
return str(uuid.uuid4())
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def generate_span_id() -> str:
|
|
21
|
+
"""
|
|
22
|
+
Generate a unique span ID.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
str: A unique span ID.
|
|
26
|
+
"""
|
|
27
|
+
return str(uuid.uuid4())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def iso_timestamp() -> str:
|
|
31
|
+
"""
|
|
32
|
+
Generate an ISO 8601 timestamp.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
str: The current time in ISO 8601 format.
|
|
36
|
+
"""
|
|
37
|
+
return datetime.now(timezone.utc).isoformat()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def configure(disabled: bool) -> None:
|
|
41
|
+
"""
|
|
42
|
+
Programmatically enable or disable tracing.
|
|
43
|
+
|
|
44
|
+
This setting takes precedence over the `DISABLE_SCALE_TRACING` environment
|
|
45
|
+
variable.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
disabled (bool): Set to True to disable tracing, False to enable.
|
|
49
|
+
"""
|
|
50
|
+
global _tracing_disabled
|
|
51
|
+
_tracing_disabled = disabled
|
|
52
|
+
is_disabled.cache_clear()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@cache
|
|
56
|
+
def is_disabled() -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Check if tracing is disabled, with programmatic control taking precedence.
|
|
59
|
+
|
|
60
|
+
Tracing is considered disabled if `configure(disabled=True)` has been called,
|
|
61
|
+
or if the `DISABLE_SCALE_TRACING` environment variable is set.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
bool: True if tracing is disabled, otherwise False.
|
|
65
|
+
"""
|
|
66
|
+
if _tracing_disabled is not None:
|
|
67
|
+
return _tracing_disabled
|
|
68
|
+
return os.getenv("DISABLE_SCALE_TRACING") is not None
|