cloudbase-agent-observability 0.1.0__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.
@@ -0,0 +1,104 @@
1
+ """
2
+ Cloudbase Agent Observability - OpenTelemetry-based tracing with OpenInference semantic conventions.
3
+
4
+ This package provides observability capabilities for Cloudbase Agent agents using OpenTelemetry
5
+ and OpenInference semantic conventions.
6
+ """
7
+
8
+ from cloudbase_agent.observability.span_wrapper import (
9
+ Observation,
10
+ ObservationAgent,
11
+ ObservationChain,
12
+ ObservationEmbedding,
13
+ ObservationLLM,
14
+ ObservationRetriever,
15
+ ObservationReranker,
16
+ ObservationSpan,
17
+ ObservationTool,
18
+ )
19
+ from cloudbase_agent.observability.tracer import get_tracer, set_tracer_provider
20
+ from cloudbase_agent.observability.attributes import (
21
+ create_observation_attributes,
22
+ create_trace_attributes,
23
+ )
24
+ from cloudbase_agent.observability.types import (
25
+ ObservationLevel,
26
+ ObservationType,
27
+ )
28
+ from cloudbase_agent.observability.api import (
29
+ start_observation,
30
+ update_active_trace,
31
+ get_active_trace_id,
32
+ get_active_span_id,
33
+ )
34
+ from cloudbase_agent.observability.constants import (
35
+ OpenInferenceAttributes,
36
+ OpenInferenceSpanKind,
37
+ OtelSpanAttributes,
38
+ OBSERVABILITY_TRACER_NAME,
39
+ OBSERVABILITY_SDK_NAME,
40
+ )
41
+
42
+ # Server utilities (optional import - requires opentelemetry-sdk)
43
+ try:
44
+ from cloudbase_agent.observability.server import (
45
+ setup_observability,
46
+ setup_observability_async,
47
+ BatchConfig,
48
+ ConsoleTraceConfig,
49
+ OTLPTraceConfig,
50
+ CustomTraceConfig,
51
+ ObservabilityConfig,
52
+ )
53
+ SERVER_AVAILABLE = True
54
+ except ImportError:
55
+ SERVER_AVAILABLE = False
56
+
57
+ __all__ = [
58
+ # Span wrapper classes
59
+ "Observation",
60
+ "ObservationSpan",
61
+ "ObservationLLM",
62
+ "ObservationEmbedding",
63
+ "ObservationAgent",
64
+ "ObservationTool",
65
+ "ObservationChain",
66
+ "ObservationRetriever",
67
+ "ObservationReranker",
68
+ # Tracer functions
69
+ "get_tracer",
70
+ "set_tracer_provider",
71
+ # Attribute functions
72
+ "create_observation_attributes",
73
+ "create_trace_attributes",
74
+ # Types
75
+ "ObservationType",
76
+ "ObservationLevel",
77
+ # API functions
78
+ "start_observation",
79
+ "update_active_trace",
80
+ "get_active_trace_id",
81
+ "get_active_span_id",
82
+ # Constants
83
+ "OpenInferenceAttributes",
84
+ "OpenInferenceSpanKind",
85
+ "OtelSpanAttributes",
86
+ "OBSERVABILITY_TRACER_NAME",
87
+ "OBSERVABILITY_SDK_NAME",
88
+ ]
89
+
90
+ # Server exports (available only when opentelemetry-sdk is installed)
91
+ if SERVER_AVAILABLE:
92
+ __all__.extend([
93
+ # Server setup API
94
+ "setup_observability",
95
+ "setup_observability_async",
96
+ # Server configuration types
97
+ "BatchConfig",
98
+ "ConsoleTraceConfig",
99
+ "OTLPTraceConfig",
100
+ "CustomTraceConfig",
101
+ "ObservabilityConfig",
102
+ ])
103
+
104
+ __version__ = "0.1.0"
@@ -0,0 +1,302 @@
1
+ """
2
+ Public API for Cloudbase Agent observability.
3
+
4
+ Provides convenient functions for creating and managing observations.
5
+ """
6
+
7
+ from datetime import datetime
8
+ from typing import Any, Dict, Optional, Union, overload
9
+
10
+ from opentelemetry import context, trace
11
+ from opentelemetry.trace import SpanContext, Status, StatusCode
12
+ from opentelemetry.util.types import AttributeValue
13
+
14
+ from cloudbase_agent.observability.types import (
15
+ ObservationType,
16
+ BaseSpanAttributes,
17
+ LLMAttributes,
18
+ EmbeddingAttributes,
19
+ AgentAttributes,
20
+ ToolAttributes,
21
+ ChainAttributes,
22
+ RetrieverAttributes,
23
+ RerankerAttributes,
24
+ EvaluatorAttributes,
25
+ GuardrailAttributes,
26
+ ObservationAttributes,
27
+ TraceAttributes,
28
+ )
29
+ from cloudbase_agent.observability.span_wrapper import (
30
+ Observation,
31
+ ObservationSpan,
32
+ ObservationLLM,
33
+ ObservationEmbedding,
34
+ ObservationAgent,
35
+ ObservationTool,
36
+ ObservationChain,
37
+ ObservationRetriever,
38
+ ObservationReranker,
39
+ ObservationEvaluator,
40
+ ObservationGuardrail,
41
+ )
42
+ from cloudbase_agent.observability.tracer import get_tracer
43
+ from cloudbase_agent.observability.attributes import (
44
+ update_active_trace as _update_active_trace,
45
+ get_active_trace_id as _get_active_trace_id,
46
+ get_active_span_id as _get_active_span_id,
47
+ )
48
+
49
+
50
+ def _create_parent_context(
51
+ parent_span_context: Optional[SpanContext],
52
+ ) -> context.Context:
53
+ """Create a parent context from a span context.
54
+
55
+ Args:
56
+ parent_span_context: The span context to use as parent.
57
+
58
+ Returns:
59
+ The created context (returns current context if no parent).
60
+ """
61
+ # Create a NonRecordingSpan with the parent context
62
+ from opentelemetry.trace import NonRecordingSpan
63
+ parent_span = NonRecordingSpan(parent_span_context)
64
+
65
+ # Use the parent context to set the span in context
66
+ return trace.set_span_in_context(parent_span)
67
+
68
+
69
+ @overload
70
+ def start_observation(
71
+ name: str,
72
+ attributes: LLMAttributes,
73
+ *,
74
+ as_type: "Literal['llm']",
75
+ parent_span_context: Optional[SpanContext] = None,
76
+ start_time: Optional[datetime] = None,
77
+ ) -> ObservationLLM:
78
+ ...
79
+
80
+
81
+ @overload
82
+ def start_observation(
83
+ name: str,
84
+ attributes: EmbeddingAttributes,
85
+ *,
86
+ as_type: "Literal['embedding']",
87
+ parent_span_context: Optional[SpanContext] = None,
88
+ start_time: Optional[datetime] = None,
89
+ ) -> ObservationEmbedding:
90
+ ...
91
+
92
+
93
+ @overload
94
+ def start_observation(
95
+ name: str,
96
+ attributes: AgentAttributes,
97
+ *,
98
+ as_type: "Literal['agent']",
99
+ parent_span_context: Optional[SpanContext] = None,
100
+ start_time: Optional[datetime] = None,
101
+ ) -> ObservationAgent:
102
+ ...
103
+
104
+
105
+ @overload
106
+ def start_observation(
107
+ name: str,
108
+ attributes: ToolAttributes,
109
+ *,
110
+ as_type: "Literal['tool']",
111
+ parent_span_context: Optional[SpanContext] = None,
112
+ start_time: Optional[datetime] = None,
113
+ ) -> ObservationTool:
114
+ ...
115
+
116
+
117
+ @overload
118
+ def start_observation(
119
+ name: str,
120
+ attributes: ChainAttributes,
121
+ *,
122
+ as_type: "Literal['chain']",
123
+ parent_span_context: Optional[SpanContext] = None,
124
+ start_time: Optional[datetime] = None,
125
+ ) -> ObservationChain:
126
+ ...
127
+
128
+
129
+ @overload
130
+ def start_observation(
131
+ name: str,
132
+ attributes: RetrieverAttributes,
133
+ *,
134
+ as_type: "Literal['retriever']",
135
+ parent_span_context: Optional[SpanContext] = None,
136
+ start_time: Optional[datetime] = None,
137
+ ) -> ObservationRetriever:
138
+ ...
139
+
140
+
141
+ @overload
142
+ def start_observation(
143
+ name: str,
144
+ attributes: RerankerAttributes,
145
+ *,
146
+ as_type: "Literal['reranker']",
147
+ parent_span_context: Optional[SpanContext] = None,
148
+ start_time: Optional[datetime] = None,
149
+ ) -> ObservationReranker:
150
+ ...
151
+
152
+
153
+ @overload
154
+ def start_observation(
155
+ name: str,
156
+ attributes: EvaluatorAttributes,
157
+ *,
158
+ as_type: "Literal['evaluator']",
159
+ parent_span_context: Optional[SpanContext] = None,
160
+ start_time: Optional[datetime] = None,
161
+ ) -> ObservationEvaluator:
162
+ ...
163
+
164
+
165
+ @overload
166
+ def start_observation(
167
+ name: str,
168
+ attributes: GuardrailAttributes,
169
+ *,
170
+ as_type: "Literal['guardrail']",
171
+ parent_span_context: Optional[SpanContext] = None,
172
+ start_time: Optional[datetime] = None,
173
+ ) -> ObservationGuardrail:
174
+ ...
175
+
176
+
177
+ @overload
178
+ def start_observation(
179
+ name: str,
180
+ attributes: Optional[BaseSpanAttributes] = None,
181
+ *,
182
+ as_type: "Literal['span']" = "span",
183
+ parent_span_context: Optional[SpanContext] = None,
184
+ start_time: Optional[datetime] = None,
185
+ ) -> ObservationSpan:
186
+ ...
187
+
188
+
189
+ def start_observation(
190
+ name: str,
191
+ attributes: Optional[ObservationAttributes] = None,
192
+ *,
193
+ as_type: ObservationType = "span",
194
+ parent_span_context: Optional[SpanContext] = None,
195
+ start_time: Optional[datetime] = None,
196
+ ) -> Observation:
197
+ """Create and start a new Cloudbase Agent observation.
198
+
199
+ Supports multiple observation types with full type safety:
200
+ - **span**: General-purpose operations (default)
201
+ - **llm**: LLM calls and AI model interactions
202
+ - **embedding**: Text embedding and vector operations
203
+ - **agent**: AI agent workflows
204
+ - **tool**: Individual tool calls
205
+ - **chain**: Multi-step processes
206
+ - **retriever**: Document retrieval
207
+ - **reranker**: Result reranking
208
+ - **evaluator**: Quality assessment
209
+ - **guardrail**: Safety checks
210
+
211
+ Args:
212
+ name: Descriptive name for the observation.
213
+ attributes: Type-specific attributes.
214
+ as_type: Type of observation to create. Defaults to 'span'.
215
+ parent_span_context: Optional parent span context for linking.
216
+ start_time: Optional custom start time.
217
+
218
+ Returns:
219
+ Strongly-typed observation object.
220
+
221
+ Examples:
222
+ LLM observation:
223
+ >>> llm = start_observation('openai-gpt-4', {
224
+ ... 'input': [{'role': 'user', 'content': 'Hello'}],
225
+ ... 'model': 'gpt-4',
226
+ ... 'model_parameters': {'temperature': 0.7}
227
+ ... }, as_type='llm')
228
+
229
+ Tool observation:
230
+ >>> tool = start_observation('weather-api', {
231
+ ... 'input': {'location': 'SF'}
232
+ ... }, as_type='tool')
233
+ """
234
+ tracer = get_tracer()
235
+
236
+ # Start the OTEL span with optional parent context
237
+ if parent_span_context:
238
+ # Use start_as_current_span with parent context for proper context propagation
239
+ from opentelemetry.trace import NonRecordingSpan
240
+ parent_span = NonRecordingSpan(parent_span_context)
241
+ parent_ctx = trace.set_span_in_context(parent_span)
242
+
243
+ # Use context manager to properly attach/detach
244
+ token = context.attach(parent_ctx)
245
+ otel_span = tracer.start_span(name, start_time=start_time)
246
+ # Keep context attached - the span wrapper will manage it
247
+ else:
248
+ token = None
249
+ otel_span = tracer.start_span(name, start_time=start_time)
250
+
251
+ # Store token in span for cleanup when span ends
252
+ if token:
253
+ otel_span._agkit_context_token = token
254
+
255
+ # Create the appropriate observation wrapper
256
+ if as_type == "llm":
257
+ return ObservationLLM(otel_span, attributes)
258
+ elif as_type == "embedding":
259
+ return ObservationEmbedding(otel_span, attributes)
260
+ elif as_type == "agent":
261
+ return ObservationAgent(otel_span, attributes)
262
+ elif as_type == "tool":
263
+ return ObservationTool(otel_span, attributes)
264
+ elif as_type == "chain":
265
+ return ObservationChain(otel_span, attributes)
266
+ elif as_type == "retriever":
267
+ return ObservationRetriever(otel_span, attributes)
268
+ elif as_type == "reranker":
269
+ return ObservationReranker(otel_span, attributes)
270
+ elif as_type == "evaluator":
271
+ return ObservationEvaluator(otel_span, attributes)
272
+ elif as_type == "guardrail":
273
+ return ObservationGuardrail(otel_span, attributes)
274
+ else:
275
+ return ObservationSpan(otel_span, attributes)
276
+
277
+
278
+ def update_active_trace(attributes: TraceAttributes) -> None:
279
+ """Update the currently active trace with new attributes.
280
+
281
+ Args:
282
+ attributes: Trace attributes to set.
283
+ """
284
+ _update_active_trace(attributes)
285
+
286
+
287
+ def get_active_trace_id() -> Optional[str]:
288
+ """Get the current active trace ID.
289
+
290
+ Returns:
291
+ The trace ID as a hex string, or None if no active span.
292
+ """
293
+ return _get_active_trace_id()
294
+
295
+
296
+ def get_active_span_id() -> Optional[str]:
297
+ """Get the current active observation ID.
298
+
299
+ Returns:
300
+ The span ID as a hex string, or None if no active span.
301
+ """
302
+ return _get_active_span_id()