mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Compare commits
72
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5786b2c5b | ||
|
|
d858c23bab | ||
|
|
365e438981 | ||
|
|
01a04a3114 | ||
|
|
ee340bef1d | ||
|
|
19ac6e57c0 | ||
|
|
292a2078fa | ||
|
|
ae06c9e4bb | ||
|
|
f7a9005235 | ||
|
|
b86c130411 | ||
|
|
d774b64df0 | ||
|
|
42ffe59592 | ||
|
|
a683c0e865 | ||
|
|
a597a925cb | ||
|
|
9c2e800189 | ||
|
|
a26e9d6274 | ||
|
|
411ee7a60f | ||
|
|
6835161f2d | ||
|
|
3cfd34836f | ||
|
|
a81279d960 | ||
|
|
931197ba95 | ||
|
|
d57303cf53 | ||
|
|
b5595f6f70 | ||
|
|
6232dd8305 | ||
|
|
e9ab514696 | ||
|
|
b69f994840 | ||
|
|
ceaea46765 | ||
|
|
1edd28b264 | ||
|
|
5c70e143a0 | ||
|
|
c2d285e614 | ||
|
|
cb343dd707 | ||
|
|
0d5d10d24a | ||
|
|
e3b700ad9f | ||
|
|
8ae33d3c2b | ||
|
|
bf326e7f78 | ||
|
|
b457d3cdbe | ||
|
|
139c292807 | ||
|
|
b26108975d | ||
|
|
a3909b8b5b | ||
|
|
67de7bcd93 | ||
|
|
e769256976 | ||
|
|
257e717608 | ||
|
|
66a399ee2d | ||
|
|
8cf8b0f995 | ||
|
|
6c624319db | ||
|
|
306c81aef8 | ||
|
|
0f2c5e6cb8 | ||
|
|
17b4dfab14 | ||
|
|
486b78126d | ||
|
|
0723f1d183 | ||
|
|
a57b37d5fa | ||
|
|
907d79ab3c | ||
|
|
ed53ba158b | ||
|
|
5d8be5836c | ||
|
|
0c1d12feca | ||
|
|
b1c210c9d8 | ||
|
|
b8260ae24c | ||
|
|
d302bffc63 | ||
|
|
7a4f6877c3 | ||
|
|
b6c3b8d3f2 | ||
|
|
1dde57981e | ||
|
|
112410ecd6 | ||
|
|
a89c15d6e6 | ||
|
|
27b3a517eb | ||
|
|
1b4103dce3 | ||
|
|
2a4802eac3 | ||
|
|
e303cc963d | ||
|
|
c78edda64f | ||
|
|
a610a4769c | ||
|
|
bcbf1b33e8 | ||
|
|
9f43108ef1 | ||
|
|
db424d56f3 |
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "C# (.NET)",
|
||||
"image": "mcr.microsoft.com/devcontainers/dotnet:9.0",
|
||||
"image": "mcr.microsoft.com/devcontainers/dotnet:10.0",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/dotnet:2.4.0": {},
|
||||
"ghcr.io/devcontainers/features/powershell:1.5.1": {},
|
||||
"ghcr.io/devcontainers/features/azure-cli:1.2.8": {}
|
||||
"ghcr.io/devcontainers/features/azure-cli:1.2.8": {},
|
||||
"ghcr.io/devcontainers/features/docker-in-docker:2.12.4": {}
|
||||
},
|
||||
"workspaceFolder": "/workspaces/agent-framework/dotnet/",
|
||||
"customizations": {
|
||||
|
||||
@@ -1636,4 +1636,4 @@ The property mapping guide from a `AutoFunctionInvocationContext` to a `Function
|
||||
| Result | Use `return` from the delegate |
|
||||
| Terminate | Terminate |
|
||||
| CancellationToken | provided via argument to middleware delegate |
|
||||
| Arguments | Arguments |
|
||||
| Arguments | Arguments |
|
||||
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
workflow-samples
|
||||
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v5.0.0
|
||||
uses: actions/setup-dotnet@v5.0.1
|
||||
with:
|
||||
global-json-file: ${{ github.workspace }}/dotnet/global.json
|
||||
- name: Build dotnet solutions
|
||||
@@ -124,6 +124,16 @@ jobs:
|
||||
popd
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
# Start Cosmos DB Emulator for Cosmos-based unit tests (only on Windows)
|
||||
- name: Start Azure Cosmos DB Emulator
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
Write-Host "Launching Azure Cosmos DB Emulator"
|
||||
Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator"
|
||||
Start-CosmosDbEmulator -NoUI -Key "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
|
||||
echo "COSMOS_EMULATOR_AVAILABLE=true" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Run Unit Tests
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -143,6 +153,10 @@ jobs:
|
||||
echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)"
|
||||
fi
|
||||
done
|
||||
env:
|
||||
# Cosmos DB Emulator connection settings
|
||||
COSMOSDB_ENDPOINT: https://localhost:8081
|
||||
COSMOSDB_KEY: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==
|
||||
|
||||
- name: Log event name and matrix integration-tests
|
||||
shell: bash
|
||||
@@ -181,6 +195,9 @@ jobs:
|
||||
fi
|
||||
done
|
||||
env:
|
||||
# Cosmos DB Emulator connection settings
|
||||
COSMOSDB_ENDPOINT: https://localhost:8081
|
||||
COSMOSDB_KEY: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==
|
||||
# OpenAI Models
|
||||
OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }}
|
||||
OpenAI__ChatModelId: ${{ vars.OPENAI__CHATMODELID }}
|
||||
@@ -200,7 +217,7 @@ jobs:
|
||||
# Generate test reports and check coverage
|
||||
- name: Generate test reports
|
||||
if: matrix.targetFramework == env.COVERAGE_FRAMEWORK
|
||||
uses: danielpalme/ReportGenerator-GitHub-Action@5.4.18
|
||||
uses: danielpalme/ReportGenerator-GitHub-Action@5.5.0
|
||||
with:
|
||||
reports: "./TestResults/Coverage/**/coverage.cobertura.xml"
|
||||
targetdir: "./TestResults/Reports"
|
||||
|
||||
@@ -48,6 +48,8 @@ dotnet add package Microsoft.Agents.AI
|
||||
- **[Migration from Semantic Kernel](https://learn.microsoft.com/en-us/agent-framework/migration-guide/from-semantic-kernel)** - Guide to migrate from Semantic Kernel
|
||||
- **[Migration from AutoGen](https://learn.microsoft.com/en-us/agent-framework/migration-guide/from-autogen)** - Guide to migrate from AutoGen
|
||||
|
||||
Still have questions? Join our [weekly office hours](./COMMUNITY.md#public-community-office-hours) or ask questions in our [Discord channel](https://discord.gg/b5zjErwbQM) to get help from the team and other users.
|
||||
|
||||
### ✨ **Highlights**
|
||||
|
||||
- **Graph-based Workflows**: Connect agents and deterministic functions using data flows with streaming, checkpointing, human-in-the-loop, and time-travel capabilities
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ Microsoft Agent Framework relies on existing LLMs. Using the framework retains c
|
||||
|
||||
- **Platform Requirements**: Python 3.10+ required, specific .NET versions (.NET 8.0, 9.0, 10.0, netstandard2.0, net472)
|
||||
- **API Dependencies**: Requires proper configuration of LLM provider keys and endpoints
|
||||
- **Orchestration Features**: Advanced orchestration patterns like GroupChat, Sequential, and Concurrent orchestrations are "coming soon" for Python implementation
|
||||
- **Orchestration Features**: Advanced orchestration patterns including GroupChat, Sequential, and Concurrent workflows are now available in both Python and .NET implementations. See the respective language documentation for examples.
|
||||
- **Privacy and Data Protection**: The framework allows for human participation in conversations between agents. It is important to ensure that user data and conversations are protected and that developers use appropriate measures to safeguard privacy.
|
||||
- **Accountability and Transparency**: The framework involves multiple agents conversing and collaborating, it is important to establish clear accountability and transparency mechanisms. Users should be able to understand and trace the decision-making process of the agents involved in order to ensure accountability and address any potential issues or biases.
|
||||
- **Security & unintended consequences**: The use of multi-agent conversations and automation in complex tasks may have unintended consequences. Especially, allowing agents to make changes in external environments through tool calls or function execution could pose significant risks. Developers should carefully consider the potential risks and ensure that appropriate safeguards are in place to prevent harm or negative outcomes, including keeping a human in the loop for decision making.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
kind: Prompt
|
||||
name: Assistant
|
||||
description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions is the language specified by the user. You return your answers in a JSON format. You must include Assistants as the type in your response.
|
||||
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format. You must include Assistants as the type in your response.
|
||||
model:
|
||||
id: =Env.AZURE_OPENAI_DEPLOYMENT_NAME
|
||||
id: gpt-4o-mini
|
||||
provider: AzureOpenAI
|
||||
apiType: Assistants
|
||||
options:
|
||||
@@ -12,14 +12,14 @@ model:
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
type:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The type of the response.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
kind: Prompt
|
||||
name: Assistant
|
||||
description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format. You must include Chat as the type in your response.
|
||||
model:
|
||||
id: gpt-4o-mini
|
||||
provider: AzureOpenAI
|
||||
apiType: Chat
|
||||
options:
|
||||
temperature: 0.9
|
||||
topP: 0.95
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
type:
|
||||
type: string
|
||||
required: true
|
||||
description: The type of the response.
|
||||
@@ -1,28 +1,25 @@
|
||||
kind: Prompt
|
||||
name: Assistant
|
||||
description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions is the language specified by the user. You return your answers in a JSON format. You must include Responses as the type in your response.
|
||||
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format. You must include Responses as the type in your response.
|
||||
model:
|
||||
id: =Env.AZURE_OPENAI_DEPLOYMENT_NAME
|
||||
id: gpt-4o-mini
|
||||
provider: AzureOpenAI
|
||||
apiType: Responses
|
||||
options:
|
||||
text:
|
||||
verbosity: medium
|
||||
connection:
|
||||
kind: remote
|
||||
endpoint: =Env.AZURE_OPENAI_ENDPOINT
|
||||
temperature: 0.9
|
||||
topP: 0.95
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
type:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The type of the response.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
kind: Prompt
|
||||
name: Assistant
|
||||
description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions is the language specified by the user. You return your answers in a JSON format.
|
||||
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format.
|
||||
model:
|
||||
options:
|
||||
temperature: 0.9
|
||||
@@ -9,10 +9,10 @@ model:
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
|
||||
@@ -4,6 +4,8 @@ description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions using the tools provided.
|
||||
model:
|
||||
options:
|
||||
temperature: 0.9
|
||||
topP: 0.95
|
||||
allowMultipleToolCalls: true
|
||||
chatToolMode: auto
|
||||
tools:
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
kind: Prompt
|
||||
name: Assistant
|
||||
description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format.
|
||||
model:
|
||||
id: gpt-4.1-mini
|
||||
options:
|
||||
temperature: 0.9
|
||||
topP: 0.95
|
||||
connection:
|
||||
kind: Remote
|
||||
endpoint: =Env.AZURE_FOUNDRY_PROJECT_ENDPOINT
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
@@ -1,30 +1,28 @@
|
||||
kind: Prompt
|
||||
name: Assistant
|
||||
description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions is the language specified by the user. You return your answers in a JSON format. You must include Assistants as the type in your response.
|
||||
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format. You must include Assistants as the type in your response.
|
||||
model:
|
||||
id: =Env.OPENAI_MODEL
|
||||
id: gpt-4.1-mini
|
||||
provider: OpenAI
|
||||
apiType: Assistants
|
||||
options:
|
||||
temperature: 0.9
|
||||
topP: 0.95
|
||||
connection:
|
||||
kind: key
|
||||
kind: ApiKey
|
||||
key: =Env.OPENAI_APIKEY
|
||||
outputSchema:
|
||||
name: AssistantResponse
|
||||
description: The response from the assistant.
|
||||
properties:
|
||||
language:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
type:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The type of the response.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
kind: Prompt
|
||||
name: Assistant
|
||||
description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format. You must include Chat as the type in your response.
|
||||
model:
|
||||
id: gpt-4.1-mini
|
||||
provider: OpenAI
|
||||
apiType: Chat
|
||||
options:
|
||||
temperature: 0.9
|
||||
topP: 0.95
|
||||
connection:
|
||||
kind: ApiKey
|
||||
key: =Env.OPENAI_APIKEY
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
type:
|
||||
type: string
|
||||
required: true
|
||||
description: The type of the response.
|
||||
@@ -1,28 +1,28 @@
|
||||
kind: Prompt
|
||||
name: Assistant
|
||||
description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions is the language specified by the user. You return your answers in a JSON format. You must include Responses as the type in your response.
|
||||
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format. You must include Responses as the type in your response.
|
||||
model:
|
||||
id: =Env.OPENAI_MODEL
|
||||
id: gpt-4.1-mini
|
||||
provider: OpenAI
|
||||
apiType: Responses
|
||||
options:
|
||||
text:
|
||||
verbosity: medium
|
||||
temperature: 0.9
|
||||
topP: 0.95
|
||||
connection:
|
||||
kind: key
|
||||
kind: ApiKey
|
||||
key: =Env.OPENAI_APIKEY
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
type:
|
||||
kind: string
|
||||
type: string
|
||||
required: true
|
||||
description: The type of the response.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<NoWarn>$(NoWarn);NU5128</NoWarn>
|
||||
<NoWarn>$(NoWarn);NU5128;CS8002</NoWarn>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<TargetFrameworksCore>net10.0;net9.0;net8.0</TargetFrameworksCore>
|
||||
<TargetFrameworks>$(TargetFrameworksCore);netstandard2.0;net472</TargetFrameworks>
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Aspire -->
|
||||
<AspireAppHostSdkVersion>13.0.0</AspireAppHostSdkVersion>
|
||||
<AspireAppHostSdkVersion>13.0.1</AspireAppHostSdkVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Aspire.* -->
|
||||
<PackageVersion Include="Anthropic" Version="11.0.0" />
|
||||
<PackageVersion Include="Anthropic.Foundry" Version="0.1.0" />
|
||||
<PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="13.0.0-preview.1.25560.3" />
|
||||
<PackageVersion Include="Aspire.Hosting.AppHost" Version="$(AspireAppHostSdkVersion)" />
|
||||
<PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="$(AspireAppHostSdkVersion)" />
|
||||
@@ -19,10 +21,17 @@
|
||||
<!-- Azure.* -->
|
||||
<PackageVersion Include="Azure.AI.Projects" Version="1.2.0-beta.3" />
|
||||
<PackageVersion Include="Azure.AI.Projects.OpenAI" Version="1.0.0-beta.4" />
|
||||
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.2.0-beta.7" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="2.5.0-beta.1" />
|
||||
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.2.0-beta.8" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="2.7.0-beta.1" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.17.0" />
|
||||
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.4.0" />
|
||||
<!-- Google Gemini -->
|
||||
<PackageVersion Include="Google.GenAI" Version="0.6.0" />
|
||||
<PackageVersion Include="Mscc.GenerativeAI.Microsoft" Version="2.9.3" />
|
||||
<!-- Microsoft.Azure.* -->
|
||||
<PackageVersion Include="Microsoft.Azure.Cosmos" Version="3.54.0" />
|
||||
<!-- Newtonsoft.Json -->
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<!-- System.* -->
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
|
||||
@@ -37,6 +46,7 @@
|
||||
<PackageVersion Include="System.Text.Json" Version="10.0.0" />
|
||||
<PackageVersion Include="System.Threading.Channels" Version="10.0.0" />
|
||||
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
|
||||
<PackageVersion Include="System.Net.Security" Version="4.3.2" />
|
||||
<!-- OpenTelemetry -->
|
||||
<PackageVersion Include="OpenTelemetry" Version="1.13.1" />
|
||||
<PackageVersion Include="OpenTelemetry.Api" Version="1.13.1" />
|
||||
@@ -91,7 +101,6 @@
|
||||
<!-- MCP -->
|
||||
<PackageVersion Include="ModelContextProtocol" Version="0.4.0-preview.3" />
|
||||
<!-- Inference SDKs -->
|
||||
<PackageVersion Include="Anthropic.SDK" Version="5.8.0" />
|
||||
<PackageVersion Include="AWSSDK.Extensions.Bedrock.MEAI" Version="4.0.4.6" />
|
||||
<PackageVersion Include="Microsoft.ML.OnnxRuntimeGenAI" Version="0.10.0" />
|
||||
<PackageVersion Include="OllamaSharp" Version="5.4.8" />
|
||||
@@ -127,6 +136,7 @@
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.3" />
|
||||
<PackageVersion Include="Xunit.SkippableFact" Version="1.5.23" />
|
||||
<PackageVersion Include="xretry" Version="1.9.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
|
||||
<!-- Symbols -->
|
||||
@@ -153,20 +163,20 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageVersion Include="Roslynator.Analyzers" Version="[4.14.1]" />
|
||||
<PackageVersion Include="Roslynator.Analyzers" Version="4.14.1" />
|
||||
<PackageReference Include="Roslynator.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageVersion Include="Roslynator.CodeAnalysis.Analyzers" Version="[4.14.0]" />
|
||||
<PackageVersion Include="Roslynator.CodeAnalysis.Analyzers" Version="4.14.1" />
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="[4.14.0]" />
|
||||
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="4.14.1" />
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -40,16 +40,19 @@
|
||||
<Folder Name="/Samples/GettingStarted/A2A/">
|
||||
<File Path="samples/GettingStarted/A2A/README.md" />
|
||||
<Project Path="samples/GettingStarted/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj" />
|
||||
<Project Path="samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/A2AAgent_PollingForTaskCompletion.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/AgentProviders/">
|
||||
<File Path="samples/GettingStarted/AgentProviders/README.md" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_A2A/Agent_With_A2A.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Agent_With_AzureAIAgentsPersistent.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/Agent_With_AzureFoundryModel.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Agent_With_AzureOpenAIChatCompletion.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIResponses/Agent_With_AzureOpenAIResponses.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Agent_With_CustomImplementation.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/Agent_With_GoogleGemini.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_ONNX/Agent_With_ONNX.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/Agent_With_OpenAIAssistants.csproj" />
|
||||
@@ -76,11 +79,21 @@
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Agent_Step16_ChatReduction.csproj" />
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Agent_Step17_BackgroundResponses.csproj" />
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Agent_Step18_DeepResearch.csproj" />
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step19_Declarative/Agent_Step19_Declarative.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/DeclarativeAgents/">
|
||||
<Project Path="samples/GettingStarted/DeclarativeAgents/ChatClient/DeclarativeChatClientAgents.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/DevUI/">
|
||||
<File Path="samples/GettingStarted/DevUI/README.md" />
|
||||
<Project Path="samples/GettingStarted/DevUI/DevUI_Step01_BasicUsage/DevUI_Step01_BasicUsage.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/AgentWithAnthropic/">
|
||||
<File Path="samples/GettingStarted/AgentWithAnthropic/README.md" />
|
||||
<Project Path="samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Agent_Anthropic_Step01_Running.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Agent_Anthropic_Step02_Reasoning.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Agent_Anthropic_Step03_UsingFunctionTools.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/AgentWithMemory/">
|
||||
<File Path="samples/GettingStarted/AgentWithMemory/README.md" />
|
||||
<Project Path="samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/AgentWithMemory_Step01_ChatHistoryMemory.csproj" />
|
||||
@@ -311,6 +324,7 @@
|
||||
<File Path="src/Shared/Demos/SampleEnvironment.cs" />
|
||||
</Folder>
|
||||
<Folder Name="/Solution Items/src/Shared/IntegrationTests/">
|
||||
<File Path="src/Shared/IntegrationTests/AnthropicConfiguration.cs" />
|
||||
<File Path="src/Shared/IntegrationTests/AzureAIConfiguration.cs" />
|
||||
<File Path="src/Shared/IntegrationTests/Mem0Configuration.cs" />
|
||||
<File Path="src/Shared/IntegrationTests/OpenAIConfiguration.cs" />
|
||||
@@ -335,9 +349,12 @@
|
||||
<Project Path="src/Microsoft.Agents.AI.A2A/Microsoft.Agents.AI.A2A.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Abstractions/Microsoft.Agents.AI.Abstractions.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.AGUI/Microsoft.Agents.AI.AGUI.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Anthropic/Microsoft.Agents.AI.Anthropic.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.AzureAI.Persistent/Microsoft.Agents.AI.AzureAI.Persistent.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.CopilotStudio/Microsoft.Agents.AI.CopilotStudio.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.CosmosNoSql/Microsoft.Agents.AI.CosmosNoSql.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Declarative/Microsoft.Agents.AI.Declarative.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.DevUI/Microsoft.Agents.AI.DevUI.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.DurableTask/Microsoft.Agents.AI.DurableTask.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csproj" />
|
||||
@@ -357,6 +374,7 @@
|
||||
<Folder Name="/Tests/" />
|
||||
<Folder Name="/Tests/IntegrationTests/">
|
||||
<Project Path="tests/AgentConformance.IntegrationTests/AgentConformance.IntegrationTests.csproj" />
|
||||
<Project Path="tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletion.IntegrationTests.csproj" />
|
||||
<Project Path="tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj" />
|
||||
<Project Path="tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistent.IntegrationTests.csproj" />
|
||||
<Project Path="tests/CopilotStudio.IntegrationTests/CopilotStudio.IntegrationTests.csproj" />
|
||||
@@ -373,8 +391,11 @@
|
||||
<Project Path="tests/Microsoft.Agents.AI.A2A.UnitTests/Microsoft.Agents.AI.A2A.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Abstractions.UnitTests/Microsoft.Agents.AI.Abstractions.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.AGUI.UnitTests/Microsoft.Agents.AI.AGUI.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Anthropic.UnitTests/Microsoft.Agents.AI.Anthropic.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.AzureAI.UnitTests/Microsoft.Agents.AI.AzureAI.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/Microsoft.Agents.AI.CosmosNoSql.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Declarative.UnitTests/Microsoft.Agents.AI.Declarative.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.DevUI.UnitTests/Microsoft.Agents.AI.DevUI.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.DurableTask.UnitTests/Microsoft.Agents.AI.DurableTask.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/Microsoft.Agents.AI.Hosting.A2A.UnitTests.csproj" />
|
||||
@@ -389,4 +410,4 @@
|
||||
<Project Path="tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.Workflows.UnitTests/Microsoft.Agents.AI.Workflows.UnitTests.csproj" />
|
||||
</Folder>
|
||||
</Solution>
|
||||
</Solution>
|
||||
@@ -2,9 +2,9 @@
|
||||
<PropertyGroup>
|
||||
<!-- Central version prefix - applies to all nuget packages. -->
|
||||
<VersionPrefix>1.0.0</VersionPrefix>
|
||||
<PackageVersion Condition="'$(VersionSuffix)' != ''">$(VersionPrefix)-$(VersionSuffix).251114.1</PackageVersion>
|
||||
<PackageVersion Condition="'$(VersionSuffix)' == ''">$(VersionPrefix)-preview.251114.1</PackageVersion>
|
||||
<GitTag>1.0.0-preview.251114.1</GitTag>
|
||||
<PackageVersion Condition="'$(VersionSuffix)' != ''">$(VersionPrefix)-$(VersionSuffix).251125.1</PackageVersion>
|
||||
<PackageVersion Condition="'$(VersionSuffix)' == ''">$(VersionPrefix)-preview.251125.1</PackageVersion>
|
||||
<GitTag>1.0.0-preview.251125.1</GitTag>
|
||||
|
||||
<Configurations>Debug;Release;Publish</Configurations>
|
||||
<IsPackable>true</IsPackable>
|
||||
|
||||
@@ -14,11 +14,6 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'net10.0'))">
|
||||
<PackageReference Include="System.Net.ServerSentEvents" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.Agents.AI.A2A\Microsoft.Agents.AI.A2A.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.Agents.AI.Abstractions\Microsoft.Agents.AI.Abstractions.csproj" />
|
||||
|
||||
-5
@@ -15,11 +15,6 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'net10.0'))">
|
||||
<PackageReference Include="System.Net.ServerSentEvents" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.A2A\Microsoft.Agents.AI.A2A.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="A2A" />
|
||||
<PackageReference Include="Azure.AI.OpenAI" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
<PackageReference Include="System.Net.ServerSentEvents" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.A2A\Microsoft.Agents.AI.A2A.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how to poll for long-running task completion using continuation tokens with an A2A AI agent.
|
||||
|
||||
using A2A;
|
||||
using Microsoft.Agents.AI;
|
||||
|
||||
var a2aAgentHost = Environment.GetEnvironmentVariable("A2A_AGENT_HOST") ?? throw new InvalidOperationException("A2A_AGENT_HOST is not set.");
|
||||
|
||||
// Initialize an A2ACardResolver to get an A2A agent card.
|
||||
A2ACardResolver agentCardResolver = new(new Uri(a2aAgentHost));
|
||||
|
||||
// Get the agent card
|
||||
AgentCard agentCard = await agentCardResolver.GetAgentCardAsync();
|
||||
|
||||
// Create an instance of the AIAgent for an existing A2A agent specified by the agent card.
|
||||
AIAgent agent = agentCard.GetAIAgent();
|
||||
|
||||
AgentThread thread = agent.GetNewThread();
|
||||
|
||||
// Start the initial run with a long-running task.
|
||||
AgentRunResponse response = await agent.RunAsync("Conduct a comprehensive analysis of quantum computing applications in cryptography, including recent breakthroughs, implementation challenges, and future roadmap. Please include diagrams and visual representations to illustrate complex concepts.", thread);
|
||||
|
||||
// Poll until the response is complete.
|
||||
while (response.ContinuationToken is { } token)
|
||||
{
|
||||
// Wait before polling again.
|
||||
await Task.Delay(TimeSpan.FromSeconds(2));
|
||||
|
||||
// Continue with the token.
|
||||
response = await agent.RunAsync(thread, options: new AgentRunOptions { ContinuationToken = token });
|
||||
}
|
||||
|
||||
// Display the result
|
||||
Console.WriteLine(response);
|
||||
@@ -0,0 +1,25 @@
|
||||
# Polling for A2A Agent Task Completion
|
||||
|
||||
This sample demonstrates how to poll for long-running task completion using continuation tokens with an A2A AI agent, following the background responses pattern.
|
||||
|
||||
The sample:
|
||||
|
||||
- Connects to an A2A agent server specified in the `A2A_AGENT_HOST` environment variable
|
||||
- Sends a request to the agent that may take time to complete
|
||||
- Polls the agent at regular intervals using continuation tokens until a final response is received
|
||||
- Displays the final result
|
||||
|
||||
This pattern is useful when an AI model cannot complete a complex task in a single response and needs multiple rounds of processing.
|
||||
|
||||
# Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 10.0 SDK or later
|
||||
- An A2A agent server running and accessible via HTTP
|
||||
|
||||
Set the following environment variable:
|
||||
|
||||
```powershell
|
||||
$env:A2A_AGENT_HOST="http://localhost:5000" # Replace with your A2A agent server host
|
||||
```
|
||||
@@ -14,6 +14,7 @@ See the README.md for each sample for the prerequisites for that sample.
|
||||
|Sample|Description|
|
||||
|---|---|
|
||||
|[A2A Agent As Function Tools](./A2AAgent_AsFunctionTools/)|This sample demonstrates how to represent an A2A agent as a set of function tools, where each function tool corresponds to a skill of the A2A agent, and register these function tools with another AI agent so it can leverage the A2A agent's skills.|
|
||||
|[A2A Agent Polling For Task Completion](./A2AAgent_PollingForTaskCompletion/)|This sample demonstrates how to poll for long-running task completion using continuation tokens with an A2A agent.|
|
||||
|
||||
## Running the samples from the console
|
||||
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<NoWarn>$(NoWarn);IDE0059</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<PackageReference Include="Anthropic.Foundry" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Anthropic\Microsoft.Agents.AI.Anthropic.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to create and use an AI agent with Anthropic as the backend.
|
||||
|
||||
using System.Net.Http.Headers;
|
||||
using Anthropic;
|
||||
using Anthropic.Foundry;
|
||||
using Azure.Core;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Sample;
|
||||
|
||||
var deploymentName = Environment.GetEnvironmentVariable("ANTHROPIC_DEPLOYMENT_NAME") ?? "claude-haiku-4-5";
|
||||
|
||||
// The resource is the subdomain name / first name coming before '.services.ai.azure.com' in the endpoint Uri
|
||||
// ie: https://(resource name).services.ai.azure.com/anthropic/v1/chat/completions
|
||||
string? resource = Environment.GetEnvironmentVariable("ANTHROPIC_RESOURCE");
|
||||
string? apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY");
|
||||
|
||||
const string JokerInstructions = "You are good at telling jokes.";
|
||||
const string JokerName = "JokerAgent";
|
||||
|
||||
AnthropicClient? client = (resource is null)
|
||||
? new AnthropicClient() { APIKey = apiKey ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is required when no ANTHROPIC_RESOURCE is provided") } // If no resource is provided, use Anthropic public API
|
||||
: (apiKey is not null)
|
||||
? new AnthropicFoundryClient(new AnthropicFoundryApiKeyCredentials(apiKey, resource)) // If an apiKey is provided, use Foundry with ApiKey authentication
|
||||
: new AnthropicFoundryClient(new AnthropicAzureTokenCredential(new AzureCliCredential(), resource)); // Otherwise, use Foundry with Azure Client authentication
|
||||
|
||||
AIAgent agent = client.CreateAIAgent(model: deploymentName, instructions: JokerInstructions, name: JokerName);
|
||||
|
||||
// Invoke the agent and output the text result.
|
||||
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for invoking the Azure hosted Anthropic models using <see cref="TokenCredential"/> types.
|
||||
/// </summary>
|
||||
public sealed class AnthropicAzureTokenCredential : IAnthropicFoundryCredentials
|
||||
{
|
||||
private readonly TokenCredential _tokenCredential;
|
||||
private readonly Lock _lock = new();
|
||||
private AccessToken? _cachedAccessToken;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ResourceName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="AnthropicAzureTokenCredential"/>.
|
||||
/// </summary>
|
||||
/// <param name="tokenCredential">The credential provider. Use any specialization of <see cref="TokenCredential"/> to get your access token in supported environments.</param>
|
||||
/// <param name="resourceName">The service resource subdomain name to use in the anthropic azure endpoint</param>
|
||||
internal AnthropicAzureTokenCredential(TokenCredential tokenCredential, string resourceName)
|
||||
{
|
||||
this.ResourceName = resourceName ?? throw new ArgumentNullException(nameof(resourceName));
|
||||
this._tokenCredential = tokenCredential ?? throw new ArgumentNullException(nameof(tokenCredential));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Apply(HttpRequestMessage requestMessage)
|
||||
{
|
||||
lock (this._lock)
|
||||
{
|
||||
// Add a 5-minute buffer to avoid using tokens that are about to expire
|
||||
if (this._cachedAccessToken is null || this._cachedAccessToken.Value.ExpiresOn <= DateTimeOffset.Now.AddMinutes(5))
|
||||
{
|
||||
this._cachedAccessToken = this._tokenCredential.GetToken(new TokenRequestContext(scopes: ["https://ai.azure.com/.default"]), CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", this._cachedAccessToken.Value.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
# Creating an AIAgent with Anthropic
|
||||
|
||||
This sample demonstrates how to create an AIAgent using Anthropic Claude models as the underlying inference service.
|
||||
|
||||
The sample supports three deployment scenarios:
|
||||
|
||||
1. **Anthropic Public API** - Direct connection to Anthropic's public API
|
||||
2. **Azure Foundry with API Key** - Anthropic models deployed through Azure Foundry using API key authentication
|
||||
3. **Azure Foundry with Azure CLI** - Anthropic models deployed through Azure Foundry using Azure CLI credentials
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 8.0 SDK or later
|
||||
|
||||
### For Anthropic Public API
|
||||
|
||||
- Anthropic API key
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key
|
||||
$env:ANTHROPIC_DEPLOYMENT_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5
|
||||
```
|
||||
|
||||
### For Azure Foundry with API Key
|
||||
|
||||
- Azure Foundry service endpoint and deployment configured
|
||||
- Anthropic API key
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:ANTHROPIC_RESOURCE="your-foundry-resource-name" # Replace with your Azure Foundry resource name (subdomain before .services.ai.azure.com)
|
||||
$env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key
|
||||
$env:ANTHROPIC_DEPLOYMENT_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5
|
||||
```
|
||||
|
||||
### For Azure Foundry with Azure CLI
|
||||
|
||||
- Azure Foundry service endpoint and deployment configured
|
||||
- Azure CLI installed and authenticated (for Azure credential authentication)
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:ANTHROPIC_RESOURCE="your-foundry-resource-name" # Replace with your Azure Foundry resource name (subdomain before .services.ai.azure.com)
|
||||
$env:ANTHROPIC_DEPLOYMENT_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5
|
||||
```
|
||||
|
||||
**Note**: When using Azure Foundry with Azure CLI, make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
# Classic Foundry Agents
|
||||
|
||||
This sample demonstrates how to create an agent using the classic Foundry Agents experience.
|
||||
|
||||
# Classic vs New Foundry Agents
|
||||
|
||||
Below is a comparison between the classic and new Foundry Agents approaches:
|
||||
|
||||
[Migration Guide](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/migrate?view=foundry)
|
||||
|
||||
# Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
@@ -10,35 +10,34 @@ using Microsoft.Agents.AI;
|
||||
var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
|
||||
var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
|
||||
|
||||
const string JokerInstructions = "You are good at telling jokes.";
|
||||
const string JokerName = "JokerAgent";
|
||||
|
||||
// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
|
||||
var aiProjectClient = new AIProjectClient(new Uri(endpoint), new AzureCliCredential());
|
||||
|
||||
// Define the agent you want to create. (Prompt Agent in this case)
|
||||
var agentVersionCreationOptions = new AgentVersionCreationOptions(new PromptAgentDefinition(model: deploymentName) { Instructions = JokerInstructions });
|
||||
var agentVersionCreationOptions = new AgentVersionCreationOptions(new PromptAgentDefinition(model: deploymentName) { Instructions = "You are good at telling jokes." });
|
||||
// Azure.AI.Agents SDK creates and manages agent by name and versions.
|
||||
// You can create a server side agent version with the Azure.AI.Agents SDK client below.
|
||||
var agentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: JokerName, options: agentVersionCreationOptions);
|
||||
var createdAgentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: JokerName, options: agentVersionCreationOptions);
|
||||
|
||||
// Note:
|
||||
// agentVersion.Id = "<agentName>:<versionNumber>",
|
||||
// agentVersion.Version = <versionNumber>,
|
||||
// agentVersion.Name = <agentName>
|
||||
|
||||
// You can retrieve an AIAgent for a already created server side agent version.
|
||||
AIAgent jokerAgentV1 = aiProjectClient.GetAIAgent(agentVersion);
|
||||
// You can retrieve an AIAgent for an already created server side agent version.
|
||||
AIAgent existingJokerAgent = aiProjectClient.GetAIAgent(createdAgentVersion);
|
||||
|
||||
// You can also create another AIAgent version (V2) by providing the same name with a different definition.
|
||||
AIAgent jokerAgentV2 = aiProjectClient.CreateAIAgent(name: JokerName, model: deploymentName, instructions: JokerInstructions + "V2");
|
||||
// You can also create another AIAgent version by providing the same name with a different definition.
|
||||
AIAgent newJokerAgent = aiProjectClient.CreateAIAgent(name: JokerName, model: deploymentName, instructions: "You are extremely hilarious at telling jokes.");
|
||||
|
||||
// You can also get the AIAgent latest version just providing its name.
|
||||
AIAgent jokerAgentLatest = aiProjectClient.GetAIAgent(name: JokerName);
|
||||
var latestVersion = jokerAgentLatest.GetService<AgentVersion>()!;
|
||||
var latestAgentVersion = jokerAgentLatest.GetService<AgentVersion>()!;
|
||||
|
||||
// The AIAgent version can be accessed via the GetService method.
|
||||
Console.WriteLine($"Latest agent version id: {latestVersion.Id}");
|
||||
Console.WriteLine($"Latest agent version id: {latestAgentVersion.Id}");
|
||||
|
||||
// Once you have the AIAgent, you can invoke it like any other AIAgent.
|
||||
AgentThread thread = jokerAgentLatest.GetNewThread();
|
||||
@@ -47,5 +46,5 @@ Console.WriteLine(await jokerAgentLatest.RunAsync("Tell me a joke about a pirate
|
||||
// This will use the same thread to continue the conversation.
|
||||
Console.WriteLine(await jokerAgentLatest.RunAsync("Now tell me a joke about a cat and a dog using last joke as the anchor.", thread));
|
||||
|
||||
// Cleanup by agent name removes both agent versions created (jokerAgentV1 + jokerAgentV2).
|
||||
aiProjectClient.Agents.DeleteAgent(jokerAgentV1.Name);
|
||||
// Cleanup by agent name removes both agent versions created.
|
||||
aiProjectClient.Agents.DeleteAgent(existingJokerAgent.Name);
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# New Foundry Agents
|
||||
|
||||
This sample demonstrates how to create an agent using the new Foundry Agents experience.
|
||||
|
||||
# Classic vs New Foundry Agents
|
||||
|
||||
Below is a comparison between the classic and new Foundry Agents approaches:
|
||||
|
||||
[Migration Guide](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/migrate?view=foundry)
|
||||
|
||||
# Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
+12
-2
@@ -39,11 +39,16 @@ namespace SampleApp
|
||||
// Create a thread if the user didn't supply one.
|
||||
thread ??= this.GetNewThread();
|
||||
|
||||
if (thread is not CustomAgentThread typedThread)
|
||||
{
|
||||
throw new ArgumentException($"The provided thread is not of type {nameof(CustomAgentThread)}.", nameof(thread));
|
||||
}
|
||||
|
||||
// Clone the input messages and turn them into response messages with upper case text.
|
||||
List<ChatMessage> responseMessages = CloneAndToUpperCase(messages, this.DisplayName).ToList();
|
||||
|
||||
// Notify the thread of the input and output messages.
|
||||
await NotifyThreadOfNewMessagesAsync(thread, messages.Concat(responseMessages), cancellationToken);
|
||||
await typedThread.MessageStore.AddMessagesAsync(messages.Concat(responseMessages), cancellationToken);
|
||||
|
||||
return new AgentRunResponse
|
||||
{
|
||||
@@ -58,11 +63,16 @@ namespace SampleApp
|
||||
// Create a thread if the user didn't supply one.
|
||||
thread ??= this.GetNewThread();
|
||||
|
||||
if (thread is not CustomAgentThread typedThread)
|
||||
{
|
||||
throw new ArgumentException($"The provided thread is not of type {nameof(CustomAgentThread)}.", nameof(thread));
|
||||
}
|
||||
|
||||
// Clone the input messages and turn them into response messages with upper case text.
|
||||
List<ChatMessage> responseMessages = CloneAndToUpperCase(messages, this.DisplayName).ToList();
|
||||
|
||||
// Notify the thread of the input and output messages.
|
||||
await NotifyThreadOfNewMessagesAsync(thread, messages.Concat(responseMessages), cancellationToken);
|
||||
await typedThread.MessageStore.AddMessagesAsync(messages.Concat(responseMessages), cancellationToken);
|
||||
|
||||
foreach (var message in responseMessages)
|
||||
{
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<NoWarn>$(NoWarn);IDE0059;NU1510</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.GenAI" />
|
||||
<PackageReference Include="Mscc.GenerativeAI.Microsoft" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0' or '$(TargetFramework)' == 'net9.0'">
|
||||
<PackageReference Include="System.Net.Security" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+558
@@ -0,0 +1,558 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using Google.Apis.Util;
|
||||
using Google.GenAI;
|
||||
using Google.GenAI.Types;
|
||||
|
||||
namespace Microsoft.Extensions.AI;
|
||||
|
||||
/// <summary>Provides an <see cref="IChatClient"/> implementation based on <see cref="Client"/>.</summary>
|
||||
internal sealed class GoogleGenAIChatClient : IChatClient
|
||||
{
|
||||
/// <summary>The wrapped <see cref="Client"/> instance (optional).</summary>
|
||||
private readonly Client? _client;
|
||||
|
||||
/// <summary>The wrapped <see cref="Models"/> instance.</summary>
|
||||
private readonly Models _models;
|
||||
|
||||
/// <summary>The default model that should be used when no override is specified.</summary>
|
||||
private readonly string? _defaultModelId;
|
||||
|
||||
/// <summary>Lazily-initialized metadata describing the implementation.</summary>
|
||||
private ChatClientMetadata? _metadata;
|
||||
|
||||
/// <summary>Initializes a new <see cref="GoogleGenAIChatClient"/> instance.</summary>
|
||||
public GoogleGenAIChatClient(Client client, string? defaultModelId)
|
||||
{
|
||||
this._client = client;
|
||||
this._models = client.Models;
|
||||
this._defaultModelId = defaultModelId;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new <see cref="GoogleGenAIChatClient"/> instance.</summary>
|
||||
public GoogleGenAIChatClient(Models client, string? defaultModelId)
|
||||
{
|
||||
this._models = client;
|
||||
this._defaultModelId = defaultModelId;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Utilities.ThrowIfNull(messages, nameof(messages));
|
||||
|
||||
// Create the request.
|
||||
(string? modelId, List<Content> contents, GenerateContentConfig config) = this.CreateRequest(messages, options);
|
||||
|
||||
// Send it.
|
||||
GenerateContentResponse generateResult = await this._models.GenerateContentAsync(modelId!, contents, config).ConfigureAwait(false);
|
||||
|
||||
// Create the response.
|
||||
ChatResponse chatResponse = new(new ChatMessage(ChatRole.Assistant, new List<AIContent>()))
|
||||
{
|
||||
CreatedAt = generateResult.CreateTime is { } dt ? new DateTimeOffset(dt) : null,
|
||||
ModelId = !string.IsNullOrWhiteSpace(generateResult.ModelVersion) ? generateResult.ModelVersion : modelId,
|
||||
RawRepresentation = generateResult,
|
||||
ResponseId = generateResult.ResponseId,
|
||||
};
|
||||
|
||||
// Populate the response messages.
|
||||
chatResponse.FinishReason = PopulateResponseContents(generateResult, chatResponse.Messages[0].Contents);
|
||||
|
||||
// Populate usage information if there is any.
|
||||
if (generateResult.UsageMetadata is { } usageMetadata)
|
||||
{
|
||||
chatResponse.Usage = ExtractUsageDetails(usageMetadata);
|
||||
}
|
||||
|
||||
// Return the response.
|
||||
return chatResponse;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
Utilities.ThrowIfNull(messages, nameof(messages));
|
||||
|
||||
// Create the request.
|
||||
(string? modelId, List<Content> contents, GenerateContentConfig config) = this.CreateRequest(messages, options);
|
||||
|
||||
// Send it, and process the results.
|
||||
await foreach (GenerateContentResponse generateResult in this._models.GenerateContentStreamAsync(modelId!, contents, config).WithCancellation(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
// Create a response update for each result in the stream.
|
||||
ChatResponseUpdate responseUpdate = new(ChatRole.Assistant, new List<AIContent>())
|
||||
{
|
||||
CreatedAt = generateResult.CreateTime is { } dt ? new DateTimeOffset(dt) : null,
|
||||
ModelId = !string.IsNullOrWhiteSpace(generateResult.ModelVersion) ? generateResult.ModelVersion : modelId,
|
||||
RawRepresentation = generateResult,
|
||||
ResponseId = generateResult.ResponseId,
|
||||
};
|
||||
|
||||
// Populate the response update contents.
|
||||
responseUpdate.FinishReason = PopulateResponseContents(generateResult, responseUpdate.Contents);
|
||||
|
||||
// Populate usage information if there is any.
|
||||
if (generateResult.UsageMetadata is { } usageMetadata)
|
||||
{
|
||||
responseUpdate.Contents.Add(new UsageContent(ExtractUsageDetails(usageMetadata)));
|
||||
}
|
||||
|
||||
// Yield the update.
|
||||
yield return responseUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? GetService(System.Type serviceType, object? serviceKey = null)
|
||||
{
|
||||
Utilities.ThrowIfNull(serviceType, nameof(serviceType));
|
||||
|
||||
if (serviceKey is null)
|
||||
{
|
||||
// If there's a request for metadata, lazily-initialize it and return it. We don't need to worry about race conditions,
|
||||
// as there's no requirement that the same instance be returned each time, and creation is idempotent.
|
||||
if (serviceType == typeof(ChatClientMetadata))
|
||||
{
|
||||
return this._metadata ??= new("gcp.gen_ai", new("https://generativelanguage.googleapis.com/"), defaultModelId: this._defaultModelId);
|
||||
}
|
||||
|
||||
// Allow a consumer to "break glass" and access the underlying client if they need it.
|
||||
if (serviceType.IsInstanceOfType(this._models))
|
||||
{
|
||||
return this._models;
|
||||
}
|
||||
|
||||
if (this._client is not null && serviceType.IsInstanceOfType(this._client))
|
||||
{
|
||||
return this._client;
|
||||
}
|
||||
|
||||
if (serviceType.IsInstanceOfType(this))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IDisposable.Dispose() { /* nop */ }
|
||||
|
||||
/// <summary>Creates the message parameters for <see cref="Models.GenerateContentAsync(string, List{Content}, GenerateContentConfig?)"/> from <paramref name="messages"/> and <paramref name="options"/>.</summary>
|
||||
private (string? ModelId, List<Content> Contents, GenerateContentConfig Config) CreateRequest(IEnumerable<ChatMessage> messages, ChatOptions? options)
|
||||
{
|
||||
// Create the GenerateContentConfig object. If the options contains a RawRepresentationFactory, try to use it to
|
||||
// create the request instance, allowing the caller to populate it with GenAI-specific options. Otherwise, create
|
||||
// a new instance directly.
|
||||
string? model = this._defaultModelId;
|
||||
List<Content> contents = new();
|
||||
GenerateContentConfig config = options?.RawRepresentationFactory?.Invoke(this) as GenerateContentConfig ?? new();
|
||||
|
||||
if (options is not null)
|
||||
{
|
||||
if (options.FrequencyPenalty is { } frequencyPenalty)
|
||||
{
|
||||
config.FrequencyPenalty ??= frequencyPenalty;
|
||||
}
|
||||
|
||||
if (options.Instructions is { } instructions)
|
||||
{
|
||||
((config.SystemInstruction ??= new()).Parts ??= new()).Add(new() { Text = instructions });
|
||||
}
|
||||
|
||||
if (options.MaxOutputTokens is { } maxOutputTokens)
|
||||
{
|
||||
config.MaxOutputTokens ??= maxOutputTokens;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.ModelId))
|
||||
{
|
||||
model = options.ModelId;
|
||||
}
|
||||
|
||||
if (options.PresencePenalty is { } presencePenalty)
|
||||
{
|
||||
config.PresencePenalty ??= presencePenalty;
|
||||
}
|
||||
|
||||
if (options.Seed is { } seed)
|
||||
{
|
||||
config.Seed ??= (int)seed;
|
||||
}
|
||||
|
||||
if (options.StopSequences is { } stopSequences)
|
||||
{
|
||||
(config.StopSequences ??= new()).AddRange(stopSequences);
|
||||
}
|
||||
|
||||
if (options.Temperature is { } temperature)
|
||||
{
|
||||
config.Temperature ??= temperature;
|
||||
}
|
||||
|
||||
if (options.TopP is { } topP)
|
||||
{
|
||||
config.TopP ??= topP;
|
||||
}
|
||||
|
||||
if (options.TopK is { } topK)
|
||||
{
|
||||
config.TopK ??= topK;
|
||||
}
|
||||
|
||||
// Populate tools. Each kind of tool is added on its own, except for function declarations,
|
||||
// which are grouped into a single FunctionDeclaration.
|
||||
List<FunctionDeclaration>? functionDeclarations = null;
|
||||
if (options.Tools is { } tools)
|
||||
{
|
||||
foreach (var tool in tools)
|
||||
{
|
||||
switch (tool)
|
||||
{
|
||||
case AIFunctionDeclaration af:
|
||||
functionDeclarations ??= new();
|
||||
functionDeclarations.Add(new()
|
||||
{
|
||||
Name = af.Name,
|
||||
Description = af.Description ?? "",
|
||||
ParametersJsonSchema = af.JsonSchema,
|
||||
});
|
||||
break;
|
||||
|
||||
case HostedCodeInterpreterTool:
|
||||
(config.Tools ??= new()).Add(new() { CodeExecution = new() });
|
||||
break;
|
||||
|
||||
case HostedFileSearchTool:
|
||||
(config.Tools ??= new()).Add(new() { Retrieval = new() });
|
||||
break;
|
||||
|
||||
case HostedWebSearchTool:
|
||||
(config.Tools ??= new()).Add(new() { GoogleSearch = new() });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (functionDeclarations is { Count: > 0 })
|
||||
{
|
||||
Tool functionTools = new();
|
||||
(functionTools.FunctionDeclarations ??= new()).AddRange(functionDeclarations);
|
||||
(config.Tools ??= new()).Add(functionTools);
|
||||
}
|
||||
|
||||
// Transfer over the tool mode if there are any tools.
|
||||
if (options.ToolMode is { } toolMode && config.Tools?.Count > 0)
|
||||
{
|
||||
switch (toolMode)
|
||||
{
|
||||
case NoneChatToolMode:
|
||||
config.ToolConfig = new() { FunctionCallingConfig = new() { Mode = FunctionCallingConfigMode.NONE } };
|
||||
break;
|
||||
|
||||
case AutoChatToolMode:
|
||||
config.ToolConfig = new() { FunctionCallingConfig = new() { Mode = FunctionCallingConfigMode.AUTO } };
|
||||
break;
|
||||
|
||||
case RequiredChatToolMode required:
|
||||
config.ToolConfig = new() { FunctionCallingConfig = new() { Mode = FunctionCallingConfigMode.ANY } };
|
||||
if (required.RequiredFunctionName is not null)
|
||||
{
|
||||
((config.ToolConfig.FunctionCallingConfig ??= new()).AllowedFunctionNames ??= new()).Add(required.RequiredFunctionName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the response format if specified.
|
||||
if (options.ResponseFormat is ChatResponseFormatJson responseFormat)
|
||||
{
|
||||
config.ResponseMimeType = "application/json";
|
||||
if (responseFormat.Schema is { } schema)
|
||||
{
|
||||
config.ResponseJsonSchema = schema;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer messages to request, handling system messages specially
|
||||
Dictionary<string, string>? callIdToFunctionNames = null;
|
||||
foreach (var message in messages)
|
||||
{
|
||||
if (message.Role == ChatRole.System)
|
||||
{
|
||||
string instruction = message.Text;
|
||||
if (!string.IsNullOrWhiteSpace(instruction))
|
||||
{
|
||||
((config.SystemInstruction ??= new()).Parts ??= new()).Add(new() { Text = instruction });
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Content content = new() { Role = message.Role == ChatRole.Assistant ? "model" : "user" };
|
||||
content.Parts ??= new();
|
||||
AddPartsForAIContents(ref callIdToFunctionNames, message.Contents, content.Parts);
|
||||
|
||||
contents.Add(content);
|
||||
}
|
||||
|
||||
// Make sure the request contains at least one content part (the request would always fail if empty).
|
||||
if (!contents.SelectMany(c => c.Parts ?? Enumerable.Empty<Part>()).Any())
|
||||
{
|
||||
contents.Add(new() { Role = "user", Parts = new() { { new() { Text = "" } } } });
|
||||
}
|
||||
|
||||
return (model, contents, config);
|
||||
}
|
||||
|
||||
/// <summary>Creates <see cref="Part"/>s for <paramref name="contents"/> and adds them to <paramref name="parts"/>.</summary>
|
||||
private static void AddPartsForAIContents(ref Dictionary<string, string>? callIdToFunctionNames, IList<AIContent> contents, List<Part> parts)
|
||||
{
|
||||
for (int i = 0; i < contents.Count; i++)
|
||||
{
|
||||
var content = contents[i];
|
||||
|
||||
byte[]? thoughtSignature = null;
|
||||
if (content is not TextReasoningContent { ProtectedData: not null } &&
|
||||
i + 1 < contents.Count &&
|
||||
contents[i + 1] is TextReasoningContent nextReasoning &&
|
||||
string.IsNullOrWhiteSpace(nextReasoning.Text) &&
|
||||
nextReasoning.ProtectedData is { } protectedData)
|
||||
{
|
||||
i++;
|
||||
thoughtSignature = Convert.FromBase64String(protectedData);
|
||||
}
|
||||
|
||||
Part? part = null;
|
||||
switch (content)
|
||||
{
|
||||
case TextContent textContent:
|
||||
part = new() { Text = textContent.Text };
|
||||
break;
|
||||
|
||||
case TextReasoningContent reasoningContent:
|
||||
part = new()
|
||||
{
|
||||
Thought = true,
|
||||
Text = !string.IsNullOrWhiteSpace(reasoningContent.Text) ? reasoningContent.Text : null,
|
||||
ThoughtSignature = reasoningContent.ProtectedData is not null ? Convert.FromBase64String(reasoningContent.ProtectedData) : null,
|
||||
};
|
||||
break;
|
||||
|
||||
case DataContent dataContent:
|
||||
part = new()
|
||||
{
|
||||
InlineData = new()
|
||||
{
|
||||
MimeType = dataContent.MediaType,
|
||||
Data = dataContent.Data.ToArray(),
|
||||
DisplayName = dataContent.Name,
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case UriContent uriContent:
|
||||
part = new()
|
||||
{
|
||||
FileData = new()
|
||||
{
|
||||
FileUri = uriContent.Uri.AbsoluteUri,
|
||||
MimeType = uriContent.MediaType,
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case FunctionCallContent functionCallContent:
|
||||
(callIdToFunctionNames ??= new())[functionCallContent.CallId] = functionCallContent.Name;
|
||||
callIdToFunctionNames[""] = functionCallContent.Name; // track last function name in case calls don't have IDs
|
||||
|
||||
part = new()
|
||||
{
|
||||
FunctionCall = new()
|
||||
{
|
||||
Id = functionCallContent.CallId,
|
||||
Name = functionCallContent.Name,
|
||||
Args = functionCallContent.Arguments is null ? null : functionCallContent.Arguments as Dictionary<string, object> ?? new(functionCallContent.Arguments!),
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case FunctionResultContent functionResultContent:
|
||||
part = new()
|
||||
{
|
||||
FunctionResponse = new()
|
||||
{
|
||||
Id = functionResultContent.CallId,
|
||||
Name = callIdToFunctionNames?.TryGetValue(functionResultContent.CallId, out string? functionName) is true || callIdToFunctionNames?.TryGetValue("", out functionName) is true ?
|
||||
functionName :
|
||||
null,
|
||||
Response = functionResultContent.Result is null ? null : new() { ["result"] = functionResultContent.Result },
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
if (part is not null)
|
||||
{
|
||||
part.ThoughtSignature ??= thoughtSignature;
|
||||
parts.Add(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Creates <see cref="AIContent"/>s for <paramref name="parts"/> and adds them to <paramref name="contents"/>.</summary>
|
||||
private static void AddAIContentsForParts(List<Part> parts, IList<AIContent> contents)
|
||||
{
|
||||
foreach (var part in parts)
|
||||
{
|
||||
AIContent? content = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(part.Text))
|
||||
{
|
||||
content = part.Thought is true ?
|
||||
new TextReasoningContent(part.Text) :
|
||||
new TextContent(part.Text);
|
||||
}
|
||||
else if (part.InlineData is { } inlineData)
|
||||
{
|
||||
content = new DataContent(inlineData.Data, inlineData.MimeType ?? "application/octet-stream")
|
||||
{
|
||||
Name = inlineData.DisplayName,
|
||||
};
|
||||
}
|
||||
else if (part.FileData is { FileUri: not null } fileData)
|
||||
{
|
||||
content = new UriContent(new Uri(fileData.FileUri), fileData.MimeType ?? "application/octet-stream");
|
||||
}
|
||||
else if (part.FunctionCall is { Name: not null } functionCall)
|
||||
{
|
||||
content = new FunctionCallContent(functionCall.Id ?? "", functionCall.Name, functionCall.Args!);
|
||||
}
|
||||
else if (part.FunctionResponse is { } functionResponse)
|
||||
{
|
||||
content = new FunctionResultContent(
|
||||
functionResponse.Id ?? "",
|
||||
functionResponse.Response?.TryGetValue("output", out var output) is true ? output :
|
||||
functionResponse.Response?.TryGetValue("error", out var error) is true ? error :
|
||||
null);
|
||||
}
|
||||
|
||||
if (content is not null)
|
||||
{
|
||||
content.RawRepresentation = part;
|
||||
contents.Add(content);
|
||||
|
||||
if (part.ThoughtSignature is { } thoughtSignature)
|
||||
{
|
||||
contents.Add(new TextReasoningContent(null)
|
||||
{
|
||||
ProtectedData = Convert.ToBase64String(thoughtSignature),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ChatFinishReason? PopulateResponseContents(GenerateContentResponse generateResult, IList<AIContent> responseContents)
|
||||
{
|
||||
ChatFinishReason? finishReason = null;
|
||||
|
||||
// Populate the response messages. There should only be at most one candidate, but if there are more, ignore all but the first.
|
||||
if (generateResult.Candidates is { Count: > 0 } &&
|
||||
generateResult.Candidates[0] is { Content: { } candidateContent } candidate)
|
||||
{
|
||||
// Grab the finish reason if one exists.
|
||||
finishReason = ConvertFinishReason(candidate.FinishReason);
|
||||
|
||||
// Add all of the response content parts as AIContents.
|
||||
if (candidateContent.Parts is { } parts)
|
||||
{
|
||||
AddAIContentsForParts(parts, responseContents);
|
||||
}
|
||||
|
||||
// Add any citation metadata.
|
||||
if (candidate.CitationMetadata is { Citations: { Count: > 0 } citations } &&
|
||||
responseContents.OfType<TextContent>().FirstOrDefault() is TextContent textContent)
|
||||
{
|
||||
foreach (var citation in citations)
|
||||
{
|
||||
textContent.Annotations = new List<AIAnnotation>()
|
||||
{
|
||||
new CitationAnnotation()
|
||||
{
|
||||
Title = citation.Title,
|
||||
Url = Uri.TryCreate(citation.Uri, UriKind.Absolute, out Uri? uri) ? uri : null,
|
||||
AnnotatedRegions = new List<AnnotatedRegion>()
|
||||
{
|
||||
new TextSpanAnnotatedRegion()
|
||||
{
|
||||
StartIndex = citation.StartIndex,
|
||||
EndIndex = citation.EndIndex,
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate error information if there is any.
|
||||
if (generateResult.PromptFeedback is { } promptFeedback)
|
||||
{
|
||||
responseContents.Add(new ErrorContent(promptFeedback.BlockReasonMessage));
|
||||
}
|
||||
|
||||
return finishReason;
|
||||
}
|
||||
|
||||
/// <summary>Creates an M.E.AI <see cref="ChatFinishReason"/> from a Google <see cref="FinishReason"/>.</summary>
|
||||
private static ChatFinishReason? ConvertFinishReason(FinishReason? finishReason)
|
||||
{
|
||||
return finishReason switch
|
||||
{
|
||||
null => null,
|
||||
|
||||
FinishReason.MAX_TOKENS =>
|
||||
ChatFinishReason.Length,
|
||||
|
||||
FinishReason.MALFORMED_FUNCTION_CALL or
|
||||
FinishReason.UNEXPECTED_TOOL_CALL =>
|
||||
ChatFinishReason.ToolCalls,
|
||||
|
||||
FinishReason.FINISH_REASON_UNSPECIFIED or
|
||||
FinishReason.STOP =>
|
||||
ChatFinishReason.Stop,
|
||||
|
||||
_ => ChatFinishReason.ContentFilter,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="UsageDetails"/> populated from the supplied <paramref name="usageMetadata"/>.</summary>
|
||||
private static UsageDetails ExtractUsageDetails(GenerateContentResponseUsageMetadata usageMetadata)
|
||||
{
|
||||
UsageDetails details = new()
|
||||
{
|
||||
InputTokenCount = usageMetadata.PromptTokenCount,
|
||||
OutputTokenCount = usageMetadata.CandidatesTokenCount,
|
||||
TotalTokenCount = usageMetadata.TotalTokenCount,
|
||||
};
|
||||
|
||||
AddIfPresent(nameof(usageMetadata.CachedContentTokenCount), usageMetadata.CachedContentTokenCount);
|
||||
AddIfPresent(nameof(usageMetadata.ThoughtsTokenCount), usageMetadata.ThoughtsTokenCount);
|
||||
AddIfPresent(nameof(usageMetadata.ToolUsePromptTokenCount), usageMetadata.ToolUsePromptTokenCount);
|
||||
|
||||
return details;
|
||||
|
||||
void AddIfPresent(string key, int? value)
|
||||
{
|
||||
if (value is int i)
|
||||
{
|
||||
(details.AdditionalCounts ??= new())[key] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Google.Apis.Util;
|
||||
using Google.GenAI;
|
||||
|
||||
namespace Microsoft.Extensions.AI;
|
||||
|
||||
/// <summary>Provides implementations of Microsoft.Extensions.AI abstractions based on <see cref="Client"/>.</summary>
|
||||
public static class GoogleGenAIExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IChatClient"/> wrapper around the specified <see cref="Client"/>.
|
||||
/// </summary>
|
||||
/// <param name="client">The <see cref="Client"/> to wrap.</param>
|
||||
/// <param name="defaultModelId">The default model ID to use for chat requests if not specified in <see cref="ChatOptions.ModelId"/>.</param>
|
||||
/// <returns>An <see cref="IChatClient"/> that wraps the specified client.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
|
||||
public static IChatClient AsIChatClient(this Client client, string? defaultModelId = null)
|
||||
{
|
||||
Utilities.ThrowIfNull(client, nameof(client));
|
||||
return new GoogleGenAIChatClient(client, defaultModelId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IChatClient"/> wrapper around the specified <see cref="Models"/>.
|
||||
/// </summary>
|
||||
/// <param name="models">The <see cref="Models"/> client to wrap.</param>
|
||||
/// <param name="defaultModelId">The default model ID to use for chat requests if not specified in <see cref="ChatOptions.ModelId"/>.</param>
|
||||
/// <returns>An <see cref="IChatClient"/> that wraps the specified <see cref="Models"/> client.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="models"/> is <see langword="null"/>.</exception>
|
||||
public static IChatClient AsIChatClient(this Models models, string? defaultModelId = null)
|
||||
{
|
||||
Utilities.ThrowIfNull(models, nameof(models));
|
||||
return new GoogleGenAIChatClient(models, defaultModelId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to create and use an AI agent with Google Gemini
|
||||
|
||||
using Google.GenAI;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Mscc.GenerativeAI.Microsoft;
|
||||
|
||||
const string JokerInstructions = "You are good at telling jokes.";
|
||||
const string JokerName = "JokerAgent";
|
||||
|
||||
string apiKey = Environment.GetEnvironmentVariable("GOOGLE_GENAI_API_KEY") ?? throw new InvalidOperationException("Please set the GOOGLE_GENAI_API_KEY environment variable.");
|
||||
string model = Environment.GetEnvironmentVariable("GOOGLE_GENAI_MODEL") ?? "gemini-2.5-fast";
|
||||
|
||||
// Using a Google GenAI IChatClient implementation
|
||||
// Until the PR https://github.com/googleapis/dotnet-genai/pull/81 is not merged this option
|
||||
// requires usage of also both GeminiChatClient.cs and GoogleGenAIExtensions.cs polyfills to work.
|
||||
|
||||
ChatClientAgent agentGenAI = new(
|
||||
new Client(vertexAI: false, apiKey: apiKey).AsIChatClient(model),
|
||||
name: JokerName,
|
||||
instructions: JokerInstructions);
|
||||
|
||||
AgentRunResponse response = await agentGenAI.RunAsync("Tell me a joke about a pirate.");
|
||||
Console.WriteLine($"Google GenAI client based agent response:\n{response}");
|
||||
|
||||
// Using a community driven Mscc.GenerativeAI.Microsoft package
|
||||
|
||||
ChatClientAgent agentCommunity = new(
|
||||
new GeminiChatClient(apiKey, model),
|
||||
name: JokerName,
|
||||
instructions: JokerInstructions);
|
||||
|
||||
response = await agentCommunity.RunAsync("Tell me a joke about a pirate.");
|
||||
Console.WriteLine($"Community client based agent response:\n{response}");
|
||||
@@ -0,0 +1,37 @@
|
||||
# Creating an AIAgent with Google Gemini
|
||||
|
||||
This sample demonstrates how to create an AIAgent using Google Gemini models as the underlying inference service.
|
||||
|
||||
The sample showcases two different `IChatClient` implementations:
|
||||
|
||||
1. **Google GenAI** - Using the official [Google.GenAI](https://www.nuget.org/packages/Google.GenAI) package
|
||||
2. **Mscc.GenerativeAI.Microsoft** - Using the community-driven [Mscc.GenerativeAI.Microsoft](https://www.nuget.org/packages/Mscc.GenerativeAI.Microsoft) package
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 10.0 SDK or later
|
||||
- Google AI Studio API key (get one at [Google AI Studio](https://aistudio.google.com/apikey))
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:GOOGLE_GENAI_API_KEY="your-google-api-key" # Replace with your Google AI Studio API key
|
||||
$env:GOOGLE_GENAI_MODEL="gemini-2.5-fast" # Optional, defaults to gemini-2.5-fast
|
||||
```
|
||||
|
||||
## Package Options
|
||||
|
||||
### Google GenAI (Official)
|
||||
|
||||
The official Google GenAI package provides direct access to Google's Generative AI models. This sample uses an extension method to convert the Google client to an `IChatClient`.
|
||||
|
||||
> [!NOTE]
|
||||
> Until PR [googleapis/dotnet-genai#81](https://github.com/googleapis/dotnet-genai/pull/81) is merged, this option requires the additional `GeminiChatClient.cs` and `GoogleGenAIExtensions.cs` files included in this sample.
|
||||
>
|
||||
> We appreciate any community push by liking and commenting in the above PR to get it merged and release as part of official Google GenAI package.
|
||||
|
||||
### Mscc.GenerativeAI.Microsoft (Community)
|
||||
|
||||
The community-driven Mscc.GenerativeAI.Microsoft package provides a ready-to-use `IChatClient` implementation for Google Gemini models through the `GeminiChatClient` class.
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
||||
+1
@@ -3,6 +3,7 @@
|
||||
// This sample shows how to create and use a simple AI agent with OpenAI Chat Completion as the backend.
|
||||
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using OpenAI;
|
||||
|
||||
var apiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY") ?? throw new InvalidOperationException("OPENAI_APIKEY is not set.");
|
||||
|
||||
@@ -15,6 +15,7 @@ See the README.md for each sample for the prerequisites for that sample.
|
||||
|Sample|Description|
|
||||
|---|---|
|
||||
|[Creating an AIAgent with A2A](./Agent_With_A2A/)|This sample demonstrates how to create AIAgent for an existing A2A agent.|
|
||||
|[Creating an AIAgent with Anthropic](./Agent_With_Anthropic/)|This sample demonstrates how to create an AIAgent using Anthropic Claude models as the underlying inference service|
|
||||
|[Creating an AIAgent with Foundry Agents using Azure.AI.Agents.Persistent](./Agent_With_AzureAIAgentsPersistent/)|This sample demonstrates how to create a Foundry Persistent agent and expose it as an AIAgent using the Azure.AI.Agents.Persistent SDK|
|
||||
|[Creating an AIAgent with Foundry Agents using Azure.AI.Project](./Agent_With_AzureAIProject/)|This sample demonstrates how to create an Foundry Project agent and expose it as an AIAgent using the Azure.AI.Project SDK|
|
||||
|[Creating an AIAgent with AzureFoundry Model](./Agent_With_AzureFoundryModel/)|This sample demonstrates how to use any model deployed to Azure Foundry to create an AIAgent|
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Anthropic\Microsoft.Agents.AI.Anthropic.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to create and use a simple AI agent with Anthropic as the backend.
|
||||
|
||||
using Anthropic;
|
||||
using Anthropic.Core;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set.");
|
||||
var model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-haiku-4-5";
|
||||
|
||||
AIAgent agent = new AnthropicClient(new ClientOptions { APIKey = apiKey })
|
||||
.CreateAIAgent(model: model, instructions: "You are good at telling jokes.", name: "Joker");
|
||||
|
||||
// Invoke the agent and output the text result.
|
||||
var response = await agent.RunAsync("Tell me a joke about a pirate.");
|
||||
Console.WriteLine(response);
|
||||
|
||||
// Invoke the agent with streaming support.
|
||||
await foreach (var update in agent.RunStreamingAsync("Tell me a joke about a pirate."))
|
||||
{
|
||||
Console.WriteLine(update);
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
# Running a simple agent with Anthropic
|
||||
|
||||
This sample demonstrates how to create and run a basic agent with Anthropic Claude models.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Creating an AI agent with Anthropic Claude
|
||||
- Running a simple agent with instructions
|
||||
- Managing agent lifecycle
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 8.0 SDK or later
|
||||
- Anthropic API key configured
|
||||
|
||||
**Note**: This sample uses Anthropic Claude models. For more information, see [Anthropic documentation](https://docs.anthropic.com/).
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key
|
||||
$env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
Navigate to the AgentWithAnthropic sample directory and run:
|
||||
|
||||
```powershell
|
||||
cd dotnet\samples\GettingStarted\AgentWithAnthropic
|
||||
dotnet run --project .\Agent_Anthropic_Step01_Running
|
||||
```
|
||||
|
||||
## Expected behavior
|
||||
|
||||
The sample will:
|
||||
|
||||
1. Create an agent with Anthropic Claude
|
||||
2. Run the agent with a simple prompt
|
||||
3. Display the agent's response
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Anthropic\Microsoft.Agents.AI.Anthropic.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to create and use an AI agent with reasoning capabilities.
|
||||
|
||||
using Anthropic;
|
||||
using Anthropic.Core;
|
||||
using Anthropic.Models.Messages;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set.");
|
||||
var model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-haiku-4-5";
|
||||
var maxTokens = 4096;
|
||||
var thinkingTokens = 2048;
|
||||
|
||||
var agent = new AnthropicClient(new ClientOptions { APIKey = apiKey })
|
||||
.CreateAIAgent(
|
||||
model: model,
|
||||
clientFactory: (chatClient) => chatClient
|
||||
.AsBuilder()
|
||||
.ConfigureOptions(
|
||||
options => options.RawRepresentationFactory = (_) => new MessageCreateParams()
|
||||
{
|
||||
Model = options.ModelId ?? model,
|
||||
MaxTokens = options.MaxOutputTokens ?? maxTokens,
|
||||
Messages = [],
|
||||
Thinking = new ThinkingConfigParam(new ThinkingConfigEnabled(budgetTokens: thinkingTokens))
|
||||
})
|
||||
.Build());
|
||||
|
||||
Console.WriteLine("1. Non-streaming:");
|
||||
var response = await agent.RunAsync("Solve this problem step by step: If a train travels 60 miles per hour and needs to cover 180 miles, how long will the journey take? Show your reasoning.");
|
||||
|
||||
Console.WriteLine("#### Start Thinking ####");
|
||||
Console.WriteLine($"\e[92m{string.Join("\n", response.Messages.SelectMany(m => m.Contents.OfType<TextReasoningContent>().Select(c => c.Text)))}\e[0m");
|
||||
Console.WriteLine("#### End Thinking ####");
|
||||
|
||||
Console.WriteLine("\n#### Final Answer ####");
|
||||
Console.WriteLine(response.Text);
|
||||
|
||||
Console.WriteLine("Token usage:");
|
||||
Console.WriteLine($"Input: {response.Usage?.InputTokenCount}, Output: {response.Usage?.OutputTokenCount}, {string.Join(", ", response.Usage?.AdditionalCounts ?? [])}");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("2. Streaming");
|
||||
await foreach (var update in agent.RunStreamingAsync("Explain the theory of relativity in simple terms."))
|
||||
{
|
||||
foreach (var item in update.Contents)
|
||||
{
|
||||
if (item is TextReasoningContent reasoningContent)
|
||||
{
|
||||
Console.WriteLine($"\e[92m{reasoningContent.Text}\e[0m");
|
||||
}
|
||||
else if (item is TextContent textContent)
|
||||
{
|
||||
Console.WriteLine(textContent.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
# Using reasoning with Anthropic agents
|
||||
|
||||
This sample demonstrates how to use extended thinking/reasoning capabilities with Anthropic Claude agents.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Creating an AI agent with Anthropic Claude extended thinking
|
||||
- Using reasoning capabilities for complex problem solving
|
||||
- Extracting thinking and response content from agent output
|
||||
- Managing agent lifecycle
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 8.0 SDK or later
|
||||
- Anthropic API key configured
|
||||
- Access to Anthropic Claude models with extended thinking support
|
||||
|
||||
**Note**: This sample uses Anthropic Claude models with extended thinking. For more information, see [Anthropic documentation](https://docs.anthropic.com/).
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key
|
||||
$env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
Navigate to the AgentWithAnthropic sample directory and run:
|
||||
|
||||
```powershell
|
||||
cd dotnet\samples\GettingStarted\AgentWithAnthropic
|
||||
dotnet run --project .\Agent_Anthropic_Step02_Reasoning
|
||||
```
|
||||
|
||||
## Expected behavior
|
||||
|
||||
The sample will:
|
||||
|
||||
1. Create an agent with Anthropic Claude extended thinking enabled
|
||||
2. Run the agent with a complex reasoning prompt
|
||||
3. Display the agent's thinking process
|
||||
4. Display the agent's final response
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Anthropic\Microsoft.Agents.AI.Anthropic.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample demonstrates how to use an agent with function tools.
|
||||
// It shows both non-streaming and streaming agent interactions using weather-related tools.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Anthropic;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set.");
|
||||
var model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-haiku-4-5";
|
||||
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather([Description("The location to get the weather for.")] string location)
|
||||
=> $"The weather in {location} is cloudy with a high of 15°C.";
|
||||
|
||||
const string AssistantInstructions = "You are a helpful assistant that can get weather information.";
|
||||
const string AssistantName = "WeatherAssistant";
|
||||
|
||||
// Define the agent with function tools.
|
||||
AITool tool = AIFunctionFactory.Create(GetWeather);
|
||||
|
||||
// Get anthropic client to create agents.
|
||||
AIAgent agent = new AnthropicClient { APIKey = apiKey }
|
||||
.CreateAIAgent(model: model, instructions: AssistantInstructions, name: AssistantName, tools: [tool]);
|
||||
|
||||
// Non-streaming agent interaction with function tools.
|
||||
AgentThread thread = agent.GetNewThread();
|
||||
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?", thread));
|
||||
|
||||
// Streaming agent interaction with function tools.
|
||||
thread = agent.GetNewThread();
|
||||
await foreach (AgentRunResponseUpdate update in agent.RunStreamingAsync("What is the weather like in Amsterdam?", thread))
|
||||
{
|
||||
Console.WriteLine(update);
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# Using Function Tools with Anthropic agents
|
||||
|
||||
This sample demonstrates how to use function tools with Anthropic Claude agents, allowing agents to call custom functions to retrieve information.
|
||||
|
||||
## What this sample demonstrates
|
||||
|
||||
- Creating function tools using AIFunctionFactory
|
||||
- Passing function tools to an Anthropic Claude agent
|
||||
- Running agents with function tools (text output)
|
||||
- Running agents with function tools (streaming output)
|
||||
- Managing agent lifecycle
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 8.0 SDK or later
|
||||
- Anthropic API key configured
|
||||
|
||||
**Note**: This sample uses Anthropic Claude models. For more information, see [Anthropic documentation](https://docs.anthropic.com/).
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key
|
||||
$env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model
|
||||
```
|
||||
|
||||
## Run the sample
|
||||
|
||||
Navigate to the AgentWithAnthropic sample directory and run:
|
||||
|
||||
```powershell
|
||||
cd dotnet\samples\GettingStarted\AgentWithAnthropic
|
||||
dotnet run --project .\Agent_Anthropic_Step03_UsingFunctionTools
|
||||
```
|
||||
|
||||
## Expected behavior
|
||||
|
||||
The sample will:
|
||||
|
||||
1. Create an agent named "WeatherAssistant" with a GetWeather function tool
|
||||
2. Run the agent with a text prompt asking about weather
|
||||
3. The agent will invoke the GetWeather function tool to retrieve weather information
|
||||
4. Run the agent again with streaming to display the response as it's generated
|
||||
5. Clean up resources by deleting the agent
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# Getting started with agents using Anthropic
|
||||
|
||||
The getting started with agents using Anthropic samples demonstrate the fundamental concepts and functionalities
|
||||
of single agents using Anthropic as the AI provider.
|
||||
|
||||
These samples use Anthropic Claude models as the AI provider and use ChatCompletion as the type of service.
|
||||
|
||||
For other samples that demonstrate how to create and configure each type of agent that come with the agent framework,
|
||||
see the [How to create an agent for each provider](../AgentProviders/README.md) samples.
|
||||
|
||||
## Getting started with agents using Anthropic prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
- .NET 8.0 SDK or later
|
||||
- Anthropic API key configured
|
||||
- User has access to Anthropic Claude models
|
||||
|
||||
**Note**: These samples use Anthropic Claude models. For more information, see [Anthropic documentation](https://docs.anthropic.com/).
|
||||
|
||||
## Using Anthropic with Azure Foundry
|
||||
|
||||
To use Anthropic with Azure Foundry, you can check the sample [AgentProviders/Agent_With_Anthropic](../AgentProviders/Agent_With_Anthropic/README.md) for more details.
|
||||
|
||||
## Samples
|
||||
|
||||
|Sample|Description|
|
||||
|---|---|
|
||||
|[Running a simple agent](./Agent_Anthropic_Step01_Running/)|This sample demonstrates how to create and run a basic agent with Anthropic Claude|
|
||||
|[Using reasoning with an agent](./Agent_Anthropic_Step02_Reasoning/)|This sample demonstrates how to use extended thinking/reasoning capabilities with Anthropic Claude agents|
|
||||
|[Using function tools with an agent](./Agent_Anthropic_Step03_UsingFunctionTools/)|This sample demonstrates how to use function tools with an Anthropic Claude agent|
|
||||
|
||||
## Running the samples from the console
|
||||
|
||||
To run the samples, navigate to the desired sample directory, e.g.
|
||||
|
||||
```powershell
|
||||
cd Agent_Anthropic_Step01_Running
|
||||
```
|
||||
|
||||
Set the following environment variables:
|
||||
|
||||
```powershell
|
||||
$env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key
|
||||
```
|
||||
|
||||
If the variables are not set, you will be prompted for the values when running the samples.
|
||||
|
||||
Execute the following command to build the sample:
|
||||
|
||||
```powershell
|
||||
dotnet build
|
||||
```
|
||||
|
||||
Execute the following command to run the sample:
|
||||
|
||||
```powershell
|
||||
dotnet run --no-build
|
||||
```
|
||||
|
||||
Or just build and run in one step:
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
|
||||
## Running the samples from Visual Studio
|
||||
|
||||
Open the solution in Visual Studio and set the desired sample project as the startup project. Then, run the project using the built-in debugger or by pressing `F5`.
|
||||
|
||||
You will be prompted for any required environment variables if they are not already set.
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ AIAgent agent = new AzureOpenAIClient(
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(new ChatClientAgentOptions
|
||||
{
|
||||
Instructions = "You are good at telling jokes.",
|
||||
ChatOptions = new() { Instructions = "You are good at telling jokes." },
|
||||
Name = "Joker",
|
||||
AIContextProviderFactory = (ctx) => new ChatHistoryMemoryProvider(
|
||||
vectorStore,
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ AIAgent agent = new AzureOpenAIClient(
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(new ChatClientAgentOptions()
|
||||
{
|
||||
Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details.",
|
||||
ChatOptions = new() { Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details." },
|
||||
AIContextProviderFactory = ctx => ctx.SerializedState.ValueKind is not JsonValueKind.Null and not JsonValueKind.Undefined
|
||||
// If each thread should have its own Mem0 scope, you can create a new id per thread here:
|
||||
// ? new Mem0Provider(mem0HttpClient, new Mem0ProviderScope() { ThreadId = Guid.NewGuid().ToString() })
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ ChatClient chatClient = new AzureOpenAIClient(
|
||||
// and its storage to that user id.
|
||||
AIAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions()
|
||||
{
|
||||
Instructions = "You are a friendly assistant. Always address the user by their name.",
|
||||
ChatOptions = new() { Instructions = "You are a friendly assistant. Always address the user by their name." },
|
||||
AIContextProviderFactory = ctx => new UserInfoMemory(chatClient.AsIChatClient(), ctx.SerializedState, ctx.JsonSerializerOptions)
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ AIAgent agent = azureOpenAIClient
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(new ChatClientAgentOptions
|
||||
{
|
||||
Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
|
||||
ChatOptions = new() { Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available." },
|
||||
AIContextProviderFactory = ctx => new TextSearchProvider(SearchAdapter, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions)
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ AIAgent agent = azureOpenAIClient
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(new ChatClientAgentOptions
|
||||
{
|
||||
Instructions = "You are a helpful support specialist for the Microsoft Agent Framework. Answer questions using the provided context and cite the source document when available. Keep responses brief.",
|
||||
ChatOptions = new() { Instructions = "You are a helpful support specialist for the Microsoft Agent Framework. Answer questions using the provided context and cite the source document when available. Keep responses brief." },
|
||||
AIContextProviderFactory = ctx => new TextSearchProvider(SearchAdapter, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions)
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ AIAgent agent = new AzureOpenAIClient(
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(new ChatClientAgentOptions
|
||||
{
|
||||
Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
|
||||
ChatOptions = new() { Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available." },
|
||||
AIContextProviderFactory = ctx => new TextSearchProvider(MockSearchAsync, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions)
|
||||
});
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ ChatClient chatClient = new AzureOpenAIClient(
|
||||
.GetChatClient(deploymentName);
|
||||
|
||||
// Create the ChatClientAgent with the specified name and instructions.
|
||||
ChatClientAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant."));
|
||||
ChatClientAgent agent = chatClient.CreateAIAgent(name: "HelpfulAssistant", instructions: "You are a helpful assistant.");
|
||||
|
||||
// Set PersonInfo as the type parameter of RunAsync method to specify the expected structured output from the agent and invoke the agent with some unstructured input.
|
||||
AgentRunResponse<PersonInfo> response = await agent.RunAsync<PersonInfo>("Please provide information about John Smith, who is a 35-year-old software engineer.");
|
||||
@@ -34,12 +34,10 @@ Console.WriteLine($"Age: {response.Result.Age}");
|
||||
Console.WriteLine($"Occupation: {response.Result.Occupation}");
|
||||
|
||||
// Create the ChatClientAgent with the specified name, instructions, and expected structured output the agent should produce.
|
||||
ChatClientAgent agentWithPersonInfo = chatClient.CreateAIAgent(new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.")
|
||||
ChatClientAgent agentWithPersonInfo = chatClient.CreateAIAgent(new ChatClientAgentOptions()
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<PersonInfo>()
|
||||
}
|
||||
Name = "HelpfulAssistant",
|
||||
ChatOptions = new() { Instructions = "You are a helpful assistant.", ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<PersonInfo>() }
|
||||
});
|
||||
|
||||
// Invoke the agent with some unstructured input while streaming, to extract the structured information from.
|
||||
|
||||
@@ -28,7 +28,7 @@ AIAgent agent = new AzureOpenAIClient(
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(new ChatClientAgentOptions
|
||||
{
|
||||
Instructions = "You are good at telling jokes.",
|
||||
ChatOptions = new() { Instructions = "You are good at telling jokes." },
|
||||
Name = "Joker",
|
||||
ChatMessageStoreFactory = ctx =>
|
||||
{
|
||||
|
||||
@@ -18,8 +18,7 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT
|
||||
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
|
||||
|
||||
// Add agent options to the service collection.
|
||||
builder.Services.AddSingleton(
|
||||
new ChatClientAgentOptions(instructions: "You are good at telling jokes.", name: "Joker"));
|
||||
builder.Services.AddSingleton(new ChatClientAgentOptions() { Name = "Joker", ChatOptions = new() { Instructions = "You are good at telling jokes." } });
|
||||
|
||||
// Add a chat client to the service collection.
|
||||
builder.Services.AddKeyedChatClient("AzureOpenAI", (sp) => new AzureOpenAIClient(
|
||||
|
||||
-4
@@ -16,10 +16,6 @@
|
||||
<PackageReference Include="ModelContextProtocol" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'net10.0'))">
|
||||
<PackageReference Include="System.Net.ServerSentEvents" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI.Persistent\Microsoft.Agents.AI.AzureAI.Persistent.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
+4
-4
@@ -44,7 +44,7 @@ while (response.ContinuationToken is not null)
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(10));
|
||||
|
||||
RestoreAgentState(agent, out thread, out object? continuationToken);
|
||||
RestoreAgentState(agent, out thread, out ResponseContinuationToken? continuationToken);
|
||||
|
||||
options.ContinuationToken = continuationToken;
|
||||
response = await agent.RunAsync(thread, options);
|
||||
@@ -52,19 +52,19 @@ while (response.ContinuationToken is not null)
|
||||
|
||||
Console.WriteLine(response.Text);
|
||||
|
||||
void PersistAgentState(AgentThread thread, object? continuationToken)
|
||||
void PersistAgentState(AgentThread thread, ResponseContinuationToken? continuationToken)
|
||||
{
|
||||
stateStore["thread"] = thread.Serialize();
|
||||
stateStore["continuationToken"] = JsonSerializer.SerializeToElement(continuationToken, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ResponseContinuationToken)));
|
||||
}
|
||||
|
||||
void RestoreAgentState(AIAgent agent, out AgentThread thread, out object? continuationToken)
|
||||
void RestoreAgentState(AIAgent agent, out AgentThread thread, out ResponseContinuationToken? continuationToken)
|
||||
{
|
||||
JsonElement serializedThread = stateStore["thread"] ?? throw new InvalidOperationException("No serialized thread found in state store.");
|
||||
JsonElement? serializedToken = stateStore["continuationToken"];
|
||||
|
||||
thread = agent.DeserializeThread(serializedThread);
|
||||
continuationToken = serializedToken?.Deserialize(AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ResponseContinuationToken)));
|
||||
continuationToken = (ResponseContinuationToken?)serializedToken?.Deserialize(AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ResponseContinuationToken)));
|
||||
}
|
||||
|
||||
[Description("Researches relevant space facts and scientific information for writing a science fiction novel")]
|
||||
|
||||
@@ -21,7 +21,7 @@ AIAgent agent = new AzureOpenAIClient(
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(new ChatClientAgentOptions
|
||||
{
|
||||
Instructions = "You are good at telling jokes.",
|
||||
ChatOptions = new() { Instructions = "You are good at telling jokes." },
|
||||
Name = "Joker",
|
||||
ChatMessageStoreFactory = ctx => new InMemoryChatMessageStore(new MessageCountingChatReducer(2), ctx.SerializedState, ctx.JsonSerializerOptions)
|
||||
});
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.OpenAI" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel.Json" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel.PowerFx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Declarative\Microsoft.Agents.AI.Declarative.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to create an agent from a YAML based declarative representation.
|
||||
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
|
||||
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
|
||||
|
||||
// Create the chat client
|
||||
IChatClient chatClient = new AzureOpenAIClient(
|
||||
new Uri(endpoint),
|
||||
new AzureCliCredential())
|
||||
.GetChatClient(deploymentName)
|
||||
.AsIChatClient();
|
||||
|
||||
// Define the agent using a YAML definition.
|
||||
var text =
|
||||
"""
|
||||
kind: Prompt
|
||||
name: Assistant
|
||||
description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format.
|
||||
model:
|
||||
options:
|
||||
temperature: 0.9
|
||||
topP: 0.95
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
""";
|
||||
|
||||
// Create the agent from the YAML definition.
|
||||
var agentFactory = new ChatClientPromptAgentFactory(chatClient);
|
||||
var agent = await agentFactory.CreateFromYamlAsync(text);
|
||||
|
||||
// Invoke the agent and output the text result.
|
||||
Console.WriteLine(await agent!.RunAsync("Tell me a joke about a pirate in English."));
|
||||
|
||||
// Invoke the agent with streaming support.
|
||||
await foreach (var update in agent!.RunStreamingAsync("Tell me a joke about a pirate in French."))
|
||||
{
|
||||
Console.WriteLine(update);
|
||||
}
|
||||
@@ -45,6 +45,7 @@ Before you begin, ensure you have the following prerequisites:
|
||||
|[Reducing chat history size](./Agent_Step16_ChatReduction/)|This sample demonstrates how to reduce the chat history to constrain its size, where chat history is maintained locally|
|
||||
|[Background responses](./Agent_Step17_BackgroundResponses/)|This sample demonstrates how to use background responses for long-running operations with polling and resumption support|
|
||||
|[Deep research with an agent](./Agent_Step18_DeepResearch/)|This sample demonstrates how to use the Deep Research Tool to perform comprehensive research on complex topics|
|
||||
|[Declarative agent](./Agent_Step19_Declarative/)|This sample demonstrates how to declaratively define an agent.|
|
||||
|
||||
## Running the samples from the console
|
||||
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.OpenAI" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel.Json" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel.PowerFx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Declarative\Microsoft.Agents.AI.Declarative.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to load an AI agent from a YAML file and process a prompt using Azure OpenAI as the backend.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
|
||||
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
|
||||
|
||||
// Create the chat client
|
||||
IChatClient chatClient = new AzureOpenAIClient(
|
||||
new Uri(endpoint),
|
||||
new AzureCliCredential())
|
||||
.GetChatClient(deploymentName)
|
||||
.AsIChatClient();
|
||||
|
||||
// Read command-line arguments
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Usage: DeclarativeAgents <yaml-file-path> <prompt>");
|
||||
Console.WriteLine(" <yaml-file-path>: The path to the YAML file containing the agent definition");
|
||||
Console.WriteLine(" <prompt>: The prompt to send to the agent");
|
||||
return;
|
||||
}
|
||||
|
||||
var yamlFilePath = args[0];
|
||||
var prompt = args[1];
|
||||
|
||||
// Verify the YAML file exists
|
||||
if (!File.Exists(yamlFilePath))
|
||||
{
|
||||
Console.WriteLine($"Error: File not found: {yamlFilePath}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the YAML content from the file
|
||||
var text = await File.ReadAllTextAsync(yamlFilePath);
|
||||
|
||||
// Example function tool that can be used by the agent.
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather(
|
||||
[Description("The city and state, e.g. San Francisco, CA")] string location,
|
||||
[Description("The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.")] string unit)
|
||||
=> $"The weather in {location} is cloudy with a high of {(unit.Equals("celsius", StringComparison.Ordinal) ? "15°C" : "59°F")}.";
|
||||
|
||||
// Create the agent from the YAML definition.
|
||||
var agentFactory = new ChatClientPromptAgentFactory(chatClient, [AIFunctionFactory.Create(GetWeather, "GetWeather")]);
|
||||
var agent = await agentFactory.CreateFromYamlAsync(text);
|
||||
|
||||
// Invoke the agent and output the text result.
|
||||
Console.WriteLine(await agent!.RunAsync(prompt));
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"profiles": {
|
||||
"GetWeather": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\chatclient\\GetWeather.yaml \"What is the weather in Cambridge, MA in °C?\""
|
||||
},
|
||||
"Assistant": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\chatclient\\Assistant.yaml \"Tell me a joke about a pirate in Italian.\""
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-11
@@ -11,19 +11,17 @@ using Microsoft.Extensions.AI;
|
||||
string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
|
||||
string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
|
||||
|
||||
const string JokerInstructionsV1 = "You are good at telling jokes.";
|
||||
const string JokerInstructionsV2 = "You are extremely hilarious at telling jokes.";
|
||||
const string JokerName = "JokerAgent";
|
||||
|
||||
// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents.
|
||||
AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential());
|
||||
|
||||
// Define the agent you want to create. (Prompt Agent in this case)
|
||||
AgentVersionCreationOptions options = new(new PromptAgentDefinition(model: deploymentName) { Instructions = JokerInstructionsV1 });
|
||||
AgentVersionCreationOptions options = new(new PromptAgentDefinition(model: deploymentName) { Instructions = "You are good at telling jokes." });
|
||||
|
||||
// Azure.AI.Agents SDK creates and manages agent by name and versions.
|
||||
// You can create a server side agent version with the Azure.AI.Agents SDK client below.
|
||||
AgentVersion agentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: JokerName, options);
|
||||
AgentVersion createdAgentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: JokerName, options);
|
||||
|
||||
// Note:
|
||||
// agentVersion.Id = "<agentName>:<versionNumber>",
|
||||
@@ -31,20 +29,20 @@ AgentVersion agentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName:
|
||||
// agentVersion.Name = <agentName>
|
||||
|
||||
// You can retrieve an AIAgent for an already created server side agent version.
|
||||
AIAgent jokerAgentV1 = aiProjectClient.GetAIAgent(agentVersion);
|
||||
AIAgent existingJokerAgent = aiProjectClient.GetAIAgent(createdAgentVersion);
|
||||
|
||||
// You can also create another AIAgent version (V2) by providing the same name with a different definition/instruction.
|
||||
AIAgent jokerAgentV2 = aiProjectClient.CreateAIAgent(name: JokerName, model: deploymentName, instructions: JokerInstructionsV2);
|
||||
// You can also create another AIAgent version by providing the same name with a different definition/instruction.
|
||||
AIAgent newJokerAgent = aiProjectClient.CreateAIAgent(name: JokerName, model: deploymentName, instructions: "You are extremely hilarious at telling jokes.");
|
||||
|
||||
// You can also get the AIAgent latest version by just providing its name.
|
||||
AIAgent jokerAgentLatest = aiProjectClient.GetAIAgent(name: JokerName);
|
||||
AgentVersion latestVersion = jokerAgentLatest.GetService<AgentVersion>()!;
|
||||
AgentVersion latestAgentVersion = jokerAgentLatest.GetService<AgentVersion>()!;
|
||||
|
||||
// The AIAgent version can be accessed via the GetService method.
|
||||
Console.WriteLine($"Latest agent version id: {latestVersion.Id}");
|
||||
Console.WriteLine($"Latest agent version id: {latestAgentVersion.Id}");
|
||||
|
||||
// Once you have the AIAgent, you can invoke it like any other AIAgent.
|
||||
Console.WriteLine(await jokerAgentLatest.RunAsync("Tell me a joke about a pirate."));
|
||||
|
||||
// Cleanup by agent name removes both agent versions created (jokerAgentV1 + jokerAgentV2).
|
||||
await aiProjectClient.Agents.DeleteAgentAsync(jokerAgentV1.Name);
|
||||
// Cleanup by agent name removes both agent versions created.
|
||||
await aiProjectClient.Agents.DeleteAgentAsync(existingJokerAgent.Name);
|
||||
|
||||
+6
-2
@@ -24,10 +24,12 @@ AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential(
|
||||
// Create ChatClientAgent directly
|
||||
ChatClientAgent agent = await aiProjectClient.CreateAIAgentAsync(
|
||||
model: deploymentName,
|
||||
new ChatClientAgentOptions(name: AssistantName, instructions: AssistantInstructions)
|
||||
new ChatClientAgentOptions()
|
||||
{
|
||||
Name = AssistantName,
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = AssistantInstructions,
|
||||
ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<PersonInfo>()
|
||||
}
|
||||
});
|
||||
@@ -44,10 +46,12 @@ Console.WriteLine($"Occupation: {response.Result.Occupation}");
|
||||
// Create the ChatClientAgent with the specified name, instructions, and expected structured output the agent should produce.
|
||||
ChatClientAgent agentWithPersonInfo = aiProjectClient.CreateAIAgent(
|
||||
model: deploymentName,
|
||||
new ChatClientAgentOptions(name: AssistantName, instructions: AssistantInstructions)
|
||||
new ChatClientAgentOptions()
|
||||
{
|
||||
Name = AssistantName,
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = AssistantInstructions,
|
||||
ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<PersonInfo>()
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,6 +6,15 @@ of Azure Foundry Agents and can be used with Azure Foundry as the AI provider.
|
||||
These samples showcase how to work with agents managed through Azure Foundry, including agent creation,
|
||||
versioning, multi-turn conversations, and advanced features like code interpretation and computer use.
|
||||
|
||||
## Classic vs New Foundry Agents
|
||||
|
||||
> [!NOTE]
|
||||
> Recently, Azure Foundry introduced a new and improved experience for creating and managing AI agents, which is the target of these samples.
|
||||
|
||||
For more information about the previous classic agents and for what's new in Foundry Agents, see the [Foundry Agents migration documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/migrate?view=foundry).
|
||||
|
||||
For a sample demonstrating how to use classic Foundry Agents, see the following: [Agent with Azure AI Persistent](../AgentProviders/Agent_With_AzureAIAgentsPersistent/README.md).
|
||||
|
||||
## Getting started with Foundry Agents prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites:
|
||||
|
||||
-4
@@ -17,10 +17,6 @@
|
||||
<PackageReference Include="ModelContextProtocol" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'net10.0'))">
|
||||
<PackageReference Include="System.Net.ServerSentEvents" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
+2
-2
@@ -34,9 +34,9 @@ AIAgent agent = await persistentAgentsClient.CreateAIAgentAsync(
|
||||
options: new()
|
||||
{
|
||||
Name = "MicrosoftLearnAgent",
|
||||
Instructions = "You answer questions by searching the Microsoft Learn content only.",
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You answer questions by searching the Microsoft Learn content only.",
|
||||
Tools = [mcpTool]
|
||||
},
|
||||
});
|
||||
@@ -67,9 +67,9 @@ AIAgent agentWithRequiredApproval = await persistentAgentsClient.CreateAIAgentAs
|
||||
options: new()
|
||||
{
|
||||
Name = "MicrosoftLearnAgentWithApproval",
|
||||
Instructions = "You answer questions by searching the Microsoft Learn content only.",
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You answer questions by searching the Microsoft Learn content only.",
|
||||
Tools = [mcpToolWithApproval]
|
||||
},
|
||||
});
|
||||
|
||||
@@ -15,5 +15,6 @@ of the agent framework.
|
||||
|[A2A](./A2A/README.md)|Getting started with A2A (Agent-to-Agent) specific features|
|
||||
|[Agent Open Telemetry](./AgentOpenTelemetry/README.md)|Getting started with OpenTelemetry for agents|
|
||||
|[Agent With OpenAI exchange types](./AgentWithOpenAI/README.md)|Using OpenAI exchange types with agents|
|
||||
|[Agent With Anthropic](./AgentWithAnthropic/README.md)|Getting started with agents using Anthropic Claude|
|
||||
|[Workflow](./Workflows/README.md)|Getting started with Workflow|
|
||||
|[Model Context Protocol](./ModelContextProtocol/README.md)|Getting started with Model Context Protocol|
|
||||
|
||||
@@ -118,10 +118,11 @@ internal sealed class SloganWriterExecutor : Executor
|
||||
/// <param name="chatClient">The chat client to use for the AI agent.</param>
|
||||
public SloganWriterExecutor(string id, IChatClient chatClient) : base(id)
|
||||
{
|
||||
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional slogan writer. You will be given a task to create a slogan.")
|
||||
ChatClientAgentOptions agentOptions = new()
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are a professional slogan writer. You will be given a task to create a slogan.",
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema<SloganResult>()
|
||||
}
|
||||
};
|
||||
@@ -193,10 +194,11 @@ internal sealed class FeedbackExecutor : Executor<SloganResult>
|
||||
/// <param name="chatClient">The chat client to use for the AI agent.</param>
|
||||
public FeedbackExecutor(string id, IChatClient chatClient) : base(id)
|
||||
{
|
||||
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.")
|
||||
ChatClientAgentOptions agentOptions = new()
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.",
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema<FeedbackResult>()
|
||||
}
|
||||
};
|
||||
|
||||
+4
-2
@@ -85,10 +85,11 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for spam detection</returns>
|
||||
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.")
|
||||
new(chatClient, new ChatClientAgentOptions()
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are a spam detection assistant that identifies spam emails.",
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema<DetectionResult>()
|
||||
}
|
||||
});
|
||||
@@ -98,10 +99,11 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email assistance</returns>
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
|
||||
new(chatClient, new ChatClientAgentOptions()
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.",
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailResponse>()
|
||||
}
|
||||
});
|
||||
|
||||
@@ -100,10 +100,11 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for spam detection</returns>
|
||||
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.")
|
||||
new(chatClient, new ChatClientAgentOptions()
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.",
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema<DetectionResult>()
|
||||
}
|
||||
});
|
||||
@@ -113,10 +114,11 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email assistance</returns>
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
|
||||
new(chatClient, new ChatClientAgentOptions()
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.",
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailResponse>()
|
||||
}
|
||||
});
|
||||
|
||||
+6
-3
@@ -140,10 +140,11 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email analysis</returns>
|
||||
private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.")
|
||||
new(chatClient, new ChatClientAgentOptions()
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are a spam detection assistant that identifies spam emails.",
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema<AnalysisResult>()
|
||||
}
|
||||
});
|
||||
@@ -153,10 +154,11 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email assistance</returns>
|
||||
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
|
||||
new(chatClient, new ChatClientAgentOptions()
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.",
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailResponse>()
|
||||
}
|
||||
});
|
||||
@@ -166,10 +168,11 @@ public static class Program
|
||||
/// </summary>
|
||||
/// <returns>A ChatClientAgent configured for email summarization</returns>
|
||||
private static ChatClientAgent GetEmailSummaryAgent(IChatClient chatClient) =>
|
||||
new(chatClient, new ChatClientAgentOptions(instructions: "You are an assistant that helps users summarize emails.")
|
||||
new(chatClient, new ChatClientAgentOptions()
|
||||
{
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = "You are an assistant that helps users summarize emails.",
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailSummary>()
|
||||
}
|
||||
});
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Anthropic.SDK" />
|
||||
<PackageReference Include="Anthropic" />
|
||||
<PackageReference Include="AWSSDK.Extensions.Bedrock.MEAI" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
|
||||
</ItemGroup>
|
||||
|
||||
+7
-10
@@ -12,19 +12,16 @@ const string Topic = "Goldendoodles make the best pets.";
|
||||
IChatClient aws = new AmazonBedrockRuntimeClient(
|
||||
Environment.GetEnvironmentVariable("BEDROCK_ACCESSKEY"!),
|
||||
Environment.GetEnvironmentVariable("BEDROCK_SECRETACCESSKEY")!,
|
||||
Amazon.RegionEndpoint.USEast1).AsIChatClient("amazon.nova-pro-v1:0");
|
||||
Amazon.RegionEndpoint.USEast1)
|
||||
.AsIChatClient("amazon.nova-pro-v1:0");
|
||||
|
||||
IChatClient anthropic = new Anthropic.SDK.AnthropicClient(
|
||||
Environment.GetEnvironmentVariable("ANTHROPIC_APIKEY")!).Messages.AsBuilder()
|
||||
.ConfigureOptions(o =>
|
||||
{
|
||||
o.ModelId ??= "claude-sonnet-4-20250514";
|
||||
o.MaxOutputTokens ??= 10 * 1024;
|
||||
})
|
||||
.Build();
|
||||
IChatClient anthropic = new Anthropic.AnthropicClient(
|
||||
new() { APIKey = Environment.GetEnvironmentVariable("ANTHROPIC_APIKEY") })
|
||||
.AsIChatClient("claude-sonnet-4-20250514");
|
||||
|
||||
IChatClient openai = new OpenAI.OpenAIClient(
|
||||
Environment.GetEnvironmentVariable("OPENAI_APIKEY")!).GetChatClient("gpt-4o-mini").AsIChatClient();
|
||||
Environment.GetEnvironmentVariable("OPENAI_APIKEY")!).GetChatClient("gpt-4o-mini")
|
||||
.AsIChatClient();
|
||||
|
||||
// Define our agents.
|
||||
AIAgent researcher = new ChatClientAgent(aws,
|
||||
|
||||
+11
-11
@@ -285,19 +285,19 @@ internal sealed class CriticExecutor : Executor<ChatMessage, CriticDecision>
|
||||
this._agent = new ChatClientAgent(chatClient, new ChatClientAgentOptions
|
||||
{
|
||||
Name = "Critic",
|
||||
Instructions = """
|
||||
You are a constructive critic. Review the content and provide specific feedback.
|
||||
Always try to provide actionable suggestions for improvement and strive to identify improvement points.
|
||||
Only approve if the content is high quality, clear, and meets the original requirements and you see no improvement points.
|
||||
|
||||
Provide your decision as structured output with:
|
||||
- approved: true if content is good, false if revisions needed
|
||||
- feedback: specific improvements needed (empty if approved)
|
||||
|
||||
Be concise but specific in your feedback.
|
||||
""",
|
||||
ChatOptions = new()
|
||||
{
|
||||
Instructions = """
|
||||
You are a constructive critic. Review the content and provide specific feedback.
|
||||
Always try to provide actionable suggestions for improvement and strive to identify improvement points.
|
||||
Only approve if the content is high quality, clear, and meets the original requirements and you see no improvement points.
|
||||
|
||||
Provide your decision as structured output with:
|
||||
- approved: true if content is good, false if revisions needed
|
||||
- feedback: specific improvements needed (empty if approved)
|
||||
|
||||
Be concise but specific in your feedback.
|
||||
""",
|
||||
ResponseFormat = ChatResponseFormat.ForJsonSchema<CriticDecision>()
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
@@ -37,15 +37,15 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.AgentServer.AgentFramework" Version="1.0.0-beta.4" />
|
||||
<PackageReference Include="Azure.AI.OpenAI" Version="2.5.0-beta.1" />
|
||||
<PackageReference Include="Azure.AI.OpenAI" Version="2.7.0-beta.1" />
|
||||
<PackageReference Include="Azure.Identity" Version="1.17.0" />
|
||||
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.0.0-preview.251110.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.10.2-preview.1.25552.1" />
|
||||
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.0.0-preview.251125.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.0.1-preview.1.25571.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Add analyzers with compatible versions -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.100">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -53,15 +53,15 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.12.9">
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.12.9">
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.12.9">
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Build the application
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
|
||||
WORKDIR /src
|
||||
|
||||
# Copy files from the current directory on the host to the working directory in the container
|
||||
@@ -7,10 +7,10 @@ COPY . .
|
||||
|
||||
RUN dotnet restore
|
||||
RUN dotnet build -c Release --no-restore
|
||||
RUN dotnet publish -c Release --no-build -o /app
|
||||
RUN dotnet publish -c Release --no-build -o /app -f net10.0
|
||||
|
||||
# Run the application
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
|
||||
WORKDIR /app
|
||||
|
||||
# Copy everything needed to run the app from the "build" stage.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
@@ -36,15 +36,15 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.AgentServer.AgentFramework" Version="1.0.0-beta.4" />
|
||||
<PackageReference Include="Azure.AI.OpenAI" Version="2.5.0-beta.1" />
|
||||
<PackageReference Include="Azure.AI.OpenAI" Version="2.7.0-beta.1" />
|
||||
<PackageReference Include="Azure.Identity" Version="1.17.0" />
|
||||
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.0.0-preview.251110.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.10.2-preview.1.25552.1" />
|
||||
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.0.0-preview.251125.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.0.1-preview.1.25571.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Add analyzers with compatible versions -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.100">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -52,15 +52,15 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.12.9">
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.12.9">
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.12.9">
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Build the application
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
|
||||
WORKDIR /src
|
||||
|
||||
# Copy files from the current directory on the host to the working directory in the container
|
||||
@@ -7,10 +7,10 @@ COPY . .
|
||||
|
||||
RUN dotnet restore
|
||||
RUN dotnet build -c Release --no-restore
|
||||
RUN dotnet publish -c Release --no-build -o /app
|
||||
RUN dotnet publish -c Release --no-build -o /app -f net10.0
|
||||
|
||||
# Run the application
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
|
||||
WORKDIR /app
|
||||
|
||||
# Copy everything needed to run the app from the "build" stage.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
@@ -36,15 +36,15 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.AgentServer.AgentFramework" Version="1.0.0-beta.4" />
|
||||
<PackageReference Include="Azure.AI.OpenAI" Version="2.5.0-beta.1" />
|
||||
<PackageReference Include="Azure.AI.OpenAI" Version="2.7.0-beta.1" />
|
||||
<PackageReference Include="Azure.Identity" Version="1.17.0" />
|
||||
<PackageReference Include="Microsoft.Agents.AI.Workflows" Version="1.0.0-preview.251110.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.10.2-preview.1.25552.1" />
|
||||
<PackageReference Include="Microsoft.Agents.AI.Workflows" Version="1.0.0-preview.251125.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.0.1-preview.1.25571.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Add analyzers with compatible versions -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.100">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -52,15 +52,15 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.12.9">
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.12.9">
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.12.9">
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Build the application
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
|
||||
WORKDIR /src
|
||||
|
||||
# Copy files from the current directory on the host to the working directory in the container
|
||||
@@ -7,10 +7,10 @@ COPY . .
|
||||
|
||||
RUN dotnet restore
|
||||
RUN dotnet build -c Release --no-restore
|
||||
RUN dotnet publish -c Release --no-build -o /app
|
||||
RUN dotnet publish -c Release --no-build -o /app -f net10.0
|
||||
|
||||
# Run the application
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
|
||||
WORKDIR /app
|
||||
|
||||
# Copy everything needed to run the app from the "build" stage.
|
||||
|
||||
@@ -33,9 +33,9 @@ public class WeatherForecastAgent : DelegatingAIAgent
|
||||
new ChatClientAgentOptions()
|
||||
{
|
||||
Name = AgentName,
|
||||
Instructions = AgentInstructions,
|
||||
ChatOptions = new ChatOptions()
|
||||
{
|
||||
Instructions = AgentInstructions,
|
||||
Tools = [new ApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather))],
|
||||
// We want the agent to return structured output in a known format
|
||||
// so that we can easily create adaptive cards from the response.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.ServerSentEvents;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
@@ -74,26 +75,28 @@ internal sealed class A2AAgent : AIAgent
|
||||
{
|
||||
_ = Throw.IfNull(messages);
|
||||
|
||||
var a2aMessage = messages.ToA2AMessage();
|
||||
|
||||
thread ??= this.GetNewThread();
|
||||
if (thread is not A2AAgentThread typedThread)
|
||||
{
|
||||
throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used.");
|
||||
}
|
||||
|
||||
// Linking the message to the existing conversation, if any.
|
||||
a2aMessage.ContextId = typedThread.ContextId;
|
||||
A2AAgentThread typedThread = this.GetA2AThread(thread, options);
|
||||
|
||||
this._logger.LogA2AAgentInvokingAgent(nameof(RunAsync), this.Id, this.Name);
|
||||
|
||||
var a2aResponse = await this._a2aClient.SendMessageAsync(new MessageSendParams { Message = a2aMessage }, cancellationToken).ConfigureAwait(false);
|
||||
A2AResponse? a2aResponse = null;
|
||||
|
||||
if (GetContinuationToken(messages, options) is { } token)
|
||||
{
|
||||
a2aResponse = await this._a2aClient.GetTaskAsync(token.TaskId, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var a2aMessage = CreateA2AMessage(typedThread, messages);
|
||||
|
||||
a2aResponse = await this._a2aClient.SendMessageAsync(new MessageSendParams { Message = a2aMessage }, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
this._logger.LogAgentChatClientInvokedAgent(nameof(RunAsync), this.Id, this.Name);
|
||||
|
||||
if (a2aResponse is AgentMessage message)
|
||||
{
|
||||
UpdateThreadConversationId(typedThread, message.ContextId);
|
||||
UpdateThread(typedThread, message.ContextId);
|
||||
|
||||
return new AgentRunResponse
|
||||
{
|
||||
@@ -101,21 +104,30 @@ internal sealed class A2AAgent : AIAgent
|
||||
ResponseId = message.MessageId,
|
||||
RawRepresentation = message,
|
||||
Messages = [message.ToChatMessage()],
|
||||
AdditionalProperties = message.Metadata.ToAdditionalProperties(),
|
||||
AdditionalProperties = message.Metadata?.ToAdditionalProperties(),
|
||||
};
|
||||
}
|
||||
|
||||
if (a2aResponse is AgentTask agentTask)
|
||||
{
|
||||
UpdateThreadConversationId(typedThread, agentTask.ContextId);
|
||||
UpdateThread(typedThread, agentTask.ContextId, agentTask.Id);
|
||||
|
||||
return new AgentRunResponse
|
||||
var response = new AgentRunResponse
|
||||
{
|
||||
AgentId = this.Id,
|
||||
ResponseId = agentTask.Id,
|
||||
RawRepresentation = agentTask,
|
||||
Messages = agentTask.ToChatMessages(),
|
||||
AdditionalProperties = agentTask.Metadata.ToAdditionalProperties(),
|
||||
Messages = agentTask.ToChatMessages() ?? [],
|
||||
ContinuationToken = CreateContinuationToken(agentTask.Id, agentTask.Status.State),
|
||||
AdditionalProperties = agentTask.Metadata?.ToAdditionalProperties(),
|
||||
};
|
||||
|
||||
if (agentTask.ToChatMessages() is { Count: > 0 } taskMessages)
|
||||
{
|
||||
response.Messages = taskMessages;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"Only Message and AgentTask responses are supported from A2A agents. Received: {a2aResponse.GetType().FullName ?? "null"}");
|
||||
@@ -126,43 +138,63 @@ internal sealed class A2AAgent : AIAgent
|
||||
{
|
||||
_ = Throw.IfNull(messages);
|
||||
|
||||
var a2aMessage = messages.ToA2AMessage();
|
||||
|
||||
thread ??= this.GetNewThread();
|
||||
if (thread is not A2AAgentThread typedThread)
|
||||
{
|
||||
throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used.");
|
||||
}
|
||||
|
||||
// Linking the message to the existing conversation, if any.
|
||||
a2aMessage.ContextId = typedThread.ContextId;
|
||||
A2AAgentThread typedThread = this.GetA2AThread(thread, options);
|
||||
|
||||
this._logger.LogA2AAgentInvokingAgent(nameof(RunStreamingAsync), this.Id, this.Name);
|
||||
|
||||
var a2aSseEvents = this._a2aClient.SendMessageStreamingAsync(new MessageSendParams { Message = a2aMessage }, cancellationToken).ConfigureAwait(false);
|
||||
ConfiguredCancelableAsyncEnumerable<SseItem<A2AEvent>> a2aSseEvents;
|
||||
|
||||
if (options?.ContinuationToken is not null)
|
||||
{
|
||||
// Task stream resumption is not well defined in the A2A v2.* specification, leaving it to the agent implementations.
|
||||
// The v3.0 specification improves this by defining task stream reconnection that allows obtaining the same stream
|
||||
// from the beginning, but it does not define stream resumption from a specific point in the stream.
|
||||
// Therefore, the code should be updated once the A2A .NET library supports the A2A v3.0 specification,
|
||||
// and AF has the necessary model to allow consumers to know whether they need to resume the stream and add new updates to
|
||||
// the existing ones or reconnect the stream and obtain all updates again.
|
||||
// For more details, see the following issue: https://github.com/microsoft/agent-framework/issues/1764
|
||||
throw new InvalidOperationException("Reconnecting to task streams using continuation tokens is not supported yet.");
|
||||
// a2aSseEvents = this._a2aClient.SubscribeToTaskAsync(token.TaskId, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var a2aMessage = CreateA2AMessage(typedThread, messages);
|
||||
|
||||
a2aSseEvents = this._a2aClient.SendMessageStreamingAsync(new MessageSendParams { Message = a2aMessage }, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
this._logger.LogAgentChatClientInvokedAgent(nameof(RunStreamingAsync), this.Id, this.Name);
|
||||
|
||||
string? contextId = null;
|
||||
string? taskId = null;
|
||||
|
||||
await foreach (var sseEvent in a2aSseEvents)
|
||||
{
|
||||
if (sseEvent.Data is not AgentMessage message)
|
||||
if (sseEvent.Data is AgentMessage message)
|
||||
{
|
||||
throw new NotSupportedException($"Only message responses are supported from A2A agents. Received: {sseEvent.Data?.GetType().FullName ?? "null"}");
|
||||
contextId = message.ContextId;
|
||||
|
||||
yield return this.ConvertToAgentResponseUpdate(message);
|
||||
}
|
||||
|
||||
UpdateThreadConversationId(typedThread, message.ContextId);
|
||||
|
||||
yield return new AgentRunResponseUpdate
|
||||
else if (sseEvent.Data is AgentTask task)
|
||||
{
|
||||
AgentId = this.Id,
|
||||
ResponseId = message.MessageId,
|
||||
RawRepresentation = message,
|
||||
Role = ChatRole.Assistant,
|
||||
MessageId = message.MessageId,
|
||||
Contents = [.. message.Parts.Select(part => part.ToAIContent()).OfType<AIContent>()],
|
||||
AdditionalProperties = message.Metadata.ToAdditionalProperties(),
|
||||
};
|
||||
contextId = task.ContextId;
|
||||
taskId = task.Id;
|
||||
|
||||
yield return this.ConvertToAgentResponseUpdate(task);
|
||||
}
|
||||
else if (sseEvent.Data is TaskUpdateEvent taskUpdateEvent)
|
||||
{
|
||||
contextId = taskUpdateEvent.ContextId;
|
||||
taskId = taskUpdateEvent.TaskId;
|
||||
|
||||
yield return this.ConvertToAgentResponseUpdate(taskUpdateEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Only message, task, task update events are supported from A2A agents. Received: {sseEvent.Data.GetType().FullName ?? "null"}");
|
||||
}
|
||||
}
|
||||
|
||||
UpdateThread(typedThread, contextId, taskId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -177,7 +209,27 @@ internal sealed class A2AAgent : AIAgent
|
||||
/// <inheritdoc/>
|
||||
public override string? Description => this._description ?? base.Description;
|
||||
|
||||
private static void UpdateThreadConversationId(A2AAgentThread? thread, string? contextId)
|
||||
private A2AAgentThread GetA2AThread(AgentThread? thread, AgentRunOptions? options)
|
||||
{
|
||||
// Aligning with other agent implementations that support background responses, where
|
||||
// a thread is required for background responses to prevent inconsistent experience
|
||||
// for callers if they forget to provide the thread for initial or follow-up runs.
|
||||
if (options?.AllowBackgroundResponses is true && thread is null)
|
||||
{
|
||||
throw new InvalidOperationException("A thread must be provided when AllowBackgroundResponses is enabled.");
|
||||
}
|
||||
|
||||
thread ??= this.GetNewThread();
|
||||
|
||||
if (thread is not A2AAgentThread typedThread)
|
||||
{
|
||||
throw new InvalidOperationException($"The provided thread type {thread.GetType()} is not compatible with the agent. Only A2A agent created threads are supported.");
|
||||
}
|
||||
|
||||
return typedThread;
|
||||
}
|
||||
|
||||
private static void UpdateThread(A2AAgentThread? thread, string? contextId, string? taskId = null)
|
||||
{
|
||||
if (thread is null)
|
||||
{
|
||||
@@ -194,5 +246,93 @@ internal sealed class A2AAgent : AIAgent
|
||||
|
||||
// Assign a server-generated context Id to the thread if it's not already set.
|
||||
thread.ContextId ??= contextId;
|
||||
thread.TaskId = taskId;
|
||||
}
|
||||
|
||||
private static AgentMessage CreateA2AMessage(A2AAgentThread typedThread, IEnumerable<ChatMessage> messages)
|
||||
{
|
||||
var a2aMessage = messages.ToA2AMessage();
|
||||
|
||||
// Linking the message to the existing conversation, if any.
|
||||
// See: https://github.com/a2aproject/A2A/blob/main/docs/topics/life-of-a-task.md#group-related-interactions
|
||||
a2aMessage.ContextId = typedThread.ContextId;
|
||||
|
||||
// Link the message as a follow-up to an existing task, if any.
|
||||
// See: https://github.com/a2aproject/A2A/blob/main/docs/topics/life-of-a-task.md#task-refinements
|
||||
a2aMessage.ReferenceTaskIds = typedThread.TaskId is null ? null : [typedThread.TaskId];
|
||||
|
||||
return a2aMessage;
|
||||
}
|
||||
|
||||
private static A2AContinuationToken? GetContinuationToken(IEnumerable<ChatMessage> messages, AgentRunOptions? options = null)
|
||||
{
|
||||
if (options?.ContinuationToken is ResponseContinuationToken token)
|
||||
{
|
||||
if (messages.Any())
|
||||
{
|
||||
throw new InvalidOperationException("Messages are not allowed when continuing a background response using a continuation token.");
|
||||
}
|
||||
|
||||
return A2AContinuationToken.FromToken(token);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static A2AContinuationToken? CreateContinuationToken(string taskId, TaskState state)
|
||||
{
|
||||
if (state == TaskState.Submitted || state == TaskState.Working)
|
||||
{
|
||||
return new A2AContinuationToken(taskId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private AgentRunResponseUpdate ConvertToAgentResponseUpdate(AgentMessage message)
|
||||
{
|
||||
return new AgentRunResponseUpdate
|
||||
{
|
||||
AgentId = this.Id,
|
||||
ResponseId = message.MessageId,
|
||||
RawRepresentation = message,
|
||||
Role = ChatRole.Assistant,
|
||||
MessageId = message.MessageId,
|
||||
Contents = message.Parts.ConvertAll(part => part.ToAIContent()),
|
||||
AdditionalProperties = message.Metadata?.ToAdditionalProperties(),
|
||||
};
|
||||
}
|
||||
|
||||
private AgentRunResponseUpdate ConvertToAgentResponseUpdate(AgentTask task)
|
||||
{
|
||||
return new AgentRunResponseUpdate
|
||||
{
|
||||
AgentId = this.Id,
|
||||
ResponseId = task.Id,
|
||||
RawRepresentation = task,
|
||||
Role = ChatRole.Assistant,
|
||||
Contents = task.ToAIContents(),
|
||||
AdditionalProperties = task.Metadata?.ToAdditionalProperties(),
|
||||
};
|
||||
}
|
||||
|
||||
private AgentRunResponseUpdate ConvertToAgentResponseUpdate(TaskUpdateEvent taskUpdateEvent)
|
||||
{
|
||||
AgentRunResponseUpdate responseUpdate = new()
|
||||
{
|
||||
AgentId = this.Id,
|
||||
ResponseId = taskUpdateEvent.TaskId,
|
||||
RawRepresentation = taskUpdateEvent,
|
||||
Role = ChatRole.Assistant,
|
||||
AdditionalProperties = taskUpdateEvent.Metadata?.ToAdditionalProperties() ?? [],
|
||||
};
|
||||
|
||||
if (taskUpdateEvent is TaskArtifactUpdateEvent artifactUpdateEvent)
|
||||
{
|
||||
responseUpdate.Contents = artifactUpdateEvent.Artifact.ToAIContents();
|
||||
responseUpdate.RawRepresentation = artifactUpdateEvent;
|
||||
}
|
||||
|
||||
return responseUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.Agents.AI.A2A;
|
||||
@@ -7,22 +8,59 @@ namespace Microsoft.Agents.AI.A2A;
|
||||
/// <summary>
|
||||
/// Thread for A2A based agents.
|
||||
/// </summary>
|
||||
public sealed class A2AAgentThread : ServiceIdAgentThread
|
||||
public sealed class A2AAgentThread : AgentThread
|
||||
{
|
||||
internal A2AAgentThread()
|
||||
{
|
||||
}
|
||||
|
||||
internal A2AAgentThread(JsonElement serializedThreadState, JsonSerializerOptions? jsonSerializerOptions = null) : base(serializedThreadState, jsonSerializerOptions)
|
||||
internal A2AAgentThread(JsonElement serializedThreadState, JsonSerializerOptions? jsonSerializerOptions = null)
|
||||
{
|
||||
if (serializedThreadState.ValueKind != JsonValueKind.Object)
|
||||
{
|
||||
throw new ArgumentException("The serialized thread state must be a JSON object.", nameof(serializedThreadState));
|
||||
}
|
||||
|
||||
var state = serializedThreadState.Deserialize(
|
||||
A2AJsonUtilities.DefaultOptions.GetTypeInfo(typeof(A2AAgentThreadState))) as A2AAgentThreadState;
|
||||
|
||||
if (state?.ContextId is string contextId)
|
||||
{
|
||||
this.ContextId = contextId;
|
||||
}
|
||||
|
||||
if (state?.TaskId is string taskId)
|
||||
{
|
||||
this.TaskId = taskId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID for the current conversation with the A2A agent.
|
||||
/// </summary>
|
||||
public string? ContextId
|
||||
public string? ContextId { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID for the task the agent is currently working on.
|
||||
/// </summary>
|
||||
public string? TaskId { get; internal set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
|
||||
{
|
||||
get { return this.ServiceThreadId; }
|
||||
internal set { this.ServiceThreadId = value; }
|
||||
var state = new A2AAgentThreadState
|
||||
{
|
||||
ContextId = this.ContextId,
|
||||
TaskId = this.TaskId
|
||||
};
|
||||
|
||||
return JsonSerializer.SerializeToElement(state, A2AJsonUtilities.DefaultOptions.GetTypeInfo(typeof(A2AAgentThreadState)));
|
||||
}
|
||||
|
||||
internal sealed class A2AAgentThreadState
|
||||
{
|
||||
public string? ContextId { get; set; }
|
||||
|
||||
public string? TaskId { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.A2A;
|
||||
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||
internal class A2AContinuationToken : ResponseContinuationToken
|
||||
{
|
||||
internal A2AContinuationToken(string taskId)
|
||||
{
|
||||
_ = Throw.IfNullOrEmpty(taskId);
|
||||
|
||||
this.TaskId = taskId;
|
||||
}
|
||||
|
||||
internal string TaskId { get; }
|
||||
|
||||
internal static A2AContinuationToken FromToken(ResponseContinuationToken token)
|
||||
{
|
||||
if (token is A2AContinuationToken longRunContinuationToken)
|
||||
{
|
||||
return longRunContinuationToken;
|
||||
}
|
||||
|
||||
ReadOnlyMemory<byte> data = token.ToBytes();
|
||||
|
||||
if (data.Length == 0)
|
||||
{
|
||||
Throw.ArgumentException(nameof(token), "Failed to create A2AContinuationToken from provided token because it does not contain any data.");
|
||||
}
|
||||
|
||||
Utf8JsonReader reader = new(data.Span);
|
||||
|
||||
string taskId = null!;
|
||||
|
||||
reader.Read();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.EndObject)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
string propertyName = reader.GetString() ?? throw new JsonException("Failed to read property name from continuation token.");
|
||||
|
||||
switch (propertyName)
|
||||
{
|
||||
case "taskId":
|
||||
reader.Read();
|
||||
taskId = reader.GetString()!;
|
||||
break;
|
||||
default:
|
||||
throw new JsonException($"Unrecognized property '{propertyName}'.");
|
||||
}
|
||||
}
|
||||
|
||||
return new(taskId);
|
||||
}
|
||||
|
||||
public override ReadOnlyMemory<byte> ToBytes()
|
||||
{
|
||||
using MemoryStream stream = new();
|
||||
using Utf8JsonWriter writer = new(stream);
|
||||
|
||||
writer.WriteStartObject();
|
||||
|
||||
writer.WriteString("taskId", this.TaskId);
|
||||
|
||||
writer.WriteEndObject();
|
||||
|
||||
writer.Flush();
|
||||
stream.Position = 0;
|
||||
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Agents.AI.A2A;
|
||||
|
||||
namespace Microsoft.Agents.AI;
|
||||
|
||||
/// <summary>
|
||||
/// Provides utility methods and configurations for JSON serialization operations for A2A agent types.
|
||||
/// </summary>
|
||||
public static partial class A2AJsonUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default <see cref="JsonSerializerOptions"/> instance used for JSON serialization operations of A2A agent types.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// For Native AOT or applications disabling <see cref="JsonSerializer.IsReflectionEnabledByDefault"/>, this instance
|
||||
/// includes source generated contracts for A2A agent types.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// It additionally turns on the following settings:
|
||||
/// <list type="number">
|
||||
/// <item><description>Enables <see cref="JsonSerializerDefaults.Web"/> defaults.</description></item>
|
||||
/// <item><description>Enables <see cref="JsonIgnoreCondition.WhenWritingNull"/> as the default ignore condition for properties.</description></item>
|
||||
/// <item><description>Enables <see cref="JsonNumberHandling.AllowReadingFromString"/> as the default number handling for number types.</description></item>
|
||||
/// <item><description>
|
||||
/// Enables <see cref="JavaScriptEncoder.UnsafeRelaxedJsonEscaping"/> when escaping JSON strings.
|
||||
/// Consuming applications must ensure that JSON outputs are adequately escaped before embedding in other document formats, such as HTML and XML.
|
||||
/// </description></item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static JsonSerializerOptions DefaultOptions { get; } = CreateDefaultOptions();
|
||||
|
||||
/// <summary>
|
||||
/// Creates and configures the default JSON serialization options for agent abstraction types.
|
||||
/// </summary>
|
||||
/// <returns>The configured options.</returns>
|
||||
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresDynamicCode", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
|
||||
private static JsonSerializerOptions CreateDefaultOptions()
|
||||
{
|
||||
// Copy the configuration from the source generated context.
|
||||
JsonSerializerOptions options = new(JsonContext.Default.Options)
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // same as AIJsonUtilities
|
||||
};
|
||||
|
||||
// Chain in the resolvers from both AIJsonUtilities and our source generated context.
|
||||
// We want AIJsonUtilities first to ensure any M.E.AI types are handled via its resolver.
|
||||
options.TypeInfoResolverChain.Clear();
|
||||
options.TypeInfoResolverChain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
|
||||
|
||||
// If reflection-based serialization is enabled by default, this includes
|
||||
// the default type info resolver that utilizes reflection, but we need to manually
|
||||
// apply the same converter AIJsonUtilities adds for string-based enum serialization,
|
||||
// as that's not propagated as part of the resolver.
|
||||
if (JsonSerializer.IsReflectionEnabledByDefault)
|
||||
{
|
||||
options.Converters.Add(new JsonStringEnumConverter());
|
||||
}
|
||||
|
||||
options.MakeReadOnly();
|
||||
return options;
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web,
|
||||
UseStringEnumConverter = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString)]
|
||||
|
||||
// A2A agent types
|
||||
[JsonSerializable(typeof(A2AAgentThread.A2AAgentThreadState))]
|
||||
[ExcludeFromCodeCoverage]
|
||||
private sealed partial class JsonContext : JsonSerializerContext;
|
||||
}
|
||||
@@ -11,20 +11,37 @@ namespace A2A;
|
||||
/// </summary>
|
||||
internal static class A2AAgentTaskExtensions
|
||||
{
|
||||
internal static IList<ChatMessage> ToChatMessages(this AgentTask agentTask)
|
||||
internal static IList<ChatMessage>? ToChatMessages(this AgentTask agentTask)
|
||||
{
|
||||
_ = Throw.IfNull(agentTask);
|
||||
|
||||
List<ChatMessage> messages = [];
|
||||
List<ChatMessage>? messages = null;
|
||||
|
||||
if (agentTask.Artifacts is not null)
|
||||
if (agentTask?.Artifacts is { Count: > 0 })
|
||||
{
|
||||
foreach (var artifact in agentTask.Artifacts)
|
||||
{
|
||||
messages.Add(artifact.ToChatMessage());
|
||||
(messages ??= []).Add(artifact.ToChatMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
internal static IList<AIContent>? ToAIContents(this AgentTask agentTask)
|
||||
{
|
||||
_ = Throw.IfNull(agentTask);
|
||||
|
||||
List<AIContent>? aiContents = null;
|
||||
|
||||
if (agentTask.Artifacts is not null)
|
||||
{
|
||||
foreach (var artifact in agentTask.Artifacts)
|
||||
{
|
||||
(aiContents ??= []).AddRange(artifact.ToAIContents());
|
||||
}
|
||||
}
|
||||
|
||||
return aiContents;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,21 +12,15 @@ internal static class A2AArtifactExtensions
|
||||
{
|
||||
internal static ChatMessage ToChatMessage(this Artifact artifact)
|
||||
{
|
||||
List<AIContent>? aiContents = null;
|
||||
|
||||
foreach (var part in artifact.Parts)
|
||||
{
|
||||
var content = part.ToAIContent();
|
||||
if (content is not null)
|
||||
{
|
||||
(aiContents ??= []).Add(content);
|
||||
}
|
||||
}
|
||||
|
||||
return new ChatMessage(ChatRole.Assistant, aiContents)
|
||||
return new ChatMessage(ChatRole.Assistant, artifact.ToAIContents())
|
||||
{
|
||||
AdditionalProperties = artifact.Metadata.ToAdditionalProperties(),
|
||||
RawRepresentation = artifact,
|
||||
};
|
||||
}
|
||||
|
||||
internal static List<AIContent> ToAIContents(this Artifact artifact)
|
||||
{
|
||||
return artifact.Parts.ConvertAll(part => part.ToAIContent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionSuffix>preview</VersionSuffix>
|
||||
<NoWarn>$(NoWarn);MEAI001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<InjectSharedThrow>true</InjectSharedThrow>
|
||||
<InjectTrimAttributesOnLegacy>true</InjectTrimAttributesOnLegacy>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="A2A" />
|
||||
<PackageReference Include="System.Net.ServerSentEvents" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.AI" />
|
||||
<PackageReference Include="System.Net.ServerSentEvents" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
<PackageReference Include="System.Net.Http.Json" />
|
||||
<PackageReference Include="System.Threading.Channels" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -32,7 +32,7 @@ internal sealed class RunAgentInput
|
||||
[JsonPropertyName("context")]
|
||||
public AGUIContextItem[] Context { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("forwardedProperties")]
|
||||
[JsonPropertyName("forwardedProps")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||
public JsonElement ForwardedProperties { get; set; }
|
||||
}
|
||||
|
||||
@@ -328,28 +328,4 @@ public abstract class AIAgent
|
||||
AgentThread? thread = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the specified thread about new messages that have been added to the conversation.
|
||||
/// </summary>
|
||||
/// <param name="thread">The conversation thread to notify about the new messages.</param>
|
||||
/// <param name="messages">The collection of new messages to report to the thread.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A task that represents the asynchronous notification operation.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="thread"/> or <paramref name="messages"/> is <see langword="null"/>.</exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method ensures that conversation threads are kept informed about message additions, which
|
||||
/// is important for threads that manage their own state, memory components, or derived context.
|
||||
/// While all agent implementations should notify their threads, the specific actions taken by
|
||||
/// each thread type may vary.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
protected static async Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IEnumerable<ChatMessage> messages, CancellationToken cancellationToken)
|
||||
{
|
||||
_ = Throw.IfNull(thread);
|
||||
_ = Throw.IfNull(messages);
|
||||
|
||||
await thread.MessagesReceivedAsync(messages, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public abstract class AIContextProvider
|
||||
/// that will be used. Context providers can use this information to determine what additional context
|
||||
/// should be provided for the invocation.
|
||||
/// </remarks>
|
||||
public class InvokingContext
|
||||
public sealed class InvokingContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvokingContext"/> class with the specified request messages.
|
||||
@@ -153,7 +153,7 @@ public abstract class AIContextProvider
|
||||
/// request messages that were used and the response messages that were generated. It also indicates
|
||||
/// whether the invocation succeeded or failed.
|
||||
/// </remarks>
|
||||
public class InvokedContext
|
||||
public sealed class InvokedContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvokedContext"/> class with the specified request messages.
|
||||
|
||||
@@ -49,7 +49,7 @@ public class AgentRunOptions
|
||||
/// can be polled for completion by obtaining the token from the <see cref="AgentRunResponse.ContinuationToken"/> property
|
||||
/// and passing it via this property on subsequent calls to <see cref="AIAgent.RunAsync(AgentThread?, AgentRunOptions?, System.Threading.CancellationToken)"/>.
|
||||
/// </remarks>
|
||||
public object? ContinuationToken { get; set; }
|
||||
public ResponseContinuationToken? ContinuationToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the background responses are allowed.
|
||||
|
||||
@@ -175,7 +175,7 @@ public class AgentRunResponse
|
||||
/// to poll for completion.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public object? ContinuationToken { get; set; }
|
||||
public ResponseContinuationToken? ContinuationToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp indicating when this response was created.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user