From 16e4198641b39b2cc0e1ba8c6f08af41130983e6 Mon Sep 17 00:00:00 2001 From: jsl517 Date: Tue, 2 Dec 2025 18:29:31 -0800 Subject: [PATCH 1/5] fix manual instrument --- nodejs/openai/sample-agent/src/agent.ts | 35 ++++++++++++++++++------ nodejs/openai/sample-agent/src/client.ts | 27 ++++++++++-------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/nodejs/openai/sample-agent/src/agent.ts b/nodejs/openai/sample-agent/src/agent.ts index 0d7df373..9ebd26c3 100644 --- a/nodejs/openai/sample-agent/src/agent.ts +++ b/nodejs/openai/sample-agent/src/agent.ts @@ -9,6 +9,7 @@ import '@microsoft/agents-a365-notifications'; import { AgentNotificationActivity } from '@microsoft/agents-a365-notifications'; import { Client, getClient } from './client'; +import { BaggageBuilder } from '@microsoft/agents-a365-observability'; export class MyAgent extends AgentApplication { static authHandlerName: string = 'agentic'; @@ -45,15 +46,31 @@ export class MyAgent extends AgentApplication { return; } - try { - const client: Client = await getClient(this.authorization, MyAgent.authHandlerName, turnContext); - const response = await client.invokeAgentWithScope(userMessage); - await turnContext.sendActivity(response); - } catch (error) { - console.error('LLM query error:', error); - const err = error as any; - await turnContext.sendActivity(`Error: ${err.message || err}`); - } + // Build and run with telemetry baggage and span scope + const baggageScope = new BaggageBuilder() + .tenantId((turnContext.activity as any)?.tenantId) + .agentId('openai-sample-agent') + .agentName('OpenAI Sample Agent') + .conversationId(turnContext.activity.conversation?.id) + .callerId((turnContext.activity.from as any)?.aadObjectId) + .callerUpn(turnContext.activity.from?.id) + .correlationId(turnContext.activity.id ?? `corr-${Date.now()}`) + .sourceMetadataName(turnContext.activity.channelId) + .build(); + + await baggageScope.run(async () => { + + try { + const client: Client = await getClient(this.authorization, MyAgent.authHandlerName, turnContext); + const response = await client.invokeAgentWithScope(userMessage); + await turnContext.sendActivity(response); + } catch (error) { + console.error('LLM query error:', error); + const err = error as any; + await turnContext.sendActivity(`Error: ${err.message || err}`); + } + }); + baggageScope.dispose(); } async handleAgentNotificationActivity(context: TurnContext, state: TurnState, agentNotificationActivity: AgentNotificationActivity) { diff --git a/nodejs/openai/sample-agent/src/client.ts b/nodejs/openai/sample-agent/src/client.ts index d43e6286..e5aaae6d 100644 --- a/nodejs/openai/sample-agent/src/client.ts +++ b/nodejs/openai/sample-agent/src/client.ts @@ -98,6 +98,8 @@ class OpenAIClient implements Client { } async invokeAgentWithScope(prompt: string) { + console.log('Invoking agent with observability scope...'); + let response = ''; const inferenceDetails: InferenceDetails = { operationName: InferenceOperationType.CHAT, model: this.agent.model.toString(), @@ -112,19 +114,20 @@ class OpenAIClient implements Client { const tenantDetails: TenantDetails = { tenantId: 'typescript-sample-tenant', }; - + const scope = InferenceScope.start(inferenceDetails, agentDetails, tenantDetails); - - const response = await this.invokeAgent(prompt); - - // Record the inference response with token usage - scope?.recordOutputMessages([response]); - scope?.recordInputMessages([prompt]); - scope?.recordResponseId(`resp-${Date.now()}`); - scope?.recordInputTokens(45); - scope?.recordOutputTokens(78); - scope?.recordFinishReasons(['stop']); - + await scope.withActiveSpanAsync(async () => { + response = await this.invokeAgent(prompt); + + // Record the inference response with token usage + scope.recordOutputMessages([response]); + scope.recordInputMessages([prompt]); + scope.recordResponseId(`resp-${Date.now()}`); + scope.recordInputTokens(45); + scope.recordOutputTokens(78); + scope.recordFinishReasons(['stop']); + }); + scope.dispose(); return response; } From 23b091deb26ef77527e8fdc04068d775704c5f73 Mon Sep 17 00:00:00 2001 From: jsl517 Date: Tue, 2 Dec 2025 18:47:02 -0800 Subject: [PATCH 2/5] Configure OpenAI Agents instrumentation --- nodejs/openai/sample-agent/package.json | 1 + nodejs/openai/sample-agent/src/agent.ts | 28 +++++++++--------- nodejs/openai/sample-agent/src/client.ts | 36 ++++++++++++++++-------- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/nodejs/openai/sample-agent/package.json b/nodejs/openai/sample-agent/package.json index 06adc79b..d1cb6483 100644 --- a/nodejs/openai/sample-agent/package.json +++ b/nodejs/openai/sample-agent/package.json @@ -21,6 +21,7 @@ "@microsoft/agents-a365-runtime": "^0.1.0-preview.30", "@microsoft/agents-a365-tooling": "^0.1.0-preview.30", "@microsoft/agents-a365-tooling-extensions-openai": "^0.1.0-preview.30", + "@microsoft/agents-a365-observability-extensions-openai": "^0.1.0-preview.30", "@openai/agents": "*", "dotenv": "^17.2.2", "express": "^5.1.0" diff --git a/nodejs/openai/sample-agent/src/agent.ts b/nodejs/openai/sample-agent/src/agent.ts index 9ebd26c3..6e69e3d9 100644 --- a/nodejs/openai/sample-agent/src/agent.ts +++ b/nodejs/openai/sample-agent/src/agent.ts @@ -58,19 +58,21 @@ export class MyAgent extends AgentApplication { .sourceMetadataName(turnContext.activity.channelId) .build(); - await baggageScope.run(async () => { - - try { - const client: Client = await getClient(this.authorization, MyAgent.authHandlerName, turnContext); - const response = await client.invokeAgentWithScope(userMessage); - await turnContext.sendActivity(response); - } catch (error) { - console.error('LLM query error:', error); - const err = error as any; - await turnContext.sendActivity(`Error: ${err.message || err}`); - } - }); - baggageScope.dispose(); + try { + await baggageScope.run(async () => { + try { + const client: Client = await getClient(this.authorization, MyAgent.authHandlerName, turnContext); + const response = await client.invokeAgentWithScope(userMessage); + await turnContext.sendActivity(response); + } catch (error) { + console.error('LLM query error:', error); + const err = error as any; + await turnContext.sendActivity(`Error: ${err.message || err}`); + } + }); + } finally { + baggageScope.dispose(); + } } async handleAgentNotificationActivity(context: TurnContext, state: TurnState, agentNotificationActivity: AgentNotificationActivity) { diff --git a/nodejs/openai/sample-agent/src/client.ts b/nodejs/openai/sample-agent/src/client.ts index e5aaae6d..433d99f0 100644 --- a/nodejs/openai/sample-agent/src/client.ts +++ b/nodejs/openai/sample-agent/src/client.ts @@ -16,6 +16,7 @@ import { TenantDetails, InferenceDetails } from '@microsoft/agents-a365-observability'; +import { OpenAIAgentsTraceInstrumentor } from '@microsoft/agents-a365-observability-extensions-openai'; export interface Client { invokeAgentWithScope(prompt: string): Promise; @@ -27,7 +28,15 @@ const sdk = ObservabilityManager.configure( .withService('TypeScript Sample Agent', '1.0.0') ); +// Initialize OpenAI Agents instrumentation +const openAIAgentsTraceInstrumentor = new OpenAIAgentsTraceInstrumentor({ + enabled: true, + tracerName: 'openai-agent-auto-instrumentation', + tracerVersion: '1.0.0' +}); + sdk.start(); +openAIAgentsTraceInstrumentor.enable(); const toolService = new McpToolRegistrationService(); @@ -116,18 +125,21 @@ class OpenAIClient implements Client { }; const scope = InferenceScope.start(inferenceDetails, agentDetails, tenantDetails); - await scope.withActiveSpanAsync(async () => { - response = await this.invokeAgent(prompt); - - // Record the inference response with token usage - scope.recordOutputMessages([response]); - scope.recordInputMessages([prompt]); - scope.recordResponseId(`resp-${Date.now()}`); - scope.recordInputTokens(45); - scope.recordOutputTokens(78); - scope.recordFinishReasons(['stop']); - }); - scope.dispose(); + try { + await scope.withActiveSpanAsync(async () => { + response = await this.invokeAgent(prompt); + + // Record the inference response with token usage + scope.recordOutputMessages([response]); + scope.recordInputMessages([prompt]); + scope.recordResponseId(`resp-${Date.now()}`); + scope.recordInputTokens(45); + scope.recordOutputTokens(78); + scope.recordFinishReasons(['stop']); + }); + } finally { + scope.dispose(); + } return response; } From 15cdd210107341cd4d14f889d9df21a8012da423 Mon Sep 17 00:00:00 2001 From: jsl517 Date: Thu, 4 Dec 2025 15:14:12 -0800 Subject: [PATCH 3/5] fix PR build error --- .github/workflows/ci-nodejs-openai-sampleagent.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci-nodejs-openai-sampleagent.yml b/.github/workflows/ci-nodejs-openai-sampleagent.yml index c79b404a..868a5d9b 100644 --- a/.github/workflows/ci-nodejs-openai-sampleagent.yml +++ b/.github/workflows/ci-nodejs-openai-sampleagent.yml @@ -35,8 +35,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - name: Install dependencies run: npm install From 2b9266d9574cbd9e5785caa06b77559172e4b532 Mon Sep 17 00:00:00 2001 From: PengF <126631706+fpfp100@users.noreply.github.com> Date: Thu, 4 Dec 2025 15:21:16 -0800 Subject: [PATCH 4/5] Update nodejs/openai/sample-agent/src/client.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- nodejs/openai/sample-agent/src/client.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/nodejs/openai/sample-agent/src/client.ts b/nodejs/openai/sample-agent/src/client.ts index 433d99f0..920d88bf 100644 --- a/nodejs/openai/sample-agent/src/client.ts +++ b/nodejs/openai/sample-agent/src/client.ts @@ -127,15 +127,21 @@ class OpenAIClient implements Client { const scope = InferenceScope.start(inferenceDetails, agentDetails, tenantDetails); try { await scope.withActiveSpanAsync(async () => { - response = await this.invokeAgent(prompt); - - // Record the inference response with token usage - scope.recordOutputMessages([response]); - scope.recordInputMessages([prompt]); - scope.recordResponseId(`resp-${Date.now()}`); - scope.recordInputTokens(45); - scope.recordOutputTokens(78); - scope.recordFinishReasons(['stop']); + try { + response = await this.invokeAgent(prompt); + + // Record the inference response with token usage + scope.recordOutputMessages([response]); + scope.recordInputMessages([prompt]); + scope.recordResponseId(`resp-${Date.now()}`); + scope.recordInputTokens(45); + scope.recordOutputTokens(78); + scope.recordFinishReasons(['stop']); + } catch (error) { + scope.recordError(error as Error); + scope.recordFinishReasons(['error']); + throw error; + } }); } finally { scope.dispose(); From d1b8aa2a8e16493ffe727877f1bea7985d29bd3f Mon Sep 17 00:00:00 2001 From: jsl517 Date: Fri, 5 Dec 2025 11:59:41 -0800 Subject: [PATCH 5/5] fix invoke scope --- .../sample-agent/Agent-Code-Walkthrough.md | 31 ++++++++----- nodejs/langchain/sample-agent/src/agent.ts | 44 +++++++++++++++---- nodejs/langchain/sample-agent/src/client.ts | 33 ++++++++------ .../sample-agent/AGENT-CODE-WALKTHROUGH.md | 28 +++++++----- nodejs/openai/sample-agent/src/agent.ts | 22 +--------- nodejs/openai/sample-agent/src/client.ts | 1 - nodejs/vercel-sdk/sample-agent/src/client.ts | 33 +++++++++----- 7 files changed, 115 insertions(+), 77 deletions(-) diff --git a/nodejs/langchain/sample-agent/Agent-Code-Walkthrough.md b/nodejs/langchain/sample-agent/Agent-Code-Walkthrough.md index 10ad4d09..ed476381 100644 --- a/nodejs/langchain/sample-agent/Agent-Code-Walkthrough.md +++ b/nodejs/langchain/sample-agent/Agent-Code-Walkthrough.md @@ -243,7 +243,7 @@ class LangChainClient implements Client { return agentMessage; } - async invokeAgentWithScope(prompt: string): Promise { + async invokeInferenceScope(prompt: string) { const inferenceDetails: InferenceDetails = { operationName: InferenceOperationType.CHAT, model: "gpt-4o-mini", @@ -259,18 +259,25 @@ class LangChainClient implements Client { tenantId: 'typescript-sample-tenant', }; + let response = ''; const scope = InferenceScope.start(inferenceDetails, agentDetails, tenantDetails); - - const response = await this.invokeAgent(prompt); - - // Record the inference response with token usage - scope?.recordOutputMessages([response]); - scope?.recordInputMessages([prompt]); - scope?.recordResponseId(`resp-${Date.now()}`); - scope?.recordInputTokens(45); - scope?.recordOutputTokens(78); - scope?.recordFinishReasons(['stop']); - + try { + await scope.withActiveSpanAsync(async () => { + response = await this.invokeAgent(prompt); + // Record the inference response with token usage + scope.recordOutputMessages([response]); + scope.recordInputMessages([prompt]); + scope.recordResponseId(`resp-${Date.now()}`); + scope.recordInputTokens(45); + scope.recordOutputTokens(78); + scope.recordFinishReasons(['stop']); + }); + } catch (error) { + scope.recordError(error as Error); + throw error; + } finally { + scope.dispose(); + } return response; } } diff --git a/nodejs/langchain/sample-agent/src/agent.ts b/nodejs/langchain/sample-agent/src/agent.ts index 0e37be58..f7322259 100644 --- a/nodejs/langchain/sample-agent/src/agent.ts +++ b/nodejs/langchain/sample-agent/src/agent.ts @@ -4,7 +4,12 @@ import { ActivityTypes } from '@microsoft/agents-activity'; // Notification Imports import '@microsoft/agents-a365-notifications'; import { AgentNotificationActivity } from '@microsoft/agents-a365-notifications'; - +// Observability Imports +import { + AgentDetails, + TenantDetails, +} from '@microsoft/agents-a365-observability'; +import { BaggageBuilder } from '@microsoft/agents-a365-observability'; import { Client, getClient } from './client'; export class A365Agent extends AgentApplication { @@ -42,14 +47,37 @@ export class A365Agent extends AgentApplication { return; } + const agentDetails: AgentDetails = { + agentId: 'typescript-compliance-agent', + agentName: 'TypeScript Compliance Agent', + conversationId: 'conv-12345', + }; + + const tenantDetails: TenantDetails = { + tenantId: 'typescript-sample-tenant', + }; + const baggageScope = new BaggageBuilder() + .tenantId(tenantDetails.tenantId) + .agentId(agentDetails.agentId) + .agentName(agentDetails.agentName) + .conversationId(agentDetails.conversationId) + .correlationId(`corr-${Date.now()}`) + .build(); + try { - const client: Client = await getClient(this.authorization, A365Agent.authHandlerName, turnContext); - const response = await client.invokeAgentWithScope(userMessage); - await turnContext.sendActivity(response); - } catch (error) { - console.error('LLM query error:', error); - const err = error as any; - await turnContext.sendActivity(`Error: ${err.message || err}`); + await baggageScope.run(async () => { + try { + const client: Client = await getClient(this.authorization, A365Agent.authHandlerName, turnContext); + const response = await client.invokeInferenceScope(userMessage); + await turnContext.sendActivity(response); + } catch (error) { + console.error('LLM query error:', error); + const err = error as any; + await turnContext.sendActivity(`Error: ${err.message || err}`); + } + }); + } finally { + baggageScope.dispose(); } } diff --git a/nodejs/langchain/sample-agent/src/client.ts b/nodejs/langchain/sample-agent/src/client.ts index e89fccfa..eae544c6 100644 --- a/nodejs/langchain/sample-agent/src/client.ts +++ b/nodejs/langchain/sample-agent/src/client.ts @@ -17,7 +17,7 @@ import { } from '@microsoft/agents-a365-observability'; export interface Client { - invokeAgentWithScope(prompt: string): Promise; + invokeInferenceScope(prompt: string): Promise; } const sdk = ObservabilityManager.configure( @@ -132,7 +132,7 @@ class LangChainClient implements Client { return agentMessage; } - async invokeAgentWithScope(prompt: string) { + async invokeInferenceScope(prompt: string) { const inferenceDetails: InferenceDetails = { operationName: InferenceOperationType.CHAT, model: "gpt-4o-mini", @@ -148,18 +148,25 @@ class LangChainClient implements Client { tenantId: 'typescript-sample-tenant', }; + let response = ''; const scope = InferenceScope.start(inferenceDetails, agentDetails, tenantDetails); - - const response = await this.invokeAgent(prompt); - - // Record the inference response with token usage - scope?.recordOutputMessages([response]); - scope?.recordInputMessages([prompt]); - scope?.recordResponseId(`resp-${Date.now()}`); - scope?.recordInputTokens(45); - scope?.recordOutputTokens(78); - scope?.recordFinishReasons(['stop']); - + try { + await scope.withActiveSpanAsync(async () => { + response = await this.invokeAgent(prompt); + // Record the inference response with token usage + scope.recordOutputMessages([response]); + scope.recordInputMessages([prompt]); + scope.recordResponseId(`resp-${Date.now()}`); + scope.recordInputTokens(45); + scope.recordOutputTokens(78); + scope.recordFinishReasons(['stop']); + }); + } catch (error) { + scope.recordError(error as Error); + throw error; + } finally { + scope.dispose(); + } return response; } } diff --git a/nodejs/openai/sample-agent/AGENT-CODE-WALKTHROUGH.md b/nodejs/openai/sample-agent/AGENT-CODE-WALKTHROUGH.md index 343c9d9c..965e43be 100644 --- a/nodejs/openai/sample-agent/AGENT-CODE-WALKTHROUGH.md +++ b/nodejs/openai/sample-agent/AGENT-CODE-WALKTHROUGH.md @@ -199,17 +199,23 @@ async invokeAgentWithScope(prompt: string) { }; const scope = InferenceScope.start(inferenceDetails, agentDetails, tenantDetails); - - const response = await this.invokeAgent(prompt); - - // Record the inference response with token usage - scope?.recordOutputMessages([response]); - scope?.recordInputMessages([prompt]); - scope?.recordResponseId(`resp-${Date.now()}`); - scope?.recordInputTokens(45); - scope?.recordOutputTokens(78); - scope?.recordFinishReasons(['stop']); - + try { + await scope.withActiveSpanAsync(async () => { + response = await this.invokeAgent(prompt); + // Record the inference response with token usage + scope.recordOutputMessages([response]); + scope.recordInputMessages([prompt]); + scope.recordResponseId(`resp-${Date.now()}`); + scope.recordInputTokens(45); + scope.recordOutputTokens(78); + scope.recordFinishReasons(['stop']); + }); + } catch (error) { + scope.recordError(error as Error); + throw error; + } finally { + scope.dispose(); + } return response; } ``` diff --git a/nodejs/openai/sample-agent/src/agent.ts b/nodejs/openai/sample-agent/src/agent.ts index 6e69e3d9..b49ad05c 100644 --- a/nodejs/openai/sample-agent/src/agent.ts +++ b/nodejs/openai/sample-agent/src/agent.ts @@ -46,21 +46,7 @@ export class MyAgent extends AgentApplication { return; } - // Build and run with telemetry baggage and span scope - const baggageScope = new BaggageBuilder() - .tenantId((turnContext.activity as any)?.tenantId) - .agentId('openai-sample-agent') - .agentName('OpenAI Sample Agent') - .conversationId(turnContext.activity.conversation?.id) - .callerId((turnContext.activity.from as any)?.aadObjectId) - .callerUpn(turnContext.activity.from?.id) - .correlationId(turnContext.activity.id ?? `corr-${Date.now()}`) - .sourceMetadataName(turnContext.activity.channelId) - .build(); - - try { - await baggageScope.run(async () => { - try { + try { const client: Client = await getClient(this.authorization, MyAgent.authHandlerName, turnContext); const response = await client.invokeAgentWithScope(userMessage); await turnContext.sendActivity(response); @@ -68,11 +54,7 @@ export class MyAgent extends AgentApplication { console.error('LLM query error:', error); const err = error as any; await turnContext.sendActivity(`Error: ${err.message || err}`); - } - }); - } finally { - baggageScope.dispose(); - } + } } async handleAgentNotificationActivity(context: TurnContext, state: TurnState, agentNotificationActivity: AgentNotificationActivity) { diff --git a/nodejs/openai/sample-agent/src/client.ts b/nodejs/openai/sample-agent/src/client.ts index 920d88bf..24ad966f 100644 --- a/nodejs/openai/sample-agent/src/client.ts +++ b/nodejs/openai/sample-agent/src/client.ts @@ -107,7 +107,6 @@ class OpenAIClient implements Client { } async invokeAgentWithScope(prompt: string) { - console.log('Invoking agent with observability scope...'); let response = ''; const inferenceDetails: InferenceDetails = { operationName: InferenceOperationType.CHAT, diff --git a/nodejs/vercel-sdk/sample-agent/src/client.ts b/nodejs/vercel-sdk/sample-agent/src/client.ts index 9ea8c8b1..ddfdd2d6 100644 --- a/nodejs/vercel-sdk/sample-agent/src/client.ts +++ b/nodejs/vercel-sdk/sample-agent/src/client.ts @@ -95,7 +95,7 @@ class VercelAiClient implements Client { return agentMessage; } - async invokeAgentWithScope(prompt: string) { + async invokeAgentWithScope(prompt: string): Promise { const inferenceDetails: InferenceDetails = { operationName: InferenceOperationType.CHAT, model: modelName, @@ -111,18 +111,27 @@ class VercelAiClient implements Client { tenantId: 'typescript-sample-tenant', }; + let response = ''; const scope = InferenceScope.start(inferenceDetails, agentDetails, tenantDetails); - - const response = await this.invokeAgent(prompt); - - // Record the inference response with token usage - scope?.recordOutputMessages([response]); - scope?.recordInputMessages([prompt]); - scope?.recordResponseId(`resp-${Date.now()}`); - scope?.recordInputTokens(45); - scope?.recordOutputTokens(78); - scope?.recordFinishReasons(['stop']); - + try { + await scope.withActiveSpanAsync(async () => { + try { + response = await this.invokeAgent(prompt); + scope.recordOutputMessages([response]); + scope.recordInputMessages([prompt]); + scope.recordResponseId(`resp-${Date.now()}`); + scope.recordInputTokens(45); + scope.recordOutputTokens(78); + scope.recordFinishReasons(['stop']); + } catch (error) { + scope.recordError(error as Error); + scope.recordFinishReasons(['error']); + throw error; + } + }); + } finally { + scope.dispose(); + } return response; } }