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 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
- autoload()
8
-
9
- __version__ = "0.2.0"
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()
@@ -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",
@@ -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 response_interceptors_async, response_interceptors_sync
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:
@@ -3,31 +3,12 @@ import platform
3
3
  from pathlib import Path
4
4
  from typing import Dict
5
5
 
6
- import tomli
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
- if self._version is None:
124
- self._version = _get_package_version()
125
- return self._version
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
- commit_hash = _get_commit_hash()
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()
@@ -3,7 +3,7 @@
3
3
  import hashlib
4
4
  import hmac
5
5
  import time
6
- from typing import Optional, Protocol
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: Optional[str] = None,
43
+ timestamp: str | None = None,
44
44
  max_age: int = 300,
45
45
  ) -> bool:
46
46
  """
@@ -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, Awaitable, Callable, Dict, List
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
- print("Job execution failed:", error, file=sys.stderr)
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 = {}) -> Awaitable[str]:
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
- print(f"Warning: Failed to abort multipart upload: {response.status_code}")
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
- print(f"Failed to abort multipart upload: {abort_error}")
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
- print(f"Warning: Failed to abort multipart upload: {response.status_code}")
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
- print(f"Failed to abort multipart upload: {abort_error}")
299
+ logger.warning(f"Failed to abort multipart upload: {abort_error}")
297
300
  raise error
298
301
 
299
302
 
blaxel/googleadk/model.py CHANGED
@@ -55,7 +55,6 @@ class AuthenticatedLiteLLMClient(LiteLLMClient):
55
55
 
56
56
 
57
57
  async def get_google_adk_model(url: str, type: str, model: str, **kwargs):
58
- print("HEER TWO")
59
58
  llm_client = AuthenticatedLiteLLMClient()
60
59
  if type == "mistral":
61
60
  return LiteLlm(
blaxel/livekit/model.py CHANGED
@@ -22,7 +22,6 @@ class DynamicHeadersHTTPClient(httpx.AsyncClient):
22
22
  auth_headers = settings.auth.get_headers()
23
23
  for key, value in auth_headers.items():
24
24
  request.headers[key] = value
25
- print(request.__dict__)
26
25
  return await super().send(request, *args, **kwargs)
27
26
 
28
27
 
@@ -1,6 +1,10 @@
1
1
  """Blaxel telemetry module."""
2
2
 
3
- from .exporters import * # noqa: F403, F401
4
- from .instrumentation import * # noqa: F403, F401
5
- from .log import * # noqa: F403, F401
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
@@ -1,13 +1,28 @@
1
- from typing import Callable, Dict, Sequence
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
- class DynamicHeadersSpanExporter(OTLPSpanExporter):
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)
@@ -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
- from opentelemetry import metrics, trace
14
- from opentelemetry._logs import set_logger_provider
15
- from opentelemetry.metrics import NoOpMeterProvider
16
- from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
17
- from opentelemetry.sdk.metrics import MeterProvider
18
- from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
19
- from opentelemetry.sdk.resources import Resource
20
- from opentelemetry.sdk.trace import TracerProvider
21
- from opentelemetry.sdk.trace.export import BatchSpanProcessor
22
- from opentelemetry.trace import NoOpTracerProvider
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
- from .exporters import (
27
- DynamicHeadersLogExporter,
28
- DynamicHeadersMetricExporter,
29
- DynamicHeadersSpanExporter,
30
- )
31
- from .instrumentation.map import MAPPINGS
32
- from .log.log import AsyncLogRecordProcessor
33
- from .span import DefaultAttributesSpanProcessor
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.27
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=dK8RSbcDKSdr3Fr45aLDs6qPkLkxrsERQo24HCATbf4,241
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=J8Hbg0MYT81n1fO4rGIdnlPuEe_1S0SywKUwmVpMvnw,555
336
- blaxel/core/common/autoload.py,sha256=KL_lmgQfzb1gmA5XYYYODeXss5p28v6Y8nhIfvjbKgM,1327
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=9YK6LdUg9CipeGVGfs7vsW_G1_vXCTViva7Zk44kxrY,5381
341
- blaxel/core/common/webhook.py,sha256=NtO2WBNJwCxbE5M9G2hsMS6vb7dnMHyDS74NuLDiym0,5344
342
- blaxel/core/jobs/__init__.py,sha256=DbZX3GUpz_z1XMcb0gINRrF1jOo2iYGwKTwmueRZ_kc,17038
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=pp5cU0W10bD6A3aKZ9f9ZmcHZAZtYF8Xac1TEHmG1m4,16980
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=k9B1Ni70Mc5u-5chEEml2wC3mTsyGJy5y1LB5NNyc-M,13758
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=exl91TXdY2Y3orUTv11W6r9SrUID7pJRAMwil1YLLfw,3780
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=AHLNJYQQujoHd5AlweR170u6LnFbBt5pqaXniwJd9q0,1719
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=b3Xr-zFq9HEhr0Hp6f9fN8agurOPJnRnr8XAxu835ow,210
479
- blaxel/telemetry/exporters.py,sha256=EoX3uaBVku1Rg49pSNXKFyHhgY5OV3Ih6UlqgjF5epw,1670
480
- blaxel/telemetry/manager.py,sha256=i_3r8W-zpluXiqdIHsoW97Hz-HyZkXMeM76UO-3nS5s,9365
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.27.dist-info/METADATA,sha256=3A2ti807NsZVfM-MKS9eXpc_lP1TT8jiuetm1KqJu2I,10068
492
- blaxel-0.2.27.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
493
- blaxel-0.2.27.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
494
- blaxel-0.2.27.dist-info/RECORD,,
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,,