UNPKG

openlit

Version:

OpenTelemetry-native Auto instrumentation library for monitoring LLM Applications, facilitating the integration of observability into your GenAI-driven projects

663 lines 31.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PassthroughTracer = exports.OPERATION_MAP = exports.adkWorkflowActive = void 0; exports.isAdkWorkflowActive = isAdkWorkflowActive; exports.getOperationType = getOperationType; exports.getSpanKind = getSpanKind; exports.generateSpanName = generateSpanName; exports.resolveModelString = resolveModelString; exports.extractModelName = extractModelName; exports.resolveServerInfo = resolveServerInfo; exports.setCommonSpanAttributes = setCommonSpanAttributes; exports.captureInputMessages = captureInputMessages; exports.captureOutputMessages = captureOutputMessages; exports.captureEventOutput = captureEventOutput; exports.extractTokenUsage = extractTokenUsage; exports.enrichLlmSpan = enrichLlmSpan; exports.enrichToolSpan = enrichToolSpan; exports.enrichMergedToolSpan = enrichMergedToolSpan; exports.setRunnerAgentAttributes = setRunnerAgentAttributes; exports.setAgentAttributes = setAgentAttributes; exports.processGoogleAdkResponse = processGoogleAdkResponse; exports.recordGoogleAdkMetrics = recordGoogleAdkMetrics; const api_1 = require("@opentelemetry/api"); const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); const async_hooks_1 = require("async_hooks"); const semantic_convention_1 = __importDefault(require("../../semantic-convention")); const config_1 = __importDefault(require("../../config")); const constant_1 = require("../../constant"); const helpers_1 = require("../../helpers"); /** * Prevents Runner.run_async from creating a second invoke_agent span * when called internally by Runner.run (mirrors Python _ADK_WORKFLOW_ACTIVE). */ exports.adkWorkflowActive = new async_hooks_1.AsyncLocalStorage(); function isAdkWorkflowActive() { return exports.adkWorkflowActive.getStore() === true; } // --------------------------------------------------------------------------- // OTel GenAI operation mapping (mirrors Python OPERATION_MAP) // --------------------------------------------------------------------------- exports.OPERATION_MAP = { agent_init: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CREATE_AGENT, runner_run_async: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT, runner_run: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT, runner_run_live: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT, agent_run_async: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT, }; const SPAN_KIND_MAP = { [semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CREATE_AGENT]: api_1.SpanKind.CLIENT, [semantic_convention_1.default.GEN_AI_OPERATION_TYPE_FRAMEWORK]: api_1.SpanKind.INTERNAL, [semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT]: api_1.SpanKind.INTERNAL, [semantic_convention_1.default.GEN_AI_OPERATION_TYPE_TOOLS]: api_1.SpanKind.INTERNAL, [semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT]: api_1.SpanKind.CLIENT, }; function getOperationType(endpoint) { return exports.OPERATION_MAP[endpoint] ?? semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT; } function getSpanKind(operationType) { return SPAN_KIND_MAP[operationType] ?? api_1.SpanKind.INTERNAL; } // --------------------------------------------------------------------------- // Span name generation (mirrors Python generate_span_name) // --------------------------------------------------------------------------- function generateSpanName(endpoint, instance) { if (endpoint === 'agent_init') { const name = instance?.name ?? 'agent'; return `create_agent ${name}`; } if (endpoint === 'runner_run_async' || endpoint === 'runner_run' || endpoint === 'runner_run_live') { const appName = instance?.app_name ?? instance?._app_name ?? 'google_adk'; return `invoke_agent ${appName}`; } if (endpoint === 'agent_run_async') { const name = instance?.name ?? 'agent'; return `invoke_agent ${name}`; } return `${getOperationType(endpoint)} ${endpoint}`; } // --------------------------------------------------------------------------- // PassthroughTracer (mirrors Python _PassthroughTracer) // --------------------------------------------------------------------------- /** * Drop-in replacement for ADK's tracer objects. Overrides * `startActiveSpan` to yield the current span instead of creating a new one, * letting OpenLIT own top-level spans while ADK's code still runs. */ class PassthroughTracer { constructor(wrapped) { this._wrapped = wrapped; } startActiveSpan(...args) { const fn = args[args.length - 1]; if (typeof fn === 'function') { const currentSpan = api_1.trace.getActiveSpan(); return fn(currentSpan); } return undefined; } startSpan(...args) { return this._wrapped.startSpan(...args); } } exports.PassthroughTracer = PassthroughTracer; // --------------------------------------------------------------------------- // Model extraction (mirrors Python _resolve_model_string / extract_model_name) // --------------------------------------------------------------------------- function resolveModelString(modelObj) { if (typeof modelObj === 'string') return modelObj; if (!modelObj) return null; const modelName = modelObj.model_name ?? modelObj.modelName; if (typeof modelName === 'string') return modelName; const inner = modelObj.model; if (typeof inner === 'string') return inner; return null; } function extractModelName(instance) { try { const model = instance?.model; if (model) { const resolved = resolveModelString(model); if (resolved) return resolved; } const rootAgent = instance?.agent; if (rootAgent) return extractModelName(rootAgent); } catch { /* ignore */ } return 'unknown'; } // --------------------------------------------------------------------------- // Server address resolution (mirrors Python resolve_server_info) // --------------------------------------------------------------------------- const PREFIX_TO_PROVIDER = { anthropic: 'anthropic', claude: 'anthropic', openai: 'openai', gpt: 'openai', mistral: 'mistral_ai', cohere: 'cohere', }; function detectProviderFromModelStr(modelStr) { if (!modelStr) return null; const lower = modelStr.toLowerCase(); const prefix = lower.includes('/') ? lower.split('/')[0] : lower.split('-')[0]; const providerKey = PREFIX_TO_PROVIDER[prefix]; if (!providerKey) return null; const [addr, port] = (0, helpers_1.getServerAddressForProvider)(providerKey); if (!addr) return null; return [addr, port, providerKey]; } function resolveServerInfo(instance, modelName) { if (modelName) { const detected = detectProviderFromModelStr(modelName); if (detected) return detected; } if (instance) { try { let modelObj = instance.model; if (!modelObj) { const agent = instance.agent; if (agent) modelObj = agent.model; } if (modelObj) { const resolved = resolveModelString(modelObj); if (resolved) { const detected = detectProviderFromModelStr(resolved); if (detected) return detected; } } } catch { /* ignore */ } } const useVertex = (process.env.GOOGLE_GENAI_USE_VERTEXAI || '').toLowerCase(); if (useVertex === 'true' || useVertex === '1') { const [addr, port] = (0, helpers_1.getServerAddressForProvider)('gcp.vertex_ai'); return [addr, port, 'gcp.vertex_ai']; } const [addr, port] = (0, helpers_1.getServerAddressForProvider)('gcp.gemini'); return [addr, port, 'gcp.gemini']; } // --------------------------------------------------------------------------- // Common span attributes (mirrors Python common_framework_span_attributes) // --------------------------------------------------------------------------- function setCommonSpanAttributes(span, operationType) { span.setAttribute(semantic_conventions_1.ATTR_TELEMETRY_SDK_NAME, constant_1.SDK_NAME); span.setAttribute(semantic_convention_1.default.ATTR_DEPLOYMENT_ENVIRONMENT, config_1.default.environment || 'default'); span.setAttribute(semantic_conventions_1.ATTR_SERVICE_NAME, config_1.default.applicationName || 'default'); span.setAttribute(semantic_convention_1.default.GEN_AI_SDK_VERSION, constant_1.SDK_VERSION); span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, operationType); span.setAttribute(semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL, semantic_convention_1.default.GEN_AI_SYSTEM_GOOGLE_ADK); } // --------------------------------------------------------------------------- // Content extraction helpers (mirrors Python _extract_parts) // --------------------------------------------------------------------------- function truncateContent(str, maxLen) { const limit = maxLen ?? config_1.default.maxContentLength; if (limit && str.length > limit) return str.slice(0, limit) + '...'; return str; } function extractParts(parts) { const textParts = []; const toolCalls = []; const toolResponses = []; for (const part of parts || []) { const text = part?.text; if (text) textParts.push(truncateContent(String(text))); const fc = part?.function_call ?? part?.functionCall; if (fc) { const entry = { name: fc.name ?? '', id: fc.id ?? '', }; const fcArgs = fc.args; if (fcArgs) { try { entry.arguments = typeof fcArgs === 'object' ? JSON.stringify(fcArgs) : String(fcArgs); } catch { entry.arguments = String(fcArgs); } } toolCalls.push(entry); } const fr = part?.function_response ?? part?.functionResponse; if (fr) { const respEntry = { name: fr.name ?? '', id: fr.id ?? '', }; const frResp = fr.response; if (frResp != null) { try { respEntry.content = typeof frResp === 'object' ? JSON.stringify(frResp) : String(frResp); } catch { respEntry.content = String(frResp); } } toolResponses.push(respEntry); } } return { textParts, toolCalls, toolResponses }; } // --------------------------------------------------------------------------- // Input/Output message capture (mirrors Python capture_input_messages / capture_output_messages) // --------------------------------------------------------------------------- function captureInputMessages(span, llmRequest, captureContent) { if (!captureContent) return; try { const contents = llmRequest?.contents; if (!contents) return; const messages = []; for (const content of contents.slice(0, 20)) { const role = content?.role ?? 'user'; const rawParts = content?.parts ?? []; const { textParts, toolCalls, toolResponses } = extractParts(rawParts); const parts = []; for (const text of textParts) parts.push({ type: 'text', content: text }); for (const tc of toolCalls) { parts.push({ type: 'tool_call', id: tc.id, name: tc.name, arguments: tc.arguments ?? '' }); } for (const tr of toolResponses) { parts.push({ type: 'tool_call_response', id: tr.id, response: tr.content ?? '' }); } if (parts.length > 0) messages.push({ role: String(role), parts }); } if (messages.length > 0) { span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, JSON.stringify(messages)); } } catch { /* ignore */ } } function captureOutputMessages(span, llmResponse, captureContent, finishReason = 'stop') { if (!captureContent) return; try { const content = llmResponse?.content; if (!content) return; const rawParts = content.parts ?? []; const { textParts, toolCalls } = extractParts(rawParts); const parts = []; for (const text of textParts) parts.push({ type: 'text', content: text }); for (const tc of toolCalls) { parts.push({ type: 'tool_call', id: tc.id, name: tc.name, arguments: tc.arguments ?? '' }); } if (parts.length > 0) { const messages = [{ role: 'assistant', parts, finish_reason: finishReason }]; span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, JSON.stringify(messages)); } } catch { /* ignore */ } } function captureEventOutput(span, event, captureContent) { if (!captureContent) return; try { const content = event?.content; if (!content) return; const rawParts = content.parts ?? []; const { textParts, toolCalls } = extractParts(rawParts); const parts = []; for (const text of textParts) parts.push({ type: 'text', content: text }); for (const tc of toolCalls) { parts.push({ type: 'tool_call', id: tc.id, name: tc.name, arguments: tc.arguments ?? '' }); } if (parts.length > 0) { const messages = [{ role: 'assistant', parts, finish_reason: 'stop' }]; span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, JSON.stringify(messages)); } } catch { /* ignore */ } } function extractTokenUsage(llmResponse) { try { const usage = llmResponse?.usage_metadata ?? llmResponse?.usageMetadata; if (!usage) return {}; return { inputTokens: usage.prompt_token_count ?? usage.promptTokenCount, outputTokens: usage.candidates_token_count ?? usage.candidatesTokenCount, reasoningTokens: usage.thoughts_token_count ?? usage.thoughtsTokenCount, cachedTokens: usage.cached_content_token_count ?? usage.cachedContentTokenCount, totalTokens: usage.total_token_count ?? usage.totalTokenCount, }; } catch { return {}; } } // --------------------------------------------------------------------------- // Output type detection (mirrors Python _determine_output_type) // --------------------------------------------------------------------------- function determineOutputType(llmResponse) { try { const content = llmResponse?.content; if (content) { for (const part of (content.parts ?? [])) { if (part?.function_call || part?.functionCall) return 'tool_calls'; } } } catch { /* ignore */ } return 'text'; } // --------------------------------------------------------------------------- // LLM span enrichment (mirrors Python enrich_llm_span) // --------------------------------------------------------------------------- function enrichLlmSpan(span, llmRequest, llmResponse, captureMessageContent) { try { const requestModel = llmRequest?.model; const modelStr = requestModel ? String(requestModel) : null; const [serverAddress, serverPort, providerName] = resolveServerInfo(undefined, modelStr); span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CHAT); span.setAttribute(semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL, providerName); span.setAttribute(semantic_convention_1.default.SERVER_ADDRESS, serverAddress); span.setAttribute(semantic_convention_1.default.SERVER_PORT, serverPort); if (llmRequest) { if (modelStr) span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MODEL, modelStr); const config = llmRequest.config; if (config) { const temp = config.temperature; if (temp != null) span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TEMPERATURE, Number(temp)); const topP = config.top_p ?? config.topP; if (topP != null) span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_TOP_P, Number(topP)); const maxTokens = config.max_output_tokens ?? config.maxOutputTokens; if (maxTokens != null) span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MAX_TOKENS, Number(maxTokens)); } if (captureMessageContent) { const sysInstr = config?.system_instruction ?? config?.systemInstruction; if (sysInstr) { const instrText = typeof sysInstr === 'string' ? sysInstr : String(sysInstr); span.setAttribute(semantic_convention_1.default.GEN_AI_SYSTEM_INSTRUCTIONS, JSON.stringify([{ type: 'text', content: truncateContent(instrText) }])); } } captureInputMessages(span, llmRequest, captureMessageContent); } if (llmResponse) { const { inputTokens, outputTokens, reasoningTokens, cachedTokens, totalTokens } = extractTokenUsage(llmResponse); if (inputTokens != null) span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, inputTokens); if (outputTokens != null) span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens); if (reasoningTokens != null) span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_COMPLETION_TOKENS_DETAILS_REASONING, reasoningTokens); if (cachedTokens != null) span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, cachedTokens); if (totalTokens != null) span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_TOTAL_TOKENS, totalTokens); const responseModel = llmResponse.model_version ?? llmResponse.modelVersion; if (responseModel) span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_MODEL, String(responseModel)); let frStr = 'stop'; const finishReason = llmResponse.finish_reason ?? llmResponse.finishReason; if (finishReason) { try { frStr = (typeof finishReason === 'object' && finishReason.value) ? String(finishReason.value).toLowerCase() : String(finishReason).toLowerCase(); } catch { frStr = String(finishReason).toLowerCase(); } span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON, [frStr]); } const responseId = llmResponse.response_id ?? llmResponse.responseId ?? llmResponse.id; if (responseId) span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, String(responseId)); const errorCode = llmResponse.error_code ?? llmResponse.errorCode; if (errorCode) { span.setAttribute(semantic_convention_1.default.ERROR_TYPE, String(errorCode)); const errorMessage = llmResponse.error_message ?? llmResponse.errorMessage; if (errorMessage) span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: String(errorMessage) }); } captureOutputMessages(span, llmResponse, captureMessageContent, frStr); span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_TYPE, determineOutputType(llmResponse)); } } catch { /* ignore */ } } // --------------------------------------------------------------------------- // ADK Event extraction (mirrors Python _extract_from_event / _is_adk_event) // --------------------------------------------------------------------------- function isAdkEvent(obj) { if (!obj) return false; return (obj.constructor?.name === 'Event') && ('content' in obj); } function extractFromEvent(eventObj) { try { const content = eventObj?.content; if (!content) return [null, null]; const parts = content.parts; if (!parts || parts.length === 0) return [null, null]; const fnResp = parts[0]?.function_response ?? parts[0]?.functionResponse; if (!fnResp) return [null, null]; return [fnResp.response ?? null, fnResp.id ?? null]; } catch { return [null, null]; } } // --------------------------------------------------------------------------- // Tool type mapping (mirrors Python _otel_gen_ai_tool_type) // --------------------------------------------------------------------------- function otelToolType(tool) { const name = tool?.constructor?.name ?? ''; if (name.includes('Function')) return 'function'; if (name.includes('Agent')) return 'extension'; return 'function'; } // --------------------------------------------------------------------------- // Tool span enrichment (mirrors Python enrich_tool_span) // --------------------------------------------------------------------------- function enrichToolSpan(span, tool, functionArgs, functionResponseEvent, captureMessageContent, error) { try { span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_TOOLS); span.setAttribute(semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL, semantic_convention_1.default.GEN_AI_SYSTEM_GOOGLE_ADK); if (tool) { const toolName = tool.name ?? tool.constructor?.name ?? 'unknown'; span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_NAME, String(toolName)); span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_TYPE, otelToolType(tool)); const toolDesc = tool.description; if (toolDesc) span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_DESCRIPTION, truncateContent(String(toolDesc))); } let responseDict = null; let toolCallId = null; if (isAdkEvent(functionResponseEvent)) { [responseDict, toolCallId] = extractFromEvent(functionResponseEvent); } else if (typeof functionResponseEvent === 'object' && functionResponseEvent !== null) { responseDict = functionResponseEvent; toolCallId = functionResponseEvent.id ?? null; } if (captureMessageContent) { if (functionArgs != null) { try { const argsStr = typeof functionArgs === 'object' ? JSON.stringify(functionArgs) : String(functionArgs); span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ARGUMENTS, truncateContent(argsStr)); } catch { span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ARGUMENTS, truncateContent(String(functionArgs))); } } if (responseDict != null) { try { const resultStr = typeof responseDict === 'object' ? JSON.stringify(responseDict) : String(responseDict); span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_RESULT, truncateContent(resultStr)); } catch { span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_RESULT, truncateContent(String(responseDict))); } } } if (toolCallId) span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ID, String(toolCallId)); if (error != null) { const errorType = error.constructor?.name || '_OTHER'; span.setAttribute(semantic_convention_1.default.ERROR_TYPE, errorType); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: String(error) }); } } catch { /* ignore */ } } // --------------------------------------------------------------------------- // Merged tool span enrichment (mirrors Python enrich_merged_tool_span) // --------------------------------------------------------------------------- function enrichMergedToolSpan(span, responseEventId, functionResponseEvent, captureMessageContent) { try { span.setAttribute(semantic_convention_1.default.GEN_AI_OPERATION, semantic_convention_1.default.GEN_AI_OPERATION_TYPE_TOOLS); span.setAttribute(semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL, semantic_convention_1.default.GEN_AI_SYSTEM_GOOGLE_ADK); span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_NAME, '(merged tools)'); if (responseEventId) span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ID, String(responseEventId)); if (captureMessageContent && functionResponseEvent != null) { try { const content = functionResponseEvent.content; if (content) { const parts = content.parts ?? []; const toolResults = []; for (const part of parts) { const fnResp = part?.function_response ?? part?.functionResponse; if (fnResp) { const entry = {}; const name = fnResp.name; if (name) entry.name = String(name); const resp = fnResp.response; if (resp != null) entry.response = resp; if (Object.keys(entry).length > 0) toolResults.push(entry); } } if (toolResults.length > 0) { span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_RESULT, truncateContent(JSON.stringify(toolResults))); } } } catch { /* ignore */ } } } catch { /* ignore */ } } // --------------------------------------------------------------------------- // Runner/Agent attribute setters (mirrors Python _set_runner_agent_attributes / _set_agent_attributes) // --------------------------------------------------------------------------- function setRunnerAgentAttributes(span, instance, endpoint) { try { const appName = instance?.app_name ?? instance?._app_name ?? 'google_adk'; span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_NAME, String(appName)); if (endpoint === 'runner_run_live') { span.setAttribute(semantic_convention_1.default.GEN_AI_EXECUTION_MODE, 'live'); } else if (endpoint === 'runner_run') { span.setAttribute(semantic_convention_1.default.GEN_AI_EXECUTION_MODE, 'sync'); } else { span.setAttribute(semantic_convention_1.default.GEN_AI_EXECUTION_MODE, 'async'); } } catch { /* ignore */ } } function setAgentAttributes(span, instance) { try { const name = instance?.name; if (name) span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_NAME, String(name)); const description = instance?.description; if (description) span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_DESCRIPTION, String(description)); } catch { /* ignore */ } } // --------------------------------------------------------------------------- // Response processing for Runner/Agent spans (mirrors Python process_google_adk_response) // --------------------------------------------------------------------------- function processGoogleAdkResponse(span, endpoint, instance, startTime, _captureMessageContent) { const endTime = Date.now(); const operationType = getOperationType(endpoint); const [serverAddress, serverPort] = resolveServerInfo(instance); const requestModel = extractModelName(instance); setCommonSpanAttributes(span, operationType); if (requestModel && requestModel !== 'unknown') { span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MODEL, requestModel); } if (serverAddress) span.setAttribute(semantic_convention_1.default.SERVER_ADDRESS, serverAddress); if (serverPort) span.setAttribute(semantic_convention_1.default.SERVER_PORT, serverPort); if (endpoint === 'runner_run_async' || endpoint === 'runner_run' || endpoint === 'runner_run_live') { setRunnerAgentAttributes(span, instance, endpoint); } else if (endpoint === 'agent_run_async') { setAgentAttributes(span, instance); } span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_TYPE, semantic_convention_1.default.GEN_AI_OUTPUT_TYPE_TEXT); span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, (endTime - startTime) / 1000); (0, helpers_1.applyCustomSpanAttributes)(span); span.setStatus({ code: api_1.SpanStatusCode.OK }); } // --------------------------------------------------------------------------- // Metrics recording (mirrors Python record_google_adk_metrics) // --------------------------------------------------------------------------- function recordGoogleAdkMetrics(operationType, duration, requestModel, serverAddress, serverPort) { try { const Metrics = require('../../otel/metrics').default; const attributes = { [semantic_conventions_1.ATTR_TELEMETRY_SDK_NAME]: constant_1.SDK_NAME, [semantic_conventions_1.ATTR_SERVICE_NAME]: config_1.default.applicationName || 'default', [semantic_convention_1.default.ATTR_DEPLOYMENT_ENVIRONMENT]: config_1.default.environment || 'default', [semantic_convention_1.default.GEN_AI_OPERATION]: operationType, [semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: semantic_convention_1.default.GEN_AI_SYSTEM_GOOGLE_ADK, }; if (requestModel && requestModel !== 'unknown') { attributes[semantic_convention_1.default.GEN_AI_REQUEST_MODEL] = requestModel; } if (serverAddress) attributes[semantic_convention_1.default.SERVER_ADDRESS] = serverAddress; if (serverPort) attributes[semantic_convention_1.default.SERVER_PORT] = serverPort; if (Metrics.genaiClientOperationDuration) { Metrics.genaiClientOperationDuration.record(duration, attributes); } } catch { /* ignore */ } } //# sourceMappingURL=utils.js.map