blaxel 0.2.27__py3-none-any.whl → 0.2.29__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.
- blaxel/__init__.py +5 -3
- blaxel/core/common/__init__.py +2 -1
- blaxel/core/common/autoload.py +104 -1
- blaxel/core/common/settings.py +43 -54
- blaxel/core/common/webhook.py +2 -2
- blaxel/core/jobs/__init__.py +4 -6
- blaxel/core/sandbox/default/filesystem.py +5 -2
- blaxel/core/sandbox/sync/filesystem.py +5 -2
- blaxel/googleadk/model.py +0 -1
- blaxel/livekit/model.py +0 -1
- blaxel/telemetry/__init__.py +7 -3
- blaxel/telemetry/exporters.py +28 -13
- blaxel/telemetry/manager.py +50 -18
- {blaxel-0.2.27.dist-info → blaxel-0.2.29.dist-info}/METADATA +2 -1
- {blaxel-0.2.27.dist-info → blaxel-0.2.29.dist-info}/RECORD +17 -17
- {blaxel-0.2.27.dist-info → blaxel-0.2.29.dist-info}/WHEEL +0 -0
- {blaxel-0.2.27.dist-info → blaxel-0.2.29.dist-info}/licenses/LICENSE +0 -0
blaxel/__init__.py
CHANGED
|
@@ -4,7 +4,9 @@ from .core.common.autoload import autoload
|
|
|
4
4
|
from .core.common.env import env
|
|
5
5
|
from .core.common.settings import settings
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
__version__ = "0.2.29"
|
|
8
|
+
__commit__ = "c29748cf62e4297aac41c1a3de70f608fbc919eb"
|
|
9
|
+
__sentry_dsn__ = "https://9711de13cd02b285ca4378c01de8dc30@o4508714045276160.ingest.us.sentry.io/4510461121462272"
|
|
10
10
|
__all__ = ["autoload", "settings", "env"]
|
|
11
|
+
|
|
12
|
+
autoload()
|
blaxel/core/common/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .autoload import autoload
|
|
1
|
+
from .autoload import autoload, capture_exception
|
|
2
2
|
from .env import env
|
|
3
3
|
from .internal import get_alphanumeric_limited_hash, get_global_unique_hash
|
|
4
4
|
from .settings import Settings, settings
|
|
@@ -11,6 +11,7 @@ from .webhook import (
|
|
|
11
11
|
|
|
12
12
|
__all__ = [
|
|
13
13
|
"autoload",
|
|
14
|
+
"capture_exception",
|
|
14
15
|
"Settings",
|
|
15
16
|
"settings",
|
|
16
17
|
"env",
|
blaxel/core/common/autoload.py
CHANGED
|
@@ -1,12 +1,109 @@
|
|
|
1
|
+
import atexit
|
|
1
2
|
import logging
|
|
3
|
+
import sys
|
|
4
|
+
import threading
|
|
5
|
+
|
|
6
|
+
from sentry_sdk import Client, Hub
|
|
2
7
|
|
|
3
8
|
from ..client import client
|
|
4
|
-
from ..client.response_interceptor import
|
|
9
|
+
from ..client.response_interceptor import (
|
|
10
|
+
response_interceptors_async,
|
|
11
|
+
response_interceptors_sync,
|
|
12
|
+
)
|
|
5
13
|
from ..sandbox.client import client as client_sandbox
|
|
6
14
|
from .settings import settings
|
|
7
15
|
|
|
8
16
|
logger = logging.getLogger(__name__)
|
|
9
17
|
|
|
18
|
+
# Isolated Sentry hub for SDK-only error tracking (doesn't interfere with user's Sentry)
|
|
19
|
+
_sentry_hub: Hub | None = None
|
|
20
|
+
_captured_exceptions: set = set() # Track already captured exceptions to avoid duplicates
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_exception_key(exc_type, exc_value, frame) -> str:
|
|
24
|
+
"""Generate a unique key for an exception based on type, message, and origin."""
|
|
25
|
+
# Use type name + message + original file/line where exception was raised
|
|
26
|
+
# This ensures the same logical exception is only captured once
|
|
27
|
+
exc_name = exc_type.__name__ if exc_type else "Unknown"
|
|
28
|
+
exc_msg = str(exc_value) if exc_value else ""
|
|
29
|
+
# Get the original traceback location (where exception was first raised)
|
|
30
|
+
tb = getattr(exc_value, '__traceback__', None)
|
|
31
|
+
if tb:
|
|
32
|
+
# Walk to the deepest frame (origin of exception)
|
|
33
|
+
while tb.tb_next:
|
|
34
|
+
tb = tb.tb_next
|
|
35
|
+
origin = f"{tb.tb_frame.f_code.co_filename}:{tb.tb_lineno}"
|
|
36
|
+
else:
|
|
37
|
+
origin = f"{frame.f_code.co_filename}:{frame.f_lineno}"
|
|
38
|
+
return f"{exc_name}:{exc_msg}:{origin}"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _trace_blaxel_exceptions(frame, event, arg):
|
|
42
|
+
"""Trace function that captures exceptions from blaxel SDK code."""
|
|
43
|
+
if event == 'exception':
|
|
44
|
+
exc_type, exc_value, exc_tb = arg
|
|
45
|
+
filename = frame.f_code.co_filename
|
|
46
|
+
|
|
47
|
+
# Only capture if it's from blaxel in site-packages
|
|
48
|
+
if 'site-packages/blaxel' in filename:
|
|
49
|
+
# Avoid capturing the same exception multiple times using a content-based key
|
|
50
|
+
exc_key = _get_exception_key(exc_type, exc_value, frame)
|
|
51
|
+
if exc_key not in _captured_exceptions:
|
|
52
|
+
_captured_exceptions.add(exc_key)
|
|
53
|
+
capture_exception(exc_value)
|
|
54
|
+
# Clean up old exception keys to prevent memory leak
|
|
55
|
+
if len(_captured_exceptions) > 1000:
|
|
56
|
+
_captured_exceptions.clear()
|
|
57
|
+
|
|
58
|
+
return _trace_blaxel_exceptions
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def sentry() -> None:
|
|
62
|
+
"""Initialize an isolated Sentry client for SDK error tracking."""
|
|
63
|
+
global _sentry_hub
|
|
64
|
+
try:
|
|
65
|
+
dsn = settings.sentry_dsn
|
|
66
|
+
if not dsn:
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
# Create an isolated client that won't interfere with user's Sentry
|
|
70
|
+
sentry_client = Client(
|
|
71
|
+
dsn=dsn,
|
|
72
|
+
environment=settings.env,
|
|
73
|
+
release=f"sdk-python@{settings.version}",
|
|
74
|
+
default_integrations=False,
|
|
75
|
+
auto_enabling_integrations=False,
|
|
76
|
+
)
|
|
77
|
+
_sentry_hub = Hub(sentry_client)
|
|
78
|
+
|
|
79
|
+
# Set SDK-specific tags
|
|
80
|
+
with _sentry_hub.configure_scope() as scope:
|
|
81
|
+
scope.set_tag("blaxel.workspace", settings.workspace)
|
|
82
|
+
scope.set_tag("blaxel.version", settings.version)
|
|
83
|
+
scope.set_tag("blaxel.commit", settings.commit)
|
|
84
|
+
|
|
85
|
+
# Install trace function to automatically capture SDK exceptions
|
|
86
|
+
sys.settrace(_trace_blaxel_exceptions)
|
|
87
|
+
threading.settrace(_trace_blaxel_exceptions)
|
|
88
|
+
|
|
89
|
+
# Register atexit handler to flush pending events
|
|
90
|
+
atexit.register(_flush_sentry)
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
logger.debug(f"Error initializing Sentry: {e}")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def capture_exception(exception: Exception | None = None) -> None:
|
|
97
|
+
"""Capture an exception to the SDK's isolated Sentry hub."""
|
|
98
|
+
if _sentry_hub is not None and _sentry_hub.client is not None:
|
|
99
|
+
_sentry_hub.capture_exception(exception)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _flush_sentry():
|
|
103
|
+
"""Flush pending Sentry events on program exit."""
|
|
104
|
+
if _sentry_hub is not None and _sentry_hub.client is not None:
|
|
105
|
+
_sentry_hub.client.flush(timeout=2)
|
|
106
|
+
|
|
10
107
|
|
|
11
108
|
def telemetry() -> None:
|
|
12
109
|
from blaxel.telemetry import telemetry_manager
|
|
@@ -33,6 +130,12 @@ def autoload() -> None:
|
|
|
33
130
|
httpx_sandbox_async_client = client_sandbox.get_async_httpx_client()
|
|
34
131
|
httpx_sandbox_async_client.event_hooks["response"] = response_interceptors_async
|
|
35
132
|
|
|
133
|
+
if settings.tracking:
|
|
134
|
+
try:
|
|
135
|
+
sentry()
|
|
136
|
+
except Exception:
|
|
137
|
+
pass
|
|
138
|
+
|
|
36
139
|
try:
|
|
37
140
|
telemetry()
|
|
38
141
|
except Exception:
|
blaxel/core/common/settings.py
CHANGED
|
@@ -3,31 +3,12 @@ import platform
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Dict
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import yaml
|
|
7
7
|
|
|
8
8
|
from ..authentication import BlaxelAuth, auth
|
|
9
9
|
from .logger import init_logger
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def _get_package_version() -> str:
|
|
13
|
-
"""Get the package version from pyproject.toml."""
|
|
14
|
-
try:
|
|
15
|
-
# Navigate up from this file to the project root
|
|
16
|
-
current_file = Path(__file__)
|
|
17
|
-
project_root = current_file.parent.parent.parent.parent.parent
|
|
18
|
-
pyproject_path = project_root / "pyproject.toml"
|
|
19
|
-
|
|
20
|
-
if pyproject_path.exists():
|
|
21
|
-
with open(pyproject_path, "rb") as f:
|
|
22
|
-
pyproject_data = tomli.load(f)
|
|
23
|
-
return pyproject_data.get("project", {}).get("version", "unknown")
|
|
24
|
-
else:
|
|
25
|
-
return "unknown"
|
|
26
|
-
except Exception as e:
|
|
27
|
-
print(f"Warning: Failed to read package version: {e}")
|
|
28
|
-
return "unknown"
|
|
29
|
-
|
|
30
|
-
|
|
31
12
|
def _get_os_arch() -> str:
|
|
32
13
|
"""Get OS and architecture information."""
|
|
33
14
|
try:
|
|
@@ -56,33 +37,6 @@ def _get_os_arch() -> str:
|
|
|
56
37
|
return "unknown/unknown"
|
|
57
38
|
|
|
58
39
|
|
|
59
|
-
def _get_commit_hash() -> str:
|
|
60
|
-
"""Get commit hash from pyproject.toml."""
|
|
61
|
-
try:
|
|
62
|
-
# Try to read from pyproject.toml (build-time injection)
|
|
63
|
-
current_file = Path(__file__)
|
|
64
|
-
project_root = current_file.parent.parent.parent.parent.parent
|
|
65
|
-
pyproject_path = project_root / "pyproject.toml"
|
|
66
|
-
|
|
67
|
-
if pyproject_path.exists():
|
|
68
|
-
with open(pyproject_path, "rb") as f:
|
|
69
|
-
pyproject_data = tomli.load(f)
|
|
70
|
-
# Check multiple possible locations for commit
|
|
71
|
-
commit = None
|
|
72
|
-
if "tool" in pyproject_data and "blaxel" in pyproject_data["tool"]:
|
|
73
|
-
commit = pyproject_data["tool"]["blaxel"].get("commit")
|
|
74
|
-
if not commit and "project" in pyproject_data:
|
|
75
|
-
commit = pyproject_data["project"].get("commit")
|
|
76
|
-
if not commit and "build" in pyproject_data:
|
|
77
|
-
commit = pyproject_data["build"].get("commit")
|
|
78
|
-
|
|
79
|
-
if commit:
|
|
80
|
-
return commit[:7] if len(commit) > 7 else commit
|
|
81
|
-
except Exception:
|
|
82
|
-
pass
|
|
83
|
-
|
|
84
|
-
return "unknown"
|
|
85
|
-
|
|
86
40
|
class Settings:
|
|
87
41
|
auth: BlaxelAuth
|
|
88
42
|
|
|
@@ -90,7 +44,6 @@ class Settings:
|
|
|
90
44
|
init_logger(self.log_level)
|
|
91
45
|
self.auth = auth(self.env, self.base_url)
|
|
92
46
|
self._headers = None
|
|
93
|
-
self._version = None
|
|
94
47
|
|
|
95
48
|
@property
|
|
96
49
|
def env(self) -> str:
|
|
@@ -117,20 +70,30 @@ class Settings:
|
|
|
117
70
|
return "https://run.blaxel.dev"
|
|
118
71
|
|
|
119
72
|
|
|
73
|
+
@property
|
|
74
|
+
def sentry_dsn(self) -> str:
|
|
75
|
+
"""Get the Sentry DSN (injected at build time)."""
|
|
76
|
+
import blaxel
|
|
77
|
+
return blaxel.__sentry_dsn__
|
|
78
|
+
|
|
120
79
|
@property
|
|
121
80
|
def version(self) -> str:
|
|
122
|
-
"""Get the package version."""
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
81
|
+
"""Get the package version (injected at build time)."""
|
|
82
|
+
import blaxel
|
|
83
|
+
return blaxel.__version__ or "unknown"
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def commit(self) -> str:
|
|
87
|
+
"""Get the commit hash (injected at build time)."""
|
|
88
|
+
import blaxel
|
|
89
|
+
return blaxel.__commit__ or "unknown"
|
|
126
90
|
|
|
127
91
|
@property
|
|
128
92
|
def headers(self) -> Dict[str, str]:
|
|
129
93
|
"""Get the headers for API requests."""
|
|
130
94
|
headers = self.auth.get_headers()
|
|
131
95
|
os_arch = _get_os_arch()
|
|
132
|
-
|
|
133
|
-
headers["User-Agent"] = f"blaxel/sdk/python/{self.version} ({os_arch}) blaxel/{commit_hash}"
|
|
96
|
+
headers["User-Agent"] = f"blaxel/sdk/python/{self.version} ({os_arch}) blaxel/{self.commit}"
|
|
134
97
|
return headers
|
|
135
98
|
|
|
136
99
|
|
|
@@ -176,4 +139,30 @@ class Settings:
|
|
|
176
139
|
"""Get the enable opentelemetry."""
|
|
177
140
|
return os.getenv("BL_ENABLE_OPENTELEMETRY", "false").lower() == "true"
|
|
178
141
|
|
|
142
|
+
@property
|
|
143
|
+
def tracking(self) -> bool:
|
|
144
|
+
"""
|
|
145
|
+
Get the tracking setting.
|
|
146
|
+
|
|
147
|
+
Priority:
|
|
148
|
+
1. Environment variable BL_TRACKING (true/false)
|
|
149
|
+
2. config.yaml tracking field
|
|
150
|
+
3. Default: true
|
|
151
|
+
"""
|
|
152
|
+
env_value = os.environ.get("BL_TRACKING")
|
|
153
|
+
if env_value is not None:
|
|
154
|
+
return env_value.lower() == "true"
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
home_dir = Path.home()
|
|
158
|
+
config_path = home_dir / ".blaxel" / "config.yaml"
|
|
159
|
+
with open(config_path, encoding="utf-8") as f:
|
|
160
|
+
config = yaml.safe_load(f)
|
|
161
|
+
if config and "tracking" in config:
|
|
162
|
+
return bool(config["tracking"])
|
|
163
|
+
except Exception:
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
return True
|
|
167
|
+
|
|
179
168
|
settings = Settings()
|
blaxel/core/common/webhook.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import hashlib
|
|
4
4
|
import hmac
|
|
5
5
|
import time
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Protocol
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class RequestLike(Protocol):
|
|
@@ -40,7 +40,7 @@ def verify_webhook_signature(
|
|
|
40
40
|
body: bytes | str,
|
|
41
41
|
signature: str,
|
|
42
42
|
secret: str,
|
|
43
|
-
timestamp:
|
|
43
|
+
timestamp: str | None = None,
|
|
44
44
|
max_age: int = 300,
|
|
45
45
|
) -> bool:
|
|
46
46
|
"""
|
blaxel/core/jobs/__init__.py
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import asyncio
|
|
3
3
|
import os
|
|
4
|
-
import sys
|
|
5
4
|
import time
|
|
6
5
|
from logging import getLogger
|
|
7
|
-
from typing import Any,
|
|
6
|
+
from typing import Any, Callable, Dict, List
|
|
8
7
|
|
|
9
8
|
import requests
|
|
10
9
|
|
|
@@ -36,7 +35,7 @@ class BlJobWrapper:
|
|
|
36
35
|
args_dict[key] = unknown[i + 1]
|
|
37
36
|
return args_dict
|
|
38
37
|
|
|
39
|
-
response = requests.get(os.getenv("BL_EXECUTION_DATA_URL"))
|
|
38
|
+
response = requests.get(os.getenv("BL_EXECUTION_DATA_URL") or "")
|
|
40
39
|
data = response.json()
|
|
41
40
|
tasks = data.get("tasks", [])
|
|
42
41
|
return tasks[self.index] if self.index < len(tasks) else {}
|
|
@@ -63,8 +62,7 @@ class BlJobWrapper:
|
|
|
63
62
|
else:
|
|
64
63
|
func(**parsed_args)
|
|
65
64
|
except Exception as error:
|
|
66
|
-
|
|
67
|
-
sys.exit(1)
|
|
65
|
+
logger.error(f"Job execution failed: {error}")
|
|
68
66
|
|
|
69
67
|
|
|
70
68
|
logger = getLogger(__name__)
|
|
@@ -145,7 +143,7 @@ class BlJob:
|
|
|
145
143
|
)
|
|
146
144
|
return response.text
|
|
147
145
|
|
|
148
|
-
async def arun(self, input: Any, headers: dict = {}, params: dict = {}) ->
|
|
146
|
+
async def arun(self, input: Any, headers: dict = {}, params: dict = {}) -> str:
|
|
149
147
|
logger.debug(f"Job Calling: {self.name}")
|
|
150
148
|
response = await self.acall(self.url, input, headers, params)
|
|
151
149
|
if response.status_code >= 400:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import io
|
|
3
3
|
import json
|
|
4
|
+
import logging
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Any, Callable, Dict, List, Union
|
|
6
7
|
|
|
@@ -21,6 +22,8 @@ MULTIPART_THRESHOLD = 5 * 1024 * 1024 # 5MB
|
|
|
21
22
|
CHUNK_SIZE = 5 * 1024 * 1024 # 5MB per part
|
|
22
23
|
MAX_PARALLEL_UPLOADS = 3 # Number of parallel part uploads
|
|
23
24
|
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
24
27
|
|
|
25
28
|
class SandboxFileSystem(SandboxAction):
|
|
26
29
|
def __init__(self, sandbox_config: SandboxConfiguration, process=None):
|
|
@@ -370,7 +373,7 @@ class SandboxFileSystem(SandboxAction):
|
|
|
370
373
|
response = await client_instance.delete(url, headers=headers)
|
|
371
374
|
# Don't raise error if abort fails - we want to throw the original error
|
|
372
375
|
if not response.is_success:
|
|
373
|
-
|
|
376
|
+
logger.warning(f"Warning: Failed to abort multipart upload: {response.status_code}")
|
|
374
377
|
|
|
375
378
|
async def _upload_with_multipart(
|
|
376
379
|
self, path: str, data: bytes, permissions: str = "0644"
|
|
@@ -426,5 +429,5 @@ class SandboxFileSystem(SandboxAction):
|
|
|
426
429
|
await self._abort_multipart_upload(upload_id)
|
|
427
430
|
except Exception as abort_error:
|
|
428
431
|
# Log but don't throw - we want to throw the original error
|
|
429
|
-
|
|
432
|
+
logger.warning(f"Failed to abort multipart upload: {abort_error}")
|
|
430
433
|
raise error
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import io
|
|
2
2
|
import json
|
|
3
|
+
import logging
|
|
3
4
|
import threading
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Any, Callable, Dict, List, Union
|
|
@@ -16,6 +17,8 @@ from ..types import (
|
|
|
16
17
|
)
|
|
17
18
|
from .action import SyncSandboxAction
|
|
18
19
|
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
19
22
|
# Multipart upload constants
|
|
20
23
|
MULTIPART_THRESHOLD = 5 * 1024 * 1024 # 5MB
|
|
21
24
|
CHUNK_SIZE = 5 * 1024 * 1024 # 5MB per part
|
|
@@ -256,7 +259,7 @@ class SyncSandboxFileSystem(SyncSandboxAction):
|
|
|
256
259
|
with self.get_client() as client_instance:
|
|
257
260
|
response = client_instance.delete(url, headers=headers)
|
|
258
261
|
if not response.is_success:
|
|
259
|
-
|
|
262
|
+
logger.warning(f"Failed to abort multipart upload: {response.status_code}")
|
|
260
263
|
|
|
261
264
|
def _upload_with_multipart(self, path: str, data: bytes, permissions: str = "0644") -> SuccessResponse:
|
|
262
265
|
init_response = self._initiate_multipart_upload(path, permissions)
|
|
@@ -293,7 +296,7 @@ class SyncSandboxFileSystem(SyncSandboxAction):
|
|
|
293
296
|
try:
|
|
294
297
|
self._abort_multipart_upload(upload_id)
|
|
295
298
|
except Exception as abort_error:
|
|
296
|
-
|
|
299
|
+
logger.warning(f"Failed to abort multipart upload: {abort_error}")
|
|
297
300
|
raise error
|
|
298
301
|
|
|
299
302
|
|
blaxel/googleadk/model.py
CHANGED
blaxel/livekit/model.py
CHANGED
blaxel/telemetry/__init__.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"""Blaxel telemetry module."""
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
from .
|
|
5
|
-
from .
|
|
3
|
+
try:
|
|
4
|
+
from .exporters import * # noqa: F403, F401
|
|
5
|
+
from .instrumentation import * # noqa: F403, F401
|
|
6
|
+
from .log import * # noqa: F403, F401
|
|
7
|
+
except ImportError:
|
|
8
|
+
pass
|
|
9
|
+
|
|
6
10
|
from .manager import * # noqa: F403, F401
|
blaxel/telemetry/exporters.py
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
|
|
4
|
-
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
|
|
5
|
-
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
6
|
-
from opentelemetry.sdk._logs import LogData
|
|
7
|
-
from opentelemetry.sdk.metrics.export import MetricExportResult, MetricsData
|
|
1
|
+
from __future__ import annotations
|
|
8
2
|
|
|
3
|
+
from typing import Callable, Dict, Sequence
|
|
9
4
|
|
|
10
|
-
|
|
5
|
+
try:
|
|
6
|
+
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
|
|
7
|
+
from opentelemetry.exporter.otlp.proto.http.metric_exporter import (
|
|
8
|
+
OTLPMetricExporter,
|
|
9
|
+
)
|
|
10
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
11
|
+
from opentelemetry.sdk._logs import LogData
|
|
12
|
+
from opentelemetry.sdk.metrics.export import MetricExportResult, MetricsData
|
|
13
|
+
|
|
14
|
+
_OPENTELEMETRY_AVAILABLE = True
|
|
15
|
+
except ImportError:
|
|
16
|
+
_OPENTELEMETRY_AVAILABLE = False
|
|
17
|
+
OTLPLogExporter = object
|
|
18
|
+
OTLPMetricExporter = object
|
|
19
|
+
OTLPSpanExporter = object
|
|
20
|
+
LogData = None
|
|
21
|
+
MetricExportResult = None
|
|
22
|
+
MetricsData = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DynamicHeadersSpanExporter(OTLPSpanExporter): # type: ignore[misc]
|
|
11
26
|
"""Span exporter with dynamic headers."""
|
|
12
27
|
|
|
13
28
|
def __init__(self, get_headers: Callable[[], Dict[str, str]]):
|
|
@@ -18,7 +33,7 @@ class DynamicHeadersSpanExporter(OTLPSpanExporter):
|
|
|
18
33
|
self._session.headers.update(self._get_headers())
|
|
19
34
|
return super().export(spans)
|
|
20
35
|
|
|
21
|
-
class DynamicHeadersMetricExporter(OTLPMetricExporter):
|
|
36
|
+
class DynamicHeadersMetricExporter(OTLPMetricExporter): # type: ignore[misc]
|
|
22
37
|
"""Metric exporter with dynamic headers."""
|
|
23
38
|
|
|
24
39
|
def __init__(self, get_headers: Callable[[], Dict[str, str]]):
|
|
@@ -27,19 +42,19 @@ class DynamicHeadersMetricExporter(OTLPMetricExporter):
|
|
|
27
42
|
|
|
28
43
|
def export(
|
|
29
44
|
self,
|
|
30
|
-
metrics_data: MetricsData,
|
|
45
|
+
metrics_data: MetricsData, # type: ignore[reportUnknownReturnType]
|
|
31
46
|
timeout_millis: float = 10_000,
|
|
32
47
|
**kwargs,
|
|
33
|
-
) -> MetricExportResult:
|
|
48
|
+
) -> MetricExportResult: # type: ignore[reportUnknownReturnType]
|
|
34
49
|
self._session.headers.update(self._get_headers())
|
|
35
50
|
return super().export(metrics_data, timeout_millis, **kwargs)
|
|
36
51
|
|
|
37
|
-
class DynamicHeadersLogExporter(OTLPLogExporter):
|
|
52
|
+
class DynamicHeadersLogExporter(OTLPLogExporter): # type: ignore[misc]
|
|
38
53
|
"""Log exporter with dynamic headers."""
|
|
39
54
|
def __init__(self, get_headers: Callable[[], Dict[str, str]]):
|
|
40
55
|
self._get_headers = get_headers
|
|
41
56
|
super().__init__()
|
|
42
57
|
|
|
43
|
-
def export(self, batch: Sequence[LogData]):
|
|
58
|
+
def export(self, batch: Sequence[LogData]): # type: ignore[reportUnknownReturnType]
|
|
44
59
|
self._session.headers.update(self._get_headers())
|
|
45
60
|
return super().export(batch)
|
blaxel/telemetry/manager.py
CHANGED
|
@@ -3,6 +3,8 @@ This module provides utilities for setting up and managing OpenTelemetry instrum
|
|
|
3
3
|
It includes classes and functions for configuring tracers, meters, loggers, and integrating with FastAPI applications.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import importlib
|
|
7
9
|
import logging
|
|
8
10
|
import os
|
|
@@ -10,27 +12,52 @@ import signal
|
|
|
10
12
|
import time
|
|
11
13
|
from typing import Any, Dict, List, Type
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
from opentelemetry
|
|
15
|
-
from opentelemetry.
|
|
16
|
-
from opentelemetry.
|
|
17
|
-
from opentelemetry.sdk.
|
|
18
|
-
from opentelemetry.sdk.metrics
|
|
19
|
-
from opentelemetry.sdk.
|
|
20
|
-
from opentelemetry.sdk.
|
|
21
|
-
from opentelemetry.sdk.trace
|
|
22
|
-
from opentelemetry.trace import
|
|
15
|
+
try:
|
|
16
|
+
from opentelemetry import metrics, trace
|
|
17
|
+
from opentelemetry._logs import set_logger_provider
|
|
18
|
+
from opentelemetry.metrics import NoOpMeterProvider
|
|
19
|
+
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
|
|
20
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
21
|
+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
|
22
|
+
from opentelemetry.sdk.resources import Resource
|
|
23
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
24
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
25
|
+
from opentelemetry.trace import NoOpTracerProvider
|
|
26
|
+
|
|
27
|
+
_OPENTELEMETRY_AVAILABLE = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
_OPENTELEMETRY_AVAILABLE = False
|
|
30
|
+
metrics = None
|
|
31
|
+
trace = None
|
|
32
|
+
set_logger_provider = None
|
|
33
|
+
NoOpMeterProvider = None
|
|
34
|
+
LoggerProvider = None
|
|
35
|
+
LoggingHandler = None
|
|
36
|
+
MeterProvider = None
|
|
37
|
+
PeriodicExportingMetricReader = None
|
|
38
|
+
Resource = None
|
|
39
|
+
TracerProvider = None
|
|
40
|
+
BatchSpanProcessor = None
|
|
41
|
+
NoOpTracerProvider = None
|
|
23
42
|
|
|
24
43
|
from blaxel.core.common import Settings
|
|
25
44
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
from .
|
|
33
|
-
from .
|
|
45
|
+
if _OPENTELEMETRY_AVAILABLE:
|
|
46
|
+
from .exporters import (
|
|
47
|
+
DynamicHeadersLogExporter,
|
|
48
|
+
DynamicHeadersMetricExporter,
|
|
49
|
+
DynamicHeadersSpanExporter,
|
|
50
|
+
)
|
|
51
|
+
from .instrumentation.map import MAPPINGS
|
|
52
|
+
from .log.log import AsyncLogRecordProcessor
|
|
53
|
+
from .span import DefaultAttributesSpanProcessor
|
|
54
|
+
else:
|
|
55
|
+
DynamicHeadersLogExporter = None
|
|
56
|
+
DynamicHeadersMetricExporter = None
|
|
57
|
+
DynamicHeadersSpanExporter = None
|
|
58
|
+
MAPPINGS = {}
|
|
59
|
+
AsyncLogRecordProcessor = None
|
|
60
|
+
DefaultAttributesSpanProcessor = None
|
|
34
61
|
|
|
35
62
|
logger = logging.getLogger(__name__)
|
|
36
63
|
|
|
@@ -46,6 +73,8 @@ class TelemetryManager:
|
|
|
46
73
|
|
|
47
74
|
@property
|
|
48
75
|
def enabled(self) -> bool:
|
|
76
|
+
if not _OPENTELEMETRY_AVAILABLE:
|
|
77
|
+
return False
|
|
49
78
|
return (self.settings and self.settings.enable_opentelemetry) or False
|
|
50
79
|
|
|
51
80
|
@property
|
|
@@ -127,6 +156,9 @@ class TelemetryManager:
|
|
|
127
156
|
def initialize(self, settings: Settings):
|
|
128
157
|
"""Initialize the telemetry system."""
|
|
129
158
|
self.settings = settings
|
|
159
|
+
if not _OPENTELEMETRY_AVAILABLE:
|
|
160
|
+
logger.debug("OpenTelemetry not available, telemetry disabled")
|
|
161
|
+
return
|
|
130
162
|
if not self.enabled or self.initialized:
|
|
131
163
|
return
|
|
132
164
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blaxel
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.29
|
|
4
4
|
Summary: Blaxel - AI development platform SDK
|
|
5
5
|
Project-URL: Homepage, https://blaxel.ai
|
|
6
6
|
Project-URL: Documentation, https://docs.blaxel.ai
|
|
@@ -17,6 +17,7 @@ Requires-Dist: pyjwt>=2.0.0
|
|
|
17
17
|
Requires-Dist: python-dateutil>=2.8.0
|
|
18
18
|
Requires-Dist: pyyaml>=6.0.0
|
|
19
19
|
Requires-Dist: requests>=2.32.3
|
|
20
|
+
Requires-Dist: sentry-sdk>=2.46.0
|
|
20
21
|
Requires-Dist: tomli>=2.2.1
|
|
21
22
|
Requires-Dist: websockets<16.0.0
|
|
22
23
|
Provides-Extra: all
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
blaxel/__init__.py,sha256=
|
|
1
|
+
blaxel/__init__.py,sha256=oIul5gSvzhGL1EVP2yx3TehB5BM-uKBq1h10z0g6MN0,412
|
|
2
2
|
blaxel/core/__init__.py,sha256=CKMC7TaCYdOdnwqcJCN9VjBbNg366coZUGTxI1mgFQQ,1710
|
|
3
3
|
blaxel/core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
blaxel/core/agents/__init__.py,sha256=nZfYRnBlpYLD2rnMpVgl3VabQm4w6NoDbGU3qHtpGhw,4376
|
|
@@ -332,14 +332,14 @@ blaxel/core/client/models/workspace.py,sha256=F3m9AqD81sQ5MfPMFy457vv_hP_a1wk9Kt
|
|
|
332
332
|
blaxel/core/client/models/workspace_labels.py,sha256=WbnUY6eCTkUNdY7hhhSF-KQCl8fWFfkCf7hzCTiNp4A,1246
|
|
333
333
|
blaxel/core/client/models/workspace_runtime.py,sha256=dxEpmwCFPOCRKHRKhY-iW7j6TbtL5qUsbjzSn00uTXU,1665
|
|
334
334
|
blaxel/core/client/models/workspace_user.py,sha256=70CcifQWYbeWG7TDui4pblTzUe5sVK0AS19vNCzKE8g,3423
|
|
335
|
-
blaxel/core/common/__init__.py,sha256=
|
|
336
|
-
blaxel/core/common/autoload.py,sha256=
|
|
335
|
+
blaxel/core/common/__init__.py,sha256=K5uIZe98NIjYkjMPdKvP5RuwzIfv1Obm6g3aWh2qjZ0,599
|
|
336
|
+
blaxel/core/common/autoload.py,sha256=bQ6BAn0WR-8cnZkxGet2uWa2k6V-bivpqvsXBHxDxY0,5061
|
|
337
337
|
blaxel/core/common/env.py,sha256=-nns-h3p0x6MGyGdUNAOEfn4Hi_J_pQfJjRKHz43A8E,1231
|
|
338
338
|
blaxel/core/common/internal.py,sha256=NDTFh9Duj84os8GkMXjGzn-UVS9zBDyLAcfPxIpoQGA,3218
|
|
339
339
|
blaxel/core/common/logger.py,sha256=Jt0MCJgYDPq36rl7UyKRDJH76a-AwYdfggNeNYJt6N0,4779
|
|
340
|
-
blaxel/core/common/settings.py,sha256=
|
|
341
|
-
blaxel/core/common/webhook.py,sha256=
|
|
342
|
-
blaxel/core/jobs/__init__.py,sha256=
|
|
340
|
+
blaxel/core/common/settings.py,sha256=T93uUhfmtqF7bhGXp1_78dq-Y1fPyIhHyt-36jbLOpA,4558
|
|
341
|
+
blaxel/core/common/webhook.py,sha256=2cbn0KVL7LSDgHpJM9NAT4o1NhG46WQwn_gFJX-FOS8,5331
|
|
342
|
+
blaxel/core/jobs/__init__.py,sha256=rFWAHAHpqpbuQ9U-fswl0b6Hu4uHyO-mMf5_3nGG8V8,16979
|
|
343
343
|
blaxel/core/mcp/__init__.py,sha256=5VjkiQFb1QWW5QKRgwPHARlxZJ9Xqaz0diJTpM8LLF0,142
|
|
344
344
|
blaxel/core/mcp/client.py,sha256=aK3wSnsO8DmT1BZqw4eiCMF71Jwvni6Qga0DhPP806Y,5437
|
|
345
345
|
blaxel/core/mcp/server.py,sha256=tXySGZKgK3IllYOzYOecp58BixKBkmAIvQp_4nSM_Ww,5919
|
|
@@ -422,7 +422,7 @@ blaxel/core/sandbox/client/models/welcome_response.py,sha256=Bx3L8qHJI1q4dl_hPmk
|
|
|
422
422
|
blaxel/core/sandbox/default/__init__.py,sha256=9URzscKkEzpAwycPbtJTSWyDAT-8JI-LobgpcLK3kn4,325
|
|
423
423
|
blaxel/core/sandbox/default/action.py,sha256=HC1Y7ou-583IKfkZH5_aj61WZEO0WK544kxCKK6-6ac,2466
|
|
424
424
|
blaxel/core/sandbox/default/codegen.py,sha256=RpcUDyjyk5VMNGQRx56R9CXEy7gje-qPnr4jexd2Sc0,3195
|
|
425
|
-
blaxel/core/sandbox/default/filesystem.py,sha256=
|
|
425
|
+
blaxel/core/sandbox/default/filesystem.py,sha256=Z1KMQiTsJGR0hg76-GMKxHNDpEp4Cz1FfRS-BwprHBI,17051
|
|
426
426
|
blaxel/core/sandbox/default/interpreter.py,sha256=EWWvezBA3xbUDP8zoL9jSBPjgS7knwe4y7_cOUu04iM,11226
|
|
427
427
|
blaxel/core/sandbox/default/network.py,sha256=3ZvrJB_9JdZrclNkwifZOIciz2OqzV0LQfbebjZXLIY,358
|
|
428
428
|
blaxel/core/sandbox/default/preview.py,sha256=dSNPo64dohA8QMPlaS8SqF-0kzkAW0CRuGacREb0bsw,6114
|
|
@@ -432,7 +432,7 @@ blaxel/core/sandbox/default/session.py,sha256=XzVpPOH_az6T38Opp4Hmj3RIg7QCzA1l5w
|
|
|
432
432
|
blaxel/core/sandbox/sync/__init__.py,sha256=Ulxw777O8CtZtmOn53uxNFx56pCzlrrBYRRoGDLDCl4,374
|
|
433
433
|
blaxel/core/sandbox/sync/action.py,sha256=H2SWoPvAn77J7oZIp5s-6BMe7bkcugKWdQRGnsH5cEw,2121
|
|
434
434
|
blaxel/core/sandbox/sync/codegen.py,sha256=20CVgcA-jYry-_YgYdafP7FoMZnoSAxzq4fNIPPwUCs,2282
|
|
435
|
-
blaxel/core/sandbox/sync/filesystem.py,sha256=
|
|
435
|
+
blaxel/core/sandbox/sync/filesystem.py,sha256=dk3NRQAUZ-i8_WMl98L5z4KaKP4APZAflnM9tCVe1i4,13820
|
|
436
436
|
blaxel/core/sandbox/sync/interpreter.py,sha256=OnqUE-atLk1Xr-XHc5LgRUVbnze4xBgZJUOpC-70lpg,10948
|
|
437
437
|
blaxel/core/sandbox/sync/network.py,sha256=cif4Q9tn8U7uogUUJ3yLtIt4Y2_zxNwR3O7LpBCt8Tg,285
|
|
438
438
|
blaxel/core/sandbox/sync/preview.py,sha256=762A05i514zPouNR3nKfjWTNFV5t9yC2psnc6Oq7vD0,5175
|
|
@@ -449,7 +449,7 @@ blaxel/crewai/model.py,sha256=4-XuXKe4b-yHYz_rblHhq5ul-IYj7eTK7djN-E6YeHk,1772
|
|
|
449
449
|
blaxel/crewai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
450
450
|
blaxel/crewai/tools.py,sha256=HVMSRTVrc0ygiCCNkL6xa3i31IWwFlGKB6oC-6iWrK8,789
|
|
451
451
|
blaxel/googleadk/__init__.py,sha256=6Pdd7kbf45d4hDLv3MqY4zipTa5rMD-ky5s71vB9oi0,224
|
|
452
|
-
blaxel/googleadk/model.py,sha256=
|
|
452
|
+
blaxel/googleadk/model.py,sha256=iW2VqUnTZjGo4d9keiHVQbzs5B5KngvYYruskYfMTWk,3758
|
|
453
453
|
blaxel/googleadk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
454
454
|
blaxel/googleadk/tools.py,sha256=6xalvhNLiwfTwL8EIlV4i-zIykN8-VXi5tIag9501NI,2252
|
|
455
455
|
blaxel/langgraph/__init__.py,sha256=lw9d7bl5TsYbemToCtus5P6XnhzR4SAcBWM-1Pffc_U,126
|
|
@@ -458,7 +458,7 @@ blaxel/langgraph/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
458
458
|
blaxel/langgraph/tools.py,sha256=P-F3iAhagoqIs6lstMRepMSfiC5u4IY20lyqlCb2-OI,1588
|
|
459
459
|
blaxel/langgraph/custom/gemini.py,sha256=cQgNVdPiVRGfUFK2upDxDV7DoHi-GMTmK3O3FGLVOVE,55062
|
|
460
460
|
blaxel/livekit/__init__.py,sha256=byUwOJ6MHeSXOYXmaVoHnC3ZUmpkqJ55u5mQglXX-SQ,124
|
|
461
|
-
blaxel/livekit/model.py,sha256=
|
|
461
|
+
blaxel/livekit/model.py,sha256=Qhl3EVa4Uum1IsT3nyMxJwXrZn2eDvCyj6V8RHOaJsQ,1687
|
|
462
462
|
blaxel/livekit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
463
463
|
blaxel/livekit/tools.py,sha256=QipxGDnKqma_ktzTUpKgzdp5x4bvpQnxEVoqSTBesu8,1053
|
|
464
464
|
blaxel/llamaindex/__init__.py,sha256=iZ3QbZhlwKvP91ChcqSXVkpRrzurMxJoQfKdZFzE2AA,127
|
|
@@ -475,9 +475,9 @@ blaxel/pydantic/model.py,sha256=LQui3m7SjpOunlvm7EjxQOfDof8gfUUnig_EkztEsj0,6039
|
|
|
475
475
|
blaxel/pydantic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
476
476
|
blaxel/pydantic/tools.py,sha256=94eYhgPsE7PwL8F2zYh5nWUh4GDgAg_GP0RgsMb2UZE,1969
|
|
477
477
|
blaxel/pydantic/custom/gemini.py,sha256=2oZFb-7-qaaCY1ajoCkiHSavZqV4uzCLwqp1192u7VM,1051
|
|
478
|
-
blaxel/telemetry/__init__.py,sha256=
|
|
479
|
-
blaxel/telemetry/exporters.py,sha256=
|
|
480
|
-
blaxel/telemetry/manager.py,sha256=
|
|
478
|
+
blaxel/telemetry/__init__.py,sha256=g5wNqaXAVdRmUe0cX7qKHN1cftF2MaHo_QCS4D-u8CQ,257
|
|
479
|
+
blaxel/telemetry/exporters.py,sha256=2eHcHlCXJ4yQcKfWnRcnRhkBim8_yUNqgFIVUDPHkv0,2194
|
|
480
|
+
blaxel/telemetry/manager.py,sha256=r7fbx1ElUO8J4aNJ8SFlT0VxmQI2-an1HrmvannkO4Q,10339
|
|
481
481
|
blaxel/telemetry/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
482
482
|
blaxel/telemetry/span.py,sha256=_mLYE_8vSEenUGmnC-OqPExJqBpOx6iT3BnkmkmQxN4,3644
|
|
483
483
|
blaxel/telemetry/instrumentation/blaxel_core.py,sha256=j0NwvC92DgMIXd6bMwC_Wor5tOPNPizNDNQDU4wsYAM,4930
|
|
@@ -488,7 +488,7 @@ blaxel/telemetry/instrumentation/map.py,sha256=PCzZJj39yiYVYJrxLBNP-NW-tjjYyTijw
|
|
|
488
488
|
blaxel/telemetry/instrumentation/utils.py,sha256=KInMYZH-mu9_wvetmf0EmgrfN3Sw8IWk2Y95v2u90_U,1901
|
|
489
489
|
blaxel/telemetry/log/log.py,sha256=k9Gcs_wCDrZFFGCkcgMBROk2vTJIy7eIVNiP_pI2EPY,2430
|
|
490
490
|
blaxel/telemetry/log/logger.py,sha256=NPAS3g82ryROjvc_DEZaTIfrcehoLEZoP-JkLxADxc0,4113
|
|
491
|
-
blaxel-0.2.
|
|
492
|
-
blaxel-0.2.
|
|
493
|
-
blaxel-0.2.
|
|
494
|
-
blaxel-0.2.
|
|
491
|
+
blaxel-0.2.29.dist-info/METADATA,sha256=wWD5UGi8aPkji6g4LqrU65tBhBONz_Bg04qZLf5iDy8,10102
|
|
492
|
+
blaxel-0.2.29.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
493
|
+
blaxel-0.2.29.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
|
|
494
|
+
blaxel-0.2.29.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|