chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
dirs:
- .
ignorePatterns:
- pattern: "/github/"
- pattern: "./actions"
- pattern: "./blob"
- pattern: "./issues"
- pattern: "./discussions"
- pattern: "./pulls"
- pattern: "https:\/\/platform.openai.com"
- pattern: "https:\/\/outlook.office.com/bookings"
excludedDirs:
# Folders which include links to localhost, since it's not ignored with regular expressions
- ./python/samples/demos/telemetry
- ./python/samples/demos/process_with_dapr
- ./dotnet/samples/Demos/ProcessWithDapr
- ./dotnet/samples/Demos/CopilotAgentPlugins
# Exclude folders that contain documents with links prone to becoming broken and temporarily unavailable. This should be removed when the link checker is implemented as a background job that does not block PRs.
- ./docs/decisions
- ./dotnet/samples
- ./dotnet/src/IntegrationTests # Cannot reach https://www.microsoft.com/en-us/bing/apis/bing-web-search-api Status: 404" location:{path:"dotnet/src/IntegrationTests/README.md"
- ./dotnet/src/Experimental/Orchestration.Flow.IntegrationTests # Cannot reach https://www.microsoft.com/en-us/bing/apis/bing-web-search-api Status: 404" location:{path:"dotnet/src/Experimental/Orchestration.Flow.IntegrationTests/README.md"
- ./python/samples
baseUrl: https://github.com/microsoft/semantic-kernel/
aliveStatusCodes:
- 200
- 206
- 429
- 500
- 503
useGitIgnore: true
+14
View File
@@ -0,0 +1,14 @@
# @microsoft/octo-semantickernel-pr-dotnet owns any files in the dotnet
# directory at the root of the repository and any of its
# subdirectories.
/dotnet/ @microsoft/octo-semantickernel-pr-dotnet
# @microsoft/octo-semantickernel-pr-python owns any files in the python
# directory at the root of the repository and any of its
# subdirectories.
/python/ @microsoft/octo-semantickernel-pr-python
# @microsoft/octo-semantickernel-pr-python owns any files in the java
# directory at the root of the repository and any of its
# subdirectories.
/java/ @microsoft/octo-semantickernel-pr-java
+36
View File
@@ -0,0 +1,36 @@
---
name: Bug report
about: Create a report to help us improve
title: 'Bug: '
type: 'bug'
labels: ["bug"]
projects: ["semantic-kernel"]
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Platform**
- Language: [e.g. C#, Python]
- Source: [e.g. NuGet package version 0.1.0, pip package version 0.1.0, main branch of repository]
- AI model: [e.g. OpenAI:GPT-4o-mini(2024-07-18)]
- IDE: [e.g. Visual Studio, VS Code]
- OS: [e.g. Windows, Mac]
**Additional context**
Add any other context about the problem here.
@@ -0,0 +1,30 @@
---
name: Feature graduation
about: Plan the graduation of an experimental feature
title: 'Graduate XXX feature'
labels: ["feature_graduation"]
type: 'feature'
projects: ["semantic-kernel"]
assignees: ''
---
---
name: Feature graduation
about: Plan the graduation of an experimental feature
---
Checklist to be completed when graduating an experimental feature
- [ ] Notify PM's and EM's that feature is ready for graduation
- [ ] Contact PM for list of sample use cases
- [ ] Verify there are sample implementations for each of the use cases
- [ ] Verify telemetry and logging are complete
- [ ] Verify API docs are complete and arrange to have them published
- [ ] Make appropriate updates to Learn docs
- [ ] Make appropriate updates to Concept samples
- [ ] Make appropriate updates to Blog posts
- [ ] Verify there are no serious open Issues
- [ ] Update table in EXPERIMENTS.md
- [ ] Remove SKEXP flag from the experimental code
+22
View File
@@ -0,0 +1,22 @@
---
name: Feature request
about: Suggest an idea for this project
title: 'New Feature: '
labels: ''
type: 'feature'
projects: ["semantic-kernel"]
assignees: ''
---
---
name: Feature request
about: Suggest an idea for this project
---
<!-- ⚠️⚠️ Do Not Delete This! feature_request_template ⚠️⚠️ -->
<!-- Please read our Rules of Conduct: https://opensource.microsoft.com/codeofconduct/ -->
<!-- Please search existing issues to avoid creating duplicates. -->
<!-- Describe the feature you'd like. -->
+61
View File
@@ -0,0 +1,61 @@
# Typos configuration file
#
# Info: https://github.com/marketplace/actions/typos-action
# Install: brew install typos-cli
# Install: conda install typos
# Run: typos -c .github/_typos.toml
[files]
extend-exclude = [
"_typos.toml",
"package-lock.json",
"*.bicep",
"encoder.json",
"vocab.bpe",
"CodeTokenizerTests.cs",
"test_code_tokenizer.py",
"*response.json",
"test_content.txt",
"google_what_is_the_semantic_kernel.json",
"what-is-semantic-kernel.json",
"serializedChatHistoryV1_15_1.json",
"MultipleFunctionsVsParameters.cs",
"PopulationByCountry.csv",
"PopulationByAdmin1.csv",
"WomensSuffrage.txt",
"SK-dotnet.slnx.DotSettings",
"**/azure_ai_search_hotel_samples/README.md",
"**/Demos/ProcessFrameworkWithAspire/ProcessFramework.Aspire/ProcessFramework.Aspire.ProcessOrchestrator/Program.cs",
"**/Demos/ProcessFrameworkWithAspire/**/*.http",
"**/samples/Concepts/Resources/travel-destination-overview.txt"
]
[default.extend-words]
ACI = "ACI" # Azure Container Instance
exercize = "exercize" # test typos
gramatical = "gramatical" # test typos
Guid = "Guid" # Globally Unique Identifier
HD = "HD" # Test header value
EOF = "EOF" # End of File
ans = "ans" # Short for answers
arange = "arange" # Method in Python numpy package
prompty = "prompty" # prompty is a format name.
ist = "ist" # German language
dall = "dall" # OpenAI model name
pn = "pn" # Kiota parameter
nin = "nin" # MongoDB "not in" operator
asend = "asend" # Async generator method
Magentic = "Magentic" # Magentic is a name of an agentic pattern
[default.extend-identifiers]
ags = "ags" # Azure Graph Service
[type.jupyter]
extend-ignore-re = [
'"[A-Fa-f0-9]{8}"', # cell id strings
]
[type.msbuild]
extend-ignore-re = [
'Version=".*"', # ignore package version numbers
]
+44
View File
@@ -0,0 +1,44 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Maintain dependencies for nuget
- package-ecosystem: "nuget"
directory: "dotnet/"
schedule:
interval: "weekly"
day: "monday"
ignore:
# For all System.* and Microsoft.Extensions/Bcl.* packages, ignore all major version updates
- dependency-name: "System.*"
update-types: ["version-update:semver-major"]
- dependency-name: "Microsoft.Extensions.*"
update-types: ["version-update:semver-major"]
- dependency-name: "Microsoft.Bcl.*"
update-types: ["version-update:semver-major"]
- dependency-name: "Moq"
labels:
- ".NET"
- "dependencies"
# Maintain dependencies for pip
- package-ecosystem: "pip"
directory: "python/"
schedule:
interval: "weekly"
day: "monday"
labels:
- "python"
- "dependencies"
# Maintain dependencies for github-actions
- package-ecosystem: "github-actions"
# Workflow files stored in the
# default location of `.github/workflows`
directory: "/"
schedule:
interval: "weekly"
day: "tuesday"
+43
View File
@@ -0,0 +1,43 @@
# Add 'kernel' label to any change within Connectors, Extensions, Skills, and tests directories
kernel:
- dotnet/src/Connectors/**/*
- dotnet/src/Extensions/**/*
- dotnet/src/Skills/**/*
- dotnet/src/IntegrationTests/**/*
- dotnet/src/SemanticKernel.UnitTests/**/*
# Add 'kernel.core' label to any change within the 'SemanticKernel', 'SemanticKernel.Abstractions', or 'SemanticKernel.MetaPackage' directories
kernel.core:
- dotnet/src/SemanticKernel/**/*
- dotnet/src/SemanticKernel.Abstractions/**/*
- dotnet/src/SemanticKernel.MetaPackage/**/*
# Add 'python' label to any change within the 'python' directory
python:
- python/**/*
# Add 'java' label to any change within the 'java' directory
java:
- java/**/*
# Add 'samples' label to any change within the 'samples' directory
samples:
- samples/**/*
# Add '.NET' label to any change within samples or kernel 'dotnet' directories.
.NET:
- dotnet/**/*
# Add 'copilot chat' label to any change within the 'samples/apps/copilot-chat-app' directory
copilot chat:
- samples/apps/copilot-chat-app/**/*
# Add 'documentation' label to any change within the 'docs' directory, or any '.md' files
documentation:
- docs/**/*
- '**/*.md'
# Add 'memory' label to any memory connectors in dotnet/ or python/
memory:
- dotnet/src/Connectors/Connectors.Memory.*/**/*
- python/semantic_kernel/connectors/memory/**/*
+23
View File
@@ -0,0 +1,23 @@
### Motivation and Context
<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following information:
1. Why is this change required?
2. What problem does it solve?
3. What scenario does it contribute to?
4. If it fixes an open issue, please link to the issue here.
-->
### Description
<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->
### Contribution Checklist
<!-- Before submitting this PR, please make sure: -->
- [ ] The code builds clean without any errors or warnings
- [ ] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations
- [ ] All unit tests pass, and I have added new tests where possible
- [ ] I didn't break anyone :smile:
File diff suppressed because it is too large Load Diff
+78
View File
@@ -0,0 +1,78 @@
param (
[string]$JsonReportPath,
[double]$CoverageThreshold
)
$jsonContent = Get-Content $JsonReportPath -Raw | ConvertFrom-Json
$coverageBelowThreshold = $false
$nonExperimentalAssemblies = [System.Collections.Generic.HashSet[string]]::new()
$assembliesCollection = @(
'Microsoft.SemanticKernel.Abstractions'
'Microsoft.SemanticKernel.Core'
'Microsoft.SemanticKernel.PromptTemplates.Handlebars'
'Microsoft.SemanticKernel.Connectors.OpenAI'
'Microsoft.SemanticKernel.Connectors.AzureOpenAI'
'Microsoft.SemanticKernel.Yaml'
'Microsoft.SemanticKernel.Agents.Abstractions'
'Microsoft.SemanticKernel.Agents.Core'
'Microsoft.SemanticKernel.Agents.OpenAI'
)
foreach ($assembly in $assembliesCollection) {
$nonExperimentalAssemblies.Add($assembly)
}
function Get-FormattedValue {
param (
[float]$Coverage,
[bool]$UseIcon = $false
)
$formattedNumber = "{0:N1}" -f $Coverage
$icon = if (-not $UseIcon) { "" } elseif ($Coverage -ge $CoverageThreshold) { '✅' } else { '❌' }
return "$formattedNumber% $icon"
}
$lineCoverage = $jsonContent.summary.linecoverage
$branchCoverage = $jsonContent.summary.branchcoverage
$totalTableData = [PSCustomObject]@{
'Metric' = 'Total Coverage'
'Line Coverage' = Get-FormattedValue -Coverage $lineCoverage
'Branch Coverage' = Get-FormattedValue -Coverage $branchCoverage
}
$totalTableData | Format-Table -AutoSize
$assemblyTableData = @()
foreach ($assembly in $jsonContent.coverage.assemblies) {
$assemblyName = $assembly.name
$assemblyLineCoverage = $assembly.coverage
$assemblyBranchCoverage = $assembly.branchcoverage
$isNonExperimentalAssembly = $nonExperimentalAssemblies -contains $assemblyName
if ($isNonExperimentalAssembly -and ($assemblyLineCoverage -lt $CoverageThreshold -or $assemblyBranchCoverage -lt $CoverageThreshold)) {
$coverageBelowThreshold = $true
}
$assemblyTableData += [PSCustomObject]@{
'Assembly Name' = $assemblyName
'Line' = Get-FormattedValue -Coverage $assemblyLineCoverage -UseIcon $isNonExperimentalAssembly
'Branch' = Get-FormattedValue -Coverage $assemblyBranchCoverage -UseIcon $isNonExperimentalAssembly
}
}
$sortedTable = $assemblyTableData | Sort-Object {
$nonExperimentalAssemblies -contains $_.'Assembly Name'
} -Descending
$sortedTable | Format-Table -AutoSize
if ($coverageBelowThreshold) {
Write-Host "Code coverage is lower than defined threshold: $CoverageThreshold. Stopping the task."
exit 1
}
@@ -0,0 +1,22 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
days-before-issue-stale: 90
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 90 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}
+70
View File
@@ -0,0 +1,70 @@
# CodeQL is the code analysis engine developed by GitHub to automate security checks.
# The results are shown as code scanning alerts in GitHub. For more details, visit:
# https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning-with-codeql
name: "CodeQL"
on:
workflow_dispatch:
push:
# TODO: Add "feature*" back in again, once we determine the cause of the ongoing CodeQL failures.
branches: ["main", "experimental*", "*-development"]
schedule:
- cron: "17 11 * * 2"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ["csharp", "python"]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
persist-credentials: false
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
if: ${{ matrix.language != 'java' }}
uses: github/codeql-action/autobuild@v3
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
+139
View File
@@ -0,0 +1,139 @@
name: DevFlow PR Review
on:
pull_request_target:
types:
- opened
- reopened
- ready_for_review
workflow_dispatch:
inputs:
pr_number:
description: Pull request number to review
required: true
type: string
permissions:
contents: read
issues: write
pull-requests: write
concurrency:
group: devflow-pr-review-${{ github.repository }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
env:
DEVFLOW_REPOSITORY: ${{ vars.DF_REPO }}
DEVFLOW_REF: main
TARGET_REPO_PATH: ${{ github.workspace }}/target-repo
DEVFLOW_PATH: ${{ github.workspace }}/devflow
jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 60
if: ${{ github.event_name != 'pull_request_target' || !github.event.pull_request.draft }}
steps:
- name: Resolve PR metadata
id: pr
shell: bash
env:
PR_HTML_URL: ${{ github.event.pull_request.html_url }}
PR_NUMBER_EVENT: ${{ github.event.pull_request.number }}
PR_NUMBER_INPUT: ${{ inputs.pr_number }}
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "pull_request_target" ]]; then
pr_number="${PR_NUMBER_EVENT}"
pr_url="${PR_HTML_URL}"
else
pr_number="${PR_NUMBER_INPUT}"
pr_url="https://github.com/${GITHUB_REPOSITORY}/pull/${pr_number}"
fi
if [[ ! "$pr_number" =~ ^[1-9][0-9]*$ ]]; then
echo "Could not determine PR number; for workflow_dispatch runs, the 'pr_number' input is required when not running on pull_request_target." >&2
exit 1
fi
echo "pr_url=${pr_url}" >> "$GITHUB_OUTPUT"
echo "pr_number=${pr_number}" >> "$GITHUB_OUTPUT"
echo "repo=${GITHUB_REPOSITORY}" >> "$GITHUB_OUTPUT"
# Safe checkout: base repo only, not the untrusted PR head.
- name: Checkout target repo base
uses: actions/checkout@v5
with:
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.sha || github.sha }}
fetch-depth: 0
persist-credentials: false
path: target-repo
# Private DevFlow checkout: the PAT/token grants access to this repo's code.
- name: Checkout DevFlow
uses: actions/checkout@v5
with:
repository: ${{ env.DEVFLOW_REPOSITORY }}
ref: ${{ env.DEVFLOW_REF }}
token: ${{ secrets.DEVFLOW_TOKEN }}
fetch-depth: 1
persist-credentials: false
path: devflow
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
- name: Install DevFlow dependencies
working-directory: ${{ env.DEVFLOW_PATH }}
run: uv sync --frozen
- name: Classify PR relevance
id: spam
working-directory: ${{ env.DEVFLOW_PATH }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_COPILOT_TOKEN: ${{ secrets.GH_COPILOT_TOKEN }}
SK_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
AGENT_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
PR_REPO: ${{ steps.pr.outputs.repo }}
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
run: |
uv run python scripts/classify_pr_spam.py \
--repo "$PR_REPO" \
--pr-number "$PR_NUMBER" \
--repo-path "${TARGET_REPO_PATH}" \
--apply-labels
- name: Stop after spam gate
if: ${{ steps.spam.outputs.decision != 'allow' }}
shell: bash
env:
SPAM_DECISION: ${{ steps.spam.outputs.decision }}
run: |
echo "Skipping review because spam gate decided: ${SPAM_DECISION}"
- name: Run PR review
if: ${{ steps.spam.outputs.decision == 'allow' }}
id: review
working-directory: ${{ env.DEVFLOW_PATH }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_COPILOT_TOKEN: ${{ secrets.GH_COPILOT_TOKEN }}
SK_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
AGENT_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
PR_URL: ${{ steps.pr.outputs.pr_url }}
run: |
uv run python scripts/trigger_pr_review.py \
--pr-url "$PR_URL" \
--github-username "$GITHUB_ACTOR" \
--no-require-comment-selection
+244
View File
@@ -0,0 +1,244 @@
#
# This workflow will build and run all unit tests using dotnet docker containers,
# each targeting a single version of the dotnet SDK.
#
name: dotnet-build-and-test
on:
workflow_dispatch:
pull_request:
branches: ["main", "feature*"]
merge_group:
branches: ["main"]
env:
COVERAGE_THRESHOLD: 80
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
id-token: "write"
jobs:
paths-filter:
runs-on: ubuntu-latest
outputs:
dotnetChanges: ${{ steps.filter.outputs.dotnet }}
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
dotnet:
- 'dotnet/**'
- '**/dotnet/**'
- '.github/workflows/check-coverage.ps1'
- '.github/workflows/dotnet-build-and-test.yml'
# run only if 'dotnet' files were changed
- name: dotnet tests
if: steps.filter.outputs.dotnet == 'true'
run: echo "Dotnet file"
# run only if not 'dotnet' files were changed
- name: not dotnet tests
if: steps.filter.outputs.dotnet != 'true'
run: echo "NOT dotnet file"
dotnet-build-and-test:
needs: paths-filter
if: needs.paths-filter.outputs.dotnetChanges == 'true'
strategy:
fail-fast: false
matrix:
include:
- {
dotnet: "10.0",
os: "ubuntu-latest",
configuration: Release,
integration-tests: true,
environment: "integration",
}
- { dotnet: "10.0", os: "windows-latest", configuration: Debug }
- { dotnet: "9.0", os: "windows-latest", configuration: Release }
runs-on: ${{ matrix.os }}
environment: ${{ matrix.environment }}
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup dotnet
uses: actions/setup-dotnet@v5
with:
global-json-file: ${{ github.workspace }}/dotnet/global.json
- name: Build dotnet solutions
shell: bash
run: |
export SOLUTIONS=$(find ./dotnet/ -type f -name "*.slnx" | grep -v "MEVD.slnx" | tr '\n' ' ')
for solution in $SOLUTIONS; do
dotnet build $solution -c ${{ matrix.configuration }} --warnaserror
done
- name: Package install check
shell: bash
run: |
export SOLUTIONS=$(find ./dotnet/ -type f -name "*.slnx" | grep -v "MEVD.slnx" | tr '\n' ' ')
for solution in $SOLUTIONS; do
dotnet pack $solution -c ${{ matrix.configuration }} --no-build --no-restore --output ./artifacts
done
dotnet new console --name packcheck --output consoleapp
# Create minimal nuget.config and use only dotnet nuget commands
echo '<?xml version="1.0" encoding="utf-8"?><configuration><packageSources><clear /></packageSources></configuration>' > consoleapp/nuget.config
# Add sources with local first using dotnet nuget commands
dotnet nuget add source ../artifacts --name local --configfile consoleapp/nuget.config
dotnet nuget add source https://api.nuget.org/v3/index.json --name nuget.org --configfile consoleapp/nuget.config
# Change to project directory to ensure local nuget.config is used
cd consoleapp
dotnet add packcheck.csproj package Microsoft.SemanticKernel
dotnet build -c ${{ matrix.configuration }} packcheck.csproj
cd ..
# Clean up
rm -rf ./artifacts
rm -rf ./consoleapp
- name: Run Unit Tests
shell: bash
run: |
export UT_PROJECTS=$(find ./dotnet -type f -name "*.UnitTests.csproj" | grep -v -E "(Experimental.Orchestration.Flow.UnitTests.csproj|Experimental.Assistants.UnitTests.csproj)" | grep -v "test/VectorData/" | tr '\n' ' ')
for project in $UT_PROJECTS; do
dotnet test -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --collect:"XPlat Code Coverage" --results-directory:"TestResults/Coverage/" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute
done
- name: Run AOT Unit Tests
shell: pwsh
run: .github/workflows/test-aot-compatibility.ps1 ${{ matrix.dotnet }}
- name: Azure CLI Login
if: github.event_name != 'pull_request' && matrix.integration-tests
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run Integration Tests
shell: bash
if: github.event_name != 'pull_request' && matrix.integration-tests
run: |
export INTEGRATION_TEST_PROJECTS=$(find ./dotnet -type f -name "*IntegrationTests.csproj" | grep -v "Experimental.Orchestration.Flow.IntegrationTests.csproj" | grep -v "VectorDataIntegrationTests.csproj" | tr '\n' ' ')
for project in $INTEGRATION_TEST_PROJECTS; do
dotnet test -f net10.0 -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx
done
env:
# Azure OpenAI Deployments
AzureOpenAI__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
AzureOpenAI__DeploymentName: ${{ vars.AZUREOPENAI__DEPLOYMENTNAME }}
AzureOpenAI__ChatDeploymentName: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }}
AzureOpenAIEmbeddings__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDING__DEPLOYMENTNAME }}
AzureOpenAITextToAudio__Endpoint: ${{ secrets.AZUREOPENAITEXTTOAUDIO__ENDPOINT }}
AzureOpenAITextToAudio__DeploymentName: ${{ vars.AZUREOPENAITEXTTOAUDIO__DEPLOYMENTNAME }}
AzureOpenAIAudioToText__Endpoint: ${{ secrets.AZUREOPENAIAUDIOTOTEXT__ENDPOINT }}
AzureOpenAIAudioToText__DeploymentName: ${{ vars.AZUREOPENAIAUDIOTOTEXT__DEPLOYMENTNAME }}
AzureOpenAITextToImage__Endpoint: ${{ secrets.AZUREOPENAITEXTTOIMAGE__ENDPOINT }}
AzureOpenAITextToImage__DeploymentName: ${{ vars.AZUREOPENAITEXTTOIMAGE__DEPLOYMENTNAME }}
Planners__AzureOpenAI__Endpoint: ${{ secrets.PLANNERS__AZUREOPENAI__ENDPOINT }}
Planners__AzureOpenAI__DeploymentName: ${{ vars.PLANNERS__AZUREOPENAI__DEPLOYMENTNAME }}
# OpenAI Models
OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }}
OpenAI__ChatModelId: ${{ vars.OPENAI__CHATMODELID }}
OpenAIEmbeddings__ApiKey: ${{ secrets.OPENAIEMBEDDINGS__APIKEY }}
OpenAIEmbeddings__ModelId: ${{ vars.OPENAIEMBEDDINGS__MODELID }}
OpenAITextToAudio__ApiKey: ${{ secrets.OPENAITEXTTOAUDIO__APIKEY }}
OpenAITextToAudio__ModelId: ${{ vars.OPENAITEXTTOAUDIO__MODELID }}
OpenAIAudioToText__ApiKey: ${{ secrets.OPENAIAUDIOTOTEXT__APIKEY }}
OpenAIAudioToText__ModelId: ${{ vars.OPENAIAUDIOTOTEXT__MODELID }}
OpenAITextToImage__ApiKey: ${{ secrets.OPENAITEXTTOIMAGE__APIKEY }}
OpenAITextToImage__ModelId: ${{ vars.OPENAITEXTTOIMAGE__MODELID }}
Planners__OpenAI__ApiKey: ${{ secrets.PLANNERS__OPENAI__APIKEY }}
Planners__OpenAI__ModelId: ${{ vars.PLANNERS__OPENAI__MODELID }}
# Bing Web Search
Bing__ApiKey: ${{ secrets.BING__APIKEY }}
# Google Web Search
Google__SearchEngineId: ${{ secrets.GOOGLE__SEARCHENGINEID }}
Google__ApiKey: ${{ secrets.GOOGLE__APIKEY }}
# Azure AI Inference Endpoint
AzureAIInference__ApiKey: ${{ secrets.AZUREAIINFERENCE__APIKEY }}
AzureAIInference__Endpoint: ${{ secrets.AZUREAIINFERENCE__ENDPOINT }}
AzureAIInference__ChatModelId: ${{ vars.AZUREAIINFERENCE__CHATMODELID }}
# Azure AI Foundry
AzureAI__Endpoint: ${{ secrets.AZUREAI__ENDPOINT }}
AzureAI__ConnectionString: ${{ secrets.AZUREAI__CONNECTIONSTRING }}
AzureAI__ChatModelId: ${{ vars.AZUREAI__CHATMODELID }}
# Generate test reports and check coverage
- name: Generate test reports
uses: danielpalme/ReportGenerator-GitHub-Action@5.4.12
with:
reports: "./TestResults/Coverage/**/coverage.cobertura.xml"
targetdir: "./TestResults/Reports"
reporttypes: "HtmlInline;JsonSummary"
- name: Upload coverage report artifact
uses: actions/upload-artifact@v4
with:
name: CoverageReport-${{ matrix.os }}-${{ matrix.dotnet }}-${{ matrix.configuration }} # Artifact name
path: ./TestResults/Reports # Directory containing files to upload
- name: Check coverage
shell: pwsh
run: .github/workflows/check-coverage.ps1 -JsonReportPath "TestResults/Reports/Summary.json" -CoverageThreshold $env:COVERAGE_THRESHOLD
# This final job is required to satisfy the merge queue. It must only run (or succeed) if no tests failed
dotnet-build-and-test-check:
if: always()
runs-on: ubuntu-latest
needs: [dotnet-build-and-test]
steps:
- name: Get Date
shell: bash
run: |
echo "date=$(date +'%m/%d/%Y %H:%M:%S')" >> "$GITHUB_ENV"
- name: Run Type is Daily
if: ${{ github.event_name == 'schedule' }}
shell: bash
run: |
echo "run_type=Daily" >> "$GITHUB_ENV"
- name: Run Type is Manual
if: ${{ github.event_name == 'workflow_dispatch' }}
shell: bash
run: |
echo "run_type=Manual" >> "$GITHUB_ENV"
- name: Run Type is ${{ github.event_name }}
if: ${{ github.event_name != 'schedule' && github.event_name != 'workflow_dispatch'}}
shell: bash
run: |
echo "run_type=${{ github.event_name }}" >> "$GITHUB_ENV"
- name: Fail workflow if tests failed
id: check_tests_failed
if: contains(join(needs.*.result, ','), 'failure')
uses: actions/github-script@v6
with:
script: core.setFailed('Integration Tests Failed!')
- name: Fail workflow if tests cancelled
id: check_tests_cancelled
if: contains(join(needs.*.result, ','), 'cancelled')
uses: actions/github-script@v6
with:
script: core.setFailed('Integration Tests Cancelled!')
+129
View File
@@ -0,0 +1,129 @@
#
# This workflow will build and run all tests using dotnet docker containers,
# each targeting a single version of the dotnet SDK.
#
name: dotnet-ci
on:
workflow_dispatch:
schedule:
- cron: '0 7 * * *'
permissions:
contents: read
jobs:
build-and-test-docker:
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, dotnet: '10.0', configuration: Debug }
- { os: ubuntu-latest, dotnet: '10.0', configuration: Release }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
with:
clean: true
persist-credentials: false
- name: Find solutions
shell: bash
run: echo "solutions=$(find ./ -type f -name "*.slnx" | tr '\n' ' ')" >> $GITHUB_ENV
- name: Pull container dotnet/sdk:${{ matrix.dotnet }}
run: |
docker pull mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }}
- name: Build
run: |
for solution in ${{ env.solutions }}; do
docker run --rm -v $(pwd):/app -w /app -e GITHUB_ACTIONS='true' mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet build -c ${{ matrix.configuration }} /app/$solution"
done
- name: Find test projects
shell: bash
run: echo "testprojects=$(find ./dotnet -type f -name "*UnitTests.csproj" | tr '\n' ' ')" >> $GITHUB_ENV
- name: Run Tests
shell: bash
run: |
for project in ${{ env.testprojects }}; do
docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet test -c ${{ matrix.configuration }} /app/$project --no-build -v Normal --logger trx"
done
- name: Upload dotnet test results
uses: actions/upload-artifact@v4
with:
name: dotnet-testresults-${{ matrix.configuration }}
path: ./TestResults
if: ${{ always() }}
build-and-test-windows:
strategy:
fail-fast: false
matrix:
os: [windows-latest]
configuration: [Release, Debug]
dotnet-version: ['10.0.x']
runs-on: ${{ matrix.os }}
env:
NUGET_CERT_REVOCATION_MODE: offline
steps:
- uses: actions/checkout@v5
with:
clean: true
persist-credentials: false
- name: Setup .NET SDK ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{ matrix.dotnet-version }}
env:
NUGET_AUTH_TOKEN: ${{ secrets.GPR_READ_TOKEN }}
- uses: actions/cache@v4
with:
path: ~/.nuget/packages
# Look to see if there is a cache hit for the corresponding requirements file
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget
- name: Find solutions
shell: bash
run: echo "solutions=$(find ./dotnet -type f -name "*.slnx" | tr '\n' ' ')" >> $GITHUB_ENV
- name: Restore dependencies
shell: bash
run: |
for solution in ${{ env.solutions }}; do
dotnet restore $solution
done
- name: Build
shell: bash
run: |
for solution in ${{ env.solutions }}; do
dotnet build $solution --no-restore --configuration ${{ matrix.configuration }}
done
- name: Find test projects
shell: bash
run: echo "testprojects=$(find ./dotnet -type f -name "*Tests.csproj" | tr '\n' ' ')" >> $GITHUB_ENV
- name: Run Tests
shell: bash
run: |
for project in ${{ env.testprojects }}; do
dotnet test $project --verbosity normal --logger trx --results-directory ./TestResults --configuration ${{ matrix.configuration }}
done
- name: Upload dotnet test results
uses: actions/upload-artifact@v4
with:
name: dotnet-testresults-${{ matrix.configuration }}
path: ./TestResults
if: ${{ always() }}
+94
View File
@@ -0,0 +1,94 @@
#
# This workflow runs the dotnet formatter on all c-sharp code.
#
name: dotnet-format
on:
workflow_dispatch:
pull_request:
branches: ["main", "feature*"]
paths:
- "dotnet/**"
- "samples/dotnet/**"
- "**.cs"
- "**.csproj"
- "**.editorconfig"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
check-format:
strategy:
fail-fast: false
matrix:
include:
- { dotnet: "10.0", configuration: Release, os: ubuntu-latest }
runs-on: ${{ matrix.os }}
env:
NUGET_CERT_REVOCATION_MODE: offline
steps:
- name: Check out code
uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false
- name: Get changed files
id: changed-files
if: github.event_name == 'pull_request'
uses: jitterbit/get-changed-files@v1
continue-on-error: true
- name: No C# files changed
id: no-csharp
if: github.event_name == 'pull_request' && steps.changed-files.outputs.added_modified == ''
run: echo "No C# files changed"
# This step will loop over the changed files and find the nearest .csproj file for each one, then store the unique csproj files in a variable
- name: Find csproj files
id: find-csproj
if: github.event_name != 'pull_request' || steps.changed-files.outputs.added_modified != '' || steps.changed-files.outcome == 'failure'
run: |
csproj_files=()
exclude_files=("Experimental.Orchestration.Flow.csproj" "Experimental.Orchestration.Flow.UnitTests.csproj" "Experimental.Orchestration.Flow.IntegrationTests.csproj")
if [[ ${{ steps.changed-files.outcome }} == 'success' ]]; then
for file in ${{ steps.changed-files.outputs.added_modified }}; do
echo "$file was changed"
dir="./$file"
while [[ $dir != "." && $dir != "/" && $dir != $GITHUB_WORKSPACE ]]; do
if find "$dir" -maxdepth 1 -name "*.csproj" -print -quit | grep -q .; then
csproj_path="$(find "$dir" -maxdepth 1 -name "*.csproj" -print -quit)"
if [[ ! "${exclude_files[@]}" =~ "${csproj_path##*/}" ]]; then
csproj_files+=("$csproj_path")
fi
break
fi
dir=$(echo ${dir%/*})
done
done
else
# if the changed-files step failed, run dotnet on the whole slnx instead of specific projects
csproj_files=$(find ./ -type f -name "*.slnx" | tr '\n' ' ');
fi
csproj_files=($(printf "%s\n" "${csproj_files[@]}" | sort -u))
echo "Found ${#csproj_files[@]} unique csproj/slnx files: ${csproj_files[*]}"
echo "::set-output name=csproj_files::${csproj_files[*]}"
- name: Pull container dotnet/sdk:${{ matrix.dotnet }}
if: steps.find-csproj.outputs.csproj_files != ''
run: docker pull mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }}
# This step will run dotnet format on each of the unique csproj files and fail if any changes are made
- name: Run dotnet format
if: steps.find-csproj.outputs.csproj_files != ''
run: |
for csproj in ${{ steps.find-csproj.outputs.csproj_files }}; do
echo "Running dotnet format on $csproj"
docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet format $csproj --verify-no-changes --verbosity diagnostic"
done
@@ -0,0 +1,67 @@
#
# This workflow will run all dotnet integrations tests.
#
name: dotnet-integration-tests
on:
workflow_dispatch:
pull_request:
branches: ["main"]
merge_group:
branches: ["main"]
permissions:
contents: read
jobs:
integration-tests:
strategy:
matrix:
os: [ubuntu-latest]
configuration: [Debug]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
if: ${{ github.event_name != 'pull_request' }}
with:
clean: true
persist-credentials: false
- name: Setup .NET
uses: actions/setup-dotnet@v5
if: ${{ github.event_name != 'pull_request' }}
with:
dotnet-version: 10.0.x
- name: Find projects
shell: bash
if: ${{ github.event_name != 'pull_request' }}
run: echo "projects=$(find ./dotnet -type f -name "*Tests.csproj" | tr '\n' ' ')" >> $GITHUB_ENV
- name: Integration Tests
shell: bash
if: ${{ github.event_name != 'pull_request' }}
env: # Set Azure credentials secret as an input
AzureOpenAI__Label: azure-text-davinci-003
AzureOpenAIEmbedding__Label: azure-text-embedding-ada-002
AzureOpenAI__DeploymentName: ${{ vars.AZUREOPENAI__DEPLOYMENTNAME }}
AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDING__DEPLOYMENTNAME }}
AzureOpenAI__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
AzureOpenAIEmbeddings__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
AzureOpenAI__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
AzureOpenAIEmbeddings__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
AzureAI__ConnectionString: ${{ secrets.AZUREAI__CONNECTIONSTRING }}
Bing__ApiKey: ${{ secrets.BING__APIKEY }}
OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }}
run: |
for project in ${{ env.projects }}; do
dotnet test $project --verbosity normal --logger trx --results-directory ./TestResults --configuration ${{ matrix.configuration }}
done
- name: Upload dotnet test results
uses: actions/upload-artifact@v4
with:
name: dotnet-testresults-${{ matrix.configuration }}
path: ./TestResults
if: ${{ github.event_name != 'pull_request' && always() }}
+107
View File
@@ -0,0 +1,107 @@
name: Label Discussions
on:
discussion:
types:
- created
jobs:
label_discussions:
runs-on: ubuntu-latest
permissions:
discussions: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Always add "triage" label
if: >
github.event.discussion.node_id != '' &&
(github.event.discussion.category.name == 'General' ||
github.event.discussion.category.name == 'Ideas' ||
github.event.discussion.category.name == 'Q&A')
uses: octokit/graphql-action@v2.x
with:
query: |
mutation($labelableId: ID!) {
addLabelsToLabelable(
input: {
labelableId: $labelableId
labelIds: ["LA_kwDOJDJ_Yc8AAAABU2klmQ"]
}
) {
clientMutationId
}
}
variables: |
{
"labelableId": "${{ github.event.discussion.node_id }}"
}
- name: Conditionally add "python" label
if: >
github.event.discussion.node_id != '' &&
(github.event.discussion.category.name == 'General' ||
github.event.discussion.category.name == 'Ideas' ||
github.event.discussion.category.name == 'Q&A') &&
(contains(github.event.discussion.body, 'python') ||
contains(github.event.discussion.body, 'Python') ||
contains(github.event.discussion.title, 'python') ||
contains(github.event.discussion.title, 'Python'))
uses: octokit/graphql-action@v2.x
with:
query: |
mutation($labelableId: ID!) {
addLabelsToLabelable(
input: {
labelableId: $labelableId
labelIds: ["LA_kwDOJDJ_Yc8AAAABO0f2Lg"]
}
) {
clientMutationId
}
}
variables: |
{
"labelableId": "${{ github.event.discussion.node_id }}"
}
- name: Conditionally add ".NET" label
if: >
github.event.discussion.node_id != '' &&
(github.event.discussion.category.name == 'General' ||
github.event.discussion.category.name == 'Ideas' ||
github.event.discussion.category.name == 'Q&A') &&
(contains(github.event.discussion.body, '.net') ||
contains(github.event.discussion.body, '.NET') ||
contains(github.event.discussion.title, '.net') ||
contains(github.event.discussion.title, '.NET') ||
contains(github.event.discussion.body, 'dotnet') ||
contains(github.event.discussion.body, 'DotNet') ||
contains(github.event.discussion.title, 'dotnet') ||
contains(github.event.discussion.title, 'DotNet') ||
contains(github.event.discussion.body, 'c#') ||
contains(github.event.discussion.body, 'C#') ||
contains(github.event.discussion.title, 'c#') ||
contains(github.event.discussion.title, 'C#') ||
contains(github.event.discussion.body, 'csharp') ||
contains(github.event.discussion.body, 'CSharp') ||
contains(github.event.discussion.title, 'csharp') ||
contains(github.event.discussion.title, 'CSharp'))
uses: octokit/graphql-action@v2.x
with:
query: |
mutation($labelableId: ID!) {
addLabelsToLabelable(
input: {
labelableId: $labelableId
labelIds: ["LA_kwDOJDJ_Yc8AAAABN_r7Lw"]
}
) {
clientMutationId
}
}
variables: |
{
"labelableId": "${{ github.event.discussion.node_id }}"
}
+54
View File
@@ -0,0 +1,54 @@
name: Label issues
on:
issues:
types:
- reopened
- opened
jobs:
label_issues:
name: "Issue: add labels"
if: ${{ github.event.action == 'opened' || github.event.action == 'reopened' }}
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/github-script@v6
with:
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
script: |
// Get the issue body and title
const body = context.payload.issue.body
let title = context.payload.issue.title
// Define the labels array
let labels = ["triage"]
// Check if the body or the title contains the word 'python' (case-insensitive)
if ((body != null && body.match(/python/i)) || (title != null && title.match(/python/i))) {
// Add the 'python' label to the array
labels.push("python")
}
// Check if the body or the title contains the word 'java' (case-insensitive)
if ((body != null && body.match(/java/i)) || (title != null && title.match(/java/i))) {
// Add the 'java' label to the array
labels.push("java")
}
// Check if the body or the title contains the words 'dotnet', '.net', 'c#' or 'csharp' (case-insensitive)
if ((body != null && body.match(/.net/i)) || (title != null && title.match(/.net/i)) ||
(body != null && body.match(/dotnet/i)) || (title != null && title.match(/dotnet/i)) ||
(body != null && body.match(/C#/i)) || (title != null && title.match(/C#/i)) ||
(body != null && body.match(/csharp/i)) || (title != null && title.match(/csharp/i))) {
// Add the '.NET' label to the array
labels.push(".NET")
}
// Add the labels to the issue
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: labels
});
+21
View File
@@ -0,0 +1,21 @@
# This workflow will triage pull requests and apply a label based on the
# paths that are modified in the pull request.
#
# To use this workflow, you will need to set up a .github/labeler.yml
# file with configuration. For more information, see:
# https://github.com/actions/labeler
name: Label pull request
on: [pull_request_target]
jobs:
add_label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GH_ACTIONS_PR_WRITE }}"
+73
View File
@@ -0,0 +1,73 @@
name: Label title prefix
on:
issues:
types: [labeled]
pull_request_target:
types: [labeled]
jobs:
add_title_prefix:
name: "Issue/PR: add title prefix"
continue-on-error: true
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/github-script@v6
name: "Issue/PR: update title"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
let prefixLabels = {
"python": "Python",
"java": "Java",
".NET": ".Net"
};
function addTitlePrefix(title, prefix)
{
// Update the title based on the label and prefix
// Check if the title starts with the prefix (case-sensitive)
if (!title.startsWith(prefix + ": ")) {
// If not, check if the first word is the label (case-insensitive)
if (title.match(new RegExp(`^${prefix}`, 'i'))) {
// If yes, replace it with the prefix (case-sensitive)
title = title.replace(new RegExp(`^${prefix}`, 'i'), prefix);
} else {
// If not, prepend the prefix to the title
title = prefix + ": " + title;
}
}
return title;
}
labelAdded = context.payload.label.name
// Check if the issue or PR has the label
if (labelAdded in prefixLabels) {
let prefix = prefixLabels[labelAdded];
switch(context.eventName) {
case 'issues':
github.rest.issues.update({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
title: addTitlePrefix(context.payload.issue.title, prefix)
});
break
case 'pull_request_target':
github.rest.pulls.update({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
title: addTitlePrefix(context.payload.pull_request.title, prefix)
});
break
default:
core.setFailed('Unrecognited eventName: ' + context.eventName);
}
}
+27
View File
@@ -0,0 +1,27 @@
name: Check .md links
on:
workflow_dispatch:
pull_request:
branches: ["main"]
permissions:
contents: read
jobs:
markdown-link-check:
runs-on: ubuntu-22.04
# check out the latest version of the code
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
# Checks the status of hyperlinks in all files
- name: Run linkspector
uses: umbrelladocs/action-linkspector@v1
with:
reporter: local
filter_mode: nofilter
fail_on_error: true
config_file: ".github/.linkspector.yml"
+32
View File
@@ -0,0 +1,32 @@
name: Merge Gatekeeper
on:
pull_request:
branches: [ "main", "feature*" ]
merge_group:
branches: ["main"]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
merge-gatekeeper:
runs-on: ubuntu-latest
# Restrict permissions of the GITHUB_TOKEN.
# Docs: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
permissions:
checks: read
statuses: read
steps:
- name: Run Merge Gatekeeper
# NOTE: v1 is updated to reflect the latest v1.x.y. Please use any tag/branch that suits your needs:
# https://github.com/upsidr/merge-gatekeeper/tags
# https://github.com/upsidr/merge-gatekeeper/branches
uses: upsidr/merge-gatekeeper@v1
if: github.event_name == 'pull_request'
with:
token: ${{ secrets.GITHUB_TOKEN }}
timeout: 3600
interval: 30
ignored: python-tests-coverage
+48
View File
@@ -0,0 +1,48 @@
name: Python Build Assets
on:
release:
types: [published]
permissions:
contents: write
id-token: write
jobs:
python-build-assets:
if: github.event_name == 'release' && startsWith(github.event.release.tag_name, 'python-')
name: Python Build Assets and add to Release
runs-on: ubuntu-latest
environment: "integration"
env:
UV_PYTHON: "3.10"
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Check version
run: |
echo "Building and uploading Python package version: ${{ github.event.release.tag_name }}"
- name: Build the package
run: cd python && make build
- name: Release
uses: softprops/action-gh-release@v2
with:
files: |
python/dist/*
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Start DevOps pipeline
uses: azure/cli@v2
with:
inlineScript: |
az pipelines run --id ${{ vars.ADO_PYTHON_RELEASE_ID }} --org ${{ vars.ADO_ORG }} --project ${{ vars.ADO_PROJECT_NAME }} --parameters tag=${{ github.event.release.tag_name }} delay=0
@@ -0,0 +1,566 @@
#
# This workflow will run all python integrations tests.
#
name: Python Integration Tests
on:
workflow_dispatch:
pull_request:
branches: ["main"]
merge_group:
branches: ["main"]
schedule:
- cron: "0 0 * * *" # Run at midnight UTC daily
permissions:
contents: read
id-token: "write"
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
Python_Integration_Tests: Python_Integration_Tests
INTEGRATION_TEST_SERVICE_SETUP_EXCEPTION: ${{ true }}
AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME }} # azure-text-embedding-ada-002
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_CHAT_DEPLOYMENT_NAME }}
AZURE_OPENAI_TEXT_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_TEXT_DEPLOYMENT_NAME }}
AZURE_OPENAI_AUDIO_TO_TEXT_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_AUDIO_TO_TEXT_DEPLOYMENT_NAME }}
AZURE_OPENAI_TEXT_TO_AUDIO_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_TEXT_TO_AUDIO_DEPLOYMENT_NAME }}
AZURE_OPENAI_TEXT_TO_IMAGE_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_TEXT_TO_IMAGE_DEPLOYMENT_NAME }}
AZURE_OPENAI_API_VERSION: ${{ vars.AZURE_OPENAI_API_VERSION }}
AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
AZURE_OPENAI_AUDIO_TO_TEXT_ENDPOINT: ${{ secrets.AZURE_OPENAI_AUDIO_TO_TEXT_ENDPOINT }}
AZURE_OPENAI_TEXT_TO_AUDIO_ENDPOINT: ${{ secrets.AZURE_OPENAI_TEXT_TO_AUDIO_ENDPOINT }}
AZURE_AI_AGENT_ENDPOINT: ${{ secrets.AZURE_AI_AGENT_ENDPOINT }}
AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME: ${{ secrets.AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME }}
AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME }}
BING_API_KEY: ${{ secrets.BING_API_KEY }}
OPENAI_RESPONSES_MODEL_ID: ${{ vars.OPENAI_RESPONSES_MODEL_ID }}
OPENAI_CHAT_MODEL_ID: ${{ vars.OPENAI_CHAT_MODEL_ID }}
OPENAI_TEXT_MODEL_ID: ${{ vars.OPENAI_TEXT_MODEL_ID }}
OPENAI_EMBEDDING_MODEL_ID: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
OPENAI_AUDIO_TO_TEXT_MODEL_ID: ${{ vars.OPENAI_AUDIO_TO_TEXT_MODEL_ID }}
OPENAI_TEXT_TO_AUDIO_MODEL_ID: ${{ vars.OPENAI_TEXT_TO_AUDIO_MODEL_ID }}
OPENAI_TEXT_TO_IMAGE_MODEL_ID: ${{ vars.OPENAI_TEXT_TO_IMAGE_MODEL_ID }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
PINECONE_API_KEY: ${{ secrets.PINECONE__APIKEY }}
POSTGRES_CONNECTION_STRING: ${{secrets.POSTGRES__CONNECTIONSTR}}
POSTGRES_MAX_POOL: ${{ vars.POSTGRES_MAX_POOL }}
AZURE_AI_SEARCH_API_KEY: ${{secrets.AZURE_AI_SEARCH_API_KEY}}
AZURE_AI_SEARCH_ENDPOINT: ${{secrets.AZURE_AI_SEARCH_ENDPOINT}}
MONGODB_ATLAS_CONNECTION_STRING: ${{secrets.MONGODB_ATLAS_CONNECTION_STRING}}
AZURE_KEY_VAULT_ENDPOINT: ${{secrets.AZURE_KEY_VAULT_ENDPOINT}}
AZURE_KEY_VAULT_CLIENT_ID: ${{secrets.AZURE_KEY_VAULT_CLIENT_ID}}
AZURE_KEY_VAULT_CLIENT_SECRET: ${{secrets.AZURE_KEY_VAULT_CLIENT_SECRET}}
ACA_POOL_MANAGEMENT_ENDPOINT: ${{secrets.ACA_POOL_MANAGEMENT_ENDPOINT}}
MISTRALAI_API_KEY: ${{secrets.MISTRALAI_API_KEY}}
MISTRALAI_CHAT_MODEL_ID: ${{ vars.MISTRALAI_CHAT_MODEL_ID }}
MISTRALAI_EMBEDDING_MODEL_ID: ${{ vars.MISTRALAI_EMBEDDING_MODEL_ID }}
ANTHROPIC_API_KEY: ${{secrets.ANTHROPIC_API_KEY}}
ANTHROPIC_CHAT_MODEL_ID: ${{ vars.ANTHROPIC_CHAT_MODEL_ID }}
OLLAMA_CHAT_MODEL_ID: "${{ vars.OLLAMA_CHAT_MODEL_ID || '' }}" # llama3.2:1b
OLLAMA_CHAT_MODEL_ID_IMAGE: "${{ vars.OLLAMA_CHAT_MODEL_ID_IMAGE || '' }}" # moondream
OLLAMA_CHAT_MODEL_ID_TOOL_CALL: "${{ vars.OLLAMA_CHAT_MODEL_ID_TOOL_CALL || '' }}" # llama3.2:1b
OLLAMA_TEXT_MODEL_ID: "${{ vars.OLLAMA_TEXT_MODEL_ID || '' }}" # llama3.2:1b
OLLAMA_EMBEDDING_MODEL_ID: "${{ vars.OLLAMA_EMBEDDING_MODEL_ID || '' }}" # nomic-embed-text
GOOGLE_AI_GEMINI_MODEL_ID: ${{ vars.GOOGLE_AI_GEMINI_MODEL_ID }}
GOOGLE_AI_EMBEDDING_MODEL_ID: ${{ vars.GOOGLE_AI_EMBEDDING_MODEL_ID }}
GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }}
GOOGLE_AI_CLOUD_PROJECT_ID: ${{ vars.GOOGLE_AI_CLOUD_PROJECT_ID }}
GOOGLE_AI_CLOUD_REGION: ${{ vars.GOOGLE_AI_CLOUD_REGION }}
VERTEX_AI_PROJECT_ID: ${{ vars.VERTEX_AI_PROJECT_ID }}
VERTEX_AI_GEMINI_MODEL_ID: ${{ vars.VERTEX_AI_GEMINI_MODEL_ID }}
VERTEX_AI_EMBEDDING_MODEL_ID: ${{ vars.VERTEX_AI_EMBEDDING_MODEL_ID }}
REDIS_CONNECTION_STRING: ${{ vars.REDIS_CONNECTION_STRING }}
AZURE_COSMOS_DB_NO_SQL_URL: ${{ vars.AZURE_COSMOS_DB_NO_SQL_URL }}
AZURE_COSMOS_DB_NO_SQL_KEY: ${{ secrets.AZURE_COSMOS_DB_NO_SQL_KEY }}
BEDROCK_AGENT_AGENT_RESOURCE_ROLE_ARN: ${{ secrets.BEDROCK_AGENT_AGENT_RESOURCE_ROLE_ARN }}
BEDROCK_AGENT_FOUNDATION_MODEL: ${{ vars.BEDROCK_AGENT_FOUNDATION_MODEL }}
jobs:
paths-filter:
runs-on: ubuntu-latest
outputs:
pythonChanges: ${{ steps.filter.outputs.python}}
steps:
- uses: actions/checkout@v5
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
python:
- 'python/**'
# run only if 'python' files were changed
- name: python tests
if: steps.filter.outputs.python == 'true'
run: echo "Python file"
# run only if not 'python' files were changed
- name: not python tests
if: steps.filter.outputs.python != 'true'
run: echo "NOT python file"
python-merge-gate-ai-services:
name: Python Pre-Merge Integration Tests - AI Services (incl samples using those)
needs: paths-filter
if: github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true'
strategy:
max-parallel: 1
fail-fast: false
matrix:
python-version: ["3.11"]
os: [ubuntu-latest]
defaults:
run:
working-directory: python
runs-on: ${{ matrix.os }}
environment: "integration"
env:
UV_PYTHON: ${{ matrix.python-version }}
COMPLETIONS_CONCEPT_SAMPLE: "true"
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install dependencies
run: |
uv sync --all-extras --dev
- name: Google auth
uses: google-github-actions/auth@v3
with:
project_id: ${{ vars.VERTEX_AI_PROJECT_ID }}
credentials_json: ${{ secrets.VERTEX_AI_SERVICE_ACCOUNT_KEY }}
- name: Set up gcloud
uses: google-github-actions/setup-gcloud@v3
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v5
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
- name: Azure CLI Login
if: github.event_name != 'pull_request'
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run Integration Tests
id: run_tests_ai_services
shell: bash
run: |
uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal -m "not ollama" ./tests/integration/completions ./tests/integration/embeddings ./tests/samples ./tests/integration/cross_language
python-merge-gate-multi-modality:
name: Python Pre-Merge Integration Tests - Multi-Modality
needs: paths-filter
if: github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true'
strategy:
max-parallel: 1
fail-fast: false
matrix:
python-version: ["3.11"]
os: [ubuntu-latest]
defaults:
run:
working-directory: python
runs-on: ${{ matrix.os }}
environment: "integration"
env:
UV_PYTHON: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
- name: Install dependencies
run: |
uv sync --all-extras --dev
- name: Azure CLI Login
if: github.event_name != 'pull_request'
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run Integration Tests
id: run_tests_multi_modality
shell: bash
run: |
uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal ./tests/integration/audio_to_text ./tests/integration/text_to_audio ./tests/integration/text_to_image
python-merge-gate-agents:
name: Python Pre-Merge Integration Tests - Agents
needs: paths-filter
if: github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true'
strategy:
max-parallel: 1
fail-fast: false
matrix:
python-version: ["3.11"]
os: [ubuntu-latest]
defaults:
run:
working-directory: python
runs-on: ${{ matrix.os }}
environment: "integration"
env:
UV_PYTHON: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
- name: Install dependencies
run: |
uv sync --all-extras --dev
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v5
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
- name: Azure CLI Login
if: github.event_name != 'pull_request'
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run Integration Tests
id: run_tests_agents
shell: bash
run: |
uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal ./tests/integration/agents
python-merge-gate-ollama:
name: Python Pre-Merge Integration Tests - Ollama
needs: paths-filter
# Ollama tests are very unstable at the moment. It often fails to pull models from the Ollama server. Thus, this job is disabled for now.
if: false && github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true'
strategy:
max-parallel: 1
fail-fast: false
matrix:
python-version: ["3.11"]
os: [ubuntu-latest]
defaults:
run:
working-directory: python
runs-on: ${{ matrix.os }}
environment: "integration"
env:
UV_PYTHON: ${{ matrix.python-version }}
COMPLETIONS_CONCEPT_SAMPLE: "true"
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install dependencies
run: |
uv sync --all-extras --dev
- name: Install Ollama
if: matrix.os == 'ubuntu-latest'
run: |
curl -fsSL https://ollama.com/install.sh | sh
ollama serve &
sleep 5
- name: Pull model in Ollama
if: matrix.os == 'ubuntu-latest'
run: |
ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID }}
ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID_IMAGE }}
ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID_TOOL_CALL }}
ollama pull ${{ vars.OLLAMA_TEXT_MODEL_ID }}
ollama pull ${{ vars.OLLAMA_EMBEDDING_MODEL_ID }}
ollama list
- name: Run Integration Tests
id: run_tests_ai_services
shell: bash
run: |
uv run pytest -v --log-cli-level=INFO --durations=0 -n logical --dist loadfile --dist worksteal -m ollama --timeout=300 ./tests/integration/completions ./tests/integration/embeddings
python-merge-gate-memory:
name: Python Pre-Merge Integration Tests - Memory (incl samples using those)
needs: paths-filter
if: github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true'
strategy:
max-parallel: 1
fail-fast: false
matrix:
python-version: ["3.11"]
os: [ubuntu-latest]
defaults:
run:
working-directory: python
runs-on: ${{ matrix.os }}
environment: "integration"
env:
UV_PYTHON: ${{ matrix.python-version }}
MEMORY_CONCEPT_SAMPLE: "true"
# Service containers to run with for the memory connectors, this only works on Ubuntu
services:
# Label used to access the service container
redis:
# Docker Hub image
image: redis/redis-stack-server:latest
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
weaviate:
image: cr.weaviate.io/semitechnologies/weaviate:1.33.3
ports:
- 8080:8080
- 50051:50051
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install dependencies
run: |
uv sync --all-extras --dev
- name: Azure CLI Login
if: github.event_name != 'pull_request'
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run Integration Tests
id: run_tests_memory
timeout-minutes: 15
shell: bash
run: |
uv run pytest -v --log-cli-level=INFO --durations=20 -n logical --dist loadfile --dist worksteal ./tests/integration/memory ./tests/samples
python-integration-tests:
name: Python Integration Tests - Scheduled run
needs: paths-filter
if: (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && needs.paths-filter.outputs.pythonChanges == 'true'
strategy:
max-parallel: 1
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
os: [ubuntu-latest, windows-latest, macos-latest]
defaults:
run:
working-directory: python
runs-on: ${{ matrix.os }}
env:
UV_PYTHON: ${{ matrix.python-version }}
MEMORY_CONCEPT_SAMPLE: "true"
COMPLETIONS_CONCEPT_SAMPLE: "true"
# Service containers to run with for the memory connectors, this only works on Ubuntu
services:
# Label used to access the service container
redis:
# Docker Hub image
image: redis/redis-stack-server:latest
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
weaviate:
image: cr.weaviate.io/semitechnologies/weaviate:1.26.6
ports:
- 8080:8080
- 50051:50051
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install dependencies
run: |
uv sync --all-extras --dev
- name: Install Ollama
if: matrix.os == 'ubuntu-latest'
run: |
curl -fsSL https://ollama.com/install.sh | sh
ollama serve &
sleep 5
- name: Pull model in Ollama
# Ollama tests are very unstable at the moment. It often fails to pull models from the Ollama server. Thus, Ollama is disabled for now.
if: false && matrix.os == 'ubuntu-latest'
run: |
ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID }}
ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID_IMAGE }}
ollama pull ${{ vars.OLLAMA_CHAT_MODEL_ID_TOOL_CALL }}
ollama pull ${{ vars.OLLAMA_TEXT_MODEL_ID }}
ollama pull ${{ vars.OLLAMA_EMBEDDING_MODEL_ID }}
ollama list
- name: Google auth
uses: google-github-actions/auth@v3
with:
project_id: ${{ vars.VERTEX_AI_PROJECT_ID }}
credentials_json: ${{ secrets.VERTEX_AI_SERVICE_ACCOUNT_KEY }}
- name: Set up gcloud
uses: google-github-actions/setup-gcloud@v3
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v5
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}
- name: Start Azure Cosmos DB emulator
if: matrix.os == 'windows-latest'
run: |
Write-Host "Launching Cosmos DB Emulator"
Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator"
Start-CosmosDbEmulator
- name: Azure CLI Login
if: github.event_name != 'pull_request'
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run Integration Tests - Completions
id: run_tests_completions
timeout-minutes: 10
shell: bash
# Ollama tests are very unstable at the moment. It often fails to pull models from the Ollama server. Thus, Ollama is disabled for now.
run: |
uv run pytest -v -n logical --dist loadfile --dist worksteal -m "not ollama" ./tests/integration/completions
- name: Run Integration Tests - Embeddings
id: run_tests_embeddings
timeout-minutes: 5
shell: bash
# Ollama tests are very unstable at the moment. It often fails to pull models from the Ollama server. Thus, Ollama is disabled for now.
run: |
uv run pytest -v -n logical --dist loadfile --dist worksteal -m "not ollama" ./tests/integration/embeddings
- name: Run Integration Tests - Memory
id: run_tests_memory
timeout-minutes: 5
shell: bash
run: |
uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/integration/memory
- name: Run Integration Tests - Cross Language
id: run_tests_cross_language
timeout-minutes: 5
shell: bash
run: |
uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/integration/cross_language
- name: Run Integration Tests - Planning
id: run_tests_planning
timeout-minutes: 5
shell: bash
run: |
uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/integration/planning
- name: Run Integration Tests - Samples
id: run_tests_samples
timeout-minutes: 5
shell: bash
run: |
uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/samples
- name: Run Integration Tests - Agents
id: run_tests_agents
timeout-minutes: 10
shell: bash
run: |
uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/integration/agents
- name: Run Integration Tests - Multi-Modality
id: run_tests_multi_modality
timeout-minutes: 5
shell: bash
run: |
uv run pytest -v -n logical --dist loadfile --dist worksteal ./tests/integration/audio_to_text ./tests/integration/text_to_audio ./tests/integration/text_to_image
# This final job is required to satisfy the merge queue. It must only run (or succeed) if no tests failed
python-integration-tests-check:
if: always()
runs-on: ubuntu-latest
strategy:
max-parallel: 1
fail-fast: false
needs:
[
python-merge-gate-ai-services,
python-merge-gate-ollama,
python-merge-gate-memory,
python-merge-gate-agents,
python-merge-gate-multi-modality,
python-integration-tests,
]
steps:
- name: Get Date
shell: bash
run: |
echo "date=$(date +'%m/%d/%Y %H:%M:%S')" >> "$GITHUB_ENV"
- name: Run Type is Daily
if: ${{ github.event_name == 'schedule' }}
shell: bash
run: |
echo "run_type=Daily" >> "$GITHUB_ENV"
- name: Run Type is Manual
if: ${{ github.event_name == 'workflow_dispatch' }}
shell: bash
run: |
echo "run_type=Manual" >> "$GITHUB_ENV"
- name: Run Type is ${{ github.event_name }}
if: ${{ github.event_name != 'schedule' && github.event_name != 'workflow_dispatch'}}
shell: bash
run: |
echo "run_type=${{ github.event_name }}" >> "$GITHUB_ENV"
- name: Fail workflow if tests failed
id: check_tests_failed
if: contains(join(needs.*.result, ','), 'failure')
uses: actions/github-script@v6
with:
script: core.setFailed('Integration Tests Failed!')
- name: Fail workflow if tests cancelled
id: check_tests_cancelled
if: contains(join(needs.*.result, ','), 'cancelled')
uses: actions/github-script@v6
with:
script: core.setFailed('Integration Tests Cancelled!')
- name: Microsoft Teams Notification
uses: skitionek/notify-microsoft-teams@v1.0.9
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
with:
webhook_url: ${{ secrets.MSTEAMS_WEBHOOK }}
dry_run: ${{ env.run_type != 'Daily' && env.run_type != 'Manual'}}
job: ${{ toJson(job) }}
steps: ${{ toJson(steps) }}
title: "{title: ` ${{ env.run_type }}: ${{ env.date }} `, text: ` ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`}"
- name: Microsoft Teams Notification (Dry Run)
uses: skitionek/notify-microsoft-teams@v1.0.9
if: github.ref != 'refs/heads/main'
with:
webhook_url: NONE
dry_run: ${{ env.run_type != 'Daily' && env.run_type != 'Manual'}}
job: ${{ toJson(job) }}
steps: ${{ toJson(steps) }}
title: "{title: ` ${{ env.run_type }}: ${{ env.date }} `, text: ` ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`}"
+41
View File
@@ -0,0 +1,41 @@
name: Python Code Quality Checks
on:
workflow_dispatch:
pull_request:
branches: ["main", "feature*"]
paths:
- "python/**"
jobs:
pre-commit:
if: "!cancelled()"
strategy:
fail-fast: false
matrix:
python-version: ["3.10"]
runs-on: ubuntu-latest
continue-on-error: true
defaults:
run:
working-directory: python
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
UV_PYTHON: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install the project
run: uv sync --all-extras --dev
- uses: pre-commit/action@v3.0.1
name: Run Pre-Commit Hooks
with:
extra_args: --config python/.pre-commit-config.yaml --all-files
- name: Run Mypy
run: uv run mypy -p semantic_kernel --config-file mypy.ini
@@ -0,0 +1,30 @@
name: Python Start Release on ADO
on:
workflow_dispatch:
inputs:
tag:
description: "Tag to release"
required: true
permissions:
contents: read
id-token: write
jobs:
python-start-ado-release-job:
name: Trigger ADO Pipeline for Python Release
runs-on: ubuntu-latest
environment: "integration"
steps:
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Start DevOps pipeline
uses: azure/cli@v2
with:
inlineScript: |
az pipelines run --id ${{ vars.ADO_PYTHON_RELEASE_ID }} --org ${{ vars.ADO_ORG }} --project ${{ vars.ADO_PROJECT_NAME }} --parameters tag=${{ inputs.tag }} delay=0
@@ -0,0 +1,57 @@
name: Python Test Coverage Report
on:
workflow_run:
workflows: ["Python Test Coverage"]
types:
- completed
permissions:
contents: read
actions: read
pull-requests: write
jobs:
python-test-coverage-report:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success'
continue-on-error: false
defaults:
run:
working-directory: python
steps:
- uses: actions/checkout@v5
- name: Download coverage report
uses: actions/download-artifact@v5
with:
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
path: ./python
merge-multiple: true
- name: Display structure of downloaded files
run: ls
- name: Read and set PR number
# Need to read the PR number from the file saved in the previous workflow
# because the workflow_run event does not have access to the PR number
# The PR number is needed to post the comment on the PR
run: |
PR_NUMBER=$(cat pr_number)
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "::error::PR number file contains invalid content"
exit 1
fi
echo "PR number: $PR_NUMBER"
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
- name: Pytest coverage comment
id: coverageComment
uses: MishaKav/pytest-coverage-comment@v1.1.57
with:
github-token: ${{ github.token }}
issue-number: ${{ env.PR_NUMBER }}
pytest-xml-coverage-path: python/python-coverage.xml
title: "Python Test Coverage Report"
badge-title: "Python Test Coverage"
junitxml-title: "Python Unit Test Overview"
junitxml-path: python/pytest.xml
default-branch: "main"
report-only-changed-files: true
@@ -0,0 +1,49 @@
name: Python Test Coverage
on:
pull_request:
branches: ["main", "feature*"]
paths:
- "python/semantic_kernel/**"
- "python/tests/unit/**"
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
jobs:
python-tests-coverage:
runs-on: ubuntu-latest
continue-on-error: false
defaults:
run:
working-directory: python
env:
UV_PYTHON: "3.10"
steps:
- uses: actions/checkout@v5
# Save the PR number to a file since the workflow_run event
# in the coverage report workflow does not have access to it
- name: Save PR number
run: |
echo ${{ github.event.number }} > ./pr_number
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ env.UV_PYTHON }}
cache-dependency-glob: "**/uv.lock"
- name: Install the project
run: uv sync --all-extras --dev -U --prerelease=if-necessary-or-explicit
- name: Test with pytest
run: uv run --frozen pytest -q --junitxml=pytest.xml --cov=semantic_kernel --cov-report=term-missing:skip-covered --cov-report=xml:python-coverage.xml ./tests/unit --ignore=./tests/unit/processes/dapr_runtime
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
path: |
python/python-coverage.xml
python/pytest.xml
python/pr_number
overwrite: true
retention-days: 1
if-no-files-found: error
+74
View File
@@ -0,0 +1,74 @@
name: Python Unit Tests
on:
pull_request:
branches: ["main", "feature*"]
paths:
- "python/**"
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache
jobs:
python-unit-tests:
name: Python Unit Tests
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: true
matrix:
python-version: ["3.10", "3.11", "3.12"]
os: [ubuntu-latest, windows-latest, macos-latest]
experimental: [false]
test-suite: ["unit-all-except-dapr", "dapr"]
exclude:
- python-version: "3.10"
os: macos-latest
- python-version: "3.11"
os: macos-latest
include:
- python-version: "3.13"
os: "ubuntu-latest"
experimental: true
test-suite: "unit-all-except-dapr"
env:
UV_PYTHON: ${{ matrix.python-version }}
permissions:
contents: write
defaults:
run:
working-directory: python
steps:
- uses: actions/checkout@v5
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ matrix.python-version }}
cache-dependency-glob: "**/uv.lock"
- name: Install the project (all extras)
if: matrix.test-suite == 'unit-all-except-dapr'
run: uv sync --all-extras --dev -U --prerelease=if-necessary-or-explicit
- name: Install the project (dapr tests)
if: matrix.test-suite == 'dapr'
run: uv sync --extra pandas --dev -U --prerelease=if-necessary-or-explicit && uv pip install "dapr>=1.14.0" "dapr-ext-fastapi>=1.14.0" "flask-dapr>=1.14.0"
- name: Test with pytest (all except dapr)
if: matrix.test-suite == 'unit-all-except-dapr'
env:
PYTHON_GIL: ${{ matrix.gil }}
run: uv run --frozen pytest --junitxml=pytest.xml ./tests/unit --ignore=tests/unit/processes/dapr_runtime
- name: Test dapr with pytest
if: matrix.test-suite == 'dapr'
env:
PYTHON_GIL: ${{ matrix.gil }}
run: uv run --frozen pytest --junitxml=pytest-dapr.xml ./tests/unit/processes/dapr_runtime
- name: Surface failing tests
if: ${{ !matrix.experimental && matrix.test-suite == 'unit-all-except-dapr' }}
uses: pmeier/pytest-results-action@v0.7.2
with:
path: python/pytest.xml
summary: true
display-options: fEX
fail-on-empty: true
title: Test results
@@ -0,0 +1,48 @@
param([string]$targetNetFramework)
$targetNetFramework = "net$targetNetFramework"
$rootDirectory = Get-Location
Write-Host "Publishing the SemanticKernel.AotTests application."
dotnet publish $rootDirectory/dotnet/src/SemanticKernel.AotTests/SemanticKernel.AotTests.csproj --framework $targetNetFramework | Tee-Object -Variable publishOutput
$warningFound = $false
if ($LastExitCode -ne 0)
{
Write-Host "Errors were detected while publishing the application. See the output for more details."
Exit $LastExitCode
}
elseif ($publishOutput -like "*analysis warning IL*" -or $publishOutput -like "*analysis error IL*")
{
Write-Host "Native AOT analysis warnings were detected while publishing the application. See the output for more details."
Exit 1
}
Write-Host "The application was published successfully."
$runtime = $IsWindows ? "win-x64" : "linux-x64"
$appPublishDirectory = Join-Path -Path $rootDirectory -ChildPath dotnet/src/SemanticKernel.AotTests/bin/Release/$targetNetFramework/$runtime/publish
$appFileName = $IsWindows ? "SemanticKernel.AotTests.exe" : "SemanticKernel.AotTests"
$app = Join-Path -Path $appPublishDirectory -ChildPath $appFileName
Write-Host "Executing the SemanticKernel.AotTests application."
& $app
if ($LastExitCode -ne 0)
{
$testPassed = 1
Write-Host "There was an error while executing the application. The Last Exit Code is: $LastExitCode"
}
else
{
Write-Host "The application was executed successfully."
}
Exit $testPassed
+31
View File
@@ -0,0 +1,31 @@
# Check pull requests for typos.
#
# Configuration: .github/_typos.toml
#
# Info: https://github.com/marketplace/actions/typos-action
# Local install: brew install typos-cli
# Local install: conda install typos
# Local run: typos -c .github/_typos.toml
name: Spell Check
on:
workflow_dispatch:
pull_request:
branches: ["main", "feature*"]
jobs:
run:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Use custom config file
uses: crate-ci/typos@v1.36.2
with:
config: .github/_typos.toml
write_changes: false
+107
View File
@@ -0,0 +1,107 @@
#!/bin/bash
POSITIONAL_ARGS=()
while [[ $# -gt 0 ]]; do
case $1 in
-f|--file)
file="$2"
shift # past argument
shift # past value
;;
-p|--propsFile)
propsFile="$2"
shift # past argument
shift # past value
;;
-b|--buildAndRevisionNumber)
buildAndRevisionNumber="$2"
shift # past argument
shift # past value
;;
-*|--*)
echo "Unknown option $1"
exit 1
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # past argument
;;
esac
done
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
if [ -z "$file" ]; then
echo "ERROR: Parameter file (-f|--file) not provided"
exit 1;
elif [ ! -f "$file" ]; then
echo "ERROR: file ${file} not found"
exit 1;
fi
if [ -n "$(cat $file | grep -i "<IsPackable>false</IsPackable>")" ]; then
echo "Project is marked as NOT packable - skipping."
exit 0;
fi
if [ -z "$propsFile" ]; then
echo "ERROR: Parameter propsFile (-f|--file) not provided"
exit 1;
elif [ ! -f "$propsFile" ]; then
echo "ERROR: propsFile ${file} not found"
exit 1;
fi
if [ -z "$buildAndRevisionNumber" ]; then
echo "ERROR: Parameter buildAndRevisionNumber (-b|--buildAndRevisionNumber) not provided"
exit 1;
fi
propsVersionString=$(cat $propsFile | grep -i "<Version>");
regex="<Version>([0-9.]*)<\/Version>"
if [[ $propsVersionString =~ $regex ]]; then
propsVersion=${BASH_REMATCH[1]}
else
echo "ERROR: Version tag not found in propsFile"
exit 1;
fi
if [ -z "$propsVersion" ]; then
echo "ERROR: Version tag not found in propsFile"
exit 1;
elif [[ ! "$propsVersion" =~ ^0.* ]]; then
echo "ERROR: Version expected to start with 0. Actual: ${propsVersion}"
exit 1;
fi
fullVersionString="${propsVersion}.${buildAndRevisionNumber}-preview"
if [[ ! "$fullVersionString" =~ ^0.* ]]; then
echo "ERROR: Version expected to start with 0. Actual: ${fullVersionString}"
exit 1;
fi
echo "==== Project: ${file} ====";
echo "propsFile = ${propsFile}"
echo "buildAndRevisionNumber = ${buildAndRevisionNumber}"
echo "version prefix from propsFile = ${propsVersion}"
echo "full version string: ${fullVersionString}"
versionInProj=$(cat $file | grep -i "<Version>");
if [ -n "$versionInProj" ]; then
# Version tag already exists in the csproj. Let's replace it.
echo "Updating version tag..."
content=$(cat $file | sed --expression="s/<Version>\([0-9]*.[0-9]*\)<\/Version>/<Version>$fullVersionString<\/Version>/g");
else
# Version tag not found in the csproj. Let's add it.
echo "Project is packable - adding version tag..."
content=$(cat $file | sed --expression="s/<\/Project>/<PropertyGroup><Version>$fullVersionString<\/Version><\/PropertyGroup><\/Project>/g");
fi
if [ $? -ne 0 ]; then exit 1; fi
echo "$content" && echo "$content" > $file;
if [ $? -ne 0 ]; then exit 1; fi
echo "DONE";
echo "";