|
13 | 13 | from sap_cloud_sdk.core.telemetry.constants import ( |
14 | 14 | REQUEST_COUNTER_NAME, |
15 | 15 | ERROR_COUNTER_NAME, |
16 | | - LLM_TOKEN_HISTOGRAM_NAME, |
17 | 16 | ATTR_SAP_TENANT_ID, |
18 | 17 | ATTR_CAPABILITY, |
19 | 18 | ATTR_FUNCTIONALITY, |
20 | 19 | ATTR_SOURCE, |
21 | 20 | ATTR_DEPRECATED, |
22 | | - ATTR_GENAI_REQUEST_MODEL, |
23 | | - ATTR_GENAI_OPERATION_NAME, |
24 | | - ATTR_GENAI_TOKEN_TYPE, |
25 | | - ATTR_GENAI_PROVIDER, |
26 | 21 | ) |
27 | 22 | from sap_cloud_sdk.core.telemetry.module import Module |
28 | 23 |
|
|
32 | 27 | # Global metric instruments |
33 | 28 | _request_counter: Optional[metrics.Counter] = None |
34 | 29 | _error_counter: Optional[metrics.Counter] = None |
35 | | -_aicore_token_histogram: Optional[metrics.Histogram] = None |
36 | 30 |
|
37 | 31 | # Context variable for per-request tenant ID |
38 | 32 | _tenant_id_var: ContextVar[str] = ContextVar("tenant_id", default="") |
@@ -136,141 +130,6 @@ def record_error_metric( |
136 | 130 | logger.debug(f"Failed to record error metric: {e}") |
137 | 131 |
|
138 | 132 |
|
139 | | -def record_aicore_metric( |
140 | | - model_name: str, |
141 | | - provider: str, |
142 | | - operation_name: str, |
143 | | - input_tokens: int, |
144 | | - output_tokens: int, |
145 | | - custom_attributes: Optional[Dict[str, Any]] = None, |
146 | | -) -> None: |
147 | | - """Record token usage metrics for GenAI model API calls. |
148 | | -
|
149 | | - This function records token consumption for Generative AI model API calls following |
150 | | - OpenTelemetry GenAI semantic conventions. It creates two separate histogram observations: |
151 | | - one for input tokens and one for output tokens, each differentiated by the gen_ai.token.type |
152 | | - attribute. The metrics include all default SDK attributes (service instance, SDK version, |
153 | | - deployment metadata, etc.) along with the model name. |
154 | | -
|
155 | | - Args: |
156 | | - model_name: The name of the GenAI model (e.g., "gpt-4", "claude-3-opus") |
157 | | - provider: The name of the GenAI provider (e.g., "openai", "anthropic", "sap-aicore") |
158 | | - operation_name: The type of GenAI operation. Well-known values include: |
159 | | - - "chat": Chat completion operation (e.g., OpenAI Chat API) |
160 | | - - "text_completion": Text completion operation |
161 | | - - "embeddings": Embeddings operation (e.g., OpenAI Create embeddings API) |
162 | | - - "generate_content": Multimodal content generation (e.g., Gemini Generate Content) |
163 | | - - "create_agent": Create GenAI agent |
164 | | - - "invoke_agent": Invoke GenAI agent |
165 | | - - "execute_tool": Execute a tool |
166 | | - Custom values are also allowed. |
167 | | - input_tokens: Number of input/prompt tokens consumed |
168 | | - output_tokens: Number of output/completion tokens generated |
169 | | - custom_attributes: Optional dictionary of additional custom attributes to include |
170 | | - in the metric. These will be merged with the standard attributes. |
171 | | -
|
172 | | - Example: |
173 | | - ```python |
174 | | - from sap_cloud_sdk.core.telemetry import record_aicore_metric |
175 | | -
|
176 | | - # Chat completion |
177 | | - record_aicore_metric( |
178 | | - model_name="gpt-4", |
179 | | - provider="openai", |
180 | | - operation_name="chat", |
181 | | - input_tokens=150, |
182 | | - output_tokens=75 |
183 | | - ) |
184 | | -
|
185 | | - # With additional custom attributes |
186 | | - record_aicore_metric( |
187 | | - model_name="gpt-4", |
188 | | - provider="openai", |
189 | | - operation_name="chat", |
190 | | - input_tokens=150, |
191 | | - output_tokens=75, |
192 | | - custom_attributes={ |
193 | | - "user_id": "user123", |
194 | | - "session_id": "session456" |
195 | | - } |
196 | | - ) |
197 | | - ``` |
198 | | - """ |
199 | | - global _aicore_token_histogram |
200 | | - |
201 | | - # Lazy initialization of metrics |
202 | | - if _aicore_token_histogram is None: |
203 | | - _initialize_aicore_metrics() |
204 | | - if _aicore_token_histogram is None: |
205 | | - return |
206 | | - |
207 | | - try: |
208 | | - base_attributes = _genai_base_attributes(model_name, provider, operation_name) |
209 | | - |
210 | | - # Merge in any additional user-provided attributes |
211 | | - if custom_attributes: |
212 | | - base_attributes.update(custom_attributes) |
213 | | - |
214 | | - # Record input tokens as separate histogram observation |
215 | | - input_attributes = base_attributes.copy() |
216 | | - input_attributes[ATTR_GENAI_TOKEN_TYPE] = "input" |
217 | | - _aicore_token_histogram.record(input_tokens, input_attributes) |
218 | | - |
219 | | - # Record output tokens as separate histogram observation |
220 | | - output_attributes = base_attributes.copy() |
221 | | - output_attributes[ATTR_GENAI_TOKEN_TYPE] = "output" |
222 | | - _aicore_token_histogram.record(output_tokens, output_attributes) |
223 | | - |
224 | | - except Exception as e: |
225 | | - logger.debug(f"Failed to record GenAI metric: {e}") |
226 | | - |
227 | | - |
228 | | -def _genai_base_attributes( |
229 | | - model_name: str, provider: str, operation_name: str |
230 | | -) -> Dict[str, Any]: |
231 | | - """Get base attributes for GenAI metrics. |
232 | | -
|
233 | | - Args: |
234 | | - model_name: The name of the LLM model |
235 | | - provider: The name of the GenAI provider |
236 | | - operation_name: The type of GenAI operation |
237 | | -
|
238 | | - Returns: |
239 | | - Dictionary of attributes with default SDK attributes plus GenAI-specific ones. |
240 | | - """ |
241 | | - # Start with default SDK attributes for AI Core module |
242 | | - attributes = default_attributes( |
243 | | - module=Module.AICORE, source=None, operation="model_call", deprecated=False |
244 | | - ) |
245 | | - |
246 | | - # Add GenAI-specific attributes |
247 | | - attributes[ATTR_GENAI_REQUEST_MODEL] = model_name |
248 | | - attributes[ATTR_GENAI_PROVIDER] = provider |
249 | | - attributes[ATTR_GENAI_OPERATION_NAME] = operation_name |
250 | | - |
251 | | - return attributes |
252 | | - |
253 | | - |
254 | | -def _initialize_aicore_metrics() -> None: |
255 | | - """Initialize GenAI-specific metric instruments.""" |
256 | | - global _aicore_token_histogram |
257 | | - |
258 | | - try: |
259 | | - meter = get_meter() |
260 | | - |
261 | | - # GenAI token usage histogram |
262 | | - _aicore_token_histogram = meter.create_histogram( |
263 | | - name=LLM_TOKEN_HISTOGRAM_NAME, |
264 | | - description="Token usage for GenAI model requests", |
265 | | - unit="{tokens}", |
266 | | - ) |
267 | | - |
268 | | - logger.debug("GenAI telemetry metrics initialized successfully") |
269 | | - |
270 | | - except Exception as e: |
271 | | - logger.error(f"Failed to initialize GenAI telemetry metrics: {e}") |
272 | | - |
273 | | - |
274 | 133 | def default_attributes( |
275 | 134 | module: Module, source: Optional[Module], operation: str, deprecated: bool = False |
276 | 135 | ) -> Dict[str, Any]: |
|
0 commit comments