diff --git a/.github/workflows/ci-orchestrator.yml b/.github/workflows/ci-orchestrator.yml index 77950af1..5233a4ca 100644 --- a/.github/workflows/ci-orchestrator.yml +++ b/.github/workflows/ci-orchestrator.yml @@ -1,92 +1,138 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -name: CI Orchestrator - -on: - push: - branches: [main, master] - pull_request: - branches: [main, master] - -permissions: - contents: read - -jobs: - # .NET Jobs - Call existing workflows - dotnet-agentframework: - name: .NET Agent Framework - uses: ./.github/workflows/ci-dotnet-agentframework-sampleagent.yml - - dotnet-semantickernel: - name: .NET Semantic Kernel - uses: ./.github/workflows/ci-dotnet-semantickernel-sampleagent.yml - - # Node.js Jobs - Call existing workflows - nodejs-claude: - name: Node.js Claude - uses: ./.github/workflows/ci-nodejs-claude-sampleagent.yml - - nodejs-langchain: - name: Node.js LangChain - uses: ./.github/workflows/ci-nodejs-langchain-sampleagent.yml - - nodejs-openai: - name: Node.js OpenAI - uses: ./.github/workflows/ci-nodejs-openai-sampleagent.yml - - nodejs-vercelsdk: - name: Node.js Vercel SDK - uses: ./.github/workflows/ci-nodejs-vercelsdk-sampleagent.yml - - # Python Jobs - Call existing workflows - python-agentframework: - name: Python Agent Framework - uses: ./.github/workflows/ci-python-agentframework-sampleagent.yml - - python-googleadk: - name: Python Google ADK - uses: ./.github/workflows/ci-python-googleadk-sampleagent.yml - - python-openai: - name: Python OpenAI - uses: ./.github/workflows/ci-python-openai-sampleagent.yml - - python-claude: - name: Python Claude - uses: ./.github/workflows/python-claude-sample.yml - - # Final status check - ALWAYS runs and reports success - # This is the ONLY check you need to mark as "Required" in branch protection - ci-status: - name: CI Status - runs-on: ubuntu-latest - needs: - - dotnet-agentframework - - dotnet-semantickernel - - nodejs-claude - - nodejs-langchain - - nodejs-openai - - nodejs-vercelsdk - - python-agentframework - - python-googleadk - - python-openai - - python-claude - if: always() - steps: - - name: Check CI Status - run: | - echo "Checking CI job results..." - - # Get all job results (skipped jobs are fine, failed jobs are not) - results="${{ needs.dotnet-agentframework.result }} ${{ needs.dotnet-semantickernel.result }} ${{ needs.nodejs-claude.result }} ${{ needs.nodejs-langchain.result }} ${{ needs.nodejs-openai.result }} ${{ needs.nodejs-vercelsdk.result }} ${{ needs.python-agentframework.result }} ${{ needs.python-googleadk.result }} ${{ needs.python-openai.result }} ${{ needs.python-claude.result }}" - - echo "Job results: $results" - - # Check if any job failed or was cancelled - if echo "$results" | grep -qE "failure|cancelled"; then - echo "❌ One or more CI jobs failed or were cancelled" - exit 1 - fi - - echo "✅ All CI jobs passed" +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +name: CI Orchestrator + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +permissions: + contents: read + pull-requests: read + +jobs: + # Detect which language directories changed + changes: + name: Detect Changes + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + outputs: + dotnet: ${{ steps.filter.outputs.dotnet }} + nodejs: ${{ steps.filter.outputs.nodejs }} + python: ${{ steps.filter.outputs.python }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + dotnet: + - 'dotnet/**' + nodejs: + - 'nodejs/**' + python: + - 'python/**' + + # .NET Jobs - Call existing workflows + dotnet-agentframework: + name: .NET Agent Framework + needs: changes + if: needs.changes.outputs.dotnet == 'true' + uses: ./.github/workflows/ci-dotnet-agentframework-sampleagent.yml + + dotnet-semantickernel: + name: .NET Semantic Kernel + needs: changes + if: needs.changes.outputs.dotnet == 'true' + uses: ./.github/workflows/ci-dotnet-semantickernel-sampleagent.yml + + # Node.js Jobs - Call existing workflows + nodejs-claude: + name: Node.js Claude + needs: changes + if: needs.changes.outputs.nodejs == 'true' + uses: ./.github/workflows/ci-nodejs-claude-sampleagent.yml + + nodejs-langchain: + name: Node.js LangChain + needs: changes + if: needs.changes.outputs.nodejs == 'true' + uses: ./.github/workflows/ci-nodejs-langchain-sampleagent.yml + + nodejs-openai: + name: Node.js OpenAI + needs: changes + if: needs.changes.outputs.nodejs == 'true' + uses: ./.github/workflows/ci-nodejs-openai-sampleagent.yml + + nodejs-vercelsdk: + name: Node.js Vercel SDK + needs: changes + if: needs.changes.outputs.nodejs == 'true' + uses: ./.github/workflows/ci-nodejs-vercelsdk-sampleagent.yml + + # Python Jobs - Call existing workflows + python-agentframework: + name: Python Agent Framework + needs: changes + if: needs.changes.outputs.python == 'true' + uses: ./.github/workflows/ci-python-agentframework-sampleagent.yml + + python-googleadk: + name: Python Google ADK + needs: changes + if: needs.changes.outputs.python == 'true' + uses: ./.github/workflows/ci-python-googleadk-sampleagent.yml + + python-openai: + name: Python OpenAI + needs: changes + if: needs.changes.outputs.python == 'true' + uses: ./.github/workflows/ci-python-openai-sampleagent.yml + + python-claude: + name: Python Claude + needs: changes + if: needs.changes.outputs.python == 'true' + uses: ./.github/workflows/python-claude-sample.yml + + # Final status check - ALWAYS runs and reports success + # This is the ONLY check you need to mark as "Required" in branch protection + ci-status: + name: CI Status + runs-on: ubuntu-latest + needs: + - changes + - dotnet-agentframework + - dotnet-semantickernel + - nodejs-claude + - nodejs-langchain + - nodejs-openai + - nodejs-vercelsdk + - python-agentframework + - python-googleadk + - python-openai + - python-claude + if: always() + steps: + - name: Check CI Status + run: | + echo "Checking CI job results..." + + # Get all job results (skipped jobs are fine, failed jobs are not) + results="${{ needs.dotnet-agentframework.result }} ${{ needs.dotnet-semantickernel.result }} ${{ needs.nodejs-claude.result }} ${{ needs.nodejs-langchain.result }} ${{ needs.nodejs-openai.result }} ${{ needs.nodejs-vercelsdk.result }} ${{ needs.python-agentframework.result }} ${{ needs.python-googleadk.result }} ${{ needs.python-openai.result }} ${{ needs.python-claude.result }}" + + echo "Job results: $results" + + # Check if any job failed or was cancelled + if echo "$results" | grep -qE "failure|cancelled"; then + echo "❌ One or more CI jobs failed or were cancelled" + exit 1 + fi + + echo "✅ All CI jobs passed" diff --git a/nodejs/claude/sample-agent/src/agent.ts b/nodejs/claude/sample-agent/src/agent.ts index f5d3ef80..6d3355c0 100644 --- a/nodejs/claude/sample-agent/src/agent.ts +++ b/nodejs/claude/sample-agent/src/agent.ts @@ -85,7 +85,7 @@ export class MyAgent extends AgentApplication { new BaggageBuilder(), turnContext ).sessionDescription('Initial onboarding session') - .correlationId("7ff6dca0-917c-4bb0-b31a-794e533d8aad") + .setPairs({ correlationId: turnContext.activity.id }) .build(); // Preload/refresh exporter token diff --git a/nodejs/claude/sample-agent/src/client.ts b/nodejs/claude/sample-agent/src/client.ts index 88b6c341..681eaa76 100644 --- a/nodejs/claude/sample-agent/src/client.ts +++ b/nodejs/claude/sample-agent/src/client.ts @@ -158,7 +158,7 @@ class ClaudeClient implements Client { // Record the inference response with token usage scope?.recordOutputMessages([response]); scope?.recordInputMessages([prompt]); - scope?.recordResponseId(`resp-${Date.now()}`); + scope?.recordAttributes({ 'gen_ai.response.id': `resp-${Date.now()}` }); scope?.recordInputTokens(45); scope?.recordOutputTokens(78); scope?.recordFinishReasons(['stop']); diff --git a/nodejs/copilot-studio/sample-agent/src/agent.ts b/nodejs/copilot-studio/sample-agent/src/agent.ts index 3c7c046f..a05bdd95 100644 --- a/nodejs/copilot-studio/sample-agent/src/agent.ts +++ b/nodejs/copilot-studio/sample-agent/src/agent.ts @@ -88,7 +88,7 @@ export class MyAgent extends AgentApplication { new BaggageBuilder(), turnContext ).sessionDescription('Copilot Studio integration session') - .correlationId(`corr-${Date.now()}`) + .setPairs({ correlationId: turnContext.activity.id }) .build(); // Preload/refresh exporter token diff --git a/nodejs/copilot-studio/sample-agent/src/client.ts b/nodejs/copilot-studio/sample-agent/src/client.ts index 3b1942a7..dd308734 100644 --- a/nodejs/copilot-studio/sample-agent/src/client.ts +++ b/nodejs/copilot-studio/sample-agent/src/client.ts @@ -149,7 +149,7 @@ class McsClient implements Client { // Record the inference telemetry scope.recordInputMessages([prompt]); scope.recordOutputMessages([response]); - scope.recordResponseId(`resp-${Date.now()}`); + scope.recordAttributes({ 'gen_ai.response.id': `resp-${Date.now()}` }); scope.recordFinishReasons(['stop']); }); } catch (error) { diff --git a/nodejs/devin/sample-agent/src/agent.ts b/nodejs/devin/sample-agent/src/agent.ts index 346db544..fa62ad40 100644 --- a/nodejs/devin/sample-agent/src/agent.ts +++ b/nodejs/devin/sample-agent/src/agent.ts @@ -27,7 +27,6 @@ import { createEmailResponseActivity, } from "@microsoft/agents-a365-notifications"; import { Stream } from "stream"; -import { v4 as uuidv4 } from "uuid"; import { devinClient } from "./devin-client"; import tokenCache from "./token-cache"; import { ApplicationTurnState } from "./types/agent.types"; @@ -93,7 +92,7 @@ export class A365Agent extends AgentApplication { const baggageScope = new BaggageBuilder() .tenantId(tenantDetails.tenantId) .agentId(invokeAgentDetails.agentId) - .correlationId(uuidv4()) + .setPairs({ correlationId: context.activity.id }) .agentName(invokeAgentDetails.agentName) .conversationId(context.activity.conversation?.id) .build(); diff --git a/nodejs/langchain/sample-agent/src/agent.ts b/nodejs/langchain/sample-agent/src/agent.ts index a00b9cb1..5440841c 100644 --- a/nodejs/langchain/sample-agent/src/agent.ts +++ b/nodejs/langchain/sample-agent/src/agent.ts @@ -80,7 +80,7 @@ export class A365Agent extends AgentApplication { new BaggageBuilder(), turnContext ).sessionDescription('Initial onboarding session') - .correlationId(`corr-${Date.now()}`) + .setPairs({ correlationId: turnContext.activity.id }) .build(); // Preload/refresh exporter token diff --git a/nodejs/langchain/sample-agent/src/client.ts b/nodejs/langchain/sample-agent/src/client.ts index fab36352..e0bbbf61 100644 --- a/nodejs/langchain/sample-agent/src/client.ts +++ b/nodejs/langchain/sample-agent/src/client.ts @@ -227,7 +227,7 @@ class LangChainClient implements Client { // Record the inference response with token usage scope.recordOutputMessages([response]); scope.recordInputMessages([prompt]); - scope.recordResponseId(`resp-${Date.now()}`); + scope.recordAttributes({ 'gen_ai.response.id': `resp-${Date.now()}` }); scope.recordInputTokens(45); scope.recordOutputTokens(78); scope.recordFinishReasons(['stop']); diff --git a/nodejs/openai/sample-agent/src/agent.ts b/nodejs/openai/sample-agent/src/agent.ts index ab66409a..aed92420 100644 --- a/nodejs/openai/sample-agent/src/agent.ts +++ b/nodejs/openai/sample-agent/src/agent.ts @@ -90,7 +90,7 @@ export class MyAgent extends AgentApplication { new BaggageBuilder(), turnContext ).sessionDescription('Initial onboarding session') - .correlationId("7ff6dca0-917c-4bb0-b31a-794e533d8aad") + .setPairs({ correlationId: turnContext.activity.id }) .build(); // Preloads or refreshes the Observability token used by the Agent 365 Observability exporter. diff --git a/nodejs/openai/sample-agent/src/client.ts b/nodejs/openai/sample-agent/src/client.ts index 37b7b2b1..67d4e45a 100644 --- a/nodejs/openai/sample-agent/src/client.ts +++ b/nodejs/openai/sample-agent/src/client.ts @@ -166,7 +166,7 @@ class OpenAIClient implements Client { // Record the inference response with token usage scope.recordOutputMessages([response]); scope.recordInputMessages([prompt]); - scope.recordResponseId(`resp-${Date.now()}`); + scope.recordAttributes({ 'gen_ai.response.id': `resp-${Date.now()}` }); scope.recordInputTokens(45); scope.recordOutputTokens(78); scope.recordFinishReasons(['stop']); diff --git a/nodejs/perplexity/sample-agent/src/agent.ts b/nodejs/perplexity/sample-agent/src/agent.ts index 05ac3498..09727a1f 100644 --- a/nodejs/perplexity/sample-agent/src/agent.ts +++ b/nodejs/perplexity/sample-agent/src/agent.ts @@ -232,13 +232,13 @@ app.onActivity(ActivityTypes.Message, async (context) => { const baggageScope = new BaggageBuilder() .tenantId(tenantId) .agentId(agentId) - .correlationId(activityId || `corr-${Date.now()}`) + .setPairs({ correlationId: activityId }) .agentName(agentName) .agentDescription( "AI answer engine for research, writing, and task assistance using live web search and citations", ) - .callerId(userId) - .callerName(userName) + .userId(userId) + .userName(userName) .conversationId(conversationId) .operationSource("sdk") .build(); diff --git a/nodejs/vercel-sdk/sample-agent/src/client.ts b/nodejs/vercel-sdk/sample-agent/src/client.ts index a0a42c15..2e4f8ab1 100644 --- a/nodejs/vercel-sdk/sample-agent/src/client.ts +++ b/nodejs/vercel-sdk/sample-agent/src/client.ts @@ -122,7 +122,7 @@ class VercelAiClient implements Client { response = await this.invokeAgent(prompt); scope.recordOutputMessages([response]); scope.recordInputMessages([prompt]); - scope.recordResponseId(`resp-${Date.now()}`); + scope.recordAttributes({ 'gen_ai.response.id': `resp-${Date.now()}` }); scope.recordInputTokens(45); scope.recordOutputTokens(78); scope.recordFinishReasons(['stop']);