mseep-agentops 0.4.18__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.
Files changed (94) hide show
  1. agentops/__init__.py +488 -0
  2. agentops/client/__init__.py +5 -0
  3. agentops/client/api/__init__.py +71 -0
  4. agentops/client/api/base.py +162 -0
  5. agentops/client/api/types.py +21 -0
  6. agentops/client/api/versions/__init__.py +10 -0
  7. agentops/client/api/versions/v3.py +65 -0
  8. agentops/client/api/versions/v4.py +104 -0
  9. agentops/client/client.py +211 -0
  10. agentops/client/http/__init__.py +0 -0
  11. agentops/client/http/http_adapter.py +116 -0
  12. agentops/client/http/http_client.py +215 -0
  13. agentops/config.py +268 -0
  14. agentops/enums.py +36 -0
  15. agentops/exceptions.py +38 -0
  16. agentops/helpers/__init__.py +44 -0
  17. agentops/helpers/dashboard.py +54 -0
  18. agentops/helpers/deprecation.py +50 -0
  19. agentops/helpers/env.py +52 -0
  20. agentops/helpers/serialization.py +137 -0
  21. agentops/helpers/system.py +178 -0
  22. agentops/helpers/time.py +11 -0
  23. agentops/helpers/version.py +36 -0
  24. agentops/instrumentation/__init__.py +598 -0
  25. agentops/instrumentation/common/__init__.py +82 -0
  26. agentops/instrumentation/common/attributes.py +278 -0
  27. agentops/instrumentation/common/instrumentor.py +147 -0
  28. agentops/instrumentation/common/metrics.py +100 -0
  29. agentops/instrumentation/common/objects.py +26 -0
  30. agentops/instrumentation/common/span_management.py +176 -0
  31. agentops/instrumentation/common/streaming.py +218 -0
  32. agentops/instrumentation/common/token_counting.py +177 -0
  33. agentops/instrumentation/common/version.py +71 -0
  34. agentops/instrumentation/common/wrappers.py +235 -0
  35. agentops/legacy/__init__.py +277 -0
  36. agentops/legacy/event.py +156 -0
  37. agentops/logging/__init__.py +4 -0
  38. agentops/logging/config.py +86 -0
  39. agentops/logging/formatters.py +34 -0
  40. agentops/logging/instrument_logging.py +91 -0
  41. agentops/sdk/__init__.py +27 -0
  42. agentops/sdk/attributes.py +151 -0
  43. agentops/sdk/core.py +607 -0
  44. agentops/sdk/decorators/__init__.py +51 -0
  45. agentops/sdk/decorators/factory.py +486 -0
  46. agentops/sdk/decorators/utility.py +216 -0
  47. agentops/sdk/exporters.py +87 -0
  48. agentops/sdk/processors.py +71 -0
  49. agentops/sdk/types.py +21 -0
  50. agentops/semconv/__init__.py +36 -0
  51. agentops/semconv/agent.py +29 -0
  52. agentops/semconv/core.py +19 -0
  53. agentops/semconv/enum.py +11 -0
  54. agentops/semconv/instrumentation.py +13 -0
  55. agentops/semconv/langchain.py +63 -0
  56. agentops/semconv/message.py +61 -0
  57. agentops/semconv/meters.py +24 -0
  58. agentops/semconv/resource.py +52 -0
  59. agentops/semconv/span_attributes.py +118 -0
  60. agentops/semconv/span_kinds.py +50 -0
  61. agentops/semconv/status.py +11 -0
  62. agentops/semconv/tool.py +15 -0
  63. agentops/semconv/workflow.py +69 -0
  64. agentops/validation.py +357 -0
  65. mseep_agentops-0.4.18.dist-info/METADATA +49 -0
  66. mseep_agentops-0.4.18.dist-info/RECORD +94 -0
  67. mseep_agentops-0.4.18.dist-info/WHEEL +5 -0
  68. mseep_agentops-0.4.18.dist-info/licenses/LICENSE +21 -0
  69. mseep_agentops-0.4.18.dist-info/top_level.txt +2 -0
  70. tests/__init__.py +0 -0
  71. tests/conftest.py +10 -0
  72. tests/unit/__init__.py +0 -0
  73. tests/unit/client/__init__.py +1 -0
  74. tests/unit/client/test_http_adapter.py +221 -0
  75. tests/unit/client/test_http_client.py +206 -0
  76. tests/unit/conftest.py +54 -0
  77. tests/unit/sdk/__init__.py +1 -0
  78. tests/unit/sdk/instrumentation_tester.py +207 -0
  79. tests/unit/sdk/test_attributes.py +392 -0
  80. tests/unit/sdk/test_concurrent_instrumentation.py +468 -0
  81. tests/unit/sdk/test_decorators.py +763 -0
  82. tests/unit/sdk/test_exporters.py +241 -0
  83. tests/unit/sdk/test_factory.py +1188 -0
  84. tests/unit/sdk/test_internal_span_processor.py +397 -0
  85. tests/unit/sdk/test_resource_attributes.py +35 -0
  86. tests/unit/test_config.py +82 -0
  87. tests/unit/test_context_manager.py +777 -0
  88. tests/unit/test_events.py +27 -0
  89. tests/unit/test_host_env.py +54 -0
  90. tests/unit/test_init_py.py +501 -0
  91. tests/unit/test_serialization.py +433 -0
  92. tests/unit/test_session.py +676 -0
  93. tests/unit/test_user_agent.py +34 -0
  94. tests/unit/test_validation.py +405 -0
