Skip to content
Open
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
230 changes: 138 additions & 92 deletions .github/workflows/ci-orchestrator.yml
Original file line number Diff line number Diff line change
@@ -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"
2 changes: 1 addition & 1 deletion nodejs/claude/sample-agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export class MyAgent extends AgentApplication<TurnState> {
new BaggageBuilder(),
turnContext
).sessionDescription('Initial onboarding session')
.correlationId("7ff6dca0-917c-4bb0-b31a-794e533d8aad")
.setPairs({ correlationId: turnContext.activity.id })
.build();

// Preload/refresh exporter token
Expand Down
2 changes: 1 addition & 1 deletion nodejs/claude/sample-agent/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down
2 changes: 1 addition & 1 deletion nodejs/copilot-studio/sample-agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class MyAgent extends AgentApplication<TurnState> {
new BaggageBuilder(),
turnContext
).sessionDescription('Copilot Studio integration session')
.correlationId(`corr-${Date.now()}`)
.setPairs({ correlationId: turnContext.activity.id })
.build();

// Preload/refresh exporter token
Expand Down
2 changes: 1 addition & 1 deletion nodejs/copilot-studio/sample-agent/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 1 addition & 2 deletions nodejs/devin/sample-agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -93,7 +92,7 @@ export class A365Agent extends AgentApplication<ApplicationTurnState> {
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();
Expand Down
2 changes: 1 addition & 1 deletion nodejs/langchain/sample-agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class A365Agent extends AgentApplication<TurnState> {
new BaggageBuilder(),
turnContext
).sessionDescription('Initial onboarding session')
.correlationId(`corr-${Date.now()}`)
.setPairs({ correlationId: turnContext.activity.id })
.build();

// Preload/refresh exporter token
Expand Down
2 changes: 1 addition & 1 deletion nodejs/langchain/sample-agent/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down
2 changes: 1 addition & 1 deletion nodejs/openai/sample-agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class MyAgent extends AgentApplication<TurnState> {
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.
Expand Down
2 changes: 1 addition & 1 deletion nodejs/openai/sample-agent/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down
6 changes: 3 additions & 3 deletions nodejs/perplexity/sample-agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion nodejs/vercel-sdk/sample-agent/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down
Loading