Repository: google/adk-go
File: internal/telemetry/telemetry.go (Go)

Code before:
		attributes := []attribute.KeyValue{
			attribute.String(genAiSystemName, systemName),
			attribute.String(genAiRequestModelName, llmRequest.Model),
			attribute.String(gcpVertexAgentInvocationID, event.InvocationID),
			attribute.String(gcpVertexAgentSessionID, agentCtx.Session().ID()),
			attribute.String(gcpVertexAgentEventID, event.ID),
			attribute.String(gcpVertexAgentLLMRequestName, safeSerialize(llmRequestToTrace(llmRequest))),
			attribute.String(gcpVertexAgentLLMResponseName, safeSerialize(event.LLMResponse)),
		}

		if llmRequest.Config.TopP != nil {
			attributes = append(attributes, attribute.Float64(genAiRequestTopP, float64(*llmRequest.Config.TopP)))
		}

		if llmRequest.Config.MaxOutputTokens != 0 {
			attributes = append(attributes, attribute.Int(genAiRequestMaxTokens, int(llmRequest.Config.MaxOutputTokens)))
		}
		if event.FinishReason != "" {
			attributes = append(attributes, attribute.String(genAiResponseFinishReason, string(event.FinishReason)))
		}
		if event.UsageMetadata != nil {
			if event.UsageMetadata.PromptTokenCount > 0 {
				attributes = append(attributes, attribute.Int(genAiResponsePromptTokens, int(event.UsageMetadata.PromptTokenCount)))
			}
			if event.UsageMetadata.CandidatesTokenCount > 0 {
				attributes = append(attributes, attribute.Int(genAiResponseCompletionTokens, int(event.UsageMetadata.CandidatesTokenCount)))
			}
			if event.UsageMetadata.CachedContentTokenCount > 0 {
				attributes = append(attributes, attribute.Int(genAiResponseCachedContentTokens, int(event.UsageMetadata.CachedContentTokenCount)))
			}
			if event.UsageMetadata.TotalTokenCount > 0 {
				attributes = append(attributes, attribute.Int(genAiResponseTotalTokens, int(event.UsageMetadata.TotalTokenCount)))
			}
		}

		span.SetAttributes(attributes...)
		span.End()
	}
}

func safeSerialize(obj any) string {
	dump, err := json.Marshal(obj)
	if err != nil {
		return "<not serializable>"
	}
	return string(dump)
}

func llmRequestToTrace(llmRequest *model.LLMRequest) map[string]any {
	result := map[string]any{
		"config":  llmRequest.Config,

Review comment (suggestion):
Mismatching names: variable name `genAiResponseCompletionTokens` while data is from `int(event.UsageMetadata.CandidatesTokenCount)`

Code after:
// TraceLLMCall fills the call_llm event details.
func TraceLLMCall(spans []trace.Span, agentCtx agent.InvocationContext, llmRequest *model.LLMRequest, event *session.Event) {
	for _, span := range spans {
		attributes := []attribute.KeyValue{
			attribute.String(genAiSystemName, systemName),
			attribute.String(genAiRequestModelName, llmRequest.Model),
			attribute.String(gcpVertexAgentInvocationID, event.InvocationID),
			attribute.String(gcpVertexAgentSessionID, agentCtx.Session().ID()),
			attribute.String(gcpVertexAgentEventID, event.ID),
			attribute.String(gcpVertexAgentLLMRequestName, safeSerialize(llmRequestToTrace(llmRequest))),
			attribute.String(gcpVertexAgentLLMResponseName, safeSerialize(event.LLMResponse)),
		}

		if llmRequest.Config.TopP != nil {
			attributes = append(attributes, attribute.Float64(genAiRequestTopP, float64(*llmRequest.Config.TopP)))
		}

		if llmRequest.Config.MaxOutputTokens != 0 {
			attributes = append(attributes, attribute.Int(genAiRequestMaxTokens, int(llmRequest.Config.MaxOutputTokens)))
		}
		if event.FinishReason != "" {
			attributes = append(attributes, attribute.String(genAiResponseFinishReason, string(event.FinishReason)))
		}
		if event.UsageMetadata != nil {
			if event.UsageMetadata.PromptTokenCount > 0 {
				attributes = append(attributes, attribute.Int(genAiResponsePromptTokenCount, int(event.UsageMetadata.PromptTokenCount)))
			}
			if event.UsageMetadata.CandidatesTokenCount > 0 {
				attributes = append(attributes, attribute.Int(genAiResponseCandidatesTokenCount, int(event.UsageMetadata.CandidatesTokenCount)))
			}
			if event.UsageMetadata.CachedContentTokenCount > 0 {
				attributes = append(attributes, attribute.Int(genAiResponseCachedContentTokenCount, int(event.UsageMetadata.CachedContentTokenCount)))
			}
			if event.UsageMetadata.TotalTokenCount > 0 {
				attributes = append(attributes, attribute.Int(genAiResponseTotalTokenCount, int(event.UsageMetadata.TotalTokenCount)))
			}
		}

		span.SetAttributes(attributes...)
		span.End()
	}
}

func safeSerialize(obj any) string {
	dump, err := json.Marshal(obj)
	if err != nil {
		return "<not serializable>"
	}
	return string(dump)
}