@@ -0,0 +1,162 @@
1
+ """
2
+ Base API client classes for making HTTP requests.
3
+
4
+ This module provides the foundation for all API clients in the AgentOps SDK.
5
+ """
6
+
7
+ from typing import Any, Dict, Optional, Protocol
8
+
9
+ import requests
10
+
11
+ from agentops.client.http.http_client import HttpClient
12
+ from agentops.helpers.version import get_agentops_version
13
+
14
+
15
+ class TokenFetcher(Protocol):
16
+ """Protocol for token fetching functions"""
17
+
18
+ def __call__(self, api_key: str) -> str:
19
+ ...
20
+
21
+
22
+ class BaseApiClient:
23
+ """
24
+ Base class for API communication with connection pooling.
25
+
26
+ This class provides the core HTTP functionality without authentication.
27
+ It should be used for APIs that don't require authentication.
28
+ """
29
+
30
+ def __init__(self, endpoint: str):
31
+ """
32
+ Initialize the base API client.
33
+
34
+ Args:
35
+ endpoint: The base URL for the API
36
+ """
37
+ self.endpoint = endpoint
38
+ self.http_client = HttpClient()
39
+ self.last_response: Optional[requests.Response] = None
40
+
41
+ def prepare_headers(self, custom_headers: Optional[Dict[str, str]] = None) -> Dict[str, str]:
42
+ """
43
+ Prepare headers for API requests.
44
+
45
+ Args:
46
+ custom_headers: Additional headers to include
47
+
48
+ Returns:
49
+ Headers dictionary with standard headers and any custom headers
50
+ """
51
+ headers = {
52
+ "Content-Type": "application/json",
53
+ "Connection": "keep-alive",
54
+ "Keep-Alive": "timeout=10, max=1000",
55
+ "User-Agent": f"agentops-python/{get_agentops_version() or 'unknown'}",
56
+ }
57
+
58
+ if custom_headers:
59
+ headers.update(custom_headers)
60
+
61
+ return headers
62
+
63
+ def _get_full_url(self, path: str) -> str:
64
+ """
65
+ Get the full URL for a path.
66
+
67
+ Args:
68
+ path: The API endpoint path
69
+
70
+ Returns:
71
+ The full URL
72
+ """
73
+ return f"{self.endpoint}{path}"
74
+
75
+ def request(
76
+ self,
77
+ method: str,
78
+ path: str,
79
+ data: Optional[Dict[str, Any]] = None,
80
+ headers: Optional[Dict[str, str]] = None,
81
+ timeout: int = 30,
82
+ ) -> requests.Response:
83
+ """
84
+ Make a generic HTTP request
85
+
86
+ Args:
87
+ method: HTTP method (e.g., 'get', 'post', 'put', 'delete')
88
+ path: API endpoint path
89
+ data: Request payload (for POST, PUT methods)
90
+ headers: Request headers
91
+ timeout: Request timeout in seconds
92
+
93
+ Returns:
94
+ Response from the API
95
+
96
+ Raises:
97
+ Exception: If the request fails
98
+ """
99
+ url = self._get_full_url(path)
100
+
101
+ try:
102
+ response = self.http_client.request(method=method, url=url, data=data, headers=headers, timeout=timeout)
103
+
104
+ self.last_response = response
105
+ return response
106
+ except requests.RequestException as e:
107
+ self.last_response = None
108
+ raise Exception(f"{method.upper()} request failed: {str(e)}") from e
109
+
110
+ def post(self, path: str, data: Dict[str, Any], headers: Dict[str, str]) -> requests.Response:
111
+ """
112
+ Make POST request
113
+
114
+ Args:
115
+ path: API endpoint path
116
+ data: Request payload
117
+ headers: Request headers
118
+
119
+ Returns:
120
+ Response from the API
121
+ """
122
+ return self.request("post", path, data=data, headers=headers)
123
+
124
+ def get(self, path: str, headers: Dict[str, str]) -> requests.Response:
125
+ """
126
+ Make GET request
127
+
128
+ Args:
129
+ path: API endpoint path
130
+ headers: Request headers
131
+
132
+ Returns:
133
+ Response from the API
134
+ """
135
+ return self.request("get", path, headers=headers)
136
+
137
+ def put(self, path: str, data: Dict[str, Any], headers: Dict[str, str]) -> requests.Response:
138
+ """
139
+ Make PUT request
140
+
141
+ Args:
142
+ path: API endpoint path
143
+ data: Request payload
144
+ headers: Request headers
145
+
146
+ Returns:
147
+ Response from the API
148
+ """
149
+ return self.request("put", path, data=data, headers=headers)
150
+
151
+ def delete(self, path: str, headers: Dict[str, str]) -> requests.Response:
152
+ """
153
+ Make DELETE request
154
+
155
+ Args:
156
+ path: API endpoint path
157
+ headers: Request headers
158
+
159
+ Returns:
160
+ Response from the API
161
+ """
162
+ return self.request("delete", path, headers=headers)
@@ -0,0 +1,21 @@
1
+ """
2
+ Common types used across API client modules.
3
+
4
+ This module contains type definitions used by multiple API client modules.
5
+ """
6
+
7
+ from typing import TypedDict
8
+
9
+
10
+ class AuthTokenResponse(TypedDict):
11
+ """Response from the auth/token endpoint"""
12
+
13
+ token: str
14
+ project_id: str
15
+
16
+
17
+ class UploadedObjectResponse(TypedDict):
18
+ """Response from the v4/objects/upload endpoint"""
19
+
20
+ url: str
21
+ size: int
@@ -0,0 +1,10 @@
1
+ """
2
+ API client versions package.
3
+
4
+ This package contains client implementations for different API versions.
5
+ """
6
+
7
+ from agentops.client.api.versions.v3 import V3Client
8
+ from agentops.client.api.versions.v4 import V4Client
9
+
10
+ __all__ = ["V3Client", "V4Client"]
@@ -0,0 +1,65 @@
1
+ """
2
+ V3 API client for the AgentOps API.
3
+
4
+ This module provides the client for the V3 version of the AgentOps API.
5
+ """
6
+
7
+ from agentops.client.api.base import BaseApiClient
8
+ from agentops.client.api.types import AuthTokenResponse
9
+ from agentops.exceptions import ApiServerException
10
+ from agentops.logging import logger
11
+ from termcolor import colored
12
+
13
+
14
+ class V3Client(BaseApiClient):
15
+ """Client for the AgentOps V3 API"""
16
+
17
+ def __init__(self, endpoint: str):
18
+ """
19
+ Initialize the V3 API client.
20
+
21
+ Args:
22
+ endpoint: The base URL for the API
23
+ """
24
+ # Set up with V3-specific auth endpoint
25
+ super().__init__(endpoint)
26
+
27
+ def fetch_auth_token(self, api_key: str) -> AuthTokenResponse:
28
+ path = "/v3/auth/token"
29
+ data = {"api_key": api_key}
30
+ headers = self.prepare_headers()
31
+
32
+ r = self.post(path, data, headers)
33
+
34
+ if r.status_code != 200:
35
+ error_msg = f"Authentication failed: {r.status_code}"
36
+ try:
37
+ error_data = r.json()
38
+ if "error" in error_data:
39
+ error_msg = f"{error_data['error']}"
40
+ except Exception:
41
+ pass
42
+ logger.error(f"{error_msg} - Perhaps an invalid API key?")
43
+ raise ApiServerException(error_msg)
44
+
45
+ try:
46
+ jr = r.json()
47
+ token = jr.get("token")
48
+ if not token:
49
+ raise ApiServerException("No token in authentication response")
50
+
51
+ # Check project premium status
52
+ if jr.get("project_prem_status") != "pro":
53
+ logger.info(
54
+ colored(
55
+ "\x1b[34mYou're on the agentops free plan 🤔\x1b[0m",
56
+ "blue",
57
+ )
58
+ )
59
+
60
+ return jr
61
+ except Exception as e:
62
+ logger.error(f"Failed to process authentication response: {str(e)}")
63
+ raise ApiServerException(f"Failed to process authentication response: {str(e)}")
64
+
65
+ # Add V3-specific API methods here
@@ -0,0 +1,104 @@
1
+ """
2
+ V4 API client for the AgentOps API.
3
+
4
+ This module provides the client for the V4 version of the AgentOps API.
5
+ """
6
+
7
+ from typing import Optional, Union, Dict
8
+
9
+ from agentops.client.api.base import BaseApiClient
10
+ from agentops.exceptions import ApiServerException
11
+ from agentops.client.api.types import UploadedObjectResponse
12
+ from agentops.helpers.version import get_agentops_version
13
+
14
+
15
+ class V4Client(BaseApiClient):
16
+ """Client for the AgentOps V4 API"""
17
+
18
+ auth_token: str
19
+
20
+ def set_auth_token(self, token: str):
21
+ """
22
+ Set the authentication token for API requests.
23
+
24
+ Args:
25
+ token: The authentication token to set
26
+ """
27
+ self.auth_token = token
28
+
29
+ def prepare_headers(self, custom_headers: Optional[Dict[str, str]] = None) -> Dict[str, str]:
30
+ """
31
+ Prepare headers for API requests.
32
+
33
+ Args:
34
+ custom_headers: Additional headers to include
35
+ Returns:
36
+ Headers dictionary with standard headers and any custom headers
37
+ """
38
+ headers = {
39
+ "Authorization": f"Bearer {self.auth_token}",
40
+ "User-Agent": f"agentops-python/{get_agentops_version() or 'unknown'}",
41
+ }
42
+ if custom_headers:
43
+ headers.update(custom_headers)
44
+ return headers
45
+
46
+ def upload_object(self, body: Union[str, bytes]) -> UploadedObjectResponse:
47
+ """
48
+ Upload an object to the API and return the response.
49
+
50
+ Args:
51
+ body: The object to upload, either as a string or bytes.
52
+ Returns:
53
+ UploadedObjectResponse: The response from the API after upload.
54
+ """
55
+ if isinstance(body, bytes):
56
+ body = body.decode("utf-8")
57
+
58
+ response = self.post("/v4/objects/upload/", body, self.prepare_headers())
59
+
60
+ if response.status_code != 200:
61
+ error_msg = f"Upload failed: {response.status_code}"
62
+ try:
63
+ error_data = response.json()
64
+ if "error" in error_data:
65
+ error_msg = error_data["error"]
66
+ except Exception:
67
+ pass
68
+ raise ApiServerException(error_msg)
69
+
70
+ try:
71
+ response_data = response.json()
72
+ return UploadedObjectResponse(**response_data)
73
+ except Exception as e:
74
+ raise ApiServerException(f"Failed to process upload response: {str(e)}")
75
+
76
+ def upload_logfile(self, body: Union[str, bytes], trace_id: int) -> UploadedObjectResponse:
77
+ """
78
+ Upload an log file to the API and return the response.
79
+
80
+ Args:
81
+ body: The log file to upload, either as a string or bytes.
82
+ Returns:
83
+ UploadedObjectResponse: The response from the API after upload.
84
+ """
85
+ if isinstance(body, bytes):
86
+ body = body.decode("utf-8")
87
+
88
+ response = self.post("/v4/logs/upload/", body, {**self.prepare_headers(), "Trace-Id": str(trace_id)})
89
+
90
+ if response.status_code != 200:
91
+ error_msg = f"Upload failed: {response.status_code}"
92
+ try:
93
+ error_data = response.json()
94
+ if "error" in error_data:
95
+ error_msg = error_data["error"]
96
+ except Exception:
97
+ pass
98
+ raise ApiServerException(error_msg)
99
+
100
+ try:
101
+ response_data = response.json()
102
+ return UploadedObjectResponse(**response_data)
103
+ except Exception as e:
104
+ raise ApiServerException(f"Failed to process upload response: {str(e)}")
@@ -0,0 +1,211 @@
1
+ import atexit
2
+ from typing import Optional, Any
3
+
4
+ from agentops.client.api import ApiClient
5
+ from agentops.config import Config
6
+ from agentops.exceptions import NoApiKeyException
7
+ from agentops.instrumentation import instrument_all
8
+ from agentops.logging import logger
9
+ from agentops.logging.config import configure_logging, intercept_opentelemetry_logging
10
+ from agentops.sdk.core import TraceContext, tracer
11
+ from agentops.legacy import Session
12
+
13
+ # Global variables to hold the client's auto-started trace and its legacy session wrapper
14
+ _client_init_trace_context: Optional[TraceContext] = None
15
+ _client_legacy_session_for_init_trace: Optional[Session] = None
16
+
17
+ # Single atexit handler registered flag
18
+ _atexit_registered = False
19
+
20
+
21
+ def _end_init_trace_atexit():
22
+ """Global atexit handler to end the client's auto-initialized trace during shutdown."""
23
+ global _client_init_trace_context, _client_legacy_session_for_init_trace
24
+ if _client_init_trace_context is not None:
25
+ logger.debug("Auto-ending client's init trace during shutdown.")
26
+ try:
27
+ # Use global tracer to end the trace directly
28
+ if tracer.initialized and _client_init_trace_context.span.is_recording():
29
+ tracer.end_trace(_client_init_trace_context, end_state="Shutdown")
30
+ except Exception as e:
31
+ logger.warning(f"Error ending client's init trace during shutdown: {e}")
32
+ finally:
33
+ _client_init_trace_context = None
34
+ _client_legacy_session_for_init_trace = None # Clear its legacy wrapper too
35
+
36
+
37
+ class Client:
38
+ """Singleton client for AgentOps service"""
39
+
40
+ config: Config
41
+ _initialized: bool
42
+ _init_trace_context: Optional[TraceContext] = None # Stores the context of the auto-started trace
43
+ _legacy_session_for_init_trace: Optional[
44
+ Session
45
+ ] = None # Stores the legacy Session wrapper for the auto-started trace
46
+
47
+ __instance = None # Class variable for singleton pattern
48
+
49
+ api: ApiClient
50
+
51
+ def __new__(cls, *args: Any, **kwargs: Any) -> "Client":
52
+ if cls.__instance is None:
53
+ cls.__instance = super(Client, cls).__new__(cls)
54
+ # Initialize instance variables that should only be set once per instance
55
+ cls.__instance._init_trace_context = None
56
+ cls.__instance._legacy_session_for_init_trace = None
57
+ return cls.__instance
58
+
59
+ def __init__(self):
60
+ # Initialization of attributes like config, _initialized should happen here if they are instance-specific
61
+ # and not shared via __new__ for a true singleton that can be re-configured.
62
+ # However, the current pattern re-initializes config in init().
63
+ if (
64
+ not hasattr(self, "_initialized") or not self._initialized
65
+ ): # Ensure init logic runs only once per actual initialization intent
66
+ self.config = Config() # Initialize config here for the instance
67
+ self._initialized = False
68
+ # self._init_trace_context = None # Already done in __new__
69
+ # self._legacy_session_for_init_trace = None # Already done in __new__
70
+
71
+ def init(self, **kwargs: Any) -> None: # Return type updated to None
72
+ # Recreate the Config object to parse environment variables at the time of initialization
73
+ # This allows re-init with new env vars if needed, though true singletons usually init once.
74
+ self.config = Config()
75
+ self.configure(**kwargs)
76
+
77
+ # Only treat as re-initialization if a different non-None API key is explicitly provided
78
+ provided_api_key = kwargs.get("api_key")
79
+ if self.initialized and provided_api_key is not None and provided_api_key != self.config.api_key:
80
+ logger.warning("AgentOps Client being re-initialized with a different API key. This is unusual.")
81
+ # Reset initialization status to allow re-init with new key/config
82
+ self._initialized = False
83
+ if self._init_trace_context and self._init_trace_context.span.is_recording():
84
+ logger.warning("Ending previously auto-started trace due to re-initialization.")
85
+ tracer.end_trace(self._init_trace_context, "Reinitialized")
86
+ self._init_trace_context = None
87
+ self._legacy_session_for_init_trace = None
88
+
89
+ if self.initialized:
90
+ logger.debug("AgentOps Client already initialized.")
91
+ # If auto_start_session was true, return the existing legacy session wrapper
92
+ if self.config.auto_start_session:
93
+ return self._legacy_session_for_init_trace
94
+ return None # If not auto-starting, and already initialized, return None
95
+
96
+ if not self.config.api_key:
97
+ raise NoApiKeyException
98
+
99
+ configure_logging(self.config)
100
+ intercept_opentelemetry_logging()
101
+
102
+ self.api = ApiClient(self.config.endpoint)
103
+
104
+ try:
105
+ response = self.api.v3.fetch_auth_token(self.config.api_key)
106
+ if response is None:
107
+ # If auth fails, we cannot proceed with tracer initialization that depends on project_id
108
+ logger.error("Failed to fetch auth token. AgentOps SDK will not be initialized.")
109
+ return None # Explicitly return None if auth fails
110
+ except Exception as e:
111
+ # Re-raise authentication exceptions so they can be caught by tests and calling code
112
+ logger.error(f"Authentication failed: {e}")
113
+ raise
114
+
115
+ self.api.v4.set_auth_token(response["token"])
116
+
117
+ tracing_config = self.config.dict()
118
+ tracing_config["project_id"] = response["project_id"]
119
+
120
+ tracer.initialize_from_config(tracing_config, jwt=response["token"])
121
+
122
+ if self.config.instrument_llm_calls:
123
+ instrument_all()
124
+
125
+ # self._initialized = True # Set initialized to True here - MOVED to after trace start attempt
126
+
127
+ global _atexit_registered
128
+ if not _atexit_registered:
129
+ atexit.register(_end_init_trace_atexit) # Register new atexit handler
130
+ _atexit_registered = True
131
+
132
+ # Auto-start trace if configured
133
+ if self.config.auto_start_session:
134
+ if self._init_trace_context is None or not self._init_trace_context.span.is_recording():
135
+ logger.debug("Auto-starting init trace.")
136
+ trace_name = self.config.trace_name or "default"
137
+ self._init_trace_context = tracer.start_trace(
138
+ trace_name=trace_name,
139
+ tags=list(self.config.default_tags) if self.config.default_tags else None,
140
+ is_init_trace=True,
141
+ )
142
+ if self._init_trace_context:
143
+ self._legacy_session_for_init_trace = Session(self._init_trace_context)
144
+
145
+ # For backward compatibility, also update the global references in legacy and client modules
146
+ # These globals are what old code might have been using via agentops.legacy.get_session() or similar indirect access.
147
+ global _client_init_trace_context, _client_legacy_session_for_init_trace
148
+ _client_init_trace_context = self._init_trace_context
149
+ _client_legacy_session_for_init_trace = self._legacy_session_for_init_trace
150
+
151
+ # Update legacy module's _current_session and _current_trace_context
152
+ # This is tricky; direct access to another module's globals is not ideal.
153
+ # Prefer explicit calls if possible, but for maximum BC:
154
+ try:
155
+ import agentops.legacy
156
+
157
+ agentops.legacy._current_session = self._legacy_session_for_init_trace
158
+ agentops.legacy._current_trace_context = self._init_trace_context
159
+ except ImportError:
160
+ pass # Should not happen
161
+
162
+ else:
163
+ logger.error("Failed to start the auto-init trace.")
164
+ # Even if auto-start fails, core services up to the tracer might be initialized.
165
+ # Set self.initialized to True if tracer is up, but return None.
166
+ self._initialized = tracer.initialized
167
+ return None # Failed to start trace
168
+
169
+ self._initialized = True # Successfully initialized and auto-trace started (if configured)
170
+ # For backward compatibility, return the legacy session wrapper when auto_start_session=True
171
+ return self._legacy_session_for_init_trace
172
+ else:
173
+ logger.debug("Auto-start session is disabled. No init trace started by client.")
174
+ self._initialized = True # Successfully initialized, just no auto-trace
175
+ return None # No auto-session, so return None
176
+
177
+ def configure(self, **kwargs: Any) -> None:
178
+ """Update client configuration"""
179
+ self.config.configure(**kwargs)
180
+
181
+ @property
182
+ def initialized(self) -> bool:
183
+ return self._initialized
184
+
185
+ @initialized.setter
186
+ def initialized(self, value: bool) -> None:
187
+ if self._initialized and self._initialized != value:
188
+ # Allow re-setting to False if we are intentionally re-initializing
189
+ # This logic is now partly in init() to handle re-init cases
190
+ pass
191
+ self._initialized = value
192
+
193
+ # ------------------------------------------------------------
194
+ # Remove the old __instance = None at the end of the class definition if it's a repeat
195
+ # __instance = None # This was a class variable, should be defined once
196
+
197
+ # Make _init_trace_context and _legacy_session_for_init_trace accessible
198
+ # to the atexit handler if it becomes a static/class method or needs access
199
+ # For now, the atexit handler is global and uses global vars copied from these.
200
+
201
+ # Deprecate and remove the old global _active_session from this module.
202
+ # Consumers should use agentops.start_trace() or rely on the auto-init trace.
203
+ # For a transition, the auto-init trace's legacy wrapper is set to legacy module's globals.
204
+
205
+
206
+ # Ensure the global _active_session (if needed for some very old compatibility) points to the client's legacy session for init trace.
207
+ # This specific global _active_session in client.py is problematic and should be phased out.
208
+ # For now, _client_legacy_session_for_init_trace is the primary global for the auto-init trace's legacy Session.
209
+
210
+ # Remove the old global _active_session defined at the top of this file if it's no longer the primary mechanism.
211
+ # The new globals _client_init_trace_context and _client_legacy_session_for_init_trace handle the auto-init trace.
File without changes