Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export class OpenTelemetryConstants {
public static readonly GEN_AI_CALLER_ID_KEY = 'gen_ai.caller.id';
public static readonly GEN_AI_CALLER_NAME_KEY = 'gen_ai.caller.name';
public static readonly GEN_AI_CALLER_UPN_KEY = 'gen_ai.caller.upn';
public static readonly GEN_AI_CALLER_CLIENT_IP_KEY = 'gen_ai.caller.client.ip';
Comment thread
fpfp100 marked this conversation as resolved.
Outdated

// Agent to Agent caller agent dimensions
public static readonly GEN_AI_CALLER_AGENT_USER_ID_KEY = 'gen_ai.caller.agent.userid';
Expand All @@ -78,7 +79,7 @@ export class OpenTelemetryConstants {
public static readonly GEN_AI_CALLER_AGENT_NAME_KEY = 'gen_ai.caller.agent.name';
public static readonly GEN_AI_CALLER_AGENT_ID_KEY = 'gen_ai.caller.agent.id';
public static readonly GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY = 'gen_ai.caller.agent.applicationid';

public static readonly GEN_AI_CALLER_AGENT_CLIENT_IP_KEY = 'gen_ai.caller.agent.user.client.ip';
Comment thread
fpfp100 marked this conversation as resolved.
Outdated
// Agent-specific dimensions
public static readonly AGENT_ID_KEY = 'gen_ai.agent.id';
public static readonly GEN_AI_TASK_ID_KEY = 'gen_ai.task.id';
Expand Down
6 changes: 6 additions & 0 deletions packages/agents-a365-observability/src/tracing/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ export interface CallerDetails {

/** The tenant ID of the caller */
tenantId?: string;

/** The client IP address for the caller */
callerClientIp?: string;
}

/**
Expand All @@ -169,6 +172,9 @@ export interface EnhancedAgentDetails extends AgentDetails {

/** The tenant ID for the agent */
tenantId?: string;

/** The client IP address for the agent user */
agentClientIP?: string;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ export class BaggageBuilder {
return this;
}

callerClientIp(value: string | null | undefined): BaggageBuilder {
this.set(OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY, value);
return this;
}
Comment thread
fpfp100 marked this conversation as resolved.

Comment thread
fpfp100 marked this conversation as resolved.
Outdated
/**
* Set the conversation ID baggage value.
* @param value The conversation ID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ export const INVOKE_AGENT_ATTRIBUTES: readonly string[] = [
consts.GEN_AI_CALLER_UPN_KEY,
consts.GEN_AI_CALLER_USER_ID_KEY,
consts.GEN_AI_CALLER_TENANT_ID_KEY,
consts.GEN_AI_CALLER_CLIENT_IP_KEY,
// Caller Agent (A2A) attributes
consts.GEN_AI_CALLER_AGENT_ID_KEY,
consts.GEN_AI_CALLER_AGENT_NAME_KEY,
consts.GEN_AI_CALLER_AGENT_USER_ID_KEY,
consts.GEN_AI_CALLER_AGENT_UPN_KEY,
consts.GEN_AI_CALLER_AGENT_TENANT_ID_KEY,
consts.GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY,
consts.GEN_AI_CALLER_AGENT_CLIENT_IP_KEY,
// Execution context
consts.GEN_AI_EXECUTION_TYPE_KEY,
consts.GEN_AI_EXECUTION_SOURCE_ID_KEY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export class InvokeAgentScope extends OpenTelemetryScope {
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_NAME_KEY, callerDetails.callerName);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_USER_ID_KEY, callerDetails.callerUserId);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_TENANT_ID_KEY, callerDetails.tenantId);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY, callerDetails.callerClientIp);
}

// Set caller agent details tags
Expand All @@ -93,6 +94,7 @@ export class InvokeAgentScope extends OpenTelemetryScope {
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_USER_ID_KEY, callerAgentDetails.agentAUID);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_UPN_KEY, callerAgentDetails.agentUPN);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_TENANT_ID_KEY, callerAgentDetails.tenantId);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_CLIENT_IP_KEY, callerAgentDetails.agentClientIP);
}
}

Expand Down
8 changes: 7 additions & 1 deletion tests/observability/core/BaggageBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,18 @@ describe('BaggageBuilder', () => {
const builder = new BaggageBuilder();
const pairs: Array<[string, string]> = [
[OpenTelemetryConstants.TENANT_ID_KEY, 'tenant-123'],
[OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY, 'agent-456']
[OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY, 'agent-456'],
[OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY, '10.0.0.5']
];
builder.setPairs(pairs);

const scope = builder.build();
expect(scope).toBeInstanceOf(BaggageScope);

const bag = propagation.getBaggage((scope as any).contextWithBaggage);
expect(bag?.getEntry(OpenTelemetryConstants.TENANT_ID_KEY)?.value).toBe('tenant-123');
expect(bag?.getEntry(OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY)?.value).toBe('agent-456');
expect(bag?.getEntry(OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY)?.value).toBe('10.0.0.5');
});

it('should ignore null values', () => {
Expand Down
6 changes: 5 additions & 1 deletion tests/observability/core/agent365-exporter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ describe('Agent365Exporter', () => {
const spans = [
makeSpan({
[OpenTelemetryConstants.TENANT_ID_KEY]: tenantId,
[OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY]: agentId
[OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY]: agentId,
[OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY]: '10.0.0.5',
[OpenTelemetryConstants.GEN_AI_CALLER_AGENT_CLIENT_IP_KEY]: '1.0.0.5'
})
];

Expand All @@ -96,6 +98,8 @@ describe('Agent365Exporter', () => {
expect(exportedSpan.attributes).toBeDefined();
expect(exportedSpan.attributes[OpenTelemetryConstants.TENANT_ID_KEY]).toBe(tenantId);
expect(exportedSpan.attributes[OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY]).toBe(agentId);
expect(exportedSpan.attributes[OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY]).toBe('10.0.0.5');
expect(exportedSpan.attributes[OpenTelemetryConstants.GEN_AI_CALLER_AGENT_CLIENT_IP_KEY]).toBe('1.0.0.5');
});

it('requires a tokenResolver and fails export when missing', async () => {
Expand Down
37 changes: 37 additions & 0 deletions tests/observability/core/scopes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
InferenceOperationType,
CallerDetails,
} from '@microsoft/agents-a365-observability';
import { OpenTelemetryConstants } from '@microsoft/agents-a365-observability/dist/cjs/tracing/constants';
import { OpenTelemetryScope } from '@microsoft/agents-a365-observability/dist/cjs/tracing/scopes/OpenTelemetryScope';

// Mock console to avoid cluttering test output
const originalConsoleWarn = console.warn;
Expand Down Expand Up @@ -194,6 +196,41 @@ describe('Scopes', () => {
expect(() => scope?.recordFinishReasons(['stop', 'length'])).not.toThrow();
scope?.dispose();
});

it('should set caller and caller-agent IP tags', () => {
const spy = jest.spyOn(OpenTelemetryScope.prototype as any, 'setTagMaybe');
const invokeAgentDetails: InvokeAgentDetails = {
agentId: 'test-agent',
agentName: 'Test Agent'
};
const callerDetails: CallerDetails = {
callerId: 'user-123',
tenantId: 'test-tenant',
callerClientIp: '10.0.0.5'
};
const callerAgentDetails: AgentDetails = {
agentId: 'caller-agent',
agentName: 'Caller Agent',
agentDescription: 'desc',
conversationId: 'conv',
agentClientIP: '192.168.1.100'
} as any;

const scope1 = InvokeAgentScope.start(invokeAgentDetails, testTenantDetails, undefined, callerDetails);
expect(scope1).toBeInstanceOf(InvokeAgentScope);
const scope2 = InvokeAgentScope.start(invokeAgentDetails, testTenantDetails, callerAgentDetails, undefined);
expect(scope2).toBeInstanceOf(InvokeAgentScope);

const calls = spy.mock.calls.map(args => ({ key: args[0], val: args[1] }));
expect(calls).toEqual(expect.arrayContaining([
expect.objectContaining({ key: OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY, val: '10.0.0.5' }),
expect.objectContaining({ key: OpenTelemetryConstants.GEN_AI_CALLER_AGENT_CLIENT_IP_KEY, val: '192.168.1.100' })
]));

scope1?.dispose();
scope2?.dispose();
spy.mockRestore();
});
Comment thread
fpfp100 marked this conversation as resolved.
Outdated
});

describe('Dispose pattern', () => {
Expand Down