Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/ai/azure-ai-agents/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features Added

- Added experimental GenAI tracing support via `GenAiTracingConfiguration.enableGenAiTracing()` and `GenAiTracingConfiguration.disableGenAiTracing()`. When enabled, OpenTelemetry spans are emitted for agent CRUD, response generation (chat/invoke_agent), and streaming operations with GenAI semantic convention attributes, token usage metrics, and optional content recording gated by the `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` environment variable.
- Added protocol-style methods on `ResponsesClient` and `ResponsesAsyncClient` that accept a raw JSON request body (`BinaryData`) and a `com.openai.core.RequestOptions`, and return the openai-java raw HTTP response. These mirror the existing `createAzureResponse` and `createStreamingAzureResponse` typed surface: `createResponseWithResponse` (returns `HttpResponseFor<Response>`) and `createResponseStreamWithResponse` (returns `HttpResponseFor<StreamResponse<ResponseStreamEvent>>`). They delegate to the underlying openai-java `ResponseService.withRawResponse()` surface and continue to flow through the Azure HTTP pipeline.

### Other Changes
Expand Down
40 changes: 40 additions & 0 deletions sdk/ai/azure-ai-agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,46 @@ If there are significant differences, API calls may fail due to incompatibility.

Always ensure that the chosen API version is fully supported and operational for your specific use case and that it aligns with the service's versioning policy.

## Tracing (Experimental)

This package supports OpenTelemetry-based tracing for GenAI operations. When enabled, spans are emitted for agent creation, response generation, and streaming with [GenAI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/).

### Quick Start

```java
// 1. Set up OpenTelemetry (console exporter example)
// SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
// .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()))
// .build();
// OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).buildAndRegisterGlobal();

// 2. Enable GenAI tracing
GenAiTracingConfiguration.enableGenAiTracing(new GenAiTracingOptions());

// 3. Use the client normally — spans are emitted automatically
AgentsClient client = new AgentsClientBuilder()
.endpoint(endpoint)
.credential(credential)
.buildAgentsClient();
```

### Content Recording

By default, message content is **NOT** recorded in traces (privacy-safe). To enable:
- Set environment variable `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`, or
- Pass programmatically: `new GenAiTracingOptions().setContentRecording(true)`

### Trace Context Propagation

W3C trace context headers (`traceparent`/`tracestate`) are injected into outgoing requests by default. To disable:
- Set environment variable `AZURE_TRACING_GEN_AI_ENABLE_TRACE_CONTEXT_PROPAGATION=false`, or
- Pass programmatically: `new GenAiTracingOptions().setTraceContextPropagation(false)`

### Samples

- [Console tracing](src/samples/java/com/azure/ai/agents/TracingConsoleSample.java)
- [Azure Monitor tracing](src/samples/java/com/azure/ai/agents/TracingAzureMonitorSample.java)

## Troubleshooting

### Enable client logging
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.ai.agents.telemetry;

import java.net.URI;
import java.util.function.Supplier;

