mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
Merge remote-tracking branch 'upstream/java-development' into java-v1
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {},
|
||||
"ghcr.io/devcontainers/features/dotnet:1": {
|
||||
"version": "7"
|
||||
"version": "8"
|
||||
},
|
||||
"ghcr.io/jlaundry/devcontainer-features/azure-functions-core-tools:1": {}
|
||||
},
|
||||
|
||||
+2
-1
@@ -150,7 +150,6 @@ dotnet_diagnostic.IDE0071.severity = warning # Simplify interpolation
|
||||
dotnet_diagnostic.IDE0073.severity = warning # Require file header
|
||||
dotnet_diagnostic.IDE0082.severity = warning # Convert typeof to nameof
|
||||
dotnet_diagnostic.IDE0090.severity = warning # Simplify new expression
|
||||
dotnet_diagnostic.IDE0130.severity = warning # Namespace does not match folder structure
|
||||
dotnet_diagnostic.IDE0161.severity = warning # Use file-scoped namespace
|
||||
|
||||
# Suppressed diagnostics
|
||||
@@ -159,6 +158,7 @@ dotnet_diagnostic.CA1032.severity = none # We're using RCS1194 which seems to co
|
||||
dotnet_diagnostic.CA1034.severity = none # Do not nest type. Alternatively, change its accessibility so that it is not externally visible
|
||||
dotnet_diagnostic.CA1062.severity = none # Disable null check, C# already does it for us
|
||||
dotnet_diagnostic.CA1303.severity = none # Do not pass literals as localized parameters
|
||||
dotnet_diagnostic.CA1508.severity = none # Avoid dead conditional code. Too many false positives.
|
||||
dotnet_diagnostic.CA1510.severity = none
|
||||
dotnet_diagnostic.CA1805.severity = none # Member is explicitly initialized to its default value
|
||||
dotnet_diagnostic.CA1822.severity = none # Member does not access instance data and can be marked as static
|
||||
@@ -220,6 +220,7 @@ dotnet_diagnostic.IDE0079.severity = none # Remove unnecessary suppression.
|
||||
dotnet_diagnostic.IDE0080.severity = none # Remove unnecessary suppression operator.
|
||||
dotnet_diagnostic.IDE0100.severity = none # Remove unnecessary equality operator
|
||||
dotnet_diagnostic.IDE0110.severity = none # Remove unnecessary discards
|
||||
dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure
|
||||
dotnet_diagnostic.IDE0032.severity = none # Use auto property
|
||||
dotnet_diagnostic.IDE0160.severity = none # Use block-scoped namespace
|
||||
dotnet_diagnostic.IDE1006.severity = warning # Naming rule violations
|
||||
|
||||
@@ -18,6 +18,7 @@ extend-exclude = [
|
||||
|
||||
[default.extend-words]
|
||||
ACI = "ACI" # Azure Container Instance
|
||||
exercize = "exercize" #test typos
|
||||
|
||||
[default.extend-identifiers]
|
||||
ags = "ags" # Azure Graph Service
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
param (
|
||||
[string]$JsonReportPath,
|
||||
[double]$CoverageThreshold
|
||||
)
|
||||
|
||||
$jsonContent = Get-Content $JsonReportPath -Raw | ConvertFrom-Json
|
||||
$coverageBelowThreshold = $false
|
||||
|
||||
function Get-FormattedValue($number) {
|
||||
$formattedNumber = "{0:N1}" -f $number
|
||||
$icon = if ($number -ge $CoverageThreshold) { '✅' } else { '❌' }
|
||||
|
||||
return "$formattedNumber% $icon"
|
||||
}
|
||||
|
||||
$lineCoverage = $jsonContent.summary.linecoverage
|
||||
$branchCoverage = $jsonContent.summary.branchcoverage
|
||||
|
||||
if ($lineCoverage -lt $CoverageThreshold -or $branchCoverage -lt $CoverageThreshold) {
|
||||
$coverageBelowThreshold = $true
|
||||
}
|
||||
|
||||
$totalTableData = [PSCustomObject]@{
|
||||
'Metric' = 'Total Coverage'
|
||||
'Line Coverage' = Get-FormattedValue $lineCoverage
|
||||
'Branch Coverage' = Get-FormattedValue $branchCoverage
|
||||
}
|
||||
|
||||
$totalTableData | Format-Table -AutoSize
|
||||
|
||||
$assemblyTableData = @()
|
||||
|
||||
foreach ($assembly in $jsonContent.coverage.assemblies) {
|
||||
$assemblyName = $assembly.name
|
||||
$assemblyLineCoverage = $assembly.coverage
|
||||
$assemblyBranchCoverage = $assembly.branchcoverage
|
||||
|
||||
if ($assemblyLineCoverage -lt $CoverageThreshold -or $assemblyBranchCoverage -lt $CoverageThreshold) {
|
||||
$coverageBelowThreshold = $true
|
||||
}
|
||||
|
||||
$assemblyTableData += [PSCustomObject]@{
|
||||
'Assembly Name' = $assemblyName
|
||||
'Line' = Get-FormattedValue $assemblyLineCoverage
|
||||
'Branch' = Get-FormattedValue $assemblyBranchCoverage
|
||||
}
|
||||
}
|
||||
|
||||
$assemblyTableData | Format-Table -AutoSize
|
||||
|
||||
if ($coverageBelowThreshold) {
|
||||
Write-Host "Code coverage is lower than defined threshold: $CoverageThreshold. Stopping the task."
|
||||
exit 1
|
||||
}
|
||||
@@ -12,6 +12,9 @@ on:
|
||||
merge_group:
|
||||
branches: ["main"]
|
||||
|
||||
env:
|
||||
COVERAGE_THRESHOLD: 80
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -51,11 +54,7 @@ jobs:
|
||||
include:
|
||||
- { dotnet: "6.0-jammy", os: "ubuntu", configuration: Debug }
|
||||
- { dotnet: "7.0-jammy", os: "ubuntu", configuration: Release }
|
||||
- {
|
||||
dotnet: "8.0-preview-jammy",
|
||||
os: "ubuntu",
|
||||
configuration: Release,
|
||||
}
|
||||
- { dotnet: "8.0-jammy", os: "ubuntu", configuration: Release }
|
||||
- { dotnet: "6.0", os: "windows", configuration: Release }
|
||||
- {
|
||||
dotnet: "7.0",
|
||||
@@ -63,7 +62,7 @@ jobs:
|
||||
configuration: Debug,
|
||||
integration-tests: true,
|
||||
}
|
||||
- { dotnet: "8.0-preview", os: "windows", configuration: Release }
|
||||
- { dotnet: "8.0", os: "windows", configuration: Release }
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
@@ -84,15 +83,15 @@ jobs:
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
export UT_PROJECTS=$(find ./dotnet -type f -name "*.UnitTests.csproj" | tr '\n' ' ')
|
||||
export UT_PROJECTS=$(find ./dotnet -type f -name "*.UnitTests.csproj" | grep -v -E "(Planners.Core.UnitTests.csproj|Experimental.Orchestration.Flow.UnitTests.csproj|Experimental.Assistants.UnitTests.csproj)" | tr '\n' ' ')
|
||||
for project in $UT_PROJECTS; do
|
||||
dotnet test -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx
|
||||
dotnet test -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --collect:"XPlat Code Coverage" --results-directory:"TestResults/Coverage/"
|
||||
done
|
||||
|
||||
- name: Run Integration Tests
|
||||
if: github.event_name != 'pull_request' && matrix.integration-tests
|
||||
run: |
|
||||
export INTEGRATION_TEST_PROJECTS=$(find ./dotnet -type f -name "*IntegrationTests.csproj" | tr '\n' ' ')
|
||||
export INTEGRATION_TEST_PROJECTS=$(find ./dotnet -type f -name "*IntegrationTests.csproj" | grep -v "Experimental.Orchestration.Flow.IntegrationTests.csproj" | tr '\n' ' ')
|
||||
for project in $INTEGRATION_TEST_PROJECTS; do
|
||||
dotnet test -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx
|
||||
done
|
||||
@@ -105,9 +104,28 @@ jobs:
|
||||
AzureOpenAIEmbeddings__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
|
||||
AzureOpenAI__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
|
||||
AzureOpenAIEmbeddings__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
|
||||
Planners__AzureOpenAI__ApiKey: ${{ secrets.PLANNERS__AZUREOPENAI__APIKEY }}
|
||||
Planners__AzureOpenAI__Endpoint: ${{ secrets.PLANNERS__AZUREOPENAI__ENDPOINT }}
|
||||
Planners__AzureOpenAI__DeploymentName: ${{ vars.PLANNERS__AZUREOPENAI__DEPLOYMENTNAME }}
|
||||
Planners__OpenAI__ApiKey: ${{ secrets.PLANNERS__OPENAI__APIKEY }}
|
||||
Planners__OpenAI__ModelId: ${{ vars.PLANNERS__OPENAI__MODELID }}
|
||||
Bing__ApiKey: ${{ secrets.BING__APIKEY }}
|
||||
OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }}
|
||||
|
||||
# Generate test reports and check coverage
|
||||
- name: Generate test reports
|
||||
uses: danielpalme/[email protected]
|
||||
with:
|
||||
reports: "./TestResults/Coverage/**/coverage.cobertura.xml"
|
||||
targetdir: "./TestResults/Reports"
|
||||
reporttypes: "JsonSummary"
|
||||
# Report for production packages only
|
||||
assemblyfilters: "+Microsoft.SemanticKernel.Abstractions;+Microsoft.SemanticKernel.Core;+Microsoft.SemanticKernel.PromptTemplates.Handlebars;+Microsoft.SemanticKernel.Connectors.OpenAI;+Microsoft.SemanticKernel.Yaml;"
|
||||
|
||||
- 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()
|
||||
|
||||
@@ -22,14 +22,14 @@ jobs:
|
||||
- { os: ubuntu-latest, dotnet: '6.0', configuration: Debug }
|
||||
- { os: ubuntu-latest, dotnet: '6.0', configuration: Release }
|
||||
- { os: ubuntu-latest, dotnet: '7.0', configuration: Release }
|
||||
- { os: ubuntu-latest, dotnet: '8.0-preview', configuration: Release }
|
||||
|
||||
- { os: ubuntu-latest, dotnet: '8.0', configuration: Release }
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
clean: true
|
||||
|
||||
|
||||
- name: Find solutions
|
||||
shell: bash
|
||||
run: echo "solutions=$(find ./ -type f -name "*.sln" | tr '\n' ' ')" >> $GITHUB_ENV
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -26,19 +26,19 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
#- { dotnet: '6.0', configuration: Release, os: ubuntu-latest }
|
||||
- { dotnet: '7.0', configuration: Release, os: ubuntu-latest }
|
||||
#- { dotnet: '8.0-preview', configuration: Release, os: ubuntu-latest }
|
||||
|
||||
#- { dotnet: '7.0', configuration: Release, os: ubuntu-latest }
|
||||
- { dotnet: '8.0', configuration: Release, os: ubuntu-latest }
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
NUGET_CERT_REVOCATION_MODE: offline
|
||||
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
if: github.event_name == 'pull_request'
|
||||
@@ -56,16 +56,20 @@ jobs:
|
||||
if: github.event_name != 'pull_request' || steps.changed-files.outputs.added_modified != '' || steps.changed-files.outcome == 'failure'
|
||||
run: |
|
||||
csproj_files=()
|
||||
exclude_files=("Planners.Core.csproj" "Planners.Core.UnitTests.csproj" "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_files+=("$(find "$dir" -maxdepth 1 -name "*.csproj" -print -quit)")
|
||||
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
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
configuration: [Debug]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
with:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
name: Build Java Semantic Kernel
|
||||
|
||||
# Triggers the workflow on manual dispatch, push, and pull request events
|
||||
# for the specified branches and paths
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
@@ -15,6 +17,7 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Determines if Java files have changed
|
||||
paths-filter:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
@@ -28,14 +31,14 @@ jobs:
|
||||
java:
|
||||
- 'java/**'
|
||||
- '**/java/**'
|
||||
# run only if 'java' files were changed
|
||||
- name: java tests
|
||||
if: steps.filter.outputs.java == 'true'
|
||||
run: echo "Java file"
|
||||
# run only if not 'java' files were changed
|
||||
- name: not java tests
|
||||
if: steps.filter.outputs.java != 'true'
|
||||
run: echo "NOT java file"
|
||||
|
||||
# Builds and tests the Java project
|
||||
java-build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: paths-filter
|
||||
@@ -43,6 +46,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Defines a matrix strategy for JDK versions 8 and 17
|
||||
java-versions: [8, 17]
|
||||
|
||||
name: Java CI on JDK${{ matrix.java-versions }}
|
||||
@@ -63,20 +67,24 @@ jobs:
|
||||
echo "JDK_VERSION=$version" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- uses: actions/setup-java@v3
|
||||
# Sets up the specified JDK version from the matrix
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: ${{ steps.set-jdk.outputs.JDK_VERSION }}
|
||||
distribution: microsoft
|
||||
cache: maven
|
||||
|
||||
# Builds the project with Maven using the matrix JDK version
|
||||
- name: Build with Maven
|
||||
run: ./mvnw -B -Pbug-check -DskipTests -Pcompile-jdk${{ matrix.java-versions }} clean install --file pom.xml
|
||||
working-directory: java
|
||||
|
||||
# Runs tests with Maven using the matrix JDK version
|
||||
- name: Run tests
|
||||
run: ./mvnw -B -Pbug-check -Pcompile-jdk${{ matrix.java-versions }} test --file pom.xml
|
||||
working-directory: java
|
||||
|
||||
# Uploads test artifacts for each JDK version
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
name: Java format automation
|
||||
on:
|
||||
issue_comment:
|
||||
types: [ created ]
|
||||
|
||||
jobs:
|
||||
comment-driven-automation:
|
||||
if: |
|
||||
github.event.issue.pull_request &&
|
||||
(
|
||||
startsWith(github.event.comment.body, '/spotless') ||
|
||||
startsWith(github.event.comment.body, '/help')
|
||||
)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Check for command
|
||||
id: command
|
||||
uses: xt0rted/slash-command-action@v2
|
||||
continue-on-error: true
|
||||
with:
|
||||
command: spotless
|
||||
reaction-type: "eyes"
|
||||
|
||||
- name: Get command
|
||||
env:
|
||||
BODY: ${{ github.event.comment.body }}
|
||||
run: |
|
||||
# intentionally only looking at the first line of the body
|
||||
command=$(echo "$BODY" | head -1 | sed "s;^/;;")
|
||||
echo "COMMAND=$command" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check out PR branch
|
||||
env:
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh pr checkout $NUMBER
|
||||
if: env.COMMAND == 'spotless'
|
||||
|
||||
- name: Set up Maven cache
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: microsoft
|
||||
cache: maven
|
||||
if: env.COMMAND == 'spotless'
|
||||
|
||||
- name: Set git user
|
||||
run: |
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email github-action[bot]@users.noreply.github.com
|
||||
if: env.COMMAND == 'spotless'
|
||||
|
||||
- name: Run command
|
||||
env:
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
available_commands="Available commands:
|
||||
* \`/spotless\` - runs \`./mvnw process-sources -DskipTests -Pbug-check\`
|
||||
* \`/help\` - displays available commands
|
||||
"
|
||||
if [[ "$COMMAND" == "spotless" ]]; then
|
||||
./mvnw process-sources -DskipTests -Pbug-check
|
||||
if git diff --quiet; then
|
||||
gh pr comment $NUMBER --body "Already up-to-date"
|
||||
exit 0 # success
|
||||
fi
|
||||
git commit -a -m "./mvnw process-sources -DskipTests -Pbug-check"
|
||||
git push
|
||||
elif [[ "$COMMAND" == "help" ]]; then
|
||||
gh pr comment $NUMBER --body "$available_commands"
|
||||
else
|
||||
body="Unknown command: \`$COMMAND\`
|
||||
|
||||
$available_commands
|
||||
"
|
||||
gh pr comment $NUMBER --body "$body"
|
||||
fi
|
||||
working-directory: java
|
||||
@@ -3,8 +3,8 @@ name: Check .md links
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
branches: ["main", "java-development"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -13,11 +13,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
# check out the latest version of the code
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Checks the status of hyperlinks in .md files in verbose mode
|
||||
- name: Check links
|
||||
uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
with:
|
||||
use-verbose-mode: 'yes'
|
||||
config-file: ".github/workflows/markdown-link-check-config.json"
|
||||
# Checks the status of hyperlinks in .md files in verbose mode
|
||||
- name: Check links
|
||||
uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
with:
|
||||
use-verbose-mode: "yes"
|
||||
config-file: ".github/workflows/markdown-link-check-config.json"
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||
|
||||
name: node-pr
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "samples/apps/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
find-yarn-projects:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-yarn-folders.outputs.matrix }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Find yarn projects
|
||||
id: set-yarn-folders
|
||||
# This step uses a bash script to find all subfolders of /samples that contain a yarn.lock file
|
||||
run: |
|
||||
#!/bin/bash
|
||||
set -e # exit with nonzero exit code if anything fails
|
||||
shopt -s globstar # enable globstar option to use ** for recursive matching
|
||||
yarndirs=()
|
||||
for lockfile in samples/apps/**/yarn.lock; do # loop over all yarn.lock files
|
||||
dir=$(dirname "$lockfile") # get the directory of the lock file
|
||||
echo "Found yarn project in $dir"
|
||||
yarndirs+=("$dir") # add the directory to the yarndirs array
|
||||
done
|
||||
|
||||
echo "All yarn projects found: '${yarndirs[*]}'"
|
||||
yarndirs_json=$(echo -n "${yarndirs[*]%\n}" | jq -R -s -j --compact-output 'split(" ")')
|
||||
matrix_json="{\"node_version\":[18], \"yarn_folder\":$yarndirs_json}"
|
||||
echo "Setting output matrix to $matrix_json"
|
||||
echo "matrix=$matrix_json" >> $GITHUB_OUTPUT
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: find-yarn-projects
|
||||
|
||||
strategy:
|
||||
matrix: ${{ fromJson(needs.find-yarn-projects.outputs.matrix) }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: "yarn"
|
||||
cache-dependency-path: "samples/apps/**/yarn.lock"
|
||||
|
||||
- name: Run yarn install & yarn build
|
||||
# This step runs yarn install and yarn build for each project.
|
||||
# The --frozen-lockfile option ensures that the dependencies are installed exactly as specified in the lock file.
|
||||
# The -cwd option sets the current working directory to the folder where the yarn.lock file is located.
|
||||
run: |
|
||||
#!/bin/bash
|
||||
set -e # exit with nonzero exit code if anything fails
|
||||
dir=${{ matrix.yarn_folder }} # get the directory of the lock file
|
||||
echo "Running yarn install and yarn build for $dir"
|
||||
yarn --cwd "$dir" install --frozen-lockfile # install dependencies
|
||||
yarn --cwd "$dir" build # run build script
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
run: |
|
||||
python -m pip install poetry pytest
|
||||
cd python
|
||||
poetry install --without chromadb --without hugging_face --without azure_cognitive_search --without weaviate --without pinecone --without postgres --without qdrant --without redis
|
||||
poetry install --without chromadb --without azure_cognitive_search --without weaviate --without pinecone --without postgres --without qdrant --without redis
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
cd python && poetry run pytest ./tests/unit
|
||||
|
||||
@@ -12,18 +12,18 @@ name: Spell Check
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [ "main", "feature*" ]
|
||||
branches: ["main", "java-development", "feature*"]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use custom config file
|
||||
uses: crate-ci/typos@master
|
||||
with:
|
||||
config: .github/_typos.toml
|
||||
write_changes: false
|
||||
- name: Use custom config file
|
||||
uses: crate-ci/typos@master
|
||||
with:
|
||||
config: .github/_typos.toml
|
||||
write_changes: false
|
||||
|
||||
+6
-2
@@ -409,15 +409,19 @@ FodyWeavers.xsd
|
||||
*.key
|
||||
*.pem
|
||||
|
||||
# JetBrains IntelliJ
|
||||
.idea
|
||||
*.ipr
|
||||
*.iml
|
||||
*.iws
|
||||
|
||||
.env
|
||||
certs/
|
||||
launchSettings.json
|
||||
!samples/dotnet/MsGraphPluginsExample/Properties/launchSettings.json
|
||||
config.development.yaml
|
||||
*.development.config
|
||||
*.development.json
|
||||
.DS_Store
|
||||
.idea/
|
||||
node_modules/
|
||||
obj/
|
||||
bin/
|
||||
|
||||
Vendored
+8
-2
@@ -71,7 +71,8 @@
|
||||
"**/Thumbs.db": true
|
||||
},
|
||||
"cSpell.words": [
|
||||
"Partitioner"
|
||||
"Partitioner",
|
||||
"SKEXP"
|
||||
],
|
||||
"[java]": {
|
||||
"editor.formatOnSave": false,
|
||||
@@ -90,5 +91,10 @@
|
||||
},
|
||||
"java.debug.settings.onBuildFailureProceed": true,
|
||||
"java.compile.nullAnalysis.mode": "disabled",
|
||||
"dotnet.defaultSolution": "dotnet\\SK-dotnet.sln"
|
||||
"dotnet.defaultSolution": "dotnet\\SK-dotnet.sln",
|
||||
"python.testing.pytestArgs": [
|
||||
"python/tests"
|
||||
],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true
|
||||
}
|
||||
Vendored
-64
@@ -371,70 +371,6 @@
|
||||
"group": "PR-Validate"
|
||||
}
|
||||
},
|
||||
// KernelHttpServer
|
||||
{
|
||||
"label": "run (KernelHttpServer)",
|
||||
"command": "func",
|
||||
"type": "shell",
|
||||
"args": ["start", "--csharp"],
|
||||
"group": "test",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/samples/dotnet/KernelHttpServer"
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"group": "Run-Samples"
|
||||
}
|
||||
},
|
||||
// chat-summary-webapp-react
|
||||
{
|
||||
"label": "run (Starter Chat WebApp React)",
|
||||
"command": "yarn",
|
||||
"type": "shell",
|
||||
"args": ["start"],
|
||||
"group": "test",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/samples/apps/chat-summary-webapp-react"
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"group": "Run-Samples"
|
||||
}
|
||||
},
|
||||
// book-creator-webapp-react
|
||||
{
|
||||
"label": "run (Book Creator WebApp React)",
|
||||
"command": "yarn",
|
||||
"type": "shell",
|
||||
"args": ["start"],
|
||||
"group": "test",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/samples/apps/book-creator-webapp-react"
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"group": "Run-Samples"
|
||||
}
|
||||
},
|
||||
// auth-api-webapp-react
|
||||
{
|
||||
"label": "run (Auth API WebApp React)",
|
||||
"command": "yarn",
|
||||
"type": "shell",
|
||||
"args": ["start"],
|
||||
"group": "test",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/samples/apps/auth-api-webapp-react"
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"group": "Run-Samples"
|
||||
}
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
|
||||
+1
-2
@@ -15,8 +15,7 @@ Add Semantic Kernel events to your calendar - we're running two community calls
|
||||
* Americas timezone: download the [calendar.ics](https://aka.ms/sk-community-calendar) file.
|
||||
* Asia Pacific timezone: download the [calendar-APAC.ics](https://aka.ms/sk-community-calendar-apac) file.
|
||||
|
||||
To keep topics organized, please submit what you'd like us to cover here:
|
||||
[https://forms.office.com/r/BbXFzmmFys](https://forms.office.com/r/BbXFzmmFys)
|
||||
If you have any questions or if you would like to showcase your project(s), please email what you'd like us to cover here: skofficehours[at]microsoft.com.
|
||||
|
||||
If you are unable to make it live, all meetings will be recorded and posted online.
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ feature parity between our currently supported languages.
|
||||
<td>
|
||||
<img align="left" width=52px height=52px src="https://upload.wikimedia.org/wikipedia/en/3/30/Java_programming_language_logo.svg" alt="Java logo">
|
||||
<div>
|
||||
<a href="https://github.com/microsoft/semantic-kernel/blob/experimental-java/java/README.md">Using Semantic Kernel in Java</a>
|
||||
<a href="https://github.com/microsoft/semantic-kernel/blob/main/java/README.md">Using Semantic Kernel in Java</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -65,7 +65,7 @@ from either OpenAI or Azure OpenAI and to run one of the C#, Python, and Java co
|
||||
### For C#:
|
||||
|
||||
1. Create a new console app.
|
||||
2. Add the semantic kernel nuget `Microsoft.SemanticKernel`.
|
||||
2. Add the semantic kernel nuget [Microsoft.SemanticKernel](https://www.nuget.org/packages/Microsoft.SemanticKernel/).
|
||||
3. Copy the code from [here](dotnet/README.md) into the app `Program.cs` file.
|
||||
4. Replace the configuration placeholders for API key and other params with your key and settings.
|
||||
5. Run with `F5` or `dotnet run`
|
||||
@@ -80,8 +80,9 @@ from either OpenAI or Azure OpenAI and to run one of the C#, Python, and Java co
|
||||
|
||||
### For Java:
|
||||
|
||||
1. Clone and checkout the experimental Java branch: `git clone -b experimental-java https://github.com/microsoft/semantic-kernel.git`
|
||||
2. Follow the instructions [here](https://github.com/microsoft/semantic-kernel/blob/experimental-java/java/samples/sample-code/README.md)
|
||||
1. Clone the repository: `git clone https://github.com/microsoft/semantic-kernel.git`
|
||||
1. To access the latest Java code, clone and checkout the Java development branch: `git clone -b java-development https://github.com/microsoft/semantic-kernel.git`
|
||||
2. Follow the instructions [here](https://github.com/microsoft/semantic-kernel/blob/main/java/samples/sample-code/README.md)
|
||||
|
||||
## Learning how to use Semantic Kernel
|
||||
|
||||
@@ -164,6 +165,10 @@ To learn more and get started:
|
||||
- Attend [regular office hours and SK community events](COMMUNITY.md)
|
||||
- Follow the team on our [blog](https://aka.ms/sk/blog)
|
||||
|
||||
## Contributor Wall of Fame
|
||||
|
||||
[](https://github.com/microsoft/semantic-kernel/graphs/contributors)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the
|
||||
|
||||
+5
-5
@@ -4,7 +4,7 @@
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://www.microsoft.com/en-us/msrc/definition-of-a-security-vulnerability?rtc=1), please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
@@ -12,9 +12,9 @@ If you believe you have found a security vulnerability in any Microsoft-owned re
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
|
||||
|
||||
If you prefer to submit without logging in, send email to [[email protected]](mailto:[email protected]). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
|
||||
If you prefer to submit without logging in, send email to [[email protected]](mailto:[email protected]). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc?rtc=2).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/en-us/msrc?rtc=2).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
@@ -28,7 +28,7 @@ Please include the requested information listed below (as much as you can provid
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://www.microsoft.com/en-us/msrc/bounty?rtc=2) page for more details about our active programs.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
@@ -36,6 +36,6 @@ We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd?rtc=2).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
### How do I get access to nightly builds?
|
||||
|
||||
Nightly builds of the Semantic Kernel are available [here](https://github.com/orgs/microsoft/packages?repo_name=semantic-kernel).
|
||||
|
||||
To download nightly builds follow the following steps:
|
||||
|
||||
1. You will need a GitHub account to complete these steps.
|
||||
1. Create a GitHub Personal Access Token with the `read:packages` scope using these [instructions](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic).
|
||||
1. If you account is part of the Microsoft organization then you must authorize the `Microsoft` organization as a single sign-on organization.
|
||||
1. Click the "Configure SSO" next to the Person Access Token you just created and then authorize `Microsoft`.
|
||||
1. Use the following command to add the Microsoft GitHub Packages source to your NuGet configuration:
|
||||
|
||||
```powershell
|
||||
dotnet nuget add source --username GITHUBUSERNAME --password GITHUBPERSONALACCESSTOKEN --store-password-in-clear-text --name GitHubMicrosoft "https://nuget.pkg.github.com/microsoft/index.json"
|
||||
```
|
||||
|
||||
1. Or you can manually create a `NuGet.Config` file.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
<add key="github" value="https://nuget.pkg.github.com/microsoft/index.json" />
|
||||
</packageSources>
|
||||
|
||||
<packageSourceMapping>
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
<packageSource key="github">
|
||||
<package pattern="*nightly"/>
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
|
||||
<packageSourceCredentials>
|
||||
<github>
|
||||
<add key="Username" value="<Your GitHub Id>" />
|
||||
<add key="ClearTextPassword" value="<Your Personal Access Token>" />
|
||||
</github>
|
||||
</packageSourceCredentials>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
* If you place this file in your project folder make sure to have Git (or whatever source control you use) ignore it.
|
||||
* For more information on where to store this file go [here](https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file).
|
||||
* You can also use the following command `he Microsoft GitHub Packages source can be added easier to NuGet:`
|
||||
1. You can now add packages from the nightly build to your project.
|
||||
* E.g. use this command `dotnet add package Microsoft.SemanticKernel.Core --version 0.26.231003.1-nightly`
|
||||
1. And the latest package release can be referenced in the project like this:
|
||||
* `<PackageReference Include="Microsoft.SemanticKernel" Version="*-*" />`
|
||||
|
||||
For more information see: <https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-nuget-registry>
|
||||
@@ -1,15 +1,17 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: accepted
|
||||
contact: semenshi
|
||||
contact: SergeyMenshykh
|
||||
date: 2023-06-23
|
||||
deciders: shawncal
|
||||
consulted: stephentoub
|
||||
informed:
|
||||
---
|
||||
|
||||
# Error handling improvements
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This ADR describes problems and their solutions for improving the error handling aspect of SK. It does not address logging, resiliency, or observability aspects.
|
||||
|
||||
## Context and Problem Statement
|
||||
@@ -41,4 +43,4 @@ Currently, there are several aspects of error handling in SK that can be enhance
|
||||
- Identify all cases where the original exception is not preserved as an inner exception of the rethrown SK exception, and address them.
|
||||
- Create a new exception HttpOperationException, which includes a StatusCode property, and implement the necessary logic to map the exception from HttpStatusCode, HttpRequestException, or Azure.RequestFailedException. Update existing SK code that interacts with the HTTP stack to throw HttpOperationException in case of a failed HTTP request and assign the original exception as its inner exception.
|
||||
- Modify all SK components that currently store exceptions to SK context to rethrow them instead.
|
||||
- Simplify the SK critical exception handling functionality by modifying the IsCriticalException extension method to exclude handling of StackOverflowException and OutOfMemoryException exceptions. This is because the former exception is not thrown, so the calling code won't be executed, while the latter exception doesn't necessarily prevent the execution of recovery code.
|
||||
- Simplify the SK critical exception handling functionality by modifying the IsCriticalException extension method to exclude handling of StackOverflowException and OutOfMemoryException exceptions. This is because the former exception is not thrown, so the calling code won't be executed, while the latter exception doesn't necessarily prevent the execution of recovery code.
|
||||
|
||||
@@ -1,32 +1,39 @@
|
||||
---
|
||||
status: accepted
|
||||
contact: semenshi
|
||||
contact: SergeyMenshykh
|
||||
date: 2023-08-15
|
||||
deciders: shawncal
|
||||
consulted:
|
||||
informed:
|
||||
---
|
||||
|
||||
# Dynamic payload building for PUT and POST RestAPI operations and parameter namespacing
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
Currently, the SK OpenAPI does not allow the dynamic creation of payload/body for PUT and POST RestAPI operations, even though all the required metadata is available. One of the reasons the functionality was not fully developed originally, and eventually removed is that JSON payload/body content of PUT and POST RestAPI operations might contain properties with identical names at various levels. It was not clear how to unambiguously resolve their values from the flat list of context variables. Another reason the functionality has not been added yet is that the 'payload' context variable, along with RestAPI operation data contract schema(OpenAPI, JSON schema, Typings?) should have been sufficient for LLM to provide fully fleshed-out JSON payload/body content without the need to build it dynamically.
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
## Decision Drivers
|
||||
* Create a mechanism that enables the dynamic construction of the payload/body for PUT and POST RestAPI operations.
|
||||
* Develop a mechanism(namespacing) that allows differentiation of payload properties with identical names at various levels for PUT and POST RestAPI operations.
|
||||
* Aim to minimize breaking changes and maintain backward compatibility of the code as much as possible.
|
||||
|
||||
- Create a mechanism that enables the dynamic construction of the payload/body for PUT and POST RestAPI operations.
|
||||
- Develop a mechanism(namespacing) that allows differentiation of payload properties with identical names at various levels for PUT and POST RestAPI operations.
|
||||
- Aim to minimize breaking changes and maintain backward compatibility of the code as much as possible.
|
||||
|
||||
## Considered Options
|
||||
* Enable the dynamic creation of payload and/or namespacing by default.
|
||||
* Enable the dynamic creation of payload and/or namespacing based on configuration.
|
||||
|
||||
- Enable the dynamic creation of payload and/or namespacing by default.
|
||||
- Enable the dynamic creation of payload and/or namespacing based on configuration.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: "Enable the dynamic creation of payload and/or namespacing based on configuration". This option keeps things compatible, so the change won't affect any SK consumer code. Additionally, it lets SK consumer code easily control both mechanisms, turning them on or off based on the scenario.
|
||||
|
||||
## Additional details
|
||||
|
||||
### Enabling dynamic creation of payload
|
||||
|
||||
In order to enable the dynamic creation of payloads/bodies for PUT and POST RestAPI operations, please set the `EnableDynamicPayload` property of the `OpenApiSkillExecutionParameters` execution parameters to `true` when importing the AI plugin:
|
||||
|
||||
```csharp
|
||||
@@ -34,12 +41,13 @@ var plugin = await kernel.ImportPluginFunctionsAsync("<skill name>", new Uri("<c
|
||||
```
|
||||
|
||||
To dynamically construct a payload for a RestAPI operation that requires payload like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"value": "secret-value",
|
||||
"attributes": {
|
||||
"enabled": true
|
||||
}
|
||||
"value": "secret-value",
|
||||
"attributes": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -52,16 +60,18 @@ contextVariables.Set("enabled", true);
|
||||
```
|
||||
|
||||
### Enabling namespacing
|
||||
|
||||
To enable namespacing, set the `EnablePayloadNamespacing` property of the `OpenApiSkillExecutionParameters` execution parameters to `true` when importing the AI plugin:
|
||||
|
||||
```csharp
|
||||
var plugin = await kernel.ImportPluginFunctionsAsync("<skill name>", new Uri("<chatGPT-plugin>"), new OpenApiSkillExecutionParameters(httpClient) { EnablePayloadNamespacing = true });
|
||||
```
|
||||
|
||||
Remember that the namespacing mechanism depends on prefixing parameter names with their parent parameter name, separated by dots. So, use the 'namespaced' parameter names when adding arguments for them to the context variables. Let's consider this JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"upn": "<sender upn>",
|
||||
{
|
||||
"upn": "<sender upn>",
|
||||
"receiver": {
|
||||
"upn": "<receiver upn>"
|
||||
},
|
||||
@@ -70,7 +80,9 @@ Remember that the namespacing mechanism depends on prefixing parameter names wit
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It contains `upn` properties at different levels. The the argument registration for the parameters(property values) will look like:
|
||||
|
||||
```csharp
|
||||
var contextVariables = new ContextVariables();
|
||||
contextVariables.Set("upn", "<sender-upn-value>");
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: accepted
|
||||
contact: markwallace-microsoft
|
||||
date: 2023-=9-15
|
||||
date: 2023-9-15
|
||||
deciders: shawncal
|
||||
consulted: stoub, lemiller, dmytrostruk
|
||||
informed:
|
||||
consulted: stephentoub, lemillermicrosoft, dmytrostruk
|
||||
informed:
|
||||
---
|
||||
|
||||
# Refactor to support generic LLM request settings
|
||||
|
||||
## Context and Problem Statement
|
||||
@@ -25,18 +26,18 @@ Link to issue raised by the implementer of the Oobabooga AI service: <https://gi
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* Semantic Kernel abstractions must be AI Service agnostic i.e., remove OpenAI specific properties.
|
||||
* Solution must continue to support loading Semantic Function configuration (which includes AI request settings) from `config.json`.
|
||||
* Provide good experience for developers e.g., must be able to program with type safety, intellisense, etc.
|
||||
* Provide a good experience for implementors of AI services i.e., should be clear how to define the appropriate AI Request Settings abstraction for the service they are supporting.
|
||||
* Semantic Kernel implementation and sample code should avoid specifying OpenAI specific request settings in code that is intended to be used with multiple AI services.
|
||||
* Semantic Kernel implementation and sample code must be clear if an implementation is intended to be OpenAI specific.
|
||||
- Semantic Kernel abstractions must be AI Service agnostic i.e., remove OpenAI specific properties.
|
||||
- Solution must continue to support loading Semantic Function configuration (which includes AI request settings) from `config.json`.
|
||||
- Provide good experience for developers e.g., must be able to program with type safety, intellisense, etc.
|
||||
- Provide a good experience for implementors of AI services i.e., should be clear how to define the appropriate AI Request Settings abstraction for the service they are supporting.
|
||||
- Semantic Kernel implementation and sample code should avoid specifying OpenAI specific request settings in code that is intended to be used with multiple AI services.
|
||||
- Semantic Kernel implementation and sample code must be clear if an implementation is intended to be OpenAI specific.
|
||||
|
||||
## Considered Options
|
||||
|
||||
* Use `dynamic` to pass request settings
|
||||
* Use `object` to pass request settings
|
||||
* Define a base class for AI request settings which all implementations must extend
|
||||
- Use `dynamic` to pass request settings
|
||||
- Use `object` to pass request settings
|
||||
- Define a base class for AI request settings which all implementations must extend
|
||||
|
||||
Note: Using generics was discounted during an earlier investigation which Dmytro conducted.
|
||||
|
||||
@@ -96,11 +97,11 @@ await kernel.RunAsync(func);
|
||||
|
||||
PR: <https://github.com/microsoft/semantic-kernel/pull/2807>
|
||||
|
||||
* Good, SK abstractions contain no references to OpenAI specific request settings
|
||||
* Neutral, because anonymous types can be used which allows a developer to pass in properties that may be supported by multiple AI services e.g., `temperature` or combine properties for different AI services e.g., `max_tokens` (OpenAI) and `max_new_tokens` (Oobabooga).
|
||||
* Bad, because it's not clear to developers what they should pass when creating a semantic function
|
||||
* Bad, because it's not clear to implementors of a chat/text completion service what they should accept or how to add service specific properties.
|
||||
* Bad, there is no compiler type checking for code paths where the dynamic argument has not been resolved which will impact code quality. Type issues manifest as `RuntimeBinderException`'s and may be difficult to troubleshoot. Special care needs to be taken with return types e.g., may be necessary to specify an explicit type rather than just `var` again to avoid errors such as `Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : Cannot apply indexing with [] to an expression of type 'object'`
|
||||
- Good, SK abstractions contain no references to OpenAI specific request settings
|
||||
- Neutral, because anonymous types can be used which allows a developer to pass in properties that may be supported by multiple AI services e.g., `temperature` or combine properties for different AI services e.g., `max_tokens` (OpenAI) and `max_new_tokens` (Oobabooga).
|
||||
- Bad, because it's not clear to developers what they should pass when creating a semantic function
|
||||
- Bad, because it's not clear to implementors of a chat/text completion service what they should accept or how to add service specific properties.
|
||||
- Bad, there is no compiler type checking for code paths where the dynamic argument has not been resolved which will impact code quality. Type issues manifest as `RuntimeBinderException`'s and may be difficult to troubleshoot. Special care needs to be taken with return types e.g., may be necessary to specify an explicit type rather than just `var` again to avoid errors such as `Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : Cannot apply indexing with [] to an expression of type 'object'`
|
||||
|
||||
### Use `object` to pass request settings
|
||||
|
||||
@@ -127,11 +128,11 @@ The calling pattern is the same as for the `dynamic` case i.e. use either an ano
|
||||
|
||||
PR: <https://github.com/microsoft/semantic-kernel/pull/2819>
|
||||
|
||||
* Good, SK abstractions contain no references to OpenAI specific request settings
|
||||
* Neutral, because anonymous types can be used which allows a developer to pass in properties that may be supported by multiple AI services e.g., `temperature` or combine properties for different AI services e.g., `max_tokens` (OpenAI) and `max_new_tokens` (Oobabooga).
|
||||
* Bad, because it's not clear to developers what they should pass when creating a semantic function
|
||||
* Bad, because it's not clear to implementors of a chat/text completion service what they should accept or how to add service specific properties.
|
||||
* Bad, code is needed to perform type checks and explicit casts. The situation is slightly better than for the `dynamic` case.
|
||||
- Good, SK abstractions contain no references to OpenAI specific request settings
|
||||
- Neutral, because anonymous types can be used which allows a developer to pass in properties that may be supported by multiple AI services e.g., `temperature` or combine properties for different AI services e.g., `max_tokens` (OpenAI) and `max_new_tokens` (Oobabooga).
|
||||
- Bad, because it's not clear to developers what they should pass when creating a semantic function
|
||||
- Bad, because it's not clear to implementors of a chat/text completion service what they should accept or how to add service specific properties.
|
||||
- Bad, code is needed to perform type checks and explicit casts. The situation is slightly better than for the `dynamic` case.
|
||||
|
||||
### Define a base class for AI request settings which all implementations must extend
|
||||
|
||||
@@ -221,12 +222,12 @@ this._summarizeConversationFunction = kernel.CreateSemanticFunction(
|
||||
|
||||
The caveat with this pattern is, assuming a more specific implementation of `AIRequestSettings` uses JSON serialization/deserialization to hydrate an instance from the base `AIRequestSettings`, this will only work if all properties are supported by the default JsonConverter e.g.,
|
||||
|
||||
* If we have `MyAIRequestSettings` which includes a `Uri` property. The implementation of `MyAIRequestSettings` would make sure to load a URI converter so that it can serialize/deserialize the settings correctly.
|
||||
* If the settings for `MyAIRequestSettings` are sent to an AI service which relies on the default JsonConverter then a `NotSupportedException` exception will be thrown.
|
||||
- If we have `MyAIRequestSettings` which includes a `Uri` property. The implementation of `MyAIRequestSettings` would make sure to load a URI converter so that it can serialize/deserialize the settings correctly.
|
||||
- If the settings for `MyAIRequestSettings` are sent to an AI service which relies on the default JsonConverter then a `NotSupportedException` exception will be thrown.
|
||||
|
||||
PR: <https://github.com/microsoft/semantic-kernel/pull/2829>
|
||||
|
||||
* Good, SK abstractions contain no references to OpenAI specific request settings
|
||||
* Good, because it is clear to developers what they should pass when creating a semantic function and it is easy to discover what service specific request setting implementations exist.
|
||||
* Good, because it is clear to implementors of a chat/text completion service what they should accept and how to extend the base abstraction to add service specific properties.
|
||||
* Neutral, because `ExtensionData` can be used which allows a developer to pass in properties that may be supported by multiple AI services e.g., `temperature` or combine properties for different AI services e.g., `max_tokens` (OpenAI) and `max_new_tokens` (Oobabooga).
|
||||
- Good, SK abstractions contain no references to OpenAI specific request settings
|
||||
- Good, because it is clear to developers what they should pass when creating a semantic function and it is easy to discover what service specific request setting implementations exist.
|
||||
- Good, because it is clear to implementors of a chat/text completion service what they should accept and how to extend the base abstraction to add service specific properties.
|
||||
- Neutral, because `ExtensionData` can be used which allows a developer to pass in properties that may be supported by multiple AI services e.g., `temperature` or combine properties for different AI services e.g., `max_tokens` (OpenAI) and `max_new_tokens` (Oobabooga).
|
||||
|
||||
@@ -4,9 +4,10 @@ status: accepted
|
||||
contact: dmytrostruk
|
||||
date: 2013-06-16
|
||||
deciders: shawncal, hario90
|
||||
consulted: dmytrostruk, matthewbolanos
|
||||
consulted: dmytrostruk, matthewbolanos
|
||||
informed: lemillermicrosoft
|
||||
---
|
||||
|
||||
# Add support for multiple named arguments in template function calls
|
||||
|
||||
## Context and Problem Statement
|
||||
@@ -15,30 +16,30 @@ Native functions now support multiple parameters, populated from context values
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* Parity with Guidance
|
||||
* Readability
|
||||
* Similarity to languages familiar to SK developers
|
||||
* YAML compatibility
|
||||
- Parity with Guidance
|
||||
- Readability
|
||||
- Similarity to languages familiar to SK developers
|
||||
- YAML compatibility
|
||||
|
||||
## Considered Options
|
||||
|
||||
### Syntax idea 1: Using commas
|
||||
|
||||
|
||||
```handlebars
|
||||
{{Skill.MyFunction street: "123 Main St", zip: "98123", city:"Seattle", age: 25}}
|
||||
```
|
||||
|
||||
Pros:
|
||||
|
||||
* Commas could make longer function calls easier to read, especially if spaces before and after the arg separator (a colon in this case) are allowed.
|
||||
- Commas could make longer function calls easier to read, especially if spaces before and after the arg separator (a colon in this case) are allowed.
|
||||
|
||||
Cons:
|
||||
|
||||
* Guidance doesn't use commas
|
||||
* Spaces are already used as delimiters elsewhere so the added complexity of supporting commas isn't necessary
|
||||
- Guidance doesn't use commas
|
||||
- Spaces are already used as delimiters elsewhere so the added complexity of supporting commas isn't necessary
|
||||
|
||||
### Syntax idea 2: JavaScript/C#-Style delimiter (colon)
|
||||
|
||||
|
||||
```handlebars
|
||||
|
||||
{{MyFunction street:"123 Main St" zip:"98123" city:"Seattle" age: "25"}}
|
||||
@@ -47,12 +48,12 @@ Cons:
|
||||
|
||||
Pros:
|
||||
|
||||
* Resembles JavaScript Object syntax and C# named argument syntax
|
||||
- Resembles JavaScript Object syntax and C# named argument syntax
|
||||
|
||||
Cons:
|
||||
|
||||
* Doesn't align with Guidance syntax which uses equal signs as arg part delimiters
|
||||
* Too similar to YAML key/value pairs if we support YAML prompts in the future. It's likely possible to support colons as delimiters but would be better to have a separator that is distinct from normal YAML syntax.
|
||||
- Doesn't align with Guidance syntax which uses equal signs as arg part delimiters
|
||||
- Too similar to YAML key/value pairs if we support YAML prompts in the future. It's likely possible to support colons as delimiters but would be better to have a separator that is distinct from normal YAML syntax.
|
||||
|
||||
### Syntax idea 3: Python/Guidance-Style delimiter
|
||||
|
||||
@@ -62,29 +63,29 @@ Cons:
|
||||
|
||||
Pros:
|
||||
|
||||
* Resembles Python's keyword argument syntax
|
||||
* Resembles Guidance's named argument syntax
|
||||
* Not too similar to YAML key/value pairs if we support YAML prompts in the future.
|
||||
- Resembles Python's keyword argument syntax
|
||||
- Resembles Guidance's named argument syntax
|
||||
- Not too similar to YAML key/value pairs if we support YAML prompts in the future.
|
||||
|
||||
Cons:
|
||||
|
||||
* Doesn't align with C# syntax
|
||||
- Doesn't align with C# syntax
|
||||
|
||||
### Syntax idea 4: Allow whitespace between arg name/value delimiter
|
||||
|
||||
```handlebars
|
||||
{{MyFunction street = "123 Main St" zip = "98123" city = "Seattle"}}
|
||||
{{MyFunction street="123 Main St" zip="98123" city="Seattle"}}
|
||||
```
|
||||
|
||||
Pros:
|
||||
|
||||
* Follows the convention followed by many programming languages of whitespace flexibility where spaces, tabs, and newlines within code don't impact a program's functionality
|
||||
- Follows the convention followed by many programming languages of whitespace flexibility where spaces, tabs, and newlines within code don't impact a program's functionality
|
||||
|
||||
Cons:
|
||||
|
||||
* Promotes code that is harder to read unless commas can be used (see [Using Commas](#syntax-idea-1-using-commas))
|
||||
* More complexity to support
|
||||
* Doesn't align with Guidance which doesn't support spaces before and after the = sign.
|
||||
- Promotes code that is harder to read unless commas can be used (see [Using Commas](#syntax-idea-1-using-commas))
|
||||
- More complexity to support
|
||||
- Doesn't align with Guidance which doesn't support spaces before and after the = sign.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
@@ -92,20 +93,18 @@ Chosen options: "Syntax idea 3: Python/Guidance-Style keyword arguments", becaus
|
||||
|
||||
Additional decisions:
|
||||
|
||||
* Continue supporting up to 1 positional argument for backward compatibility. Currently, the argument passed to a function is assumed to be the `$input` context variable.
|
||||
- Continue supporting up to 1 positional argument for backward compatibility. Currently, the argument passed to a function is assumed to be the `$input` context variable.
|
||||
|
||||
Example
|
||||
|
||||
```handlebars
|
||||
|
||||
{{MyFunction "inputVal" street="123 Main St" zip="98123" city="Seattle"}}
|
||||
|
||||
```
|
||||
|
||||
* Allow arg values to be defined as strings or variables ONLY, e.g.
|
||||
|
||||
- Allow arg values to be defined as strings or variables ONLY, e.g.
|
||||
|
||||
```handlebars
|
||||
{{MyFunction street=$street zip="98123" city='Seattle'}}
|
||||
{{MyFunction street=$street zip="98123" city="Seattle"}}
|
||||
```
|
||||
|
||||
If function expects a value other than a string for an argument, the SDK will use the corresponding TypeConverter to parse the string provided when evaluating the expression.
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
---
|
||||
|
||||
# These are optional elements. Feel free to remove any of them
|
||||
|
||||
status: accepted
|
||||
contact: markwallace-microsoft
|
||||
date: 2023-09-29
|
||||
deciders: semenshi, dmytrostruk, rbarreto
|
||||
consulted: shawncal, stoub, lemiller
|
||||
informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication}
|
||||
deciders: SergeyMenshykh, dmytrostruk, RogerBarreto
|
||||
consulted: shawncal, stephentoub, lemillermicrosoft
|
||||
informed:
|
||||
{
|
||||
list everyone who is kept up-to-date on progress; and with whom there is a one-way communication,
|
||||
}
|
||||
---
|
||||
|
||||
# DotNet Project Structure for 1.0 Release
|
||||
@@ -49,13 +51,13 @@ Chosen option: Option #2: Folder naming matches assembly name, because:
|
||||
|
||||
Main categories for the projects will be:
|
||||
|
||||
1. `Connectors`: ***A connector project allows the Semantic Kernel to connect to AI and Memory services***. Some of the existing connector projects may move to other repositories.
|
||||
1. `Planners`: ***A planner project provides one or more planner implementations which take an ask and convert it into an executable plan to achieve that ask***. This category will include the current action, sequential and stepwise planners (these could be merged into a single project). Additional planning implementations e.g., planners that generate Powershell or Python code can be added as separate projects.
|
||||
1. `Functions`: ***A function project that enables the Semantic Kernel to access the functions it will orchestrate***. This category will include:
|
||||
1. Semantic functions i.e., prompts executed against an LLM
|
||||
1. GRPC remote procedures i.e., procedures executed remotely using the GRPC framework
|
||||
1. Open API endpoints i.e., REST endpoints that have Open API definitions executed remotely using the HTTP protocol
|
||||
1. `Plugins`: ***A plugin project contains the implementation(s) of a Semantic Kernel plugin***. A Semantic Kernel plugin is contains a concrete implementation of a function e.g., a plugin may include code for basic text operations.
|
||||
1. `Connectors`: **_A connector project allows the Semantic Kernel to connect to AI and Memory services_**. Some of the existing connector projects may move to other repositories.
|
||||
1. `Planners`: **_A planner project provides one or more planner implementations which take an ask and convert it into an executable plan to achieve that ask_**. This category will include the current action, sequential and stepwise planners (these could be merged into a single project). Additional planning implementations e.g., planners that generate Powershell or Python code can be added as separate projects.
|
||||
1. `Functions`: **_A function project that enables the Semantic Kernel to access the functions it will orchestrate_**. This category will include:
|
||||
1. Semantic functions i.e., prompts executed against an LLM
|
||||
1. GRPC remote procedures i.e., procedures executed remotely using the GRPC framework
|
||||
1. Open API endpoints i.e., REST endpoints that have Open API definitions executed remotely using the HTTP protocol
|
||||
1. `Plugins`: **_A plugin project contains the implementation(s) of a Semantic Kernel plugin_**. A Semantic Kernel plugin is contains a concrete implementation of a function e.g., a plugin may include code for basic text operations.
|
||||
|
||||
### Option #1: New `planning`, `functions` and `plugins` project areas
|
||||
|
||||
@@ -97,17 +99,17 @@ SK-dotnet
|
||||
|
||||
### Changes
|
||||
|
||||
| Project | Description |
|
||||
|-------------------------------------|-------------|
|
||||
| `Functions.Native` | Extract native functions from Semantic Kernel core and abstractions. |
|
||||
| `Functions.Semantic` | Extract semantic functions from Semantic Kernel core and abstractions. Include the prompt template engine. |
|
||||
| `Functions.Planning` | Extract planning from Semantic Kernel core and abstractions. |
|
||||
| `Functions.Grpc` | Old `Skills.Grpc` project |
|
||||
| `Functions.OpenAPI` | Old `Skills.OpenAPI` project |
|
||||
| `Plugins.Core` | Old `Skills.Core` project |
|
||||
| `Plugins.Document` | Old `Skills.Document` project |
|
||||
| `Plugins.MsGraph` | Old `Skills.MsGraph` project |
|
||||
| `Plugins.WebSearch` | Old `Skills.WebSearch` project |
|
||||
| Project | Description |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `Functions.Native` | Extract native functions from Semantic Kernel core and abstractions. |
|
||||
| `Functions.Semantic` | Extract semantic functions from Semantic Kernel core and abstractions. Include the prompt template engine. |
|
||||
| `Functions.Planning` | Extract planning from Semantic Kernel core and abstractions. |
|
||||
| `Functions.Grpc` | Old `Skills.Grpc` project |
|
||||
| `Functions.OpenAPI` | Old `Skills.OpenAPI` project |
|
||||
| `Plugins.Core` | Old `Skills.Core` project |
|
||||
| `Plugins.Document` | Old `Skills.Document` project |
|
||||
| `Plugins.MsGraph` | Old `Skills.MsGraph` project |
|
||||
| `Plugins.WebSearch` | Old `Skills.WebSearch` project |
|
||||
|
||||
### Semantic Kernel Skills and Functions
|
||||
|
||||
@@ -125,19 +127,19 @@ SK-dotnet
|
||||
│
|
||||
├── Microsoft.SemanticKernel.Connectors.AI.OpenAI*
|
||||
│ ├── src
|
||||
│ └── tests
|
||||
│ └── tests
|
||||
│ (Not shown but all projects will have src and tests subfolders)
|
||||
├── Microsoft.SemanticKernel.Connectors.AI.HuggingFace
|
||||
├── Microsoft.SemanticKernel.Connectors.Memory.AzureCognitiveSearch
|
||||
├── Microsoft.SemanticKernel.Connectors.Memory.Qdrant
|
||||
│
|
||||
├── Microsoft.SemanticKernel.Planners*
|
||||
│
|
||||
│
|
||||
├── Microsoft.SemanticKernel.Reliability.Basic*
|
||||
├── Microsoft.SemanticKernel.Reliability.Polly
|
||||
│
|
||||
│
|
||||
├── Microsoft.SemanticKernel.TemplateEngines.Basic*
|
||||
│
|
||||
│
|
||||
├── Microsoft.SemanticKernel.Functions.Semantic*
|
||||
├── Microsoft.SemanticKernel.Functions.Grpc
|
||||
├── Microsoft.SemanticKernel.Functions.OpenAPI
|
||||
@@ -156,7 +158,7 @@ SK-dotnet
|
||||
└── Microsoft.SemanticKernel.MetaPackage
|
||||
```
|
||||
|
||||
***Notes:***
|
||||
**_Notes:_**
|
||||
|
||||
- There will only be a single solution file (initially).
|
||||
- Projects will be grouped in the solution i.e., connectors, planners, plugins, functions, extensions, ...
|
||||
@@ -197,33 +199,33 @@ SK-dotnet
|
||||
└── SemanticKernel.UnitTests
|
||||
```
|
||||
|
||||
\\* - Means the project is part of the Semantic Kernel meta package
|
||||
\\\* - Means the project is part of the Semantic Kernel meta package
|
||||
|
||||
### Project Descriptions
|
||||
|
||||
| Project | Description |
|
||||
|-------------------------------------|-------------|
|
||||
| Connectors.AI.OpenAI | Azure OpenAI and OpenAI service connectors |
|
||||
| Connectors... | Collection of other AI service connectors, some of which will move to another repository |
|
||||
| Connectors.UnitTests | Connector unit tests |
|
||||
| Planner.ActionPlanner | Semantic Kernel implementation of an action planner |
|
||||
| Planner.SequentialPlanner | Semantic Kernel implementation of a sequential planner |
|
||||
| Planner.StepwisePlanner | Semantic Kernel implementation of a stepwise planner |
|
||||
| TemplateEngine.Basic | Prompt template engine basic implementations which are used by Semantic Functions only |
|
||||
| Extensions.UnitTests | Extensions unit tests |
|
||||
| InternalUtilities | Internal utilities which are reused by multiple NuGet packages (all internal) |
|
||||
| Skills.Core | Core set of native functions which are provided to support Semantic Functions |
|
||||
| Skills.Document | Native functions for interacting with Microsoft documents |
|
||||
| Skills.Grpc | Semantic Kernel integration for GRPC based endpoints |
|
||||
| Skills.MsGraph | Native functions for interacting with Microsoft Graph endpoints |
|
||||
| Skills.OpenAPI | Semantic Kernel integration for OpenAI endpoints and reference Azure Key Vault implementation |
|
||||
| Skills.Web | Native functions for interacting with Web endpoints e.g., Bing, Google, File download |
|
||||
| Skills.UnitTests | Skills unit tests |
|
||||
| IntegrationTests | Semantic Kernel integration tests |
|
||||
| SemanticKernel | Semantic Kernel core implementation |
|
||||
| SemanticKernel.Abstractions | Semantic Kernel abstractions i.e., interface, abstract classes, supporting classes, ... |
|
||||
| SemanticKernel.MetaPackage | Semantic Kernel meta package i.e., a NuGet package that references other required Semantic Kernel NuGet packages |
|
||||
| SemanticKernel.UnitTests | Semantic Kernel unit tests |
|
||||
| Project | Description |
|
||||
| --------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| Connectors.AI.OpenAI | Azure OpenAI and OpenAI service connectors |
|
||||
| Connectors... | Collection of other AI service connectors, some of which will move to another repository |
|
||||
| Connectors.UnitTests | Connector unit tests |
|
||||
| Planner.ActionPlanner | Semantic Kernel implementation of an action planner |
|
||||
| Planner.SequentialPlanner | Semantic Kernel implementation of a sequential planner |
|
||||
| Planner.StepwisePlanner | Semantic Kernel implementation of a stepwise planner |
|
||||
| TemplateEngine.Basic | Prompt template engine basic implementations which are used by Semantic Functions only |
|
||||
| Extensions.UnitTests | Extensions unit tests |
|
||||
| InternalUtilities | Internal utilities which are reused by multiple NuGet packages (all internal) |
|
||||
| Skills.Core | Core set of native functions which are provided to support Semantic Functions |
|
||||
| Skills.Document | Native functions for interacting with Microsoft documents |
|
||||
| Skills.Grpc | Semantic Kernel integration for GRPC based endpoints |
|
||||
| Skills.MsGraph | Native functions for interacting with Microsoft Graph endpoints |
|
||||
| Skills.OpenAPI | Semantic Kernel integration for OpenAI endpoints and reference Azure Key Vault implementation |
|
||||
| Skills.Web | Native functions for interacting with Web endpoints e.g., Bing, Google, File download |
|
||||
| Skills.UnitTests | Skills unit tests |
|
||||
| IntegrationTests | Semantic Kernel integration tests |
|
||||
| SemanticKernel | Semantic Kernel core implementation |
|
||||
| SemanticKernel.Abstractions | Semantic Kernel abstractions i.e., interface, abstract classes, supporting classes, ... |
|
||||
| SemanticKernel.MetaPackage | Semantic Kernel meta package i.e., a NuGet package that references other required Semantic Kernel NuGet packages |
|
||||
| SemanticKernel.UnitTests | Semantic Kernel unit tests |
|
||||
|
||||
### Naming Patterns
|
||||
|
||||
@@ -283,10 +285,9 @@ dotnet/
|
||||
|
||||
This diagram show current skills are integrated with the Semantic Kernel core.
|
||||
|
||||
***Note:***
|
||||
**_Note:_**
|
||||
|
||||
- This is not a true class hierarchy diagram. It show some class relationships and dependencies.
|
||||
- Namespaces are abbreviated to remove Microsoft.SemanticKernel prefix. Namespaces use `_` rather than `.`.
|
||||
|
||||
<img src="./diagrams/skfunctions-preview.png" alt="ISKFunction class relationships" width="400"/>
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ status: accepted
|
||||
contact: dmytrostruk
|
||||
date: 2023-10-03
|
||||
deciders: dmytrostruk
|
||||
consulted: semenshi, rbarreto, markwallace-microsoft
|
||||
informed:
|
||||
consulted: SergeyMenshykh, RogerBarreto, markwallace-microsoft
|
||||
informed:
|
||||
---
|
||||
|
||||
# Kernel Service Registration
|
||||
|
||||
## Context and Problem Statement
|
||||
@@ -82,12 +83,12 @@ Custom service collection and service provider on Kernel level to simplify depen
|
||||
Interface `IKernel` will have its own service provider `KernelServiceProvider` with minimal functionality to get required service.
|
||||
|
||||
```csharp
|
||||
public interface IKernelServiceProvider
|
||||
public interface IKernelServiceProvider
|
||||
{
|
||||
T? GetService<T>(string? name = null);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IKernel
|
||||
public interface IKernel
|
||||
{
|
||||
IKernelServiceProvider Services { get; }
|
||||
}
|
||||
@@ -174,7 +175,7 @@ Cons:
|
||||
|
||||
- Additional dependency for Semantic Kernel package - `Microsoft.Extensions.DependencyInjection`.
|
||||
- No possibility to include specific list of services (lack of isolation from host application).
|
||||
- Possibility of `Microsoft.Extensions.DependencyInjection` version mismatch and runtime errors (e.g. users have `Microsoft.Extensions.DependencyInjection` `--version 2.0` while Semantic Kernel uses `--version 6.0`)
|
||||
- Possibility of `Microsoft.Extensions.DependencyInjection` version mismatch and runtime errors (e.g. users have `Microsoft.Extensions.DependencyInjection` `--version 2.0` while Semantic Kernel uses `--version 6.0`)
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
|
||||
@@ -1,146 +1,165 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: accepted
|
||||
contact: semenshi
|
||||
contact: SergeyMenshykh
|
||||
date: 2023-10-23
|
||||
deciders: markwallace-microsoft, mabolan
|
||||
deciders: markwallace-microsoft, matthewbolanos
|
||||
consulted:
|
||||
informed:
|
||||
---
|
||||
|
||||
# SK prompt syntax for chat completion roles
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
Today, SK does not have the ability to mark a block of text in a prompt as a message with a specific role, such as assistant, system, or user. As a result, SK can't chunk the prompt into the list of messages required by chat completion connectors.
|
||||
|
||||
Additionally, prompts can be defined using a range of template syntaxes supported by various template engines, such as Handlebars, Jinja, and others. Each of these syntaxes may represent chat messages or roles in a distinct way. Consequently, the template engine syntax may leak into SK's domain if no proper abstraction is put in place, coupling SK with the template engines and making it impossible to support new ones.
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
## Decision Drivers
|
||||
* It should be possible to mark a block of text in a prompt as a message with a role so that it can be converted into a list of chat messages for use by chat completion connectors.
|
||||
* The syntax specific to the template engine message/role should be mapped to the SK message/role syntax to abstract SK from a specific template engine syntax.
|
||||
|
||||
- It should be possible to mark a block of text in a prompt as a message with a role so that it can be converted into a list of chat messages for use by chat completion connectors.
|
||||
- The syntax specific to the template engine message/role should be mapped to the SK message/role syntax to abstract SK from a specific template engine syntax.
|
||||
|
||||
## Considered Options
|
||||
|
||||
**1. Message/role tags are generated by functions specified in a prompt.** This option relies on the fact that many template engines can invoke functions specified in the template. Therefore, an internal function can be registered with a template engine, and the function will create a message/model tag based on the provided arguments. The prompt template engine will execute the function and emit the function result into the prompt template, and the rendered prompt will have a section for each message/role decorated with these tags. Here's an example of how this can be done using the SK basic template engine and Handlebars:
|
||||
|
||||
Function:
|
||||
```csharp
|
||||
internal class SystemFunctions
|
||||
{
|
||||
public string Message(string role)
|
||||
{
|
||||
return $"<message role=\"{role}\">";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Prompt:
|
||||
Function:
|
||||
|
||||
```bash
|
||||
{{message role="system"}}
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
{{message role="system"}}
|
||||
```csharp
|
||||
internal class SystemFunctions
|
||||
{
|
||||
public string Message(string role)
|
||||
{
|
||||
return $"<message role=\"{role}\">";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{message role="user"}}
|
||||
I want to {{$input}}
|
||||
{{message role="user"}}
|
||||
```
|
||||
Prompt:
|
||||
|
||||
Rendered prompt:
|
||||
```bash
|
||||
{{message role="system"}}
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
{{message role="system"}}
|
||||
|
||||
```xml
|
||||
<message role="system">
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
</message>
|
||||
<message role="user">
|
||||
I want to buy a house.
|
||||
</message>
|
||||
```
|
||||
{{message role="user"}}
|
||||
I want to {{$input}}
|
||||
{{message role="user"}}
|
||||
```
|
||||
|
||||
**2. Message/role tags are generated by a prompt-specific mechanism.** This option utilizes template engine syntax constructions, helpers, and handlers other than functions to inject SK message/role tags into the final prompt.
|
||||
In the example below, to parse the prompt that uses the handlebars syntax we need to register a block helper (a callback that is invoked when the Handlebars engine encounters it) to emit the SK message/role tags in the resulting prompt.
|
||||
|
||||
Block helpers:
|
||||
```csharp
|
||||
this.handlebarsEngine.RegisterHelper("system", (EncodedTextWriter output, Context context, Arguments arguments) => {
|
||||
//Emit the <message role="system"> tags
|
||||
});
|
||||
this.handlebarsEngine.RegisterHelper("user", (EncodedTextWriter output, Context context, Arguments arguments) => {
|
||||
//Emit the <message role="user"> tags
|
||||
});
|
||||
```
|
||||
Rendered prompt:
|
||||
|
||||
Prompt:
|
||||
```bash
|
||||
{{#system~}}
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
{{~/system}}
|
||||
{{#user~}}
|
||||
I want to {{$input}}
|
||||
{{~/user}}
|
||||
```
|
||||
```xml
|
||||
<message role="system">
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
</message>
|
||||
<message role="user">
|
||||
I want to buy a house.
|
||||
</message>
|
||||
```
|
||||
|
||||
Rendered prompt:
|
||||
```xml
|
||||
<message role="system">
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
</message>
|
||||
<message role="user">
|
||||
I want to buy a house.
|
||||
</message>
|
||||
```
|
||||
**2. Message/role tags are generated by a prompt-specific mechanism.** This option utilizes template engine syntax constructions, helpers, and handlers other than functions to inject SK message/role tags into the final prompt.
|
||||
In the example below, to parse the prompt that uses the handlebars syntax we need to register a block helper (a callback that is invoked when the Handlebars engine encounters it) to emit the SK message/role tags in the resulting prompt.
|
||||
|
||||
Block helpers:
|
||||
|
||||
```csharp
|
||||
this.handlebarsEngine.RegisterHelper("system", (EncodedTextWriter output, Context context, Arguments arguments) => {
|
||||
//Emit the <message role="system"> tags
|
||||
});
|
||||
this.handlebarsEngine.RegisterHelper("user", (EncodedTextWriter output, Context context, Arguments arguments) => {
|
||||
//Emit the <message role="user"> tags
|
||||
});
|
||||
```
|
||||
|
||||
Prompt:
|
||||
|
||||
```bash
|
||||
{{#system~}}
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
{{~/system}}
|
||||
{{#user~}}
|
||||
I want to {{$input}}
|
||||
{{~/user}}
|
||||
```
|
||||
|
||||
Rendered prompt:
|
||||
|
||||
```xml
|
||||
<message role="system">
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
</message>
|
||||
<message role="user">
|
||||
I want to buy a house.
|
||||
</message>
|
||||
```
|
||||
|
||||
**3. Message/role tags are applied on top of prompt template engine**. This option presumes specifying the SK message/role tags directly in a prompt to denote message/role blocks in way that template engine does not parse/handle them and considers them as a regular text.
|
||||
In the example below, the prompt the `<message role="*">` tags are marking boundaries of the system and user messages and SK basic template engine consider them as regular text without processing them.
|
||||
|
||||
Prompt:
|
||||
```xml
|
||||
<message role="system">
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
</message>
|
||||
<message role="user">
|
||||
I want to {{$input}}
|
||||
</message>
|
||||
```
|
||||
In the example below, the prompt the `<message role="*">` tags are marking boundaries of the system and user messages and SK basic template engine consider them as regular text without processing them.
|
||||
|
||||
Rendered prompt:
|
||||
```xml
|
||||
<message role="system">
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
</message>
|
||||
<message role="user">
|
||||
I want to buy a house.
|
||||
</message>
|
||||
```
|
||||
Prompt:
|
||||
|
||||
```xml
|
||||
<message role="system">
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
</message>
|
||||
<message role="user">
|
||||
I want to {{$input}}
|
||||
</message>
|
||||
```
|
||||
|
||||
Rendered prompt:
|
||||
|
||||
```xml
|
||||
<message role="system">
|
||||
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
|
||||
</message>
|
||||
<message role="user">
|
||||
I want to buy a house.
|
||||
</message>
|
||||
```
|
||||
|
||||
## Pros and Cons
|
||||
**1. Message/role tags are generated by functions specified in a prompt**
|
||||
|
||||
Pros:
|
||||
* Functions can be defined once and reused in prompt templates that support function calling.
|
||||
|
||||
Cons:
|
||||
* Functions might not be supported by some template engines.
|
||||
* The system/internal functions should be pre-registered by SK so users don't need to import them.
|
||||
* Each prompt template engine will have how to discover and call the system/internal functions.
|
||||
**1. Message/role tags are generated by functions specified in a prompt**
|
||||
|
||||
Pros:
|
||||
|
||||
- Functions can be defined once and reused in prompt templates that support function calling.
|
||||
|
||||
Cons:
|
||||
|
||||
- Functions might not be supported by some template engines.
|
||||
- The system/internal functions should be pre-registered by SK so users don't need to import them.
|
||||
- Each prompt template engine will have how to discover and call the system/internal functions.
|
||||
|
||||
**2. Message/role tags are generated by prompt specific mechanism**
|
||||
|
||||
Pros:
|
||||
* Enables message/role representation with the optimal template engine syntax constructions, aligning with other constructions for that specific engine.
|
||||
|
||||
Cons:
|
||||
* Each prompt template engine will have to register callbacks/handlers to handle template syntax constructions rendering to emit SK message/role tags.
|
||||
Pros:
|
||||
|
||||
- Enables message/role representation with the optimal template engine syntax constructions, aligning with other constructions for that specific engine.
|
||||
|
||||
Cons:
|
||||
|
||||
- Each prompt template engine will have to register callbacks/handlers to handle template syntax constructions rendering to emit SK message/role tags.
|
||||
|
||||
**3. Message/role tags are applied on top of prompt template engine**
|
||||
|
||||
Pros:
|
||||
* No changes are required to prompt template engines.
|
||||
Pros:
|
||||
|
||||
Cons:
|
||||
* The message/role tag syntax may not align with other syntax constructions for that template engine.
|
||||
* Syntax errors in message/role tags will be detected by components parsing the prompt and not by prompt template engines.
|
||||
- No changes are required to prompt template engines.
|
||||
|
||||
Cons:
|
||||
|
||||
- The message/role tag syntax may not align with other syntax constructions for that template engine.
|
||||
- Syntax errors in message/role tags will be detected by components parsing the prompt and not by prompt template engines.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
It was agreed not to limit ourselves to only one possible option because it may not be feasible to apply that option to new template engines we might need to support in the future. Instead, each time a new template engine is added, every option should be considered, and the optimal one should be preferred for that particular template engine.
|
||||
|
||||
It was also agreed that, at the moment, we will go with the "3. Message/role tags are applied on top of the prompt template engine" option to support the message/role prompt syntax in SK, which currently uses the `BasicPromptTemplateEngine` engine.
|
||||
It was also agreed that, at the moment, we will go with the "3. Message/role tags are applied on top of the prompt template engine" option to support the message/role prompt syntax in SK, which currently uses the `BasicPromptTemplateEngine` engine.
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: accepted
|
||||
contact: SergeyMenshykh
|
||||
date: 2023-10-25
|
||||
deciders: markwallace, mabolan
|
||||
deciders: markwallace-microsoft, matthewbolanos
|
||||
consulted:
|
||||
informed:
|
||||
---
|
||||
|
||||
# Completion service type selection strategy
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
Today, SK runs all text prompts using the text completion service. With the addition of a new chat completion prompts and potentially other prompt types, such as image, on the horizon, we need a way to select a completion service type to run these prompts.
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
## Decision Drivers
|
||||
* Semantic function should be able to identify a completion service type to use when processing text, chat, or image prompts.
|
||||
|
||||
- Semantic function should be able to identify a completion service type to use when processing text, chat, or image prompts.
|
||||
|
||||
## Considered Options
|
||||
|
||||
**1. Completion service type identified by the "prompt_type" property.** This option presumes adding the 'prompt_type' property to the prompt template config model class, 'PromptTemplateConfig.' The property will be specified once by a prompt developer and will be used by the 'SemanticFunction' class to decide which completion service type (not instance) to use when resolving an instance of that particular completion service type.
|
||||
|
||||
**Prompt template**
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "1",
|
||||
@@ -29,12 +36,13 @@ Today, SK runs all text prompts using the text completion service. With the addi
|
||||
```
|
||||
|
||||
**Semantic function pseudocode**
|
||||
|
||||
```csharp
|
||||
if(string.IsNullOrEmpty(promptTemplateConfig.PromptType) || promptTemplateConfig.PromptType == "text")
|
||||
{
|
||||
var service = this._serviceSelector.SelectAIService<ITextCompletion>(context.ServiceProvider, this._modelSettings);
|
||||
//render the prompt, call the service, process and return result
|
||||
}
|
||||
}
|
||||
else (promptTemplateConfig.PromptType == "chat")
|
||||
{
|
||||
var service = this._serviceSelector.SelectAIService<IChatCompletion>(context.ServiceProvider, this._modelSettings);
|
||||
@@ -68,12 +76,12 @@ config: {
|
||||
```
|
||||
|
||||
Pros:
|
||||
- Deterministically specifies which completion service **type** to use, so image prompts won't be rendered by a text completion service, and vice versa.
|
||||
|
||||
- Deterministically specifies which completion service **type** to use, so image prompts won't be rendered by a text completion service, and vice versa.
|
||||
|
||||
Cons:
|
||||
- Another property to specify by a prompt developer.
|
||||
|
||||
|
||||
- Another property to specify by a prompt developer.
|
||||
|
||||
**2. Completion service type identified by prompt content.** The idea behind this option is to analyze the rendered prompt by using regex to check for the presence of specific markers associated with the prompt type. For example, the presence of the `<message role="*"></message>` tag in the rendered prompt might indicate that the prompt is a chat prompt and should be handled by the chat completion service. This approach may work reliably when we have two completion service types - text and chat - since the logic would be straightforward: if the message tag is found in the rendered prompt, handle it with the chat completion service; otherwise, use the text completion service. However, this logic becomes unreliable when we start adding new prompt types, and those prompts lack markers specific to their prompt type. For example, if we add an image prompt, we won't be able to distinguish between a text prompt and an image prompt unless the image prompt has a unique marker identifying it as such.
|
||||
|
||||
@@ -107,11 +115,15 @@ config: {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Pros:
|
||||
|
||||
- No need for a new property to identify the prompt type.
|
||||
|
||||
Cons:
|
||||
|
||||
- Unreliable unless the prompt contains unique markers specifically identifying the prompt type.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
We decided to choose the '2. Completion service type identified by prompt content' option and will reconsider it when we encounter another completion service type that cannot be supported by this option or when we have a solid set of requirements for using a different mechanism for selecting the completion service type.
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
---
|
||||
status: approved
|
||||
contact: markwallace-microsoft
|
||||
date: 2023-10-26
|
||||
deciders: matthewbolanos, markwallace-microsoft, SergeyMenshykh, RogerBarreto
|
||||
consulted: dmytrostruk
|
||||
informed:
|
||||
---
|
||||
|
||||
# Custom Prompt Template Formats
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
Semantic Kernel currently supports a custom prompt template language that allows for variable interpolation and function execution.
|
||||
Semantic Kernel allows for custom prompt template formats to be integrated e.g., prompt templates using [Handlebars](https://handlebarsjs.com/) syntax.
|
||||
|
||||
The purpose of this ADR is to describe how a custom prompt template formats will be supported in the Semantic Kernel.
|
||||
|
||||
### Current Design
|
||||
|
||||
By default the `Kernel` uses the `BasicPromptTemplateEngine` which supports the Semantic Kernel specific template format.
|
||||
|
||||
#### Code Patterns
|
||||
|
||||
Below is an expanded example of how to create a semantic function from a prompt template string which uses the built-in Semantic Kernel format:
|
||||
|
||||
```csharp
|
||||
IKernel kernel = Kernel.Builder
|
||||
.WithPromptTemplateEngine(new BasicPromptTemplateEngine())
|
||||
.WithOpenAIChatCompletionService(
|
||||
modelId: openAIModelId,
|
||||
apiKey: openAIApiKey)
|
||||
.Build();
|
||||
|
||||
kernel.ImportFunctions(new TimePlugin(), "time");
|
||||
|
||||
string templateString = "Today is: {{time.Date}} Is it weekend time (weekend/not weekend)?";
|
||||
var promptTemplateConfig = new PromptTemplateConfig();
|
||||
var promptTemplate = new PromptTemplate(templateString, promptTemplateConfig, kernel.PromptTemplateEngine);
|
||||
var kindOfDay = kernel.RegisterSemanticFunction("KindOfDay", promptTemplateConfig, promptTemplate);
|
||||
|
||||
var result = await kernel.RunAsync(kindOfDay);
|
||||
Console.WriteLine(result.GetValue<string>());
|
||||
```
|
||||
|
||||
We have an extension method `var kindOfDay = kernel.CreateSemanticFunction(promptTemplate);` to simplify the process to create and register a semantic function but the expanded format is shown above to highlight the dependency on `kernel.PromptTemplateEngine`.
|
||||
Also the `BasicPromptTemplateEngine` is the default prompt template engine and will be loaded automatically if the package is available and not other prompt template engine is specified.
|
||||
|
||||
Some issues with this:
|
||||
|
||||
1. `Kernel` only supports a single `IPromptTemplateEngine` so we cannot support using multiple prompt templates at the same time.
|
||||
1. `IPromptTemplateEngine` is stateless and must perform a parse of the template for each render
|
||||
1. Our semantic function extension methods relay on our implementation of `IPromptTemplate` (i.e., `PromptTemplate`) which stores the template string and uses the `IPromptTemplateEngine` to render it every time. Note implementations of `IPromptTemplate` are currently stateful as they also store the parameters.
|
||||
|
||||
#### Performance
|
||||
|
||||
The `BasicPromptTemplateEngine` uses the `TemplateTokenizer` to parse the template i.e. extract the blocks.
|
||||
Then it renders the template i.e. inserts variables and executes functions. Some sample timings for these operations:
|
||||
|
||||
| Operation | Ticks | Milliseconds |
|
||||
| ---------------- | ------- | ------------ |
|
||||
| Extract blocks | 1044427 | 103 |
|
||||
| Render variables | 168 | 0 |
|
||||
|
||||
Sample template used was: `"{{variable1}} {{variable2}} {{variable3}} {{variable4}} {{variable5}}"`
|
||||
|
||||
**Note: We will use the sample implementation to support the f-string template format.**
|
||||
|
||||
Using `HandlebarsDotNet` for the same use case results in the following timings:
|
||||
|
||||
| Operation | Ticks | Milliseconds |
|
||||
| ---------------- | ----- | ------------ |
|
||||
| Compile template | 66277 | 6 |
|
||||
| Render variables | 4173 | 0 |
|
||||
|
||||
**By separating the extract blocks/compile from the render variables operation it will be possible to optimise performance by compiling templates just once.**
|
||||
|
||||
#### Implementing a Custom Prompt Template Engine
|
||||
|
||||
There are two interfaces provided:
|
||||
|
||||
```csharp
|
||||
public interface IPromptTemplateEngine
|
||||
{
|
||||
Task<string> RenderAsync(string templateText, SKContext context, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public interface IPromptTemplate
|
||||
{
|
||||
IReadOnlyList<ParameterView> Parameters { get; }
|
||||
|
||||
public Task<string> RenderAsync(SKContext executionContext, CancellationToken cancellationToken = default);
|
||||
}
|
||||
```
|
||||
|
||||
A prototype implementation of a handlebars prompt template engine could look something like this:
|
||||
|
||||
```csharp
|
||||
public class HandlebarsTemplateEngine : IPromptTemplateEngine
|
||||
{
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
public HandlebarsTemplateEngine(ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
|
||||
}
|
||||
|
||||
public async Task<string> RenderAsync(string templateText, SKContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var handlebars = HandlebarsDotNet.Handlebars.Create();
|
||||
|
||||
var functionViews = context.Functions.GetFunctionViews();
|
||||
foreach (FunctionView functionView in functionViews)
|
||||
{
|
||||
var skfunction = context.Functions.GetFunction(functionView.PluginName, functionView.Name);
|
||||
handlebars.RegisterHelper($"{functionView.PluginName}_{functionView.Name}", async (writer, hcontext, parameters) =>
|
||||
{
|
||||
var result = await skfunction.InvokeAsync(context).ConfigureAwait(true);
|
||||
writer.WriteSafeString(result.GetValue<string>());
|
||||
});
|
||||
}
|
||||
|
||||
var template = handlebars.Compile(templateText);
|
||||
|
||||
var prompt = template(context.Variables);
|
||||
|
||||
return await Task.FromResult(prompt).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note: This is just a prototype implementation for illustration purposes only.**
|
||||
|
||||
Some issues:
|
||||
|
||||
1. The `IPromptTemplate` interface is not used and causes confusion.
|
||||
1. There is no way to allow developers to support multiple prompt template formats at the same time.
|
||||
|
||||
There is one implementation of `IPromptTemplate` provided in the Semantic Kernel core package.
|
||||
The `RenderAsync` implementation just delegates to the `IPromptTemplateEngine`.
|
||||
The `Parameters` list get's populated with the parameters defined in the `PromptTemplateConfig` and any missing variables defined in the template.
|
||||
|
||||
#### Handlebars Considerations
|
||||
|
||||
Handlebars does not support dynamic binding of helpers. Consider the following snippet:
|
||||
|
||||
```csharp
|
||||
HandlebarsHelper link_to = (writer, context, parameters) =>
|
||||
{
|
||||
writer.WriteSafeString($"<a href='{context["url"]}'>{context["text"]}</a>");
|
||||
};
|
||||
|
||||
string source = @"Click here: {{link_to}}";
|
||||
|
||||
var data = new
|
||||
{
|
||||
url = "https://github.com/rexm/handlebars.net",
|
||||
text = "Handlebars.Net"
|
||||
};
|
||||
|
||||
// Act
|
||||
var handlebars = HandlebarsDotNet.Handlebars.Create();
|
||||
handlebars.RegisterHelper("link_to", link_to);
|
||||
var template = handlebars1.Compile(source);
|
||||
// handlebars.RegisterHelper("link_to", link_to); This also works
|
||||
var result = template1(data);
|
||||
```
|
||||
|
||||
Handlebars allows the helpers to be registered with the `Handlebars` instance either before or after a template is compiled.
|
||||
The optimum would be to have a shared `Handlebars` instance for a specific collection of functions and register the helpers just once.
|
||||
For use cases where the Kernel function collection may have been mutated we will be forced to create a `Handlebars` instance at render time
|
||||
and then register the helpers. This means we cannot take advantage of the performance improvement provided by compiling the template.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
In no particular order:
|
||||
|
||||
- Support creating a semantic function without a `IKernel`instance.
|
||||
- Support late binding of functions i.e., having functions resolved when the prompt is rendered.
|
||||
- Support allowing the prompt template to be parsed (compiled) just once to optimize performance if needed.
|
||||
- Support using multiple prompt template formats with a single `Kernel` instance.
|
||||
- Provide simple abstractions which allow third parties to implement support for custom prompt template formats.
|
||||
|
||||
## Considered Options
|
||||
|
||||
- Obsolete `IPromptTemplateEngine` and replace with `IPromptTemplateFactory`.
|
||||
-
|
||||
|
||||
### Obsolete `IPromptTemplateEngine` and replace with `IPromptTemplateFactory`
|
||||
|
||||
<img src="./diagrams/prompt-template-factory.png" alt="ISKFunction class relationships"/>
|
||||
|
||||
Below is an expanded example of how to create a semantic function from a prompt template string which uses the built-in Semantic Kernel format:
|
||||
|
||||
```csharp
|
||||
// Semantic function can be created once
|
||||
var promptTemplateFactory = new BasicPromptTemplateFactory();
|
||||
string templateString = "Today is: {{time.Date}} Is it weekend time (weekend/not weekend)?";
|
||||
var promptTemplateConfig = new PromptTemplateConfig();
|
||||
// Line below will replace the commented out code
|
||||
var promptTemplate = promptTemplateFactory.CreatePromptTemplate(templateString, promptTemplateConfig);
|
||||
var kindOfDay = ISKFunction.CreateSemanticFunction("KindOfDay", promptTemplateConfig, promptTemplate)
|
||||
// var promptTemplate = new PromptTemplate(promptTemplate, promptTemplateConfig, kernel.PromptTemplateEngine);
|
||||
// var kindOfDay = kernel.RegisterSemanticFunction("KindOfDay", promptTemplateConfig, promptTemplate);
|
||||
|
||||
// Create Kernel after creating the semantic function
|
||||
// Later we will support passing a function collection to the KernelBuilder
|
||||
IKernel kernel = Kernel.Builder
|
||||
.WithOpenAIChatCompletionService(
|
||||
modelId: openAIModelId,
|
||||
apiKey: openAIApiKey)
|
||||
.Build();
|
||||
|
||||
kernel.ImportFunctions(new TimePlugin(), "time");
|
||||
// Optionally register the semantic function with the Kernel
|
||||
kernel.RegisterCustomFunction(kindOfDay);
|
||||
|
||||
var result = await kernel.RunAsync(kindOfDay);
|
||||
Console.WriteLine(result.GetValue<string>());
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- `BasicPromptTemplateFactory` will be the default implementation and will be automatically provided in `KernelSemanticFunctionExtensions`. Developers will also be able to provide their own implementation.
|
||||
- The factory uses the new `PromptTemplateConfig.TemplateFormat` to create the appropriate `IPromptTemplate` instance.
|
||||
- We should look to remove `promptTemplateConfig` as a parameter to `CreateSemanticFunction`. That change is outside of the scope of this ADR.
|
||||
|
||||
The `BasicPromptTemplateFactory` and `BasicPromptTemplate` implementations look as follows:
|
||||
|
||||
```csharp
|
||||
public sealed class BasicPromptTemplateFactory : IPromptTemplateFactory
|
||||
{
|
||||
private readonly IPromptTemplateFactory _promptTemplateFactory;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
public BasicPromptTemplateFactory(IPromptTemplateFactory promptTemplateFactory, ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
this._promptTemplateFactory = promptTemplateFactory;
|
||||
this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
|
||||
}
|
||||
|
||||
public IPromptTemplate? CreatePromptTemplate(string templateString, PromptTemplateConfig promptTemplateConfig)
|
||||
{
|
||||
if (promptTemplateConfig.TemplateFormat.Equals(PromptTemplateConfig.SEMANTICKERNEL, System.StringComparison.Ordinal))
|
||||
{
|
||||
return new BasicPromptTemplate(templateString, promptTemplateConfig, this._loggerFactory);
|
||||
}
|
||||
else if (this._promptTemplateFactory is not null)
|
||||
{
|
||||
return this._promptTemplateFactory.CreatePromptTemplate(templateString, promptTemplateConfig);
|
||||
}
|
||||
|
||||
throw new SKException($"Invalid prompt template format {promptTemplateConfig.TemplateFormat}");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BasicPromptTemplate : IPromptTemplate
|
||||
{
|
||||
public BasicPromptTemplate(string templateString, PromptTemplateConfig promptTemplateConfig, ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
|
||||
this._logger = this._loggerFactory.CreateLogger(typeof(BasicPromptTemplate));
|
||||
this._templateString = templateString;
|
||||
this._promptTemplateConfig = promptTemplateConfig;
|
||||
this._parameters = new(() => this.InitParameters());
|
||||
this._blocks = new(() => this.ExtractBlocks(this._templateString));
|
||||
this._tokenizer = new TemplateTokenizer(this._loggerFactory);
|
||||
}
|
||||
|
||||
public IReadOnlyList<ParameterView> Parameters => this._parameters.Value;
|
||||
|
||||
public async Task<string> RenderAsync(SKContext executionContext, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await this.RenderAsync(this._blocks.Value, executionContext, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Not showing the implementation details
|
||||
}
|
||||
```
|
||||
|
||||
**Note:**
|
||||
|
||||
- The call to `ExtractBlocks` is called lazily once for each prompt template
|
||||
- The `RenderAsync` doesn't need to extract the blocks every time
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: "Obsolete `IPromptTemplateEngine` and replace with `IPromptTemplateFactory`", because
|
||||
addresses the requirements and provides good flexibility for the future.
|
||||
+27
-24
@@ -6,11 +6,13 @@ deciders: gitri-ms, shawncal
|
||||
consulted: lemillermicrosoft, awharrison-28, dmytrostruk, nacharya1
|
||||
informed: eavanvalkenburg, kevdome3000
|
||||
---
|
||||
|
||||
# OpenAI Function Calling Support
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
The [function calling](https://platform.openai.com/docs/guides/gpt/function-calling) capability of OpenAI's Chat Completions API allows developers to describe functions to the model, and have the model decide whether to output a JSON object specifying a function and appropriate arguments to call in response to the given prompt. This capability is enabled by two new API parameters to the `/v1/chat/completions` endpoint:
|
||||
The [function calling](https://platform.openai.com/docs/guides/gpt/function-calling) capability of OpenAI's Chat Completions API allows developers to describe functions to the model, and have the model decide whether to output a JSON object specifying a function and appropriate arguments to call in response to the given prompt. This capability is enabled by two new API parameters to the `/v1/chat/completions` endpoint:
|
||||
|
||||
- `function_call` - auto (default), none, or a specific function to call
|
||||
- `functions` - JSON descriptions of the functions available to the model
|
||||
|
||||
@@ -20,15 +22,15 @@ We have received several community requests to provide support for this capabili
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* Minimize changes to the core kernel for OpenAI-specific functionality
|
||||
* Cost concerns with including a long list of function descriptions in the request
|
||||
* Security and cost concerns with automatically executing functions returned by the model
|
||||
- Minimize changes to the core kernel for OpenAI-specific functionality
|
||||
- Cost concerns with including a long list of function descriptions in the request
|
||||
- Security and cost concerns with automatically executing functions returned by the model
|
||||
|
||||
## Considered Options
|
||||
|
||||
* Support sending/receiving functions via chat completions endpoint _with_ modifications to interfaces
|
||||
* Support sending/receiving functions via chat completions endpoint _without_ modifications to interfaces
|
||||
* Implement a planner around the function calling capability
|
||||
- Support sending/receiving functions via chat completions endpoint _with_ modifications to interfaces
|
||||
- Support sending/receiving functions via chat completions endpoint _without_ modifications to interfaces
|
||||
- Implement a planner around the function calling capability
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
@@ -38,32 +40,33 @@ With this option, we utilize the existing request settings object to send functi
|
||||
|
||||
### Consequences
|
||||
|
||||
* Good, because avoids breaking changes to the core kernel
|
||||
* Good, because OpenAI-specific functionality is contained to the OpenAI connector package
|
||||
* Good, because allows app to control what functions are available to the model (including non-SK functions)
|
||||
* Good, because keeps the option open for integrating with planners in the future
|
||||
* Neutral, because requires app developer to validate and execute resulting function
|
||||
* Bad, because not as obvious how to use this capability and access the function results
|
||||
- Good, because avoids breaking changes to the core kernel
|
||||
- Good, because OpenAI-specific functionality is contained to the OpenAI connector package
|
||||
- Good, because allows app to control what functions are available to the model (including non-SK functions)
|
||||
- Good, because keeps the option open for integrating with planners in the future
|
||||
- Neutral, because requires app developer to validate and execute resulting function
|
||||
- Bad, because not as obvious how to use this capability and access the function results
|
||||
|
||||
## Pros and Cons of the Options
|
||||
|
||||
### Support sending/receiving functions _with_ modifications to chat completions interfaces
|
||||
|
||||
This option would update the `IChatCompletion` and `IChatResult` interfaces to expose parameters/methods for providing and accessing function information.
|
||||
This option would update the `IChatCompletion` and `IChatResult` interfaces to expose parameters/methods for providing and accessing function information.
|
||||
|
||||
* Good, because provides a clear path for using the function calling capability
|
||||
* Good, because allows app to control what functions are available to the model (including non-SK functions)
|
||||
* Neutral, because requires app developer to validate and execute resulting function
|
||||
* Bad, because introduces breaking changes to core kernel abstractions
|
||||
* Bad, because OpenAI-specific functionality would be included in core kernel abstractions and would need to be ignored by other model providers
|
||||
- Good, because provides a clear path for using the function calling capability
|
||||
- Good, because allows app to control what functions are available to the model (including non-SK functions)
|
||||
- Neutral, because requires app developer to validate and execute resulting function
|
||||
- Bad, because introduces breaking changes to core kernel abstractions
|
||||
- Bad, because OpenAI-specific functionality would be included in core kernel abstractions and would need to be ignored by other model providers
|
||||
|
||||
### Implement a planner around the function calling capability
|
||||
|
||||
Orchestrating external function calls fits within SK's concept of planning. With this approach, we would implement a planner that would take the function calling result and produce a plan that the app developer could execute (similar to SK's ActionPlanner).
|
||||
Orchestrating external function calls fits within SK's concept of planning. With this approach, we would implement a planner that would take the function calling result and produce a plan that the app developer could execute (similar to SK's ActionPlanner).
|
||||
|
||||
* Good, because producing a plan result makes it easy for the app developer to execute the chosen function
|
||||
* Bad, because functions would need to be registered with the kernel in order to be executed
|
||||
* Bad, because would create confusion about when to use which planner
|
||||
- Good, because producing a plan result makes it easy for the app developer to execute the chosen function
|
||||
- Bad, because functions would need to be registered with the kernel in order to be executed
|
||||
- Bad, because would create confusion about when to use which planner
|
||||
|
||||
## Additional notes
|
||||
There has been much discussion and debate over the pros and cons of automatically invoking a function returned by the OpenAI model, if it is registered with the kernel. As there are still many open questions around this behavior and its implications, we have decided to not include this capability in the initial implementation. We will continue to explore this option and may include it in a future update.
|
||||
|
||||
There has been much discussion and debate over the pros and cons of automatically invoking a function returned by the OpenAI model, if it is registered with the kernel. As there are still many open questions around this behavior and its implications, we have decided to not include this capability in the initial implementation. We will continue to explore this option and may include it in a future update.
|
||||
+19
-18
@@ -2,10 +2,11 @@
|
||||
status: approved
|
||||
contact: markwallace-microsoft
|
||||
date: 2023-10-26
|
||||
deciders: mabolan, markwallace-microsoft, semenshi, rbarreto
|
||||
deciders: matthewbolanos, markwallace-microsoft, SergeyMenshykh, RogerBarreto
|
||||
consulted: dmytrostruk
|
||||
informed:
|
||||
informed:
|
||||
---
|
||||
|
||||
# Custom Prompt Template Formats
|
||||
|
||||
## Context and Problem Statement
|
||||
@@ -58,7 +59,7 @@ The `BasicPromptTemplateEngine` uses the `TemplateTokenizer` to parse the templa
|
||||
Then it renders the template i.e. inserts variables and executes functions. Some sample timings for these operations:
|
||||
|
||||
| Operation | Ticks | Milliseconds |
|
||||
|------------------|---------|--------------|
|
||||
| ---------------- | ------- | ------------ |
|
||||
| Extract blocks | 1044427 | 103 |
|
||||
| Render variables | 168 | 0 |
|
||||
|
||||
@@ -68,10 +69,10 @@ Sample template used was: `"{{variable1}} {{variable2}} {{variable3}} {{variable
|
||||
|
||||
Using `HandlebarsDotNet` for the same use case results in the following timings:
|
||||
|
||||
| Operation | Ticks | Milliseconds |
|
||||
|------------------|---------|--------------|
|
||||
| Compile template | 66277 | 6 |
|
||||
| Render variables | 4173 | 0 |
|
||||
| Operation | Ticks | Milliseconds |
|
||||
| ---------------- | ----- | ------------ |
|
||||
| Compile template | 66277 | 6 |
|
||||
| Render variables | 4173 | 0 |
|
||||
|
||||
**By separating the extract blocks/compile from the render variables operation it will be possible to optimise performance by compiling templates just once.**
|
||||
|
||||
@@ -175,15 +176,15 @@ and then register the helpers. This means we cannot take advantage of the perfor
|
||||
|
||||
In no particular order:
|
||||
|
||||
* Support creating a semantic function without a `IKernel`instance.
|
||||
* Support late binding of functions i.e., having functions resolved when the prompt is rendered.
|
||||
* Support allowing the prompt template to be parsed (compiled) just once to optimize performance if needed.
|
||||
* Support using multiple prompt template formats with a single `Kernel` instance.
|
||||
* Provide simple abstractions which allow third parties to implement support for custom prompt template formats.
|
||||
- Support creating a semantic function without a `IKernel`instance.
|
||||
- Support late binding of functions i.e., having functions resolved when the prompt is rendered.
|
||||
- Support allowing the prompt template to be parsed (compiled) just once to optimize performance if needed.
|
||||
- Support using multiple prompt template formats with a single `Kernel` instance.
|
||||
- Provide simple abstractions which allow third parties to implement support for custom prompt template formats.
|
||||
|
||||
## Considered Options
|
||||
|
||||
* Obsolete `IPromptTemplateEngine` and replace with `IPromptTemplateFactory`.
|
||||
- Obsolete `IPromptTemplateEngine` and replace with `IPromptTemplateFactory`.
|
||||
|
||||
### Obsolete `IPromptTemplateEngine` and replace with `IPromptTemplateFactory`
|
||||
|
||||
@@ -216,9 +217,9 @@ Console.WriteLine(result.GetValue<string>());
|
||||
|
||||
**Notes:**
|
||||
|
||||
* `BasicPromptTemplateFactory` will be the default implementation and will be automatically provided in `KernelSemanticFunctionExtensions`. Developers will also be able to provide their own implementation.
|
||||
* The factory uses the new `PromptTemplateConfig.TemplateFormat` to create the appropriate `IPromptTemplate` instance.
|
||||
* We should look to remove `promptTemplateConfig` as a parameter to `CreateSemanticFunction`. That change is outside of the scope of this ADR.
|
||||
- `BasicPromptTemplateFactory` will be the default implementation and will be automatically provided in `KernelSemanticFunctionExtensions`. Developers will also be able to provide their own implementation.
|
||||
- The factory uses the new `PromptTemplateConfig.TemplateFormat` to create the appropriate `IPromptTemplate` instance.
|
||||
- We should look to remove `promptTemplateConfig` as a parameter to `CreateSemanticFunction`. That change is outside of the scope of this ADR.
|
||||
|
||||
The `BasicPromptTemplateFactory` and `BasicPromptTemplate` implementations look as follows:
|
||||
|
||||
@@ -275,8 +276,8 @@ public sealed class BasicPromptTemplate : IPromptTemplate
|
||||
|
||||
**Note:**
|
||||
|
||||
* The call to `ExtractBlocks` is called lazily once for each prompt template
|
||||
* The `RenderAsync` doesn't need to extract the blocks every time
|
||||
- The call to `ExtractBlocks` is called lazily once for each prompt template
|
||||
- The `RenderAsync` doesn't need to extract the blocks every time
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
@@ -0,0 +1,454 @@
|
||||
## Context and Problem Statement
|
||||
|
||||
Currently Kernel invoking and invoked handlers don't expose the prompt to the handlers.
|
||||
|
||||
The proposal is a way to expose the prompt to the handlers.
|
||||
|
||||
- Pre-Execution / Invoking
|
||||
|
||||
- Get: Prompt generated by the current `SemanticFunction.TemplateEngine` before calling the LLM
|
||||
- Set: Modify a prompt content before sending it to LLM
|
||||
|
||||
- Post-Execution / Invoked
|
||||
|
||||
- Get: Generated Prompt
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
- Prompt template should be generated just once per function execution within the Kernel.RunAsync execution.
|
||||
- Handlers should be able to see and modify the prompt before the LLM execution.
|
||||
- Handlers should be able to see prompt after the LLM execution.
|
||||
- Calling Kernel.RunAsync(function) or ISKFunction.InvokeAsync(kernel) should trigger the events.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Skip plan steps using Pre-Hooks.
|
||||
- Get the used services (Template Engine, IAIServices, etc) in the Pre/Post Hooks.
|
||||
- Get the request settings in the Pre/Post Hooks.
|
||||
|
||||
## Current State of Kernel for Pre/Post Hooks
|
||||
|
||||
Current state of Kernel:
|
||||
|
||||
```csharp
|
||||
class Kernel : IKernel
|
||||
|
||||
RunAsync()
|
||||
{
|
||||
var context = this.CreateNewContext(variables);
|
||||
var functionDetails = skFunction.Describe();
|
||||
var functionInvokingArgs = this.OnFunctionInvoking(functionDetails, context);
|
||||
|
||||
functionResult = await skFunction.InvokeAsync(context, cancellationToken: cancellationToken);
|
||||
var functionInvokedArgs = this.OnFunctionInvoked(functionDetails, functionResult);
|
||||
}
|
||||
```
|
||||
|
||||
## Developer Experience
|
||||
|
||||
Below is the expected end user experience when coding using Pre/Post Hooks to get or modify prompts.
|
||||
|
||||
```csharp
|
||||
const string FunctionPrompt = "Write a random paragraph about: {{$input}}.";
|
||||
|
||||
var excuseFunction = kernel.CreateSemanticFunction(...);
|
||||
|
||||
void MyPreHandler(object? sender, FunctionInvokingEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"{e.FunctionView.PluginName}.{e.FunctionView.Name} : Pre Execution Handler - Triggered");
|
||||
|
||||
// Will be false for non semantic functions
|
||||
if (e.TryGetRenderedPrompt(out var prompt))
|
||||
{
|
||||
Console.WriteLine("Rendered Prompt:");
|
||||
Console.WriteLine(prompt);
|
||||
|
||||
// Update the prompt if needed
|
||||
e.TryUpdateRenderedPrompt("Write a random paragraph about: Overriding a prompt");
|
||||
}
|
||||
}
|
||||
|
||||
void MyPostHandler(object? sender, FunctionInvokedEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"{e.FunctionView.PluginName}.{e.FunctionView.Name} : Post Execution Handler - Triggered");
|
||||
// Will be false for non semantic functions
|
||||
if (e.TryGetRenderedPrompt(out var prompt))
|
||||
{
|
||||
Console.WriteLine("Used Prompt:");
|
||||
Console.WriteLine(prompt);
|
||||
}
|
||||
}
|
||||
|
||||
kernel.FunctionInvoking += MyPreHandler;
|
||||
kernel.FunctionInvoked += MyPostHandler;
|
||||
|
||||
const string Input = "I missed the F1 final race";
|
||||
var result = await kernel.RunAsync(Input, excuseFunction);
|
||||
Console.WriteLine($"Function Result: {result.GetValue<string>()}");
|
||||
```
|
||||
|
||||
Expected output:
|
||||
|
||||
```
|
||||
MyPlugin.MyFunction : Pre Execution Handler - Triggered
|
||||
Rendered Prompt:
|
||||
Write a random paragraph about: I missed the F1 final race.
|
||||
|
||||
MyPlugin.MyFunction : Post Execution Handler - Triggered
|
||||
Used Prompt:
|
||||
Write a random paragraph about: Overriding a prompt
|
||||
|
||||
FunctionResult: <LLM Completion>
|
||||
```
|
||||
|
||||
## Considered Options
|
||||
|
||||
### Improvements Common to all options
|
||||
|
||||
Move `Dictionary<string, object>` property `Metadata` from `FunctionInvokedEventArgs` to `SKEventArgs` abstract class.
|
||||
|
||||
Pro:
|
||||
|
||||
- This will make all SKEventArgs extensible, allowing extra information to be passed to the EventArgs when `specialization` isn't possible.
|
||||
|
||||
### Option 1: Kernel awareness of SemanticFunctions
|
||||
|
||||
```csharp
|
||||
class Kernel : IKernel
|
||||
|
||||
RunAsync()
|
||||
{
|
||||
|
||||
if (skFunction is SemanticFunction semanticFunction)
|
||||
{
|
||||
var prompt = await semanticFunction.TemplateEngine.RenderAsync(semanticFunction.Template, context);
|
||||
var functionInvokingArgs = this.OnFunctionInvoking(functionDetails, context, prompt);
|
||||
// InvokeWithPromptAsync internal
|
||||
functionResult = await semanticFunction.InternalInvokeWithPromptAsync(prompt, context, cancellationToken: cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
functionResult = await skFunction.InvokeAsync(context, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
class SemanticFunction : ISKFunction
|
||||
|
||||
public InvokeAsync(context, cancellationToken)
|
||||
{
|
||||
var prompt = _templateEngine.RenderAsync();
|
||||
return InternalInvokeWithPromptAsync(prompt, context, cancellationToken);
|
||||
}
|
||||
|
||||
internal InternalInvokeWithPromptAsync(string prompt)
|
||||
{
|
||||
... current logic to call LLM
|
||||
}
|
||||
```
|
||||
|
||||
### Pros and Cons
|
||||
|
||||
Pros:
|
||||
|
||||
- Simpler and quicker to implement
|
||||
- Small number of changes limited mostly to `Kernel` and `SemanticFunction` classes
|
||||
|
||||
Cons:
|
||||
|
||||
- `Kernel` is aware of `SemanticFunction` implementation details
|
||||
- Not extensible to show prompts of custom `ISKFunctions` implementations
|
||||
|
||||
### Option 2: Delegate to the ISKFunction how to handle events (Interfaces approach)
|
||||
|
||||
```csharp
|
||||
class Kernel : IKernel
|
||||
{
|
||||
RunAsync() {
|
||||
var functionInvokingArgs = await this.TriggerEvent<FunctionInvokingEventArgs>(this.FunctionInvoking, skFunction, context);
|
||||
|
||||
var functionResult = await skFunction.InvokeAsync(context, cancellationToken: cancellationToken);
|
||||
|
||||
var functionInvokedArgs = await this.TriggerEvent<FunctionInvokedEventArgs>(
|
||||
this.FunctionInvoked,
|
||||
skFunction,
|
||||
context);
|
||||
}
|
||||
|
||||
private TEventArgs? TriggerEvent<TEventArgs>(EventHandler<TEventArgs>? eventHandler, ISKFunction function, SKContext context) where TEventArgs : SKEventArgs
|
||||
{
|
||||
if (eventHandler is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (function is ISKFunctionEventSupport<TEventArgs> supportedFunction)
|
||||
{
|
||||
var eventArgs = await supportedFunction.PrepareEventArgsAsync(context);
|
||||
eventHandler.Invoke(this, eventArgs);
|
||||
return eventArgs;
|
||||
}
|
||||
|
||||
// Think about allowing to add data with the extra interface.
|
||||
|
||||
// If a function don't support the specific event we can:
|
||||
return null; // Ignore or Throw.
|
||||
throw new NotSupportedException($"The provided function \"{function.Name}\" does not supports and implements ISKFunctionHandles<{typeof(TEventArgs).Name}>");
|
||||
}
|
||||
}
|
||||
|
||||
public interface ISKFunctionEventSupport<TEventArgs> where TEventArgs : SKEventArgs
|
||||
{
|
||||
Task<TEventArgs> PrepareEventArgsAsync(SKContext context, TEventArgs? eventArgs = null);
|
||||
}
|
||||
|
||||
class SemanticFunction : ISKFunction,
|
||||
ISKFunctionEventSupport<FunctionInvokingEventArgs>,
|
||||
ISKFunctionEventSupport<FunctionInvokedEventArgs>
|
||||
{
|
||||
|
||||
public FunctionInvokingEventArgs PrepareEventArgsAsync(SKContext context, FunctionInvokingEventArgs? eventArgs = null)
|
||||
{
|
||||
var renderedPrompt = await this.RenderPromptTemplateAsync(context);
|
||||
context.Variables.Set(SemanticFunction.RenderedPromptKey, renderedPrompt);
|
||||
|
||||
return new SemanticFunctionInvokingEventArgs(this.Describe(), context);
|
||||
// OR Metadata Dictionary<string, object>
|
||||
return new FunctionInvokingEventArgs(this.Describe(), context, new Dictionary<string, object>() { { RenderedPrompt, renderedPrompt } });
|
||||
}
|
||||
|
||||
public FunctionInvokedEventArgs PrepareEventArgsAsync(SKContext context, FunctionInvokedEventArgs? eventArgs = null)
|
||||
{
|
||||
return Task.FromResult<FunctionInvokedEventArgs>(new SemanticFunctionInvokedEventArgs(this.Describe(), context));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SemanticFunctionInvokedEventArgs : FunctionInvokedEventArgs
|
||||
{
|
||||
public SemanticFunctionInvokedEventArgs(FunctionDescription functionDescription, SKContext context)
|
||||
: base(functionDescription, context)
|
||||
{
|
||||
_context = context;
|
||||
Metadata[RenderedPromptKey] = this._context.Variables[RenderedPromptKey];
|
||||
}
|
||||
|
||||
public string? RenderedPrompt => this.Metadata[RenderedPromptKey];
|
||||
|
||||
}
|
||||
|
||||
public sealed class SemanticFunctionInvokingEventArgs : FunctionInvokingEventArgs
|
||||
{
|
||||
public SemanticFunctionInvokingEventArgs(FunctionDescription functionDescription, SKContext context)
|
||||
: base(functionDescription, context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
public string? RenderedPrompt => this._context.Variables[RenderedPromptKey];
|
||||
}
|
||||
```
|
||||
|
||||
### Pros and Cons
|
||||
|
||||
Pros:
|
||||
|
||||
- `Kernel` is not aware of `SemanticFunction` implementation details or any other `ISKFunction` implementation
|
||||
- Extensible to show dedicated EventArgs per custom `ISKFunctions` implementation, including prompts for semantic functions
|
||||
- Extensible to support future events on the Kernel thru the `ISKFunctionEventSupport<NewEvent>` interface
|
||||
- Functions can have their own EventArgs specialization.
|
||||
- Interface is optional, so custom `ISKFunctions` can choose to implement it or not
|
||||
|
||||
Cons:
|
||||
|
||||
- Any custom functions now will have to responsibility implement the `ISKFunctionEventSupport` interface if they want to support events.
|
||||
- Handling events in another `ISKFunction` requires more complex approaches to manage the context and the prompt + any other data in different event handling methods.
|
||||
|
||||
### Option 3: Delegate to the ISKFunction how to handle events (InvokeAsync Delegates approach)
|
||||
|
||||
Add Kernel event handler delegate wrappers to `ISKFunction.InvokeAsync` interface.
|
||||
This approach shares the responsibility of handling the events between the `Kernel` and the `ISKFunction` implementation, flow control will be handled by the Kernel and the `ISKFunction` will be responsible for calling the delegate wrappers and adding data to the `SKEventArgs` that will be passed to the handlers.
|
||||
|
||||
```csharp
|
||||
class Kernel : IKernel
|
||||
{
|
||||
RunAsync() {
|
||||
var functionInvokingDelegateWrapper = new(this.FunctionInvoking);
|
||||
var functionInvokedDelegateWrapper = new(this.FunctionInvoked);
|
||||
|
||||
var functionResult = await skFunction.InvokeAsync(context, functionInvokingDelegateWrapper, functionInvokingDelegateWrapper, functionInvokedDelegateWrapper);
|
||||
|
||||
// Kernel will analyze the delegate results and make flow related decisions
|
||||
if (functionInvokingDelegateWrapper.EventArgs.CancelRequested ... ) { ... }
|
||||
if (functionInvokingDelegateWrapper.EventArgs.SkipRequested ... ) { ... }
|
||||
if (functionInvokedDelegateWrapper.EventArgs.Repeat ... ) { ... }
|
||||
}
|
||||
}
|
||||
|
||||
class SemanticFunction : ISKFunction {
|
||||
InvokeAsync(
|
||||
SKContext context,
|
||||
FunctionInvokingDelegateWrapper functionInvokingDelegateWrapper,
|
||||
FunctionInvokedDelegateWrapper functionInvokedDelegateWrapper)
|
||||
{
|
||||
// The Semantic will have to call the delegate wrappers and share responsibility with the `Kernel`.
|
||||
if (functionInvokingDelegateWrapper.Handler is not null)
|
||||
{
|
||||
var renderedPrompt = await this.RenderPromptTemplateAsync(context);
|
||||
functionInvokingDelegateWrapper.EventArgs.RenderedPrompt = renderedPrompt;
|
||||
|
||||
functionInvokingDelegateWrapper.Handler.Invoke(this, functionInvokingDelegateWrapper.EventArgs);
|
||||
|
||||
if (functionInvokingDelegateWrapper.EventArgs?.CancelToken.IsCancellationRequested ?? false)
|
||||
{
|
||||
// Need to enforce an non processed result
|
||||
return new SKFunctionResult(context);
|
||||
|
||||
//OR make InvokeAsync allow returning null FunctionResult?
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for the EventHandler
|
||||
class FunctionDelegateWrapper<TEventArgs> where TEventArgs : SKEventArgs
|
||||
{
|
||||
FunctionInvokingDelegateWrapper(EventHandler<TEventArgs> eventHandler) {}
|
||||
|
||||
// Set allows specialized eventargs to be set.
|
||||
public TEventArgs EventArgs { get; set; }
|
||||
public EventHandler<TEventArgs> Handler => _eventHandler;
|
||||
}
|
||||
```
|
||||
|
||||
### Pros and Cons
|
||||
|
||||
Pros:
|
||||
|
||||
- `ISKFunction` has less code/complexity to handle and expose data (Rendered Prompt) and state in the EventArgs.
|
||||
- `Kernel` is not aware of `SemanticFunction` implementation details or any other `ISKFunction` implementation
|
||||
- `Kernel` has less code/complexity
|
||||
- Could be extensible to show dedicated EventArgs per custom `ISKFunctions` implementation, including prompts for semantic functions
|
||||
|
||||
Cons:
|
||||
|
||||
- Unable to add new events if needed (ISKFunction interface change needed)
|
||||
- Functions need to implement behavior related to dependency (Kernel) events
|
||||
- Since Kernel needs to interact with the result of an event handler, a wrapper strategy is needed to access results by reference at the kernel level (control of flow)
|
||||
- Passing Kernel event handlers full responsibility downstream to the functions don't sound quite right (Single Responsibility)
|
||||
|
||||
### Option 4: Delegate to the ISKFunction how to handle events (SKContext Delegates approach)
|
||||
|
||||
Add Kernel event handler delegate wrappers to `ISKFunction.InvokeAsync` interface.
|
||||
This approach shares the responsibility of handling the events between the `Kernel` and the `ISKFunction` implementation, flow control will be handled by the Kernel and the `ISKFunction` will be responsible for calling the delegate wrappers and adding data to the `SKEventArgs` that will be passed to the handlers.
|
||||
|
||||
```csharp
|
||||
class Kernel : IKernel
|
||||
{
|
||||
CreateNewContext() {
|
||||
var context = new SKContext(...);
|
||||
context.AddEventHandlers(this.FunctionInvoking, this.FunctionInvoked);
|
||||
return context;
|
||||
}
|
||||
RunAsync() {
|
||||
functionResult = await skFunction.InvokeAsync(context, ...);
|
||||
if (this.IsCancelRequested(functionResult.Context)))
|
||||
break;
|
||||
if (this.IsSkipRequested(functionResult.Context))
|
||||
continue;
|
||||
if (this.IsRepeatRequested(...))
|
||||
goto repeat;
|
||||
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
class SKContext {
|
||||
|
||||
internal EventHandlerWrapper<FunctionInvokingEventArgs>? FunctionInvokingHandler { get; private set; }
|
||||
internal EventHandlerWrapper<FunctionInvokedEventArgs>? FunctionInvokedHandler { get; private set; }
|
||||
|
||||
internal SKContext(
|
||||
...
|
||||
ICollection<EventHandlerWrapper?>? eventHandlerWrappers = null
|
||||
{
|
||||
...
|
||||
this.InitializeEventWrappers(eventHandlerWrappers);
|
||||
}
|
||||
|
||||
void InitializeEventWrappers(ICollection<EventHandlerWrapper?>? eventHandlerWrappers)
|
||||
{
|
||||
if (eventHandlerWrappers is not null)
|
||||
{
|
||||
foreach (var handler in eventHandlerWrappers)
|
||||
{
|
||||
if (handler is EventHandlerWrapper<FunctionInvokingEventArgs> invokingWrapper)
|
||||
{
|
||||
this.FunctionInvokingHandler = invokingWrapper;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (handler is EventHandlerWrapper<FunctionInvokedEventArgs> invokedWrapper)
|
||||
{
|
||||
this.FunctionInvokedHandler = invokedWrapper;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SemanticFunction : ISKFunction {
|
||||
InvokeAsync(
|
||||
SKContext context
|
||||
{
|
||||
string renderedPrompt = await this._promptTemplate.RenderAsync(context, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
this.CallFunctionInvoking(context, renderedPrompt);
|
||||
if (this.IsInvokingCancelOrSkipRequested(context, out var stopReason))
|
||||
{
|
||||
return new StopFunctionResult(this.Name, this.PluginName, context, stopReason!.Value);
|
||||
}
|
||||
|
||||
string completion = await GetCompletionsResultContentAsync(...);
|
||||
|
||||
var result = new FunctionResult(this.Name, this.PluginName, context, completion);
|
||||
result.Metadata.Add(SemanticFunction.RenderedPromptMetadataKey, renderedPrompt);
|
||||
|
||||
this.CallFunctionInvoked(result, context, renderedPrompt);
|
||||
if (this.IsInvokedCancelRequested(context, out stopReason))
|
||||
{
|
||||
return new StopFunctionResult(this.Name, this.PluginName, context, result.Value, stopReason!.Value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pros and Cons
|
||||
|
||||
Pros:
|
||||
|
||||
- `ISKFunction` has less code/complexity to handle and expose data (Rendered Prompt) and state in the EventArgs.
|
||||
- `Kernel` is not aware of `SemanticFunction` implementation details or any other `ISKFunction` implementation
|
||||
- `Kernel` has less code/complexity
|
||||
- Could be extensible to show dedicated EventArgs per custom `ISKFunctions` implementation, including prompts for semantic functions
|
||||
- More extensible as `ISKFunction` interface doesn't need to change to add new events.
|
||||
- `SKContext` can be extended to add new events without introducing breaking changes.
|
||||
|
||||
Cons:
|
||||
|
||||
- Functions now need to implement logic to handle in-context events
|
||||
- Since Kernel needs to interact with the result of an event handler, a wrapper strategy is needed to access results by reference at the kernel level (control of flow)
|
||||
- Passing Kernel event handlers full responsibility downstream to the functions don't sound quite right (Single Responsibility)
|
||||
|
||||
## Decision outcome
|
||||
|
||||
### Option 4: Delegate to the ISKFunction how to handle events (SKContext Delegates approach)
|
||||
|
||||
This allow the functions to implement some of the kernel logic but has the big benefit of not splitting logic in different methods for the same Execution Context.
|
||||
|
||||
Biggest benefit:
|
||||
**`ISKFunction` has less code/complexity to handle and expose data and state in the EventArgs.**
|
||||
**`ISKFunction` interface doesn't need to change to add new events.**
|
||||
|
||||
This implementation allows to get the renderedPrompt in the InvokeAsync without having to manage the context and the prompt in different methods.
|
||||
|
||||
The above also applies for any other data that is available in the invocation and can be added as a new EventArgs property.
|
||||
+26
-25
@@ -3,10 +3,11 @@
|
||||
status: approved
|
||||
contact: markwallace-microsoft
|
||||
date: 2023-10-26
|
||||
deciders: markwallace-microsoft, semenshi, rogerbarreto
|
||||
consulted: mabolan, dmytrostruk
|
||||
informed:
|
||||
deciders: markwallace-microsoft, SergeyMenshykh, rogerbarreto
|
||||
consulted: matthewbolanos, dmytrostruk
|
||||
informed:
|
||||
---
|
||||
|
||||
# Multiple Model Support for Semantic Functions
|
||||
|
||||
## Context and Problem Statement
|
||||
@@ -18,13 +19,13 @@ Developers need to be able to use multiple models simultaneously e.g., using GPT
|
||||
In scope for Semantic Kernel V1.0 is the ability to select AI Service and Model Request Settings:
|
||||
|
||||
1. By service id.
|
||||
* A Service id uniquely identifies a registered AI Service and is typically defined in the scope of an application.
|
||||
- A Service id uniquely identifies a registered AI Service and is typically defined in the scope of an application.
|
||||
1. By developer defined strategy.
|
||||
* A _developer defined strategy_ is a code first approach where a developer provides the logic.
|
||||
- A _developer defined strategy_ is a code first approach where a developer provides the logic.
|
||||
1. By model id.
|
||||
* A model id uniquely identifies a Large Language Model. Multiple AI service providers can support the same LLM.
|
||||
- A model id uniquely identifies a Large Language Model. Multiple AI service providers can support the same LLM.
|
||||
1. By arbitrary AI service attributes
|
||||
* E.g. an AI service can define a provider id which uniquely identifies an AI provider e.g. "Azure OpenAI", "OpenAI", "Hugging Face"
|
||||
- E.g. an AI service can define a provider id which uniquely identifies an AI provider e.g. "Azure OpenAI", "OpenAI", "Hugging Face"
|
||||
|
||||
**This ADR focuses on items 1 & 2 in the above list. To implement 3 & 4 we need to provide the ability to store `AIService` metadata.**
|
||||
|
||||
@@ -80,18 +81,18 @@ The interface is defined as follows:
|
||||
public interface IAIServiceSelector
|
||||
{
|
||||
(T?, AIRequestSettings?) SelectAIService<T>(
|
||||
string renderedPrompt,
|
||||
IAIServiceProvider serviceProvider,
|
||||
string renderedPrompt,
|
||||
IAIServiceProvider serviceProvider,
|
||||
IReadOnlyList<AIRequestSettings>? modelSettings) where T : IAIService;
|
||||
}
|
||||
```
|
||||
|
||||
A default `OrderedIAIServiceSelector` implementation is provided which selects the AI service based on the order of the model request settings defined for the semantic function.
|
||||
|
||||
* The implementation checks if a service exists which the corresponding service id and if it does it and the associated model request settings will be used.
|
||||
* In no model request settings are defined then the default text completion service is used.
|
||||
* A default set of request settings can be specified by leaving the service id undefined or empty, the first such default will be used.
|
||||
* If no default if specified and none of the specified services are available the operation will fail.
|
||||
- The implementation checks if a service exists which the corresponding service id and if it does it and the associated model request settings will be used.
|
||||
- In no model request settings are defined then the default text completion service is used.
|
||||
- A default set of request settings can be specified by leaving the service id undefined or empty, the first such default will be used.
|
||||
- If no default if specified and none of the specified services are available the operation will fail.
|
||||
|
||||
### Select AI Service and Model Request Settings By Developer Defined Strategy
|
||||
|
||||
@@ -144,30 +145,30 @@ var oai = TestConfiguration.OpenAI;
|
||||
// Configure a Kernel with multiple LLM's
|
||||
IKernel kernel = Kernel.Builder
|
||||
.WithLoggerFactory(ConsoleLogger.LoggerFactory)
|
||||
.WithAzureTextCompletionService(deploymentName: aoai.DeploymentName,
|
||||
.WithAzureTextCompletionService(deploymentName: aoai.DeploymentName,
|
||||
endpoint: aoai.Endpoint, serviceId: "AzureText", apiKey: aoai.ApiKey)
|
||||
.WithAzureChatCompletionService(deploymentName: aoai.ChatDeploymentName,
|
||||
.WithAzureChatCompletionService(deploymentName: aoai.ChatDeploymentName,
|
||||
endpoint: aoai.Endpoint, serviceId: "AzureChat", apiKey: aoai.ApiKey)
|
||||
.WithOpenAITextCompletionService(modelId: oai.ModelId,
|
||||
.WithOpenAITextCompletionService(modelId: oai.ModelId,
|
||||
serviceId: "OpenAIText", apiKey: oai.ApiKey)
|
||||
.WithOpenAIChatCompletionService(modelId: oai.ChatModelId,
|
||||
.WithOpenAIChatCompletionService(modelId: oai.ChatModelId,
|
||||
serviceId: "OpenAIChat", apiKey: oai.ApiKey)
|
||||
.Build();
|
||||
|
||||
// Invoke the semantic function and service and request settings to use
|
||||
result = await kernel.InvokeSemanticFunctionAsync(prompt,
|
||||
requestSettings: new OpenAIRequestSettings()
|
||||
result = await kernel.InvokeSemanticFunctionAsync(prompt,
|
||||
requestSettings: new OpenAIRequestSettings()
|
||||
{ ServiceId = "AzureText", MaxTokens = 60 });
|
||||
|
||||
result = await kernel.InvokeSemanticFunctionAsync(prompt,
|
||||
requestSettings: new OpenAIRequestSettings()
|
||||
result = await kernel.InvokeSemanticFunctionAsync(prompt,
|
||||
requestSettings: new OpenAIRequestSettings()
|
||||
{ ServiceId = "AzureChat", MaxTokens = 120 });
|
||||
|
||||
result = await kernel.InvokeSemanticFunctionAsync(prompt,
|
||||
requestSettings: new OpenAIRequestSettings()
|
||||
result = await kernel.InvokeSemanticFunctionAsync(prompt,
|
||||
requestSettings: new OpenAIRequestSettings()
|
||||
{ ServiceId = "OpenAIText", MaxTokens = 180 });
|
||||
|
||||
result = await kernel.InvokeSemanticFunctionAsync(prompt,
|
||||
requestSettings: new OpenAIRequestSettings()
|
||||
result = await kernel.InvokeSemanticFunctionAsync(prompt,
|
||||
requestSettings: new OpenAIRequestSettings()
|
||||
{ ServiceId = "OpenAIChat", MaxTokens = 240 });
|
||||
```
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: accepted
|
||||
date: 2023-10-27
|
||||
contact: SergeyMenshykh
|
||||
deciders: markwallace, mabolan
|
||||
consulted:
|
||||
informed:
|
||||
---
|
||||
# Mapping of prompt syntax to completion service model
|
||||
|
||||
## Context and Problem Statement
|
||||
Today, SK runs all prompts using the text completion service by simply passing the rendered prompt as is, without any modifications, directly to a configured text completion service/connector. With the addition of new chat completion prompt and potentially other prompt types, such as image, on the horizon, we need a way to map completion-specific prompt syntax to the corresponding completion service data model.
|
||||
|
||||
For example, [the chat completion syntax](https://github.com/microsoft/semantic-kernel/blob/main/docs/decisions/0014-chat-completion-roles-in-prompt.md) in chat completion prompts:
|
||||
```xml
|
||||
<message role="system">
|
||||
You are a creative assistant helping individuals and businesses with their innovative projects.
|
||||
</message>
|
||||
<message role="user">
|
||||
I want to brainstorm the idea of {{$input}}
|
||||
</message>
|
||||
```
|
||||
should be mapped to an instance of the [ChatHistory](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatHistory.cs) class with two chat messages:
|
||||
|
||||
```csharp
|
||||
var messages = new ChatHistory();
|
||||
messages.Add(new ChatMessage(new AuthorRole("system"), "You are a creative assistant helping individuals and businesses with their innovative projects."));
|
||||
messages.Add(new ChatMessage(new AuthorRole("user"), "I want to brainstorm the idea of {{$input}}"));
|
||||
```
|
||||
|
||||
This ADR outlines potential options for the location of the prompt syntax mapping functionality.
|
||||
|
||||
## Considered Options
|
||||
**1. Completion connector classes.** This option proposes to have the completion connector classes responsible for the `prompt syntax -> completion service data model` mapping. The decision regarding whether this mapping functionality will be implemented in the connector classes themselves or delegated to mapper classes should be made during the implementation phase and is out of the scope of this ADR.
|
||||
|
||||
Pros:
|
||||
- The `SemanticFunction` won't need to change to support the mapping of a new prompt syntax when new completion type connectors (audio, video, etc.) are added.
|
||||
|
||||
- Prompts can be run by
|
||||
- Kernel.RunAsync
|
||||
- Completion connectors
|
||||
|
||||
Cons:
|
||||
- Every new completion connector, whether of an existing type or a new type, will have to implement the mapping functionality
|
||||
|
||||
**2. The SemanticFunction class.** This option proposes that the `SemanticFunction` class be responsible for the mapping. Similar to the previous option, the exact location of this functionality (whether in the `SemanticFunction` class or in the mapper classes) should be decided during the implementation phase.
|
||||
|
||||
Pros:
|
||||
- New connectors of a new type or existing ones don't have to implement the mapping functionality
|
||||
|
||||
Cons:
|
||||
- The `SemanticFunction` class has to be changed every time a new completion type needs to be supported by SK
|
||||
- Prompts can be run by Kernel.RunAsync method only.
|
||||
|
||||
## Decision Outcome
|
||||
It was agreed to go with the option 1 - `1. Completion connector classes` since it a more flexible solution and allows adding new connectors without modifying the `SemanticFunction` class.
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: {proposed}
|
||||
date: {2023-11-10}
|
||||
deciders: SergeyMenshykh, markwallace, rbarreto, dmytrostruk
|
||||
consulted:
|
||||
informed:
|
||||
---
|
||||
# Add AI Service Metadata
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
Developers need to be able to know more information about the `IAIService` that will be used to execute a semantic function or a plan.
|
||||
Some examples of why they need this information:
|
||||
|
||||
1. As an SK developer I want to write a `IAIServiceSelector` which allows me to select the OpenAI service to used based on the configured model id so that I can select the optimum (could eb cheapest) model to use based on the prompt I am executing.
|
||||
2. As an SK developer I want to write a pre-invocation hook which will compute the token size of a prompt before the prompt is sent to the LLM, so that I can determine the optimum `IAIService` to use. The library I am using to compute the token size of the prompt requires the model id.
|
||||
|
||||
Current implementation of `IAIService` is empty.
|
||||
|
||||
```csharp
|
||||
public interface IAIService
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
We can retrieve `IAIService` instances using `T IKernel.GetService<T>(string? name = null) where T : IAIService;` i.e., by service type and name (aka service id).
|
||||
The concrete instance of an `IAIService` can have different attributes depending on the service provider e.g. Azure OpenAI has a deployment name and OpenAI services have a model id.
|
||||
|
||||
Consider the following code snippet:
|
||||
|
||||
```csharp
|
||||
IKernel kernel = new KernelBuilder()
|
||||
.WithLoggerFactory(ConsoleLogger.LoggerFactory)
|
||||
.WithAzureChatCompletionService(
|
||||
deploymentName: chatDeploymentName,
|
||||
endpoint: endpoint,
|
||||
serviceId: "AzureOpenAIChat",
|
||||
apiKey: apiKey)
|
||||
.WithOpenAIChatCompletionService(
|
||||
modelId: openAIModelId,
|
||||
serviceId: "OpenAIChat",
|
||||
apiKey: openAIApiKey)
|
||||
.Build();
|
||||
|
||||
var service = kernel.GetService<IChatCompletion>("OpenAIChat");
|
||||
```
|
||||
|
||||
For Azure OpenAI we create the service with a deployment name. This is an arbitrary name specified by the person who deployed the AI model e.g. it could be `eastus-gpt-4` or `foo-bar`.
|
||||
For OpenAI we create the service with a model id. This must match one of the deployed OpenAI models.
|
||||
|
||||
From the perspective of a prompt creator using OpenAI, they will typically tune their prompts based on the model. So when the prompt is executed we need to be able to retrieve the service using the model id. As shown in the code snippet above the `IKernel` only supports retrieving an `IAService` instance by id. Additionally the `IChatCompletion` is a generic interface so it doesn't contain any properties which provide information about a specific connector instance.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* We need a mechanism to store generic metadata for an `IAIService` instance.
|
||||
* It will be the responsibility of the concrete `IAIService` instance to store the metadata that is relevant e.g., model id for OpenAI and HuggingFace AI services.
|
||||
* We need to be able to iterate over the available `IAIService` instances.
|
||||
|
||||
## Considered Options
|
||||
|
||||
* Option #1
|
||||
* Extend `IAIService` to include the following properties:
|
||||
* `string? ModelId { get; }` which returns the model id. It will be the responsibility of each `IAIService` implementation to populate this with the appropriate value.
|
||||
* `IReadOnlyDictionary<string, object> Attributes { get; }` which returns the attributes as a readonly dictionary. It will be the responsibility of each `IAIService` implementation to populate this with the appropriate metadata.
|
||||
* Extend `INamedServiceProvider` to include this method `ICollection<T> GetServices<T>() where T : TService;`
|
||||
* Extend `OpenAIKernelBuilderExtensions` so that `WithAzureXXX` methods will include a `modelId` property if a specific model can be targeted.
|
||||
* Option #2
|
||||
* Extend `IAIService` to include the following method:
|
||||
* `T? GetAttributes<T>() where T : AIServiceAttributes;` which returns an instance of `AIServiceAttributes`. It will be the responsibility of each `IAIService` implementation to define it's own service attributes class and populate this with the appropriate values.
|
||||
* Extend `INamedServiceProvider` to include this method `ICollection<T> GetServices<T>() where T : TService;`
|
||||
* Extend `OpenAIKernelBuilderExtensions` so that `WithAzureXXX` methods will include a `modelId` property if a specific model can be targeted.
|
||||
* Option #3
|
||||
* Option #2
|
||||
* Extend `IAIService` to include the following properties:
|
||||
* `public IReadOnlyDictionary<string, object> Attributes => this.InternalAttributes;` which returns a read only dictionary. It will be the responsibility of each `IAIService` implementation to define it's own service attributes class and populate this with the appropriate values.
|
||||
* `ModelId`
|
||||
* `Endpoint`
|
||||
* `ApiVersion`
|
||||
* Extend `INamedServiceProvider` to include this method `ICollection<T> GetServices<T>() where T : TService;`
|
||||
* Extend `OpenAIKernelBuilderExtensions` so that `WithAzureXXX` methods will include a `modelId` property if a specific model can be targeted.
|
||||
|
||||
These options would be used as follows:
|
||||
|
||||
As an SK developer I want to write a custom `IAIServiceSelector` which will select an AI service based on the model id because I want to restrict which LLM is used.
|
||||
In the sample below the service selector implementation looks for the first service that is a GPT3 model.
|
||||
|
||||
### Option 1
|
||||
|
||||
``` csharp
|
||||
public class Gpt3xAIServiceSelector : IAIServiceSelector
|
||||
{
|
||||
public (T?, AIRequestSettings?) SelectAIService<T>(string renderedPrompt, IAIServiceProvider serviceProvider, IReadOnlyList<AIRequestSettings>? modelSettings) where T : IAIService
|
||||
{
|
||||
var services = serviceProvider.GetServices<T>();
|
||||
foreach (var service in services)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(service.ModelId) && service.ModelId.StartsWith("gpt-3", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"Selected model: {service.ModelId}");
|
||||
return (service, new OpenAIRequestSettings());
|
||||
}
|
||||
}
|
||||
|
||||
throw new SKException("Unable to find AI service for GPT 3.x.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Option 2
|
||||
|
||||
``` csharp
|
||||
public class Gpt3xAIServiceSelector : IAIServiceSelector
|
||||
{
|
||||
public (T?, AIRequestSettings?) SelectAIService<T>(string renderedPrompt, IAIServiceProvider serviceProvider, IReadOnlyList<AIRequestSettings>? modelSettings) where T : IAIService
|
||||
{
|
||||
var services = serviceProvider.GetServices<T>();
|
||||
foreach (var service in services)
|
||||
{
|
||||
var serviceModelId = service.GetAttributes<AIServiceAttributes>()?.ModelId;
|
||||
if (!string.IsNullOrEmpty(serviceModelId) && serviceModelId.StartsWith("gpt-3", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"Selected model: {serviceModelId}");
|
||||
return (service, new OpenAIRequestSettings());
|
||||
}
|
||||
}
|
||||
|
||||
throw new SKException("Unable to find AI service for GPT 3.x.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Option 3
|
||||
|
||||
```csharp
|
||||
public (T?, AIRequestSettings?) SelectAIService<T>(string renderedPrompt, IAIServiceProvider serviceProvider, IReadOnlyList<AIRequestSettings>? modelSettings) where T : IAIService
|
||||
{
|
||||
var services = serviceProvider.GetServices<T>();
|
||||
foreach (var service in services)
|
||||
{
|
||||
var serviceModelId = service.GetModelId();
|
||||
var serviceOrganization = service.GetAttribute(OpenAIServiceAttributes.OrganizationKey);
|
||||
var serviceDeploymentName = service.GetAttribute(AzureOpenAIServiceAttributes.DeploymentNameKey);
|
||||
if (!string.IsNullOrEmpty(serviceModelId) && serviceModelId.StartsWith("gpt-3", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"Selected model: {serviceModelId}");
|
||||
return (service, new OpenAIRequestSettings());
|
||||
}
|
||||
}
|
||||
|
||||
throw new SKException("Unable to find AI service for GPT 3.x.");
|
||||
}
|
||||
```
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: Option 1, because it's a simple implementation and allows easy iteration over all possible attributes.
|
||||
@@ -0,0 +1,124 @@
|
||||
---
|
||||
status: proposed
|
||||
contact: dehoward
|
||||
date: 2023-11-06
|
||||
deciders: alliscode, markwallace-microsoft
|
||||
consulted:
|
||||
informed:
|
||||
---
|
||||
|
||||
# JSON Serializable Custom Types
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
This ADR aims to simplify the usage of custom types by allowing developers to use any type that can be serialized using `System.Text.Json`.
|
||||
|
||||
Standardizing on a JSON-serializable type is necessary to allow functions to be described using a JSON Schema within a planner's function manual. Using a JSON Schema to describe a function's input and output types will allow the planner to validate that the function is being used correctly.
|
||||
|
||||
Today, use of custom types within Semantic Kernel requires developers to implement a custom `TypeConverter` to convert to/from the string representation of the type. This is demonstrated in [Example60_AdvancedNativeFunctions](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/KernelSyntaxExamples/Example60_AdvancedNativeFunctions.cs#L202C44-L202C44) as seen below:
|
||||
|
||||
```csharp
|
||||
[TypeConverter(typeof(MyCustomTypeConverter))]
|
||||
private sealed class MyCustomType
|
||||
{
|
||||
public int Number { get; set; }
|
||||
|
||||
public string? Text { get; set; }
|
||||
}
|
||||
|
||||
private sealed class MyCustomTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => true;
|
||||
|
||||
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
|
||||
{
|
||||
return JsonSerializer.Deserialize<MyCustomType>((string)value);
|
||||
}
|
||||
|
||||
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
|
||||
{
|
||||
return JsonSerializer.Serialize(value);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The above approach will now only be needed when a custom type cannot be serialized using `System.Text.Json`.
|
||||
|
||||
## Considered Options
|
||||
|
||||
**1. Fallback to serialization using `System.Text.Json` if a `TypeConverter` is not available for the given type**
|
||||
|
||||
- Primitive types will be handled using their native `TypeConverter`s
|
||||
- We preserve the use of the native `TypeConverter` for primitive types to prevent any lossy conversions.
|
||||
- Complex types will be handled by their registered `TypeConverter`, if provided.
|
||||
- If no `TypeConverter` is registered for a complex type, our own `JsonSerializationTypeConverter` will be used to attempt JSON serialization/deserialization using `System.Text.Json`.
|
||||
- A detailed error message will be thrown if the type cannot be serialized/deserialized.
|
||||
|
||||
This will change the `GetTypeConverter()` method in `NativeFunction.cs` to look like the following, where before `null` was returned if no `TypeConverter` was found for the type:
|
||||
|
||||
```csharp
|
||||
private static TypeConverter GetTypeConverter(Type targetType)
|
||||
{
|
||||
if (targetType == typeof(byte)) { return new ByteConverter(); }
|
||||
if (targetType == typeof(sbyte)) { return new SByteConverter(); }
|
||||
if (targetType == typeof(bool)) { return new BooleanConverter(); }
|
||||
if (targetType == typeof(ushort)) { return new UInt16Converter(); }
|
||||
if (targetType == typeof(short)) { return new Int16Converter(); }
|
||||
if (targetType == typeof(char)) { return new CharConverter(); }
|
||||
if (targetType == typeof(uint)) { return new UInt32Converter(); }
|
||||
if (targetType == typeof(int)) { return new Int32Converter(); }
|
||||
if (targetType == typeof(ulong)) { return new UInt64Converter(); }
|
||||
if (targetType == typeof(long)) { return new Int64Converter(); }
|
||||
if (targetType == typeof(float)) { return new SingleConverter(); }
|
||||
if (targetType == typeof(double)) { return new DoubleConverter(); }
|
||||
if (targetType == typeof(decimal)) { return new DecimalConverter(); }
|
||||
if (targetType == typeof(TimeSpan)) { return new TimeSpanConverter(); }
|
||||
if (targetType == typeof(DateTime)) { return new DateTimeConverter(); }
|
||||
if (targetType == typeof(DateTimeOffset)) { return new DateTimeOffsetConverter(); }
|
||||
if (targetType == typeof(Uri)) { return new UriTypeConverter(); }
|
||||
if (targetType == typeof(Guid)) { return new GuidConverter(); }
|
||||
|
||||
if (targetType.GetCustomAttribute<TypeConverterAttribute>() is TypeConverterAttribute tca &&
|
||||
Type.GetType(tca.ConverterTypeName, throwOnError: false) is Type converterType &&
|
||||
Activator.CreateInstance(converterType) is TypeConverter converter)
|
||||
{
|
||||
return converter;
|
||||
}
|
||||
|
||||
// now returns a JSON-serializing TypeConverter by default, instead of returning null
|
||||
return new JsonSerializationTypeConverter();
|
||||
}
|
||||
|
||||
private sealed class JsonSerializationTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => true;
|
||||
|
||||
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
|
||||
{
|
||||
return JsonSerializer.Deserialize<object>((string)value);
|
||||
}
|
||||
|
||||
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
|
||||
{
|
||||
return JsonSerializer.Serialize(value);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
_When is serialization/deserialization required?_
|
||||
|
||||
Required
|
||||
|
||||
- **Native to Semantic:** Passing variables from Native to Semantic **will** require serialization of the output of the Native Function from complex type to string so that it can be passed to the LLM.
|
||||
- **Semantic to Native:** Passing variables from Semantic to Native **will** require de-serialization of the output of the Semantic Function between string to the complex type format that the Native Function is expecting.
|
||||
|
||||
Not required
|
||||
|
||||
- **Native to Native:** Passing variables from Native to Native **will not** require any serialization or deserialization as the complex type can be passed as-is.
|
||||
- **Semantic to Semantic:** Passing variables from Semantic to Semantic **will not** require any serialization or deserialization as the the complex type will be passed around using its string representation.
|
||||
|
||||
**2. Only use native serialization methods**
|
||||
This option was originally considered, which would have effectively removed the use of the `TypeConverter`s in favor of a simple `JsonConverter`, but it was pointed out that this may result in lossy conversion between primitive types. For example, when converting from a `float` to an `int`, the primitive may be truncated in a way by the native serialization methods that does not provide an accurate result.
|
||||
|
||||
## Decision Outcome
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: proposed
|
||||
contact: markwallace-microsoft
|
||||
date: 2023-11-21
|
||||
deciders: SergeyMenshykh, markwallace, rbarreto, mabolan, stephentoub
|
||||
consulted:
|
||||
informed:
|
||||
---
|
||||
|
||||
# Semantic Kernel Functions are defined using Interface or Abstract Base Class
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
The Semantic Kernel must define an abstraction to represent a Function i.e. a method that can be called as part of an AI orchestration.
|
||||
Currently this abstraction is the `ISKFunction` interface.
|
||||
The goal of the ADR is decide if this is the best abstraction to use to meet the long term goals of Semantic Kernel.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
- The abstraction **must** extensible so that new functionality can be added later.
|
||||
- Changes to the abstraction **must not** result in breaking changes for consumers.
|
||||
- It is not clear at this time if we need to allow consumers to provide their own `SKFunction` implementations. If we do we this may cause problems as we add new functionality to the Semantic Kernel e.g. what if we define a new hook type?
|
||||
|
||||
## Considered Options
|
||||
|
||||
- `ISKFunction` interface
|
||||
- `SKFunction` base class
|
||||
|
||||
### `ISKFunction` Interface
|
||||
|
||||
- Good, because implementations can extend any arbitrary class
|
||||
- Bad, because we can only change the default behavior of our implementations and customer implementations may become incompatible.
|
||||
- Bad, because we cannot prevent customers for implementing this interface.
|
||||
- Bad, because changes to the interface are breaking changes for consumers.
|
||||
|
||||
### `SKFunction` Case Class
|
||||
|
||||
- Good, because the changes to the interface are **not** breaking changes for consumers.
|
||||
- Good, because class constructor can be made `internal` so we can prevent extensions until we know there are valid use cases.
|
||||
- Good, because we can change the default implementation easily in future.
|
||||
- Bad, because implementations can only extend `SKFunction`.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: "`SKFunction` base class", because we can provide some default implementation and we can restrict creation of new SKFunctions until we better understand those use cases.
|
||||
@@ -0,0 +1,267 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: accepted
|
||||
contact: teresaqhoang
|
||||
date: 2023-12-06
|
||||
deciders: markwallace, alliscode, SergeyMenshykh
|
||||
consulted: markwallace, mabolan
|
||||
informed: stephentoub
|
||||
---
|
||||
|
||||
# Handlebars Prompt Template Helpers
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
We want to use Handlebars as a template factory for rendering prompts and planners in the Semantic Kernel. Handlebars provides a simple and expressive syntax for creating dynamic templates with logic and data. However, Handlebars does not have built-in support for some features and scenarios that are relevant for our use cases, such as:
|
||||
|
||||
- Marking a block of text as a message with a role for chat completion connectors.
|
||||
- Invoking functions from the kernel and passing parameters to them.
|
||||
- Setting and getting variables in the template context.
|
||||
- Performing common operations such as concatenation, arithmetic, comparison, and JSON serialization.
|
||||
- Supporting different output types and formats for the rendered template.
|
||||
|
||||
Therefore, we need to extend Handlebars with custom helpers that can address these gaps and provide a consistent and convenient way for prompt and planner engineers to write templates.
|
||||
|
||||
First, we will do this by **_baking in a defined set of custom system helpers_** for common operations and utilities that are not provided any the built-in Handlebars helpers, which:
|
||||
|
||||
- Allows us full control over what functionality can be executed by the Handlebars template factory.
|
||||
- Enhances the functionality and usability of the template factory, by providing helpers for common operations and utilities that are not provided by any built-in Handlebars helpers but are commonly hallucinated by the model.
|
||||
- Improves the expressiveness and readability of the rendered template, as the helpers can be used to perform simple or complex logic or transformations on the template data / arguments.
|
||||
- Provides flexibility and convenience for the users, as they can:
|
||||
|
||||
- Choose the syntax, and
|
||||
- Extend, add, or omit certain helpers
|
||||
|
||||
to best suits their needs and preferences.
|
||||
|
||||
- Allows for customization of specific operations or utilities that may have different behavior or requirements, such as handling output types, formats, or errors.
|
||||
|
||||
These helpers would handle the evaluation of the arguments, the execution of the operation or utility, and the writing of the result to the template. Examples of such operations are `{{concat string1 string2 ...}}`, `{{equal value1 value2}}`, `{{json object}}`, `{{set name=value}}`, `{{get name}}`, `{{or condition1 condition2}}`, etc.
|
||||
|
||||
Secondly, we have to **_expose the functions that are registered in the Kernel as helpers_** to the Handlebars template factory. Options for this are detailed below.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
- We want to leverage the existing Handlebars helpers, syntax, and mechanisms for loading helpers as much as possible, without introducing unnecessary complexity or inconsistency.
|
||||
- We want to provide helpers that are useful and intuitive for prompt and SK engineers.
|
||||
- We want to ensure that the helpers are well-documented, tested, and maintained, and that they do not conflict with each other or with the built-in Handlebars helpers.
|
||||
- We want to support different output types and formats for the rendered template, such as text, JSON, or complex objects, and allow the template to specify the desired output type.
|
||||
|
||||
## Considered Options
|
||||
|
||||
We considered the following options for extending Handlebars with kernel functions as custom helpers:
|
||||
|
||||
**1. Use a single helper for invoking functions from the kernel.** This option would use a generic helper, such as `{{invoke pluginName-functionName param1=value1 param2=value2 ...}}`, to call any function from the kernel and pass parameters to it. The helper would handle the execution of the function, the conversion of the parameters and the result, and the writing of the result to the template.
|
||||
|
||||
**2. Use a separate helper for each function from the kernel.** This option would register a new helper for each function, such as `{{pluginName-functionName param1=value1 param2=value2 ...}}`, to handle the execution of the function, the conversion of the parameters and the result, and the writing of the result to the template.
|
||||
|
||||
## Pros and Cons
|
||||
|
||||
### 1. Use a single generic helper for invoking functions from the kernel
|
||||
|
||||
Pros:
|
||||
|
||||
- Simplifies the registration and maintenance of the helper, as only one helper, `invoke`, needs to be defined and updated.
|
||||
- Provides a consistent and uniform syntax for calling any function from the kernel, regardless of the plugin or function name, parameter details, or the result.
|
||||
- Allows for customization and special logic of kernel functions, such as handling output types, execution restrictions, or errors.
|
||||
- Allows the use of positional or named arguments, as well as hash arguments, for passing parameters to the function.
|
||||
|
||||
Cons:
|
||||
|
||||
- Reduces the expressiveness and readability of the template, as the function name and parameters are wrapped in a generic helper invocation.
|
||||
- Adds additional syntax for the model to learn and keep track of, potentially leading to more errors during render.
|
||||
|
||||
### 2. Use a generic helper for _each_ function from the kernel
|
||||
|
||||
Pros:
|
||||
|
||||
- Has all the benefits of option 1, but largely improves the expressiveness and readability of the template, as the function name and parameters are directly written in the template.
|
||||
- Maintains ease of maintenance for handling each function, as each helper will follow the same templated logic for registration and execution.
|
||||
|
||||
Cons:
|
||||
|
||||
- May cause conflicts or confusion with the built-in Handlebars helpers or the kernel variables, if the function name or the parameter name matches them.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
We decided to go with option 2: providing special helpers to invoke any function in the kernel. These helpers will follow the same logic and syntax for each registered function. We believe that this approach, alongside the custom system helpers that will enable special utility logic or behavior, provides the best balance between simplicity, expressiveness, flexibility, and functionality for the Handlebars template factory and our users.
|
||||
|
||||
With this approach,
|
||||
|
||||
- We will allow customers to use any of the built-in [Handlebars.Net helpers](https://github.com/Handlebars-Net/Handlebars.Net.Helpers).
|
||||
- We will provide utility helpers, which are registered by default.
|
||||
- We will provide prompt helpers (e.g. chat message), which are registered by default.
|
||||
- We will register all plugin functions registered on the `Kernel`.
|
||||
- We will allow customers to control which plugins are registered as helpers and the syntax of helpers' signatures.
|
||||
- By default, we will honor all options defined in [HandlebarsHelperOptions](https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/8f7c9c082e18845f6a620bbe34bf4607dcba405b/src/Handlebars.Net.Helpers/Options/HandlebarsHelpersOptions.cs#L12).
|
||||
- Additionally, we will extend this configuration to include a `RegisterCustomHelpersCallback` option that users can set to register custom helpers.
|
||||
- We will allow Kernel function arguments to be easily accessed, i.e., function variables and execution settings, via a `KernelArguments` object.
|
||||
- We will allow customers to control when plugin functions are registered as helpers.
|
||||
- By default, this is done when template is rendered.
|
||||
- Optionally, this can be done when the Handlebars template factory is constructed by passing in a Plugin collection.
|
||||
- If conflicts arise between built-in helpers, variables, or kernel objects:
|
||||
- We will throw an error clearly explaining what the issue is, as well as
|
||||
- Allow customers to provide their own implementations and overrides, including an option to not register default helpers. This can be done by setting `Options.Categories` to an empty array `[]`.
|
||||
|
||||
We also decided to follow some guidelines and best practices for designing and implementing the helpers, such as:
|
||||
|
||||
- Documenting the purpose, syntax, parameters, and behavior of each helper, and providing examples and tests for them.
|
||||
- Naming the helpers in a clear and consistent way, and avoiding conflicts or confusion with the built-in Handlebars helpers or the kernel functions or variables.
|
||||
- Using standalone function names for custom system helpers (i.e., json, set)
|
||||
- Using the delimiter "`-`" for helpers registered to handle the kernel functions, to distinguish them from each other and from our system or built-in Handlebars helpers.
|
||||
- Supporting both positional and hash arguments, for passing parameters to the helpers, and validating the arguments for the required type and count.
|
||||
- Handling the output types, formats, and errors of the helpers, including complex types or JSON schemas.
|
||||
- Implementing the helpers in a performant and secure way, and avoiding any side effects or unwanted modifications to the template context or data.
|
||||
|
||||
Effectively, there will be four buckets of helpers enabled in the Handlebars Template Engine:
|
||||
|
||||
1. Default helpers from the Handlebars library, including:
|
||||
- [Built-in helpers](https://handlebarsjs.com/guide/builtin-helpers.html) that enable loops and conditions (#if, #each, #with, #unless)
|
||||
- [Handlebars.Net.Helpers](https://github.com/Handlebars-Net/Handlebars.Net.Helpers/wiki)
|
||||
2. Functions in the kernel
|
||||
3. Helpers helpful to prompt engineers (i.e., message, or)
|
||||
4. Utility helpers that can be used to perform simple logic or transformations on the template data or arguments (i.e., set, get, json, concat, equals, range, array)
|
||||
|
||||
### Pseudocode for the Handlebars Prompt Template Engine
|
||||
|
||||
A prototype implementation of a Handlebars prompt template factory with built-in helpers could look something like this:
|
||||
|
||||
```csharp
|
||||
/// Options for Handlebars helpers (built-in and custom).
|
||||
public sealed class HandlebarsPromptTemplateOptions : HandlebarsHelpersOptions
|
||||
{
|
||||
// Categories tracking built-in system helpers
|
||||
public enum KernelHelperCategories
|
||||
{
|
||||
Prompt,
|
||||
Plugin,
|
||||
Context,
|
||||
String,
|
||||
...
|
||||
}
|
||||
|
||||
/// Default character to use for delimiting plugin name and function name in a Handlebars template.
|
||||
public string DefaultNameDelimiter { get; set; } = "-";
|
||||
|
||||
/// Delegate for registering custom helpers.
|
||||
public delegate void RegisterCustomHelpersCallback(IHandlebars handlebarsInstance, KernelArguments executionContext);
|
||||
|
||||
/// Callback for registering custom helpers.
|
||||
public RegisterCustomHelpersCallback? RegisterCustomHelpers { get; set; } = null;
|
||||
|
||||
// Pseudocode, some combination of both KernelHelperCategories and the default HandlebarsHelpersOptions.Categories.
|
||||
public List<Enum> AllCategories = KernelHelperCategories.AddRange(Categories);
|
||||
}
|
||||
```
|
||||
|
||||
```csharp
|
||||
// Handlebars Prompt Template
|
||||
internal class HandlebarsPromptTemplate : IPromptTemplate
|
||||
{
|
||||
public async Task<string> RenderAsync(Kernel kernel, KernelArguments arguments, CancellationToken cancellationToken = default)
|
||||
{
|
||||
arguments ??= new();
|
||||
var handlebarsInstance = HandlebarsDotNet.Handlebars.Create();
|
||||
|
||||
// Add helpers for kernel functions
|
||||
KernelFunctionHelpers.Register(handlebarsInstance, kernel, arguments, this._options.PrefixSeparator, cancellationToken);
|
||||
|
||||
// Add built-in system helpers
|
||||
KernelSystemHelpers.Register(handlebarsInstance, arguments, this._options);
|
||||
|
||||
// Register any custom helpers
|
||||
if (this._options.RegisterCustomHelpers is not null)
|
||||
{
|
||||
this._options.RegisterCustomHelpers(handlebarsInstance, arguments);
|
||||
}
|
||||
...
|
||||
|
||||
return await Task.FromResult(prompt).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```csharp
|
||||
/// Extension class to register Kernel functions as helpers.
|
||||
public static class KernelFunctionHelpers
|
||||
{
|
||||
public static void Register(
|
||||
IHandlebars handlebarsInstance,
|
||||
Kernel kernel,
|
||||
KernelArguments executionContext,
|
||||
string nameDelimiter,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
kernel.Plugins.GetFunctionsMetadata().ToList()
|
||||
.ForEach(function =>
|
||||
RegisterFunctionAsHelper(kernel, executionContext, handlebarsInstance, function, nameDelimiter, cancellationToken)
|
||||
);
|
||||
}
|
||||
|
||||
private static void RegisterFunctionAsHelper(
|
||||
Kernel kernel,
|
||||
KernelArguments executionContext,
|
||||
IHandlebars handlebarsInstance,
|
||||
KernelFunctionMetadata functionMetadata,
|
||||
string nameDelimiter,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Register helper for each function
|
||||
handlebarsInstance.RegisterHelper(fullyResolvedFunctionName, (in HelperOptions options, in Context context, in Arguments handlebarsArguments) =>
|
||||
{
|
||||
// Get parameters from template arguments; check for required parameters + type match
|
||||
|
||||
// If HashParameterDictionary
|
||||
ProcessHashArguments(functionMetadata, executionContext, handlebarsArguments[0] as IDictionary<string, object>, nameDelimiter);
|
||||
|
||||
// Else
|
||||
ProcessPositionalArguments(functionMetadata, executionContext, handlebarsArguments);
|
||||
|
||||
KernelFunction function = kernel.Plugins.GetFunction(functionMetadata.PluginName, functionMetadata.Name);
|
||||
|
||||
InvokeSKFunction(kernel, function, GetKernelArguments(executionContext), cancellationToken);
|
||||
});
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```csharp
|
||||
/// Extension class to register additional helpers as Kernel System helpers.
|
||||
public static class KernelSystemHelpers
|
||||
{
|
||||
public static void Register(IHandlebars handlebarsInstance, KernelArguments arguments, HandlebarsPromptTemplateOptions options)
|
||||
{
|
||||
RegisterHandlebarsDotNetHelpers(handlebarsInstance, options);
|
||||
RegisterSystemHelpers(handlebarsInstance, arguments, options);
|
||||
}
|
||||
|
||||
// Registering all helpers provided by https://github.com/Handlebars-Net/Handlebars.Net.Helpers.
|
||||
private static void RegisterHandlebarsDotNetHelpers(IHandlebars handlebarsInstance, HandlebarsPromptTemplateOptions helperOptions)
|
||||
{
|
||||
HandlebarsHelpers.Register(handlebarsInstance, optionsCallback: options =>
|
||||
{
|
||||
...helperOptions
|
||||
});
|
||||
}
|
||||
|
||||
// Registering all helpers built by the SK team to support the kernel.
|
||||
private static void RegisterSystemHelpers(
|
||||
IHandlebars handlebarsInstance, KernelArguments arguments, HandlebarsPromptTemplateOptions helperOptions)
|
||||
{
|
||||
// Where each built-in helper will have its own defined class, following the same pattern that is used by Handlebars.Net.Helpers.
|
||||
// https://github.com/Handlebars-Net/Handlebars.Net.Helpers
|
||||
if (helperOptions.AllCategories contains helperCategory)
|
||||
...
|
||||
KernelPromptHelpers.Register(handlebarsContext);
|
||||
KernelPluginHelpers.Register(handlebarsContext);
|
||||
KernelStringHelpers..Register(handlebarsContext);
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note: This is just a prototype implementation for illustration purposes only.**
|
||||
|
||||
Handlebars supports different object types as variables on render. This opens up the option to use objects outright rather than just strings in semantic functions, i.e., loop over arrays or access properties of complex objects, without serializing or deserializing objects before invocation.
|
||||
@@ -0,0 +1,350 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: proposed
|
||||
date: 2023-11-13
|
||||
deciders: rogerbarreto,markwallace-microsoft,SergeyMenshykh,dmytrostruk
|
||||
consulted:
|
||||
informed:
|
||||
---
|
||||
|
||||
# Streaming Capability for Kernel and Functions usage - Phase 1
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
It is quite common in co-pilot implementations to have a streamlined output of messages from the LLM (large language models)M and currently that is not possible while using ISKFunctions.InvokeAsync or Kernel.RunAsync methods, which enforces users to work around the Kernel and Functions to use `ITextCompletion` and `IChatCompletion` services directly as the only interfaces that currently support streaming.
|
||||
|
||||
Currently streaming is a capability that not all providers do support and this as part of our design we try to ensure the services will have the proper abstractions to support streaming not only of text but be open to other types of data like images, audio, video, etc.
|
||||
|
||||
Needs to be clear for the sk developer when he is attempting to get streaming data.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
1. The sk developer should be able to get streaming data from the Kernel and Functions using Kernel.RunAsync or ISKFunctions.InvokeAsync methods
|
||||
|
||||
2. The sk developer should be able to get the data in a generic way, so the Kernel and Functions can be able to stream data of any type, not limited to text.
|
||||
|
||||
3. The sk developer when using streaming from a model that does not support streaming should still be able to use it with only one streaming update representing the whole data.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Streaming with plans will not be supported in this phase. Attempting to do so will throw an exception.
|
||||
- Kernel streaming will not support multiple functions (pipeline).
|
||||
- Input streaming will not be supported in this phase.
|
||||
- Post Hook Skipping, Repeat and Cancelling of streaming functions are not supported.
|
||||
|
||||
## Considered Options
|
||||
|
||||
### Option 1 - Dedicated Streaming Interfaces
|
||||
|
||||
Using dedicated streaming interfaces that allow the sk developer to get the streaming data in a generic way, including string, byte array directly from the connector as well as allowing the Kernel and Functions implementations to be able to stream data of any type, not limited to text.
|
||||
|
||||
This approach also exposes dedicated interfaces in the kernel and functions to use streaming making it clear to the sk developer what is the type of data being returned in IAsyncEnumerable format.
|
||||
|
||||
`ITextCompletion` and `IChatCompletion` will have new APIs to get `byte[]` and `string` streaming data directly as well as the specialized `StreamingContent` return.
|
||||
|
||||
The sk developer will be able to specify a generic type to the `Kernel.RunStreamingAsync<T>()` and `ISKFunction.InvokeStreamingAsync<T>` to get the streaming data. If the type is not specified, the Kernel and Functions will return the data as StreamingContent.
|
||||
|
||||
If the type is not specified or if the string representation cannot be cast, an exception will be thrown.
|
||||
|
||||
If the type specified is `StreamingContent` or another any type supported by the connector no error will be thrown.
|
||||
|
||||
## User Experience Goal
|
||||
|
||||
```csharp
|
||||
//(providing the type at as generic parameter)
|
||||
|
||||
// Getting a Raw Streaming data from Kernel
|
||||
await foreach(string update in kernel.RunStreamingAsync<byte[]>(function, variables))
|
||||
|
||||
// Getting a String as Streaming data from Kernel
|
||||
await foreach(string update in kernel.RunStreamingAsync<string>(function, variables))
|
||||
|
||||
// Getting a StreamingContent as Streaming data from Kernel
|
||||
await foreach(StreamingContent update in kernel.RunStreamingAsync<StreamingContent>(variables, function))
|
||||
// OR
|
||||
await foreach(StreamingContent update in kernel.RunStreamingAsync(function, variables)) // defaults to Generic above)
|
||||
{
|
||||
Console.WriteLine(update);
|
||||
}
|
||||
```
|
||||
|
||||
Abstraction class for any stream content, connectors will be responsible to provide the specialized type of `StreamingContent` which will contain the data as well as any metadata related to the streaming result.
|
||||
|
||||
```csharp
|
||||
|
||||
public abstract class StreamingContent
|
||||
{
|
||||
public abstract int ChoiceIndex { get; }
|
||||
|
||||
/// Returns a string representation of the chunk content
|
||||
public abstract override string ToString();
|
||||
|
||||
/// Abstract byte[] representation of the chunk content in a way it could be composed/appended with previous chunk contents.
|
||||
/// Depending on the nature of the underlying type, this method may be more efficient than <see cref="ToString"/>.
|
||||
public abstract byte[] ToByteArray();
|
||||
|
||||
/// Internal chunk content object reference. (Breaking glass).
|
||||
/// Each connector will have its own internal object representing the content chunk content.
|
||||
/// The usage of this property is considered "unsafe". Use it only if strictly necessary.
|
||||
public object? InnerContent { get; }
|
||||
|
||||
/// The metadata associated with the content.
|
||||
public Dictionary<string, object>? Metadata { get; set; }
|
||||
|
||||
/// The current context associated the function call.
|
||||
internal SKContext? Context { get; set; }
|
||||
|
||||
/// <param name="innerContent">Inner content object reference</param>
|
||||
protected StreamingContent(object? innerContent)
|
||||
{
|
||||
this.InnerContent = innerContent;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Specialization example of a StreamingChatContent
|
||||
|
||||
```csharp
|
||||
//
|
||||
public class StreamingChatContent : StreamingContent
|
||||
{
|
||||
public override int ChoiceIndex { get; }
|
||||
public FunctionCall? FunctionCall { get; }
|
||||
public string? Content { get; }
|
||||
public AuthorRole? Role { get; }
|
||||
public string? Name { get; }
|
||||
|
||||
public StreamingChatContent(AzureOpenAIChatMessage chatMessage, int resultIndex) : base(chatMessage)
|
||||
{
|
||||
this.ChoiceIndex = resultIndex;
|
||||
this.FunctionCall = chatMessage.InnerChatMessage?.FunctionCall;
|
||||
this.Content = chatMessage.Content;
|
||||
this.Role = new AuthorRole(chatMessage.Role.ToString());
|
||||
this.Name = chatMessage.InnerChatMessage?.Name;
|
||||
}
|
||||
|
||||
public override byte[] ToByteArray() => Encoding.UTF8.GetBytes(this.ToString());
|
||||
public override string ToString() => this.Content ?? string.Empty;
|
||||
}
|
||||
```
|
||||
|
||||
`IChatCompletion` and `ITextCompletion` interfaces will have new APIs to get a generic streaming content data.
|
||||
|
||||
```csharp
|
||||
interface ITextCompletion + IChatCompletion
|
||||
{
|
||||
IAsyncEnumerable<T> GetStreamingContentAsync<T>(...);
|
||||
|
||||
// Throw exception if T is not supported
|
||||
}
|
||||
|
||||
interface IKernel
|
||||
{
|
||||
// Get streaming function content of T
|
||||
IAsyncEnumerable<T> RunStreamingAsync<T>(ContextVariables variables, ISKFunction function);
|
||||
}
|
||||
|
||||
interface ISKFunction
|
||||
{
|
||||
// Get streaming function content of T
|
||||
IAsyncEnumerable<T> InvokeStreamingAsync<T>(SKContext context);
|
||||
}
|
||||
```
|
||||
|
||||
## Prompt/Semantic Functions Behavior
|
||||
|
||||
When Prompt Functions are invoked using the Streaming API, they will attempt to use the Connectors streaming implementation.
|
||||
The connector will be responsible to provide the specialized type of `StreamingContent` and even if the underlying backend API don't support streaming the output will be one streamingcontent with the whole data.
|
||||
|
||||
## Method/Native Functions Behavior
|
||||
|
||||
Method Functions will support `StreamingContent` automatically with as a `StreamingMethodContent` wrapping the object returned in the iterator.
|
||||
|
||||
```csharp
|
||||
public sealed class StreamingMethodContent : StreamingContent
|
||||
{
|
||||
public override int ChoiceIndex => 0;
|
||||
|
||||
/// Method object value that represents the content chunk
|
||||
public object Value { get; }
|
||||
|
||||
/// Default implementation
|
||||
public override byte[] ToByteArray()
|
||||
{
|
||||
if (this.Value is byte[])
|
||||
{
|
||||
// If the method value is byte[] we return it directly
|
||||
return (byte[])this.Value;
|
||||
}
|
||||
|
||||
// By default if a native value is not byte[] we output the UTF8 string representation of the value
|
||||
return Encoding.UTF8.GetBytes(this.Value?.ToString());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Value.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StreamingMethodContent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="innerContent">Underlying object that represents the chunk</param>
|
||||
public StreamingMethodContent(object innerContent) : base(innerContent)
|
||||
{
|
||||
this.Value = innerContent;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If a MethodFunction is returning an `IAsyncEnumerable` each enumerable result will be automatically wrapped in the `StreamingMethodContent` keeping the streaming behavior and the overall abstraction consistent.
|
||||
|
||||
When a MethodFunction is not an `IAsyncEnumerable`, the complete result will be wrapped in a `StreamingMethodContent` and will be returned as a single item.
|
||||
|
||||
## Pros
|
||||
|
||||
1. All the User Experience Goal section options will be possible.
|
||||
2. Kernel and Functions implementations will be able to stream data of any type, not limited to text
|
||||
3. The sk developer will be able to provide the streaming content type it expects from the `GetStreamingContentAsync<T>` method.
|
||||
4. Sk developer will be able to get streaming from the Kernel, Functions and Connectors with the same result type.
|
||||
|
||||
## Cons
|
||||
|
||||
1. If the sk developer wants to use the specialized type of `StreamingContent` he will need to know what the connector is being used to use the correct **StreamingContent extension method** or to provide directly type in `<T>`.
|
||||
2. Connectors will have greater responsibility to support the correct special types of `StreamingContent`.
|
||||
|
||||
### Option 2 - Dedicated Streaming Interfaces (Returning a Class)
|
||||
|
||||
All changes from option 1 with the small difference below:
|
||||
|
||||
- The Kernel and SKFunction streaming APIs interfaces will return `StreamingFunctionResult<T>` which also implements `IAsyncEnumerable<T>`
|
||||
- Connectors streaming APIs interfaces will return `StreamingConnectorContent<T>` which also implements `IAsyncEnumerable<T>`
|
||||
|
||||
The `StreamingConnectorContent` class is needed for connectors as one way to pass any information relative to the request and not the chunk that can be used by the functions to fill `StreamingFunctionResult` metadata.
|
||||
|
||||
## User Experience Goal
|
||||
|
||||
Option 2 Biggest benefit:
|
||||
|
||||
```csharp
|
||||
// When the caller needs to know more about the streaming he can get the result reference before starting the streaming.
|
||||
var streamingResult = await kernel.RunStreamingAsync(function);
|
||||
// Do something with streamingResult properties
|
||||
|
||||
// Consuming the streamingResult requires an extra await:
|
||||
await foreach(StreamingContent chunk content in await streamingResult)
|
||||
```
|
||||
|
||||
Using the other operations will be quite similar (only needing an extra `await` to get the iterator)
|
||||
|
||||
```csharp
|
||||
// Getting a Raw Streaming data from Kernel
|
||||
await foreach(string update in await kernel.RunStreamingAsync<byte[]>(function, variables))
|
||||
|
||||
// Getting a String as Streaming data from Kernel
|
||||
await foreach(string update in await kernel.RunStreamingAsync<string>(function, variables))
|
||||
|
||||
// Getting a StreamingContent as Streaming data from Kernel
|
||||
await foreach(StreamingContent update in await kernel.RunStreamingAsync<StreamingContent>(variables, function))
|
||||
// OR
|
||||
await foreach(StreamingContent update in await kernel.RunStreamingAsync(function, variables)) // defaults to Generic above)
|
||||
{
|
||||
Console.WriteLine(update);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
StreamingConnectorResult is a class that can store information regarding the result before the stream is consumed as well as any underlying object (breaking glass) that the stream consumes at the connector level.
|
||||
|
||||
```csharp
|
||||
|
||||
public sealed class StreamingConnectorResult<T> : IAsyncEnumerable<T>
|
||||
{
|
||||
private readonly IAsyncEnumerable<T> _StreamingContentource;
|
||||
|
||||
public object? InnerResult { get; private set; } = null;
|
||||
|
||||
public StreamingConnectorResult(Func<IAsyncEnumerable<T>> streamingReference, object? innerConnectorResult)
|
||||
{
|
||||
this._StreamingContentource = streamingReference.Invoke();
|
||||
this.InnerResult = innerConnectorResult;
|
||||
}
|
||||
}
|
||||
|
||||
interface ITextCompletion + IChatCompletion
|
||||
{
|
||||
Task<StreamingConnectorResult<T>> GetStreamingContentAsync<T>();
|
||||
// Throw exception if T is not supported
|
||||
// Initially connectors
|
||||
}
|
||||
```
|
||||
|
||||
StreamingFunctionResult is a class that can store information regarding the result before the stream is consumed as well as any underlying object (breaking glass) that the stream consumes from Kernel and SKFunctions.
|
||||
|
||||
```csharp
|
||||
public sealed class StreamingFunctionResult<T> : IAsyncEnumerable<T>
|
||||
{
|
||||
internal Dictionary<string, object>? _metadata;
|
||||
private readonly IAsyncEnumerable<T> _streamingResult;
|
||||
|
||||
public string FunctionName { get; internal set; }
|
||||
public Dictionary<string, object> Metadata { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal object reference. (Breaking glass).
|
||||
/// Each connector will have its own internal object representing the result.
|
||||
/// </summary>
|
||||
public object? InnerResult { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Instance of <see cref="SKContext"/> used by the function.
|
||||
/// </summary>
|
||||
internal SKContext Context { get; private set; }
|
||||
|
||||
public StreamingFunctionResult(string functionName, SKContext context, Func<IAsyncEnumerable<T>> streamingResult, object? innerFunctionResult)
|
||||
{
|
||||
this.FunctionName = functionName;
|
||||
this.Context = context;
|
||||
this._streamingResult = streamingResult.Invoke();
|
||||
this.InnerResult = innerFunctionResult;
|
||||
}
|
||||
}
|
||||
|
||||
interface ISKFunction
|
||||
{
|
||||
// Extension generic method to get from type <T>
|
||||
Task<StreamingFunctionResult<T>> InvokeStreamingAsync<T>(...);
|
||||
}
|
||||
|
||||
static class KernelExtensions
|
||||
{
|
||||
public static async Task<StreamingFunctionResult<T>> RunStreamingAsync<T>(this Kernel kernel, ISKFunction skFunction, ContextVariables? variables, CancellationToken cancellationToken)
|
||||
{
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Pros
|
||||
|
||||
1. All benefits from Option 1 +
|
||||
2. Having StreamingFunctionResults allow sk developer to know more details about the result before consuming the stream, like:
|
||||
- Any metadata provided by the underlying API,
|
||||
- SKContext
|
||||
- Function Name and Details
|
||||
3. Experience using the Streaming is quite similar (need an extra await to get the result) to option 1
|
||||
4. APIs behave similarly to the non-streaming API (returning a result representation to get the value)
|
||||
|
||||
## Cons
|
||||
|
||||
1. All cons from Option 1 +
|
||||
2. Added complexity as the IAsyncEnumerable cannot be passed directly in the method result demanding a delegate approach to be adapted inside of the Results that implements the IAsyncEnumerator.
|
||||
3. Added complexity where IDisposable is needed to be implemented in the Results to dispose the response object and the caller would need to handle the disposal of the result.
|
||||
4. As soon the caller gets a `StreamingFunctionResult` a network connection will be kept open until the caller implementation consume it (Enumerate over the `IAsyncEnumerable`).
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Option 1 was chosen as the best option as small benefit of the Option 2 don't justify the complexity involved described in the Cons.
|
||||
|
||||
Was also decided that the Metadata related to a connector backend response can be added to the `StreamingContent.Metadata` property. This will allow the sk developer to get the metadata even without a `StreamingConnectorResult` or `StreamingFunctionResult`.
|
||||
@@ -0,0 +1,236 @@
|
||||
## Proposal
|
||||
|
||||
### IChatCompletion
|
||||
|
||||
Before:
|
||||
|
||||
```csharp
|
||||
public interface IChatCompletion : IAIService
|
||||
{
|
||||
ChatHistory CreateNewChat(string? instructions = null);
|
||||
|
||||
Task<IReadOnlyList<IChatResult>> GetChatCompletionsAsync(ChatHistory chat, ...);
|
||||
|
||||
Task<IReadOnlyList<IChatResult>> GetChatCompletionsAsync(string prompt, ...);
|
||||
|
||||
IAsyncEnumerable<T> GetStreamingContentAsync<T>(ChatHistory chatHistory, ...);
|
||||
}
|
||||
|
||||
public static class ChatCompletionExtensions
|
||||
{
|
||||
public static async Task<string> GenerateMessageAsync(ChatHistory chat, ...);
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```csharp
|
||||
public interface IChatCompletion : IAIService
|
||||
{
|
||||
Task<IReadOnlyList<ChatContent>> GetChatContentsAsync(ChatHistory chat, ..> tags)
|
||||
|
||||
IAsyncEnumerable<StreamingChatContent> GetStreamingChatContentsAsync(ChatHistory chatHistory, ...);
|
||||
}
|
||||
|
||||
public static class ChatCompletionExtensions
|
||||
{
|
||||
// v Single vv Standardized Prompt (Parse <message> tags)
|
||||
public static async Task<ChatContent> GetChatContentAsync(string prompt, ...);
|
||||
|
||||
// v Single
|
||||
public static async Task<ChatContent> GetChatContentAsync(ChatHistory chatHistory, ...);
|
||||
|
||||
public static IAsyncEnumerable<StreamingChatContent> GetStreamingChatContentsAsync(string prompt, ...);
|
||||
}
|
||||
```
|
||||
|
||||
### ITextCompletion
|
||||
|
||||
Before:
|
||||
|
||||
```csharp
|
||||
public interface ITextCompletion : IAIService
|
||||
{
|
||||
Task<IReadOnlyList<ITextResult>> GetCompletionsAsync(string prompt, ...);
|
||||
|
||||
IAsyncEnumerable<T> GetStreamingContentAsync<T>(string prompt, ...);
|
||||
}
|
||||
|
||||
public static class TextCompletionExtensions
|
||||
{
|
||||
public static async Task<string> CompleteAsync(string text, ...);
|
||||
|
||||
public static IAsyncEnumerable<StreamingContent> GetStreamingContentAsync(string input, ...);
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```csharp
|
||||
public interface ITextCompletion : IAIService
|
||||
{
|
||||
Task<IReadOnlyList<TextContent>> GetTextContentsAsync(string prompt, ...);
|
||||
|
||||
IAsyncEnumerable<StreamingTextContent> GetStreamingTextContentsAsync(string prompt, ...);
|
||||
}
|
||||
|
||||
public static class TextCompletionExtensions
|
||||
{
|
||||
public static async Task<TextContent> GetTextContentAsync(string prompt, ...);
|
||||
}
|
||||
```
|
||||
|
||||
## Content Abstractions
|
||||
|
||||
### Model Comparisons
|
||||
|
||||
#### Current Streaming Abstractions
|
||||
|
||||
| Streaming (Current) | Specialized\* Streaming (Current) |
|
||||
| ------------------------------------------- | --------------------------------------------------------------- |
|
||||
| `StreamingChatContent` : `StreamingContent` | `OpenAIStreamingChatContent` |
|
||||
| `StreamingTextContent` : `StreamingContent` | `OpenAIStreamingTextContent`, `HuggingFaceStreamingTextContent` |
|
||||
|
||||
#### Non-Streaming Abstractions (Before and After)
|
||||
|
||||
| Non-Streaming (Before) | Non-Streaming (After) | Specialized\* Non-Streaming (After) |
|
||||
| ----------------------------- | ------------------------------ | --------------------------------------------- |
|
||||
| `IChatResult` : `IResultBase` | `ChatContent` : `ModelContent` | `OpenAIChatContent` |
|
||||
| `ITextResult` : `IResultBase` | `TextContent` : `ModelContent` | `OpenAITextContent`, `HuggingFaceTextContent` |
|
||||
| `ChatMessage` | `ChatContent` : `ModelContent` | `OpenAIChatContent` |
|
||||
|
||||
_\*Specialized: Connector implementations that are specific to a single AI Service._
|
||||
|
||||
### New Non-Streaming Abstractions:
|
||||
|
||||
`ModelContent` was chosen to represent a `non-streaming content` top-most abstraction which can be specialized and contains all the information that the AI Service returned. (Metadata, Raw Content, etc.)
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// Base class for all AI non-streaming results
|
||||
/// </summary>
|
||||
public abstract class ModelContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Raw content object reference. (Breaking glass).
|
||||
/// </summary>
|
||||
public object? InnerContent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The metadata associated with the content.
|
||||
/// ⚠️ (Token Usage + More Backend API Metadata) info will be in this dictionary. Old IResult.ModelResult) ⚠️
|
||||
/// </summary>
|
||||
public Dictionary<string, object?>? Metadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CompleteContent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="rawContent">Raw content object reference</param>
|
||||
/// <param name="metadata">Metadata associated with the content</param>
|
||||
protected CompleteContent(object rawContent, Dictionary<string, object>? metadata = null)
|
||||
{
|
||||
this.InnerContent = rawContent;
|
||||
this.Metadata = metadata;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// Chat content abstraction
|
||||
/// </summary>
|
||||
public class ChatContent : ModelContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Role of the author of the message
|
||||
/// </summary>
|
||||
public AuthorRole Role { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content of the message
|
||||
/// </summary>
|
||||
public string Content { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ChatContent"/> class
|
||||
/// </summary>
|
||||
/// <param name="chatMessage"></param>
|
||||
/// <param name="metadata">Dictionary for any additional metadata</param>
|
||||
public ChatContent(ChatMessage chatMessage, Dictionary<string, object>? metadata = null) : base(chatMessage, metadata)
|
||||
{
|
||||
this.Role = chatMessage.Role;
|
||||
this.Content = chatMessage.Content;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// Represents a text content result.
|
||||
/// </summary>
|
||||
public class TextContent : ModelContent
|
||||
{
|
||||
/// <summary>
|
||||
/// The text content.
|
||||
/// </summary>
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TextContent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">Text content</param>
|
||||
/// <param name="metadata">Additional metadata</param>
|
||||
public TextContent(string text, Dictionary<string, object>? metadata = null) : base(text, metadata)
|
||||
{
|
||||
this.Text = text;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### End-User Experience
|
||||
|
||||
- No changes to the end-user experience when using `Function.InvokeAsync` or `Kernel.InvokeAsync`
|
||||
- Changes only when using Connector APIs directly
|
||||
|
||||
#### Example 16 - Custom LLMS
|
||||
|
||||
Before
|
||||
|
||||
```csharp
|
||||
await foreach (var message in textCompletion.GetStreamingContentAsync(prompt, executionSettings))
|
||||
{
|
||||
Console.Write(message);
|
||||
}
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```csharp
|
||||
await foreach (var message in textCompletion.GetStreamingTextContentAsync(prompt, executionSettings))
|
||||
{
|
||||
Console.Write(message);
|
||||
}
|
||||
```
|
||||
|
||||
#### Example 17 - ChatGPT
|
||||
|
||||
Before
|
||||
|
||||
```csharp
|
||||
string reply = await chatGPT.GenerateMessageAsync(chatHistory);
|
||||
chatHistory.AddAssistantMessage(reply);
|
||||
```
|
||||
|
||||
After
|
||||
|
||||
```csharp
|
||||
var reply = await chatGPT.GetChatContentAsync(chatHistory);
|
||||
chatHistory.AddMessage(reply);
|
||||
|
||||
// OR
|
||||
chatHistory.AddAssistantMessage(reply.Content);
|
||||
```
|
||||
|
||||
### Clean-up
|
||||
|
||||
All old interfaces and classes will be removed in favor of the new ones.
|
||||
@@ -0,0 +1,305 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: accepted
|
||||
contact: dmytrostruk
|
||||
date: 2023-12-08
|
||||
deciders: SergeyMenshykh, markwallace, rbarreto, mabolan, stephentoub, dmytrostruk
|
||||
consulted:
|
||||
informed:
|
||||
---
|
||||
# Chat Models
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
In latest OpenAI API, `content` property of `chat message` object can accept two types of values `string` or `array` ([Documentation](https://platform.openai.com/docs/api-reference/chat/create)).
|
||||
|
||||
We should update current implementation of `ChatMessageContent` class with `string Content` property to support this API.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
1. New design should not be coupled to OpenAI API and should work for other AI providers.
|
||||
2. Naming of classes and properties should be consistent and intuitive.
|
||||
|
||||
## Considered Options
|
||||
|
||||
Some of the option variations can be combined.
|
||||
|
||||
### Option #1: Naming updates and new data type for `chat message content`
|
||||
|
||||
Since `chat message content` can be an object now instead of `string`, it requires reserved name for better understanding in domain.
|
||||
|
||||
1. `ChatMessageContent` will be renamed to `ChatMessage`. (Same for `StreamingChatMessageContent`).
|
||||
2. `GetChatMessageContent` methods will be renamed to `GetChatMessage`.
|
||||
3. New abstract class `ChatMessageContent` that will have property `ChatMessageContentType Type` with values `text`, `image`. (Will be extended with `audio`, `video` in the future).
|
||||
4. `ChatMessage` will contain collection of `ChatMessageContent` objects `IList<ChatMessageContent> Contents`.
|
||||
5. There will be concrete implementations of `ChatMessageContent` - `ChatMessageTextContent` and `ChatMessageImageContent`.
|
||||
|
||||
New _ChatMessageContentType.cs_
|
||||
|
||||
```csharp
|
||||
public readonly struct ChatMessageContentType : IEquatable<ChatMessageContentType>
|
||||
{
|
||||
public static ChatMessageContentType Text { get; } = new("text");
|
||||
|
||||
public static ChatMessageContentType Image { get; } = new("image");
|
||||
|
||||
public string Label { get; }
|
||||
|
||||
// Implementation of `IEquatable`...
|
||||
}
|
||||
```
|
||||
|
||||
New _ChatMessageContent.cs_
|
||||
|
||||
```csharp
|
||||
public abstract class ChatMessageContent
|
||||
{
|
||||
public ChatMessageContentType Type { get; set; }
|
||||
|
||||
public ChatMessageContent(ChatMessageContentType type)
|
||||
{
|
||||
this.Type = type;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Updated _ChatMessage.cs_:
|
||||
|
||||
```csharp
|
||||
public class ChatMessage : ContentBase
|
||||
{
|
||||
public AuthorRole Role { get; set; }
|
||||
|
||||
public IList<ChatMessageContent> Contents { get; set; }
|
||||
```
|
||||
|
||||
New _ChatMessageTextContent.cs_
|
||||
|
||||
```csharp
|
||||
public class ChatMessageTextContent : ChatMessageContent
|
||||
{
|
||||
public string Text { get; set; }
|
||||
|
||||
public ChatMessageTextContent(string text) : base(ChatMessageContentType.Text)
|
||||
{
|
||||
this.Text = text;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
New _ChatMessageImageContent.cs_
|
||||
|
||||
```csharp
|
||||
public class ChatMessageImageContent : ChatMessageContent
|
||||
{
|
||||
public Uri Uri { get; set; }
|
||||
|
||||
public ChatMessageImageContent(Uri uri) : base(ChatMessageContentType.Image)
|
||||
{
|
||||
this.Uri = uri;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```csharp
|
||||
var chatHistory = new ChatHistory("You are friendly assistant.");
|
||||
|
||||
// Construct request
|
||||
var userContents = new List<ChatMessageContent>
|
||||
{
|
||||
new ChatMessageTextContent("What's in this image?"),
|
||||
new ChatMessageImageContent(new Uri("https://link-to-image.com"))
|
||||
};
|
||||
|
||||
chatHistory.AddUserMessage(userContents);
|
||||
|
||||
// Get response
|
||||
var message = await chatCompletionService.GetChatMessageAsync(chatHistory);
|
||||
|
||||
foreach (var content in message.Contents)
|
||||
{
|
||||
// Possibility to get content type (text or image).
|
||||
var contentType = content.Type;
|
||||
|
||||
// Cast for specific content type
|
||||
// Extension methods can be provided for better usability
|
||||
// (e.g. message GetContent<ChatMessageTextContent>()).
|
||||
if (content is ChatMessageTextContent textContent)
|
||||
{
|
||||
Console.WriteLine(textContent);
|
||||
}
|
||||
|
||||
if (content is ChatMessageImageContent imageContent)
|
||||
{
|
||||
Console.WriteLine(imageContent.Uri);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Option #2: Avoid renaming and new data type for `chat message content`
|
||||
|
||||
Same as Option #1, but without naming changes. In order to differentiate actual `chat message` and `chat message content`:
|
||||
|
||||
- `Chat Message` will be `ChatMessageContent` (as it is right now).
|
||||
- `Chat Message Content` will be `ChatMessageContentItem`.
|
||||
|
||||
1. New abstract class `ChatMessageContentItem` that will have property `ChatMessageContentItemType Type` with values `text`, `image`. (Will be extended with `audio`, `video` in the future).
|
||||
2. `ChatMessageContent` will contain collection of `ChatMessageContentItem` objects `IList<ChatMessageContentItem> Items`.
|
||||
3. There will be concrete implementations of `ChatMessageContentItem` - `ChatMessageTextContentItem` and `ChatMessageImageContentItem`.
|
||||
|
||||
New _ChatMessageContentItemType.cs_
|
||||
|
||||
```csharp
|
||||
public readonly struct ChatMessageContentItemType : IEquatable<ChatMessageContentItemType>
|
||||
{
|
||||
public static ChatMessageContentItemType Text { get; } = new("text");
|
||||
|
||||
public static ChatMessageContentItemType Image { get; } = new("image");
|
||||
|
||||
public string Label { get; }
|
||||
|
||||
// Implementation of `IEquatable`...
|
||||
}
|
||||
```
|
||||
|
||||
New _ChatMessageContentItem.cs_
|
||||
|
||||
```csharp
|
||||
public abstract class ChatMessageContentItem
|
||||
{
|
||||
public ChatMessageContentItemType Type { get; set; }
|
||||
|
||||
public ChatMessageContentItem(ChatMessageContentItemType type)
|
||||
{
|
||||
this.Type = type;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Updated _ChatMessageContent.cs_:
|
||||
|
||||
```csharp
|
||||
public class ChatMessageContent : ContentBase
|
||||
{
|
||||
public AuthorRole Role { get; set; }
|
||||
|
||||
public IList<ChatMessageContentItem> Items { get; set; }
|
||||
```
|
||||
|
||||
New _ChatMessageTextContentItem.cs_
|
||||
|
||||
```csharp
|
||||
public class ChatMessageTextContentItem : ChatMessageContentItem
|
||||
{
|
||||
public string Text { get; set; }
|
||||
|
||||
public ChatMessageTextContentItem(string text) : base(ChatMessageContentType.Text)
|
||||
{
|
||||
this.Text = text;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
New _ChatMessageImageContent.cs_
|
||||
|
||||
```csharp
|
||||
public class ChatMessageImageContentItem : ChatMessageContentItem
|
||||
{
|
||||
public Uri Uri { get; set; }
|
||||
|
||||
public ChatMessageImageContentItem(Uri uri) : base(ChatMessageContentType.Image)
|
||||
{
|
||||
this.Uri = uri;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```csharp
|
||||
var chatHistory = new ChatHistory("You are friendly assistant.");
|
||||
|
||||
// Construct request
|
||||
var userContentItems = new List<ChatMessageContentItem>
|
||||
{
|
||||
new ChatMessageTextContentItem("What's in this image?"),
|
||||
new ChatMessageImageContentItem(new Uri("https://link-to-image.com"))
|
||||
};
|
||||
|
||||
chatHistory.AddUserMessage(userContentItems);
|
||||
|
||||
// Get response
|
||||
var message = await chatCompletionService.GetChatMessageContentAsync(chatHistory);
|
||||
|
||||
foreach (var contentItem in message.Items)
|
||||
{
|
||||
// Possibility to get content type (text or image).
|
||||
var contentItemType = contentItem.Type;
|
||||
|
||||
// Cast for specific content type
|
||||
// Extension methods can be provided for better usability
|
||||
// (e.g. message GetContent<ChatMessageTextContentItem>()).
|
||||
if (contentItem is ChatMessageTextContentItem textContentItem)
|
||||
{
|
||||
Console.WriteLine(textContentItem);
|
||||
}
|
||||
|
||||
if (contentItem is ChatMessageImageContentItem imageContentItem)
|
||||
{
|
||||
Console.WriteLine(imageContentItem.Uri);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Option #3: Add new property to `ChatMessageContent` - collection of content items
|
||||
|
||||
This option will keep `string Content` property as it is, but will add new property - collection of `ContentBase` items.
|
||||
|
||||
Updated _ChatMessageContent.cs_
|
||||
|
||||
```csharp
|
||||
public class ChatMessageContent : ContentBase
|
||||
{
|
||||
public AuthorRole Role { get; set; }
|
||||
|
||||
public string? Content { get; set; }
|
||||
|
||||
public ChatMessageContentItemCollection? Items { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
New _ChatMessageContentItemCollection.cs_
|
||||
|
||||
```csharp
|
||||
public class ChatMessageContentItemCollection : IList<ContentBase>, IReadOnlyList<ContentBase>
|
||||
{
|
||||
// Implementation of IList<ContentBase>, IReadOnlyList<ContentBase> to catch null values.
|
||||
}
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```csharp
|
||||
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
|
||||
|
||||
var chatHistory = new ChatHistory("You are a friendly assistant.");
|
||||
|
||||
chatHistory.AddUserMessage(new ChatMessageContentItemCollection
|
||||
{
|
||||
new TextContent("What’s in this image?"),
|
||||
new ImageContent(new Uri(ImageUri))
|
||||
});
|
||||
|
||||
var reply = await chatCompletionService.GetChatMessageContentAsync(chatHistory);
|
||||
|
||||
Console.WriteLine(reply.Content);
|
||||
```
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Option #3 was preferred as it requires small amount of changes to existing hierarchy and provides clean usability for end-user.
|
||||
|
||||
Diagram:
|
||||

|
||||
@@ -0,0 +1,317 @@
|
||||
---
|
||||
status: { accepted }
|
||||
contact: { TaoChenOSU }
|
||||
date: { 2023-11-21 }
|
||||
deciders: alliscode, dmytrostruk, markwallace, SergeyMenshykh, stephentoub
|
||||
consulted: {}
|
||||
informed: {}
|
||||
---
|
||||
|
||||
# Planner Telemetry Enhancement
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
It would be extremely beneficial for applications using Semantic Kernel's planning features to be able to continuously monitor the performance of planners and plans as well as debugging them.
|
||||
|
||||
## Scenarios
|
||||
|
||||
Contoso is a company that is developing an AI application using SK.
|
||||
|
||||
1. Contoso needs to continuously monitor the token usage of a particular planner, including prompt tokens, completion tokens, and the total tokens.
|
||||
2. Contoso needs to continuously monitor the time it takes for a particular planner to create a plan.
|
||||
3. Contoso needs to continuously monitor the success rate of a particular planner in creating a valid plan.
|
||||
4. Contoso needs to continuously monitor the success rate of a particular plan type being executed successfully.
|
||||
5. Contoso wants to be able to see the token usage of a particular planner run.
|
||||
6. Contoso wants to be able to see the time taken to create a plan of a particular planner run.
|
||||
7. Contoso wants to be able to see the steps in a plan.
|
||||
8. Contoso wants to be able to see the inputs&outputs of each plan step.
|
||||
9. Contoso wants to change a few settings that may affect the performance of the planners. They would like to know how the performance will be affected before committing the changes.
|
||||
10. Contoso wants to update to a new model that is cheaper and faster. They would like to know how the new model performs in planning tasks.
|
||||
|
||||
## Out of scope
|
||||
|
||||
1. We provide an example on how to send telemetry to Application Insights. Although other telemetry service options are supported technically, we will not cover possible ways of setting them up in this ADR.
|
||||
2. This ADR does not seek to modify the current instrumentation design in SK.
|
||||
3. We do not consider services that do not return token usage.
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
- The framework should be telemetry service agnostic.
|
||||
- The following metrics should be emitted by SK:
|
||||
- Input token usage for prompt (Prompt)
|
||||
- Description: A prompt is the smallest unit that consumes tokens (`KernelFunctionFromPrompt`).
|
||||
- Dimensions: ComponentType, ComponentName, Service ID, Model ID
|
||||
- Type: Histogram
|
||||
- Example:
|
||||
| ComponentType | ComponentName | Service ID | Model ID | Value |
|
||||
|---|---|---|---|---|
|
||||
| Function | WritePoem | | GPT-3.5-Turbo | 40
|
||||
| Function | TellJoke | | GPT-4 | 50
|
||||
| Function | WriteAndTellJoke | | GPT-3.5-Turbo | 30
|
||||
| Planner | CreateHandlebarsPlan | | GPT-3.5-Turbo | 100
|
||||
- Output token usage for prompt (Completion)
|
||||
- Description: A prompt is the smallest unit that consumes tokens (`KernelFunctionFromPrompt`).
|
||||
- Dimensions: ComponentType, ComponentName, Service ID, Model ID
|
||||
- Type: Histogram
|
||||
- Example:
|
||||
| ComponentType | ComponentName | Service ID | Model ID | Value |
|
||||
|---|---|---|---|---|
|
||||
| Function | WritePoem | | GPT-3.5-Turbo | 40
|
||||
| Function | TellJoke | | GPT-4 | 50
|
||||
| Function | WriteAndTellJoke | | GPT-3.5-Turbo | 30
|
||||
| Planner | CreateHandlebarsPlan | | GPT-3.5-Turbo | 100
|
||||
- Aggregated execution time for functions
|
||||
- Description: A function can consist of zero or more prompts. The execution time of a function is the duration from start to end of a function's `invoke` call.
|
||||
- Dimensions: ComponentType, ComponentName, Service ID, Model ID
|
||||
- Type: Histogram
|
||||
- Example:
|
||||
| ComponentType | ComponentName | Value |
|
||||
|---|---|---|
|
||||
| Function | WritePoem | 1m
|
||||
| Function | TellJoke | 1m
|
||||
| Function | WriteAndTellJoke | 1.5m
|
||||
| Planner | CreateHandlebarsPlan | 2m
|
||||
- Success/failure count for planners
|
||||
- Description: A planner run is considered successful when it generates a valid plan. A plan is valid when the model response is successfully parsed into a plan of desired format and it contains one or more steps.
|
||||
- Dimensions: ComponentType, ComponentName, Service ID, Model ID
|
||||
- Type: Counter
|
||||
- Example:
|
||||
| ComponentType | ComponentName | Fail | Success
|
||||
|---|---|---|---|
|
||||
| Planner | CreateHandlebarsPlan | 5 | 95
|
||||
| Planner | CreateHSequentialPlan | 20 | 80
|
||||
- Success/failure count for plans
|
||||
- Description: A plan execution is considered successful when all steps in the plan are executed successfully.
|
||||
- Dimensions: ComponentType, ComponentName, Service ID, Model ID
|
||||
- Type: Counter
|
||||
- Example:
|
||||
| ComponentType | ComponentName | Fail | Success
|
||||
|---|---|---|---|
|
||||
| Plan | HandlebarsPlan | 5 | 95
|
||||
| Plan | SequentialPlan | 20 | 80
|
||||
|
||||
## Considered Options
|
||||
|
||||
- Function hooks
|
||||
- Inject logic to functions that will get executed before or after a function is invoked.
|
||||
- Instrumentation
|
||||
- Logging
|
||||
- Metrics
|
||||
- Traces
|
||||
|
||||
## Other Considerations
|
||||
|
||||
SK currently tracks token usage metrics in connectors; however, these metrics are not categorized. Consequently, developers cannot determine token usage for different operations. To address this issue, we propose the following two approaches:
|
||||
|
||||
- Bottom-up: Propagate token usage information from connectors back to the functions.
|
||||
- Top-down: Propagate function information down to the connectors, enabling them to tag metric items with function information.
|
||||
|
||||
We have decided to implement the bottom-up approach for the following reasons:
|
||||
|
||||
1. SK is already configured to propagate token usage information from connectors via `ContentBase`. We simply need to extend the list of items that need to be propagated, such as model information.
|
||||
2. Currently, SK does not have a method for passing function information down to the connector level. Although we considered using [baggage](https://opentelemetry.io/docs/concepts/signals/baggage/#:~:text=In%20OpenTelemetry%2C%20Baggage%20is%20contextual%20information%20that%E2%80%99s%20passed,available%20to%20any%20span%20created%20within%20that%20trace.) as a means of propagating information downward, experts from the OpenTelemetry team advised against this approach due to security concerns.
|
||||
|
||||
With the bottom-up approach, we need to retrieve the token usage information from the metadata:
|
||||
|
||||
```csharp
|
||||
// Note that not all services support usage details.
|
||||
/// <summary>
|
||||
/// Captures usage details, including token information.
|
||||
/// </summary>
|
||||
private void CaptureUsageDetails(string? modelId, IDictionary<string, object?>? metadata, ILogger logger)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(modelId))
|
||||
{
|
||||
logger.LogWarning("No model ID provided to capture usage details.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (metadata is null)
|
||||
{
|
||||
logger.LogWarning("No metadata provided to capture usage details.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!metadata.TryGetValue("Usage", out object? usageObject) || usageObject is null)
|
||||
{
|
||||
logger.LogWarning("No usage details provided to capture usage details.");
|
||||
return;
|
||||
}
|
||||
|
||||
var promptTokens = 0;
|
||||
var completionTokens = 0;
|
||||
try
|
||||
{
|
||||
var jsonObject = JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(usageObject));
|
||||
promptTokens = jsonObject.GetProperty("PromptTokens").GetInt32();
|
||||
completionTokens = jsonObject.GetProperty("CompletionTokens").GetInt32();
|
||||
}
|
||||
catch (Exception ex) when (ex is KeyNotFoundException)
|
||||
{
|
||||
logger.LogInformation("Usage details not found in model result.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error while parsing usage details from model result.");
|
||||
throw;
|
||||
}
|
||||
|
||||
logger.LogInformation(
|
||||
"Prompt tokens: {PromptTokens}. Completion tokens: {CompletionTokens}.",
|
||||
promptTokens, completionTokens);
|
||||
|
||||
TagList tags = new() {
|
||||
{ "semantic_kernel.function.name", this.Name },
|
||||
{ "semantic_kernel.function.model_id", modelId }
|
||||
};
|
||||
|
||||
s_invocationTokenUsagePrompt.Record(promptTokens, in tags);
|
||||
s_invocationTokenUsageCompletion.Record(completionTokens, in tags);
|
||||
}
|
||||
```
|
||||
|
||||
> Note that we do not consider services that do not return token usage. Currently only OpenAI & Azure OpenAI services return token usage information.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
1. New metrics names:
|
||||
| Meter | Metrics |
|
||||
|---|---|
|
||||
|Microsoft.SemanticKernel.Planning| <ul><li>semantic_kernel.planning.invoke_plan.duration</li></ul> |
|
||||
|Microsoft.SemanticKernel| <ul><li>semantic_kernel.function.invocation.token_usage.prompt</li><li>semantic_kernel.function.invocation.token_usage.completion</li></ul> |
|
||||
> Note: we are also replacing the "sk" prefixes with "semantic_kernel" for all existing metrics to avoid ambiguity.
|
||||
2. Instrumentation
|
||||
|
||||
## Validation
|
||||
|
||||
Tests can be added to make sure that all the expected telemetry items are in place and of the correct format.
|
||||
|
||||
## Description the Options
|
||||
|
||||
### Function hooks
|
||||
|
||||
Function hooks allow developers to inject logic to the kernel that will be executed before or after a function is invoked. Example use cases include logging the function input before a function is invoked, and logging results after the function returns.
|
||||
For more information, please refer to the following ADRs:
|
||||
|
||||
1. [Kernel Hooks Phase 1](./0005-kernel-hooks-phase1.md)
|
||||
2. [Kernel Hooks Phase 2](./0018-kernel-hooks-phase2.md)
|
||||
|
||||
We can inject, during function registration, default callbacks to log critical information for all functions.
|
||||
|
||||
Pros:
|
||||
|
||||
1. Maximum exposure and flexibility to the developers. i.e. App developers can very easily log additional information for individual functions by adding more callbacks.
|
||||
|
||||
Cons:
|
||||
|
||||
1. Does not create metrics and need additional works to aggregate results.
|
||||
2. Relying only on logs does not provide trace details.
|
||||
3. Logs are modified more frequently, which could lead an unstable implementation and require extra maintenance.
|
||||
4. Hooks only have access to limited function data.
|
||||
|
||||
> Note: with distributed tracing already implemented in SK, developers can create custom telemetry within the hooks, which will be sent to the telemetry service once configured, as long as the information is available in the hooks. However, telemetry items created inside the hooks will not be correlated to the functions as parent-child relationships, since they are outside the scope of the functions.
|
||||
|
||||
### Distributed tracing
|
||||
|
||||
Distributed tracing is a diagnostic technique that can localize failures and performance bottlenecks within distributed applications. .Net has native support to add distributed tracing in libraries and .Net libraries are also instrumented to produce distributed tracing information automatically.
|
||||
|
||||
For more information, please refer to this document: [.Net distributed tracing](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/)
|
||||
|
||||
Overall pros:
|
||||
|
||||
1. Native .Net support.
|
||||
2. Distributed tracing is already implemented in SK. We just need to add more telemetry.
|
||||
3. Telemetry service agnostic with [OpenTelemetry](https://opentelemetry.io/docs/what-is-opentelemetry/).
|
||||
|
||||
Overall cons:
|
||||
|
||||
1. Less flexibility for app developers consuming SK as a library to add custom traces and metrics.
|
||||
|
||||
#### Logging
|
||||
|
||||
Logs will be used to record interesting events while the code is running.
|
||||
|
||||
```csharp
|
||||
// Use LoggerMessage attribute for optimal performance
|
||||
this._logger.LogPlanCreationStarted();
|
||||
this._logger.LogPlanCreated();
|
||||
```
|
||||
|
||||
#### [Metrics](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics)
|
||||
|
||||
Metrics will be used to record measurements overtime.
|
||||
|
||||
```csharp
|
||||
/// <summary><see cref="Meter"/> for function-related metrics.</summary>
|
||||
private static readonly Meter s_meter = new("Microsoft.SemanticKernel");
|
||||
|
||||
/// <summary><see cref="Histogram{T}"/> to record plan execution duration.</summary>
|
||||
private static readonly Histogram<double> s_planExecutionDuration =
|
||||
s_meter.CreateHistogram<double>(
|
||||
name: "semantic_kernel.planning.invoke_plan.duration",
|
||||
unit: "s",
|
||||
description: "Duration time of plan execution.");
|
||||
|
||||
TagList tags = new() { { "semantic_kernel.plan.name", planName } };
|
||||
|
||||
try
|
||||
{
|
||||
...
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// If a measurement is tagged with "error.type", then it's a failure.
|
||||
tags.Add("error.type", ex.GetType().FullName);
|
||||
}
|
||||
|
||||
s_planExecutionDuration.Record(duration.TotalSeconds, in tags);
|
||||
```
|
||||
|
||||
#### [Traces](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing)
|
||||
|
||||
Activities are used to track dependencies through an application, correlating work done by other components, and form a tree of activities known as a trace.
|
||||
|
||||
```csharp
|
||||
ActivitySource s_activitySource = new("Microsoft.SemanticKernel");
|
||||
|
||||
// Create and start an activity
|
||||
using var activity = s_activitySource.StartActivity(this.Name);
|
||||
|
||||
// Use LoggerMessage attribute for optimal performance
|
||||
logger.LoggerGoal(goal);
|
||||
logger.LoggerPlan(plan);
|
||||
```
|
||||
|
||||
> Note: Trace log will contain sensitive data and should be turned off in production: https://learn.microsoft.com/en-us/dotnet/core/extensions/logging?tabs=command-line#log-level
|
||||
|
||||
## Example of how an application would send the telemetry to Application Insights
|
||||
|
||||
```csharp
|
||||
using var traceProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddSource("Microsoft.SemanticKernel*")
|
||||
.AddAzureMonitorTraceExporter(options => options.ConnectionString = connectionString)
|
||||
.Build();
|
||||
|
||||
using var meterProvider = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter("Microsoft.SemanticKernel*")
|
||||
.AddAzureMonitorMetricExporter(options => options.ConnectionString = connectionString)
|
||||
.Build();
|
||||
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
// Add OpenTelemetry as a logging provider
|
||||
builder.AddOpenTelemetry(options =>
|
||||
{
|
||||
options.AddAzureMonitorLogExporter(options => options.ConnectionString = connectionString);
|
||||
// Format log messages. This is default to false.
|
||||
options.IncludeFormattedMessage = true;
|
||||
});
|
||||
builder.SetMinimumLevel(MinLogLevel);
|
||||
});
|
||||
```
|
||||
|
||||
## More information
|
||||
|
||||
Additional works that need to be done:
|
||||
|
||||
1. Update [telemetry doc](../../dotnet/docs/TELEMETRY.md)
|
||||
@@ -0,0 +1,130 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: proposed
|
||||
contact: SergeyMenshykh
|
||||
date: 2024-01-04
|
||||
deciders: markwallace-microsoft
|
||||
consulted: rogerbarreto, dmytrostruk
|
||||
informed:
|
||||
---
|
||||
|
||||
# SK Branching Strategy
|
||||
|
||||
## Industry-adopted branching strategies
|
||||
There are several industry-adopted branching strategies for Git, such as GitHub Flow, Git-Flow, and GitLab Flow. However, we will only focus on the two most widely-used ones: GitHub Flow and Git-Flow.
|
||||
|
||||
### GitHub Flow
|
||||
GitHub Flow is a straightforward branching strategy that centres around the 'main' branch. Developers create a new branch for each feature or bugfix, make changes, submit a pull request, and merge the changes back to the 'main' branch. Releases are done directly from the 'main' branch, making this model ideal for projects with continuous integration/deployment. Learn more about [GitHub Flow](https://docs.github.com/en/get-started/quickstart/github-flow).
|
||||
|
||||
<img src="./diagrams/git-hub-flow.png" alt="GitFlow" width="500"/>
|
||||
|
||||
[Image source](https://www.abtasty.com/blog/git-branching-strategies/)
|
||||
|
||||
Pros:
|
||||
- Straightforward with fewer branches to manage and less merge conflicts.
|
||||
- No long running development branches.
|
||||
|
||||
Cons:
|
||||
- Not as well organized as Git-Flow.
|
||||
- The 'main' branch can get cluttered more easily since it functions as both the production and development branch.
|
||||
|
||||
### Git-Flow
|
||||
Git-Flow is a branching strategy that organizes software development around two long-lived main branches, 'main' and 'develop', along with short-lived feature, release, and hotfix branches. Developers work on new features in feature branches, which are then merged into the 'develop' branch. When preparing for a release, to avoid blocking future release features, a release branch is created, and once finalized (testing & bug fixing), it is merged into both 'main' and 'develop'. Hotfix branches in Git Flow are created from the 'main' branch to address critical bug fixes and are subsequently merged back into both the 'main' and 'develop' branches. The actual release(deployable artifact) is done from the 'main' branch that is reflects actual production worthy official releases. Learn more about [Git-Flow](https://nvie.com/posts/a-successful-git-branching-model/).
|
||||
|
||||
<img src="./diagrams/git-flow.png" alt="GitFlow" width="700"/>
|
||||
|
||||
Pros:
|
||||
- Clear separation between code under development and production-ready code.
|
||||
- Efficient release management.
|
||||
|
||||
Cons:
|
||||
- More complex than GitHub Flow, which may be overwhelming for smaller teams or projects that do not require as much structure.
|
||||
- Less suited for projects that prioritize continuous deployment, as it emphasizes a more controlled release process.
|
||||
- Not ideal for projects with continuous deployment due to the overhead of managing multiple branches.
|
||||
- Spaghetti history in Git - [GitFlow considered harmful](https://www.endoflineblog.com/gitflow-considered-harmful)
|
||||
|
||||
# SK branching strategies
|
||||
Today, the SK SDK is available in three languages: .NET, Java and Python. All of them coexist in the same Git repository, organized under corresponding folders. However, the branching strategies for those differ.
|
||||
|
||||
For both .NET and Python versions, development takes place in short-lived topic branches that branch off the 'main' branch. These topic branches are merged back into the 'main' branch when features are considered production-ready through PR reviews, unit tests, and integration test runs. Releases are carried out directly from the 'main' branch. This approach aligns with the GitHub Flow branching strategy, with a minor deviation where releases are conducted weekly rather than being continuously deployed.
|
||||
|
||||
The Java version of SK adheres to the Git-Flow strategy by being developed in a dedicated development branch. Topic branches are created from the development branch and merged back through pull requests after unit tests and integration test runs. Release branches are also created from the development branch and merged to both the development branch and the 'main' one when a release is considered production-ready. This strategy deviates slightly from vanilla Git-Flow in that release artifacts are generated from release branches rather than from the 'main' branch.
|
||||
|
||||
## Decision Drivers
|
||||
- The strategy should be easy to implement and maintain without requiring significant investments.
|
||||
- The strategy should allow for maintaining several releases in parallel if required.
|
||||
- Ideally, the strategy is intuitive and simple so that everyone familiar with Git can adopt and follow it.
|
||||
- Ideally, all SK languages are able to adopt and use the same branching strategy.
|
||||
- Ability to continually deploy new release with minimal overhead.
|
||||
- Ability to release language versions independently and on different schedules.
|
||||
- Allow the .Net, Java and Python teams to be able to operate independently.
|
||||
- Ability to patch a release (for all languages).
|
||||
- Consolidation of PR's and Issues to simplify the triage and review process.
|
||||
|
||||
Another aspect to consider when deciding on a branching strategy for SK is access permissions and action scopes. GitHub does not allow enforcing access restrictions on just a part of a repository, such as a folder. This means that it is not possible to restrict SK .NET contributors from pushing Python PRs, which ideally should be done by the corresponding team. However, GitHub does allow assigning access permissions to a branch, which can be successfully leveraged if the appropriate strategy option is chosen. The similar issue occurs with GitHub's required actions/status checks, which can only be set at the branch level. Considering that development for .NET and Python takes place in the 'main' branch, and status checks are configured per branch rather than per folder, it is not possible to configure separate status checks for .NET and Python PRs. As a result, the same status check runs for both .NET and Python PRs, even though it may not be relevant to a specific language.
|
||||
|
||||

|
||||
|
||||
Regardless of the chosen strategy, it should be possible to support multiple versions of SK. For example, applying a bug fix or a security patch to released SK v1.1.0 and v2.4.0 should be feasible while working on v3.0.0. One way to achieve this would be to create a release branch for each SK release. So that the required patch/fix can be pushed to the branch and released from it. However, marking released commits with tags should suffice, as it is always possible to create a new branch from a tag retrospectively when needed, if at all. Existing release pipelines should accept a source branch as a parameter, enabling releases from any branch and not only from the 'main' one.
|
||||
|
||||
## Considered Options
|
||||
|
||||
### Repository per SK language
|
||||
This option suggests having a separate GitHub repository for each SK language. These repositories can be created under a corresponding organization. Development and releases will follow the GitHub flow, with new features and fixes being developed in topic branches that created from the 'main' branch and eventually merged back.
|
||||
|
||||
Pros:
|
||||
- Each repository will have only language-specific status checks and actions.
|
||||
- Branch commits and release history will not contain irrelevant commits or releases.
|
||||
- Utilizes the familiar GitHub Flow without Git-Flow overhead, resulting in a shorter learning curve.
|
||||
- Access permissions are limited to the specific owning team.
|
||||
|
||||
Cons:
|
||||
- There is an initial overhead in setting up the three repositories.
|
||||
- There may be potential ongoing maintenance overhead for the three repositories.
|
||||
- Secrets must be managed across three repositories instead of just one.
|
||||
- Each repo will have a backlog that will have to be managed separately.
|
||||
|
||||
### Branch per SK language
|
||||
This option involves having a dedicated, language-specific development branch for each SDK language: 'net-development', 'java-development', and 'python-development'. SDK Java is already using this option. Development and releases will follow the GitHub Flow, with new features and fixes being developed in topic branches that are branched off the corresponding language branch and eventually merged back.
|
||||
|
||||
Pros:
|
||||
- Simple, language specific, status checks, actions and rules configured per language branch.
|
||||
- Allow only teams that own language-specific branches to push or merge to them, rather than just approving PRs.
|
||||
- Branch commits history does not contain irrelevant commits.
|
||||
|
||||
Cons:
|
||||
- GitHub release history contains releases for all languages.
|
||||
- Language-specific branches may not be straightforward to discover/use.
|
||||
|
||||
This option has two sub-options that define the way the 'main' branch is used:
|
||||
1. The 'main' branch will contain general/common artifacts such as documentation, GitHub actions, and samples. All language folders will be removed from the 'main' branch, and it can be locked to prevent accidental merges.
|
||||
2. The 'main' branch will include everything that dev branches have for discoverability purposes. A job/action will be implemented to merge commits from dev branches to the 'main' branch. The number of common artifacts between SK languages should be minimized to reduce the potential for merge conflicts. A solution for the squash merge problem that SK Java is experiencing today should be found before deciding on the sub-option.
|
||||
|
||||
The second sub-option is preferred over the first one due to its discoverability benefits. There is no need to select a development branch in the GitHub UI when searching for something in the repository. The 'main' branch is selected by default, and as soon as the latest bits are in the branch, they can be found easily. This intuitive approach is familiar to many, and changing it by requiring the selection of a branch before searching would complicate the search experience and introduce frustration.
|
||||
|
||||
### All SK languages in the 'main'
|
||||
This option assumes maintaining the code for all SK languages - .NET, Java, and Python in the 'main' branch. Development would occur using typical topic branches, while releases would also be made from the 'main' branch. This is the strategy currently adopted by .NET and Python, and corresponds to the GitHub Flow.
|
||||
|
||||
Pros:
|
||||
- All code in one place - the 'main' branch.
|
||||
- Familiar GitHub Flow, no Git-Flow overhead - shorter learning curve.
|
||||
|
||||
Cons:
|
||||
- Branch commits/release history contains irrelevant commits/releases.
|
||||
- Complex and irrelevant GitHub status checks/actions.
|
||||
- PRs can be pushed by non-owner teams.
|
||||
|
||||
### Current 'Hybrid' approach
|
||||
This choice keeps the existing method used by SK. .NET and Python development is done in the 'main' branch using GitHub Flow, while Java development happens in the java-development branch following Git-Flow.
|
||||
|
||||
Pros:
|
||||
- No changes required.
|
||||
- Each SK language uses a strategy that is convenient for it.
|
||||
|
||||
Cons:
|
||||
- Branch commits/release history contains irrelevant commits/releases.
|
||||
- Complex and irrelevant GitHub status checks/actions.
|
||||
- PRs can be pushed by non-owner teams.
|
||||
|
||||
## Decision Outcome
|
||||
Chosen option: "Current 'Hybrid' approach" because it works with minor inefficiencies (such as cluttered release history and multi-language complex actions) and requires no investments now. Later, depending on the team size and the problems the team encounters with the "Current 'Hybrid' approach," we may consider either the 'Repository per SK language' option or the 'Branch per SK language' one.
|
||||
@@ -0,0 +1,156 @@
|
||||
---
|
||||
# Strategy for Community Driven Connectors and Features
|
||||
|
||||
status: approved
|
||||
contact: rogerbarreto
|
||||
date: 2024-01-24
|
||||
deciders: rogerbarreto, markwallace-microsoft, dmytrostruk, sergeymenshik
|
||||
consulted:
|
||||
informed:
|
||||
---
|
||||
|
||||
# Strategy for Community Driven Connectors and Features
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
Normally Connectors are Middle to Complex new Features that can be developed by a single person or a team. In order to avoid conflicts and to have a better control of the development process, we strongly suggest the usage of a Feature Branch Strategy in our repositories.
|
||||
|
||||
In our current software development process, managing changes in the main branch has become increasingly complex, leading to potential conflicts and delays in release cycles.
|
||||
|
||||
## Standards and Guidelines Principles
|
||||
|
||||
- **Pattern**: The Feature Branch Strategy is a well-known pattern for managing changes in a codebase. It is widely used in the industry and is supported by most version control systems, including GitHub, this also gives further clear picture on how the community can meaningfully contribute to the development of connectors or any other bigger feature for SK.
|
||||
- **Isolated Development Environments**: By using feature branches, each developer can work on different aspects of the project without interfering with others' work. This isolation reduces conflicts and ensures that the main branch remains stable.
|
||||
- **Streamlined Integration**: Feature branches simplify the process of integrating new code into the main branch. By dealing with smaller, more manageable changes, the risk of major conflicts during integration is minimized.
|
||||
- **Efficiency in Code Review**: Smaller, more focused changes in feature branches lead to quicker and more efficient code reviews. This efficiency is not just about the ease of reviewing less code at a time but also about the time saved in understanding the context and impact of the changes.
|
||||
- **Reduced Risk of Bugs**: Isolating development in feature branches reduces the likelihood of introducing bugs into the main branch. It's easier to identify and fix issues within the confined context of a single feature.
|
||||
- **Timely Feature Integration**: Small, incremental pull requests allow for quicker reviews and faster integration of features into the feature branch and make it easier to merge down into main as the code was already previously reviewed. This timeliness ensures that features are merged and ready for deployment sooner, improving the responsiveness to changes.
|
||||
- **Code Testing, Coverage and Quality**: To keep a good code quality is imperative that any new code or feature introduced to the codebase is properly tested and validated. Any new feature or code should be covered by unit tests and integration tests. The code should also be validated by our CI/CD pipeline and follow our code quality standards and guidelines.
|
||||
- **Examples**: Any new feature or code should be accompanied by examples that demonstrate how to use the new feature or code. This is important to ensure that the new feature or code is properly documented and that the community can easily understand and use it.
|
||||
|
||||
### Community Feature Branch Strategy
|
||||
|
||||
As soon we identify that contributors are willing to take/create a Feature Issue as a potential connector implementation, we will create a new branch for that feature.
|
||||
|
||||
Once we have agreed to take a new connector we will work with the contributors to make sure the implementation progresses and is supported if needed.
|
||||
|
||||
The contributor(s) will then be one of the responsibles to incrementally add the majority of changes through small Pull Requests to the feature branch under our supervision and review process.
|
||||
|
||||
This strategy involves creating a separate branch in the repository for each new big feature, like connectors. This isolation means that changes are made in a controlled environment without affecting the main branch.
|
||||
|
||||
We may also engage in the development and changes to the feature branch when needed, the changes and full or co-authorship on the PRs will be tracked and properly referred into the Release Notes.
|
||||
|
||||
#### Pros and Cons
|
||||
|
||||
- Good, because it allows for focused development on one feature at a time.
|
||||
- Good, because it promotes smaller, incremental Pull Requests (PRs), simplifying review processes.
|
||||
- Good, because it reduces the risk of major bugs being merged into the main branch.
|
||||
- Good, because it makes the process of integrating features into the main branch easier and faster.
|
||||
- Bad, potentially, if not managed properly, as it can lead to outdated branches if not regularly synchronized with the main branch.
|
||||
|
||||
## Local Deployment Platforms / Offline
|
||||
|
||||
### LM Studio
|
||||
|
||||
LM Studio has a local deployment option, which can be used to deploy models locally. This option is available for Windows, Linux, and MacOS.
|
||||
|
||||
Pros:
|
||||
|
||||
- API is very similar to OpenAI API
|
||||
- Many models are already supported
|
||||
- Easy to use
|
||||
- Easy to deploy
|
||||
- GPU support
|
||||
|
||||
Cons:
|
||||
|
||||
- May require a license to use in a work environment
|
||||
|
||||
### Ollama
|
||||
|
||||
Ollama has a local deployment option, which can be used to deploy models locally. This option is available for Linux and MacOS only for now.
|
||||
|
||||
Pros:
|
||||
|
||||
- Easy to use
|
||||
- Easy to deploy
|
||||
- Supports Docker deployment
|
||||
- GPU support
|
||||
|
||||
Cons:
|
||||
|
||||
- API is not similar to OpenAI API (Needs a dedicated connector)
|
||||
- Dont have Windows support
|
||||
|
||||
### Comparison
|
||||
|
||||
| Feature | Ollama | LM Studio |
|
||||
| --------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| Local LLM | Yes | Yes |
|
||||
| OpenAI API Similarity | Yes | Yes |
|
||||
| Windows Support | No | Yes |
|
||||
| Linux Support | Yes | Yes |
|
||||
| MacOS Support | Yes | Yes |
|
||||
| Number of Models | [61](https://ollama.ai/library) +Any GGUF converted | [25](https://github.com/lmstudio-ai/model-catalog/tree/main/models) +Any GGUF Converted |
|
||||
|
||||
| Model Support | Ollama | LM Studio |
|
||||
| --------------- | ------ | --------- |
|
||||
| Phi-2 Support | Yes | Yes |
|
||||
| Llama-2 Support | Yes | Yes |
|
||||
| Mistral Support | Yes | Yes |
|
||||
|
||||
## Connector/Model Priorities
|
||||
|
||||
Currently we are looking for community support on the following models
|
||||
|
||||
The support on the below can be either achieved creating a practical example using one of the existing Connectors against one of this models or providing a new Connector that supports a deployment platform that hosts one of the models below:
|
||||
|
||||
| Model Name | Local Support | Deployment | Connectors |
|
||||
| ---------- | ------------- | -------------------------------------- | ------------------------------------------------------ |
|
||||
| Gpt-4 | No | OpenAI, Azure | Azure+OpenAI |
|
||||
| Phi-2 | Yes | Azure, Hugging Face, LM Studio, Ollama | OpenAI, HuggingFace, LM Studio\*\*\*, Ollama\*\* |
|
||||
| Gemini | No | Google AI Platform | GoogleAI\*\* |
|
||||
| Llama-2 | Yes | Azure, LM Studio, HuggingFace, Ollama | HuggingFace, Azure+OpenAI, LM Studio\*\*\*, Ollama\*\* |
|
||||
| Mistral | Yes | Azure, LM Studio, HuggingFace, Ollama | HuggingFace, Azure+OpenAI, LM Studio\*\*\*, Ollama\*\* |
|
||||
| Claude | No | Anthropic, Amazon Bedrock | Anthropic**, Amazon** |
|
||||
| Titan | No | Amazon Bedrock | Amazon\*\* |
|
||||
|
||||
_\*\* Connectors not yet available_
|
||||
|
||||
_\*\*\* May not be needed as an OpenAI Connector can be used_
|
||||
|
||||
Connectors may be needed not per Model basis but rather per deployment platform.
|
||||
For example, using OpenAI or HuggingFace connector you may be able to call a Phi-2 Model.
|
||||
|
||||
## Expected Connectors to be implemented
|
||||
|
||||
The following deployment platforms are not yet supported by any Connectors and we strongly encourage the community to engage and support on those:
|
||||
|
||||
Currently the priorities are ordered but not necessarily needs to be implemented sequentially, an
|
||||
|
||||
| Deployment Platform | Local Model Support |
|
||||
| ------------------- | ------------------- |
|
||||
| Ollama | Yes |
|
||||
| GoogleAI | No |
|
||||
| Anthropic | No |
|
||||
| Amazon | No |
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: "Feature Branch Strategy", because it allows individual features to be developed in isolation, minimizing conflicts with the main branch and facilitating easier code reviews.
|
||||
|
||||
## Fequent Asked Questions
|
||||
|
||||
### Is there a migration strategy for initiatives that followed the old contribution way with forks, and now have to switch to branches in microsoft/semantic-kernel?
|
||||
|
||||
You proceed normally with the fork and PR targeting `main`, as soon we identify that your contribution PR to main is a big and desirable feature (Look at the ones we described as expected in this ADR) we will create a dedicated feature branch (`feature-yourfeature`) where you can retarget our forks PR to target it.
|
||||
All further incremental changes and contributions will follow as normal, but instead of `main` you will be targeting the `feature-*` branch.
|
||||
|
||||
### How do you want to solve the "up to date with main branch" problem?
|
||||
|
||||
This will happen when we all agreed that the current feature implementation is complete and ready to merge in `main`.
|
||||
|
||||
As soon the feature is finished, a merge from main will be pushed into the feature branch.
|
||||
This will normally trigger the conflicts that need to be sorted.
|
||||
That normally will be the last PR targeting the feature branch which will be followed right away by another PR from the `feature` branch targeting `main` with minimal conflicts if any.
|
||||
The merging to main might be fast (as all the intermediate feature PRs were all agreed and approved before)
|
||||
@@ -0,0 +1,276 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: proposed
|
||||
contact: SergeyMenshykh
|
||||
date: 2024-01-24
|
||||
deciders: markwallace-microsoft, matthewbolanos
|
||||
consulted: rogerbarreto, dmytrostruk
|
||||
informed:
|
||||
---
|
||||
|
||||
# SK Agents Overview and High Level Design
|
||||
|
||||
## Context and Problem Statement
|
||||
Currently, agents in SK .NET are represented by the [IAgent](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/Experimental/Agents/IAgent.cs) interface, the [Agent](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/Experimental/Agents/Internal/Agent.cs) class, and a set of classes in the [Agents folder](https://github.com/microsoft/semantic-kernel/tree/main/dotnet/src/Experimental/Agents). These classes enable agent communication with the OpenAI Assistant API, agent collaboration, and more.
|
||||
|
||||
The [Agent](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/Experimental/Agents/Internal/Agent.cs) class is the only implementation of an agent in SK that utilizes the [OpenAI Assistant API](https://platform.openai.com/docs/assistants/how-it-works). It accomplishes this through a series of abstractions, which are implemented as wrappers around the OpenAI Assistant API, hiding the complexity and details of HTTP calls to the OpenAI API.
|
||||
|
||||
The [IAgent](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/Experimental/Agents/IAgent.cs) interface is implemented by the Agent class. This interface is intended to be implemented by all the future agents. Its design was shaped around the OpenAI Assistant API and contains several members that might not be relevant to all agents, such as NewThreadAsync, AddFileAsync, FileIds, etc. This can make it more challenging to use the interface when building custom agents.
|
||||
|
||||
Current agent functionality lacks the necessary building blocks and a unified agent interface, which would enable quick and easy configuration of how agents collaborate. For example, it would be beneficial to create several agents, add them to an agent chat, and ask them to collaborate on solving a task at hand without writing any code to facilitate the collaboration. In cases where turn-based communication between two or more agents is required, it should be a matter of simply adding the agents to the agent turn-based chat to enable their collaboration.
|
||||
|
||||
## Out of Scope
|
||||
This ADR outlines a high-level design for SK agents, with the primary goal of getting everyone on the same page regarding potential implementations and gathering early feedback to determine the next steps. As such, the design for certain aspects of agents may be omitted and completed as part of separate ARDs or PRs, depending on the complexity of the area.
|
||||
|
||||
## Decision Drivers
|
||||
- SK should provide sufficient abstraction to enable the construction of agents that could utilize potentially any LLM API.
|
||||
- SK should provide sufficient abstraction and building blocks for the most frequent types of agent collaboration. It should be easy to add new blocks as new collaboration methods emerge.
|
||||
- SK should provide building blocks to modify agents' input and output to cover various customization scenarios.
|
||||
- SK agents' input and output should be represented by the same data type, making it easy to pass the output of one agent as input to another.
|
||||
- SK agents should leverage all SK tools DI, plugins, function-calling, etc.
|
||||
- SK agents API should be as simple as possible so that other libraries can build their agents on top of it if needed.
|
||||
|
||||
## Agents Overview
|
||||
What is agent? An agent is a class that has the following attributes and abilities:
|
||||
- Name: Allows the agent to impersonate a persona.
|
||||
- Tools/Functions: Enables the agent to perform specific tasks or actions.
|
||||
- System Instructions: A set of directives that guide the agent's behavior.
|
||||
- Settings: Agent specific settings including LLM settings - such as Temperature, TopP, StopSequence, etc
|
||||
- Interaction: It's possible to interact with the agent by sending and receiving messages.
|
||||
|
||||
## OpenAI Assistant API
|
||||
|
||||
<img src="./diagrams/open-ai-assistant-api-objects.png" alt="OpenAI Assistant API Objects.png" width="700"/>
|
||||
|
||||
[Source](https://platform.openai.com/docs/assistants/how-it-works/objects)
|
||||
|
||||
[Playground](https://platform.openai.com/playground)
|
||||
|
||||
## Unified Interface And Data Contract
|
||||
To enable collaboration and customization scenarios, further details of which will be provided below, agents should have a unified interface. They should inherit from the same abstract Agent class and have one data type - AgentMessage, for both input and output data contracts. This will enable a very powerful and extensible model, allowing the addition of any agent to any chat/collaboration, regardless of its implementation, and enabling a seamless chat conversation where the agent's output message is added to the chat without any conversion, and the chat is sent as input for the next agent without any conversion as well.
|
||||
|
||||
For example, the scenario where two agents need to communicate together can be configured like this:
|
||||
```C#
|
||||
class AgentTurnBasedChat(Agent[] agents)
|
||||
{
|
||||
async Task<AgentMessage[]> StartConversationAsync(AgentMessage[] messages)
|
||||
{
|
||||
var chat = new List<AgentMessage>(messages);
|
||||
|
||||
var nextAgentIndex = 0;
|
||||
|
||||
while (chatExitCondition)
|
||||
{
|
||||
var nextAgent = agents[nextAgentIndex];
|
||||
|
||||
var result = await nextAgent.InvokeAsync(chat); // Agents accept a list of the 'AgentMessage' class, so chat can be passed without any conversion required.
|
||||
|
||||
chat.AddRange(result); // The agent's result can be added back to the chat as is, without any conversion needed.
|
||||
|
||||
nextAgentIndex = ...;
|
||||
}
|
||||
|
||||
return chat.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Agent copywriter = new ChatCompletionAgent(name: "Mike", instructions: "You're a helpful ....", ...); // Agent using the Chat Completion API
|
||||
|
||||
Agent artDirector = new OpenAIAssistant(name: "Roy", instructions: ...); // Agent using OpenAI Assistance API.
|
||||
|
||||
AgentTurnBasedChat chat = new(agents: new {copywriter, artDirector}); // Any agent can be added to the chat as long as it inherits from the 'Agent' class.
|
||||
|
||||
var result = await chat.StartConversationAsync(new[] { new AgentMessage(role: "user", "collaborate on advertising campaigns for out latest product ....") });
|
||||
|
||||
```
|
||||
Pros:
|
||||
- No changes are needed for existing agent chat/collaboration classes to accommodate new agents.
|
||||
- No custom logic is required in the chat/collaboration classes to support new agents.
|
||||
|
||||
Cons:
|
||||
- Some features of certain agents might not be fully utilized, as chat/collaboration classes are only aware of the unified interface supported by all agents.
|
||||
|
||||
## Agent Collaboration
|
||||
There are three identified methods for agents to collaborate with each other so far. As new collaboration strategies are discovered, it should be relatively easy to support them. One prerequisite for this is to ensure that agents remain unaware of the conversations they participate in, maintaining a one-way dependency - the chat/collaboration blocks know about the agents, but the agents do not know about them.
|
||||
|
||||
### Collaboration Chat
|
||||
This collaboration strategy assumes the presence of a facilitator/coordinator agent - an admin, along with agent participants. All agents collaborate in the same chat, following the order determined by the admin agent. The admin agent decides on the next agent each time the chat is updated (new messages returned by an agent are added to the chat) by asking LLM to select the next agent based on chat history and the list of agents. The conversation continues until the exit condition(can be configured on the admin agent using the customization blocks described below) is met or the maximum number of turns is reached.
|
||||
|
||||
```C#
|
||||
class AgentCollaborationChat(Agent admin, Agent[] participants)
|
||||
{
|
||||
async Task<AgentMessage[]> StartConversationAsync(AgentMessage[] messages)
|
||||
{
|
||||
var chat = new List<AgentMessage>(messages);
|
||||
|
||||
while (chatExitCondition)
|
||||
{
|
||||
var nextAgent = GetNextAgent(chat); // Admin agent decides on the next best agent to continue the conversation.
|
||||
|
||||
var result = await nextAgent.InvokeAsync(chat);
|
||||
|
||||
chat.AddRange(result);
|
||||
}
|
||||
|
||||
return chat.ToArray();
|
||||
}
|
||||
|
||||
private Agent GetNextAgent(IList<AgentMessage> chat)
|
||||
{
|
||||
var agentNames = string.Join(",", participants.Select(p => p.Name));
|
||||
|
||||
var ask = new AgentMessage(role: "assistant", content: $"Identify the next agent based on the chat history and the list of agents - {agentNames}.");
|
||||
|
||||
var response = admin.InvokeAsync(chat.Concat(new[] { ask }));
|
||||
|
||||
return participants.Single(p => p.Name == response.Content);
|
||||
}
|
||||
}
|
||||
|
||||
Agent projectManager = new ChatCompletionAgent(name: "Mike", instructions: "You're a PM working on the new TODO app ....", ...);
|
||||
|
||||
Agent designer = new ChatCompletionAgent(name: "Peter", instructions: "You're a UI/UX designer ....", ...);
|
||||
|
||||
Agent engineer = new OpenAIAssistant(name: "Roy", instructions: "You are an engineer with front-end skills ...");
|
||||
|
||||
AgentCollaborationChat chat = new(admin: projectManager, participants: new {designer, engineer});
|
||||
|
||||
var result = await chat.StartConversationAsync(new[] { new AgentMessage(role: "user", content: "collaborate on a new user experience for the 'Add item' feature.") });
|
||||
|
||||
```
|
||||
|
||||
### Turn-Based Chat
|
||||
The turn-based collaboration strategy involves agents taking turns in a conversation, following a predetermined order. Each agent is invoked after the previous one, continuing until the exit condition is met or the maximum number of turns is reached. Once the last agent has finished, the turn is given back to the first agent in the sequence.
|
||||
|
||||
```C#
|
||||
class AgentTurnBasedChat(Agent[] agents)
|
||||
{
|
||||
async Task<AgentMessage[]> StartConversationAsync(AgentMessage[] messages)
|
||||
{
|
||||
var chat = new List<AgentMessage>(messages);
|
||||
|
||||
var nextAgentIndex = 0;
|
||||
|
||||
while (chatExitCondition)
|
||||
{
|
||||
var nextAgent = agents[nextAgentIndex];
|
||||
|
||||
var result = await nextAgent.InvokeAsync(chat);
|
||||
|
||||
chat.AddRange(result);
|
||||
|
||||
nextAgentIndex = (nextAgentIndex + 1) % agents.Count()
|
||||
}
|
||||
|
||||
return chat.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Agent copywriter = new ChatCompletionAgent(name: "Mike", instructions: "You're a helpful ....", ...);
|
||||
|
||||
Agent artDirector = new OpenAIAssistant(name: "Roy", instructions: ...);
|
||||
|
||||
AgentTurnBasedChat chat = new(agents: new {copywriter, artDirector});
|
||||
|
||||
var result = await chat.StartConversationAsync(new[] { new AgentMessage(role: "user", "collaborate on advertising campaigns for out latest product ....") });
|
||||
|
||||
```
|
||||
|
||||
### Agents As Plugins
|
||||
This type of collaboration more closely resembles a delegation method of communication, as the agents are not collaborating in a chat but rather "delegating" by having one agent call the others as functions.
|
||||
```C#
|
||||
Agent designer = new ChatCompletionAgent(name: "Peter", instructions: "You're a UI/UX designer ....", ...);
|
||||
|
||||
Agent engineer = new OpenAIAssistant(name: "Roy", instructions: "You are an engineer with front-end skills ...");
|
||||
|
||||
Agent projectManager = new ChatCompletionAgent(name: "Mike", instructions: "You're a PM working on the new TODO app ....", ...);
|
||||
projectManager.Plugins.Add(designer.AsPlugin());
|
||||
projectManager.Plugins.Add(engineer.AsPlugin());
|
||||
|
||||
var result = await projectManager.InvokeAsync(new[] { new AgentMessage(role: "user", content: "Work with the design and engineering teams to produce a draft version of 'Add Item' UI.") });
|
||||
```
|
||||
|
||||
Similarly, since agents can be represented as plugins, nothing prevents registering them as plugins on the Kernel so that it calls the agents as it would call any other plugin/function if necessary.
|
||||
```C#
|
||||
Agent designer = new ChatCompletionAgent(name: "Peter", instructions: "You're a UI/UX designer ....", ...);
|
||||
|
||||
Agent engineer = new OpenAIAssistant(name: "Roy", instructions: "You are an engineer with front-end skills ...");
|
||||
|
||||
Kernel kernel = Kernel.CreateBuilder().Build();
|
||||
kernel.Plugins.Add(designer.AsPlugin());
|
||||
kernel.Plugins.Add(engineer.AsPlugin());
|
||||
|
||||
var result = await kernel.InvokePromptAsync("Work with the design and engineering teams to produce a draft version of 'Add Item' UI.")
|
||||
```
|
||||
|
||||
## Agent Customization & Filters
|
||||
To cover complex agent collaboration scenarios, it might be necessary to modify agents' input and/or output messages as they travel to and from Agents. This may be useful for various scenarios, such as converting agents' message content from one format/type to another. There could be situations when messages should not be propagated to Agents or not added to the collaboration chat. For example, in the scenario above where the PM, designer, and engineer collaborate on a new experience for the TODO app, the PM's behavior could be extended to generate a chat exit signal based on whether the function used by the agent indicates that the new 'Add item' user experience is good enough as POC:
|
||||
|
||||
```C#
|
||||
class AgentCollaborationChat(Agent admin, Agent[] participants)
|
||||
{
|
||||
async Task<AgentMessage[]> StartConversationAsync(AgentMessage[] messages)
|
||||
{
|
||||
...
|
||||
while (chatExitCondition)
|
||||
{
|
||||
...
|
||||
var result = await nextAgent.InvokeAsync(chat);
|
||||
...
|
||||
chatExitCondition == string.Contains(result.Content, "exit_chat") // The chat can be parameterized with agent condition or callback.
|
||||
}
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
Agent projectManager = new ChatCompletionAgent(name: "Mike", instructions: "You're a PM working on the new TODO app ....", ...);
|
||||
projectManager.Plugins.AddFromType<UIUsabilityAssessor>()
|
||||
projectManager = projectManager.PostProcess((reply) => {
|
||||
if(reply.IsFunctionCall)
|
||||
{
|
||||
if(reply.FunctionName == "EvaluateUsability")
|
||||
{
|
||||
if(reply.FunctionResult.IsCoreFunctionalityAccessible)
|
||||
{
|
||||
return new [] { new AgentMessage(role: "system", content: "exit_chat") };
|
||||
}
|
||||
|
||||
return new [] { new AgentMessage(role: "system", content: "Peter/designer please improve the design so that core functionality is accessible.") };
|
||||
}
|
||||
}
|
||||
return reply;
|
||||
});
|
||||
|
||||
Agent designer = new ChatCompletionAgent(name: "Peter", instructions: "You're a UI/UX designer ....", ...);
|
||||
|
||||
Agent engineer = new OpenAIAssistant(name: "Roy", instructions: "You are an engineer with front-end skills ...");
|
||||
|
||||
AgentCollaborationChat chat = new(admin: projectManager, participants: new {designer, engineer});
|
||||
|
||||
var result = await chat.StartConversationAsync(new[] { new AgentMessage(role: "user", content: "collaborate on a new user experience for the 'Add item' feature.") });
|
||||
```
|
||||
Some scenarios can be implemented without filters by just plugins. However, this may require extra hops to LLMs and prompt tuning to have those scenarios working.
|
||||
|
||||
Filter examples:
|
||||
- PostProcess: Accepts an agent and a delegate capable of modifying or replacing messages returned by the agent. It calls the agent and then calls the delegate with the agent's response messages. Finally, it returns the modified messages to the caller.
|
||||
- PreProcess: Accepts an agent and a delegate capable of modifying or replacing messages to be passed to the agent. It calls the callback to handle the messages and pass the modified messages to the agent. Returns the agent result to the caller.
|
||||
|
||||
Each of the filter can be implemented as a decorator pattern that will allow to build agents pipelines/chains:
|
||||
```C#
|
||||
var agent = new ChatCompletionAgent(...)
|
||||
.PostProcess((m) => { Console.WriteLine($"Agent response: {m.Content}"); return m; })
|
||||
.PreProcess(m => { Console.WriteLine($"User input: {m.Content}"); return m; });
|
||||
|
||||
await agent.InvokeAsync(...);
|
||||
|
||||
```
|
||||
|
||||
SK, today, already has the concept of filters for [prompts](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel.Abstractions/Filters/Prompt/IPromptFilter.cs) and [functions](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel.Abstractions/Filters/Function/IFunctionFilter.cs). Ideally, the same approach should be taken for Agent filters.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: "{title of option 1}", because
|
||||
{justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}.
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: accepted
|
||||
contact: dmytrostruk
|
||||
date: 2023-01-23
|
||||
deciders: sergeymenshykh, markwallace, rbarreto, stephentoub, dmytrostruk
|
||||
---
|
||||
|
||||
# Kernel Filters
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
Current way of intercepting some event during function execution works as expected using Kernel Events and event handlers. Example:
|
||||
|
||||
```csharp
|
||||
ILogger logger = loggerFactory.CreateLogger("MyLogger");
|
||||
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion(
|
||||
modelId: TestConfiguration.OpenAI.ChatModelId,
|
||||
apiKey: TestConfiguration.OpenAI.ApiKey)
|
||||
.Build();
|
||||
|
||||
void MyInvokingHandler(object? sender, FunctionInvokingEventArgs e)
|
||||
{
|
||||
logger.LogInformation("Invoking: {FunctionName}", e.Function.Name)
|
||||
}
|
||||
|
||||
void MyInvokedHandler(object? sender, FunctionInvokedEventArgs e)
|
||||
{
|
||||
if (e.Result.Metadata is not null && e.Result.Metadata.ContainsKey("Usage"))
|
||||
{
|
||||
logger.LogInformation("Token usage: {TokenUsage}", e.Result.Metadata?["Usage"]?.AsJson());
|
||||
}
|
||||
}
|
||||
|
||||
kernel.FunctionInvoking += MyInvokingHandler;
|
||||
kernel.FunctionInvoked += MyInvokedHandler;
|
||||
|
||||
var result = await kernel.InvokePromptAsync("How many days until Christmas? Explain your thinking.")
|
||||
```
|
||||
|
||||
There are a couple of problems with this approach:
|
||||
|
||||
1. Event handlers does not support dependency injection. It's hard to get access to specific service, which is registered in application, unless the handler is defined in the same scope where specific service is available. This approach provides some limitations in what place in solution the handler could be defined. (e.g. If developer wants to use `ILoggerFactory` in handler, the handler should be defined in place where `ILoggerFactory` instance is available).
|
||||
2. It's not clear in what specific period of application runtime the handler should be attached to kernel. Also, it's not clear if developer needs to detach it at some point.
|
||||
3. Mechanism of events and event handlers in .NET may not be familiar to .NET developers who didn't work with events previously.
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
1. Dependency injection for handlers should be supported to easily access registered services within application.
|
||||
2. There should not be any limitations where handlers are defined within solution, whether it's Startup.cs or separate file.
|
||||
3. There should be clear way of registering and removing handlers at specific point of application runtime.
|
||||
4. The mechanism of receiving and processing events in Kernel should be easy and common in .NET ecosystem.
|
||||
5. New approach should support the same functionality that is available in Kernel Events - cancel function execution, change kernel arguments, change rendered prompt before sending it to AI etc.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Introduce Kernel Filters - the approach of receiving the events in Kernel in similar way as action filters in ASP.NET.
|
||||
|
||||
Two new abstractions will be used across Semantic Kernel and developers will have to implement these abstractions in a way that will cover their needs.
|
||||
|
||||
For function-related events: `IFunctionFilter`
|
||||
|
||||
```csharp
|
||||
public interface IFunctionFilter
|
||||
{
|
||||
void OnFunctionInvoking(FunctionInvokingContext context);
|
||||
|
||||
void OnFunctionInvoked(FunctionInvokedContext context);
|
||||
}
|
||||
```
|
||||
|
||||
For prompt-related events: `IPromptFilter`
|
||||
|
||||
```csharp
|
||||
public interface IPromptFilter
|
||||
{
|
||||
void OnPromptRendering(PromptRenderingContext context);
|
||||
|
||||
void OnPromptRendered(PromptRenderedContext context);
|
||||
}
|
||||
```
|
||||
|
||||
New approach will allow developers to define filters in separate classes and easily inject required services to process kernel event correctly:
|
||||
|
||||
MyFunctionFilter.cs - filter with the same logic as event handler presented above:
|
||||
|
||||
```csharp
|
||||
public sealed class MyFunctionFilter : IFunctionFilter
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MyFunctionFilter(ILoggerFactory loggerFactory)
|
||||
{
|
||||
this._logger = loggerFactory.CreateLogger("MyLogger");
|
||||
}
|
||||
|
||||
public void OnFunctionInvoking(FunctionInvokingContext context)
|
||||
{
|
||||
this._logger.LogInformation("Invoking {FunctionName}", context.Function.Name);
|
||||
}
|
||||
|
||||
public void OnFunctionInvoked(FunctionInvokedContext context)
|
||||
{
|
||||
var metadata = context.Result.Metadata;
|
||||
|
||||
if (metadata is not null && metadata.ContainsKey("Usage"))
|
||||
{
|
||||
this._logger.LogInformation("Token usage: {TokenUsage}", metadata["Usage"]?.AsJson());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As soon as new filter is defined, it's easy to configure it to be used in Kernel using dependency injection (pre-construction) or add filter after Kernel initialization (post-construction):
|
||||
|
||||
```csharp
|
||||
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
|
||||
kernelBuilder.AddOpenAIChatCompletion(
|
||||
modelId: TestConfiguration.OpenAI.ChatModelId,
|
||||
apiKey: TestConfiguration.OpenAI.ApiKey);
|
||||
|
||||
// Adding filter with DI (pre-construction)
|
||||
kernelBuilder.Services.AddSingleton<IFunctionFilter, MyFunctionFilter>();
|
||||
|
||||
Kernel kernel = kernelBuilder.Build();
|
||||
|
||||
// Adding filter after Kernel initialization (post-construction)
|
||||
// kernel.FunctionFilters.Add(new MyAwesomeFilter());
|
||||
|
||||
var result = await kernel.InvokePromptAsync("How many days until Christmas? Explain your thinking.");
|
||||
```
|
||||
|
||||
It's also possible to configure multiple filters which will be triggered in order of registration:
|
||||
|
||||
```csharp
|
||||
kernelBuilder.Services.AddSingleton<IFunctionFilter, Filter1>();
|
||||
kernelBuilder.Services.AddSingleton<IFunctionFilter, Filter2>();
|
||||
kernelBuilder.Services.AddSingleton<IFunctionFilter, Filter3>();
|
||||
```
|
||||
|
||||
And it's possible to change the order of filter execution in runtime or remove specific filter if needed:
|
||||
|
||||
```csharp
|
||||
kernel.FunctionFilters.Insert(0, new InitialFilter());
|
||||
kernel.FunctionFilters.RemoveAt(1);
|
||||
```
|
||||
@@ -0,0 +1,299 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: proposed
|
||||
contact: dmytrostruk
|
||||
date: 2023-01-29
|
||||
deciders: sergeymenshykh, markwallace, rbarreto, dmytrostruk
|
||||
---
|
||||
|
||||
# Retrieval-Augmented Generation (RAG) in Semantic Kernel
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
### General information
|
||||
|
||||
There are several ways how to use RAG pattern in Semantic Kernel (SK). Some of the approaches already exist in SK, and some of them could be added in the future for diverse development experience.
|
||||
|
||||
The purpose of this ADR is to describe problematic places with memory-related functionality in SK, demonstrate how to achieve RAG in current version of SK and propose new design of public API for RAG.
|
||||
|
||||
Considered options, that are presented in this ADR, do not contradict each other and can be supported all at the same time. The decision which option to support will be based on different factors including priority, actual requirement for specific functionality and general feedback.
|
||||
|
||||
### Vector DB integrations - Connectors
|
||||
|
||||
There are 12 [vector DB connectors](https://github.com/microsoft/semantic-kernel/tree/main/dotnet/src/Connectors) (also known as `memory connectors`) implemented at the moment, and it may be unclear for developers how to use them. It's possible to call connector methods directly or use it via `TextMemoryPlugin` from [Plugins.Memory](https://www.nuget.org/packages/Microsoft.SemanticKernel.Plugins.Memory) NuGet package (prompt example: `{{recall 'company budget by year'}} What is my budget for 2024?`)
|
||||
|
||||
Each connector has unique implementation, some of them rely on already existing .NET SDK from specific vector DB provider, and some of them have implemented functionality to use REST API of vector DB provider.
|
||||
|
||||
Ideally, each connector should be always up-to-date and support new functionality. For some connectors maintenance cost is low, since there are no breaking changes included in new features or vector DB provides .NET SDK which is relatively easy to re-use. For other connectors maintenance cost is high, since some of them are still in `alpha` or `beta` development stage, breaking changes can be included or .NET SDK is not provided, which makes it harder to update.
|
||||
|
||||
### IMemoryStore interface
|
||||
|
||||
Each memory connector implements `IMemoryStore` interface with methods like `CreateCollectionAsync`, `GetNearestMatchesAsync` etc., so it can be used as part of `TextMemoryPlugin`.
|
||||
|
||||
By implementing the same interface, each integration is aligned, which makes it possible to use different vector DBs at runtime. At the same time it is disadvantage, because each vector DB can work differently, and it becomes harder to fit all integrations into already existing abstraction. For example, method `CreateCollectionAsync` from `IMemoryStore` is used when application tries to add new record to vector DB to the collection, which doesn't exist, so before insert operation, it creates new collection. In case of [Pinecone](https://www.pinecone.io/) vector DB, this scenario is not supported, because Pinecone index creation is an asynchronous process - API service will return 201 Created HTTP response with following property in response body (index is not ready for usage):
|
||||
|
||||
```json
|
||||
{
|
||||
// Other properties...
|
||||
"status": {
|
||||
"ready": false,
|
||||
"state": "Initializing"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this case, it's impossible to insert a record to database immediately, so HTTP polling or similar mechanism should be implemented to cover this scenario.
|
||||
|
||||
### MemoryRecord as storage schema
|
||||
|
||||
`IMemoryStore` interface uses `MemoryRecord` class as storage schema in vector DB. This means that `MemoryRecord` properties should be aligned to all possible connectors. As soon as developers will use this schema in their databases, any changes to schema may break the application, which is not a flexible approach.
|
||||
|
||||
`MemoryRecord` contains property `ReadOnlyMemory<float> Embedding` for embeddings and `MemoryRecordMetadata Metadata` for embeddings metadata. `MemoryRecordMetadata` contains properties like:
|
||||
|
||||
- `string Id` - unique identifier.
|
||||
- `string Text` - data-related text.
|
||||
- `string Description` - optional title describing the content.
|
||||
- `string AdditionalMetadata` - field for saving custom metadata with a record.
|
||||
|
||||
Since `MemoryRecord` and `MemoryRecordMetadata` are not sealed classes, it should be possible to extend them and add more properties as needed. Although, current approach still forces developers to have specific base schema in their vector DBs, which ideally should be avoided. Developers should have the ability to work with any schema of their choice, which will cover their business scenarios (similarly to Code First approach in Entity Framework).
|
||||
|
||||
### TextMemoryPlugin
|
||||
|
||||
TextMemoryPlugin contains 4 Kernel functions:
|
||||
|
||||
- `Retrieve` - returns concrete record from DB by key.
|
||||
- `Recall` - performs vector search and returns multiple records based on relevance.
|
||||
- `Save` - saves record in vector DB.
|
||||
- `Remove` - removes record from vector DB.
|
||||
|
||||
All functions can be called directly from prompt. Moreover, as soon as these functions are registered in Kernel and Function Calling is enabled, LLM may decide to call specific function to achieve provided goal.
|
||||
|
||||
`Retrieve` and `Recall` functions are useful to provide some context to LLM and ask a question based on data, but functions `Save` and `Remove` perform some manipulations with data in vector DB, which could be unpredicted or sometimes even dangerous (there should be no situations when LLM decides to remove some records, which shouldn't be deleted).
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
1. All manipulations with data in Semantic Kernel should be safe.
|
||||
2. There should be a clear way(s) how to use RAG pattern in Semantic Kernel.
|
||||
3. Abstractions should not block developers from using vector DB of their choice with functionality, that cannot be achieved with provided interfaces or data types.
|
||||
|
||||
## Out of scope
|
||||
|
||||
Some of the RAG-related frameworks contain functionality to support full cycle of RAG pattern:
|
||||
|
||||
1. **Read** data from specific resource (e.g. Wikipedia, OneDrive, local PDF file).
|
||||
2. **Split** data in multiple chunks using specific logic.
|
||||
3. **Generate** embeddings from data.
|
||||
4. **Store** data to preferred vector DB.
|
||||
5. **Search** data in preferred vector DB based on user query.
|
||||
6. **Ask** LLM a question based on provided data.
|
||||
|
||||
As for now, Semantic Kernel has following experimental features:
|
||||
|
||||
- `TextChunker` class to **split** data in chunks.
|
||||
- `ITextEmbeddingGenerationService` abstraction and implementations to **generate** embeddings using OpenAI and HuggingFace models.
|
||||
- Memory connectors to **store** and **search** data.
|
||||
|
||||
Since these features are experimental, they may be deprecated in the future if the decisions for RAG pattern won't require to provide and maintain listed abstractions, classes and connectors in Semantic Kernel.
|
||||
|
||||
Tools for data **reading** is out of scope as for now.
|
||||
|
||||
## Considered Options
|
||||
|
||||
### Option 1 [Supported] - Prompt concatenation
|
||||
|
||||
This option allows to manually construct a prompt with data, so LLM can respond to query based on provided context. It can be achieved by using manual string concatenation or by using prompt template and Kernel arguments. Developers are responsible for integration with vector DB of their choice, data search and prompt construction to send it to LLM.
|
||||
|
||||
This approach doesn't include any memory connectors in Semantic Kernel out-of-the-box, but at the same time it gives an opportunity for developers to handle their data in the way that works for them the best.
|
||||
|
||||
String concatenation:
|
||||
|
||||
```csharp
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion("model-id", "api-key")
|
||||
.Build();
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
// User is responsible for searching the data in a way of their choice, this is an example how it could look like.
|
||||
var data = await this._vectorDB.SearchAsync("Company budget by year");
|
||||
|
||||
builder.AppendLine(data);
|
||||
builder.AppendLine("What is my budget for 2024?");
|
||||
|
||||
var result = await kernel.InvokePromptAsync(builder.ToString());
|
||||
```
|
||||
|
||||
Prompt template and Kernel arguments:
|
||||
|
||||
```csharp
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion("model-id", "api-key")
|
||||
.Build();
|
||||
|
||||
// User is responsible for searching the data in a way of their choice, this is an example how it could look like.
|
||||
var data = await this._vectorDB.SearchAsync("Company budget by year");
|
||||
|
||||
var arguments = new KernelArguments { ["budgetByYear"] = data };
|
||||
|
||||
var result = await kernel.InvokePromptAsync("{{budgetByYear}} What is my budget for 2024?", arguments);
|
||||
```
|
||||
|
||||
### Option 2 [Supported] - Memory as Plugin
|
||||
|
||||
This approach is similar to Option 1, but data search step is part of prompt rendering process. Following list contains possible plugins to use for data search:
|
||||
|
||||
- [ChatGPT Retrieval Plugin](https://github.com/openai/chatgpt-retrieval-plugin) - this plugin should be hosted as a separate service. It has integration with various [vector databases](https://github.com/openai/chatgpt-retrieval-plugin?tab=readme-ov-file#choosing-a-vector-database).
|
||||
- [SemanticKernel.Plugins.Memory.TextMemoryPlugin](https://www.nuget.org/packages/Microsoft.SemanticKernel.Plugins.Memory) - Semantic Kernel solution, which supports various [vector databases](https://learn.microsoft.com/en-us/semantic-kernel/memories/vector-db#available-connectors-to-vector-databases).
|
||||
- Custom user plugin.
|
||||
|
||||
ChatGPT Retrieval Plugin:
|
||||
|
||||
```csharp
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion("model-id", "api-key")
|
||||
.Build();
|
||||
|
||||
// Import ChatGPT Retrieval Plugin using OpenAPI specification
|
||||
// https://github.com/openai/chatgpt-retrieval-plugin/blob/main/.well-known/openapi.yaml
|
||||
await kernel.ImportPluginFromOpenApiAsync("ChatGPTRetrievalPlugin", openApi!, executionParameters: new(authCallback: async (request, cancellationToken) =>
|
||||
{
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "chat-gpt-retrieval-plugin-token");
|
||||
}));
|
||||
|
||||
const string Query = "What is my budget for 2024?";
|
||||
const string Prompt = "{{ChatGPTRetrievalPlugin.query_query_post queries=$queries}} {{$query}}";
|
||||
|
||||
var arguments = new KernelArguments
|
||||
{
|
||||
["query"] = Query,
|
||||
["queries"] = JsonSerializer.Serialize(new List<object> { new { query = Query, top_k = 1 } }),
|
||||
};
|
||||
|
||||
var result = await kernel.InvokePromptAsync(Prompt, arguments);
|
||||
```
|
||||
|
||||
TextMemoryPlugin:
|
||||
|
||||
```csharp
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion("model-id", "api-key")
|
||||
.Build();
|
||||
|
||||
// NOTE: If the decision will be to continue support memory-related public API, then it should be revisited.
|
||||
// It should be up-to-date with new Semantic Kernel patterns.
|
||||
// Example: instead of `WithChromaMemoryStore`, it should be `AddChromaMemoryStore`.
|
||||
var memory = new MemoryBuilder()
|
||||
.WithChromaMemoryStore("https://chroma-endpoint")
|
||||
.WithOpenAITextEmbeddingGeneration("text-embedding-ada-002", "api-key")
|
||||
.Build();
|
||||
|
||||
kernel.ImportPluginFromObject(new TextMemoryPlugin(memory));
|
||||
|
||||
var result = await kernel.InvokePromptAsync("{{recall 'Company budget by year'}} What is my budget for 2024?");
|
||||
```
|
||||
|
||||
Custom user plugin:
|
||||
|
||||
```csharp
|
||||
public class MyDataPlugin
|
||||
{
|
||||
[KernelFunction("search")]
|
||||
public async Task<string> SearchAsync(string query)
|
||||
{
|
||||
// Make a call to vector DB and return results.
|
||||
// Here developer can use already existing .NET SDK from specific vector DB provider.
|
||||
// It's also possible to re-use Semantic Kernel memory connector directly here:
|
||||
// new ChromaMemoryStore(...).GetNearestMatchAsync(...)
|
||||
}
|
||||
}
|
||||
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion("model-id", "api-key")
|
||||
.Build();
|
||||
|
||||
kernel.ImportPluginFromType<MyDataPlugin>();
|
||||
|
||||
var result = await kernel.InvokePromptAsync("{{search 'Company budget by year'}} What is my budget for 2024?");
|
||||
```
|
||||
|
||||
The reason why custom user plugin is more flexible than `TextMemoryPlugin` is because `TextMemoryPlugin` requires all vector DBs to implement `IMemoryStore` interface with disadvantages described above, while custom user plugin can be implemented in a way of developer's choice. There won't be any restrictions on DB record schema or requirement to implement specific interface.
|
||||
|
||||
### Option 3 [Partially supported] - Prompt concatenation using Prompt Filter
|
||||
|
||||
This option is similar to Option 1, but prompt concatenation will happen on Prompt Filter level:
|
||||
|
||||
Prompt filter:
|
||||
|
||||
```csharp
|
||||
public sealed class MyPromptFilter : IPromptFilter
|
||||
{
|
||||
public void OnPromptRendering(PromptRenderingContext context)
|
||||
{
|
||||
// Handling of prompt rendering event...
|
||||
}
|
||||
|
||||
public void OnPromptRendered(PromptRenderedContext context)
|
||||
{
|
||||
var data = "some data";
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine(data);
|
||||
builder.AppendLine(context.RenderedPrompt);
|
||||
|
||||
// Override rendered prompt before sending it to AI and include data
|
||||
context.RenderedPrompt = builder.ToString();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```csharp
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion("model-id", "api-key")
|
||||
.Build();
|
||||
|
||||
kernel.PromptFilters.Add(new MyPromptFilter());
|
||||
|
||||
var result = await kernel.InvokePromptAsync("What is my budget for 2024?");
|
||||
```
|
||||
|
||||
From the usage perspective, prompt will contain just user query without additional data. The data will be added to the prompt behind the scenes.
|
||||
|
||||
The reason why this approach is **partially supported** is because a call to vector DB most probably will be an asynchronous, but current Kernel filters don't support asynchronous scenarios. So, in order to support asynchronous calls, new type of filters should be added to Kernel: `IAsyncFunctionFilter` and `IAsyncPromptFilter`. They will be the same as current `IFunctionFilter` and `IPromptFilter` but with async methods.
|
||||
|
||||
### Option 4 [Proposal] - Memory as part of PromptExecutionSettings
|
||||
|
||||
This proposal is another possible way how to implement RAG pattern in SK, on top of already existing approaches described above. Similarly to `TextMemoryPlugin`, this approach will require abstraction layer and each vector DB integration will be required to implement specific interface (it could be existing `IMemoryStore` or completely new one) to be compatible with SK. As described in _Context and Problem Statement_ section, the abstraction layer has its advantages and disadvantages.
|
||||
|
||||
User code will look like this:
|
||||
|
||||
```csharp
|
||||
var kernel = Kernel.CreateBuilder()
|
||||
.AddOpenAIChatCompletion("model-id", "api-key")
|
||||
.Build();
|
||||
|
||||
var executionSettings = new OpenAIPromptExecutionSettings
|
||||
{
|
||||
Temperature = 0.8,
|
||||
MemoryConfig = new()
|
||||
{
|
||||
// This service could be also registered using DI with specific lifetime
|
||||
Memory = new ChromaMemoryStore("https://chroma-endpoint"),
|
||||
MinRelevanceScore = 0.8,
|
||||
Limit = 3
|
||||
}
|
||||
};
|
||||
|
||||
var function = KernelFunctionFactory.CreateFromPrompt("What is my budget for 2024?", executionSettings);
|
||||
|
||||
var result = await kernel.InvokePromptAsync("What is my budget for 2024?");
|
||||
```
|
||||
|
||||
Data search and prompt concatenation will happen behind the scenes in `KernelFunctionFromPrompt` class.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Temporary decision is to provide more examples how to use memory in Semantic Kernel as Plugin.
|
||||
|
||||
The final decision will be ready based on next memory-related requirements.
|
||||
@@ -1,31 +1,34 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0001](0001-madr-architecture-decisions.md)}
|
||||
contact: {person proposing the ADR}
|
||||
date: {YYYY-MM-DD when the decision was last updated}
|
||||
deciders: {list everyone involved in the decision}
|
||||
consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication}
|
||||
informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication}
|
||||
---
|
||||
|
||||
# {short title of solved problem and solution}
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
{Describe the context and problem statement, e.g., in free form using two to three sentences or in the form of an illustrative story.
|
||||
You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.}
|
||||
You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.}
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* {decision driver 1, e.g., a force, facing concern, …}
|
||||
* {decision driver 2, e.g., a force, facing concern, …}
|
||||
* … <!-- numbers of drivers can vary -->
|
||||
- {decision driver 1, e.g., a force, facing concern, …}
|
||||
- {decision driver 2, e.g., a force, facing concern, …}
|
||||
- … <!-- numbers of drivers can vary -->
|
||||
|
||||
## Considered Options
|
||||
|
||||
* {title of option 1}
|
||||
* {title of option 2}
|
||||
* {title of option 3}
|
||||
* … <!-- numbers of options can vary -->
|
||||
- {title of option 1}
|
||||
- {title of option 2}
|
||||
- {title of option 3}
|
||||
- … <!-- numbers of options can vary -->
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
|
||||
@@ -1,31 +1,34 @@
|
||||
---
|
||||
# These are optional elements. Feel free to remove any of them.
|
||||
status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0001](0001-madr-architecture-decisions.md)}
|
||||
contact: {person proposing the ADR}
|
||||
date: {YYYY-MM-DD when the decision was last updated}
|
||||
deciders: {list everyone involved in the decision}
|
||||
consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication}
|
||||
informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication}
|
||||
---
|
||||
|
||||
# {short title of solved problem and solution}
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
{Describe the context and problem statement, e.g., in free form using two to three sentences or in the form of an illustrative story.
|
||||
You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.}
|
||||
You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.}
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
* {decision driver 1, e.g., a force, facing concern, …}
|
||||
* {decision driver 2, e.g., a force, facing concern, …}
|
||||
* … <!-- numbers of drivers can vary -->
|
||||
- {decision driver 1, e.g., a force, facing concern, …}
|
||||
- {decision driver 2, e.g., a force, facing concern, …}
|
||||
- … <!-- numbers of drivers can vary -->
|
||||
|
||||
## Considered Options
|
||||
|
||||
* {title of option 1}
|
||||
* {title of option 2}
|
||||
* {title of option 3}
|
||||
* … <!-- numbers of options can vary -->
|
||||
- {title of option 1}
|
||||
- {title of option 2}
|
||||
- {title of option 3}
|
||||
- … <!-- numbers of options can vary -->
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
@@ -33,47 +36,52 @@ Chosen option: "{title of option 1}", because
|
||||
{justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}.
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
### Consequences
|
||||
|
||||
* Good, because {positive consequence, e.g., improvement of one or more desired qualities, …}
|
||||
* Bad, because {negative consequence, e.g., compromising one or more desired qualities, …}
|
||||
* … <!-- numbers of consequences can vary -->
|
||||
- Good, because {positive consequence, e.g., improvement of one or more desired qualities, …}
|
||||
- Bad, because {negative consequence, e.g., compromising one or more desired qualities, …}
|
||||
- … <!-- numbers of consequences can vary -->
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
## Validation
|
||||
|
||||
{describe how the implementation of/compliance with the ADR is validated. E.g., by a review or an ArchUnit test}
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
## Pros and Cons of the Options
|
||||
|
||||
### {title of option 1}
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
{example | description | pointer to more information | …}
|
||||
|
||||
* Good, because {argument a}
|
||||
* Good, because {argument b}
|
||||
- Good, because {argument a}
|
||||
- Good, because {argument b}
|
||||
<!-- use "neutral" if the given argument weights neither for good nor bad -->
|
||||
* Neutral, because {argument c}
|
||||
* Bad, because {argument d}
|
||||
* … <!-- numbers of pros and cons can vary -->
|
||||
- Neutral, because {argument c}
|
||||
- Bad, because {argument d}
|
||||
- … <!-- numbers of pros and cons can vary -->
|
||||
|
||||
### {title of other option}
|
||||
|
||||
{example | description | pointer to more information | …}
|
||||
|
||||
* Good, because {argument a}
|
||||
* Good, because {argument b}
|
||||
* Neutral, because {argument c}
|
||||
* Bad, because {argument d}
|
||||
* …
|
||||
- Good, because {argument a}
|
||||
- Good, because {argument b}
|
||||
- Neutral, because {argument c}
|
||||
- Bad, because {argument d}
|
||||
- …
|
||||
|
||||
<!-- This is an optional element. Feel free to remove. -->
|
||||
|
||||
## More Information
|
||||
|
||||
{You might want to provide additional evidence/confidence for the decision outcome here and/or
|
||||
document the team agreement on the decision and/or
|
||||
define when this decision when and how the decision should be realized and if/when it should be re-visited and/or
|
||||
how the decision is validated.
|
||||
Links to other decisions and resources might appear here as well.}
|
||||
document the team agreement on the decision and/or
|
||||
define when this decision when and how the decision should be realized and if/when it should be re-visited and/or
|
||||
how the decision is validated.
|
||||
Links to other decisions and resources might appear here as well.}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: Chat & Text Models
|
||||
---
|
||||
classDiagram
|
||||
%% Use https://mermaid.live/ to preview this diagram. The VS Code extension does not handle namespaces.
|
||||
direction LR
|
||||
|
||||
namespace Microsoft_SemanticKernel {
|
||||
class KernelContent {
|
||||
<<abstract>>
|
||||
+InnerContent : Object
|
||||
+ModelId : String
|
||||
+Metadata : IDictionary
|
||||
+string(modelContent : KernelContent)
|
||||
}
|
||||
|
||||
class StreamingKernelContent {
|
||||
<<abstract>>
|
||||
+ChoiceIndex : Integer
|
||||
+InnerContent : Object
|
||||
+Metadata : IDictionary
|
||||
+ToString()
|
||||
+ToByteArray()
|
||||
+string(modelContent : StreamingKernelContent)
|
||||
}
|
||||
|
||||
class TextContent {
|
||||
+Text : String
|
||||
+Encoding : Encoding
|
||||
+ToString()
|
||||
}
|
||||
|
||||
class StreamingTextContent {
|
||||
+Text : String
|
||||
+Encoding : Encoding
|
||||
+ToString()
|
||||
+ToByteArray()
|
||||
}
|
||||
|
||||
class ChatMessageContent {
|
||||
+Role : AuthorRole
|
||||
+Content : String
|
||||
+Items : ChatMessageContentItemCollection
|
||||
+Encoding : Encoding
|
||||
+ToString()
|
||||
}
|
||||
|
||||
class StreamingChatMessageContent {
|
||||
+Content : String
|
||||
+Role : AuthorRole
|
||||
+Encoding : Encoding
|
||||
+ToString()
|
||||
+ToByteArray()
|
||||
}
|
||||
|
||||
class ImageContent {
|
||||
+Uri : Uri
|
||||
+ToString()
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft_SemanticKernel_ChatCompletion {
|
||||
class ChatMessageContentItemCollection {
|
||||
+Count
|
||||
+Add(item: KernelContent)
|
||||
}
|
||||
|
||||
class ChatHistory {
|
||||
+AddMessage(chatMessageContent : ChatMessageContent)
|
||||
+AddMessage(authorRole : AuthorRole, content : string, encoding : Encoding, metadata : IDictionary)
|
||||
+AddUserMessage(content : string)
|
||||
+AddAssistantMessage(content : string)
|
||||
+AddSystemMessage(content : string)
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft_SemanticKernel_Connectors_OpenAI {
|
||||
class OpenAIChatMessageContent {
|
||||
+FunctionCall : FunctionCall
|
||||
+Name : Name
|
||||
+GetOpenAIFunctionResponse()
|
||||
}
|
||||
|
||||
class AzureOpenAIWithDataChatMessageContent {
|
||||
+ToolContent : String
|
||||
}
|
||||
|
||||
class OpenAIStreamingTextContent {
|
||||
+ToByteArray()
|
||||
+ToString()
|
||||
}
|
||||
|
||||
class OpenAIStreamingChatMessageContent {
|
||||
+Name : String
|
||||
+FunctionName : String
|
||||
+FunctionArgument : String
|
||||
+ToByteArray()
|
||||
+ToString()
|
||||
+GetOpenAIStreamingFunctionResponse(fullContent : OpenAIStreamingChatMessageContent[])
|
||||
}
|
||||
|
||||
class AzureOpenAIWithDataStreamingChatMessageContent {
|
||||
+FunctionName : String
|
||||
+FunctionArgument : String
|
||||
-IsValidMessage(message : ChatWithDataStreamingMessage)
|
||||
}
|
||||
|
||||
class OpenAIChatHistory {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
KernelContent <|-- TextContent
|
||||
KernelContent <|-- ImageContent
|
||||
KernelContent <|-- ChatMessageContent
|
||||
KernelContent *-- ChatMessageContentItemCollection
|
||||
ChatMessageContent <|-- OpenAIChatMessageContent
|
||||
ChatMessageContent <|-- AzureOpenAIWithDataChatMessageContent
|
||||
StreamingKernelContent <|-- StreamingTextContent
|
||||
StreamingTextContent <|-- OpenAIStreamingTextContent
|
||||
StreamingKernelContent <|-- StreamingChatMessageContent
|
||||
StreamingChatMessageContent <|-- OpenAIStreamingChatMessageContent
|
||||
StreamingChatMessageContent <|-- AzureOpenAIWithDataStreamingChatMessageContent
|
||||
ChatHistory <|-- OpenAIChatHistory
|
||||
ChatMessageContent o-- ChatMessageContentItemCollection
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 191 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 185 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 374 KiB |
@@ -5,68 +5,81 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.8" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.10.3" />
|
||||
<PackageVersion Include="Azure.Search.Documents" Version="11.5.0-beta.5" />
|
||||
<PackageVersion Include="Microsoft.ApplicationInsights.WorkerService" Version="2.21.0" />
|
||||
<PackageVersion Include="Microsoft.Azure.Kusto.Data" Version="11.3.4" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.13" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.10.4" />
|
||||
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.2.0" />
|
||||
<PackageVersion Include="Azure.Search.Documents" Version="11.5.1" />
|
||||
<PackageVersion Include="Handlebars.Net.Helpers" Version="2.4.1.3" />
|
||||
<PackageVersion Include="Markdig" Version="0.34.0" />
|
||||
<PackageVersion Include="Handlebars.Net" Version="2.1.4" />
|
||||
<PackageVersion Include="JsonSchema.Net.Generation" Version="3.5.1" />
|
||||
<PackageVersion Include="Microsoft.Azure.Functions.Worker" Version="1.20.1" />
|
||||
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.1.0" />
|
||||
<PackageVersion Include="Microsoft.Azure.Kusto.Data" Version="11.3.5" />
|
||||
<PackageVersion Include="Microsoft.Azure.WebJobs.Extensions.OpenApi" Version="1.5.1" />
|
||||
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="[1.1.0, )" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Bcl.TimeProvider" Version="8.0.0-rc.2.23479.6" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageVersion Include="Polly" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.3.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0" />
|
||||
<PackageVersion Include="Microsoft.Bcl.TimeProvider" Version="8.0.1" />
|
||||
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Text.Json" Version="6.0.8" />
|
||||
<PackageVersion Include="System.Numerics.Tensors" Version="8.0.0-rc.2.23479.6" />
|
||||
<PackageVersion Include="System.Numerics.Tensors" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Text.Json" Version="8.0.1" />
|
||||
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
|
||||
<!-- Tokenizers -->
|
||||
<PackageVersion Include="Microsoft.ML.Tokenizers" Version="0.21.0-preview.23266.6" />
|
||||
<PackageVersion Include="Microsoft.DeepDev.TokenizerLib" Version="1.3.2" />
|
||||
<PackageVersion Include="SharpToken" Version="1.2.12" />
|
||||
<!-- Microsoft.Extensions.Logging -->
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
|
||||
<!-- Microsoft.Extensions.Configuration -->
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.ML.Tokenizers" Version="0.21.1" />
|
||||
<PackageVersion Include="Microsoft.DeepDev.TokenizerLib" Version="1.3.3" />
|
||||
<PackageVersion Include="SharpToken" Version="1.2.15" />
|
||||
<!-- Microsoft.Extensions.* -->
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.1.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="8.0.0" />
|
||||
<!-- Test -->
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageVersion Include="Moq" Version="[4.18.4]" />
|
||||
<PackageVersion Include="System.Threading.Channels" Version="7.0.0" />
|
||||
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="7.0.0" />
|
||||
<PackageVersion Include="xunit" Version="2.5.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.3" />
|
||||
<PackageVersion Include="System.Threading.Channels" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="8.0.0" />
|
||||
<PackageVersion Include="xunit" Version="2.6.6" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6" />
|
||||
<PackageVersion Include="xretry" Version="1.9.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="8.0.0-rc.2.23510.2" />
|
||||
<!-- Plugins -->
|
||||
<PackageVersion Include="DocumentFormat.OpenXml" Version="2.20.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="7.0.12" />
|
||||
<PackageVersion Include="DuckDB.NET.Data.Full" Version="0.9.1" />
|
||||
<PackageVersion Include="DuckDB.NET.Data" Version="0.9.1" />
|
||||
<PackageVersion Include="DocumentFormat.OpenXml" Version="3.0.1" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.0" />
|
||||
<PackageVersion Include="DuckDB.NET.Data.Full" Version="0.9.2" />
|
||||
<PackageVersion Include="DuckDB.NET.Data" Version="0.9.2" />
|
||||
<PackageVersion Include="MongoDB.Driver" Version="2.23.1" />
|
||||
<PackageVersion Include="Microsoft.Graph" Version="[4.51.0, 5)" />
|
||||
<PackageVersion Include="Microsoft.Azure.Cosmos" Version="[3.32.3, )" />
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="[2.28.0, )" />
|
||||
<PackageVersion Include="Microsoft.OpenApi" Version="[1.6.3, )" />
|
||||
<PackageVersion Include="Microsoft.OpenApi.Readers" Version="[1.6.3, )" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="[13.0.3, )" />
|
||||
<PackageVersion Include="Microsoft.OpenApi" Version="1.6.12" />
|
||||
<PackageVersion Include="Microsoft.OpenApi.Readers" Version="1.6.11" />
|
||||
<PackageVersion Include="Google.Apis.CustomSearchAPI.v1" Version="[1.60.0.3001, )" />
|
||||
<PackageVersion Include="Grpc.Net.Client" Version="2.58.0" />
|
||||
<PackageVersion Include="protobuf-net" Version="3.2.26" />
|
||||
<PackageVersion Include="Grpc.Net.Client" Version="2.60.0" />
|
||||
<PackageVersion Include="protobuf-net" Version="3.2.30" />
|
||||
<PackageVersion Include="protobuf-net.Reflection" Version="3.2.12" />
|
||||
<PackageVersion Include="CoreCLR-NCalc" Version="2.2.113" />
|
||||
<PackageVersion Include="YamlDotNet" Version="13.7.1" />
|
||||
<!-- Memory stores -->
|
||||
<PackageVersion Include="Pgvector" Version="0.1.4"/>
|
||||
<PackageVersion Include="NRedisStack" Version="0.9.0"/>
|
||||
<PackageVersion Include="Milvus.Client" Version="2.2.2-preview.6"/>
|
||||
<PackageVersion Include="Pgvector" Version="0.2.0" />
|
||||
<PackageVersion Include="NRedisStack" Version="0.11.0" />
|
||||
<PackageVersion Include="Milvus.Client" Version="2.2.2-preview.6" />
|
||||
<!-- Symbols -->
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
|
||||
<!-- Analyzers -->
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0-preview1.23165.1" />
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
<!-- Toolset -->
|
||||
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -76,12 +89,12 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.7.30" />
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.9.28" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageVersion Include="xunit.analyzers" Version="1.4.0" />
|
||||
<PackageVersion Include="xunit.analyzers" Version="1.10.0" />
|
||||
<PackageReference Include="xunit.analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -91,17 +104,17 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageVersion Include="Roslynator.Analyzers" Version="[4.3.0]" />
|
||||
<PackageVersion Include="Roslynator.Analyzers" Version="[4.10.0]" />
|
||||
<PackageReference Include="Roslynator.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageVersion Include="Roslynator.CodeAnalysis.Analyzers" Version="[4.3.0]" />
|
||||
<PackageVersion Include="Roslynator.CodeAnalysis.Analyzers" Version="[4.10.0]" />
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="[4.3.0]" />
|
||||
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="[4.10.0]" />
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
+18
-52
@@ -14,7 +14,7 @@ First, let's create a new project, targeting .NET 6 or newer, and add the
|
||||
`Microsoft.SemanticKernel` nuget package to your project from the command prompt
|
||||
in Visual Studio:
|
||||
|
||||
dotnet add package Microsoft.SemanticKernel --prerelease
|
||||
dotnet add package Microsoft.SemanticKernel
|
||||
|
||||
# Running prompts with input parameters
|
||||
|
||||
@@ -22,17 +22,17 @@ Copy and paste the following code into your project, with your Azure OpenAI key
|
||||
|
||||
```csharp
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.AI.OpenAI;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
|
||||
var builder = new KernelBuilder();
|
||||
var builder = Kernel.CreateBuilder();
|
||||
|
||||
builder.WithAzureChatCompletionService(
|
||||
builder.AddAzureOpenAIChatCompletion(
|
||||
"gpt-35-turbo", // Azure OpenAI Deployment Name
|
||||
"https://contoso.openai.azure.com/", // Azure OpenAI Endpoint
|
||||
"...your Azure OpenAI Key..."); // Azure OpenAI Key
|
||||
|
||||
// Alternative using OpenAI
|
||||
//builder.WithOpenAIChatCompletionService(
|
||||
//builder.AddOpenAIChatCompletion(
|
||||
// "gpt-3.5-turbo", // OpenAI Model name
|
||||
// "...your OpenAI API Key..."); // OpenAI API Key
|
||||
|
||||
@@ -42,7 +42,7 @@ var prompt = @"{{$input}}
|
||||
|
||||
One line TLDR with the fewest words.";
|
||||
|
||||
var summarize = kernel.CreateSemanticFunction(prompt, requestSettings: new OpenAIRequestSettings { MaxTokens = 100 });
|
||||
var summarize = kernel.CreateFunctionFromPrompt(prompt, executionSettings: new OpenAIPromptExecutionSettings { MaxTokens = 100 });
|
||||
|
||||
string text1 = @"
|
||||
1st Law of Thermodynamics - Energy cannot be created or destroyed.
|
||||
@@ -54,49 +54,15 @@ string text2 = @"
|
||||
2. The acceleration of an object depends on the mass of the object and the amount of force applied.
|
||||
3. Whenever one object exerts a force on another object, the second object exerts an equal and opposite on the first.";
|
||||
|
||||
Console.WriteLine(await kernel.RunAsync(text1, summarize));
|
||||
Console.WriteLine(await kernel.InvokeAsync(summarize, new() { ["input"] = text1 }));
|
||||
|
||||
Console.WriteLine(await kernel.RunAsync(text2, summarize));
|
||||
Console.WriteLine(await kernel.InvokeAsync(summarize, new() { ["input"] = text2 }));
|
||||
|
||||
// Output:
|
||||
// Energy conserved, entropy increases, zero entropy at 0K.
|
||||
// Objects move in response to forces.
|
||||
```
|
||||
|
||||
# Prompt chaining
|
||||
|
||||
The previous code shows how to invoke individual semantic functions, but you can
|
||||
also chain functions (aka prompt chaining) to process the initial input with multiple
|
||||
operations.
|
||||
|
||||
The following code for example, translates an initial text to math symbols and
|
||||
then generates a summary:
|
||||
|
||||
```csharp
|
||||
string translationPrompt = @"{{$input}}
|
||||
|
||||
Translate the text to math.";
|
||||
|
||||
string summarizePrompt = @"{{$input}}
|
||||
|
||||
Give me a TLDR with the fewest words.";
|
||||
|
||||
var translator = kernel.CreateSemanticFunction(translationPrompt, requestSettings: new OpenAIRequestSettings { MaxTokens = 200 });
|
||||
var summarize = kernel.CreateSemanticFunction(summarizePrompt, requestSettings: new OpenAIRequestSettings { MaxTokens = 100 });
|
||||
|
||||
string inputText = @"
|
||||
1st Law of Thermodynamics - Energy cannot be created or destroyed.
|
||||
2nd Law of Thermodynamics - For a spontaneous process, the entropy of the universe increases.
|
||||
3rd Law of Thermodynamics - A perfect crystal at zero Kelvin has zero entropy.";
|
||||
|
||||
// Run two prompts in sequence (prompt chaining)
|
||||
var output = await kernel.RunAsync(inputText, translator, summarize);
|
||||
|
||||
Console.WriteLine(output);
|
||||
|
||||
// Output: ΔE = 0, ΔSuniv > 0, S = 0 at 0K.
|
||||
```
|
||||
|
||||
# Semantic Kernel Notebooks
|
||||
|
||||
The repository contains also a few C# Jupyter notebooks that demonstrates
|
||||
@@ -109,11 +75,12 @@ requirements and setup instructions.
|
||||
2. [Loading and configuring Semantic Kernel](./notebooks/01-basic-loading-the-kernel.ipynb)
|
||||
3. [Running AI prompts from file](./notebooks/02-running-prompts-from-file.ipynb)
|
||||
4. [Creating Semantic Functions at runtime (i.e. inline functions)](./notebooks/03-semantic-function-inline.ipynb)
|
||||
5. [Using Context Variables to Build a Chat Experience](./notebooks/04-context-variables-chat.ipynb)
|
||||
5. [Using Kernel Arguments to Build a Chat Experience](./notebooks/04-kernel-arguments-chat.ipynb)
|
||||
6. [Creating and Executing Plans](./notebooks/05-using-the-planner.ipynb)
|
||||
7. [Building Memory with Embeddings](./notebooks/06-memory-and-embeddings.ipynb)
|
||||
8. [Creating images with DALL-E 2](./notebooks/07-DALL-E-2.ipynb)
|
||||
9. [Chatting with ChatGPT and Images](./notebooks/08-chatGPT-with-DALL-E-2.ipynb)
|
||||
8. [Creating images with DALL-E 3](./notebooks/07-DALL-E-3.ipynb)
|
||||
9. [Chatting with ChatGPT and Images](./notebooks/08-chatGPT-with-DALL-E-3.ipynb)
|
||||
10. [BingSearch using Kernel](./notebooks/10-BingSearch-using-kernel.ipynb)
|
||||
|
||||
# Nuget packages
|
||||
|
||||
@@ -133,21 +100,20 @@ Packages included in **Microsoft.SemanticKernel**:
|
||||
used by the core and other SK components.
|
||||
1. **Microsoft.SemanticKernel.Core**: contains the core logic of SK, such as prompt
|
||||
engineering, semantic memory and semantic functions definition and orchestration.
|
||||
1. **Microsoft.SemanticKernel.Connectors.AI.OpenAI**: connectors to OpenAI and Azure
|
||||
OpenAI, allowing to run semantic functions, chats, image generation with GPT3,
|
||||
GPT3.5, GPT4, DALL-E2.
|
||||
1. **Microsoft.SemanticKernel.Connectors.OpenAI**: connectors to OpenAI and Azure
|
||||
OpenAI, allowing to run semantic functions, chats, text to image with GPT3,
|
||||
GPT3.5, GPT4, DALL-E3.
|
||||
|
||||
Other SK packages available at nuget.org:
|
||||
|
||||
1. **Microsoft.SemanticKernel.Connectors.Memory.Qdrant**: Qdrant connector for
|
||||
1. **Microsoft.SemanticKernel.Connectors.Qdrant**: Qdrant connector for
|
||||
plugins and semantic memory.
|
||||
2. **Microsoft.SemanticKernel.Connectors.Memory.Sqlite**: SQLite connector for
|
||||
2. **Microsoft.SemanticKernel.Connectors.Sqlite**: SQLite connector for
|
||||
plugins and semantic memory
|
||||
3. **Microsoft.SemanticKernel.Plugins.Document**: Document Plugin: Word processing,
|
||||
OpenXML, etc.
|
||||
4. **Microsoft.SemanticKernel.Plugins.MsGraph**: Microsoft Graph Plugin: access your
|
||||
tenant data, schedule meetings, send emails, etc.
|
||||
5. **Microsoft.SemanticKernel.Plugins.OpenAPI**: OpenAPI Plugin.
|
||||
5. **Microsoft.SemanticKernel.Plugins.OpenApi**: OpenAPI Plugin.
|
||||
6. **Microsoft.SemanticKernel.Plugins.Web**: Web Plugin: search the web, download
|
||||
files, etc.
|
||||
7. **Microsoft.SemanticKernel.Reliability.Polly**: Extension for http resiliency.
|
||||
|
||||
+221
-112
@@ -1,3 +1,4 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33213.308
|
||||
@@ -7,18 +8,9 @@ EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{FA3720F1-C99A-49B2-9577-A940257098BF}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\samples\dotnet\Directory.Build.props = ..\samples\dotnet\Directory.Build.props
|
||||
..\samples\dotnet\Directory.Build.targets = ..\samples\dotnet\Directory.Build.targets
|
||||
..\samples\dotnet\Directory.Packages.props = ..\samples\dotnet\Directory.Packages.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KernelSyntaxExamples", "samples\KernelSyntaxExamples\KernelSyntaxExamples.csproj", "{47C6F821-5103-431F-B3B8-A2868A68BB78}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MsGraphPluginsExample", "..\samples\dotnet\MsGraphPluginsExample\MsGraphPluginsExample.csproj", "{3EB61E99-C39B-4620-9482-F8DA18E48525}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KernelHttpServer", "..\samples\dotnet\KernelHttpServer\KernelHttpServer.csproj", "{34A7F1EF-D243-4160-A413-D713FEABCD94}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "src\IntegrationTests\IntegrationTests.csproj", "{E4B777A1-28E1-41BE-96AE-7F3EC61FD5D4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugins.Document", "src\Plugins\Plugins.Document\Plugins.Document.csproj", "{F94D1938-9DB7-4B24-9FF3-166DDFD96330}"
|
||||
@@ -32,6 +24,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
..\.editorconfig = ..\.editorconfig
|
||||
..\.gitignore = ..\.gitignore
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Directory.Build.targets = Directory.Build.targets
|
||||
Directory.Packages.props = Directory.Packages.props
|
||||
..\.github\workflows\dotnet-format.yml = ..\.github\workflows\dotnet-format.yml
|
||||
..\nuget.config = ..\nuget.config
|
||||
@@ -51,7 +44,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{F4243136
|
||||
nuget\NUGET.md = nuget\NUGET.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Functions.OpenAPI", "src\Functions\Functions.OpenAPI\Functions.OpenAPI.csproj", "{F2A1F81E-700E-4C0E-B021-B9EF29AA20BD}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Functions.OpenApi", "src\Functions\Functions.OpenApi\Functions.OpenApi.csproj", "{F2A1F81E-700E-4C0E-B021-B9EF29AA20BD}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "connectors", "connectors", "{0247C2C9-86C3-45BA-8873-28B0948EDC0C}"
|
||||
EndProject
|
||||
@@ -67,7 +60,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.Redis", "
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.Chroma", "src\Connectors\Connectors.Memory.Chroma\Connectors.Memory.Chroma.csproj", "{185E0CE8-C2DA-4E4C-A491-E8EB40316315}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.AI.OpenAI", "src\Connectors\Connectors.AI.OpenAI\Connectors.AI.OpenAI.csproj", "{AFA81EB7-F869-467D-8A90-744305D80AAC}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.OpenAI", "src\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj", "{AFA81EB7-F869-467D-8A90-744305D80AAC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SemanticKernel.Abstractions", "src\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj", "{627742DB-1E52-468A-99BD-6FF1A542D25B}"
|
||||
EndProject
|
||||
@@ -77,26 +70,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "extensions", "extensions",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extensions.UnitTests", "src\Extensions\Extensions.UnitTests\Extensions.UnitTests.csproj", "{F51017A9-15C8-472D-893C-080046D710A6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.AzureCognitiveSearch", "src\Connectors\Connectors.Memory.AzureCognitiveSearch\Connectors.Memory.AzureCognitiveSearch.csproj", "{EC3BB6D1-2FB2-4702-84C6-F791DE533ED4}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.AzureAISearch", "src\Connectors\Connectors.Memory.AzureAISearch\Connectors.Memory.AzureAISearch.csproj", "{EC3BB6D1-2FB2-4702-84C6-F791DE533ED4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.Pinecone", "src\Connectors\Connectors.Memory.Pinecone\Connectors.Memory.Pinecone.csproj", "{4D226C2F-AE9F-4EFB-AF2D-45C8FE5CB34E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Functions.Grpc", "src\Functions\Functions.Grpc\Functions.Grpc.csproj", "{E52F805C-794A-4CA9-B684-DFF358B18820}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.AI.HuggingFace", "src\Connectors\Connectors.AI.HuggingFace\Connectors.AI.HuggingFace.csproj", "{136823BE-8665-4D57-87E0-EF41535539E2}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.HuggingFace", "src\Connectors\Connectors.HuggingFace\Connectors.HuggingFace.csproj", "{136823BE-8665-4D57-87E0-EF41535539E2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InternalUtilities", "InternalUtilities", "{4D3DAE63-41C6-4E1C-A35A-E77BDFC40675}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.Weaviate", "src\Connectors\Connectors.Memory.Weaviate\Connectors.Memory.Weaviate.csproj", "{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiPluginsExample", "..\samples\dotnet\OpenApiPluginsExample\OpenApiPluginsExample.csproj", "{4D91A3E0-C404-495B-AD4A-411C4E83CF54}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.DuckDB", "src\Connectors\Connectors.Memory.DuckDB\Connectors.Memory.DuckDB.csproj", "{50FAE231-6F24-4779-9D02-12ABBC9A49E2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5C246969-D794-4EC3-8E8F-F90D4D166420}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\test\AssertExtensions.cs = src\InternalUtilities\test\AssertExtensions.cs
|
||||
src\InternalUtilities\test\FunctionHelpers.cs = src\InternalUtilities\test\FunctionHelpers.cs
|
||||
src\InternalUtilities\test\TestInternalUtilities.props = src\InternalUtilities\test\TestInternalUtilities.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
@@ -109,7 +99,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Diagnostics", "Diagnostics"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\src\Diagnostics\CompilerServicesAttributes.cs = src\InternalUtilities\src\Diagnostics\CompilerServicesAttributes.cs
|
||||
src\InternalUtilities\src\Diagnostics\ExceptionExtensions.cs = src\InternalUtilities\src\Diagnostics\ExceptionExtensions.cs
|
||||
src\InternalUtilities\src\Diagnostics\HttpStatusCodeType.cs = src\InternalUtilities\src\Diagnostics\HttpStatusCodeType.cs
|
||||
src\InternalUtilities\src\Diagnostics\ExperimentalAttribute.cs = src\InternalUtilities\src\Diagnostics\ExperimentalAttribute.cs
|
||||
src\InternalUtilities\src\Diagnostics\IsExternalInit.cs = src\InternalUtilities\src\Diagnostics\IsExternalInit.cs
|
||||
src\InternalUtilities\src\Diagnostics\NullableAttributes.cs = src\InternalUtilities\src\Diagnostics\NullableAttributes.cs
|
||||
src\InternalUtilities\src\Diagnostics\Verify.cs = src\InternalUtilities\src\Diagnostics\Verify.cs
|
||||
EndProjectSection
|
||||
@@ -119,51 +110,115 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linq", "Linq", "{B00AD427-0
|
||||
src\InternalUtilities\src\Linq\AsyncEnumerable.cs = src\InternalUtilities\src\Linq\AsyncEnumerable.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Text", "Text", "{DB950192-30F1-48B1-88D7-F43FECCA1A1C}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\src\Text\Json.cs = src\InternalUtilities\src\Text\Json.cs
|
||||
src\InternalUtilities\src\Text\StringExtensions.cs = src\InternalUtilities\src\Text\StringExtensions.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Http", "Http", "{1C19D805-3573-4477-BF07-40180FCDE1BD}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\src\Http\HttpClientExtensions.cs = src\InternalUtilities\src\Http\HttpClientExtensions.cs
|
||||
src\InternalUtilities\src\Http\HttpClientProvider.cs = src\InternalUtilities\src\Http\HttpClientProvider.cs
|
||||
src\InternalUtilities\src\Http\NonDisposableHttpClientHandler.cs = src\InternalUtilities\src\Http\NonDisposableHttpClientHandler.cs
|
||||
src\InternalUtilities\src\Http\HttpContentExtensions.cs = src\InternalUtilities\src\Http\HttpContentExtensions.cs
|
||||
src\InternalUtilities\src\Http\HttpHeaderValues.cs = src\InternalUtilities\src\Http\HttpHeaderValues.cs
|
||||
src\InternalUtilities\src\Http\HttpRequest.cs = src\InternalUtilities\src\Http\HttpRequest.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "System", "System", "{3CDE10B2-AE8F-4FC4-8D55-92D4AD32E144}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\src\System\EnvExtensions.cs = src\InternalUtilities\src\System\EnvExtensions.cs
|
||||
src\InternalUtilities\src\System\InternalTypeConverter.cs = src\InternalUtilities\src\System\InternalTypeConverter.cs
|
||||
src\InternalUtilities\src\System\NonNullCollection.cs = src\InternalUtilities\src\System\NonNullCollection.cs
|
||||
src\InternalUtilities\src\System\TypeConverterFactory.cs = src\InternalUtilities\src\System\TypeConverterFactory.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Type", "Type", "{E85EA4D0-BB7E-4DFD-882F-A76EB8C0B8FF}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\src\Type\TypeExtensions.cs = src\InternalUtilities\src\Type\TypeExtensions.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugins.Core", "src\Plugins\Plugins.Core\Plugins.Core.csproj", "{0D0C4DAD-E6BC-4504-AE3A-EEA4E35920C1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NCalcPlugins", "samples\NCalcPlugins\NCalcPlugins.csproj", "{E6EDAB8F-3406-4DBF-9AAB-DF40DC2CA0FA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationInsightsExample", "samples\ApplicationInsightsExample\ApplicationInsightsExample.csproj", "{C754950A-E16C-4F96-9CC7-9328E361B5AF}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelemetryExample", "samples\TelemetryExample\TelemetryExample.csproj", "{C754950A-E16C-4F96-9CC7-9328E361B5AF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.Kusto", "src\Connectors\Connectors.Memory.Kusto\Connectors.Memory.Kusto.csproj", "{E07608CC-D710-4655-BB9E-D22CF3CDD193}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TemplateEngine.Basic", "src\Extensions\TemplateEngine.Basic\TemplateEngine.Basic.csproj", "{10E4B697-D4E8-468D-872D-49670FD150FB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reliability.Polly", "src\Extensions\Reliability.Polly\Reliability.Polly.csproj", "{D4540A0F-98E3-4E70-9093-1948AE5B2AAD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reliability.Basic", "src\Extensions\Reliability.Basic\Reliability.Basic.csproj", "{3DC4DBD8-20A5-4937-B4F5-BB5E24E7A567}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{D6D598DF-C17C-46F4-B2B9-CDE82E2DE132}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugins.UnitTests", "src\Plugins\Plugins.UnitTests\Plugins.UnitTests.csproj", "{5CB78CE4-895B-4A14-98AA-716A37DEEBB1}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "planners", "planners", "{A21FAC7C-0C09-4EAD-843B-926ACEF73C80}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Planners.Core", "src\Planners\Planners.Core\Planners.Core.csproj", "{F224B869-FA0E-4EEE-A6BF-C2D61FF8E731}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Planners.Core.UnitTests", "src\Planners\Planners.Core.UnitTests\Planners.Core.UnitTests.csproj", "{CC77DCFA-A419-4202-A98A-868CDF457792}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.Milvus", "src\Connectors\Connectors.Memory.Milvus\Connectors.Memory.Milvus.csproj", "{8B754E80-7A97-4585-8D7E-1D588FA5F727}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugins.Memory", "src\Plugins\Plugins.Memory\Plugins.Memory.csproj", "{E91365A1-8B01-4AB8-825F-67E3515EADCD}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "experimental", "experimental", "{A2357CF8-3BB9-45A1-93F1-B366C9B63658}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Planners.OpenAI", "src\Planners\Planners.OpenAI\Planners.OpenAI.csproj", "{348BBF45-23B4-4599-83A6-8AE1795227FB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.MongoDB", "src\Connectors\Connectors.Memory.MongoDB\Connectors.Memory.MongoDB.csproj", "{6009CC87-32F1-4282-88BB-8E5A7BA12925}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PromptTemplates.Handlebars", "src\Extensions\PromptTemplates.Handlebars\PromptTemplates.Handlebars.csproj", "{B0646036-0C50-4F66-B479-ADA9C1166816}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Functions.Yaml", "src\Functions\Functions.Yaml\Functions.Yaml.csproj", "{4AD4E731-16E7-4A0E-B403-6C96459F989B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Functions.Markdown", "src\Functions\Functions.Markdown\Functions.Markdown.csproj", "{E576E260-4030-4C4C-B207-CA3B684E9669}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Planners.Handlebars", "src\Planners\Planners.Handlebars\Planners.Handlebars.csproj", "{352DAE69-E31C-44E3-A35F-E0342A955869}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "planning", "planning", "{A7DAB812-81CF-4931-B38C-468A3261C4C8}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\planning\PlannerInstrumentation.cs = src\InternalUtilities\planning\PlannerInstrumentation.cs
|
||||
src\InternalUtilities\planning\PlannerOptions.cs = src\InternalUtilities\planning\PlannerOptions.cs
|
||||
src\InternalUtilities\planning\PlanningUtilities.props = src\InternalUtilities\planning\PlanningUtilities.props
|
||||
src\InternalUtilities\planning\SemanticMemoryConfig.cs = src\InternalUtilities\planning\SemanticMemoryConfig.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{3F4E0DC5-2241-4EF2-9F69-E7EC7834D349}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\planning\Extensions\ChatHistoryExtensions.cs = src\InternalUtilities\planning\Extensions\ChatHistoryExtensions.cs
|
||||
src\InternalUtilities\planning\Extensions\KernelFunctionMetadataExtensions.cs = src\InternalUtilities\planning\Extensions\KernelFunctionMetadataExtensions.cs
|
||||
src\InternalUtilities\planning\Extensions\ReadOnlyFunctionCollectionPlannerExtensions.cs = src\InternalUtilities\planning\Extensions\ReadOnlyFunctionCollectionPlannerExtensions.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Schema", "Schema", "{873281F6-9C31-4641-98AE-F1D2844DE9F0}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\planning\Schema\JsonSchemaFunctionContent.cs = src\InternalUtilities\planning\Schema\JsonSchemaFunctionContent.cs
|
||||
src\InternalUtilities\planning\Schema\JsonSchemaFunctionParameters.cs = src\InternalUtilities\planning\Schema\JsonSchemaFunctionParameters.cs
|
||||
src\InternalUtilities\planning\Schema\JsonSchemaFunctionResponse.cs = src\InternalUtilities\planning\Schema\JsonSchemaFunctionResponse.cs
|
||||
src\InternalUtilities\planning\Schema\JsonSchemaFunctionView.cs = src\InternalUtilities\planning\Schema\JsonSchemaFunctionView.cs
|
||||
src\InternalUtilities\planning\Schema\JsonSchemaResponse.cs = src\InternalUtilities\planning\Schema\JsonSchemaResponse.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Planners.Handlebars.UnitTests", "src\Planners\Planners.Handlebars.UnitTests\Planners.Handlebars.UnitTests.csproj", "{3FC4A81B-8ABE-473F-BC7C-6F4885775534}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AI", "AI", "{1B4CBDE0-10C2-4E7D-9CD0-FE7586C96ED1}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Memory", "Memory", "{24503383-A8C4-4255-9998-28D70FE8E99A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Experimental.Agents", "src\Experimental\Agents\Experimental.Agents.csproj", "{5438D1E3-E03D-444B-BBBA-478F93161AA8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Experimental.Agents.UnitTests", "src\Experimental\Agents.UnitTests\Experimental.Agents.UnitTests.csproj", "{4AD80279-9AC1-476F-8103-E6CD5E4FD525}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Experimental.Orchestration.Flow", "src\Experimental\Orchestration.Flow\Experimental.Orchestration.Flow.csproj", "{B0CE8C69-EC56-4825-94AB-01CA7E8BA55B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Experimental.Orchestration.Flow.IntegrationTests", "src\Experimental\Orchestration.Flow.IntegrationTests\Experimental.Orchestration.Flow.IntegrationTests.csproj", "{3A4B8F90-3E74-43E0-800C-84F8AA9B5BF3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Experimental.Orchestration.Flow.UnitTests", "src\Experimental\Orchestration.Flow.UnitTests\Experimental.Orchestration.Flow.UnitTests.csproj", "{731CC542-8BE9-42D4-967D-99206EC2B310}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocumentationExamples", "samples\DocumentationExamples\DocumentationExamples.csproj", "{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateChatGptPlugin", "samples\CreateChatGptPlugin\Solution\CreateChatGptPlugin.csproj", "{87AB5AF5-5783-4372-9789-664895E0A2FF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Functions.OpenApi.Extensions", "src\Functions\Functions.OpenApi.Extensions\Functions.OpenApi.Extensions.csproj", "{95CAA25F-A0DE-4A5B-92BA-7D56C0E822A8}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Text", "Text", "{EB2C141A-AE5F-4080-8790-13EB16323CEF}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\src\Text\JsonOptionsCache.cs = src\InternalUtilities\src\Text\JsonOptionsCache.cs
|
||||
src\InternalUtilities\src\Text\ReadOnlyMemoryConverter.cs = src\InternalUtilities\src\Text\ReadOnlyMemoryConverter.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linq", "Linq", "{607DD6FA-FA0D-45E6-80BA-22A373609E89}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\InternalUtilities\test\Linq\AsyncEnumerable.cs = src\InternalUtilities\test\Linq\AsyncEnumerable.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -182,16 +237,6 @@ Global
|
||||
{47C6F821-5103-431F-B3B8-A2868A68BB78}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{47C6F821-5103-431F-B3B8-A2868A68BB78}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{47C6F821-5103-431F-B3B8-A2868A68BB78}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3EB61E99-C39B-4620-9482-F8DA18E48525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3EB61E99-C39B-4620-9482-F8DA18E48525}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3EB61E99-C39B-4620-9482-F8DA18E48525}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3EB61E99-C39B-4620-9482-F8DA18E48525}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3EB61E99-C39B-4620-9482-F8DA18E48525}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{34A7F1EF-D243-4160-A413-D713FEABCD94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{34A7F1EF-D243-4160-A413-D713FEABCD94}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{34A7F1EF-D243-4160-A413-D713FEABCD94}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{34A7F1EF-D243-4160-A413-D713FEABCD94}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{34A7F1EF-D243-4160-A413-D713FEABCD94}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4B777A1-28E1-41BE-96AE-7F3EC61FD5D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E4B777A1-28E1-41BE-96AE-7F3EC61FD5D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E4B777A1-28E1-41BE-96AE-7F3EC61FD5D4}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -324,11 +369,6 @@ Global
|
||||
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4D91A3E0-C404-495B-AD4A-411C4E83CF54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4D91A3E0-C404-495B-AD4A-411C4E83CF54}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4D91A3E0-C404-495B-AD4A-411C4E83CF54}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4D91A3E0-C404-495B-AD4A-411C4E83CF54}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4D91A3E0-C404-495B-AD4A-411C4E83CF54}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{50FAE231-6F24-4779-9D02-12ABBC9A49E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{50FAE231-6F24-4779-9D02-12ABBC9A49E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{50FAE231-6F24-4779-9D02-12ABBC9A49E2}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
@@ -341,11 +381,6 @@ Global
|
||||
{0D0C4DAD-E6BC-4504-AE3A-EEA4E35920C1}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{0D0C4DAD-E6BC-4504-AE3A-EEA4E35920C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0D0C4DAD-E6BC-4504-AE3A-EEA4E35920C1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E6EDAB8F-3406-4DBF-9AAB-DF40DC2CA0FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E6EDAB8F-3406-4DBF-9AAB-DF40DC2CA0FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E6EDAB8F-3406-4DBF-9AAB-DF40DC2CA0FA}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E6EDAB8F-3406-4DBF-9AAB-DF40DC2CA0FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E6EDAB8F-3406-4DBF-9AAB-DF40DC2CA0FA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C754950A-E16C-4F96-9CC7-9328E361B5AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C754950A-E16C-4F96-9CC7-9328E361B5AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C754950A-E16C-4F96-9CC7-9328E361B5AF}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -357,42 +392,12 @@ Global
|
||||
{E07608CC-D710-4655-BB9E-D22CF3CDD193}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{E07608CC-D710-4655-BB9E-D22CF3CDD193}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E07608CC-D710-4655-BB9E-D22CF3CDD193}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{10E4B697-D4E8-468D-872D-49670FD150FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{10E4B697-D4E8-468D-872D-49670FD150FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{10E4B697-D4E8-468D-872D-49670FD150FB}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{10E4B697-D4E8-468D-872D-49670FD150FB}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{10E4B697-D4E8-468D-872D-49670FD150FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{10E4B697-D4E8-468D-872D-49670FD150FB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D4540A0F-98E3-4E70-9093-1948AE5B2AAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D4540A0F-98E3-4E70-9093-1948AE5B2AAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D4540A0F-98E3-4E70-9093-1948AE5B2AAD}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{D4540A0F-98E3-4E70-9093-1948AE5B2AAD}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{D4540A0F-98E3-4E70-9093-1948AE5B2AAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D4540A0F-98E3-4E70-9093-1948AE5B2AAD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3DC4DBD8-20A5-4937-B4F5-BB5E24E7A567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3DC4DBD8-20A5-4937-B4F5-BB5E24E7A567}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3DC4DBD8-20A5-4937-B4F5-BB5E24E7A567}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{3DC4DBD8-20A5-4937-B4F5-BB5E24E7A567}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{3DC4DBD8-20A5-4937-B4F5-BB5E24E7A567}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3DC4DBD8-20A5-4937-B4F5-BB5E24E7A567}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5CB78CE4-895B-4A14-98AA-716A37DEEBB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5CB78CE4-895B-4A14-98AA-716A37DEEBB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5CB78CE4-895B-4A14-98AA-716A37DEEBB1}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5CB78CE4-895B-4A14-98AA-716A37DEEBB1}.Publish|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5CB78CE4-895B-4A14-98AA-716A37DEEBB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5CB78CE4-895B-4A14-98AA-716A37DEEBB1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F224B869-FA0E-4EEE-A6BF-C2D61FF8E731}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F224B869-FA0E-4EEE-A6BF-C2D61FF8E731}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F224B869-FA0E-4EEE-A6BF-C2D61FF8E731}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{F224B869-FA0E-4EEE-A6BF-C2D61FF8E731}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{F224B869-FA0E-4EEE-A6BF-C2D61FF8E731}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F224B869-FA0E-4EEE-A6BF-C2D61FF8E731}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CC77DCFA-A419-4202-A98A-868CDF457792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CC77DCFA-A419-4202-A98A-868CDF457792}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CC77DCFA-A419-4202-A98A-868CDF457792}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CC77DCFA-A419-4202-A98A-868CDF457792}.Publish|Any CPU.Build.0 = Release|Any CPU
|
||||
{CC77DCFA-A419-4202-A98A-868CDF457792}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CC77DCFA-A419-4202-A98A-868CDF457792}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8B754E80-7A97-4585-8D7E-1D588FA5F727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8B754E80-7A97-4585-8D7E-1D588FA5F727}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8B754E80-7A97-4585-8D7E-1D588FA5F727}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
@@ -401,10 +406,100 @@ Global
|
||||
{8B754E80-7A97-4585-8D7E-1D588FA5F727}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E91365A1-8B01-4AB8-825F-67E3515EADCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E91365A1-8B01-4AB8-825F-67E3515EADCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E91365A1-8B01-4AB8-825F-67E3515EADCD}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E91365A1-8B01-4AB8-825F-67E3515EADCD}.Publish|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E91365A1-8B01-4AB8-825F-67E3515EADCD}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{E91365A1-8B01-4AB8-825F-67E3515EADCD}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{E91365A1-8B01-4AB8-825F-67E3515EADCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E91365A1-8B01-4AB8-825F-67E3515EADCD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{348BBF45-23B4-4599-83A6-8AE1795227FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{348BBF45-23B4-4599-83A6-8AE1795227FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{348BBF45-23B4-4599-83A6-8AE1795227FB}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{348BBF45-23B4-4599-83A6-8AE1795227FB}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{348BBF45-23B4-4599-83A6-8AE1795227FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{348BBF45-23B4-4599-83A6-8AE1795227FB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6009CC87-32F1-4282-88BB-8E5A7BA12925}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6009CC87-32F1-4282-88BB-8E5A7BA12925}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6009CC87-32F1-4282-88BB-8E5A7BA12925}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{6009CC87-32F1-4282-88BB-8E5A7BA12925}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{6009CC87-32F1-4282-88BB-8E5A7BA12925}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6009CC87-32F1-4282-88BB-8E5A7BA12925}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B0646036-0C50-4F66-B479-ADA9C1166816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B0646036-0C50-4F66-B479-ADA9C1166816}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B0646036-0C50-4F66-B479-ADA9C1166816}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{B0646036-0C50-4F66-B479-ADA9C1166816}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{B0646036-0C50-4F66-B479-ADA9C1166816}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B0646036-0C50-4F66-B479-ADA9C1166816}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4AD4E731-16E7-4A0E-B403-6C96459F989B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4AD4E731-16E7-4A0E-B403-6C96459F989B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4AD4E731-16E7-4A0E-B403-6C96459F989B}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{4AD4E731-16E7-4A0E-B403-6C96459F989B}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{4AD4E731-16E7-4A0E-B403-6C96459F989B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4AD4E731-16E7-4A0E-B403-6C96459F989B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E576E260-4030-4C4C-B207-CA3B684E9669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E576E260-4030-4C4C-B207-CA3B684E9669}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E576E260-4030-4C4C-B207-CA3B684E9669}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{E576E260-4030-4C4C-B207-CA3B684E9669}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{E576E260-4030-4C4C-B207-CA3B684E9669}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E576E260-4030-4C4C-B207-CA3B684E9669}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{352DAE69-E31C-44E3-A35F-E0342A955869}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{352DAE69-E31C-44E3-A35F-E0342A955869}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{352DAE69-E31C-44E3-A35F-E0342A955869}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{352DAE69-E31C-44E3-A35F-E0342A955869}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{352DAE69-E31C-44E3-A35F-E0342A955869}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{352DAE69-E31C-44E3-A35F-E0342A955869}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3FC4A81B-8ABE-473F-BC7C-6F4885775534}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3FC4A81B-8ABE-473F-BC7C-6F4885775534}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3FC4A81B-8ABE-473F-BC7C-6F4885775534}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3FC4A81B-8ABE-473F-BC7C-6F4885775534}.Publish|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3FC4A81B-8ABE-473F-BC7C-6F4885775534}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3FC4A81B-8ABE-473F-BC7C-6F4885775534}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5438D1E3-E03D-444B-BBBA-478F93161AA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5438D1E3-E03D-444B-BBBA-478F93161AA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5438D1E3-E03D-444B-BBBA-478F93161AA8}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{5438D1E3-E03D-444B-BBBA-478F93161AA8}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{5438D1E3-E03D-444B-BBBA-478F93161AA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5438D1E3-E03D-444B-BBBA-478F93161AA8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4AD80279-9AC1-476F-8103-E6CD5E4FD525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4AD80279-9AC1-476F-8103-E6CD5E4FD525}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4AD80279-9AC1-476F-8103-E6CD5E4FD525}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4AD80279-9AC1-476F-8103-E6CD5E4FD525}.Publish|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4AD80279-9AC1-476F-8103-E6CD5E4FD525}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4AD80279-9AC1-476F-8103-E6CD5E4FD525}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B0CE8C69-EC56-4825-94AB-01CA7E8BA55B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B0CE8C69-EC56-4825-94AB-01CA7E8BA55B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B0CE8C69-EC56-4825-94AB-01CA7E8BA55B}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{B0CE8C69-EC56-4825-94AB-01CA7E8BA55B}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{B0CE8C69-EC56-4825-94AB-01CA7E8BA55B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B0CE8C69-EC56-4825-94AB-01CA7E8BA55B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3A4B8F90-3E74-43E0-800C-84F8AA9B5BF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3A4B8F90-3E74-43E0-800C-84F8AA9B5BF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3A4B8F90-3E74-43E0-800C-84F8AA9B5BF3}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3A4B8F90-3E74-43E0-800C-84F8AA9B5BF3}.Publish|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3A4B8F90-3E74-43E0-800C-84F8AA9B5BF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3A4B8F90-3E74-43E0-800C-84F8AA9B5BF3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{731CC542-8BE9-42D4-967D-99206EC2B310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{731CC542-8BE9-42D4-967D-99206EC2B310}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{731CC542-8BE9-42D4-967D-99206EC2B310}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{731CC542-8BE9-42D4-967D-99206EC2B310}.Publish|Any CPU.Build.0 = Debug|Any CPU
|
||||
{731CC542-8BE9-42D4-967D-99206EC2B310}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{731CC542-8BE9-42D4-967D-99206EC2B310}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Publish|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Publish|Any CPU.Build.0 = Debug|Any CPU
|
||||
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{87AB5AF5-5783-4372-9789-664895E0A2FF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{95CAA25F-A0DE-4A5B-92BA-7D56C0E822A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{95CAA25F-A0DE-4A5B-92BA-7D56C0E822A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{95CAA25F-A0DE-4A5B-92BA-7D56C0E822A8}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
|
||||
{95CAA25F-A0DE-4A5B-92BA-7D56C0E822A8}.Publish|Any CPU.Build.0 = Publish|Any CPU
|
||||
{95CAA25F-A0DE-4A5B-92BA-7D56C0E822A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{95CAA25F-A0DE-4A5B-92BA-7D56C0E822A8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -412,8 +507,6 @@ Global
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A284C7EB-2248-4A75-B112-F5DCDE65410D} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
|
||||
{47C6F821-5103-431F-B3B8-A2868A68BB78} = {FA3720F1-C99A-49B2-9577-A940257098BF}
|
||||
{3EB61E99-C39B-4620-9482-F8DA18E48525} = {FA3720F1-C99A-49B2-9577-A940257098BF}
|
||||
{34A7F1EF-D243-4160-A413-D713FEABCD94} = {FA3720F1-C99A-49B2-9577-A940257098BF}
|
||||
{E4B777A1-28E1-41BE-96AE-7F3EC61FD5D4} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
|
||||
{F94D1938-9DB7-4B24-9FF3-166DDFD96330} = {D6D598DF-C17C-46F4-B2B9-CDE82E2DE132}
|
||||
{689A5041-BAE7-448F-9BDC-4672E96249AA} = {D6D598DF-C17C-46F4-B2B9-CDE82E2DE132}
|
||||
@@ -425,45 +518,61 @@ Global
|
||||
{F2A1F81E-700E-4C0E-B021-B9EF29AA20BD} = {9ECD1AA0-75B3-4E25-B0B5-9F0945B64974}
|
||||
{0247C2C9-86C3-45BA-8873-28B0948EDC0C} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
|
||||
{EB3FC57F-E591-4C88-BCD5-B6A1BC635168} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{5DEBAA62-F117-496A-8778-FED3604B70E2} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{EC004F12-2F60-4EDD-B3CD-3A504900D929} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{C9F957FA-A70F-4A6D-8F95-23FCD7F4FB87} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{3720F5ED-FB4D-485E-8A93-CDE60DEF0805} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{185E0CE8-C2DA-4E4C-A491-E8EB40316315} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{AFA81EB7-F869-467D-8A90-744305D80AAC} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{5DEBAA62-F117-496A-8778-FED3604B70E2} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{EC004F12-2F60-4EDD-B3CD-3A504900D929} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{C9F957FA-A70F-4A6D-8F95-23FCD7F4FB87} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{3720F5ED-FB4D-485E-8A93-CDE60DEF0805} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{185E0CE8-C2DA-4E4C-A491-E8EB40316315} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{AFA81EB7-F869-467D-8A90-744305D80AAC} = {1B4CBDE0-10C2-4E7D-9CD0-FE7586C96ED1}
|
||||
{627742DB-1E52-468A-99BD-6FF1A542D25B} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
|
||||
{E3299033-EB81-4C4C-BCD9-E8DC40937969} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
|
||||
{078F96B4-09E1-4E0E-B214-F71A4F4BF633} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
|
||||
{F51017A9-15C8-472D-893C-080046D710A6} = {078F96B4-09E1-4E0E-B214-F71A4F4BF633}
|
||||
{EC3BB6D1-2FB2-4702-84C6-F791DE533ED4} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{4D226C2F-AE9F-4EFB-AF2D-45C8FE5CB34E} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{EC3BB6D1-2FB2-4702-84C6-F791DE533ED4} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{4D226C2F-AE9F-4EFB-AF2D-45C8FE5CB34E} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{E52F805C-794A-4CA9-B684-DFF358B18820} = {9ECD1AA0-75B3-4E25-B0B5-9F0945B64974}
|
||||
{136823BE-8665-4D57-87E0-EF41535539E2} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{136823BE-8665-4D57-87E0-EF41535539E2} = {1B4CBDE0-10C2-4E7D-9CD0-FE7586C96ED1}
|
||||
{4D3DAE63-41C6-4E1C-A35A-E77BDFC40675} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
|
||||
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{4D91A3E0-C404-495B-AD4A-411C4E83CF54} = {FA3720F1-C99A-49B2-9577-A940257098BF}
|
||||
{50FAE231-6F24-4779-9D02-12ABBC9A49E2} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{50FAE231-6F24-4779-9D02-12ABBC9A49E2} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{5C246969-D794-4EC3-8E8F-F90D4D166420} = {4D3DAE63-41C6-4E1C-A35A-E77BDFC40675}
|
||||
{958AD708-F048-4FAF-94ED-D2F2B92748B9} = {4D3DAE63-41C6-4E1C-A35A-E77BDFC40675}
|
||||
{29E7D971-1308-4171-9872-E8E4669A1134} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
|
||||
{B00AD427-0047-4850-BEF9-BA8237EA9D8B} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
|
||||
{DB950192-30F1-48B1-88D7-F43FECCA1A1C} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
|
||||
{1C19D805-3573-4477-BF07-40180FCDE1BD} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
|
||||
{3CDE10B2-AE8F-4FC4-8D55-92D4AD32E144} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
|
||||
{E85EA4D0-BB7E-4DFD-882F-A76EB8C0B8FF} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
|
||||
{0D0C4DAD-E6BC-4504-AE3A-EEA4E35920C1} = {D6D598DF-C17C-46F4-B2B9-CDE82E2DE132}
|
||||
{E6EDAB8F-3406-4DBF-9AAB-DF40DC2CA0FA} = {FA3720F1-C99A-49B2-9577-A940257098BF}
|
||||
{C754950A-E16C-4F96-9CC7-9328E361B5AF} = {FA3720F1-C99A-49B2-9577-A940257098BF}
|
||||
{E07608CC-D710-4655-BB9E-D22CF3CDD193} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{10E4B697-D4E8-468D-872D-49670FD150FB} = {078F96B4-09E1-4E0E-B214-F71A4F4BF633}
|
||||
{D4540A0F-98E3-4E70-9093-1948AE5B2AAD} = {078F96B4-09E1-4E0E-B214-F71A4F4BF633}
|
||||
{3DC4DBD8-20A5-4937-B4F5-BB5E24E7A567} = {078F96B4-09E1-4E0E-B214-F71A4F4BF633}
|
||||
{E07608CC-D710-4655-BB9E-D22CF3CDD193} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{D6D598DF-C17C-46F4-B2B9-CDE82E2DE132} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
|
||||
{5CB78CE4-895B-4A14-98AA-716A37DEEBB1} = {D6D598DF-C17C-46F4-B2B9-CDE82E2DE132}
|
||||
{A21FAC7C-0C09-4EAD-843B-926ACEF73C80} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
|
||||
{F224B869-FA0E-4EEE-A6BF-C2D61FF8E731} = {A21FAC7C-0C09-4EAD-843B-926ACEF73C80}
|
||||
{CC77DCFA-A419-4202-A98A-868CDF457792} = {A21FAC7C-0C09-4EAD-843B-926ACEF73C80}
|
||||
{8B754E80-7A97-4585-8D7E-1D588FA5F727} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{8B754E80-7A97-4585-8D7E-1D588FA5F727} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{E91365A1-8B01-4AB8-825F-67E3515EADCD} = {D6D598DF-C17C-46F4-B2B9-CDE82E2DE132}
|
||||
{A2357CF8-3BB9-45A1-93F1-B366C9B63658} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
|
||||
{348BBF45-23B4-4599-83A6-8AE1795227FB} = {A21FAC7C-0C09-4EAD-843B-926ACEF73C80}
|
||||
{6009CC87-32F1-4282-88BB-8E5A7BA12925} = {24503383-A8C4-4255-9998-28D70FE8E99A}
|
||||
{B0646036-0C50-4F66-B479-ADA9C1166816} = {078F96B4-09E1-4E0E-B214-F71A4F4BF633}
|
||||
{4AD4E731-16E7-4A0E-B403-6C96459F989B} = {9ECD1AA0-75B3-4E25-B0B5-9F0945B64974}
|
||||
{E576E260-4030-4C4C-B207-CA3B684E9669} = {9ECD1AA0-75B3-4E25-B0B5-9F0945B64974}
|
||||
{352DAE69-E31C-44E3-A35F-E0342A955869} = {A21FAC7C-0C09-4EAD-843B-926ACEF73C80}
|
||||
{A7DAB812-81CF-4931-B38C-468A3261C4C8} = {4D3DAE63-41C6-4E1C-A35A-E77BDFC40675}
|
||||
{3F4E0DC5-2241-4EF2-9F69-E7EC7834D349} = {A7DAB812-81CF-4931-B38C-468A3261C4C8}
|
||||
{873281F6-9C31-4641-98AE-F1D2844DE9F0} = {A7DAB812-81CF-4931-B38C-468A3261C4C8}
|
||||
{3FC4A81B-8ABE-473F-BC7C-6F4885775534} = {A21FAC7C-0C09-4EAD-843B-926ACEF73C80}
|
||||
{1B4CBDE0-10C2-4E7D-9CD0-FE7586C96ED1} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{24503383-A8C4-4255-9998-28D70FE8E99A} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
|
||||
{5438D1E3-E03D-444B-BBBA-478F93161AA8} = {A2357CF8-3BB9-45A1-93F1-B366C9B63658}
|
||||
{4AD80279-9AC1-476F-8103-E6CD5E4FD525} = {A2357CF8-3BB9-45A1-93F1-B366C9B63658}
|
||||
{B0CE8C69-EC56-4825-94AB-01CA7E8BA55B} = {A2357CF8-3BB9-45A1-93F1-B366C9B63658}
|
||||
{3A4B8F90-3E74-43E0-800C-84F8AA9B5BF3} = {A2357CF8-3BB9-45A1-93F1-B366C9B63658}
|
||||
{731CC542-8BE9-42D4-967D-99206EC2B310} = {A2357CF8-3BB9-45A1-93F1-B366C9B63658}
|
||||
{A8E0D3B2-49D7-4DF6-BF91-B234C1C5E25D} = {FA3720F1-C99A-49B2-9577-A940257098BF}
|
||||
{87AB5AF5-5783-4372-9789-664895E0A2FF} = {FA3720F1-C99A-49B2-9577-A940257098BF}
|
||||
{95CAA25F-A0DE-4A5B-92BA-7D56C0E822A8} = {9ECD1AA0-75B3-4E25-B0B5-9F0945B64974}
|
||||
{EB2C141A-AE5F-4080-8790-13EB16323CEF} = {958AD708-F048-4FAF-94ED-D2F2B92748B9}
|
||||
{607DD6FA-FA0D-45E6-80BA-22A373609E89} = {5C246969-D794-4EC3-8E8F-F90D4D166420}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {FBDC56A3-86AD-4323-AA0F-201E59123B83}
|
||||
|
||||
@@ -135,6 +135,8 @@
|
||||
<s:Int64 x:Key="/Default/Environment/Hierarchy/Build/BuildTool/MsBuildSolutionLoadingNodeCount/@EntryValue">2</s:Int64>
|
||||
<s:Boolean x:Key="/Default/Environment/Hierarchy/Build/SolutionBuilderNext/LogToFile/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/Hierarchy/Build/SolutionBuilderNext/ShouldRestoreNugetPackages/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECodeCleanup_002EFileHeader_002EFileHeaderSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/Environment/Hierarchy/NuGetOptions/IntegratedRestoreEngine/@EntryValue">Console</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
@@ -227,6 +229,7 @@ public void It$SOMENAME$()
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Upsert/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=upserted/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Upserts/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Weaviate/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=wellknown/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Wordprocessing/@EntryIndexedValue">True</s:Boolean>
|
||||
</wpf:ResourceDictionary>
|
||||
@@ -0,0 +1,5 @@
|
||||
<SolutionConfiguration>
|
||||
<Settings>
|
||||
<SolutionConfigured>True</SolutionConfigured>
|
||||
</Settings>
|
||||
</SolutionConfiguration>
|
||||
@@ -0,0 +1,60 @@
|
||||
# This script is for local use to analyze code coverage in more detail using HTML report.
|
||||
|
||||
Param(
|
||||
[switch]$ProdPackagesOnly = $false
|
||||
)
|
||||
|
||||
# Generate a timestamp for the current date and time
|
||||
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
|
||||
|
||||
# Define paths
|
||||
$scriptPath = Get-Item -Path $PSScriptRoot
|
||||
$coverageOutputPath = Join-Path $scriptPath "TestResults\Coverage\$timestamp"
|
||||
$reportOutputPath = Join-Path $scriptPath "TestResults\Reports\$timestamp"
|
||||
|
||||
# Create output directories
|
||||
New-Item -ItemType Directory -Force -Path $coverageOutputPath
|
||||
New-Item -ItemType Directory -Force -Path $reportOutputPath
|
||||
|
||||
# Find tests for projects ending with 'UnitTests.csproj'
|
||||
$testProjects = Get-ChildItem $scriptPath -Filter "*UnitTests.csproj" -Recurse
|
||||
|
||||
foreach ($project in $testProjects) {
|
||||
$testProjectPath = $project.FullName
|
||||
Write-Host "Running tests for project: $($testProjectPath)"
|
||||
|
||||
# Run tests
|
||||
dotnet test $testProjectPath `
|
||||
--collect:"XPlat Code Coverage" `
|
||||
--results-directory:$coverageOutputPath `
|
||||
|
||||
}
|
||||
|
||||
# Install required tools
|
||||
& dotnet tool install -g coverlet.console
|
||||
& dotnet tool install -g dotnet-reportgenerator-globaltool
|
||||
|
||||
# Generate HTML report
|
||||
if ($ProdPackagesOnly) {
|
||||
$assemblies = @(
|
||||
"+Microsoft.SemanticKernel.Abstractions",
|
||||
"+Microsoft.SemanticKernel.Core",
|
||||
"+Microsoft.SemanticKernel.PromptTemplates.Handlebars",
|
||||
"+Microsoft.SemanticKernel.Connectors.OpenAI",
|
||||
"+Microsoft.SemanticKernel.Yaml"
|
||||
)
|
||||
|
||||
$assemblyFilters = $assemblies -join ";"
|
||||
|
||||
# Generate report for production assemblies only
|
||||
& reportgenerator -reports:"$coverageOutputPath/**/coverage.cobertura.xml" -targetdir:$reportOutputPath -reporttypes:Html -assemblyfilters:$assemblyFilters
|
||||
}
|
||||
else {
|
||||
& reportgenerator -reports:"$coverageOutputPath/**/coverage.cobertura.xml" -targetdir:$reportOutputPath -reporttypes:Html
|
||||
}
|
||||
|
||||
Write-Host "Code coverage report generated at: $reportOutputPath"
|
||||
|
||||
# Open report
|
||||
$reportIndexHtml = Join-Path $reportOutputPath "index.html"
|
||||
Invoke-Item -Path $reportIndexHtml
|
||||
@@ -0,0 +1,67 @@
|
||||
# Experiments
|
||||
|
||||
The following capabilities are marked experimental in the .NET SDK. Once the APIs for these features are stable, the experimental attribute will be removed. In the meantime, these features are subject to change.
|
||||
|
||||
You can use the following diagnostic IDs to ignore warnings or errors for a particular experimental feature. For example, to ignore warnings for the embedding services, add `SKEXP0001` to your list of ignored warnings in your .NET project file as well as the ID for the embedding service you want to use. For example:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<NoWarn>SKEXP0001,SKEXP0011</NoWarn>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
## Core
|
||||
|
||||
- SKEXP0001: Embedding services
|
||||
- SKEXP0002: Image services
|
||||
- SKEXP0003: Memory connectors
|
||||
- SKEXP0004: Kernel Filters
|
||||
|
||||
## OpenAI and Azure OpenAI services
|
||||
|
||||
- SKEXP0010: Azure OpenAI with your data service
|
||||
- SKEXP0011: OpenAI embedding service
|
||||
- SKEXP0012: OpenAI image service
|
||||
- SKEXP0013: OpenAI parameters
|
||||
- SKEXP0014: OpenAI chat history extension
|
||||
|
||||
## Memory connectors
|
||||
|
||||
- SKEXP0020: Hugging Face AI connector
|
||||
- SKEXP0021: Azure AI Search memory connector
|
||||
- SKEXP0022: Chroma memory connector
|
||||
- SKEXP0023: DuckDB memory connector
|
||||
- SKEXP0024: Kusto memory connector
|
||||
- SKEXP0025: Milvus memory connector
|
||||
- SKEXP0026: Qdrant memory connector
|
||||
- SKEXP0027: Redis memory connector
|
||||
- SKEXP0028: Sqlite memory connector
|
||||
- SKEXP0029: Weaviate memory connector
|
||||
- SKEXP0030: MongoDB memory connector
|
||||
- SKEXP0031: Pinecone memory connector
|
||||
- SKEXP0032: Postgres memory connector
|
||||
|
||||
## Functions
|
||||
|
||||
- SKEXP0040: GRPC functions
|
||||
- SKEXP0041: Markdown functions
|
||||
- SKEXP0042: OpenAPI functions
|
||||
|
||||
## Out-of-the-box plugins
|
||||
|
||||
- SKEXP0050: Core plugins
|
||||
- SKEXP0051: Document plugins
|
||||
- SKEXP0052: Memory plugins
|
||||
- SKEXP0053: Microsoft 365 plugins
|
||||
- SKEXP0054: Web plugins
|
||||
- SKEXP0055: Text chunkcer plugin
|
||||
|
||||
## Planners
|
||||
|
||||
- SKEXP0060: Handlebars planner
|
||||
- SKEXP0061: OpenAI Stepwise planner
|
||||
|
||||
## Experiments
|
||||
|
||||
- SKEXP0101: Experiment with Assistants
|
||||
- SKEXP0102: Experiment with Flow Orchestration
|
||||
+46
-78
@@ -3,7 +3,7 @@
|
||||
Telemetry in Semantic Kernel (SK) .NET implementation includes _logging_, _metering_ and _tracing_.
|
||||
The code is instrumented using native .NET instrumentation tools, which means that it's possible to use different monitoring platforms (e.g. Application Insights, Prometheus, Grafana etc.).
|
||||
|
||||
Code example using Application Insights can be found [here](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/ApplicationInsightsExample/Program.cs).
|
||||
Code example using Application Insights can be found [here](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/TelemetryExample).
|
||||
|
||||
## Logging
|
||||
|
||||
@@ -18,15 +18,16 @@ Log levels used in SK:
|
||||
- Prompt (template and rendered version) for AI to create a plan
|
||||
- Created plan with function arguments (arguments may contain sensitive data)
|
||||
- Prompt (template and rendered version) for AI to execute a function
|
||||
- Arguments to functions (arguments may contain sensitive data)
|
||||
- Debug - contains more detailed messages without sensitive data. Can be enabled in production environments.
|
||||
- Information (default) - log level that is enabled by default and provides information about general flow of the application. Contains following data:
|
||||
- AI model used to create a plan
|
||||
- Plan creation status (Success/Failed)
|
||||
- Plan creation execution time (in milliseconds)
|
||||
- Plan creation execution time (in seconds)
|
||||
- Created plan without function arguments
|
||||
- AI model used to execute a function
|
||||
- Function execution status (Success/Failed)
|
||||
- Function execution time (in milliseconds)
|
||||
- Function execution time (in seconds)
|
||||
- Warning - includes information about unusual events that don't cause the application to fail.
|
||||
- Error - used for logging exception details.
|
||||
|
||||
@@ -38,85 +39,67 @@ Enable logging for Kernel instance:
|
||||
var kernel = new KernelBuilder().WithLoggerFactory(loggerFactory);
|
||||
```
|
||||
|
||||
Enable logging for Planner instance (_metering_ and _tracing_ will be enabled as well):
|
||||
|
||||
```csharp
|
||||
var planner = new SequentialPlanner(kernel, plannerConfig).WithInstrumentation(loggerFactory);
|
||||
```
|
||||
All kernel functions and planners will be instrumented. It includes _logs_, _metering_ and _tracing_.
|
||||
|
||||
### Log Filtering Configuration
|
||||
|
||||
Log filtering configuration has been refined to strike a balance between visibility and relevance:
|
||||
|
||||
```csharp
|
||||
// Add OpenTelemetry as a logging provider
|
||||
builder.AddOpenTelemetry(options =>
|
||||
{
|
||||
options.AddAzureMonitorLogExporter(options => options.ConnectionString = connectionString);
|
||||
// Format log messages. This is default to false.
|
||||
options.IncludeFormattedMessage = true;
|
||||
});
|
||||
builder.AddFilter("Microsoft", LogLevel.Warning);
|
||||
builder.AddFilter("Microsoft.SemanticKernel", LogLevel.Critical);
|
||||
builder.AddFilter("Microsoft.SemanticKernel.Reliability", LogLevel.Information);
|
||||
```
|
||||
|
||||
> Read more at: https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/logs/customizing-the-sdk/README.md
|
||||
|
||||
## Metering
|
||||
|
||||
Metering is implemented with `Meter` class from `System.Diagnostics.Metrics` namespace.
|
||||
|
||||
Available meters:
|
||||
|
||||
- _Microsoft.SemanticKernel.Planning.Action.InstrumentedActionPlanner_ - captures metrics for `ActionPlanner`. List of metrics:
|
||||
- `SK.ActionPlanner.CreatePlan.ExecutionTime` - execution time of plan creation (in milliseconds)
|
||||
- _Microsoft.SemanticKernel.Planning.Sequential.InstrumentedSequentialPlanner_ - captures metrics for `SequentialPlanner`. List of metrics:
|
||||
- `SK.SequentialPlanner.CreatePlan.ExecutionTime` - execution time of plan creation (in milliseconds)
|
||||
- _Microsoft.SemanticKernel.Planning.Stepwise.StepwisePlanner_ - captures metrics for `StepwisePlanner`. List of metrics:
|
||||
- `SK.StepwisePlanner.CreatePlan.ExecutionTime` - execution time of plan creation (in milliseconds)
|
||||
- _Microsoft.SemanticKernel.Planning.Plan_ - captures metrics for `Plan`. List of metrics:
|
||||
- `SK.Plan.Execution.ExecutionTime` - plan execution time (in milliseconds)
|
||||
- `SK.Plan.Execution.ExecutionTotal` - total number of plan executions
|
||||
- `SK.Plan.Execution.ExecutionSuccess` - number of successful plan executions
|
||||
- `SK.Plan.Execution.ExecutionFailure` - number of failed plan executions
|
||||
- _Microsoft.SemanticKernel.SKFunction_ - captures metrics for `SKFunction`. List of metrics:
|
||||
- `SK.<PluginName><FunctionName>.ExecutionTime` - function execution time (in milliseconds)
|
||||
- `SK.<PluginName><FunctionName>.ExecutionTotal` - total number of function executions
|
||||
- `SK.<PluginName><FunctionName>.ExecutionSuccess` - number of successful function executions
|
||||
- `SK.<PluginName><FunctionName>.ExecutionFailure` - number of failed function executions
|
||||
- _Microsoft.SemanticKernel.Connectors.AI.OpenAI_ - captures metrics for OpenAI functionality. List of metrics:
|
||||
- `SK.Connectors.OpenAI.PromptTokens` - number of prompt tokens used.
|
||||
- `SK.Connectors.OpenAI.CompletionTokens` - number of completion tokens used.
|
||||
- `SK.Connectors.OpenAI.TotalTokens` - total number of tokens used.
|
||||
- _Microsoft.SemanticKernel.Planning_ - contains all metrics related to planning. List of metrics:
|
||||
- `semantic_kernel.planning.create_plan.duration` (Histogram) - execution time of plan creation (in seconds)
|
||||
- `semantic_kernel.planning.invoke_plan.duration` (Histogram) - execution time of plan execution (in seconds)
|
||||
- _Microsoft.SemanticKernel_ - captures metrics for `KernelFunction`. List of metrics:
|
||||
- `semantic_kernel.function.invocation.duration` (Histogram) - function execution time (in seconds)
|
||||
- `semantic_kernel.function.streaming.duration` (Histogram) - function streaming execution time (in seconds)
|
||||
- `semantic_kernel.function.invocation.token_usage.prompt` (Histogram) - number of prompt token usage (only for `KernelFunctionFromPrompt`)
|
||||
- `semantic_kernel.function.invocation.token_usage.completion` (Histogram) - number of completion token usage (only for `KernelFunctionFromPrompt`)
|
||||
- _Microsoft.SemanticKernel.Connectors.OpenAI_ - captures metrics for OpenAI functionality. List of metrics:
|
||||
- `semantic_kernel.connectors.openai.tokens.prompt` (Counter) - number of prompt tokens used.
|
||||
- `semantic_kernel.connectors.openai.tokens.completion` (Counter) - number of completion tokens used.
|
||||
- `semantic_kernel.connectors.openai.tokens.total` (Counter) - total number of tokens used.
|
||||
|
||||
### Examples
|
||||
|
||||
Depending on monitoring tool, there are different ways how to subscribe to available meters. Following example shows how to subscribe to available meters and export metrics to Application Insights using `MeterListener`:
|
||||
Measurements will be associated with tags that will allow data to be categorized for analysis:
|
||||
|
||||
```csharp
|
||||
var meterListener = new MeterListener();
|
||||
|
||||
meterListener.InstrumentPublished = (instrument, listener) =>
|
||||
{
|
||||
if (instrument.Meter.Name.StartsWith("Microsoft.SemanticKernel", StringComparison.Ordinal))
|
||||
{
|
||||
listener.EnableMeasurementEvents(instrument);
|
||||
}
|
||||
};
|
||||
|
||||
// Set callback to specific numeric type - double.
|
||||
meterListener.SetMeasurementEventCallback<double>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
// Export to Application Insights using telemetry client instance
|
||||
telemetryClient.GetMetric(instrument.Name).TrackValue(measurement);
|
||||
});
|
||||
|
||||
meterListener.Start();
|
||||
TagList tags = new() { { "semantic_kernel.function.name", this.Name } };
|
||||
s_invocationDuration.Record(duration.TotalSeconds, in tags);
|
||||
```
|
||||
|
||||
It's possible to control for what meters to subscribe. For example, following condition will allow to subscribe to all meters in Semantic Kernel:
|
||||
### [Examples](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/TelemetryExample/Program.cs)
|
||||
|
||||
Depending on monitoring tool, there are different ways how to subscribe to available meters. Following example shows how to subscribe to available meters and export metrics to Application Insights using `OpenTelemetry.Sdk`:
|
||||
|
||||
```csharp
|
||||
instrument.Meter.Name.StartsWith("Microsoft.SemanticKernel", StringComparison.Ordinal)
|
||||
using var meterProvider = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter("Microsoft.SemanticKernel*")
|
||||
.AddAzureMonitorMetricExporter(options => options.ConnectionString = connectionString)
|
||||
.Build();
|
||||
```
|
||||
|
||||
It's also possible to subscribe to specific meter. Following condition will allow to subscribe to meter for `SKFunction` only:
|
||||
> Read more at: https://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-enable?tabs=net
|
||||
|
||||
```csharp
|
||||
instrument.Meter.Name.Equals("Microsoft.SemanticKernel.SKFunction", StringComparison.Ordinal)
|
||||
```
|
||||
> Read more at: https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/metrics/customizing-the-sdk/README.md
|
||||
|
||||
## Tracing
|
||||
|
||||
@@ -124,33 +107,18 @@ Tracing is implemented with `Activity` class from `System.Diagnostics` namespace
|
||||
|
||||
Available activity sources:
|
||||
|
||||
- _Microsoft.SemanticKernel.Planning.Action.InstrumentedActionPlanner_ - creates activities for `ActionPlanner`.
|
||||
- _Microsoft.SemanticKernel.Planning.Sequential.InstrumentedSequentialPlanner_ - creates activities for `SequentialPlanner`.
|
||||
- _Microsoft.SemanticKernel.Planning.Stepwise.StepwisePlanner_ - creates activities for `StepwisePlanner`.
|
||||
- _Microsoft.SemanticKernel.Planning.Plan_ - creates activities for `Plan`.
|
||||
- _Microsoft.SemanticKernel.SKFunction_ - creates activities for `SKFunction`.
|
||||
- _Microsoft.SemanticKernel.Planning_ - creates activities for all planners.
|
||||
- _Microsoft.SemanticKernel_ - creates activities for `KernelFunction`.
|
||||
|
||||
### Examples
|
||||
|
||||
Subscribe to available activity sources using `ActivityListener`:
|
||||
Subscribe to available activity sources using `OpenTelemetry.Sdk`:
|
||||
|
||||
```csharp
|
||||
var activityListener = new ActivityListener();
|
||||
|
||||
activityListener.ShouldListenTo =
|
||||
activitySource => activitySource.Name.StartsWith("Microsoft.SemanticKernel", StringComparison.Ordinal);
|
||||
|
||||
ActivitySource.AddActivityListener(activityListener);
|
||||
using var traceProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddSource("Microsoft.SemanticKernel*")
|
||||
.AddAzureMonitorTraceExporter(options => options.ConnectionString = connectionString)
|
||||
.Build();
|
||||
```
|
||||
|
||||
Following condition will allow to subscribe to all activity sources in Semantic Kernel:
|
||||
|
||||
```csharp
|
||||
activitySource.Name.StartsWith("Microsoft.SemanticKernel", StringComparison.Ordinal)
|
||||
```
|
||||
|
||||
It's also possible to subscribe to specific activity source. Following condition will allow to subscribe to activity source for `SKFunction` only:
|
||||
|
||||
```csharp
|
||||
activitySource.Name.Equals("Microsoft.SemanticKernel.SKFunction", StringComparison.Ordinal)
|
||||
```
|
||||
> Read more at: https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/trace/customizing-the-sdk/README.md
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"#### Watch the Getting Started Quick Start [Video](https://aka.ms/SK-Getting-Started-Notebook)\n",
|
||||
"\n",
|
||||
"> [!IMPORTANT]\n",
|
||||
"> You will need an [.Net 7 SDK](https://dotnet.microsoft.com/en-us/download) and [Polyglot](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) to get started with this notebook using .Net Interactive"
|
||||
"> You will need an [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) and [Polyglot](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) to get started with this notebook using .NET Interactive."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -61,7 +61,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"// Import Semantic Kernel\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\""
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -86,9 +86,10 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"//Create Kernel builder\n",
|
||||
"var builder = new KernelBuilder();"
|
||||
"var builder = Kernel.CreateBuilder();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -108,11 +109,11 @@
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
" builder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
|
||||
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" builder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
|
||||
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"IKernel kernel = builder.Build();"
|
||||
"var kernel = builder.Build();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -136,33 +137,20 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"// Load the Plugins Directory\n",
|
||||
"var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"..\", \"..\", \"samples\", \"plugins\");\n",
|
||||
"// FunPlugin directory path\n",
|
||||
"var funPluginDirectoryPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"..\", \"..\", \"samples\", \"plugins\", \"FunPlugin\");\n",
|
||||
"\n",
|
||||
"// Load the FunPlugin from the Plugins Directory\n",
|
||||
"var funPluginFunctions = kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, \"FunPlugin\");\n",
|
||||
"var funPluginFunctions = kernel.ImportPluginFromPromptDirectory(funPluginDirectoryPath);\n",
|
||||
"\n",
|
||||
"// Construct arguments\n",
|
||||
"var arguments = new KernelArguments() { [\"input\"] = \"time travel to dinosaur age\" };\n",
|
||||
"\n",
|
||||
"// Run the Function called Joke\n",
|
||||
"var result = await kernel.RunAsync(\"time travel to dinosaur age\", funPluginFunctions[\"Joke\"]);\n",
|
||||
"var resultString = result.GetValue<string>();\n",
|
||||
"var result = await kernel.InvokeAsync(funPluginFunctions[\"Joke\"], arguments);\n",
|
||||
"\n",
|
||||
"// Return the result to the Notebook\n",
|
||||
"Console.WriteLine(resultString);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Next Steps**: You know the basics, let's try this in a sample app so you can learn the core concepts!\n",
|
||||
"\n",
|
||||
"Sample app learning examples:\n",
|
||||
"- [Simple chat summary](../../samples/apps/chat-summary-webapp-react/README.md) (**Recommended**) – learn how basic semantic functions can be added to an app\n",
|
||||
"- [Book creator](../../samples/apps/book-creator-webapp-react/README.md) – learn how Planner and chaining of semantic functions can be used in your app\n",
|
||||
"- [Authentication and APIs](../../samples/dotnet/MsGraphPluginsExample/README.md) – learn how to connect to external API's with authentication while using Semantic Kernel\n",
|
||||
"- [GitHub repository Q&A](../../samples/apps/github-qna-webapp-react/README.md) - Use embeddings and memory to store and query your data\n",
|
||||
"- [Copilot Chat](../../samples/apps/copilot-chat-app/README.md) – Build your own chatbot based on Semantic Kernel"
|
||||
"Console.WriteLine(result);"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\""
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -40,7 +40,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"After adding the nuget package, you can instantiate the kernel in a few ways, depending on your use case.\n"
|
||||
"After adding the nuget package, you can instantiate the kernel:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -60,36 +60,19 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"\n",
|
||||
"// Simple instance\n",
|
||||
"IKernel kernel_1 = KernelBuilder.Create();"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"dotnet_interactive": {
|
||||
"language": "csharp"
|
||||
},
|
||||
"polyglot_notebook": {
|
||||
"kernelName": "csharp"
|
||||
},
|
||||
"vscode": {
|
||||
"languageId": "polyglot-notebook"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using Microsoft.Extensions.Logging;\n",
|
||||
"using Microsoft.Extensions.Logging.Abstractions;\n",
|
||||
"using Microsoft.Extensions.DependencyInjection;\n",
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"// Inject your logger \n",
|
||||
"// see Microsoft.Extensions.Logging.ILogger @ https://learn.microsoft.com/dotnet/core/extensions/logging\n",
|
||||
"ILoggerFactory myLoggerFactory = NullLoggerFactory.Instance;\n",
|
||||
"IKernel kernel_2 = new KernelBuilder()\n",
|
||||
" .WithLoggerFactory(myLoggerFactory)\n",
|
||||
" .Build();"
|
||||
"\n",
|
||||
"var builder = Kernel.CreateBuilder();\n",
|
||||
"builder.Services.AddSingleton(myLoggerFactory);\n",
|
||||
"\n",
|
||||
"var kernel = builder.Build();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -120,14 +103,14 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"Kernel.Builder\n",
|
||||
".WithAzureChatCompletionService(\n",
|
||||
"Kernel.CreateBuilder()\n",
|
||||
".AddAzureOpenAIChatCompletion(\n",
|
||||
" \"my-finetuned-model\", // Azure OpenAI *Deployment Name*\n",
|
||||
" \"https://contoso.openai.azure.com/\", // Azure OpenAI *Endpoint*\n",
|
||||
" \"...your Azure OpenAI Key...\", // Azure OpenAI *Key*\n",
|
||||
" serviceId: \"Azure_curie\" // alias used in the prompt templates' config.json\n",
|
||||
")\n",
|
||||
".WithOpenAIChatCompletionService(\n",
|
||||
".AddOpenAIChatCompletion(\n",
|
||||
" \"gpt-3.5-turbo\", // OpenAI Model Name\n",
|
||||
" \"...your OpenAI API Key...\", // OpenAI API key\n",
|
||||
" \"...your OpenAI Org ID...\", // *optional* OpenAI Organization ID\n",
|
||||
@@ -144,35 +127,7 @@
|
||||
"is also the \"**default**\" used in these scenarios:\n",
|
||||
"\n",
|
||||
"* a prompt configuration doesn't specify which AI backend to use\n",
|
||||
"* a prompt configuration requires a backend unknown to the kernel\n",
|
||||
"\n",
|
||||
"The default can be set programmatically:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"dotnet_interactive": {
|
||||
"language": "csharp"
|
||||
},
|
||||
"polyglot_notebook": {
|
||||
"kernelName": "csharp"
|
||||
},
|
||||
"vscode": {
|
||||
"languageId": "polyglot-notebook"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"Kernel.Builder\n",
|
||||
".WithOpenAIChatCompletionService(\n",
|
||||
" \"gpt-3.5-turbo\", // OpenAI Model Name\n",
|
||||
" \"...your OpenAI API Key...\", // OpenAI API key\n",
|
||||
" \"...your OpenAI Org ID...\", // *optional* OpenAI Organization ID\n",
|
||||
" \"OpenAI_davinci\", // alias used in the prompt templates' config.json\n",
|
||||
" true // This flag specifies that this service is the default one.\n",
|
||||
");"
|
||||
"* a prompt configuration requires a backend unknown to the kernel"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -57,11 +57,13 @@
|
||||
"{\n",
|
||||
" \"schema\": 1,\n",
|
||||
" \"description\": \"Generate a funny joke\",\n",
|
||||
" \"models\": [\n",
|
||||
" \"execution_settings\": [\n",
|
||||
" {\n",
|
||||
" \"max_tokens\": 500,\n",
|
||||
" \"temperature\": 0.5,\n",
|
||||
" \"top_p\": 0.5\n",
|
||||
" \"max_tokens\": 1000,\n",
|
||||
" \"temperature\": 0.9,\n",
|
||||
" \"top_p\": 0.0,\n",
|
||||
" \"presence_penalty\": 0.0,\n",
|
||||
" \"frequency_penalty\": 0.0\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
"}\n",
|
||||
@@ -91,22 +93,24 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\"\n",
|
||||
"\n",
|
||||
"#!import config/Settings.cs\n",
|
||||
"\n",
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"var builder = new KernelBuilder();\n",
|
||||
"var builder = Kernel.CreateBuilder();\n",
|
||||
"\n",
|
||||
"// Configure AI backend used by the kernel\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
" builder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" builder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"IKernel kernel = builder.Build();"
|
||||
"if (useAzureOpenAI)\n",
|
||||
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"var kernel = builder.Build();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -130,10 +134,11 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"// note: using plugins from the repo\n",
|
||||
"var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"..\", \"..\", \"samples\", \"plugins\");\n",
|
||||
"// FunPlugin directory path\n",
|
||||
"var funPluginDirectoryPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"..\", \"..\", \"samples\", \"plugins\", \"FunPlugin\");\n",
|
||||
"\n",
|
||||
"var funPluginFunctions = kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, \"FunPlugin\");"
|
||||
"// Load the FunPlugin from the Plugins Directory\n",
|
||||
"var funPluginFunctions = kernel.ImportPluginFromPromptDirectory(funPluginDirectoryPath);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -157,10 +162,14 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var result = await kernel.RunAsync(\"time travel to dinosaur age\", funPluginFunctions[\"Joke\"]);\n",
|
||||
"var resultString = result.GetValue<string>();\n",
|
||||
"// Construct arguments\n",
|
||||
"var arguments = new KernelArguments() { [\"input\"] = \"time travel to dinosaur age\" };\n",
|
||||
"\n",
|
||||
"Console.WriteLine(resultString);"
|
||||
"// Run the Function called Joke\n",
|
||||
"var result = await kernel.InvokeAsync(funPluginFunctions[\"Joke\"], arguments);\n",
|
||||
"\n",
|
||||
"// Return the result to the Notebook\n",
|
||||
"Console.WriteLine(result);"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"For now we'll use only the `{{$input}}` variable, and see more complex templates later.\n",
|
||||
"\n",
|
||||
"Almost all semantic function prompts have a reference to `{{$input}}`, which is the default way\n",
|
||||
"a user can import content from the context variables."
|
||||
"a user can import content from the kernel arguments."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -51,24 +51,26 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\"\n",
|
||||
"\n",
|
||||
"#!import config/Settings.cs\n",
|
||||
"\n",
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"using Microsoft.SemanticKernel.SemanticFunctions;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.AI.OpenAI;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.OpenAI;\n",
|
||||
"using Microsoft.SemanticKernel.TemplateEngine;\n",
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"var builder = new KernelBuilder();\n",
|
||||
"var builder = Kernel.CreateBuilder();\n",
|
||||
"\n",
|
||||
"// Configure AI backend used by the kernel\n",
|
||||
"// Configure AI service credentials used by the kernel\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
" builder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" builder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"IKernel kernel = builder.Build();"
|
||||
"if (useAzureOpenAI)\n",
|
||||
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"var kernel = builder.Build();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -120,15 +122,12 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var aiRequestSettings = new OpenAIRequestSettings \n",
|
||||
"var executionSettings = new OpenAIPromptExecutionSettings \n",
|
||||
"{\n",
|
||||
" MaxTokens = 2000,\n",
|
||||
" Temperature = 0.2,\n",
|
||||
" TopP = 0.5\n",
|
||||
"};\n",
|
||||
"\n",
|
||||
"var promptConfig = new PromptTemplateConfig();\n",
|
||||
"promptConfig.ModelSettings.Add(aiRequestSettings);"
|
||||
"};"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -137,7 +136,9 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The following code prepares an instance of the template, passing in the TXT and configuration above, \n",
|
||||
"and a couple of other parameters (how to render the TXT and how the template can access other functions)."
|
||||
"and a couple of other parameters (how to render the TXT and how the template can access other functions).\n",
|
||||
"\n",
|
||||
"This allows to see the prompt before it's sent to AI."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -153,11 +154,14 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var promptTemplate = new PromptTemplate(\n",
|
||||
" skPrompt, // Prompt template defined in natural language\n",
|
||||
" promptConfig, // Prompt configuration\n",
|
||||
" kernel // SK instance\n",
|
||||
");"
|
||||
"var promptTemplateConfig = new PromptTemplateConfig(skPrompt);\n",
|
||||
"\n",
|
||||
"var promptTemplateFactory = new KernelPromptTemplateFactory();\n",
|
||||
"var promptTemplate = promptTemplateFactory.Create(promptTemplateConfig);\n",
|
||||
"\n",
|
||||
"var renderedPrompt = await promptTemplate.RenderAsync(kernel);\n",
|
||||
"\n",
|
||||
"Console.WriteLine(renderedPrompt);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -181,9 +185,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var functionConfig = new SemanticFunctionConfig(promptConfig, promptTemplate);\n",
|
||||
"\n",
|
||||
"var summaryFunction = kernel.RegisterSemanticFunction(\"MyPlugin\", \"Summary\", functionConfig);"
|
||||
"var summaryFunction = kernel.CreateFunctionFromPrompt(skPrompt, executionSettings);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -241,10 +243,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var summaryResult = await kernel.RunAsync(input, summaryFunction);\n",
|
||||
"var summary = summaryResult.GetValue<string>();\n",
|
||||
"var summaryResult = await kernel.InvokeAsync(summaryFunction, new() { [\"input\"] = input });\n",
|
||||
"\n",
|
||||
"Console.WriteLine(summary);"
|
||||
"Console.WriteLine(summaryResult);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -255,7 +256,7 @@
|
||||
"The code above shows all the steps, to understand how the function is composed step by step. However, the kernel\n",
|
||||
"includes also some helpers to achieve the same more concisely.\n",
|
||||
"\n",
|
||||
"The same function above can be created with less code:"
|
||||
"The same function above can be executed with less code:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -277,12 +278,9 @@
|
||||
"Summarize the content above.\n",
|
||||
"\"\"\";\n",
|
||||
"\n",
|
||||
"var summaryFunction = kernel.CreateSemanticFunction(skPrompt, requestSettings: new OpenAIRequestSettings { MaxTokens = 2000, Temperature = 0.2, TopP = 0.5 });\n",
|
||||
"var result = await kernel.InvokePromptAsync(skPrompt, new() { [\"input\"] = input });\n",
|
||||
"\n",
|
||||
"var summaryResult = await kernel.RunAsync(input, summaryFunction);\n",
|
||||
"var summary = summaryResult.GetValue<string>();\n",
|
||||
"\n",
|
||||
"Console.WriteLine(summary);"
|
||||
"Console.WriteLine(result);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -309,17 +307,6 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var builder = new KernelBuilder();\n",
|
||||
"\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
" builder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" builder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"var kernel = builder.Build();\n",
|
||||
"\n",
|
||||
"string skPrompt = @\"\n",
|
||||
"{{$input}}\n",
|
||||
"\n",
|
||||
@@ -337,14 +324,9 @@
|
||||
" does not conflict with the First or Second Law.\n",
|
||||
"\";\n",
|
||||
"\n",
|
||||
"var tldrFunction = kernel.CreateSemanticFunction(skPrompt, requestSettings: new OpenAIRequestSettings { MaxTokens = 2000, Temperature = 0.2, TopP = 0.5 });\n",
|
||||
"var result = await kernel.InvokePromptAsync(skPrompt, new() { [\"input\"] = textToSummarize });\n",
|
||||
"\n",
|
||||
"var summaryResult = await kernel.RunAsync(textToSummarize, tldrFunction);\n",
|
||||
"var summary = summaryResult.GetValue<string>();\n",
|
||||
"\n",
|
||||
"Console.WriteLine(summary);\n",
|
||||
"\n",
|
||||
"// Output => Robots must not harm humans."
|
||||
"Console.WriteLine(result);"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
+37
-43
@@ -5,17 +5,13 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Creating a basic chat experience with context variables\n",
|
||||
"# Creating a basic chat experience with kernel arguments\n",
|
||||
"\n",
|
||||
"In this example, we show how you can build a simple chat bot by sending and updating context with your requests. \n",
|
||||
"In this example, we show how you can build a simple chat bot by sending and updating arguments with your requests. \n",
|
||||
"\n",
|
||||
"We introduce the Context Variables object which in this demo functions similarly as a key-value store that you can use when running the kernel.\n",
|
||||
"We introduce the Kernel Arguments object which in this demo functions similarly as a key-value store that you can use when running the kernel. \n",
|
||||
"\n",
|
||||
"The context is local (i.e. in your computer's RAM) and not persisted anywhere beyond the life of this Jupyter session.\n",
|
||||
"\n",
|
||||
"In future examples, we will show how to persist the context on disk so that you can bring it into your applications. \n",
|
||||
"\n",
|
||||
"In this chat scenario, as the user talks back and forth with the bot, the context gets populated with the history of the conversation. During each new run of the kernel, the context can provide the AI with its variables' content. "
|
||||
"In this chat scenario, as the user talks back and forth with the bot, the arguments get populated with the history of the conversation. During each new run of the kernel, the arguments will be provided to the AI with content. "
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -34,24 +30,24 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\"\n",
|
||||
"#!import config/Settings.cs\n",
|
||||
"\n",
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"using Microsoft.SemanticKernel.SemanticFunctions;\n",
|
||||
"using Microsoft.SemanticKernel.Orchestration;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.AI.OpenAI;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.OpenAI;\n",
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"var builder = new KernelBuilder();\n",
|
||||
"var builder = Kernel.CreateBuilder();\n",
|
||||
"\n",
|
||||
"// Configure AI backend used by the kernel\n",
|
||||
"// Configure AI service credentials used by the kernel\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
" builder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" builder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"IKernel kernel = builder.Build();"
|
||||
"if (useAzureOpenAI)\n",
|
||||
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"var kernel = builder.Build();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -86,15 +82,12 @@
|
||||
"User: {{$userInput}}\n",
|
||||
"ChatBot:\";\n",
|
||||
"\n",
|
||||
"var aiRequestSettings = new OpenAIRequestSettings \n",
|
||||
"var executionSettings = new OpenAIPromptExecutionSettings \n",
|
||||
"{\n",
|
||||
" MaxTokens = 2000,\n",
|
||||
" Temperature = 0.7,\n",
|
||||
" TopP = 0.5\n",
|
||||
"};\n",
|
||||
"\n",
|
||||
"var promptConfig = new PromptTemplateConfig();\n",
|
||||
"promptConfig.ModelSettings.Add(aiRequestSettings);"
|
||||
"};"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -121,9 +114,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var promptTemplate = new PromptTemplate(skPrompt, promptConfig, kernel);\n",
|
||||
"var functionConfig = new SemanticFunctionConfig(promptConfig, promptTemplate);\n",
|
||||
"var chatFunction = kernel.RegisterSemanticFunction(\"ChatBot\", \"Chat\", functionConfig);"
|
||||
"var chatFunction = kernel.CreateFunctionFromPrompt(skPrompt, executionSettings);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -131,7 +122,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Initialize your context"
|
||||
"Initialize your arguments"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -150,10 +141,11 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var context = kernel.CreateNewContext();\n",
|
||||
"\n",
|
||||
"var history = \"\";\n",
|
||||
"context.Variables[\"history\"] = history;"
|
||||
"var arguments = new KernelArguments()\n",
|
||||
"{\n",
|
||||
" [\"history\"] = history\n",
|
||||
"};"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -181,9 +173,9 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var userInput = \"Hi, I'm looking for book suggestions\";\n",
|
||||
"context.Variables[\"userInput\"] = userInput;\n",
|
||||
"arguments[\"userInput\"] = userInput;\n",
|
||||
"\n",
|
||||
"var bot_answer = await chatFunction.InvokeAsync(context);"
|
||||
"var bot_answer = await chatFunction.InvokeAsync(kernel, arguments);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -210,10 +202,10 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"history += $\"\\nUser: {userInput}\\nMelody: {bot_answer.GetValue<string>()}\\n\";\n",
|
||||
"context.Variables.Update(history);\n",
|
||||
"history += $\"\\nUser: {userInput}\\nAI: {bot_answer}\\n\";\n",
|
||||
"arguments[\"history\"] = history;\n",
|
||||
"\n",
|
||||
"Console.WriteLine(context);"
|
||||
"Console.WriteLine(history);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -241,18 +233,20 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"Func<string, Task> Chat = async (string input) => {\n",
|
||||
" // Save new message in the context variables\n",
|
||||
" context.Variables[\"userInput\"] = input;\n",
|
||||
" // Save new message in the arguments\n",
|
||||
" arguments[\"userInput\"] = input;\n",
|
||||
"\n",
|
||||
" // Process the user message and get an answer\n",
|
||||
" var answer = await chatFunction.InvokeAsync(context);\n",
|
||||
" var answer = await chatFunction.InvokeAsync(kernel, arguments);\n",
|
||||
"\n",
|
||||
" // Append the new interaction to the chat history\n",
|
||||
" history += $\"\\nUser: {input}\\nMelody: {answer.GetValue<string>()}\\n\"; \n",
|
||||
" context.Variables[\"history\"] = history;\n",
|
||||
" var result = $\"\\nUser: {input}\\nAI: {answer}\\n\";\n",
|
||||
" history += result;\n",
|
||||
"\n",
|
||||
" arguments[\"history\"] = history;\n",
|
||||
" \n",
|
||||
" // Show the response\n",
|
||||
" Console.WriteLine(context);\n",
|
||||
" Console.WriteLine(result);\n",
|
||||
"};"
|
||||
]
|
||||
},
|
||||
@@ -356,7 +350,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"Console.WriteLine(context.Variables[\"history\"]);"
|
||||
"Console.WriteLine(history);"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -25,27 +25,25 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel.Planners.Handlebars, 1.0.1-preview\"\n",
|
||||
"\n",
|
||||
"#!import config/Settings.cs\n",
|
||||
"#!import config/Utils.cs\n",
|
||||
"\n",
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"using Microsoft.SemanticKernel.Plugins.Core;\n",
|
||||
"using Microsoft.SemanticKernel.Orchestration;\n",
|
||||
"using Microsoft.SemanticKernel.Planning;\n",
|
||||
"using Microsoft.SemanticKernel.Planners;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.AI.OpenAI;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.OpenAI;\n",
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"var builder = new KernelBuilder();\n",
|
||||
"var builder = Kernel.CreateBuilder();\n",
|
||||
"\n",
|
||||
"// Configure AI backend used by the kernel\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
" builder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
|
||||
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" builder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
|
||||
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"var kernel = builder.Build();"
|
||||
]
|
||||
@@ -55,8 +53,8 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Setting Up the Planner\n",
|
||||
"The planner is located in the `Microsoft.SemanticKernel.Planners.Core` package and requires Orchestration"
|
||||
"### Setting Up Handlebars Planner\n",
|
||||
"Handlebars Planner is located in the `Microsoft.SemanticKernel.Planning.Handlebars` package."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -72,8 +70,11 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"// Load native plugin into the kernel registry, sharing its functions with prompt templates\n",
|
||||
"var planner = new SequentialPlanner(kernel);"
|
||||
"using Microsoft.SemanticKernel.Planning.Handlebars;\n",
|
||||
"\n",
|
||||
"#pragma warning disable SKEXP0060\n",
|
||||
"\n",
|
||||
"var planner = new HandlebarsPlanner();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -82,7 +83,7 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Providing plugins to the planner\n",
|
||||
"The planner needs to know what plugins are available to it. Here we'll give it access to the `SummarizePlugin` and `WriterPlugin` we have defined on disk."
|
||||
"The planner needs to know what plugins are available to it. Here we'll import the `SummarizePlugin` and `WriterPlugin` we have defined on disk."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -99,8 +100,9 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"..\", \"..\", \"samples\", \"plugins\");\n",
|
||||
"kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, \"SummarizePlugin\");\n",
|
||||
"kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, \"WriterPlugin\");"
|
||||
"\n",
|
||||
"kernel.ImportPluginFromPromptDirectory(Path.Combine(pluginsDirectory, \"SummarizePlugin\"));\n",
|
||||
"kernel.ImportPluginFromPromptDirectory(Path.Combine(pluginsDirectory, \"WriterPlugin\"));"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -124,11 +126,13 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#pragma warning disable SKEXP0060\n",
|
||||
"\n",
|
||||
"var ask = \"Tomorrow is Valentine's day. I need to come up with a few date ideas. My significant other likes poems so write them in the form of a poem.\";\n",
|
||||
"var originalPlan = await planner.CreatePlanAsync(ask);\n",
|
||||
"var originalPlan = await planner.CreatePlanAsync(kernel, ask);\n",
|
||||
"\n",
|
||||
"Console.WriteLine(\"Original plan:\\n\");\n",
|
||||
"Console.WriteLine(JsonSerializer.Serialize(originalPlan, new JsonSerializerOptions { WriteIndented = true }));"
|
||||
"Console.WriteLine(originalPlan);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -138,9 +142,7 @@
|
||||
"source": [
|
||||
"As you can see in the above plan, the Planner has taken the user's ask and converted it into a Plan object detailing how the AI would go about solving this task.\n",
|
||||
"\n",
|
||||
"It makes use of the plugins that the Kernel has available to it and determines which functions to call in order to fulfill the user's ask.\n",
|
||||
"\n",
|
||||
"The output of each step of the plan gets set as `setContextVariable` which makes it available as `input` to the next plugin."
|
||||
"It makes use of the plugins that the Kernel has available to it and determines which functions to call in order to fulfill the user's ask."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -170,7 +172,15 @@
|
||||
"\n",
|
||||
"Rewrite the above in the style of Shakespeare.\n",
|
||||
"\"\"\";\n",
|
||||
"var shakespeareFunction = kernel.CreateSemanticFunction(skPrompt, \"Shakespeare\", \"ShakespearePlugin\", requestSettings: new OpenAIRequestSettings { MaxTokens = 2000, Temperature = 0.2, TopP = 0.5 });"
|
||||
"\n",
|
||||
"var executionSettings = new OpenAIPromptExecutionSettings \n",
|
||||
"{\n",
|
||||
" MaxTokens = 2000,\n",
|
||||
" Temperature = 0.7,\n",
|
||||
" TopP = 0.5\n",
|
||||
"};\n",
|
||||
"\n",
|
||||
"var shakespeareFunction = kernel.CreateFunctionFromPrompt(skPrompt, executionSettings, \"Shakespeare\");"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -194,13 +204,15 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#pragma warning disable SKEXP0060\n",
|
||||
"\n",
|
||||
"var ask = @\"Tomorrow is Valentine's day. I need to come up with a few date ideas.\n",
|
||||
"She likes Shakespeare so write using his style. Write them in the form of a poem.\";\n",
|
||||
"\n",
|
||||
"var newPlan = await planner.CreatePlanAsync(ask);\n",
|
||||
"var newPlan = await planner.CreatePlanAsync(kernel, ask);\n",
|
||||
"\n",
|
||||
"Console.WriteLine(\"Updated plan:\\n\");\n",
|
||||
"Console.WriteLine(JsonSerializer.Serialize(newPlan, new JsonSerializerOptions { WriteIndented = true }));"
|
||||
"Console.WriteLine(newPlan);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -226,10 +238,12 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var originalPlanResult = await kernel.RunAsync(originalPlan);\n",
|
||||
"#pragma warning disable SKEXP0060\n",
|
||||
"\n",
|
||||
"var originalPlanResult = await originalPlan.InvokeAsync(kernel, new KernelArguments());\n",
|
||||
"\n",
|
||||
"Console.WriteLine(\"Original Plan results:\\n\");\n",
|
||||
"Console.WriteLine(Utils.WordWrap(originalPlanResult.GetValue<string>(), 100));"
|
||||
"Console.WriteLine(Utils.WordWrap(originalPlanResult.ToString(), 100));"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -253,10 +267,12 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var newPlanResult = await kernel.RunAsync(newPlan);\n",
|
||||
"#pragma warning disable SKEXP0060\n",
|
||||
"\n",
|
||||
"var newPlanResult = await newPlan.InvokeAsync(kernel, new KernelArguments());\n",
|
||||
"\n",
|
||||
"Console.WriteLine(\"New Plan results:\\n\");\n",
|
||||
"Console.WriteLine(newPlanResult.GetValue<string>());"
|
||||
"Console.WriteLine(newPlanResult);"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
"So far, we've mostly been treating the kernel as a stateless orchestration engine.\n",
|
||||
"We send text into a model API and receive text out. \n",
|
||||
"\n",
|
||||
"In a [previous notebook](04-context-variables-chat.ipynb), we used `context variables` to pass in additional\n",
|
||||
"text into prompts to enrich them with more context. This allowed us to create a basic chat experience. \n",
|
||||
"In a [previous notebook](04-kernel-arguments-chat.ipynb), we used `kernel arguments` to pass in additional\n",
|
||||
"text into prompts to enrich them with more data. This allowed us to create a basic chat experience. \n",
|
||||
"\n",
|
||||
"However, if you solely relied on context variables, you would quickly realize that eventually your prompt\n",
|
||||
"would grow so large that you would run into a the model's token limit. What we need is a way to persist state\n",
|
||||
"However, if you solely relied on kernel arguments, you would quickly realize that eventually your prompt\n",
|
||||
"would grow so large that you would run into the model's token limit. What we need is a way to persist state\n",
|
||||
"and build both short-term and long-term memory to empower even more intelligent applications. \n",
|
||||
"\n",
|
||||
"To do this, we dive into the key concept of `Semantic Memory` in the Semantic Kernel. "
|
||||
@@ -33,26 +33,26 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel.Plugins.Memory, 1.0.1-alpha\"\n",
|
||||
"#r \"nuget: System.Linq.Async, 6.0.1\"\n",
|
||||
"\n",
|
||||
"#!import config/Settings.cs\n",
|
||||
"\n",
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"using Microsoft.SemanticKernel.SemanticFunctions;\n",
|
||||
"using Microsoft.SemanticKernel.Orchestration;\n",
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"var kernelBuilder = new KernelBuilder();\n",
|
||||
"var builder = Kernel.CreateBuilder();\n",
|
||||
"\n",
|
||||
"// Configure AI backend used by the kernel\n",
|
||||
"// Configure AI service credentials used by the kernel\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
" kernelBuilder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
|
||||
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" kernelBuilder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
|
||||
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"var kernel = kernelBuilder.Build();"
|
||||
"var kernel = builder.Build();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -84,8 +84,11 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using Microsoft.SemanticKernel.Plugins.Memory;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.AI.OpenAI;\n",
|
||||
"using Microsoft.SemanticKernel.Memory;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.OpenAI;\n",
|
||||
"\n",
|
||||
"// Memory functionality is experimental\n",
|
||||
"#pragma warning disable SKEXP0003, SKEXP0011, SKEXP0052\n",
|
||||
"\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"\n",
|
||||
@@ -93,11 +96,15 @@
|
||||
"\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
"{\n",
|
||||
" memoryBuilder.WithAzureTextEmbeddingGenerationService(\"text-embedding-ada-002\", azureEndpoint, apiKey);\n",
|
||||
" memoryBuilder.WithAzureOpenAITextEmbeddingGeneration(\n",
|
||||
" \"text-embedding-ada-002\",\n",
|
||||
" azureEndpoint, \n",
|
||||
" apiKey,\n",
|
||||
" \"model-id\");\n",
|
||||
"}\n",
|
||||
"else\n",
|
||||
"{\n",
|
||||
" memoryBuilder.WithOpenAITextEmbeddingGenerationService(\"text-embedding-ada-002\", apiKey);\n",
|
||||
" memoryBuilder.WithOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", apiKey);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"memoryBuilder.WithMemoryStore(new VolatileMemoryStore());\n",
|
||||
@@ -197,7 +204,7 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's now revisit our chat sample from the [previous notebook](04-context-variables-chat.ipynb).\n",
|
||||
"If you remember, we used context variables to fill the prompt with a `history` that continuously got populated as we chatted with the bot. Let's add also memory to it!"
|
||||
"If you remember, we used kernel arguments to fill the prompt with a `history` that continuously got populated as we chatted with the bot. Let's add also memory to it!"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -224,8 +231,12 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using Microsoft.SemanticKernel.Plugins.Memory;\n",
|
||||
"\n",
|
||||
"#pragma warning disable SKEXP0052\n",
|
||||
"\n",
|
||||
"// TextMemoryPlugin provides the \"recall\" function\n",
|
||||
"kernel.ImportFunctions(new TextMemoryPlugin(memory));"
|
||||
"kernel.ImportPluginFromObject(new TextMemoryPlugin(memory));"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -257,7 +268,7 @@
|
||||
"User: {{$userInput}}\n",
|
||||
"ChatBot: \";\n",
|
||||
"\n",
|
||||
"var chatFunction = kernel.CreateSemanticFunction(skPrompt, requestSettings: new OpenAIRequestSettings { MaxTokens = 200, Temperature = 0.8 });"
|
||||
"var chatFunction = kernel.CreateFunctionFromPrompt(skPrompt, new OpenAIPromptExecutionSettings { MaxTokens = 200, Temperature = 0.8 });"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -281,16 +292,19 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var context = kernel.CreateNewContext();\n",
|
||||
"#pragma warning disable SKEXP0052\n",
|
||||
"\n",
|
||||
"context.Variables[\"fact1\"] = \"what is my name?\";\n",
|
||||
"context.Variables[\"fact2\"] = \"where do I live?\";\n",
|
||||
"context.Variables[\"fact3\"] = \"where is my family from?\";\n",
|
||||
"context.Variables[\"fact4\"] = \"where have I travelled?\";\n",
|
||||
"context.Variables[\"fact5\"] = \"what do I do for work?\";\n",
|
||||
"var arguments = new KernelArguments();\n",
|
||||
"\n",
|
||||
"context.Variables[TextMemoryPlugin.CollectionParam] = MemoryCollectionName;\n",
|
||||
"context.Variables[TextMemoryPlugin.RelevanceParam] = \"0.8\";"
|
||||
"arguments[\"fact1\"] = \"what is my name?\";\n",
|
||||
"arguments[\"fact2\"] = \"where do I live?\";\n",
|
||||
"arguments[\"fact3\"] = \"where is my family from?\";\n",
|
||||
"arguments[\"fact4\"] = \"where have I travelled?\";\n",
|
||||
"arguments[\"fact5\"] = \"what do I do for work?\";\n",
|
||||
"\n",
|
||||
"arguments[TextMemoryPlugin.CollectionParam] = MemoryCollectionName;\n",
|
||||
"arguments[TextMemoryPlugin.LimitParam] = \"2\";\n",
|
||||
"arguments[TextMemoryPlugin.RelevanceParam] = \"0.8\";"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -315,20 +329,22 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var history = \"\";\n",
|
||||
"context.Variables[\"history\"] = history;\n",
|
||||
"arguments[\"history\"] = history;\n",
|
||||
"Func<string, Task> Chat = async (string input) => {\n",
|
||||
" // Save new message in the context variables\n",
|
||||
" context.Variables[\"userInput\"] = input;\n",
|
||||
" // Save new message in the kernel arguments\n",
|
||||
" arguments[\"userInput\"] = input;\n",
|
||||
"\n",
|
||||
" // Process the user message and get an answer\n",
|
||||
" var answer = await chatFunction.InvokeAsync(context);\n",
|
||||
" var answer = await chatFunction.InvokeAsync(kernel, arguments);\n",
|
||||
"\n",
|
||||
" // Append the new interaction to the chat history\n",
|
||||
" history += $\"\\nUser: {input}\\nChatBot: {answer.GetValue<string>()}\\n\";\n",
|
||||
" context.Variables[\"history\"] = history;\n",
|
||||
" var result = $\"\\nUser: {input}\\nChatBot: {answer}\\n\";\n",
|
||||
"\n",
|
||||
" history += result;\n",
|
||||
" arguments[\"history\"] = history;\n",
|
||||
" \n",
|
||||
" // Show the bot response\n",
|
||||
" Console.WriteLine(\"ChatBot: \" + context);\n",
|
||||
" Console.WriteLine(result);\n",
|
||||
"};"
|
||||
]
|
||||
},
|
||||
@@ -419,10 +435,6 @@
|
||||
" = \"Sample demonstrating how to create a chat plugin interfacing with ChatGPT\",\n",
|
||||
" [\"https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/Plugins/Plugins.Memory/VolatileMemoryStore.cs\"]\n",
|
||||
" = \"C# class that defines a volatile embedding store\",\n",
|
||||
" [\"https://github.com/microsoft/semantic-kernel/tree/main/samples/dotnet/KernelHttpServer/README.md\"]\n",
|
||||
" = \"README: How to set up a Semantic Kernel Service API using Azure Function Runtime v4\",\n",
|
||||
" [\"https://github.com/microsoft/semantic-kernel/tree/main/samples/apps/chat-summary-webapp-react/README.md\"]\n",
|
||||
" = \"README: README associated with a sample starter react-based chat summary webapp\",\n",
|
||||
"};"
|
||||
]
|
||||
},
|
||||
@@ -447,15 +459,22 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"// Memory functionality is experimental\n",
|
||||
"#pragma warning disable SKEXP0003, SKEXP0011, SKEXP0052\n",
|
||||
"\n",
|
||||
"var memoryBuilder = new MemoryBuilder();\n",
|
||||
"\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
"{\n",
|
||||
" memoryBuilder.WithAzureTextEmbeddingGenerationService(\"text-embedding-ada-002\", azureEndpoint, apiKey);\n",
|
||||
" memoryBuilder.WithAzureOpenAITextEmbeddingGeneration(\n",
|
||||
" \"text-embedding-ada-002\",\n",
|
||||
" azureEndpoint, \n",
|
||||
" apiKey,\n",
|
||||
" \"model-id\");\n",
|
||||
"}\n",
|
||||
"else\n",
|
||||
"{\n",
|
||||
" memoryBuilder.WithOpenAITextEmbeddingGenerationService(\"text-embedding-ada-002\", apiKey);\n",
|
||||
" memoryBuilder.WithOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", apiKey);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"memoryBuilder.WithMemoryStore(new VolatileMemoryStore());\n",
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
"source": [
|
||||
"# Generating images with AI\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use OpenAI DALL-E 2 to generate images, in combination with other LLM features like text and embedding generation.\n",
|
||||
"This notebook demonstrates how to use OpenAI DALL-E 3 to generate images, in combination with other LLM features like text and embedding generation.\n",
|
||||
"\n",
|
||||
"Here, we use Chat Completion to generate a random image description and DALL-E 2 to create an image from that description, showing the image inline.\n",
|
||||
"Here, we use Chat Completion to generate a random image description and DALL-E 3 to create an image from that description, showing the image inline.\n",
|
||||
"\n",
|
||||
"Lastly, the notebook asks the user to describe the image. The embedding of the user's description is compared to the original description, using Cosine Similarity, and returning a score from 0 to 1, where 1 means exact match."
|
||||
]
|
||||
@@ -33,7 +33,8 @@
|
||||
"source": [
|
||||
"// Usual setup: importing Semantic Kernel SDK and SkiaSharp, used to display images inline.\n",
|
||||
"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\"\n",
|
||||
"#r \"nuget: System.Numerics.Tensors, 8.0.0\"\n",
|
||||
"#r \"nuget: SkiaSharp, 2.88.3\"\n",
|
||||
"\n",
|
||||
"#!import config/Settings.cs\n",
|
||||
@@ -41,10 +42,10 @@
|
||||
"#!import config/SkiaUtils.cs\n",
|
||||
"\n",
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"using Microsoft.SemanticKernel.AI.ImageGeneration; \n",
|
||||
"using Microsoft.SemanticKernel.AI.Embeddings;\n",
|
||||
"using Microsoft.SemanticKernel.AI.Embeddings.VectorOperations;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.AI.OpenAI;"
|
||||
"using Microsoft.SemanticKernel.TextToImage;\n",
|
||||
"using Microsoft.SemanticKernel.Embeddings;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.OpenAI;\n",
|
||||
"using System.Numerics.Tensors;"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -56,7 +57,7 @@
|
||||
"\n",
|
||||
"The notebook uses:\n",
|
||||
"\n",
|
||||
"* **OpenAI Dall-E 2** to transform the image description into an image\n",
|
||||
"* **OpenAI Dall-E 3** to transform the image description into an image\n",
|
||||
"* **text-embedding-ada-002** to compare your guess against the real image description\n",
|
||||
"\n",
|
||||
"**Note:**: For Azure OpenAI, your endpoint should have DALL-E API enabled."
|
||||
@@ -78,32 +79,36 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"#pragma warning disable SKEXP0001, SKEXP0002, SKEXP0011, SKEXP0012\n",
|
||||
"\n",
|
||||
"// Load OpenAI credentials from config/settings.json\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"\n",
|
||||
"// Configure the three AI features: text embedding (using Ada), text completion (using DaVinci 3), image generation (DALL-E 2)\n",
|
||||
"var builder = new KernelBuilder();\n",
|
||||
"// Configure the three AI features: text embedding (using Ada), chat completion, image generation (DALL-E 3)\n",
|
||||
"var builder = Kernel.CreateBuilder();\n",
|
||||
"\n",
|
||||
"if(useAzureOpenAI)\n",
|
||||
"{\n",
|
||||
" builder.WithAzureTextEmbeddingGenerationService(\"text-embedding-ada-002\", azureEndpoint, apiKey);\n",
|
||||
" builder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
|
||||
" builder.WithAzureOpenAIImageGenerationService(azureEndpoint, apiKey);\n",
|
||||
" builder.AddAzureOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", azureEndpoint, apiKey);\n",
|
||||
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
|
||||
" builder.AddAzureOpenAITextToImage(\"dall-e-3\", azureEndpoint, apiKey);\n",
|
||||
"}\n",
|
||||
"else\n",
|
||||
"{\n",
|
||||
" builder.WithOpenAITextEmbeddingGenerationService(\"text-embedding-ada-002\", apiKey, orgId);\n",
|
||||
" builder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
|
||||
" builder.WithOpenAIImageGenerationService(apiKey, orgId);\n",
|
||||
" builder.AddOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", apiKey, orgId);\n",
|
||||
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
|
||||
" builder.AddOpenAITextToImage(apiKey, orgId);\n",
|
||||
"}\n",
|
||||
" \n",
|
||||
"var kernel = builder.Build();\n",
|
||||
"\n",
|
||||
"// Get AI service instance used to generate images\n",
|
||||
"var dallE = kernel.GetService<IImageGeneration>();\n",
|
||||
"var dallE = kernel.GetRequiredService<ITextToImageService>();\n",
|
||||
"\n",
|
||||
"// Get AI service instance used to extract embedding from a text\n",
|
||||
"var textEmbedding = kernel.GetService<ITextEmbeddingGeneration>();"
|
||||
"var textEmbedding = kernel.GetRequiredService<ITextEmbeddingGenerationService>();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -111,12 +116,12 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Generate a (random) image with DALL-E 2\n",
|
||||
"# Generate a (random) image with DALL-E 3\n",
|
||||
"\n",
|
||||
"**genImgDescription** is a Semantic Function used to generate a random image description. \n",
|
||||
"The function takes in input a random number to increase the diversity of its output.\n",
|
||||
"\n",
|
||||
"The random image description is then given to **Dall-E 2** asking to create an image."
|
||||
"The random image description is then given to **Dall-E 3** asking to create an image."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -136,20 +141,29 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#pragma warning disable SKEXP0002\n",
|
||||
"\n",
|
||||
"var prompt = @\"\n",
|
||||
"Think about an artificial object correlated to number {{$input}}.\n",
|
||||
"Describe the image with one detailed sentence. The description cannot contain numbers.\";\n",
|
||||
"\n",
|
||||
"var executionSettings = new OpenAIPromptExecutionSettings \n",
|
||||
"{\n",
|
||||
" MaxTokens = 256,\n",
|
||||
" Temperature = 1\n",
|
||||
"};\n",
|
||||
"\n",
|
||||
"// Create a semantic function that generate a random image description.\n",
|
||||
"var genImgDescription = kernel.CreateSemanticFunction(\n",
|
||||
" \"Think about an artificial object correlated to number {{$input}}. \" +\n",
|
||||
" \"Describe the image with one detailed sentence. The description cannot contain numbers.\", \n",
|
||||
" requestSettings: new OpenAIRequestSettings { MaxTokens = 256, Temperature = 1 });\n",
|
||||
"var genImgDescription = kernel.CreateFunctionFromPrompt(prompt, executionSettings);\n",
|
||||
"\n",
|
||||
"var random = new Random().Next(0, 200);\n",
|
||||
"var imageDescriptionResult = await kernel.RunAsync($\"{random}\", genImgDescription);\n",
|
||||
"var imageDescription = imageDescriptionResult.GetValue<string>();\n",
|
||||
"var imageDescriptionResult = await kernel.InvokeAsync(genImgDescription, new() { [\"input\"] = random });\n",
|
||||
"var imageDescription = imageDescriptionResult.ToString();\n",
|
||||
"\n",
|
||||
"// Use DALL-E 2 to generate an image. OpenAI in this case returns a URL (though you can ask to return a base64 image)\n",
|
||||
"var imageUrl = await dallE.GenerateImageAsync(imageDescription.Trim(), 512, 512);\n",
|
||||
"// Use DALL-E 3 to generate an image. OpenAI in this case returns a URL (though you can ask to return a base64 image)\n",
|
||||
"var imageUrl = await dallE.GenerateImageAsync(imageDescription.Trim(), 1024, 1024);\n",
|
||||
"\n",
|
||||
"await SkiaUtils.ShowImage(imageUrl, 512, 512);"
|
||||
"await SkiaUtils.ShowImage(imageUrl, 1024, 1024);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -187,7 +201,7 @@
|
||||
"// Compare user guess with real description and calculate score\n",
|
||||
"var origEmbedding = await textEmbedding.GenerateEmbeddingsAsync(new List<string> { imageDescription } );\n",
|
||||
"var guessEmbedding = await textEmbedding.GenerateEmbeddingsAsync(new List<string> { guess } );\n",
|
||||
"var similarity = origEmbedding.First().Span.CosineSimilarity(guessEmbedding.First().Span);\n",
|
||||
"var similarity = TensorPrimitives.CosineSimilarity(origEmbedding.First().Span, guessEmbedding.First().Span);\n",
|
||||
"\n",
|
||||
"Console.WriteLine($\"Your description:\\n{Utils.WordWrap(guess, 90)}\\n\");\n",
|
||||
"Console.WriteLine($\"Real description:\\n{Utils.WordWrap(imageDescription.Trim(), 90)}\\n\");\n",
|
||||
+27
-23
@@ -5,7 +5,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Using ChatGPT with the Semantic Kernel featuring DALL-E 2\n",
|
||||
"# Using ChatGPT with the Semantic Kernel featuring DALL-E 3\n",
|
||||
"\n",
|
||||
"This notebook shows how to make use of the new ChatCompletion API from OpenAI, popularized by ChatGPT. This API brings a new ChatML schema which is different from the TextCompletion API. While the text completion API expects input a prompt and returns a simple string, the chat completion API expects in input a Chat history and returns a new message:\n",
|
||||
"\n",
|
||||
@@ -34,7 +34,7 @@
|
||||
"source": [
|
||||
"When deciding between which one to use, know that ChatGPT models (i.e. gpt-3.5-turbo) are optimized for chat applications and have been fine-tuned for instruction-following and dialogue. As such, for creating semantic plugins with the Semantic Kernel, users may still find the TextCompletion model better suited for certain use cases.\n",
|
||||
"\n",
|
||||
"The code below shows how to setup SK with ChatGPT, how to manage the Chat history object, and to make things a little more interesting asks ChatGPT to reply with image descriptions instead so we can have a dialog using images, leveraging DALL-E 2 integration."
|
||||
"The code below shows how to setup SK with ChatGPT, how to manage the Chat history object, and to make things a little more interesting asks ChatGPT to reply with image descriptions instead so we can have a dialog using images, leveraging DALL-E 3 integration."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -56,7 +56,7 @@
|
||||
"source": [
|
||||
"// Usual setup: importing Semantic Kernel SDK and SkiaSharp, used to display images inline.\n",
|
||||
"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\"\n",
|
||||
"#r \"nuget: SkiaSharp, 2.88.3\"\n",
|
||||
"\n",
|
||||
"#!import config/Settings.cs\n",
|
||||
@@ -64,9 +64,9 @@
|
||||
"#!import config/SkiaUtils.cs\n",
|
||||
"\n",
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"using Microsoft.SemanticKernel.AI.ImageGeneration;\n",
|
||||
"using Microsoft.SemanticKernel.AI.ChatCompletion;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.AI.OpenAI;"
|
||||
"using Microsoft.SemanticKernel.TextToImage;\n",
|
||||
"using Microsoft.SemanticKernel.ChatCompletion;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.OpenAI;"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -77,7 +77,7 @@
|
||||
"The notebook uses:\n",
|
||||
"\n",
|
||||
"* **OpenAI ChatGPT** to chat with the user\n",
|
||||
"* **OpenAI Dall-E 2** to transform messages into images"
|
||||
"* **OpenAI Dall-E 3** to transform messages into images"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -96,30 +96,34 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"#pragma warning disable SKEXP0002, SKEXP0012\n",
|
||||
"\n",
|
||||
"// Load OpenAI credentials from config/settings.json\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"\n",
|
||||
"// Configure the two AI features: OpenAI Chat and DALL-E 2 for image generation\n",
|
||||
"var builder = new KernelBuilder();\n",
|
||||
"// Configure the two AI features: OpenAI Chat and DALL-E 3 for image generation\n",
|
||||
"var builder = Kernel.CreateBuilder();\n",
|
||||
"\n",
|
||||
"if(useAzureOpenAI)\n",
|
||||
"{\n",
|
||||
" builder.WithAzureChatCompletionService(\"gpt-35-turbo\", azureEndpoint, apiKey);\n",
|
||||
" builder.WithAzureOpenAIImageGenerationService(azureEndpoint, apiKey);\n",
|
||||
" builder.AddAzureOpenAIChatCompletion(\"gpt-35-turbo\", azureEndpoint, apiKey);\n",
|
||||
" builder.AddAzureOpenAITextToImage(\"dall-e-3\", azureEndpoint, apiKey);\n",
|
||||
"}\n",
|
||||
"else\n",
|
||||
"{\n",
|
||||
" builder.WithOpenAIChatCompletionService(\"gpt-3.5-turbo\", apiKey, orgId);\n",
|
||||
" builder.WithOpenAIImageGenerationService(apiKey, orgId);\n",
|
||||
" builder.AddOpenAIChatCompletion(\"gpt-3.5-turbo\", apiKey, orgId);\n",
|
||||
" builder.AddOpenAITextToImage(apiKey, orgId);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"var kernel = builder.Build();\n",
|
||||
"\n",
|
||||
"// Get AI service instance used to generate images\n",
|
||||
"var dallE = kernel.GetService<IImageGeneration>();\n",
|
||||
"var dallE = kernel.GetRequiredService<ITextToImageService>();\n",
|
||||
"\n",
|
||||
"// Get AI service instance used to manage the user chat\n",
|
||||
"var chatGPT = kernel.GetService<IChatCompletion>();"
|
||||
"var chatGPT = kernel.GetRequiredService<IChatCompletionService>();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -131,7 +135,7 @@
|
||||
"\n",
|
||||
"Before starting the chat, we create a new chat object with some instructions, which are included in the chat history. \n",
|
||||
"\n",
|
||||
"The instructions tell OpenAI what kind of chat we want to have, in this case we ask to reply with \"image descriptions\", so that we can chain ChatGPT with DALL-E 2."
|
||||
"The instructions tell OpenAI what kind of chat we want to have, in this case we ask to reply with \"image descriptions\", so that we can chain ChatGPT with DALL-E 3."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -151,14 +155,12 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletion;\n",
|
||||
"\n",
|
||||
"var systemMessage = \"You're chatting with a user. Instead of replying directly to the user\"\n",
|
||||
" + \" provide a description of a cartoonish image that expresses what you want to say.\"\n",
|
||||
" + \" The user won't see your message, they will see only the image.\"\n",
|
||||
" + \" Describe the image with details in one sentence.\";\n",
|
||||
"\n",
|
||||
"var chat = (OpenAIChatHistory)chatGPT.CreateNewChat(systemMessage);"
|
||||
"var chat = new ChatHistory(systemMessage);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -193,6 +195,8 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#pragma warning disable SKEXP0002\n",
|
||||
"\n",
|
||||
"while (true)\n",
|
||||
"{\n",
|
||||
" // 1. Ask the user for a message. The user enters a message. Add the user message into the Chat History object.\n",
|
||||
@@ -201,13 +205,13 @@
|
||||
" chat.AddUserMessage(userMessage);\n",
|
||||
"\n",
|
||||
" // 2. Send the chat object to AI asking to generate a response. Add the bot message into the Chat History object.\n",
|
||||
" string assistantReply = await chatGPT.GenerateMessageAsync(chat, new OpenAIRequestSettings());\n",
|
||||
" chat.AddAssistantMessage(assistantReply);\n",
|
||||
" var assistantReply = await chatGPT.GetChatMessageContentAsync(chat, new OpenAIPromptExecutionSettings());\n",
|
||||
" chat.AddAssistantMessage(assistantReply.Content);\n",
|
||||
"\n",
|
||||
" // 3. Show the reply as an image\n",
|
||||
" Console.WriteLine($\"\\nBot:\");\n",
|
||||
" var imageUrl = await dallE.GenerateImageAsync(assistantReply, 256, 256);\n",
|
||||
" await SkiaUtils.ShowImage(imageUrl, 256, 256);\n",
|
||||
" var imageUrl = await dallE.GenerateImageAsync(assistantReply.Content, 1024, 1024);\n",
|
||||
" await SkiaUtils.ShowImage(imageUrl, 1024, 1024);\n",
|
||||
" Console.WriteLine($\"[{assistantReply}]\\n\");\n",
|
||||
"}"
|
||||
]
|
||||
@@ -9,10 +9,10 @@
|
||||
"\n",
|
||||
"In this notebook, we show how to use [Chroma](https://www.trychroma.com/) with Semantic Kernel to create even more\n",
|
||||
"intelligent applications. We assume that you are already familiar with the concepts of Semantic Kernel\n",
|
||||
"and memory. [Previously](04-context-variables-chat.ipynb), we have used `context variables` to pass\n",
|
||||
"and memory. [Previously](04-kernel-arguments-chat.ipynb), we have used `kernel arguments` to pass\n",
|
||||
"additional text into prompts, enriching them with more context for a basic chat experience.\n",
|
||||
"\n",
|
||||
"However, relying solely on context variables has its limitations, such as the model's token limit.\n",
|
||||
"However, relying solely on kernel arguments has its limitations, such as the model's token limit.\n",
|
||||
"To overcome these limitations, we will use **SK Semantic Memory**, leveraging Chroma as a persistent\n",
|
||||
"Semantic Memory Storage.\n",
|
||||
"\n",
|
||||
@@ -38,8 +38,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel.Connectors.Memory.Chroma, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel.Connectors.Chroma, 1.0.1-alpha\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel.Plugins.Memory, 1.0.1-alpha\"\n",
|
||||
"#r \"nuget: System.Linq.Async, 6.0.1\"\n",
|
||||
"\n",
|
||||
"#!import config/Settings.cs\n",
|
||||
@@ -49,21 +50,22 @@
|
||||
"using System.Linq;\n",
|
||||
"using System.Threading.Tasks;\n",
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.Memory.Chroma;\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.Chroma;\n",
|
||||
"using Microsoft.SemanticKernel.Memory;\n",
|
||||
"using Microsoft.SemanticKernel.Plugins.Memory;\n",
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"var kernelBuilder = new KernelBuilder();\n",
|
||||
"var builder = Kernel.CreateBuilder();\n",
|
||||
"\n",
|
||||
"// Configure AI backend used by the kernel\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
" kernelBuilder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
|
||||
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" kernelBuilder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
|
||||
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"var kernel = kernelBuilder.Build();"
|
||||
"var kernel = builder.Build();"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -98,7 +100,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using Microsoft.SemanticKernel.Connectors.AI.OpenAI;\n",
|
||||
"#pragma warning disable SKEXP0003, SKEXP0011, SKEXP0022, SKEXP0052\n",
|
||||
"\n",
|
||||
"using Microsoft.SemanticKernel.Connectors.OpenAI;\n",
|
||||
"\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"\n",
|
||||
@@ -106,11 +110,11 @@
|
||||
"\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
"{\n",
|
||||
" memoryBuilder.WithAzureTextEmbeddingGenerationService(\"text-embedding-ada-002\", azureEndpoint, apiKey);\n",
|
||||
" memoryBuilder.WithAzureOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", azureEndpoint, apiKey, \"model-id\");\n",
|
||||
"}\n",
|
||||
"else\n",
|
||||
"{\n",
|
||||
" memoryBuilder.WithOpenAITextEmbeddingGenerationService(\"text-embedding-ada-002\", apiKey);\n",
|
||||
" memoryBuilder.WithOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", apiKey);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"var chromaMemoryStore = new ChromaMemoryStore(\"http://127.0.0.1:8000\");\n",
|
||||
@@ -212,8 +216,8 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's now revisit our chat sample from the [previous notebook](04-context-variables-chat.ipynb).\n",
|
||||
"If you remember, we used context variables to fill the prompt with a `history` that continuously got populated as we chatted with the bot. Let's add also memory to it!"
|
||||
"Let's now revisit our chat sample from the [previous notebook](04-kernel-arguments-chat.ipynb).\n",
|
||||
"If you remember, we used kernel arguments to fill the prompt with a `history` that continuously got populated as we chatted with the bot. Let's add also memory to it!"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -240,10 +244,10 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using Microsoft.SemanticKernel.Plugins.Memory;\n",
|
||||
"#pragma warning disable SKEXP0052\n",
|
||||
"\n",
|
||||
"// TextMemoryPlugin provides the \"recall\" function\n",
|
||||
"kernel.ImportFunctions(new TextMemoryPlugin(memory));"
|
||||
"kernel.ImportPluginFromObject(new TextMemoryPlugin(memory));"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -275,7 +279,7 @@
|
||||
"User: {{$userInput}}\n",
|
||||
"ChatBot: \";\n",
|
||||
"\n",
|
||||
"var chatFunction = kernel.CreateSemanticFunction(skPrompt, requestSettings: new OpenAIRequestSettings { MaxTokens = 200, Temperature = 0.8 });"
|
||||
"var chatFunction = kernel.CreateFunctionFromPrompt(skPrompt, new OpenAIPromptExecutionSettings { MaxTokens = 200, Temperature = 0.8 });"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -299,16 +303,19 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var context = kernel.CreateNewContext();\n",
|
||||
"#pragma warning disable SKEXP0052\n",
|
||||
"\n",
|
||||
"context.Variables[\"fact1\"] = \"what is my name?\";\n",
|
||||
"context.Variables[\"fact2\"] = \"where do I live?\";\n",
|
||||
"context.Variables[\"fact3\"] = \"where is my family from?\";\n",
|
||||
"context.Variables[\"fact4\"] = \"where have I travelled?\";\n",
|
||||
"context.Variables[\"fact5\"] = \"what do I do for work?\";\n",
|
||||
"var arguments = new KernelArguments();\n",
|
||||
"\n",
|
||||
"context.Variables[TextMemoryPlugin.CollectionParam] = MemoryCollectionName;\n",
|
||||
"context.Variables[TextMemoryPlugin.RelevanceParam] = \"0.6\";"
|
||||
"arguments[\"fact1\"] = \"what is my name?\";\n",
|
||||
"arguments[\"fact2\"] = \"where do I live?\";\n",
|
||||
"arguments[\"fact3\"] = \"where is my family from?\";\n",
|
||||
"arguments[\"fact4\"] = \"where have I travelled?\";\n",
|
||||
"arguments[\"fact5\"] = \"what do I do for work?\";\n",
|
||||
"\n",
|
||||
"arguments[TextMemoryPlugin.CollectionParam] = MemoryCollectionName;\n",
|
||||
"arguments[TextMemoryPlugin.LimitParam] = \"2\";\n",
|
||||
"arguments[TextMemoryPlugin.RelevanceParam] = \"0.8\";"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -333,20 +340,22 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var history = \"\";\n",
|
||||
"context.Variables[\"history\"] = history;\n",
|
||||
"arguments[\"history\"] = history;\n",
|
||||
"Func<string, Task> Chat = async (string input) => {\n",
|
||||
" // Save new message in the context variables\n",
|
||||
" context.Variables[\"userInput\"] = input;\n",
|
||||
" // Save new message in the kernel arguments\n",
|
||||
" arguments[\"userInput\"] = input;\n",
|
||||
"\n",
|
||||
" // Process the user message and get an answer\n",
|
||||
" var answer = await chatFunction.InvokeAsync(context);\n",
|
||||
" var answer = await chatFunction.InvokeAsync(kernel, arguments);\n",
|
||||
"\n",
|
||||
" // Append the new interaction to the chat history\n",
|
||||
" history += $\"\\nUser: {input}\\nChatBot: {answer.GetValue<string>()}\\n\";\n",
|
||||
" context.Variables[\"history\"] = history;\n",
|
||||
" var result = $\"\\nUser: {input}\\nChatBot: {answer}\\n\";\n",
|
||||
"\n",
|
||||
" history += result;\n",
|
||||
" arguments[\"history\"] = history;\n",
|
||||
" \n",
|
||||
" // Show the bot response\n",
|
||||
" Console.WriteLine(\"ChatBot: \" + context);\n",
|
||||
" Console.WriteLine(result);\n",
|
||||
"};"
|
||||
]
|
||||
},
|
||||
@@ -437,10 +446,6 @@
|
||||
" = \"Sample demonstrating how to create a chat plugin interfacing with ChatGPT\",\n",
|
||||
" [\"https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/Plugins/Plugins.Memory/VolatileMemoryStore.cs\"]\n",
|
||||
" = \"C# class that defines a volatile embedding store\",\n",
|
||||
" [\"https://github.com/microsoft/semantic-kernel/tree/main/samples/dotnet/KernelHttpServer/README.md\"]\n",
|
||||
" = \"README: How to set up a Semantic Kernel Service API using Azure Function Runtime v4\",\n",
|
||||
" [\"https://github.com/microsoft/semantic-kernel/tree/main/samples/apps/chat-summary-webapp-react/README.md\"]\n",
|
||||
" = \"README: README associated with a sample starter react-based chat summary webapp\",\n",
|
||||
"};"
|
||||
]
|
||||
},
|
||||
@@ -465,15 +470,17 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#pragma warning disable SKEXP0003, SKEXP0011, SKEXP0022, SKEXP0052\n",
|
||||
"\n",
|
||||
"var memoryBuilder = new MemoryBuilder();\n",
|
||||
"\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
"{\n",
|
||||
" memoryBuilder.WithAzureTextEmbeddingGenerationService(\"text-embedding-ada-002\", azureEndpoint, apiKey);\n",
|
||||
" memoryBuilder.WithAzureOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", azureEndpoint, apiKey, \"model-id\");\n",
|
||||
"}\n",
|
||||
"else\n",
|
||||
"{\n",
|
||||
" memoryBuilder.WithOpenAITextEmbeddingGenerationService(\"text-embedding-ada-002\", apiKey);\n",
|
||||
" memoryBuilder.WithOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", apiKey);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"var chromaMemoryStore = new ChromaMemoryStore(\"http://127.0.0.1:8000\");\n",
|
||||
|
||||
@@ -35,29 +35,28 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel.Plugins.Web, 1.0.0-beta1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel, 1.0.1\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel.Plugins.Web, 1.0.1-alpha\"\n",
|
||||
"#r \"nuget: Microsoft.SemanticKernel.Plugins.Core, 1.0.1-alpha\"\n",
|
||||
"\n",
|
||||
"#!import config/Settings.cs\n",
|
||||
"#!import config/Utils.cs\n",
|
||||
"\n",
|
||||
"using Microsoft.SemanticKernel;\n",
|
||||
"using Microsoft.SemanticKernel.Plugins.Core;\n",
|
||||
"using Microsoft.SemanticKernel.Orchestration;\n",
|
||||
"using Microsoft.SemanticKernel.Planning;\n",
|
||||
"using Microsoft.SemanticKernel.Planners;\n",
|
||||
"using Microsoft.SemanticKernel.TemplateEngine;\n",
|
||||
"using InteractiveKernel = Microsoft.DotNet.Interactive.Kernel;\n",
|
||||
"using Kernel = Microsoft.SemanticKernel.Kernel;\n",
|
||||
"\n",
|
||||
"var builder = new KernelBuilder();\n",
|
||||
"var builder = Kernel.CreateBuilder();\n",
|
||||
"\n",
|
||||
"// Configure AI backend used by the kernel\n",
|
||||
"var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
|
||||
"\n",
|
||||
"if (useAzureOpenAI)\n",
|
||||
" builder.WithAzureChatCompletionService(model, azureEndpoint, apiKey);\n",
|
||||
" builder.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n",
|
||||
"else\n",
|
||||
" builder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
|
||||
" builder.AddOpenAIChatCompletion(model, apiKey, orgId);\n",
|
||||
"\n",
|
||||
"var kernel = builder.Build();"
|
||||
]
|
||||
@@ -134,19 +133,18 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"private static async Task Example1Async(IKernel kernel)\n",
|
||||
"private static async Task Example1Async(Microsoft.SemanticKernel.Kernel kernel)\n",
|
||||
"{\n",
|
||||
" Console.WriteLine(\"Example 1\");\n",
|
||||
"\n",
|
||||
" // Run \n",
|
||||
" var question = \"What is quantum tunnelling\";\n",
|
||||
" var function = kernel.Functions.GetFunction(\"bing\", \"search\");\n",
|
||||
" var bingResult = await kernel.RunAsync(question, function);\n",
|
||||
" var bingResultString = bingResult.GetValue<string>();\n",
|
||||
" var function = kernel.Plugins[\"bing\"][\"search\"];\n",
|
||||
" var bingResult = await kernel.InvokeAsync(function, new() { [\"query\"] = question });\n",
|
||||
"\n",
|
||||
" Console.WriteLine(question);\n",
|
||||
" Console.WriteLine(\"----\");\n",
|
||||
" Console.WriteLine(bingResultString);\n",
|
||||
" Console.WriteLine(bingResult);\n",
|
||||
" Console.WriteLine();\n",
|
||||
"\n",
|
||||
" /* OUTPUT:\n",
|
||||
@@ -175,29 +173,20 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"private static async Task Example2Async(IKernel kernel)\n",
|
||||
"private static async Task Example2Async(Microsoft.SemanticKernel.Kernel kernel)\n",
|
||||
"{\n",
|
||||
" Console.WriteLine(\"Example 2\");\n",
|
||||
"\n",
|
||||
" //The following function only works in interactive notebooks\n",
|
||||
" string question = await InteractiveKernel.GetInputAsync(\"Please ask your question\"); \n",
|
||||
"\n",
|
||||
" var function = kernel.Functions.GetFunction(\"bing\", \"search\");\n",
|
||||
" var bingResult = await kernel.RunAsync(question, function);\n",
|
||||
" var bingResultString = bingResult.GetValue<string>();\n",
|
||||
" var function = kernel.Plugins[\"bing\"][\"search\"];\n",
|
||||
" var bingResult = await kernel.InvokeAsync(function, new() { [\"query\"] = question });\n",
|
||||
"\n",
|
||||
" Console.WriteLine(bingResultString);\n",
|
||||
" Console.WriteLine(bingResult);\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Just uncomment the examples to run them in the following code:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
@@ -211,13 +200,15 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#pragma warning disable SKEXP0054\n",
|
||||
"\n",
|
||||
"// Load Bing plugin\n",
|
||||
"var bingConnector = new BingConnector(BING_KEY);\n",
|
||||
"\n",
|
||||
"kernel.ImportFunctions(new WebSearchEnginePlugin(bingConnector), \"bing\");\n",
|
||||
"kernel.ImportPluginFromObject(new WebSearchEnginePlugin(bingConnector), \"bing\");\n",
|
||||
"\n",
|
||||
"//await Example1Async(kernel);\n",
|
||||
"//await Example2Async(kernel);"
|
||||
"await Example1Async(kernel);\n",
|
||||
"await Example2Async(kernel);"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -5,10 +5,10 @@ the Semantic Kernel. The notebooks are organized in order of increasing complexi
|
||||
|
||||
To run the notebooks, we recommend the following steps:
|
||||
|
||||
- [Install .NET 7](https://dotnet.microsoft.com/download/dotnet/7.0)
|
||||
- [Install .NET 8](https://dotnet.microsoft.com/download/dotnet/8.0)
|
||||
- [Install Visual Studio Code (VS Code)](https://code.visualstudio.com)
|
||||
- Launch VS Code and [install the "Polyglot" extension](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode).
|
||||
Min version required: v1.0.4102020 (Feb 2022).
|
||||
Min version required: v1.0.4606021 (Dec 2023).
|
||||
|
||||
The steps above should be sufficient, you can now **open all the C# notebooks in VS Code**.
|
||||
|
||||
@@ -57,18 +57,20 @@ For a quick dive, look at the [getting started notebook](00-getting-started.ipyn
|
||||
1. [Loading and configuring Semantic Kernel](01-basic-loading-the-kernel.ipynb)
|
||||
2. [Running AI prompts from file](02-running-prompts-from-file.ipynb)
|
||||
3. [Creating Semantic Functions at runtime (i.e. inline functions)](03-semantic-function-inline.ipynb)
|
||||
4. [Using Context Variables to Build a Chat Experience](04-context-variables-chat.ipynb)
|
||||
4. [Using Kernel Arguments to Build a Chat Experience](04-kernel-arguments-chat.ipynb)
|
||||
5. [Creating and Executing Plans](05-using-the-planner.ipynb)
|
||||
6. [Building Memory with Embeddings](06-memory-and-embeddings.ipynb)
|
||||
7. [Creating images with DALL-E 2](07-DALL-E-2.ipynb)
|
||||
8. [Chatting with ChatGPT and Images](08-chatGPT-with-DALL-E-2.ipynb)
|
||||
7. [Creating images with DALL-E 3](07-DALL-E-3.ipynb)
|
||||
8. [Chatting with ChatGPT and Images](08-chatGPT-with-DALL-E-3.ipynb)
|
||||
9. [Building Semantic Memory with Chroma](09-memory-with-chroma.ipynb)
|
||||
10. [BingSearch using Kernel](10-BingSearch-using-kernel.ipynb)
|
||||
|
||||
# Run notebooks in the browser with JupyterLab
|
||||
|
||||
You can run the notebooks also in the browser with JupyterLab. These steps
|
||||
should be sufficient to start:
|
||||
|
||||
Install Python 3, Pip and .NET 7 in your system, then:
|
||||
Install Python 3, Pip and .NET 8 in your system, then:
|
||||
|
||||
pip install jupyterlab
|
||||
dotnet tool install -g Microsoft.dotnet-interactive
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- Central version prefix - applies to all nuget packages. -->
|
||||
<Version>1.0.0-beta4</Version>
|
||||
<VersionPrefix>1.3.1</VersionPrefix>
|
||||
|
||||
<PackageVersion Condition="'$(VersionSuffix)' != ''">$(VersionPrefix)-$(VersionSuffix)</PackageVersion>
|
||||
<PackageVersion Condition="'$(VersionSuffix)' == ''">$(VersionPrefix)</PackageVersion>
|
||||
|
||||
<Configurations>Debug;Release;Publish</Configurations>
|
||||
<IsPackable>true</IsPackable>
|
||||
|
||||
<!-- Package validation. Baseline Version should be lower than current version. -->
|
||||
<PackageValidationBaselineVersion>1.3.0</PackageValidationBaselineVersion>
|
||||
<!-- Validate assembly attributes only for Publish builds -->
|
||||
<NoWarn Condition="'$(Configuration)' != 'Publish'">$(NoWarn);CP0003</NoWarn>
|
||||
<!-- Do not validate reference assemblies -->
|
||||
<NoWarn>$(NoWarn);CP1002</NoWarn>
|
||||
|
||||
<!-- Default description and tags. Packages can override. -->
|
||||
<Authors>Microsoft</Authors>
|
||||
<Company>Microsoft</Company>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Setting errors for SDK projects under samples folder
|
||||
[*.cs]
|
||||
dotnet_diagnostic.CA2007.severity = error # Do not directly await a Task
|
||||
dotnet_diagnostic.VSTHRD111.severity = error # Use .ConfigureAwait(bool)
|
||||
dotnet_diagnostic.IDE1006.severity = error # Naming rule violations
|
||||
dotnet_diagnostic.RCS1110.severity = none # Declare type inside namespace
|
||||
dotnet_diagnostic.CA2201.severity = none # Exception is not sufficiently specific
|
||||
dotnet_diagnostic.CS1998.severity = none # Async method lacks 'await' operators and will run synchronously
|
||||
dotnet_diagnostic.CA1851.severity = none # Possible multiple enumerations of 'IEnumerable' collection
|
||||
dotnet_diagnostic.CA1819.severity = none # Properties should not return arrays
|
||||
dotnet_diagnostic.CA1812.severity = none # Avoid uninstantiated internal classes
|
||||
dotnet_diagnostic.VSTHRD002.severity = none # Avoid problematic synchronous waits
|
||||
dotnet_diagnostic.CS1587.severity = none # XML comment is not placed on a valid language element
|
||||
dotnet_diagnostic.CA1031.severity = none # Do not catch general exception types
|
||||
dotnet_diagnostic.CA2000.severity = none # Dispose objects before losing scope
|
||||
dotnet_diagnostic.RCS1110.severity = none # Declare type inside namespace
|
||||
dotnet_diagnostic.CA5394.severity = none # Do not use insecure randomness
|
||||
|
||||
# Resharper disabled rules: https://www.jetbrains.com/help/resharper/Reference__Code_Inspections_CSHARP.html#CodeSmell
|
||||
resharper_condition_is_always_true_or_false_according_to_nullable_api_contract_highlighting = none # ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
resharper_inconsistent_naming_highlighting = none # InconsistentNaming
|
||||
resharper_equal_expression_comparison_highlighting = none # EqualExpressionComparison
|
||||
resharper_check_namespace_highlighting = none # CheckNamespace
|
||||
resharper_arrange_object_creation_when_type_not_evident_highlighting = none # Disable "Arrange object creation when type is not evident" highlighting
|
||||
resharper_arrange_this_qualifier_highlighting = none # Disable "Arrange 'this.' qualifier" highlighting
|
||||
@@ -1,236 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.ApplicationInsights;
|
||||
using Microsoft.ApplicationInsights.DataContracts;
|
||||
using Microsoft.ApplicationInsights.Extensibility;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.ApplicationInsights;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Planners;
|
||||
using Microsoft.SemanticKernel.Planning;
|
||||
using Microsoft.SemanticKernel.Plugins.Core;
|
||||
using Microsoft.SemanticKernel.Plugins.Web;
|
||||
using Microsoft.SemanticKernel.Plugins.Web.Bing;
|
||||
using NCalcPlugins;
|
||||
|
||||
/// <summary>
|
||||
/// Example of telemetry in Semantic Kernel using Application Insights within console application.
|
||||
/// </summary>
|
||||
public sealed class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// Log level to be used by <see cref="ILogger"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="LogLevel.Information"/> is set by default. <para />
|
||||
/// <see cref="LogLevel.Trace"/> will enable logging with more detailed information, including sensitive data. Should not be used in production. <para />
|
||||
/// </remarks>
|
||||
private const LogLevel MinLogLevel = LogLevel.Information;
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
public static async Task Main()
|
||||
{
|
||||
var serviceProvider = GetServiceProvider();
|
||||
|
||||
var telemetryClient = serviceProvider.GetRequiredService<TelemetryClient>();
|
||||
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
|
||||
|
||||
using var meterListener = new MeterListener();
|
||||
using var activityListener = new ActivityListener();
|
||||
|
||||
ConfigureMetering(meterListener, telemetryClient);
|
||||
ConfigureTracing(activityListener, telemetryClient);
|
||||
|
||||
var kernel = GetKernel(loggerFactory);
|
||||
var planner = GetSequentialPlanner(kernel, loggerFactory);
|
||||
|
||||
try
|
||||
{
|
||||
using var operation = telemetryClient.StartOperation<DependencyTelemetry>("ApplicationInsights.Example");
|
||||
|
||||
Console.WriteLine("Operation/Trace ID:");
|
||||
Console.WriteLine(Activity.Current?.TraceId);
|
||||
|
||||
var plan = await planner.CreatePlanAsync("Write a poem about John Doe, then translate it into Italian.");
|
||||
|
||||
Console.WriteLine("Original plan:");
|
||||
Console.WriteLine(plan.ToPlanString());
|
||||
|
||||
var result = await kernel.RunAsync(plan);
|
||||
|
||||
Console.WriteLine("Result:");
|
||||
Console.WriteLine(result.GetValue<string>());
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Explicitly call Flush() followed by sleep is required in console apps.
|
||||
// This is to ensure that even if application terminates, telemetry is sent to the back-end.
|
||||
telemetryClient.Flush();
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
}
|
||||
|
||||
private static ServiceProvider GetServiceProvider()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
ConfigureApplicationInsightsTelemetry(services);
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
private static void ConfigureApplicationInsightsTelemetry(ServiceCollection services)
|
||||
{
|
||||
string instrumentationKey = Env.Var("ApplicationInsights__InstrumentationKey");
|
||||
|
||||
services.AddLogging(loggingBuilder =>
|
||||
{
|
||||
loggingBuilder.AddFilter<ApplicationInsightsLoggerProvider>(logLevel => logLevel == MinLogLevel);
|
||||
loggingBuilder.SetMinimumLevel(MinLogLevel);
|
||||
});
|
||||
|
||||
services.AddApplicationInsightsTelemetryWorkerService(options =>
|
||||
{
|
||||
options.ConnectionString = $"InstrumentationKey={instrumentationKey}";
|
||||
});
|
||||
}
|
||||
|
||||
private static IKernel GetKernel(ILoggerFactory loggerFactory)
|
||||
{
|
||||
var folder = RepoFiles.SamplePluginsPath();
|
||||
var bingConnector = new BingConnector(Env.Var("Bing__ApiKey"));
|
||||
var webSearchEnginePlugin = new WebSearchEnginePlugin(bingConnector);
|
||||
|
||||
var kernel = new KernelBuilder()
|
||||
.WithLoggerFactory(loggerFactory)
|
||||
.WithAzureChatCompletionService(
|
||||
Env.Var("AzureOpenAI__ChatDeploymentName"),
|
||||
Env.Var("AzureOpenAI__Endpoint"),
|
||||
Env.Var("AzureOpenAI__ApiKey"))
|
||||
.Build();
|
||||
|
||||
kernel.ImportSemanticFunctionsFromDirectory(folder, "SummarizePlugin", "WriterPlugin");
|
||||
|
||||
kernel.ImportFunctions(webSearchEnginePlugin, "WebSearch");
|
||||
kernel.ImportFunctions(new LanguageCalculatorPlugin(kernel), "advancedCalculator");
|
||||
kernel.ImportFunctions(new TimePlugin(), "time");
|
||||
|
||||
return kernel;
|
||||
}
|
||||
|
||||
private static ISequentialPlanner GetSequentialPlanner(
|
||||
IKernel kernel,
|
||||
ILoggerFactory loggerFactory,
|
||||
int maxTokens = 1024)
|
||||
{
|
||||
var plannerConfig = new SequentialPlannerConfig { MaxTokens = maxTokens };
|
||||
|
||||
return new SequentialPlanner(kernel, plannerConfig).WithInstrumentation(loggerFactory);
|
||||
}
|
||||
|
||||
private static IActionPlanner GetActionPlanner(
|
||||
IKernel kernel,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
return new ActionPlanner(kernel).WithInstrumentation(loggerFactory);
|
||||
}
|
||||
|
||||
private static IStepwisePlanner GetStepwisePlanner(
|
||||
IKernel kernel,
|
||||
ILoggerFactory loggerFactory,
|
||||
int minIterationTimeMs = 1500,
|
||||
int maxTokens = 2000)
|
||||
{
|
||||
var plannerConfig = new StepwisePlannerConfig
|
||||
{
|
||||
MinIterationTimeMs = minIterationTimeMs,
|
||||
MaxTokens = maxTokens
|
||||
};
|
||||
|
||||
return new StepwisePlanner(kernel, plannerConfig).WithInstrumentation(loggerFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example of metering configuration in Application Insights
|
||||
/// using <see cref="MeterListener"/> to attach for <see cref="Meter"/> recordings.
|
||||
/// </summary>
|
||||
/// <param name="meterListener">Instance of <see cref="MeterListener"/> for metering configuration.</param>
|
||||
/// <param name="telemetryClient">Instance of Application Insights <see cref="TelemetryClient"/>.</param>
|
||||
private static void ConfigureMetering(MeterListener meterListener, TelemetryClient telemetryClient)
|
||||
{
|
||||
meterListener.InstrumentPublished = (instrument, listener) =>
|
||||
{
|
||||
// Subscribe to all metrics in Semantic Kernel
|
||||
if (instrument.Meter.Name.StartsWith("Microsoft.SemanticKernel", StringComparison.Ordinal))
|
||||
{
|
||||
listener.EnableMeasurementEvents(instrument);
|
||||
}
|
||||
};
|
||||
|
||||
meterListener.SetMeasurementEventCallback(GetMeasurementCallback<int>(telemetryClient));
|
||||
meterListener.SetMeasurementEventCallback(GetMeasurementCallback<double>(telemetryClient));
|
||||
|
||||
meterListener.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The callback which can be used to get measurement recording.
|
||||
/// </summary>
|
||||
/// <param name="telemetryClient">Instance of Application Insights <see cref="TelemetryClient"/>.</param>
|
||||
private static MeasurementCallback<T> GetMeasurementCallback<T>(TelemetryClient telemetryClient) where T : struct
|
||||
{
|
||||
return (instrument, measurement, tags, state) =>
|
||||
{
|
||||
telemetryClient.GetMetric(instrument.Name).TrackValue(measurement);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example of advanced distributed tracing configuration in Application Insights
|
||||
/// using <see cref="ActivityListener"/> to attach for <see cref="Activity"/> events.
|
||||
/// </summary>
|
||||
/// <param name="activityListener">Instance of <see cref="ActivityListener"/> for tracing configuration.</param>
|
||||
/// <param name="telemetryClient">Instance of Application Insights <see cref="TelemetryClient"/>.</param>
|
||||
private static void ConfigureTracing(ActivityListener activityListener, TelemetryClient telemetryClient)
|
||||
{
|
||||
var operations = new ConcurrentDictionary<string, IOperationHolder<DependencyTelemetry>>();
|
||||
|
||||
// For more detailed tracing we need to attach Activity entity to Application Insights operation manually.
|
||||
void activityStarted(Activity activity)
|
||||
{
|
||||
var operation = telemetryClient.StartOperation<DependencyTelemetry>(activity);
|
||||
operation.Telemetry.Type = activity.Kind.ToString();
|
||||
|
||||
operations.TryAdd(activity.TraceId.ToString(), operation);
|
||||
}
|
||||
|
||||
// We also need to manually stop Application Insights operation when Activity entity is stopped.
|
||||
void activityStopped(Activity activity)
|
||||
{
|
||||
if (operations.TryRemove(activity.TraceId.ToString(), out var operation))
|
||||
{
|
||||
telemetryClient.StopOperation(operation);
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe to all traces in Semantic Kernel
|
||||
activityListener.ShouldListenTo =
|
||||
activitySource => activitySource.Name.StartsWith("Microsoft.SemanticKernel", StringComparison.Ordinal);
|
||||
|
||||
activityListener.Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllData;
|
||||
activityListener.SampleUsingParentId = (ref ActivityCreationOptions<string> _) => ActivitySamplingResult.AllData;
|
||||
activityListener.ActivityStarted = activityStarted;
|
||||
activityListener.ActivityStopped = activityStopped;
|
||||
|
||||
ActivitySource.AddActivityListener(activityListener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
[*.cs]
|
||||
dotnet_diagnostic.CA1016.severity = none
|
||||
@@ -0,0 +1,4 @@
|
||||
**/bin
|
||||
**/obj
|
||||
local.settings.json
|
||||
azure-function/appsettings.json
|
||||
@@ -0,0 +1,54 @@
|
||||
# Semantic Kernel OpenAI plugin starter
|
||||
|
||||
This project provides starter code to create a OpenAI plugin. It includes the following components:
|
||||
|
||||
- An endpoint that serves up an ai-plugin.json file for ChatGPT to discover the plugin
|
||||
- A generator that automatically converts prompts into prompt endpoints
|
||||
- The ability to add additional native functions as endpoints to the plugin
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [.NET 6](https://dotnet.microsoft.com/download/dotnet/8.0) is required to run this starter.
|
||||
- [Azure Functions Core Tools](https://www.npmjs.com/package/azure-functions-core-tools) is required to run this starter.
|
||||
- Install the recommended extensions
|
||||
- [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp)
|
||||
- [Semantic Kernel Tools](https://marketplace.visualstudio.com/items?itemName=ms-semantic-kernel.semantic-kernel) (optional)
|
||||
|
||||
## Configuring the starter
|
||||
|
||||
To configure the starter, you need to provide the following information:
|
||||
|
||||
- Define the properties of the plugin in the appsettings.json file.
|
||||
- Enter the API key for your AI endpoint in the [local.settings.json](./azure-function/local.settings.json.example) file.
|
||||
|
||||
### Using appsettings.json
|
||||
|
||||
Configure an OpenAI endpoint
|
||||
|
||||
1. Copy [settings.json.openai-example](./azure-function/config-samples/appsettings.json.openai-example) to `./appsettings.json`
|
||||
1. Edit the `kernel` object to add your OpenAI endpoint configuration
|
||||
1. Edit the `aiPlugin` object to define the properties that get exposed in the ai-plugin.json file
|
||||
|
||||
Configure an Azure OpenAI endpoint
|
||||
|
||||
1. Copy [settings.json.azure-example](./azure-function/config-samples/appsettings.json.azure-example) to `./appsettings.json`
|
||||
1. Edit the `kernel` object to add your Azure OpenAI endpoint configuration
|
||||
1. Edit the `aiPlugin` object to define the properties that get exposed in the ai-plugin.json file
|
||||
|
||||
### Using local.settings.json
|
||||
|
||||
1. Copy [local.settings.json.example](./azure-function/local.settings.json.example) to `./azure-function/local.settings.json`
|
||||
1. Edit the `Values` object to add your OpenAI endpoint configuration in the `apiKey` property
|
||||
|
||||
## Running the starter
|
||||
|
||||
To run the Azure Functions application just hit `F5`.
|
||||
|
||||
To build and run the Azure Functions application from a terminal use the following commands:
|
||||
|
||||
```powershell
|
||||
cd azure-function
|
||||
dotnet build
|
||||
cd bin/Debug/net8.0
|
||||
func host start
|
||||
```
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Net;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
using Models;
|
||||
|
||||
public class AIPluginJson
|
||||
{
|
||||
[Function("GetAIPluginJson")]
|
||||
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = ".well-known/ai-plugin.json")] HttpRequestData req)
|
||||
{
|
||||
var currentDomain = $"{req.Url.Scheme}://{req.Url.Host}:{req.Url.Port}";
|
||||
|
||||
HttpResponseData response = req.CreateResponse(HttpStatusCode.OK);
|
||||
response.Headers.Add("Content-Type", "application/json");
|
||||
|
||||
var appSettings = AppSettings.LoadSettings();
|
||||
|
||||
// serialize app settings to json using System.Text.Json
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(appSettings.AIPlugin);
|
||||
|
||||
// replace {url} with the current domain
|
||||
json = json.Replace("{url}", currentDomain, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
response.WriteString(json);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- Default properties inherited by all projects. Projects can override. -->
|
||||
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
|
||||
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<AnalysisLevel>latest</AnalysisLevel>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<LangVersion>11</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<NoWarn>CS1591,CA1852,CA1050</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Disable NuGet packaging by default. Projects can override. -->
|
||||
<IsPackable>disable</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugType>portable</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<RepoRoot>$([System.IO.Path]::GetDirectoryName($([MSBuild]::GetPathOfFileAbove('.gitignore', '$(MSBuildThisFileDirectory)'))))</RepoRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,25 @@
|
||||
<Project>
|
||||
<!-- Direct all packages under 'dotnet' to get versions from Directory.Packages.props -->
|
||||
<!-- using Central Package Management feature -->
|
||||
<!-- https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management -->
|
||||
<Sdk Name="Microsoft.Build.CentralPackageVersions" Version="2.1.3" />
|
||||
|
||||
<!-- Only run 'dotnet format' on dev machines, Release builds. Skip on GitHub Actions -->
|
||||
<!-- as this runs in its own Actions job. -->
|
||||
<Target Name="DotnetFormatOnBuild" BeforeTargets="Build"
|
||||
Condition=" '$(Configuration)' == 'Release' AND '$(GITHUB_ACTIONS)' == '' ">
|
||||
<Message Text="Running dotnet format" Importance="high" />
|
||||
<Exec Command="dotnet format --no-restore -v diag $(ProjectFileName)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="AddInternalsVisibleTo" BeforeTargets="BeforeCompile">
|
||||
<!-- Handle Add InternalsVisibleTo to any targets that don't support it. -->
|
||||
<ItemGroup Condition="'@(InternalsVisibleTo->Count())' > 0 AND $([MSBuild]::VersionLessThan($(NETCoreSdkVersion), '5.0.0'))">
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1 Condition="'%(InternalsVisibleTo.PublicKey)' != ''">%(InternalsVisibleTo.Identity), PublicKey="%(InternalsVisibleTo.PublicKey)</_Parameter1>
|
||||
<_Parameter1 Condition="'%(InternalsVisibleTo.PublicKey)' == ''">%(InternalsVisibleTo.Identity)</_Parameter1>
|
||||
<_Parameter1_TypeName>System.String</_Parameter1_TypeName>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace Plugins.AzureFunctions.Extensions;
|
||||
|
||||
public class AIPluginRunner
|
||||
{
|
||||
private readonly ILogger<AIPluginRunner> _logger;
|
||||
private readonly Kernel _kernel;
|
||||
|
||||
public AIPluginRunner(Kernel kernel, ILoggerFactory loggerFactory)
|
||||
{
|
||||
this._kernel = kernel;
|
||||
this._logger = loggerFactory.CreateLogger<AIPluginRunner>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a prompt using the operationID and returns back an HTTP response.
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
/// <param name="pluginName"></param>
|
||||
/// <param name="functionName"></param>
|
||||
public async Task<HttpResponseData> RunAIPluginOperationAsync<T>(HttpRequestData req, string pluginName, string functionName)
|
||||
{
|
||||
KernelArguments arguments = ConvertToKernelArguments((await JsonSerializer.DeserializeAsync<T>(req.Body).ConfigureAwait(true))!);
|
||||
|
||||
var response = req.CreateResponse(HttpStatusCode.OK);
|
||||
response.Headers.Add("Content-Type", "text/plain;charset=utf-8");
|
||||
await response.WriteStringAsync(
|
||||
(await this._kernel.InvokeAsync(pluginName, functionName, arguments).ConfigureAwait(false)).ToString()
|
||||
).ConfigureAwait(false);
|
||||
return response;
|
||||
}
|
||||
|
||||
// Method to convert model to dictionary
|
||||
private static KernelArguments ConvertToKernelArguments<T>(T model)
|
||||
{
|
||||
{
|
||||
var arguments = new KernelArguments();
|
||||
foreach (PropertyInfo property in typeof(T).GetProperties())
|
||||
{
|
||||
if (property.GetValue(model) != null)
|
||||
{
|
||||
arguments.Add(property.Name, property.GetValue(model));
|
||||
}
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Models;
|
||||
|
||||
internal static class KernelBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a chat completion service to the list. It can be either an OpenAI or Azure OpenAI backend service.
|
||||
/// </summary>
|
||||
/// <param name="kernelBuilder"></param>
|
||||
/// <param name="kernelSettings"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
internal static IServiceCollection WithChatCompletionService(this IServiceCollection kernelBuilder, KernelSettings kernelSettings)
|
||||
{
|
||||
switch (kernelSettings.ServiceType.ToUpperInvariant())
|
||||
{
|
||||
case ServiceTypes.AzureOpenAI:
|
||||
kernelBuilder.AddAzureOpenAIChatCompletion(deploymentName: kernelSettings.DeploymentOrModelId, modelId: kernelSettings.DeploymentOrModelId, endpoint: kernelSettings.Endpoint, apiKey: kernelSettings.ApiKey, serviceId: kernelSettings.ServiceId);
|
||||
break;
|
||||
|
||||
case ServiceTypes.OpenAI:
|
||||
kernelBuilder.AddOpenAIChatCompletion(modelId: kernelSettings.DeploymentOrModelId, apiKey: kernelSettings.ApiKey, orgId: kernelSettings.OrgId, serviceId: kernelSettings.ServiceId);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Invalid service type value: {kernelSettings.ServiceType}");
|
||||
}
|
||||
|
||||
return kernelBuilder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Net;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
|
||||
public class Logo
|
||||
{
|
||||
[Function("GetLogo")]
|
||||
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "logo.png")] HttpRequestData req)
|
||||
{
|
||||
// Return logo.png that's in the root of the project
|
||||
var response = req.CreateResponse(HttpStatusCode.OK);
|
||||
response.Headers.Add("Content-Type", "image/png");
|
||||
|
||||
var logo = System.IO.File.ReadAllBytes("logo.png");
|
||||
response.Body.Write(logo);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Models;
|
||||
|
||||
#pragma warning disable CA1056
|
||||
#pragma warning disable CA1034
|
||||
|
||||
public class Pluginsettings
|
||||
{
|
||||
[JsonPropertyName("schema_version")]
|
||||
public string SchemaVersion { get; set; } = "v1";
|
||||
|
||||
[JsonPropertyName("name_for_model")]
|
||||
public string NameForModel { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("name_for_human")]
|
||||
public string NameForHuman { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("description_for_model")]
|
||||
public string DescriptionForModel { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("description_for_human")]
|
||||
public string DescriptionForHuman { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("auth")]
|
||||
public AuthModel Auth { get; set; } = new AuthModel();
|
||||
|
||||
[JsonPropertyName("api")]
|
||||
public ApiModel Api { get; set; } = new ApiModel();
|
||||
|
||||
[JsonPropertyName("logo_url")]
|
||||
public string LogoUrl { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("contact_email")]
|
||||
public string ContactEmail { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("legal_info_url")]
|
||||
public string LegalInfoUrl { get; set; } = string.Empty;
|
||||
|
||||
public class AuthModel
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("authorization_url")]
|
||||
public string AuthorizationType { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class ApiModel
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; } = "openapi";
|
||||
|
||||
[JsonPropertyName("url")]
|
||||
public string Url { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("has_user_authentication")]
|
||||
public bool HasUserAuthentication { get; set; } = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Models;
|
||||
|
||||
#pragma warning disable CA1724
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
public class AppSettings
|
||||
{
|
||||
public const string DefaultConfigFile = "appsettings.json";
|
||||
|
||||
public KernelSettings Kernel { get; set; }
|
||||
public Pluginsettings AIPlugin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Load the kernel settings from settings.json if the file exists and if not attempt to use user secrets.
|
||||
/// </summary>
|
||||
public static AppSettings LoadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var appSettings = FromFile(DefaultConfigFile);
|
||||
appSettings.Kernel.ApiKey = GetApiKey();
|
||||
|
||||
return appSettings;
|
||||
}
|
||||
catch (InvalidDataException ide)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
"Unable to load app settings, please provide configuration settings using instructions in the README.\n" +
|
||||
"Please refer to: https://github.com/microsoft/semantic-kernel-starters/blob/main/sk-csharp-chatgpt-plugin/README.md#configuring-the-starter"
|
||||
);
|
||||
throw new InvalidOperationException(ide.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the kernel settings from the specified configuration file if it exists.
|
||||
/// </summary>
|
||||
private static AppSettings FromFile(string configFile = DefaultConfigFile)
|
||||
{
|
||||
if (!File.Exists(configFile))
|
||||
{
|
||||
throw new FileNotFoundException($"Configuration not found: {configFile}");
|
||||
}
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
|
||||
.AddJsonFile(configFile, optional: true, reloadOnChange: true)
|
||||
.Build();
|
||||
|
||||
return configuration.Get<AppSettings>()
|
||||
?? throw new InvalidDataException($"Invalid app settings in '{configFile}', please provide configuration settings using instructions in the README.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the API key for the AI endpoint from user secrets.
|
||||
/// </summary>
|
||||
internal static string GetApiKey()
|
||||
{
|
||||
return System.Environment.GetEnvironmentVariable("apiKey", EnvironmentVariableTarget.Process)
|
||||
?? throw new InvalidDataException("Invalid semantic kernel settings in user secrets, please provide configuration settings using instructions in the README.");
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Models;
|
||||
|
||||
#pragma warning disable CA1812
|
||||
public class KernelSettings
|
||||
{
|
||||
public string ServiceType { get; set; } = string.Empty;
|
||||
public string ServiceId { get; set; } = string.Empty;
|
||||
public string DeploymentOrModelId { get; set; } = string.Empty;
|
||||
public string Endpoint { get; set; } = string.Empty;
|
||||
public string OrgId { get; set; } = string.Empty;
|
||||
public LogLevel? LogLevel { get; set; }
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Models;
|
||||
|
||||
internal static class ServiceTypes
|
||||
{
|
||||
internal const string OpenAI = "OPENAI";
|
||||
internal const string AzureOpenAI = "AZUREOPENAI";
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace Plugins;
|
||||
|
||||
public sealed class MathPlugin
|
||||
{
|
||||
[KernelFunction, Description("Take the square root of a number")]
|
||||
public static double Sqrt(
|
||||
[Description("The number to take a square root of")] double number1
|
||||
)
|
||||
{
|
||||
return Math.Sqrt(number1);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Add two numbers")]
|
||||
public static double Add(
|
||||
[Description("The first number to add")] double number1,
|
||||
[Description("The second number to add")] double number2
|
||||
)
|
||||
{
|
||||
return number1 + number2;
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Subtract two numbers")]
|
||||
public static double Subtract(
|
||||
[Description("The first number to subtract from")] double number1,
|
||||
[Description("The second number to subtract away")] double number2
|
||||
)
|
||||
{
|
||||
return number1 - number2;
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Multiply two numbers. When increasing by a percentage, don't forget to add 1 to the percentage.")]
|
||||
public static double Multiply(
|
||||
[Description("The first number to multiply")] double number1,
|
||||
[Description("The second number to multiply")] double number2
|
||||
)
|
||||
{
|
||||
return number1 * number2;
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Divide two numbers")]
|
||||
public static double Divide(
|
||||
[Description("The first number to divide from")] double number1,
|
||||
[Description("The second number to divide by")] double number2
|
||||
)
|
||||
{
|
||||
return number1 / number2;
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Raise a number to a power")]
|
||||
public static double Power(
|
||||
[Description("The number to raise")] double number1,
|
||||
[Description("The power to raise the number to")] double number2
|
||||
)
|
||||
{
|
||||
return Math.Pow(number1, number2);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Take the log of a number")]
|
||||
public static double Log(
|
||||
[Description("The number to take the log of")] double number1,
|
||||
[Description("The base of the log")] double number2
|
||||
)
|
||||
{
|
||||
return Math.Log(number1, number2);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Round a number to the target number of decimal places")]
|
||||
public static double Round(
|
||||
[Description("The number to round")] double number1,
|
||||
[Description("The number of decimal places to round to")] double number2
|
||||
)
|
||||
{
|
||||
return Math.Round(number1, (int)number2);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Take the absolute value of a number")]
|
||||
public static double Abs(
|
||||
[Description("The number to take the absolute value of")] double number1
|
||||
)
|
||||
{
|
||||
return Math.Abs(number1);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Take the floor of a number")]
|
||||
public static double Floor(
|
||||
[Description("The number to take the floor of")] double number1
|
||||
)
|
||||
{
|
||||
return Math.Floor(number1);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Take the ceiling of a number")]
|
||||
public static double Ceiling(
|
||||
[Description("The number to take the ceiling of")] double number1
|
||||
)
|
||||
{
|
||||
return Math.Ceiling(number1);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Take the sine of a number")]
|
||||
public static double Sin(
|
||||
[Description("The number to take the sine of")] double number1
|
||||
)
|
||||
{
|
||||
return Math.Sin(number1);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Take the cosine of a number")]
|
||||
public static double Cos(
|
||||
[Description("The number to take the cosine of")] double number1
|
||||
)
|
||||
{
|
||||
return Math.Cos(number1);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Take the tangent of a number")]
|
||||
public static double Tan(
|
||||
[Description("The number to take the tangent of")] double number1
|
||||
)
|
||||
{
|
||||
return Math.Tan(number1);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Take the arcsine of a number")]
|
||||
public static double Asin(
|
||||
[Description("The number to take the arcsine of")] double number1
|
||||
)
|
||||
{
|
||||
return Math.Asin(number1);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Take the arccosine of a number")]
|
||||
public static double Acos(
|
||||
[Description("The number to take the arccosine of")] double number1
|
||||
)
|
||||
{
|
||||
return Math.Acos(number1);
|
||||
}
|
||||
|
||||
[KernelFunction, Description("Take the arctangent of a number")]
|
||||
public static double Atan(
|
||||
[Description("The number to take the arctangent of")] double number1
|
||||
)
|
||||
{
|
||||
return Math.Atan(number1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Text.Json;
|
||||
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
|
||||
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
|
||||
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Models;
|
||||
using Plugins;
|
||||
using Plugins.AzureFunctions.Extensions;
|
||||
|
||||
const string DefaultSemanticFunctionsFolder = "Prompts";
|
||||
string semanticFunctionsFolder = Environment.GetEnvironmentVariable("SEMANTIC_SKILLS_FOLDER") ?? DefaultSemanticFunctionsFolder;
|
||||
|
||||
var host = new HostBuilder()
|
||||
.ConfigureFunctionsWorkerDefaults()
|
||||
.ConfigureAppConfiguration(configuration =>
|
||||
{
|
||||
var config = configuration.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true);
|
||||
var builtConfig = config.Build();
|
||||
})
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
services.Configure<JsonSerializerOptions>(options =>
|
||||
{
|
||||
// `ConfigureFunctionsWorkerDefaults` sets the default to ignore casing already.
|
||||
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||
});
|
||||
|
||||
services.AddSingleton<IOpenApiConfigurationOptions>(_ =>
|
||||
{
|
||||
var options = new OpenApiConfigurationOptions()
|
||||
{
|
||||
Info = new OpenApiInfo()
|
||||
{
|
||||
Version = "1.0.0",
|
||||
Title = "My Plugin",
|
||||
Description = "This plugin does..."
|
||||
},
|
||||
Servers = DefaultOpenApiConfigurationOptions.GetHostNames(),
|
||||
OpenApiVersion = OpenApiVersionType.V3,
|
||||
//IncludeRequestingHostName = true,
|
||||
ForceHttps = false,
|
||||
ForceHttp = false,
|
||||
};
|
||||
|
||||
return options;
|
||||
});
|
||||
services
|
||||
.AddTransient((providers) =>
|
||||
{
|
||||
var appSettings = AppSettings.LoadSettings();
|
||||
var builder = Kernel.CreateBuilder();
|
||||
builder.Services.WithChatCompletionService(appSettings.Kernel);
|
||||
builder.Services.AddLogging(loggingBuilder =>
|
||||
{
|
||||
loggingBuilder.AddFilter(level => true);
|
||||
loggingBuilder.AddConsole();
|
||||
});
|
||||
builder.Plugins.AddFromType<MathPlugin>();
|
||||
return builder.Build();
|
||||
})
|
||||
.AddScoped<AIPluginRunner>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"description": "If the request does not provide an explicit number for a parameter, use this function to make up a logical value; do not make up a value yourself.",
|
||||
"type": "completion",
|
||||
"completion": {
|
||||
"max_tokens": 1000,
|
||||
"temperature": 0.9,
|
||||
"top_p": 0.0,
|
||||
"presence_penalty": 0.0,
|
||||
"frequency_penalty": 0.0
|
||||
},
|
||||
"input": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "input",
|
||||
"description": "A detailed description (2-3 sentences) of the missing value; provide all the context the user provided to get the best results.",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"name": "units",
|
||||
"description": "The units used to measure the value (e.g., 'meters', 'seconds', 'dollars', etc.). (required)",
|
||||
"defaultValue": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
INSTURCTIONS:
|
||||
Provide a realistic value for the missing parameter. If you don't know the answer, provide a best guess using the limited information provided.
|
||||
Do not give a range of values. Do not give a value that is not realistic. Do not give a value that is not possible.
|
||||
|
||||
OUTPUT FORMAT:
|
||||
{
|
||||
"value": "<value>",
|
||||
"reason": "<reason>",
|
||||
"units": "<units>",
|
||||
}
|
||||
|
||||
MISSING PARAMETER DESCRIPTION:
|
||||
{{$input}}
|
||||
|
||||
PARAMETER UNITS:
|
||||
{{$units}}
|
||||
|
||||
ANSWER:
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.002.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sk-chatgpt-azure-function", "sk-chatgpt-azure-function.csproj", "{20F608E8-E748-4432-B673-7D98782A6E23}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PluginShared", "shared\PluginShared.csproj", "{82A779FB-9CFD-4159-8AFC-54D00C76947C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{20F608E8-E748-4432-B673-7D98782A6E23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{20F608E8-E748-4432-B673-7D98782A6E23}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{20F608E8-E748-4432-B673-7D98782A6E23}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{20F608E8-E748-4432-B673-7D98782A6E23}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{82A779FB-9CFD-4159-8AFC-54D00C76947C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{82A779FB-9CFD-4159-8AFC-54D00C76947C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{82A779FB-9CFD-4159-8AFC-54D00C76947C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{82A779FB-9CFD-4159-8AFC-54D00C76947C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {76F2FDDE-51A7-4934-BEB6-2D6F082412BD}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"kernel": {
|
||||
"serviceType": "AzureOpenAI",
|
||||
"serviceId": "gpt-35-turbo",
|
||||
"deploymentOrModelId": "gpt-35-turbo",
|
||||
"endpoint": "https:// ... your endpoint ... .openai.azure.com/"
|
||||
},
|
||||
"aiPlugin": {
|
||||
"schemaVersion": "v1",
|
||||
"nameForModel": "MyPlugin",
|
||||
"nameForHuman": "My Plugin",
|
||||
"descriptionForModel": "<Instructions for the model>",
|
||||
"descriptionForHuman": "<Description for users>",
|
||||
"auth": {
|
||||
"type": "none"
|
||||
},
|
||||
"api": {
|
||||
"type": "openapi",
|
||||
"url": "{url}/swagger.json"
|
||||
},
|
||||
"logoUrl": "{url}/logo.png",
|
||||
"contactEmail": "[email protected]",
|
||||
"legalInfoUrl": "http://www.example.com/legal"
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"kernel": {
|
||||
"serviceType": "OpenAI",
|
||||
"serviceId": "gpt-3.5-turbo",
|
||||
"deploymentOrModelId": "gpt-3.5-turbo",
|
||||
"orgId": ""
|
||||
},
|
||||
"aiPlugin": {
|
||||
"schemaVersion": "v1",
|
||||
"nameForModel": "MyPlugin",
|
||||
"nameForHuman": "My Plugin",
|
||||
"descriptionForModel": "<Instructions for the model>",
|
||||
"descriptionForHuman": "<Description for users>",
|
||||
"auth": {
|
||||
"type": "none"
|
||||
},
|
||||
"api": {
|
||||
"type": "openapi",
|
||||
"url": "{url}/swagger.json"
|
||||
},
|
||||
"logoUrl": "{url}/logo.png",
|
||||
"contactEmail": "[email protected]",
|
||||
"legalInfoUrl": "http://www.example.com/legal"
|
||||
}
|
||||
}
|
||||
+5
@@ -7,5 +7,10 @@
|
||||
"excludedTypes": "Request"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"http": {
|
||||
"routePrefix": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user