scale-gp-beta 0.1.0a21__py3-none-any.whl → 0.1.0a22__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-0.1.0a21.dist-info → scale_gp_beta-0.1.0a22.dist-info}/METADATA +239 -1
- {scale_gp_beta-0.1.0a21.dist-info → scale_gp_beta-0.1.0a22.dist-info}/RECORD +5 -5
- {scale_gp_beta-0.1.0a21.dist-info → scale_gp_beta-0.1.0a22.dist-info}/WHEEL +0 -0
- {scale_gp_beta-0.1.0a21.dist-info → scale_gp_beta-0.1.0a22.dist-info}/licenses/LICENSE +0 -0
scale_gp_beta/_version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: scale-gp-beta
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0a22
|
|
4
4
|
Summary: The official Python library for the Scale GP API
|
|
5
5
|
Project-URL: Homepage, https://github.com/scaleapi/sgp-python-beta
|
|
6
6
|
Project-URL: Repository, https://github.com/scaleapi/sgp-python-beta
|
|
@@ -77,6 +77,244 @@ we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
|
|
|
77
77
|
to add `SGP_API_KEY="My API Key"` to your `.env` file
|
|
78
78
|
so that your API Key is not stored in source control.
|
|
79
79
|
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Tracing & Spans
|
|
83
|
+
|
|
84
|
+
The SGP Tracing library provides a convenient way to instrument your Python applications with tracing capabilities, allowing you to generate, manage, and send spans to the Scale GP platform. This enables detailed monitoring and debugging of your workflows.
|
|
85
|
+
|
|
86
|
+
### Quick Start Examples
|
|
87
|
+
|
|
88
|
+
For runnable examples, see the [examples/tracing](https://github.com/scaleapi/sgp-python-beta/tree/main/examples/tracing) directory in the repository.
|
|
89
|
+
|
|
90
|
+
### Using the SDK
|
|
91
|
+
|
|
92
|
+
#### Initialization
|
|
93
|
+
|
|
94
|
+
Before you can create any traces or spans, you should initialize the tracing SDK with your `SGPClient`. It's best practice to do this once at your application's entry point. You can omit this step if you have set the `SGP_API_KEY` and `SGP_ACCOUNT_ID` environment variables, as the SDK will attempt to create a default client.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
import scale_gp_beta.lib.tracing as tracing
|
|
98
|
+
from scale_gp_beta import SGPClient
|
|
99
|
+
|
|
100
|
+
client = SGPClient(api_key="YOUR_API_KEY", account_id="YOUR_ACCOUNT_ID")
|
|
101
|
+
tracing.init(client=client)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Tracing uses the `SGPClient` for all requests. You can edit the `base_url` or any other parameters via the client you pass to `init()`.
|
|
105
|
+
|
|
106
|
+
#### Disabling Tracing
|
|
107
|
+
|
|
108
|
+
You can disable tracing by setting the environment variable `DISABLE_SCALE_TRACING` or programmatically via the `disabled` parameter in `init()`.
|
|
109
|
+
|
|
110
|
+
#### Core Concepts
|
|
111
|
+
|
|
112
|
+
The SDK revolves around two primary concepts: **Traces** and **Spans**.
|
|
113
|
+
|
|
114
|
+
* **Trace:** A trace represents a complete workflow or transaction, such as a web request or an AI agent's operation. It's a collection of related spans. Every trace has a single **root span**.
|
|
115
|
+
* **Span:** A span represents a single unit of work within a trace, like a function call, a database query, or an external API request. Spans can be nested to show hierarchical relationships.
|
|
116
|
+
|
|
117
|
+
When starting a trace, we will also create a root span. Server-side, we do not record the trace resource, only spans, but rely on the root span for trace data.
|
|
118
|
+
|
|
119
|
+
#### Creating Traces and Spans
|
|
120
|
+
|
|
121
|
+
The SDK offers flexible ways to create traces and spans: using **context managers** for automatic start/end handling, or **explicit control** for manual lifecycle management.
|
|
122
|
+
|
|
123
|
+
##### 1\. Using Context Managers (Recommended)
|
|
124
|
+
|
|
125
|
+
The most straightforward way to create traces and spans is by using them as context managers (`with` statements). This ensures that spans are automatically started and ended, and errors are captured.
|
|
126
|
+
|
|
127
|
+
**Creating a Trace with a Root Span:**
|
|
128
|
+
|
|
129
|
+
Use `tracing.create_trace()` as a context manager to define a new trace. This automatically creates a root span for your trace.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
import scale_gp_beta.lib.tracing as tracing
|
|
133
|
+
|
|
134
|
+
def my_workflow():
|
|
135
|
+
with tracing.create_trace(name="my_application_workflow", metadata={"env": "production"}):
|
|
136
|
+
# All spans created within this block will belong to "my_application_workflow" trace
|
|
137
|
+
print("Starting my application workflow...")
|
|
138
|
+
# ... your workflow logic
|
|
139
|
+
print("Application workflow completed.")
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Creating Spans within a Trace:**
|
|
143
|
+
|
|
144
|
+
Inside a `create_trace` block, use `tracing.create_span()` as a context manager. These spans will automatically be associated with the current trace and parent span (if one exists).
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
import time
|
|
148
|
+
import scale_gp_beta.lib.tracing as tracing
|
|
149
|
+
|
|
150
|
+
def fibonacci(curr: int) -> int:
|
|
151
|
+
with tracing.create_span("fibonacci_calculation", input={"curr": curr}) as span:
|
|
152
|
+
time.sleep(0.1) # Simulate some work
|
|
153
|
+
if curr < 2:
|
|
154
|
+
span.output = {"res": curr}
|
|
155
|
+
return curr
|
|
156
|
+
res = fibonacci(curr - 1) + fibonacci(curr - 2)
|
|
157
|
+
span.output = {"res": res}
|
|
158
|
+
return res
|
|
159
|
+
|
|
160
|
+
def main_traced_example():
|
|
161
|
+
with tracing.create_trace("my_fibonacci_trace"): # Creates a root span
|
|
162
|
+
# This span will be a child of the "my_fibonacci_trace" root span
|
|
163
|
+
with tracing.create_span("main_execution", metadata={"version": "1.0"}) as main_span:
|
|
164
|
+
fib_result = fibonacci(5)
|
|
165
|
+
main_span.output = {"final_fib_result": fib_result}
|
|
166
|
+
print(f"Fibonacci(5) = {fib_result}")
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
##### 2\. Explicit Control
|
|
170
|
+
|
|
171
|
+
For scenarios where context managers aren't suitable, you can manually start and end spans. This approach requires more diligence to ensure all spans are properly ended and maintaining consistency.
|
|
172
|
+
|
|
173
|
+
**Manually Managing Spans (without an explicit Trace context):**
|
|
174
|
+
|
|
175
|
+
You can create spans and explicitly provide their `trace_id` and `parent_id` for fine-grained control. This is useful when integrating with existing systems that manage trace IDs.
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
import uuid
|
|
179
|
+
import time
|
|
180
|
+
import random
|
|
181
|
+
from typing import Any, Dict
|
|
182
|
+
import scale_gp_beta.lib.tracing as tracing
|
|
183
|
+
|
|
184
|
+
class MockDatabase:
|
|
185
|
+
def __init__(self) -> None:
|
|
186
|
+
self._data = {
|
|
187
|
+
"SELECT * FROM users WHERE id = 1;": {"id": 1, "name": "Alice"},
|
|
188
|
+
"SELECT * FROM users WHERE id = 2;": {"id": 2, "name": "Bob"},
|
|
189
|
+
}
|
|
190
|
+
def execute_query(self, query: str, trace_id: str) -> Dict[str, Any]:
|
|
191
|
+
db_span = tracing.create_span("db_query", input={"query": query}, trace_id=trace_id)
|
|
192
|
+
db_span.start()
|
|
193
|
+
try:
|
|
194
|
+
time.sleep(random.uniform(0.1, 0.3)) # Simulate delay
|
|
195
|
+
result = self._data.get(query, {})
|
|
196
|
+
db_span.output = {"result": result}
|
|
197
|
+
return result
|
|
198
|
+
finally:
|
|
199
|
+
db_span.end()
|
|
200
|
+
|
|
201
|
+
def get_user_from_db_explicit(db: MockDatabase, user_id: int, trace_id: str) -> Dict[str, Any]:
|
|
202
|
+
with tracing.create_span("get_user_from_db", input={"user_id": user_id}, trace_id=trace_id):
|
|
203
|
+
query = f"SELECT * FROM users WHERE id = {user_id};"
|
|
204
|
+
return db.execute_query(query, trace_id)
|
|
205
|
+
|
|
206
|
+
def main_explicit_control_example():
|
|
207
|
+
db = MockDatabase()
|
|
208
|
+
my_trace_id = str(uuid.uuid4())
|
|
209
|
+
# Manually create a root span
|
|
210
|
+
main_span = tracing.create_span("main_explicit_call", metadata={"env": "local"}, trace_id=my_trace_id)
|
|
211
|
+
main_span.start()
|
|
212
|
+
try:
|
|
213
|
+
user = get_user_from_db_explicit(db, 1, my_trace_id)
|
|
214
|
+
print(f"Retrieved user: {user.get('name')}")
|
|
215
|
+
finally:
|
|
216
|
+
main_span.end()
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Exporting Existing Tracing Data (Manual Timestamps)**
|
|
220
|
+
|
|
221
|
+
You can even pre-define `start_time`, `end_time`, `span_id`, `parent_id`, and `trace_id` if you need to report historical data or reconstruct traces.
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
import uuid
|
|
225
|
+
from datetime import datetime, timezone, timedelta
|
|
226
|
+
import scale_gp_beta.lib.tracing as tracing
|
|
227
|
+
|
|
228
|
+
parent_span_id = str(uuid.uuid4())
|
|
229
|
+
trace_id = str(uuid.uuid4())
|
|
230
|
+
child_span_id = str(uuid.uuid4())
|
|
231
|
+
|
|
232
|
+
now = datetime.now(timezone.utc)
|
|
233
|
+
|
|
234
|
+
# Parent Span
|
|
235
|
+
parent_span = tracing.create_span(
|
|
236
|
+
"my_parent_span_name",
|
|
237
|
+
input={"test": "input"},
|
|
238
|
+
output={"test": "output"},
|
|
239
|
+
metadata={"test": "metadata"},
|
|
240
|
+
span_id=parent_span_id,
|
|
241
|
+
trace_id=trace_id,
|
|
242
|
+
)
|
|
243
|
+
parent_span.start_time = (now - timedelta(minutes=10)).isoformat()
|
|
244
|
+
parent_span.end_time = now.isoformat()
|
|
245
|
+
parent_span.flush(blocking=True)
|
|
246
|
+
|
|
247
|
+
# Child Span
|
|
248
|
+
child_span = tracing.create_span(
|
|
249
|
+
"my_child_span_name",
|
|
250
|
+
input={"test": "another input"},
|
|
251
|
+
output={"test": "another output"},
|
|
252
|
+
metadata={"test": "another metadata"},
|
|
253
|
+
span_id=child_span_id,
|
|
254
|
+
trace_id=trace_id,
|
|
255
|
+
parent_id=parent_span_id,
|
|
256
|
+
)
|
|
257
|
+
child_span.start_time = (now - timedelta(minutes=6)).isoformat()
|
|
258
|
+
child_span.end_time = (now - timedelta(minutes=2)).isoformat()
|
|
259
|
+
child_span.flush()
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Note that `span.flush()` will by default block the main thread until the request has finished. Use `blocking=False` to enqueue the request which will be picked up by the background worker.
|
|
263
|
+
|
|
264
|
+
#### Helper Methods
|
|
265
|
+
|
|
266
|
+
You can retrieve the currently active span or trace in the execution context using `current_span()` and `current_trace()`:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
import scale_gp_beta.lib.tracing as tracing
|
|
270
|
+
|
|
271
|
+
def nested_function():
|
|
272
|
+
with tracing.create_span("nested_operation"):
|
|
273
|
+
current = tracing.current_span()
|
|
274
|
+
if current:
|
|
275
|
+
print(f"Currently active span: {current.name} (ID: {current.span_id})")
|
|
276
|
+
current_t = tracing.current_trace()
|
|
277
|
+
if current_t:
|
|
278
|
+
print(f"Currently active trace: (ID: {current_t.trace_id})")
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
#### Flushing Tracing Data
|
|
282
|
+
|
|
283
|
+
Spans are generally batched and sent asynchronously by a background worker for efficiency. However, you might need to ensure all buffered spans are sent before an application exits or at critical points in your workflow (e.g., in a distributed worker setting).
|
|
284
|
+
|
|
285
|
+
You can force a synchronous flush of all queued spans using `flush_queue()` or on individual spans and traces (via their root span) with `span.flush()` & `trace.root_span.flush()`.
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
import scale_gp_beta.lib.tracing as tracing
|
|
289
|
+
|
|
290
|
+
# ... (create some spans) ...
|
|
291
|
+
|
|
292
|
+
# Ensure all spans are sent before continuing
|
|
293
|
+
tracing.flush_queue()
|
|
294
|
+
print("All pending spans have been flushed.")
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
You do not need to manually flush all spans on program exit; when shutting down the background worker, we will attempt to flush all tracing data before exiting.
|
|
298
|
+
|
|
299
|
+
#### Configuration Options
|
|
300
|
+
|
|
301
|
+
| ENV Variable | Description |
|
|
302
|
+
|:------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
|
|
303
|
+
| `DISABLE_SCALE_TRACING` | If set, no tracing data will be exported. You can still observe tracing data programmatically via the No-Op variant of Trace and Span objects. |
|
|
304
|
+
| `SGP_API_KEY` | SGP API Key. Used by `SGPClient`. |
|
|
305
|
+
| `SGP_ACCOUNT_ID` | SGP Account ID. Used by `SGPClient`. |
|
|
306
|
+
|
|
307
|
+
#### Multi-Process / Multi-Worker Tracing
|
|
308
|
+
|
|
309
|
+
> **_WARNING:_** Developers should be careful when attempting tracing over multiple workers / Python processes. The SGP backend will expect well-formed trace data, and there is a strong chance of race conditions if a child span is reported before a parent span.
|
|
310
|
+
|
|
311
|
+
The easiest approach to working over multiple workers and Python processes is to only create one trace per worker. You can group traces with a `group_id`.
|
|
312
|
+
|
|
313
|
+
If you want to track an entire workflow over multiple workers, ensure you call `tracing.flush_queue()` before you enqueue a job which creates child spans of the current trace.
|
|
314
|
+
|
|
315
|
+
You will need to use the explicit controls to forward trace and parent span IDs to your workers. The automatic context detection works within the context of the original Python process only.
|
|
316
|
+
|
|
317
|
+
|
|
80
318
|
## Async usage
|
|
81
319
|
|
|
82
320
|
Simply import `AsyncSGPClient` instead of `SGPClient` and use `await` with each API call:
|
|
@@ -11,7 +11,7 @@ scale_gp_beta/_resource.py,sha256=siZly_U6D0AOVLAzaOsqUdEFFzVMbWRj-ml30nvRp7E,11
|
|
|
11
11
|
scale_gp_beta/_response.py,sha256=GemuybPk0uemovTlGHyHkj-ScYTTDJA0jqH5FQqIPwQ,28852
|
|
12
12
|
scale_gp_beta/_streaming.py,sha256=fcCSGXslmi2SmmkM05g2SACXHk2Mj7k1X5uMBu6U5s8,10112
|
|
13
13
|
scale_gp_beta/_types.py,sha256=0wSs40TefKMPBj2wQKenEeZ0lzedoHClNJeqrpAgkII,6204
|
|
14
|
-
scale_gp_beta/_version.py,sha256=
|
|
14
|
+
scale_gp_beta/_version.py,sha256=JLVPWU9jvr2kcPJpjRIxvVmQjhYdVN2g4Wjh0V9d8zc,174
|
|
15
15
|
scale_gp_beta/pagination.py,sha256=t-U86PYxl20VRsz8VXOMJJDe7HxkX7ISFMvRNbBNy9s,4054
|
|
16
16
|
scale_gp_beta/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
scale_gp_beta/_utils/__init__.py,sha256=PNZ_QJuzZEgyYXqkO1HVhGkj5IU9bglVUcw7H-Knjzw,2062
|
|
@@ -117,7 +117,7 @@ scale_gp_beta/types/chat/completion_models_params.py,sha256=ETxafJIUx4tTvkiR-ZCr
|
|
|
117
117
|
scale_gp_beta/types/chat/completion_models_response.py,sha256=Ctgj6o-QWPSdjBKzG9J4Id0-DjXu4UGGw1NR6-840Ec,403
|
|
118
118
|
scale_gp_beta/types/chat/model_definition.py,sha256=NNgopTm900GD0Zs2YHkcvoW67uKaWUKVyPbhKBHvKdQ,817
|
|
119
119
|
scale_gp_beta/types/files/__init__.py,sha256=OKfJYcKb4NObdiRObqJV_dOyDQ8feXekDUge2o_4pXQ,122
|
|
120
|
-
scale_gp_beta-0.1.
|
|
121
|
-
scale_gp_beta-0.1.
|
|
122
|
-
scale_gp_beta-0.1.
|
|
123
|
-
scale_gp_beta-0.1.
|
|
120
|
+
scale_gp_beta-0.1.0a22.dist-info/METADATA,sha256=Ex9RXx9qwGNdoyA56V6KvsQb4Z0FVQRJHN8i-irCkqI,27524
|
|
121
|
+
scale_gp_beta-0.1.0a22.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
122
|
+
scale_gp_beta-0.1.0a22.dist-info/licenses/LICENSE,sha256=x49Bj8r_ZpqfzThbmfHyZ_bE88XvHdIMI_ANyLHFFRE,11338
|
|
123
|
+
scale_gp_beta-0.1.0a22.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|