/**
* Provides tracing integration for GenAI agent CRUD operations.
*
* <p>Usage pattern:</p>
* <pre>{@code
* AgentVersionDetails result = GenAiAgentTracing.traceCreateAgent(
* "MyAgent", endpoint, agentDefinition,
* () -> agentsClient.createAgentVersion("MyAgent", input));
* }</pre>
*/
public final class GenAiAgentTracing {

private GenAiAgentTracing() {
// utility class
}

/**
* Traces a create_agent operation.
*
* @param <T> the return type of the operation.
* @param agentName the agent name.
* @param endpoint the service endpoint.
* @param agentId the agent ID (e.g., "name:version").
* @param agentVersion the agent version string.
* @param agentType the agent type ("prompt", "hosted", "workflow").
* @param model the model name.
* @param temperature temperature parameter (may be null).
* @param topP top_p parameter (may be null).
* @param instructions system instructions text (content-gated).
* @param operation the supplier that performs the actual API call.
* @return the result of the operation.
*/
public static <T> T traceCreateAgent(String agentName, URI endpoint, String agentId, String agentVersion,
String agentType, String model, Double temperature, Double topP, String instructions, Supplier<T> operation) {
GenAiTracingScope scope = GenAiTracingScope.startCreateAgent(agentName, endpoint);
if (scope == null) {
return operation.get();
}

try {
scope.setAgentAttributes(agentId, agentName, agentVersion, agentType);
scope.setRequestModelAttributes(model, temperature, topP);
scope.setSystemInstructions(instructions);

T result = operation.get();
return result;
} catch (Throwable ex) {
scope.recordError(ex);
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
throw new RuntimeException(ex);
} finally {
scope.close();
}
}

/**
* Traces a create_agent operation with hosted agent attributes.
*
* @param <T> the return type of the operation.
* @param agentName the agent name.
* @param endpoint the service endpoint.
* @param agentId the agent ID.
* @param agentVersion the agent version.
* @param model the model name.
* @param temperature temperature (may be null).
* @param topP top_p (may be null).
* @param instructions system instructions (content-gated).
* @param cpu hosted CPU allocation.
* @param memory hosted memory allocation.
* @param image hosted container image.
* @param protocol hosted protocol.
* @param protocolVersion hosted protocol version.
* @param operation the supplier that performs the actual API call.
* @return the result of the operation.
*/
public static <T> T traceCreateHostedAgent(String agentName, URI endpoint, String agentId, String agentVersion,
String model, Double temperature, Double topP, String instructions, String cpu, String memory, String image,
String protocol, String protocolVersion, Supplier<T> operation) {
GenAiTracingScope scope = GenAiTracingScope.startCreateAgent(agentName, endpoint);
if (scope == null) {
return operation.get();
}

try {
scope.setAgentAttributes(agentId, agentName, agentVersion, GenAiConstants.AGENT_TYPE_HOSTED);
scope.setRequestModelAttributes(model, temperature, topP);
scope.setSystemInstructions(instructions);
scope.setHostedAgentAttributes(cpu, memory, image, protocol, protocolVersion);

T result = operation.get();
return result;
} catch (Throwable ex) {
scope.recordError(ex);
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
throw new RuntimeException(ex);
} finally {
scope.close();
}
}

/**
* Traces a create_conversation operation.
*
* @param <T> the return type of the operation.
* @param endpoint the service endpoint.
* @param operation the supplier that performs the actual API call.
* @return the result of the operation.
*/
public static <T> T traceCreateConversation(URI endpoint, Supplier<T> operation) {
GenAiTracingScope scope = GenAiTracingScope.startCreateConversation(endpoint);
if (scope == null) {
return operation.get();
}

try {
T result = operation.get();
return result;
} catch (Throwable ex) {
scope.recordError(ex);
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
throw new RuntimeException(ex);
} finally {
scope.close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.ai.agents.telemetry;

/**
* Constants for GenAI semantic convention attribute names and operation names.
* <p>
* These follow the OpenTelemetry GenAI semantic conventions:
* <a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/">GenAI Semantic Conventions</a>
*/
final class GenAiConstants {

private GenAiConstants() {
// utility class
}

// --- Operation names ---
static final String OPERATION_CREATE_AGENT = "create_agent";
static final String OPERATION_INVOKE_AGENT = "invoke_agent";
static final String OPERATION_CHAT = "chat";
static final String OPERATION_CREATE_CONVERSATION = "create_conversation";

// --- System / Provider ---
static final String GEN_AI_SYSTEM = "gen_ai.system";
static final String GEN_AI_SYSTEM_VALUE = "az.ai.agents";
static final String GEN_AI_PROVIDER_NAME = "gen_ai.provider.name";
static final String GEN_AI_PROVIDER_NAME_VALUE = "microsoft.foundry";
static final String AZ_NAMESPACE = "az.namespace";
static final String AZ_NAMESPACE_VALUE = "Microsoft.CognitiveServices";

// --- Operation ---
static final String GEN_AI_OPERATION_NAME = "gen_ai.operation.name";

// --- Agent attributes ---
static final String GEN_AI_AGENT_ID = "gen_ai.agent.id";
static final String GEN_AI_AGENT_NAME = "gen_ai.agent.name";
static final String GEN_AI_AGENT_DESCRIPTION = "gen_ai.agent.description";
static final String GEN_AI_AGENT_VERSION = "gen_ai.agent.version";
static final String GEN_AI_AGENT_TYPE = "gen_ai.agent.type";

// --- Hosted agent attributes ---
static final String GEN_AI_AGENT_HOSTED_CPU = "gen_ai.agent.hosted.cpu";
static final String GEN_AI_AGENT_HOSTED_MEMORY = "gen_ai.agent.hosted.memory";
static final String GEN_AI_AGENT_HOSTED_IMAGE = "gen_ai.agent.hosted.image";
static final String GEN_AI_AGENT_HOSTED_PROTOCOL = "gen_ai.agent.hosted.protocol";
static final String GEN_AI_AGENT_HOSTED_PROTOCOL_VERSION = "gen_ai.agent.hosted.protocol_version";

// --- Request parameters ---
static final String GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
static final String GEN_AI_REQUEST_TEMPERATURE = "gen_ai.request.temperature";
static final String GEN_AI_REQUEST_TOP_P = "gen_ai.request.top_p";
static final String GEN_AI_REQUEST_MAX_INPUT_TOKENS = "gen_ai.request.max_input_tokens";
static final String GEN_AI_REQUEST_MAX_OUTPUT_TOKENS = "gen_ai.request.max_output_tokens";
static final String GEN_AI_REQUEST_REASONING_EFFORT = "gen_ai.request.reasoning.effort";
static final String GEN_AI_REQUEST_TOOLS = "gen_ai.request.tools";

// --- Response attributes ---
static final String GEN_AI_RESPONSE_MODEL = "gen_ai.response.model";
static final String GEN_AI_RESPONSE_ID = "gen_ai.response.id";
static final String GEN_AI_RESPONSE_FINISH_REASONS = "gen_ai.response.finish_reasons";

// --- Token usage ---
static final String GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens";
static final String GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens";

// --- Messages (content-gated) ---
static final String GEN_AI_SYSTEM_INSTRUCTIONS = "gen_ai.system_instructions";
static final String GEN_AI_INPUT_MESSAGES = "gen_ai.input.messages";
static final String GEN_AI_OUTPUT_MESSAGES = "gen_ai.output.messages";

// --- Conversation ---
static final String GEN_AI_CONVERSATION_ID = "gen_ai.conversation.id";

// --- Server ---
static final String SERVER_ADDRESS = "server.address";
static final String SERVER_PORT = "server.port";

// --- Error ---
static final String ERROR_TYPE = "error.type";

// --- Events ---
static final String GEN_AI_AGENT_WORKFLOW = "gen_ai.agent.workflow";
static final String GEN_AI_EVENT_CONTENT = "gen_ai.event.content";
static final String GEN_AI_WORKFLOW_ACTION = "gen_ai.workflow.action";

// --- Token type (for metrics) ---
static final String GEN_AI_TOKEN_TYPE = "gen_ai.token.type";
static final String TOKEN_TYPE_INPUT = "input";
static final String TOKEN_TYPE_OUTPUT = "output";

// --- Metric names ---
static final String METRIC_OPERATION_DURATION = "gen_ai.client.operation.duration";
static final String METRIC_TOKEN_USAGE = "gen_ai.client.token.usage";

// --- Metric units ---
static final String METRIC_UNIT_SECONDS = "s";
static final String METRIC_UNIT_TOKENS = "{token}";

// --- Agent type values ---
static final String AGENT_TYPE_PROMPT = "prompt";
static final String AGENT_TYPE_HOSTED = "hosted";
static final String AGENT_TYPE_WORKFLOW = "workflow";
static final String AGENT_TYPE_UNKNOWN = "unknown";

// --- Default port (HTTPS) ---
static final int DEFAULT_HTTPS_PORT = 443;
}
Loading